1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2025 KEBA Industrial Automation GmbH
4 *
5 * Driver for KEBA fan controller FPGA IP core
6 *
7 */
8
9 #include <linux/hwmon.h>
10 #include <linux/io.h>
11 #include <linux/module.h>
12 #include <linux/device.h>
13 #include <linux/auxiliary_bus.h>
14 #include <linux/misc/keba.h>
15
16 #define KFAN "kfan"
17
18 #define KFAN_CONTROL_REG 0x04
19
20 #define KFAN_STATUS_REG 0x08
21 #define KFAN_STATUS_PRESENT 0x01
22 #define KFAN_STATUS_REGULABLE 0x02
23 #define KFAN_STATUS_TACHO 0x04
24 #define KFAN_STATUS_BLOCKED 0x08
25
26 #define KFAN_TACHO_REG 0x0c
27
28 #define KFAN_DEFAULT_DIV 2
29
30 struct kfan {
31 void __iomem *base;
32 bool tacho;
33 bool regulable;
34
35 /* hwmon API configuration */
36 u32 fan_channel_config[2];
37 struct hwmon_channel_info fan_info;
38 u32 pwm_channel_config[2];
39 struct hwmon_channel_info pwm_info;
40 const struct hwmon_channel_info *info[3];
41 struct hwmon_chip_info chip;
42 };
43
kfan_get_fault(struct kfan * kfan)44 static bool kfan_get_fault(struct kfan *kfan)
45 {
46 u8 status = ioread8(kfan->base + KFAN_STATUS_REG);
47
48 if (!(status & KFAN_STATUS_PRESENT))
49 return true;
50
51 if (!kfan->tacho && (status & KFAN_STATUS_BLOCKED))
52 return true;
53
54 return false;
55 }
56
kfan_count_to_rpm(u16 count)57 static unsigned int kfan_count_to_rpm(u16 count)
58 {
59 if (count == 0 || count == 0xffff)
60 return 0;
61
62 return 5000000UL / (KFAN_DEFAULT_DIV * count);
63 }
64
kfan_get_rpm(struct kfan * kfan)65 static unsigned int kfan_get_rpm(struct kfan *kfan)
66 {
67 unsigned int rpm;
68 u16 count;
69
70 count = ioread16(kfan->base + KFAN_TACHO_REG);
71 rpm = kfan_count_to_rpm(count);
72
73 return rpm;
74 }
75
kfan_get_pwm(struct kfan * kfan)76 static unsigned int kfan_get_pwm(struct kfan *kfan)
77 {
78 return ioread8(kfan->base + KFAN_CONTROL_REG);
79 }
80
kfan_set_pwm(struct kfan * kfan,long val)81 static int kfan_set_pwm(struct kfan *kfan, long val)
82 {
83 if (val < 0 || val > 0xff)
84 return -EINVAL;
85
86 /* if none-regulable, then only 0 or 0xff can be written */
87 if (!kfan->regulable && val > 0)
88 val = 0xff;
89
90 iowrite8(val, kfan->base + KFAN_CONTROL_REG);
91
92 return 0;
93 }
94
kfan_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)95 static int kfan_write(struct device *dev, enum hwmon_sensor_types type,
96 u32 attr, int channel, long val)
97 {
98 struct kfan *kfan = dev_get_drvdata(dev);
99
100 switch (type) {
101 case hwmon_pwm:
102 switch (attr) {
103 case hwmon_pwm_input:
104 return kfan_set_pwm(kfan, val);
105 default:
106 break;
107 }
108 break;
109 default:
110 break;
111 }
112
113 return -EOPNOTSUPP;
114 }
115
kfan_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)116 static int kfan_read(struct device *dev, enum hwmon_sensor_types type,
117 u32 attr, int channel, long *val)
118 {
119 struct kfan *kfan = dev_get_drvdata(dev);
120
121 switch (type) {
122 case hwmon_fan:
123 switch (attr) {
124 case hwmon_fan_fault:
125 *val = kfan_get_fault(kfan);
126 return 0;
127 case hwmon_fan_input:
128 *val = kfan_get_rpm(kfan);
129 return 0;
130 default:
131 break;
132 }
133 break;
134 case hwmon_pwm:
135 switch (attr) {
136 case hwmon_pwm_input:
137 *val = kfan_get_pwm(kfan);
138 return 0;
139 default:
140 break;
141 }
142 break;
143 default:
144 break;
145 }
146
147 return -EOPNOTSUPP;
148 }
149
kfan_is_visible(const void * data,enum hwmon_sensor_types type,u32 attr,int channel)150 static umode_t kfan_is_visible(const void *data, enum hwmon_sensor_types type,
151 u32 attr, int channel)
152 {
153 switch (type) {
154 case hwmon_fan:
155 switch (attr) {
156 case hwmon_fan_input:
157 return 0444;
158 case hwmon_fan_fault:
159 return 0444;
160 default:
161 break;
162 }
163 break;
164 case hwmon_pwm:
165 switch (attr) {
166 case hwmon_pwm_input:
167 return 0644;
168 default:
169 break;
170 }
171 break;
172 default:
173 break;
174 }
175
176 return 0;
177 }
178
179 static const struct hwmon_ops kfan_hwmon_ops = {
180 .is_visible = kfan_is_visible,
181 .read = kfan_read,
182 .write = kfan_write,
183 };
184
kfan_probe(struct auxiliary_device * auxdev,const struct auxiliary_device_id * id)185 static int kfan_probe(struct auxiliary_device *auxdev,
186 const struct auxiliary_device_id *id)
187 {
188 struct keba_fan_auxdev *kfan_auxdev =
189 container_of(auxdev, struct keba_fan_auxdev, auxdev);
190 struct device *dev = &auxdev->dev;
191 struct device *hwmon_dev;
192 struct kfan *kfan;
193 u8 status;
194
195 kfan = devm_kzalloc(dev, sizeof(*kfan), GFP_KERNEL);
196 if (!kfan)
197 return -ENOMEM;
198
199 kfan->base = devm_ioremap_resource(dev, &kfan_auxdev->io);
200 if (IS_ERR(kfan->base))
201 return PTR_ERR(kfan->base);
202
203 status = ioread8(kfan->base + KFAN_STATUS_REG);
204 if (status & KFAN_STATUS_REGULABLE)
205 kfan->regulable = true;
206 if (status & KFAN_STATUS_TACHO)
207 kfan->tacho = true;
208
209 /* fan */
210 kfan->fan_channel_config[0] = HWMON_F_FAULT;
211 if (kfan->tacho)
212 kfan->fan_channel_config[0] |= HWMON_F_INPUT;
213 kfan->fan_info.type = hwmon_fan;
214 kfan->fan_info.config = kfan->fan_channel_config;
215 kfan->info[0] = &kfan->fan_info;
216
217 /* PWM */
218 kfan->pwm_channel_config[0] = HWMON_PWM_INPUT;
219 kfan->pwm_info.type = hwmon_pwm;
220 kfan->pwm_info.config = kfan->pwm_channel_config;
221 kfan->info[1] = &kfan->pwm_info;
222
223 kfan->chip.ops = &kfan_hwmon_ops;
224 kfan->chip.info = kfan->info;
225 hwmon_dev = devm_hwmon_device_register_with_info(dev, KFAN, kfan,
226 &kfan->chip, NULL);
227 return PTR_ERR_OR_ZERO(hwmon_dev);
228 }
229
230 static const struct auxiliary_device_id kfan_devtype_aux[] = {
231 { .name = "keba.fan" },
232 {}
233 };
234 MODULE_DEVICE_TABLE(auxiliary, kfan_devtype_aux);
235
236 static struct auxiliary_driver kfan_driver_aux = {
237 .name = KFAN,
238 .id_table = kfan_devtype_aux,
239 .probe = kfan_probe,
240 };
241 module_auxiliary_driver(kfan_driver_aux);
242
243 MODULE_AUTHOR("Petar Bojanic <boja@keba.com>");
244 MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
245 MODULE_DESCRIPTION("KEBA fan controller driver");
246 MODULE_LICENSE("GPL");
247