| .. | .. |
|---|
| 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 | |
|---|