.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * hwmon.c - part of lm_sensors, Linux kernel modules for hardware monitoring |
---|
3 | 4 | * |
---|
4 | 5 | * This file defines the sysfs class "hwmon", for use by sensors drivers. |
---|
5 | 6 | * |
---|
6 | 7 | * Copyright (C) 2005 Mark M. Hoffman <mhoffman@lightlink.com> |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or modify |
---|
9 | | - * it under the terms of the GNU General Public License as published by |
---|
10 | | - * the Free Software Foundation; version 2 of the License. |
---|
11 | 8 | */ |
---|
12 | 9 | |
---|
13 | 10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
.. | .. |
---|
18 | 15 | #include <linux/gfp.h> |
---|
19 | 16 | #include <linux/hwmon.h> |
---|
20 | 17 | #include <linux/idr.h> |
---|
| 18 | +#include <linux/list.h> |
---|
21 | 19 | #include <linux/module.h> |
---|
22 | 20 | #include <linux/pci.h> |
---|
23 | 21 | #include <linux/slab.h> |
---|
24 | 22 | #include <linux/string.h> |
---|
25 | 23 | #include <linux/thermal.h> |
---|
| 24 | + |
---|
| 25 | +#define CREATE_TRACE_POINTS |
---|
| 26 | +#include <trace/events/hwmon.h> |
---|
26 | 27 | |
---|
27 | 28 | #define HWMON_ID_PREFIX "hwmon" |
---|
28 | 29 | #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" |
---|
.. | .. |
---|
31 | 32 | const char *name; |
---|
32 | 33 | struct device dev; |
---|
33 | 34 | const struct hwmon_chip_info *chip; |
---|
34 | | - |
---|
| 35 | + struct list_head tzdata; |
---|
35 | 36 | struct attribute_group group; |
---|
36 | 37 | const struct attribute_group **groups; |
---|
37 | 38 | }; |
---|
.. | .. |
---|
55 | 56 | |
---|
56 | 57 | /* |
---|
57 | 58 | * Thermal zone information |
---|
58 | | - * In addition to the reference to the hwmon device, |
---|
59 | | - * also provides the sensor index. |
---|
60 | 59 | */ |
---|
61 | 60 | struct hwmon_thermal_data { |
---|
| 61 | + struct list_head node; /* hwmon tzdata list entry */ |
---|
62 | 62 | struct device *dev; /* Reference to hwmon device */ |
---|
63 | 63 | int index; /* sensor index */ |
---|
| 64 | + struct thermal_zone_device *tzd;/* thermal zone device */ |
---|
64 | 65 | }; |
---|
65 | 66 | |
---|
66 | 67 | static ssize_t |
---|
.. | .. |
---|
134 | 135 | * The complex conditional is necessary to avoid a cyclic dependency |
---|
135 | 136 | * between hwmon and thermal_sys modules. |
---|
136 | 137 | */ |
---|
137 | | -#if IS_REACHABLE(CONFIG_THERMAL) && defined(CONFIG_THERMAL_OF) && \ |
---|
138 | | - (!defined(CONFIG_THERMAL_HWMON) || \ |
---|
139 | | - !(defined(MODULE) && IS_MODULE(CONFIG_THERMAL))) |
---|
| 138 | +#ifdef CONFIG_THERMAL_OF |
---|
140 | 139 | static int hwmon_thermal_get_temp(void *data, int *temp) |
---|
141 | 140 | { |
---|
142 | 141 | struct hwmon_thermal_data *tdata = data; |
---|
.. | .. |
---|
158 | 157 | .get_temp = hwmon_thermal_get_temp, |
---|
159 | 158 | }; |
---|
160 | 159 | |
---|
| 160 | +static void hwmon_thermal_remove_sensor(void *data) |
---|
| 161 | +{ |
---|
| 162 | + list_del(data); |
---|
| 163 | +} |
---|
| 164 | + |
---|
161 | 165 | static int hwmon_thermal_add_sensor(struct device *dev, int index) |
---|
162 | 166 | { |
---|
| 167 | + struct hwmon_device *hwdev = to_hwmon_device(dev); |
---|
163 | 168 | struct hwmon_thermal_data *tdata; |
---|
164 | 169 | struct thermal_zone_device *tzd; |
---|
| 170 | + int err; |
---|
165 | 171 | |
---|
166 | 172 | tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL); |
---|
167 | 173 | if (!tdata) |
---|
.. | .. |
---|
172 | 178 | |
---|
173 | 179 | tzd = devm_thermal_zone_of_sensor_register(dev, index, tdata, |
---|
174 | 180 | &hwmon_thermal_ops); |
---|
175 | | - /* |
---|
176 | | - * If CONFIG_THERMAL_OF is disabled, this returns -ENODEV, |
---|
177 | | - * so ignore that error but forward any other error. |
---|
178 | | - */ |
---|
179 | | - if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV)) |
---|
180 | | - return PTR_ERR(tzd); |
---|
| 181 | + if (IS_ERR(tzd)) { |
---|
| 182 | + if (PTR_ERR(tzd) != -ENODEV) |
---|
| 183 | + return PTR_ERR(tzd); |
---|
| 184 | + dev_info(dev, "temp%d_input not attached to any thermal zone\n", |
---|
| 185 | + index + 1); |
---|
| 186 | + devm_kfree(dev, tdata); |
---|
| 187 | + return 0; |
---|
| 188 | + } |
---|
| 189 | + |
---|
| 190 | + err = devm_add_action(dev, hwmon_thermal_remove_sensor, &tdata->node); |
---|
| 191 | + if (err) |
---|
| 192 | + return err; |
---|
| 193 | + |
---|
| 194 | + tdata->tzd = tzd; |
---|
| 195 | + list_add(&tdata->node, &hwdev->tzdata); |
---|
181 | 196 | |
---|
182 | 197 | return 0; |
---|
183 | 198 | } |
---|
| 199 | + |
---|
| 200 | +static int hwmon_thermal_register_sensors(struct device *dev) |
---|
| 201 | +{ |
---|
| 202 | + struct hwmon_device *hwdev = to_hwmon_device(dev); |
---|
| 203 | + const struct hwmon_chip_info *chip = hwdev->chip; |
---|
| 204 | + const struct hwmon_channel_info **info = chip->info; |
---|
| 205 | + void *drvdata = dev_get_drvdata(dev); |
---|
| 206 | + int i; |
---|
| 207 | + |
---|
| 208 | + for (i = 1; info[i]; i++) { |
---|
| 209 | + int j; |
---|
| 210 | + |
---|
| 211 | + if (info[i]->type != hwmon_temp) |
---|
| 212 | + continue; |
---|
| 213 | + |
---|
| 214 | + for (j = 0; info[i]->config[j]; j++) { |
---|
| 215 | + int err; |
---|
| 216 | + |
---|
| 217 | + if (!(info[i]->config[j] & HWMON_T_INPUT) || |
---|
| 218 | + !chip->ops->is_visible(drvdata, hwmon_temp, |
---|
| 219 | + hwmon_temp_input, j)) |
---|
| 220 | + continue; |
---|
| 221 | + |
---|
| 222 | + err = hwmon_thermal_add_sensor(dev, j); |
---|
| 223 | + if (err) |
---|
| 224 | + return err; |
---|
| 225 | + } |
---|
| 226 | + } |
---|
| 227 | + |
---|
| 228 | + return 0; |
---|
| 229 | +} |
---|
| 230 | + |
---|
| 231 | +static void hwmon_thermal_notify(struct device *dev, int index) |
---|
| 232 | +{ |
---|
| 233 | + struct hwmon_device *hwdev = to_hwmon_device(dev); |
---|
| 234 | + struct hwmon_thermal_data *tzdata; |
---|
| 235 | + |
---|
| 236 | + list_for_each_entry(tzdata, &hwdev->tzdata, node) { |
---|
| 237 | + if (tzdata->index == index) { |
---|
| 238 | + thermal_zone_device_update(tzdata->tzd, |
---|
| 239 | + THERMAL_EVENT_UNSPECIFIED); |
---|
| 240 | + } |
---|
| 241 | + } |
---|
| 242 | +} |
---|
| 243 | + |
---|
184 | 244 | #else |
---|
185 | | -static int hwmon_thermal_add_sensor(struct device *dev, int index) |
---|
| 245 | +static int hwmon_thermal_register_sensors(struct device *dev) |
---|
186 | 246 | { |
---|
187 | 247 | return 0; |
---|
188 | 248 | } |
---|
| 249 | + |
---|
| 250 | +static void hwmon_thermal_notify(struct device *dev, int index) { } |
---|
| 251 | + |
---|
189 | 252 | #endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */ |
---|
| 253 | + |
---|
| 254 | +static int hwmon_attr_base(enum hwmon_sensor_types type) |
---|
| 255 | +{ |
---|
| 256 | + if (type == hwmon_in || type == hwmon_intrusion) |
---|
| 257 | + return 0; |
---|
| 258 | + return 1; |
---|
| 259 | +} |
---|
190 | 260 | |
---|
191 | 261 | /* sysfs attribute management */ |
---|
192 | 262 | |
---|
.. | .. |
---|
202 | 272 | if (ret < 0) |
---|
203 | 273 | return ret; |
---|
204 | 274 | |
---|
| 275 | + trace_hwmon_attr_show(hattr->index + hwmon_attr_base(hattr->type), |
---|
| 276 | + hattr->name, val); |
---|
| 277 | + |
---|
205 | 278 | return sprintf(buf, "%ld\n", val); |
---|
206 | 279 | } |
---|
207 | 280 | |
---|
.. | .. |
---|
210 | 283 | char *buf) |
---|
211 | 284 | { |
---|
212 | 285 | struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); |
---|
| 286 | + enum hwmon_sensor_types type = hattr->type; |
---|
213 | 287 | const char *s; |
---|
214 | 288 | int ret; |
---|
215 | 289 | |
---|
.. | .. |
---|
217 | 291 | hattr->index, &s); |
---|
218 | 292 | if (ret < 0) |
---|
219 | 293 | return ret; |
---|
| 294 | + |
---|
| 295 | + trace_hwmon_attr_show_string(hattr->index + hwmon_attr_base(type), |
---|
| 296 | + hattr->name, s); |
---|
220 | 297 | |
---|
221 | 298 | return sprintf(buf, "%s\n", s); |
---|
222 | 299 | } |
---|
.. | .. |
---|
238 | 315 | if (ret < 0) |
---|
239 | 316 | return ret; |
---|
240 | 317 | |
---|
241 | | - return count; |
---|
242 | | -} |
---|
| 318 | + trace_hwmon_attr_store(hattr->index + hwmon_attr_base(hattr->type), |
---|
| 319 | + hattr->name, val); |
---|
243 | 320 | |
---|
244 | | -static int hwmon_attr_base(enum hwmon_sensor_types type) |
---|
245 | | -{ |
---|
246 | | - if (type == hwmon_in) |
---|
247 | | - return 0; |
---|
248 | | - return 1; |
---|
| 321 | + return count; |
---|
249 | 322 | } |
---|
250 | 323 | |
---|
251 | 324 | static bool is_string_attr(enum hwmon_sensor_types type, u32 attr) |
---|
.. | .. |
---|
270 | 343 | struct device_attribute *dattr; |
---|
271 | 344 | struct attribute *a; |
---|
272 | 345 | umode_t mode; |
---|
273 | | - char *name; |
---|
| 346 | + const char *name; |
---|
274 | 347 | bool is_string = is_string_attr(type, attr); |
---|
275 | 348 | |
---|
276 | 349 | /* The attribute is invisible if there is no template string */ |
---|
.. | .. |
---|
281 | 354 | if (!mode) |
---|
282 | 355 | return ERR_PTR(-ENOENT); |
---|
283 | 356 | |
---|
284 | | - if ((mode & S_IRUGO) && ((is_string && !ops->read_string) || |
---|
| 357 | + if ((mode & 0444) && ((is_string && !ops->read_string) || |
---|
285 | 358 | (!is_string && !ops->read))) |
---|
286 | 359 | return ERR_PTR(-EINVAL); |
---|
287 | | - if ((mode & S_IWUGO) && !ops->write) |
---|
| 360 | + if ((mode & 0222) && !ops->write) |
---|
288 | 361 | return ERR_PTR(-EINVAL); |
---|
289 | 362 | |
---|
290 | 363 | hattr = kzalloc(sizeof(*hattr), GFP_KERNEL); |
---|
.. | .. |
---|
292 | 365 | return ERR_PTR(-ENOMEM); |
---|
293 | 366 | |
---|
294 | 367 | if (type == hwmon_chip) { |
---|
295 | | - name = (char *)template; |
---|
| 368 | + name = template; |
---|
296 | 369 | } else { |
---|
297 | 370 | scnprintf(hattr->name, sizeof(hattr->name), template, |
---|
298 | 371 | index + hwmon_attr_base(type)); |
---|
.. | .. |
---|
327 | 400 | [hwmon_chip_power_reset_history] = "power_reset_history", |
---|
328 | 401 | [hwmon_chip_update_interval] = "update_interval", |
---|
329 | 402 | [hwmon_chip_alarms] = "alarms", |
---|
| 403 | + [hwmon_chip_samples] = "samples", |
---|
| 404 | + [hwmon_chip_curr_samples] = "curr_samples", |
---|
| 405 | + [hwmon_chip_in_samples] = "in_samples", |
---|
| 406 | + [hwmon_chip_power_samples] = "power_samples", |
---|
| 407 | + [hwmon_chip_temp_samples] = "temp_samples", |
---|
330 | 408 | }; |
---|
331 | 409 | |
---|
332 | 410 | static const char * const hwmon_temp_attr_templates[] = { |
---|
| 411 | + [hwmon_temp_enable] = "temp%d_enable", |
---|
333 | 412 | [hwmon_temp_input] = "temp%d_input", |
---|
334 | 413 | [hwmon_temp_type] = "temp%d_type", |
---|
335 | 414 | [hwmon_temp_lcrit] = "temp%d_lcrit", |
---|
.. | .. |
---|
354 | 433 | [hwmon_temp_lowest] = "temp%d_lowest", |
---|
355 | 434 | [hwmon_temp_highest] = "temp%d_highest", |
---|
356 | 435 | [hwmon_temp_reset_history] = "temp%d_reset_history", |
---|
| 436 | + [hwmon_temp_rated_min] = "temp%d_rated_min", |
---|
| 437 | + [hwmon_temp_rated_max] = "temp%d_rated_max", |
---|
357 | 438 | }; |
---|
358 | 439 | |
---|
359 | 440 | static const char * const hwmon_in_attr_templates[] = { |
---|
| 441 | + [hwmon_in_enable] = "in%d_enable", |
---|
360 | 442 | [hwmon_in_input] = "in%d_input", |
---|
361 | 443 | [hwmon_in_min] = "in%d_min", |
---|
362 | 444 | [hwmon_in_max] = "in%d_max", |
---|
.. | .. |
---|
372 | 454 | [hwmon_in_max_alarm] = "in%d_max_alarm", |
---|
373 | 455 | [hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm", |
---|
374 | 456 | [hwmon_in_crit_alarm] = "in%d_crit_alarm", |
---|
| 457 | + [hwmon_in_rated_min] = "in%d_rated_min", |
---|
| 458 | + [hwmon_in_rated_max] = "in%d_rated_max", |
---|
375 | 459 | }; |
---|
376 | 460 | |
---|
377 | 461 | static const char * const hwmon_curr_attr_templates[] = { |
---|
| 462 | + [hwmon_curr_enable] = "curr%d_enable", |
---|
378 | 463 | [hwmon_curr_input] = "curr%d_input", |
---|
379 | 464 | [hwmon_curr_min] = "curr%d_min", |
---|
380 | 465 | [hwmon_curr_max] = "curr%d_max", |
---|
.. | .. |
---|
390 | 475 | [hwmon_curr_max_alarm] = "curr%d_max_alarm", |
---|
391 | 476 | [hwmon_curr_lcrit_alarm] = "curr%d_lcrit_alarm", |
---|
392 | 477 | [hwmon_curr_crit_alarm] = "curr%d_crit_alarm", |
---|
| 478 | + [hwmon_curr_rated_min] = "curr%d_rated_min", |
---|
| 479 | + [hwmon_curr_rated_max] = "curr%d_rated_max", |
---|
393 | 480 | }; |
---|
394 | 481 | |
---|
395 | 482 | static const char * const hwmon_power_attr_templates[] = { |
---|
| 483 | + [hwmon_power_enable] = "power%d_enable", |
---|
396 | 484 | [hwmon_power_average] = "power%d_average", |
---|
397 | 485 | [hwmon_power_average_interval] = "power%d_average_interval", |
---|
398 | 486 | [hwmon_power_average_interval_max] = "power%d_interval_max", |
---|
.. | .. |
---|
421 | 509 | [hwmon_power_max_alarm] = "power%d_max_alarm", |
---|
422 | 510 | [hwmon_power_lcrit_alarm] = "power%d_lcrit_alarm", |
---|
423 | 511 | [hwmon_power_crit_alarm] = "power%d_crit_alarm", |
---|
| 512 | + [hwmon_power_rated_min] = "power%d_rated_min", |
---|
| 513 | + [hwmon_power_rated_max] = "power%d_rated_max", |
---|
424 | 514 | }; |
---|
425 | 515 | |
---|
426 | 516 | static const char * const hwmon_energy_attr_templates[] = { |
---|
| 517 | + [hwmon_energy_enable] = "energy%d_enable", |
---|
427 | 518 | [hwmon_energy_input] = "energy%d_input", |
---|
428 | 519 | [hwmon_energy_label] = "energy%d_label", |
---|
429 | 520 | }; |
---|
430 | 521 | |
---|
431 | 522 | static const char * const hwmon_humidity_attr_templates[] = { |
---|
| 523 | + [hwmon_humidity_enable] = "humidity%d_enable", |
---|
432 | 524 | [hwmon_humidity_input] = "humidity%d_input", |
---|
433 | 525 | [hwmon_humidity_label] = "humidity%d_label", |
---|
434 | 526 | [hwmon_humidity_min] = "humidity%d_min", |
---|
.. | .. |
---|
437 | 529 | [hwmon_humidity_max_hyst] = "humidity%d_max_hyst", |
---|
438 | 530 | [hwmon_humidity_alarm] = "humidity%d_alarm", |
---|
439 | 531 | [hwmon_humidity_fault] = "humidity%d_fault", |
---|
| 532 | + [hwmon_humidity_rated_min] = "humidity%d_rated_min", |
---|
| 533 | + [hwmon_humidity_rated_max] = "humidity%d_rated_max", |
---|
440 | 534 | }; |
---|
441 | 535 | |
---|
442 | 536 | static const char * const hwmon_fan_attr_templates[] = { |
---|
| 537 | + [hwmon_fan_enable] = "fan%d_enable", |
---|
443 | 538 | [hwmon_fan_input] = "fan%d_input", |
---|
444 | 539 | [hwmon_fan_label] = "fan%d_label", |
---|
445 | 540 | [hwmon_fan_min] = "fan%d_min", |
---|
.. | .. |
---|
460 | 555 | [hwmon_pwm_freq] = "pwm%d_freq", |
---|
461 | 556 | }; |
---|
462 | 557 | |
---|
| 558 | +static const char * const hwmon_intrusion_attr_templates[] = { |
---|
| 559 | + [hwmon_intrusion_alarm] = "intrusion%d_alarm", |
---|
| 560 | + [hwmon_intrusion_beep] = "intrusion%d_beep", |
---|
| 561 | +}; |
---|
| 562 | + |
---|
463 | 563 | static const char * const *__templates[] = { |
---|
464 | 564 | [hwmon_chip] = hwmon_chip_attrs, |
---|
465 | 565 | [hwmon_temp] = hwmon_temp_attr_templates, |
---|
.. | .. |
---|
470 | 570 | [hwmon_humidity] = hwmon_humidity_attr_templates, |
---|
471 | 571 | [hwmon_fan] = hwmon_fan_attr_templates, |
---|
472 | 572 | [hwmon_pwm] = hwmon_pwm_attr_templates, |
---|
| 573 | + [hwmon_intrusion] = hwmon_intrusion_attr_templates, |
---|
473 | 574 | }; |
---|
474 | 575 | |
---|
475 | 576 | static const int __templates_size[] = { |
---|
.. | .. |
---|
482 | 583 | [hwmon_humidity] = ARRAY_SIZE(hwmon_humidity_attr_templates), |
---|
483 | 584 | [hwmon_fan] = ARRAY_SIZE(hwmon_fan_attr_templates), |
---|
484 | 585 | [hwmon_pwm] = ARRAY_SIZE(hwmon_pwm_attr_templates), |
---|
| 586 | + [hwmon_intrusion] = ARRAY_SIZE(hwmon_intrusion_attr_templates), |
---|
485 | 587 | }; |
---|
| 588 | + |
---|
| 589 | +int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, |
---|
| 590 | + u32 attr, int channel) |
---|
| 591 | +{ |
---|
| 592 | + char sattr[MAX_SYSFS_ATTR_NAME_LENGTH]; |
---|
| 593 | + const char * const *templates; |
---|
| 594 | + const char *template; |
---|
| 595 | + int base; |
---|
| 596 | + |
---|
| 597 | + if (type >= ARRAY_SIZE(__templates)) |
---|
| 598 | + return -EINVAL; |
---|
| 599 | + if (attr >= __templates_size[type]) |
---|
| 600 | + return -EINVAL; |
---|
| 601 | + |
---|
| 602 | + templates = __templates[type]; |
---|
| 603 | + template = templates[attr]; |
---|
| 604 | + |
---|
| 605 | + base = hwmon_attr_base(type); |
---|
| 606 | + |
---|
| 607 | + scnprintf(sattr, MAX_SYSFS_ATTR_NAME_LENGTH, template, base + channel); |
---|
| 608 | + sysfs_notify(&dev->kobj, NULL, sattr); |
---|
| 609 | + kobject_uevent(&dev->kobj, KOBJ_CHANGE); |
---|
| 610 | + |
---|
| 611 | + if (type == hwmon_temp) |
---|
| 612 | + hwmon_thermal_notify(dev, channel); |
---|
| 613 | + |
---|
| 614 | + return 0; |
---|
| 615 | +} |
---|
| 616 | +EXPORT_SYMBOL_GPL(hwmon_notify_event); |
---|
486 | 617 | |
---|
487 | 618 | static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) |
---|
488 | 619 | { |
---|
.. | .. |
---|
569 | 700 | { |
---|
570 | 701 | struct hwmon_device *hwdev; |
---|
571 | 702 | struct device *hdev; |
---|
572 | | - int i, j, err, id; |
---|
| 703 | + struct device *tdev = dev; |
---|
| 704 | + int i, err, id; |
---|
573 | 705 | |
---|
574 | 706 | /* Complain about invalid characters in hwmon name attribute */ |
---|
575 | 707 | if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) |
---|
.. | .. |
---|
626 | 758 | hwdev->name = name; |
---|
627 | 759 | hdev->class = &hwmon_class; |
---|
628 | 760 | hdev->parent = dev; |
---|
629 | | - hdev->of_node = dev ? dev->of_node : NULL; |
---|
| 761 | + while (tdev && !tdev->of_node) |
---|
| 762 | + tdev = tdev->parent; |
---|
| 763 | + hdev->of_node = tdev ? tdev->of_node : NULL; |
---|
630 | 764 | hwdev->chip = chip; |
---|
631 | 765 | dev_set_drvdata(hdev, drvdata); |
---|
632 | 766 | dev_set_name(hdev, HWMON_ID_FORMAT, id); |
---|
.. | .. |
---|
636 | 770 | goto ida_remove; |
---|
637 | 771 | } |
---|
638 | 772 | |
---|
639 | | - if (dev && dev->of_node && chip && chip->ops->read && |
---|
| 773 | + INIT_LIST_HEAD(&hwdev->tzdata); |
---|
| 774 | + |
---|
| 775 | + if (hdev->of_node && chip && chip->ops->read && |
---|
640 | 776 | chip->info[0]->type == hwmon_chip && |
---|
641 | 777 | (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) { |
---|
642 | | - const struct hwmon_channel_info **info = chip->info; |
---|
643 | | - |
---|
644 | | - for (i = 1; info[i]; i++) { |
---|
645 | | - if (info[i]->type != hwmon_temp) |
---|
646 | | - continue; |
---|
647 | | - |
---|
648 | | - for (j = 0; info[i]->config[j]; j++) { |
---|
649 | | - if (!chip->ops->is_visible(drvdata, hwmon_temp, |
---|
650 | | - hwmon_temp_input, j)) |
---|
651 | | - continue; |
---|
652 | | - if (info[i]->config[j] & HWMON_T_INPUT) { |
---|
653 | | - err = hwmon_thermal_add_sensor(hdev, j); |
---|
654 | | - if (err) { |
---|
655 | | - device_unregister(hdev); |
---|
656 | | - goto ida_remove; |
---|
657 | | - } |
---|
658 | | - } |
---|
659 | | - } |
---|
| 778 | + err = hwmon_thermal_register_sensors(hdev); |
---|
| 779 | + if (err) { |
---|
| 780 | + device_unregister(hdev); |
---|
| 781 | + /* |
---|
| 782 | + * Don't worry about hwdev; hwmon_dev_release(), called |
---|
| 783 | + * from device_unregister(), will free it. |
---|
| 784 | + */ |
---|
| 785 | + goto ida_remove; |
---|
660 | 786 | } |
---|
661 | 787 | } |
---|
662 | 788 | |
---|