diff options
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 8 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/ibmpowernv.c | 529 |
3 files changed, 0 insertions, 538 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b13172cfbeef..bc196f49ec53 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -554,14 +554,6 @@ config SENSORS_IBMPEX | |||
554 | This driver can also be built as a module. If so, the module | 554 | This driver can also be built as a module. If so, the module |
555 | will be called ibmpex. | 555 | will be called ibmpex. |
556 | 556 | ||
557 | config SENSORS_IBMPOWERNV | ||
558 | tristate "IBM PowerNv Platform temperature/power/fan sensor" | ||
559 | depends on PPC_POWERNV | ||
560 | default y | ||
561 | help | ||
562 | If you say yes here you get support for the temperature/fan/power | ||
563 | sensors on your platform. | ||
564 | |||
565 | config SENSORS_IIO_HWMON | 557 | config SENSORS_IIO_HWMON |
566 | tristate "Hwmon driver that uses channels specified via iio maps" | 558 | tristate "Hwmon driver that uses channels specified via iio maps" |
567 | depends on IIO | 559 | depends on IIO |
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 199c401bf8d9..c48f9873ac73 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile | |||
@@ -71,7 +71,6 @@ obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o | |||
71 | obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o | 71 | obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o |
72 | obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o | 72 | obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o |
73 | obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o | 73 | obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o |
74 | obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o | ||
75 | obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o | 74 | obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o |
76 | obj-$(CONFIG_SENSORS_INA209) += ina209.o | 75 | obj-$(CONFIG_SENSORS_INA209) += ina209.o |
77 | obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o | 76 | obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o |
diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c deleted file mode 100644 index b7b1297a9b02..000000000000 --- a/drivers/hwmon/ibmpowernv.c +++ /dev/null | |||
@@ -1,529 +0,0 @@ | |||
1 | /* | ||
2 | * hwmon driver for temperature/power/fan on IBM PowerNV platform | ||
3 | * Copyright (C) 2013 IBM | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/init.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/hwmon.h> | ||
24 | #include <linux/hwmon-sysfs.h> | ||
25 | #include <linux/of.h> | ||
26 | #include <linux/slab.h> | ||
27 | |||
28 | #include <linux/jiffies.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | #include <asm/opal.h> | ||
31 | #include <linux/err.h> | ||
32 | |||
33 | MODULE_DESCRIPTION("IBM PowerNV Platform power/temp/fan sensor hwmon module"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | |||
36 | #define MAX_ATTR_LENGTH 32 | ||
37 | |||
38 | /* Device tree sensor name prefixes. The device tree has the names in the | ||
39 | * format "cooling-fan#2-faulted" where the "cooling-fan" is the sensor type, | ||
40 | * 2 is the sensor count, and "faulted" is the sensor data attribute type. | ||
41 | */ | ||
42 | #define DT_FAULT_ATTR_SUFFIX "faulted" | ||
43 | #define DT_DATA_ATTR_SUFFIX "data" | ||
44 | #define DT_THRESHOLD_ATTR_SUFFIX "thrs" | ||
45 | |||
46 | enum sensors { | ||
47 | FAN, | ||
48 | TEMPERATURE, | ||
49 | POWERSUPPLY, | ||
50 | POWER, | ||
51 | MAX_SENSOR_TYPE, | ||
52 | }; | ||
53 | |||
54 | enum attributes { | ||
55 | INPUT, | ||
56 | MINIMUM, | ||
57 | MAXIMUM, | ||
58 | FAULT, | ||
59 | MAX_ATTR_TYPES | ||
60 | }; | ||
61 | |||
62 | static struct sensor_name { | ||
63 | char *name; | ||
64 | char *compaible; | ||
65 | } sensor_names[] = { | ||
66 | {"fan-sensor", "ibm,opal-sensor-cooling-fan"}, | ||
67 | {"amb-temp-sensor", "ibm,opal-sensor-amb-temp"}, | ||
68 | {"power-sensor", "ibm,opal-sensor-power-supply"}, | ||
69 | {"power", "ibm,opal-sensor-power"} | ||
70 | }; | ||
71 | |||
72 | static const char * const attribute_type_table[] = { | ||
73 | "input", | ||
74 | "min", | ||
75 | "max", | ||
76 | "fault", | ||
77 | NULL | ||
78 | }; | ||
79 | |||
80 | struct pdev_entry { | ||
81 | struct list_head list; | ||
82 | struct platform_device *pdev; | ||
83 | enum sensors type; | ||
84 | }; | ||
85 | |||
86 | static LIST_HEAD(pdev_list); | ||
87 | |||
88 | /* The sensors are categorised on type. | ||
89 | * | ||
90 | * The sensors of same type are categorised under a common platform device. | ||
91 | * So, The pdev is shared by all sensors of same type. | ||
92 | * Ex : temp1_input, temp1_max, temp2_input,temp2_max all share same platform | ||
93 | * device. | ||
94 | * | ||
95 | * "sensor_data" is the Platform device specific data. | ||
96 | * There is one hwmon_device instance for all the sensors of same type. | ||
97 | * This also holds the list of all sensors with same type but different | ||
98 | * attribute and index. | ||
99 | */ | ||
100 | struct sensor_specific_data { | ||
101 | u32 sensor_id; /* The hex value as in the device tree */ | ||
102 | u32 sensor_index; /* The sensor instance index */ | ||
103 | struct sensor_device_attribute sd_attr; | ||
104 | enum attributes attr_type; | ||
105 | char attr_name[64]; | ||
106 | }; | ||
107 | |||
108 | struct sensor_data { | ||
109 | struct device *hwmon_dev; | ||
110 | struct list_head sensor_list; | ||
111 | struct device_attribute name_attr; | ||
112 | }; | ||
113 | |||
114 | struct sensor_entry { | ||
115 | struct list_head list; | ||
116 | struct sensor_specific_data *sensor_data; | ||
117 | }; | ||
118 | |||
119 | static struct platform_device *powernv_sensor_get_pdev(enum sensors type) | ||
120 | { | ||
121 | struct pdev_entry *p; | ||
122 | list_for_each_entry(p, &pdev_list, list) | ||
123 | if (p->type == type) | ||
124 | return p->pdev; | ||
125 | |||
126 | return NULL; | ||
127 | } | ||
128 | |||
129 | static struct sensor_specific_data *powernv_sensor_get_sensor_data( | ||
130 | struct sensor_data *pdata, | ||
131 | int index, enum attributes attr_type) | ||
132 | { | ||
133 | struct sensor_entry *p; | ||
134 | list_for_each_entry(p, &pdata->sensor_list, list) | ||
135 | if ((p->sensor_data->sensor_index == index) && | ||
136 | (attr_type == p->sensor_data->attr_type)) | ||
137 | return p->sensor_data; | ||
138 | |||
139 | return NULL; | ||
140 | } | ||
141 | |||
142 | static ssize_t show_name(struct device *dev, | ||
143 | struct device_attribute *devattr, char *buf) | ||
144 | { | ||
145 | struct platform_device *pdev = to_platform_device(dev); | ||
146 | |||
147 | return sprintf(buf, "%s\n", pdev->name); | ||
148 | } | ||
149 | |||
150 | /* Note: Data from the sensors for each sensor type needs to be converted to | ||
151 | * the dimension appropriate. | ||
152 | */ | ||
153 | static ssize_t show_sensor(struct device *dev, | ||
154 | struct device_attribute *devattr, char *buf) | ||
155 | { | ||
156 | struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(devattr); | ||
157 | struct platform_device *pdev = to_platform_device(dev); | ||
158 | struct sensor_data *pdata = platform_get_drvdata(pdev); | ||
159 | struct sensor_specific_data *tdata = NULL; | ||
160 | enum sensors sensor_type = pdev->id; | ||
161 | u32 x = -1; | ||
162 | int ret; | ||
163 | |||
164 | if (sd_attr && sd_attr->dev_attr.attr.name) { | ||
165 | char *pos = strchr(sd_attr->dev_attr.attr.name, '_'); | ||
166 | int i; | ||
167 | |||
168 | for (i = 0; i < MAX_ATTR_TYPES; i++) { | ||
169 | if (strcmp(pos+1, attribute_type_table[i]) == 0) { | ||
170 | tdata = powernv_sensor_get_sensor_data(pdata, | ||
171 | sd_attr->index, i); | ||
172 | break; | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | |||
177 | if (tdata) { | ||
178 | ret = opal_get_sensor_data(tdata->sensor_id, &x); | ||
179 | if (ret) | ||
180 | x = -1; | ||
181 | } | ||
182 | |||
183 | if (sensor_type == TEMPERATURE && x > 0) { | ||
184 | /* Temperature comes in Degrees and convert it to | ||
185 | * milli-degrees. | ||
186 | */ | ||
187 | x = x*1000; | ||
188 | } else if (sensor_type == POWER && x > 0) { | ||
189 | /* Power value comes in watts, convert to micro-watts */ | ||
190 | x = x * 1000000; | ||
191 | } | ||
192 | |||
193 | return sprintf(buf, "%d\n", x); | ||
194 | } | ||
195 | |||
196 | static u32 get_sensor_index_from_name(const char *name) | ||
197 | { | ||
198 | char *hash_position = strchr(name, '#'); | ||
199 | u32 index = 0, copy_length; | ||
200 | char newbuf[8]; | ||
201 | |||
202 | if (hash_position) { | ||
203 | copy_length = strchr(hash_position, '-') - hash_position - 1; | ||
204 | if (copy_length < sizeof(newbuf)) { | ||
205 | strncpy(newbuf, hash_position + 1, copy_length); | ||
206 | sscanf(newbuf, "%d", &index); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | return index; | ||
211 | } | ||
212 | |||
213 | static inline void get_sensor_suffix_from_name(const char *name, char *suffix) | ||
214 | { | ||
215 | char *dash_position = strrchr(name, '-'); | ||
216 | if (dash_position) | ||
217 | strncpy(suffix, dash_position+1, MAX_ATTR_LENGTH); | ||
218 | else | ||
219 | strcpy(suffix,""); | ||
220 | } | ||
221 | |||
222 | static int get_sensor_attr_properties(const char *sensor_name, | ||
223 | enum sensors sensor_type, enum attributes *attr_type, | ||
224 | u32 *sensor_index) | ||
225 | { | ||
226 | char suffix[MAX_ATTR_LENGTH]; | ||
227 | |||
228 | *attr_type = MAX_ATTR_TYPES; | ||
229 | *sensor_index = get_sensor_index_from_name(sensor_name); | ||
230 | if (*sensor_index == 0) | ||
231 | return -EINVAL; | ||
232 | |||
233 | get_sensor_suffix_from_name(sensor_name, suffix); | ||
234 | if (strcmp(suffix, "") == 0) | ||
235 | return -EINVAL; | ||
236 | |||
237 | if (strcmp(suffix, DT_FAULT_ATTR_SUFFIX) == 0) | ||
238 | *attr_type = FAULT; | ||
239 | else if (strcmp(suffix, DT_DATA_ATTR_SUFFIX) == 0) | ||
240 | *attr_type = INPUT; | ||
241 | else if ((sensor_type == TEMPERATURE) && | ||
242 | (strcmp(suffix, DT_THRESHOLD_ATTR_SUFFIX) == 0)) | ||
243 | *attr_type = MAXIMUM; | ||
244 | else if ((sensor_type == FAN) && | ||
245 | (strcmp(suffix, DT_THRESHOLD_ATTR_SUFFIX) == 0)) | ||
246 | *attr_type = MINIMUM; | ||
247 | else | ||
248 | return -ENOENT; | ||
249 | |||
250 | if (((sensor_type == FAN) && ((*attr_type == INPUT) || | ||
251 | (*attr_type == MINIMUM))) | ||
252 | || ((sensor_type == TEMPERATURE) && ((*attr_type == INPUT) || | ||
253 | (*attr_type == MAXIMUM))) | ||
254 | || ((sensor_type == POWER) && ((*attr_type == INPUT)))) | ||
255 | return 0; | ||
256 | |||
257 | return -ENOENT; | ||
258 | } | ||
259 | |||
260 | static int create_sensor_attr(struct sensor_specific_data *tdata, | ||
261 | struct device *dev, enum sensors sensor_type, | ||
262 | enum attributes attr_type) | ||
263 | { | ||
264 | int err = 0; | ||
265 | char temp_file_prefix[50]; | ||
266 | static const char *const file_name_format = "%s%d_%s"; | ||
267 | |||
268 | tdata->attr_type = attr_type; | ||
269 | |||
270 | if (sensor_type == FAN) | ||
271 | strcpy(temp_file_prefix, "fan"); | ||
272 | else if (sensor_type == TEMPERATURE) | ||
273 | strcpy(temp_file_prefix, "temp"); | ||
274 | else if (sensor_type == POWERSUPPLY) | ||
275 | strcpy(temp_file_prefix, "powersupply"); | ||
276 | else if (sensor_type == POWER) | ||
277 | strcpy(temp_file_prefix, "power"); | ||
278 | |||
279 | snprintf(tdata->attr_name, sizeof(tdata->attr_name), file_name_format, | ||
280 | temp_file_prefix, tdata->sensor_index, | ||
281 | attribute_type_table[tdata->attr_type]); | ||
282 | |||
283 | sysfs_attr_init(&tdata->sd_attr.dev_attr.attr); | ||
284 | tdata->sd_attr.dev_attr.attr.name = tdata->attr_name; | ||
285 | tdata->sd_attr.dev_attr.attr.mode = S_IRUGO; | ||
286 | tdata->sd_attr.dev_attr.show = show_sensor; | ||
287 | |||
288 | tdata->sd_attr.index = tdata->sensor_index; | ||
289 | err = device_create_file(dev, &tdata->sd_attr.dev_attr); | ||
290 | |||
291 | return err; | ||
292 | } | ||
293 | |||
294 | static int create_name_attr(struct sensor_data *pdata, | ||
295 | struct device *dev) | ||
296 | { | ||
297 | sysfs_attr_init(&pdata->name_attr.attr); | ||
298 | pdata->name_attr.attr.name = "name"; | ||
299 | pdata->name_attr.attr.mode = S_IRUGO; | ||
300 | pdata->name_attr.show = show_name; | ||
301 | return device_create_file(dev, &pdata->name_attr); | ||
302 | } | ||
303 | |||
304 | static int create_platform_device(enum sensors sensor_type, | ||
305 | struct platform_device **pdev) | ||
306 | { | ||
307 | struct pdev_entry *pdev_entry = NULL; | ||
308 | int err; | ||
309 | |||
310 | *pdev = platform_device_alloc(sensor_names[sensor_type].name, | ||
311 | sensor_type); | ||
312 | if (!*pdev) { | ||
313 | pr_err("Device allocation failed\n"); | ||
314 | err = -ENOMEM; | ||
315 | goto exit; | ||
316 | } | ||
317 | |||
318 | pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL); | ||
319 | if (!pdev_entry) { | ||
320 | pr_err("Device allocation failed\n"); | ||
321 | err = -ENOMEM; | ||
322 | goto exit_device_put; | ||
323 | } | ||
324 | |||
325 | err = platform_device_add(*pdev); | ||
326 | if (err) { | ||
327 | pr_err("Device addition failed (%d)\n", err); | ||
328 | goto exit_device_free; | ||
329 | } | ||
330 | |||
331 | pdev_entry->pdev = *pdev; | ||
332 | pdev_entry->type = (*pdev)->id; | ||
333 | |||
334 | list_add_tail(&pdev_entry->list, &pdev_list); | ||
335 | |||
336 | return 0; | ||
337 | exit_device_free: | ||
338 | kfree(pdev_entry); | ||
339 | exit_device_put: | ||
340 | platform_device_put(*pdev); | ||
341 | exit: | ||
342 | return err; | ||
343 | } | ||
344 | |||
345 | static int create_sensor_data(struct platform_device *pdev) | ||
346 | { | ||
347 | struct sensor_data *pdata = NULL; | ||
348 | int err = 0; | ||
349 | |||
350 | pdata = kzalloc(sizeof(struct sensor_data), GFP_KERNEL); | ||
351 | if (!pdata) { | ||
352 | err = -ENOMEM; | ||
353 | goto exit; | ||
354 | } | ||
355 | |||
356 | err = create_name_attr(pdata, &pdev->dev); | ||
357 | if (err) | ||
358 | goto exit_free; | ||
359 | |||
360 | pdata->hwmon_dev = hwmon_device_register(&pdev->dev); | ||
361 | if (IS_ERR(pdata->hwmon_dev)) { | ||
362 | err = PTR_ERR(pdata->hwmon_dev); | ||
363 | dev_err(&pdev->dev, "Class registration failed (%d)\n", | ||
364 | err); | ||
365 | goto exit_name; | ||
366 | } | ||
367 | |||
368 | INIT_LIST_HEAD(&pdata->sensor_list); | ||
369 | platform_set_drvdata(pdev, pdata); | ||
370 | |||
371 | return 0; | ||
372 | |||
373 | exit_name: | ||
374 | device_remove_file(&pdev->dev, &pdata->name_attr); | ||
375 | exit_free: | ||
376 | kfree(pdata); | ||
377 | exit: | ||
378 | return err; | ||
379 | } | ||
380 | |||
381 | static void delete_sensor_attr(struct sensor_data *pdata) | ||
382 | { | ||
383 | struct sensor_entry *s, *l; | ||
384 | |||
385 | list_for_each_entry_safe(s, l, &pdata->sensor_list, list) { | ||
386 | struct sensor_specific_data *tdata = s->sensor_data; | ||
387 | kfree(tdata); | ||
388 | list_del(&s->list); | ||
389 | kfree(s); | ||
390 | } | ||
391 | } | ||
392 | |||
393 | static int powernv_sensor_init(u32 sensor_id, const struct device_node *np, | ||
394 | enum sensors sensor_type, enum attributes attr_type, | ||
395 | u32 sensor_index) | ||
396 | { | ||
397 | struct platform_device *pdev = powernv_sensor_get_pdev(sensor_type); | ||
398 | struct sensor_specific_data *tdata; | ||
399 | struct sensor_entry *sensor_entry; | ||
400 | struct sensor_data *pdata; | ||
401 | int err = 0; | ||
402 | |||
403 | if (!pdev) { | ||
404 | err = create_platform_device(sensor_type, &pdev); | ||
405 | if (err) | ||
406 | goto exit; | ||
407 | |||
408 | err = create_sensor_data(pdev); | ||
409 | if (err) | ||
410 | goto exit; | ||
411 | } | ||
412 | |||
413 | pdata = platform_get_drvdata(pdev); | ||
414 | if (!pdata) { | ||
415 | err = -ENOMEM; | ||
416 | goto exit; | ||
417 | } | ||
418 | |||
419 | tdata = kzalloc(sizeof(struct sensor_specific_data), GFP_KERNEL); | ||
420 | if (!tdata) { | ||
421 | err = -ENOMEM; | ||
422 | goto exit; | ||
423 | } | ||
424 | |||
425 | tdata->sensor_id = sensor_id; | ||
426 | tdata->sensor_index = sensor_index; | ||
427 | |||
428 | err = create_sensor_attr(tdata, &pdev->dev, sensor_type, attr_type); | ||
429 | if (err) | ||
430 | goto exit_free; | ||
431 | |||
432 | sensor_entry = kzalloc(sizeof(struct sensor_entry), GFP_KERNEL); | ||
433 | if (!sensor_entry) { | ||
434 | err = -ENOMEM; | ||
435 | goto exit_attr; | ||
436 | } | ||
437 | |||
438 | sensor_entry->sensor_data = tdata; | ||
439 | |||
440 | list_add_tail(&sensor_entry->list, &pdata->sensor_list); | ||
441 | |||
442 | return 0; | ||
443 | exit_attr: | ||
444 | device_remove_file(&pdev->dev, &tdata->sd_attr.dev_attr); | ||
445 | exit_free: | ||
446 | kfree(tdata); | ||
447 | exit: | ||
448 | return err; | ||
449 | } | ||
450 | |||
451 | static void delete_unregister_sensors(void) | ||
452 | { | ||
453 | struct pdev_entry *p, *n; | ||
454 | |||
455 | list_for_each_entry_safe(p, n, &pdev_list, list) { | ||
456 | struct sensor_data *pdata = platform_get_drvdata(p->pdev); | ||
457 | if (pdata) { | ||
458 | delete_sensor_attr(pdata); | ||
459 | |||
460 | hwmon_device_unregister(pdata->hwmon_dev); | ||
461 | kfree(pdata); | ||
462 | } | ||
463 | platform_device_unregister(p->pdev); | ||
464 | list_del(&p->list); | ||
465 | kfree(p); | ||
466 | } | ||
467 | } | ||
468 | |||
469 | static int __init powernv_hwmon_init(void) | ||
470 | { | ||
471 | struct device_node *opal, *np = NULL; | ||
472 | enum attributes attr_type; | ||
473 | enum sensors type; | ||
474 | const u32 *sensor_id; | ||
475 | u32 sensor_index; | ||
476 | int err; | ||
477 | |||
478 | opal = of_find_node_by_path("/ibm,opal/sensors"); | ||
479 | if (!opal) { | ||
480 | pr_err("%s: Opal 'sensors' node not found\n", __func__); | ||
481 | return -ENXIO; | ||
482 | } | ||
483 | |||
484 | for_each_child_of_node(opal, np) { | ||
485 | if (np->name == NULL) | ||
486 | continue; | ||
487 | |||
488 | for (type = 0; type < MAX_SENSOR_TYPE; type++) | ||
489 | if (of_device_is_compatible(np, | ||
490 | sensor_names[type].compaible)) | ||
491 | break; | ||
492 | |||
493 | if (type == MAX_SENSOR_TYPE) | ||
494 | continue; | ||
495 | |||
496 | if (get_sensor_attr_properties(np->name, type, &attr_type, | ||
497 | &sensor_index)) | ||
498 | continue; | ||
499 | |||
500 | sensor_id = of_get_property(np, "sensor-id", NULL); | ||
501 | if (!sensor_id) { | ||
502 | pr_info("%s: %s doesn't have sensor-id\n", __func__, | ||
503 | np->name); | ||
504 | continue; | ||
505 | } | ||
506 | |||
507 | err = powernv_sensor_init(*sensor_id, np, type, attr_type, | ||
508 | sensor_index); | ||
509 | if (err) { | ||
510 | of_node_put(opal); | ||
511 | goto exit; | ||
512 | } | ||
513 | } | ||
514 | of_node_put(opal); | ||
515 | |||
516 | return 0; | ||
517 | exit: | ||
518 | delete_unregister_sensors(); | ||
519 | return err; | ||
520 | |||
521 | } | ||
522 | |||
523 | static void powernv_hwmon_exit(void) | ||
524 | { | ||
525 | delete_unregister_sensors(); | ||
526 | } | ||
527 | |||
528 | module_init(powernv_hwmon_init); | ||
529 | module_exit(powernv_hwmon_exit); | ||