1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Driver for Gateworks System Controller Hardware Monitor module |
4 | * |
5 | * Copyright (C) 2020 Gateworks Corporation |
6 | */ |
7 | #include <linux/hwmon.h> |
8 | #include <linux/hwmon-sysfs.h> |
9 | #include <linux/mfd/gsc.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/slab.h> |
15 | |
16 | #include <linux/platform_data/gsc_hwmon.h> |
17 | |
18 | #define GSC_HWMON_MAX_TEMP_CH 16 |
19 | #define GSC_HWMON_MAX_IN_CH 16 |
20 | #define GSC_HWMON_MAX_FAN_CH 16 |
21 | |
22 | #define GSC_HWMON_RESOLUTION 12 |
23 | #define GSC_HWMON_VREF 2500 |
24 | |
25 | struct gsc_hwmon_data { |
26 | struct gsc_dev *gsc; |
27 | struct gsc_hwmon_platform_data *pdata; |
28 | struct regmap *regmap; |
29 | const struct gsc_hwmon_channel *temp_ch[GSC_HWMON_MAX_TEMP_CH]; |
30 | const struct gsc_hwmon_channel *in_ch[GSC_HWMON_MAX_IN_CH]; |
31 | const struct gsc_hwmon_channel *fan_ch[GSC_HWMON_MAX_FAN_CH]; |
32 | u32 temp_config[GSC_HWMON_MAX_TEMP_CH + 1]; |
33 | u32 in_config[GSC_HWMON_MAX_IN_CH + 1]; |
34 | u32 fan_config[GSC_HWMON_MAX_FAN_CH + 1]; |
35 | struct hwmon_channel_info temp_info; |
36 | struct hwmon_channel_info in_info; |
37 | struct hwmon_channel_info fan_info; |
38 | const struct hwmon_channel_info *info[4]; |
39 | struct hwmon_chip_info chip; |
40 | }; |
41 | |
42 | static struct regmap_bus gsc_hwmon_regmap_bus = { |
43 | .reg_read = gsc_read, |
44 | .reg_write = gsc_write, |
45 | }; |
46 | |
47 | static const struct regmap_config gsc_hwmon_regmap_config = { |
48 | .reg_bits = 8, |
49 | .val_bits = 8, |
50 | .cache_type = REGCACHE_NONE, |
51 | }; |
52 | |
53 | static ssize_t pwm_auto_point_temp_show(struct device *dev, |
54 | struct device_attribute *devattr, |
55 | char *buf) |
56 | { |
57 | struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); |
58 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |
59 | u8 reg = hwmon->pdata->fan_base + (2 * attr->index); |
60 | u8 regs[2]; |
61 | int ret; |
62 | |
63 | ret = regmap_bulk_read(map: hwmon->regmap, reg, val: regs, val_count: 2); |
64 | if (ret) |
65 | return ret; |
66 | |
67 | ret = regs[0] | regs[1] << 8; |
68 | return sprintf(buf, fmt: "%d\n" , ret * 10); |
69 | } |
70 | |
71 | static ssize_t pwm_auto_point_temp_store(struct device *dev, |
72 | struct device_attribute *devattr, |
73 | const char *buf, size_t count) |
74 | { |
75 | struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); |
76 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |
77 | u8 reg = hwmon->pdata->fan_base + (2 * attr->index); |
78 | u8 regs[2]; |
79 | long temp; |
80 | int err; |
81 | |
82 | if (kstrtol(s: buf, base: 10, res: &temp)) |
83 | return -EINVAL; |
84 | |
85 | temp = clamp_val(temp, 0, 100000); |
86 | temp = DIV_ROUND_CLOSEST(temp, 100); |
87 | |
88 | regs[0] = temp & 0xff; |
89 | regs[1] = (temp >> 8) & 0xff; |
90 | err = regmap_bulk_write(map: hwmon->regmap, reg, val: regs, val_count: 2); |
91 | if (err) |
92 | return err; |
93 | |
94 | return count; |
95 | } |
96 | |
97 | static ssize_t pwm_auto_point_pwm_show(struct device *dev, |
98 | struct device_attribute *devattr, |
99 | char *buf) |
100 | { |
101 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |
102 | |
103 | return sprintf(buf, fmt: "%d\n" , 255 * (50 + (attr->index * 10))); |
104 | } |
105 | |
106 | static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point1_pwm, pwm_auto_point_pwm, 0); |
107 | static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, pwm_auto_point_temp, 0); |
108 | |
109 | static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point2_pwm, pwm_auto_point_pwm, 1); |
110 | static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp, pwm_auto_point_temp, 1); |
111 | |
112 | static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm_auto_point_pwm, 2); |
113 | static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp, pwm_auto_point_temp, 2); |
114 | |
115 | static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point4_pwm, pwm_auto_point_pwm, 3); |
116 | static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp, pwm_auto_point_temp, 3); |
117 | |
118 | static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point5_pwm, pwm_auto_point_pwm, 4); |
119 | static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point5_temp, pwm_auto_point_temp, 4); |
120 | |
121 | static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point6_pwm, pwm_auto_point_pwm, 5); |
122 | static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point6_temp, pwm_auto_point_temp, 5); |
123 | |
124 | static struct attribute *gsc_hwmon_attributes[] = { |
125 | &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, |
126 | &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, |
127 | &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, |
128 | &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, |
129 | &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, |
130 | &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, |
131 | &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, |
132 | &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, |
133 | &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, |
134 | &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, |
135 | &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr, |
136 | &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr, |
137 | NULL |
138 | }; |
139 | |
140 | static const struct attribute_group gsc_hwmon_group = { |
141 | .attrs = gsc_hwmon_attributes, |
142 | }; |
143 | __ATTRIBUTE_GROUPS(gsc_hwmon); |
144 | |
145 | static int |
146 | gsc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
147 | int channel, long *val) |
148 | { |
149 | struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); |
150 | const struct gsc_hwmon_channel *ch; |
151 | int sz, ret; |
152 | long tmp; |
153 | u8 buf[3]; |
154 | |
155 | switch (type) { |
156 | case hwmon_in: |
157 | ch = hwmon->in_ch[channel]; |
158 | break; |
159 | case hwmon_temp: |
160 | ch = hwmon->temp_ch[channel]; |
161 | break; |
162 | case hwmon_fan: |
163 | ch = hwmon->fan_ch[channel]; |
164 | break; |
165 | default: |
166 | return -EOPNOTSUPP; |
167 | } |
168 | |
169 | sz = (ch->mode == mode_voltage_24bit) ? 3 : 2; |
170 | ret = regmap_bulk_read(map: hwmon->regmap, reg: ch->reg, val: buf, val_count: sz); |
171 | if (ret) |
172 | return ret; |
173 | |
174 | tmp = 0; |
175 | while (sz-- > 0) |
176 | tmp |= (buf[sz] << (8 * sz)); |
177 | |
178 | switch (ch->mode) { |
179 | case mode_temperature: |
180 | if (tmp > 0x8000) |
181 | tmp -= 0xffff; |
182 | tmp *= 100; /* convert to millidegrees celsius */ |
183 | break; |
184 | case mode_voltage_raw: |
185 | tmp = clamp_val(tmp, 0, BIT(GSC_HWMON_RESOLUTION)); |
186 | /* scale based on ref voltage and ADC resolution */ |
187 | tmp *= GSC_HWMON_VREF; |
188 | tmp >>= GSC_HWMON_RESOLUTION; |
189 | /* scale based on optional voltage divider */ |
190 | if (ch->vdiv[0] && ch->vdiv[1]) { |
191 | tmp *= (ch->vdiv[0] + ch->vdiv[1]); |
192 | tmp /= ch->vdiv[1]; |
193 | } |
194 | /* adjust by uV offset */ |
195 | tmp += ch->mvoffset; |
196 | break; |
197 | case mode_fan: |
198 | tmp *= 30; /* convert to revolutions per minute */ |
199 | break; |
200 | case mode_voltage_24bit: |
201 | case mode_voltage_16bit: |
202 | /* no adjustment needed */ |
203 | break; |
204 | } |
205 | |
206 | *val = tmp; |
207 | |
208 | return 0; |
209 | } |
210 | |
211 | static int |
212 | gsc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, |
213 | u32 attr, int channel, const char **buf) |
214 | { |
215 | struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev); |
216 | |
217 | switch (type) { |
218 | case hwmon_in: |
219 | *buf = hwmon->in_ch[channel]->name; |
220 | break; |
221 | case hwmon_temp: |
222 | *buf = hwmon->temp_ch[channel]->name; |
223 | break; |
224 | case hwmon_fan: |
225 | *buf = hwmon->fan_ch[channel]->name; |
226 | break; |
227 | default: |
228 | return -ENOTSUPP; |
229 | } |
230 | |
231 | return 0; |
232 | } |
233 | |
234 | static umode_t |
235 | gsc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr, |
236 | int ch) |
237 | { |
238 | return 0444; |
239 | } |
240 | |
241 | static const struct hwmon_ops gsc_hwmon_ops = { |
242 | .is_visible = gsc_hwmon_is_visible, |
243 | .read = gsc_hwmon_read, |
244 | .read_string = gsc_hwmon_read_string, |
245 | }; |
246 | |
247 | static struct gsc_hwmon_platform_data * |
248 | gsc_hwmon_get_devtree_pdata(struct device *dev) |
249 | { |
250 | struct gsc_hwmon_platform_data *pdata; |
251 | struct gsc_hwmon_channel *ch; |
252 | struct fwnode_handle *child; |
253 | struct device_node *fan; |
254 | int nchannels; |
255 | |
256 | nchannels = device_get_child_node_count(dev); |
257 | if (nchannels == 0) |
258 | return ERR_PTR(error: -ENODEV); |
259 | |
260 | pdata = devm_kzalloc(dev, struct_size(pdata, channels, nchannels), |
261 | GFP_KERNEL); |
262 | if (!pdata) |
263 | return ERR_PTR(error: -ENOMEM); |
264 | pdata->nchannels = nchannels; |
265 | |
266 | /* fan controller base address */ |
267 | of_node_get(node: dev->parent->of_node); |
268 | fan = of_find_compatible_node(from: dev->parent->of_node, NULL, compat: "gw,gsc-fan" ); |
269 | if (fan && of_property_read_u32(np: fan, propname: "reg" , out_value: &pdata->fan_base)) { |
270 | of_node_put(node: fan); |
271 | dev_err(dev, "fan node without base\n" ); |
272 | return ERR_PTR(error: -EINVAL); |
273 | } |
274 | |
275 | of_node_put(node: fan); |
276 | |
277 | ch = pdata->channels; |
278 | /* allocate structures for channels and count instances of each type */ |
279 | device_for_each_child_node(dev, child) { |
280 | if (fwnode_property_read_string(fwnode: child, propname: "label" , val: &ch->name)) { |
281 | dev_err(dev, "channel without label\n" ); |
282 | fwnode_handle_put(fwnode: child); |
283 | return ERR_PTR(error: -EINVAL); |
284 | } |
285 | if (fwnode_property_read_u32(fwnode: child, propname: "reg" , val: &ch->reg)) { |
286 | dev_err(dev, "channel without reg\n" ); |
287 | fwnode_handle_put(fwnode: child); |
288 | return ERR_PTR(error: -EINVAL); |
289 | } |
290 | if (fwnode_property_read_u32(fwnode: child, propname: "gw,mode" , val: &ch->mode)) { |
291 | dev_err(dev, "channel without mode\n" ); |
292 | fwnode_handle_put(fwnode: child); |
293 | return ERR_PTR(error: -EINVAL); |
294 | } |
295 | if (ch->mode > mode_max) { |
296 | dev_err(dev, "invalid channel mode\n" ); |
297 | fwnode_handle_put(fwnode: child); |
298 | return ERR_PTR(error: -EINVAL); |
299 | } |
300 | |
301 | if (!fwnode_property_read_u32(fwnode: child, |
302 | propname: "gw,voltage-offset-microvolt" , |
303 | val: &ch->mvoffset)) |
304 | ch->mvoffset /= 1000; |
305 | fwnode_property_read_u32_array(fwnode: child, |
306 | propname: "gw,voltage-divider-ohms" , |
307 | val: ch->vdiv, ARRAY_SIZE(ch->vdiv)); |
308 | ch++; |
309 | } |
310 | |
311 | return pdata; |
312 | } |
313 | |
314 | static int gsc_hwmon_probe(struct platform_device *pdev) |
315 | { |
316 | struct gsc_dev *gsc = dev_get_drvdata(dev: pdev->dev.parent); |
317 | struct device *dev = &pdev->dev; |
318 | struct device *hwmon_dev; |
319 | struct gsc_hwmon_platform_data *pdata = dev_get_platdata(dev); |
320 | struct gsc_hwmon_data *hwmon; |
321 | const struct attribute_group **groups; |
322 | int i, i_in, i_temp, i_fan; |
323 | |
324 | if (!pdata) { |
325 | pdata = gsc_hwmon_get_devtree_pdata(dev); |
326 | if (IS_ERR(ptr: pdata)) |
327 | return PTR_ERR(ptr: pdata); |
328 | } |
329 | |
330 | hwmon = devm_kzalloc(dev, size: sizeof(*hwmon), GFP_KERNEL); |
331 | if (!hwmon) |
332 | return -ENOMEM; |
333 | hwmon->gsc = gsc; |
334 | hwmon->pdata = pdata; |
335 | |
336 | hwmon->regmap = devm_regmap_init(dev, &gsc_hwmon_regmap_bus, |
337 | gsc->i2c_hwmon, |
338 | &gsc_hwmon_regmap_config); |
339 | if (IS_ERR(ptr: hwmon->regmap)) |
340 | return PTR_ERR(ptr: hwmon->regmap); |
341 | |
342 | for (i = 0, i_in = 0, i_temp = 0, i_fan = 0; i < hwmon->pdata->nchannels; i++) { |
343 | const struct gsc_hwmon_channel *ch = &pdata->channels[i]; |
344 | |
345 | switch (ch->mode) { |
346 | case mode_temperature: |
347 | if (i_temp == GSC_HWMON_MAX_TEMP_CH) { |
348 | dev_err(gsc->dev, "too many temp channels\n" ); |
349 | return -EINVAL; |
350 | } |
351 | hwmon->temp_ch[i_temp] = ch; |
352 | hwmon->temp_config[i_temp] = HWMON_T_INPUT | |
353 | HWMON_T_LABEL; |
354 | i_temp++; |
355 | break; |
356 | case mode_fan: |
357 | if (i_fan == GSC_HWMON_MAX_FAN_CH) { |
358 | dev_err(gsc->dev, "too many fan channels\n" ); |
359 | return -EINVAL; |
360 | } |
361 | hwmon->fan_ch[i_fan] = ch; |
362 | hwmon->fan_config[i_fan] = HWMON_F_INPUT | |
363 | HWMON_F_LABEL; |
364 | i_fan++; |
365 | break; |
366 | case mode_voltage_24bit: |
367 | case mode_voltage_16bit: |
368 | case mode_voltage_raw: |
369 | if (i_in == GSC_HWMON_MAX_IN_CH) { |
370 | dev_err(gsc->dev, "too many input channels\n" ); |
371 | return -EINVAL; |
372 | } |
373 | hwmon->in_ch[i_in] = ch; |
374 | hwmon->in_config[i_in] = |
375 | HWMON_I_INPUT | HWMON_I_LABEL; |
376 | i_in++; |
377 | break; |
378 | default: |
379 | dev_err(gsc->dev, "invalid mode: %d\n" , ch->mode); |
380 | return -EINVAL; |
381 | } |
382 | } |
383 | |
384 | /* setup config structures */ |
385 | hwmon->chip.ops = &gsc_hwmon_ops; |
386 | hwmon->chip.info = hwmon->info; |
387 | hwmon->info[0] = &hwmon->temp_info; |
388 | hwmon->info[1] = &hwmon->in_info; |
389 | hwmon->info[2] = &hwmon->fan_info; |
390 | hwmon->temp_info.type = hwmon_temp; |
391 | hwmon->temp_info.config = hwmon->temp_config; |
392 | hwmon->in_info.type = hwmon_in; |
393 | hwmon->in_info.config = hwmon->in_config; |
394 | hwmon->fan_info.type = hwmon_fan; |
395 | hwmon->fan_info.config = hwmon->fan_config; |
396 | |
397 | groups = pdata->fan_base ? gsc_hwmon_groups : NULL; |
398 | hwmon_dev = devm_hwmon_device_register_with_info(dev, |
399 | KBUILD_MODNAME, drvdata: hwmon, |
400 | info: &hwmon->chip, extra_groups: groups); |
401 | return PTR_ERR_OR_ZERO(ptr: hwmon_dev); |
402 | } |
403 | |
404 | static const struct of_device_id gsc_hwmon_of_match[] = { |
405 | { .compatible = "gw,gsc-adc" , }, |
406 | {} |
407 | }; |
408 | |
409 | static struct platform_driver gsc_hwmon_driver = { |
410 | .driver = { |
411 | .name = "gsc-hwmon" , |
412 | .of_match_table = gsc_hwmon_of_match, |
413 | }, |
414 | .probe = gsc_hwmon_probe, |
415 | }; |
416 | |
417 | module_platform_driver(gsc_hwmon_driver); |
418 | |
419 | MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>" ); |
420 | MODULE_DESCRIPTION("GSC hardware monitor driver" ); |
421 | MODULE_LICENSE("GPL v2" ); |
422 | |