Blame view
drivers/hwmon/ibmpowernv.c
16.8 KB
d5bb994bc treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
24c1aa858 hwmon: (powerpc/p... |
2 3 4 |
/* * IBM PowerNV platform sensors for temperature/fan/voltage/power * Copyright (C) 2014 IBM |
24c1aa858 hwmon: (powerpc/p... |
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
*/ #define DRVNAME "ibmpowernv" #define pr_fmt(fmt) DRVNAME ": " fmt #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/of.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <asm/opal.h> #include <linux/err.h> |
3df2f59f0 hwmon: (ibmpowern... |
21 |
#include <asm/cputhreads.h> |
8416915c1 hwmon: (ibmpowern... |
22 |
#include <asm/smp.h> |
24c1aa858 hwmon: (powerpc/p... |
23 24 |
#define MAX_ATTR_LEN 32 |
2bcd3787b hwmon: (ibmpowern... |
25 |
#define MAX_LABEL_LEN 64 |
24c1aa858 hwmon: (powerpc/p... |
26 27 28 29 30 31 32 33 34 35 36 37 |
/* Sensor suffix name from DT */ #define DT_FAULT_ATTR_SUFFIX "faulted" #define DT_DATA_ATTR_SUFFIX "data" #define DT_THRESHOLD_ATTR_SUFFIX "thrs" /* * Enumerates all the types of sensors in the POWERNV platform and does index * into 'struct sensor_group' */ enum sensors { FAN, |
96124610e hwmon: (ibmpowern... |
38 |
TEMP, |
24c1aa858 hwmon: (powerpc/p... |
39 40 |
POWER_SUPPLY, POWER_INPUT, |
3a2b3d37e hwmon: (ibmpowern... |
41 |
CURRENT, |
43d2974b6 hwmon: (ibmpowern... |
42 |
ENERGY, |
24c1aa858 hwmon: (powerpc/p... |
43 44 |
MAX_SENSOR_TYPE, }; |
14681637a hwmon: (ibmpowern... |
45 |
#define INVALID_INDEX (-1U) |
3ab521601 hwmon: (ibmpowern... |
46 47 48 49 50 51 52 53 54 55 |
/* * 'compatible' string properties for sensor types as defined in old * PowerNV firmware (skiboot). These are ordered as 'enum sensors'. */ static const char * const legacy_compatibles[] = { "ibm,opal-sensor-cooling-fan", "ibm,opal-sensor-amb-temp", "ibm,opal-sensor-power-supply", "ibm,opal-sensor-power" }; |
24c1aa858 hwmon: (powerpc/p... |
56 |
static struct sensor_group { |
3ab521601 hwmon: (ibmpowern... |
57 |
const char *name; /* matches property 'sensor-type' */ |
24c1aa858 hwmon: (powerpc/p... |
58 59 |
struct attribute_group group; u32 attr_count; |
fcaf57b67 hwmon: (ibmpowern... |
60 |
u32 hwmon_index; |
24c1aa858 hwmon: (powerpc/p... |
61 |
} sensor_groups[] = { |
3ab521601 hwmon: (ibmpowern... |
62 63 64 |
{ "fan" }, { "temp" }, { "in" }, |
3a2b3d37e hwmon: (ibmpowern... |
65 66 |
{ "power" }, { "curr" }, |
43d2974b6 hwmon: (ibmpowern... |
67 |
{ "energy" }, |
24c1aa858 hwmon: (powerpc/p... |
68 69 70 71 |
}; struct sensor_data { u32 id; /* An opaque id of the firmware for each sensor */ |
fcaf57b67 hwmon: (ibmpowern... |
72 73 |
u32 hwmon_index; u32 opal_index; |
24c1aa858 hwmon: (powerpc/p... |
74 |
enum sensors type; |
2bcd3787b hwmon: (ibmpowern... |
75 |
char label[MAX_LABEL_LEN]; |
24c1aa858 hwmon: (powerpc/p... |
76 77 |
char name[MAX_ATTR_LEN]; struct device_attribute dev_attr; |
e0da99123 hwmon: (ibmpowern... |
78 79 80 81 82 83 84 |
struct sensor_group_data *sgrp_data; }; struct sensor_group_data { struct mutex mutex; u32 gid; bool enable; |
24c1aa858 hwmon: (powerpc/p... |
85 86 87 88 |
}; struct platform_data { const struct attribute_group *attr_groups[MAX_SENSOR_TYPE + 1]; |
e0da99123 hwmon: (ibmpowern... |
89 |
struct sensor_group_data *sgrp_data; |
24c1aa858 hwmon: (powerpc/p... |
90 |
u32 sensors_count; /* Total count of sensors from each group */ |
e0da99123 hwmon: (ibmpowern... |
91 |
u32 nr_sensor_groups; /* Total number of sensor groups */ |
24c1aa858 hwmon: (powerpc/p... |
92 |
}; |
24c1aa858 hwmon: (powerpc/p... |
93 94 95 96 97 98 |
static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_data *sdata = container_of(devattr, struct sensor_data, dev_attr); ssize_t ret; |
3c8c049aa hwmon: (ibmpowern... |
99 |
u64 x; |
e0da99123 hwmon: (ibmpowern... |
100 101 |
if (sdata->sgrp_data && !sdata->sgrp_data->enable) return -ENODATA; |
3c8c049aa hwmon: (ibmpowern... |
102 |
ret = opal_get_sensor_data_u64(sdata->id, &x); |
24c1aa858 hwmon: (powerpc/p... |
103 |
|
24c1aa858 hwmon: (powerpc/p... |
104 105 106 107 |
if (ret) return ret; /* Convert temperature to milli-degrees */ |
96124610e hwmon: (ibmpowern... |
108 |
if (sdata->type == TEMP) |
24c1aa858 hwmon: (powerpc/p... |
109 110 111 112 |
x *= 1000; /* Convert power to micro-watts */ else if (sdata->type == POWER_INPUT) x *= 1000000; |
3c8c049aa hwmon: (ibmpowern... |
113 114 |
return sprintf(buf, "%llu ", x); |
24c1aa858 hwmon: (powerpc/p... |
115 |
} |
e0da99123 hwmon: (ibmpowern... |
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
static ssize_t show_enable(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_data *sdata = container_of(devattr, struct sensor_data, dev_attr); return sprintf(buf, "%u ", sdata->sgrp_data->enable); } static ssize_t store_enable(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_data *sdata = container_of(devattr, struct sensor_data, dev_attr); struct sensor_group_data *sgrp_data = sdata->sgrp_data; int ret; bool data; ret = kstrtobool(buf, &data); if (ret) return ret; ret = mutex_lock_interruptible(&sgrp_data->mutex); if (ret) return ret; if (data != sgrp_data->enable) { ret = sensor_group_enable(sgrp_data->gid, data); if (!ret) sgrp_data->enable = data; } if (!ret) ret = count; mutex_unlock(&sgrp_data->mutex); return ret; } |
2bcd3787b hwmon: (ibmpowern... |
156 157 158 159 160 161 162 163 164 |
static ssize_t show_label(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_data *sdata = container_of(devattr, struct sensor_data, dev_attr); return sprintf(buf, "%s ", sdata->label); } |
e3e61f01d hwmon: (ibmpowern... |
165 |
static int get_logical_cpu(int hwcpu) |
3df2f59f0 hwmon: (ibmpowern... |
166 167 168 169 170 171 172 173 174 |
{ int cpu; for_each_possible_cpu(cpu) if (get_hard_smp_processor_id(cpu) == hwcpu) return cpu; return -ENOENT; } |
e3e61f01d hwmon: (ibmpowern... |
175 176 |
static void make_sensor_label(struct device_node *np, struct sensor_data *sdata, const char *label) |
2bcd3787b hwmon: (ibmpowern... |
177 |
{ |
3df2f59f0 hwmon: (ibmpowern... |
178 |
u32 id; |
2bcd3787b hwmon: (ibmpowern... |
179 |
size_t n; |
6a096871b hwmon: (ibmpowern... |
180 |
n = scnprintf(sdata->label, sizeof(sdata->label), "%s", label); |
3df2f59f0 hwmon: (ibmpowern... |
181 182 183 184 185 186 187 188 189 190 |
/* * Core temp pretty print */ if (!of_property_read_u32(np, "ibm,pir", &id)) { int cpuid = get_logical_cpu(id); if (cpuid >= 0) /* * The digital thermal sensors are associated |
acf32964d hwmon: (ibmpowern... |
191 |
* with a core. |
3df2f59f0 hwmon: (ibmpowern... |
192 |
*/ |
6a096871b hwmon: (ibmpowern... |
193 |
n += scnprintf(sdata->label + n, |
acf32964d hwmon: (ibmpowern... |
194 195 |
sizeof(sdata->label) - n, " %d", cpuid); |
3df2f59f0 hwmon: (ibmpowern... |
196 |
else |
6a096871b hwmon: (ibmpowern... |
197 |
n += scnprintf(sdata->label + n, |
3df2f59f0 hwmon: (ibmpowern... |
198 199 200 201 202 203 204 |
sizeof(sdata->label) - n, " phy%d", id); } /* * Membuffer pretty print */ if (!of_property_read_u32(np, "ibm,chip-id", &id)) |
6a096871b hwmon: (ibmpowern... |
205 |
n += scnprintf(sdata->label + n, sizeof(sdata->label) - n, |
3df2f59f0 hwmon: (ibmpowern... |
206 |
" %d", id & 0xffff); |
2bcd3787b hwmon: (ibmpowern... |
207 208 209 |
} static int get_sensor_index_attr(const char *name, u32 *index, char *attr) |
24c1aa858 hwmon: (powerpc/p... |
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
{ char *hash_pos = strchr(name, '#'); char buf[8] = { 0 }; char *dash_pos; u32 copy_len; int err; if (!hash_pos) return -EINVAL; dash_pos = strchr(hash_pos, '-'); if (!dash_pos) return -EINVAL; copy_len = dash_pos - hash_pos - 1; if (copy_len >= sizeof(buf)) return -EINVAL; strncpy(buf, hash_pos + 1, copy_len); err = kstrtou32(buf, 10, index); if (err) return err; strncpy(attr, dash_pos + 1, MAX_ATTR_LEN); return 0; } |
ccc9ac6cc hwmon: (ibmpowern... |
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
static const char *convert_opal_attr_name(enum sensors type, const char *opal_attr) { const char *attr_name = NULL; if (!strcmp(opal_attr, DT_FAULT_ATTR_SUFFIX)) { attr_name = "fault"; } else if (!strcmp(opal_attr, DT_DATA_ATTR_SUFFIX)) { attr_name = "input"; } else if (!strcmp(opal_attr, DT_THRESHOLD_ATTR_SUFFIX)) { if (type == TEMP) attr_name = "max"; else if (type == FAN) attr_name = "min"; } return attr_name; } |
24c1aa858 hwmon: (powerpc/p... |
256 257 258 259 260 261 |
/* * This function translates the DT node name into the 'hwmon' attribute name. * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc. * which need to be mapped as fan2_input, temp1_max respectively before * populating them inside hwmon device class. */ |
f9f54f16b hwmon: (ibmpowern... |
262 263 |
static const char *parse_opal_node_name(const char *node_name, enum sensors type, u32 *index) |
24c1aa858 hwmon: (powerpc/p... |
264 265 |
{ char attr_suffix[MAX_ATTR_LEN]; |
ccc9ac6cc hwmon: (ibmpowern... |
266 |
const char *attr_name; |
24c1aa858 hwmon: (powerpc/p... |
267 |
int err; |
f9f54f16b hwmon: (ibmpowern... |
268 269 270 |
err = get_sensor_index_attr(node_name, index, attr_suffix); if (err) return ERR_PTR(err); |
24c1aa858 hwmon: (powerpc/p... |
271 |
|
ccc9ac6cc hwmon: (ibmpowern... |
272 273 |
attr_name = convert_opal_attr_name(type, attr_suffix); if (!attr_name) |
f9f54f16b hwmon: (ibmpowern... |
274 |
return ERR_PTR(-ENOENT); |
24c1aa858 hwmon: (powerpc/p... |
275 |
|
f9f54f16b hwmon: (ibmpowern... |
276 |
return attr_name; |
24c1aa858 hwmon: (powerpc/p... |
277 |
} |
c4ad47206 hwmon: (ibmpowern... |
278 279 280 |
static int get_sensor_type(struct device_node *np) { enum sensors type; |
14681637a hwmon: (ibmpowern... |
281 |
const char *str; |
c4ad47206 hwmon: (ibmpowern... |
282 |
|
3ab521601 hwmon: (ibmpowern... |
283 284 |
for (type = 0; type < ARRAY_SIZE(legacy_compatibles); type++) { if (of_device_is_compatible(np, legacy_compatibles[type])) |
c4ad47206 hwmon: (ibmpowern... |
285 286 |
return type; } |
14681637a hwmon: (ibmpowern... |
287 288 289 290 291 292 293 294 295 296 297 298 299 |
/* * Let's check if we have a newer device tree */ if (!of_device_is_compatible(np, "ibm,opal-sensor")) return MAX_SENSOR_TYPE; if (of_property_read_string(np, "sensor-type", &str)) return MAX_SENSOR_TYPE; for (type = 0; type < MAX_SENSOR_TYPE; type++) if (!strcmp(str, sensor_groups[type].name)) return type; |
c4ad47206 hwmon: (ibmpowern... |
300 301 |
return MAX_SENSOR_TYPE; } |
fcaf57b67 hwmon: (ibmpowern... |
302 303 304 305 |
static u32 get_sensor_hwmon_index(struct sensor_data *sdata, struct sensor_data *sdata_table, int count) { int i; |
14681637a hwmon: (ibmpowern... |
306 307 308 309 310 311 312 313 314 |
/* * We don't use the OPAL index on newer device trees */ if (sdata->opal_index != INVALID_INDEX) { for (i = 0; i < count; i++) if (sdata_table[i].opal_index == sdata->opal_index && sdata_table[i].type == sdata->type) return sdata_table[i].hwmon_index; } |
fcaf57b67 hwmon: (ibmpowern... |
315 316 |
return ++sensor_groups[sdata->type].hwmon_index; } |
e0da99123 hwmon: (ibmpowern... |
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
static int init_sensor_group_data(struct platform_device *pdev, struct platform_data *pdata) { struct sensor_group_data *sgrp_data; struct device_node *groups, *sgrp; int count = 0, ret = 0; enum sensors type; groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group"); if (!groups) return ret; for_each_child_of_node(groups, sgrp) { type = get_sensor_type(sgrp); if (type != MAX_SENSOR_TYPE) pdata->nr_sensor_groups++; } if (!pdata->nr_sensor_groups) goto out; sgrp_data = devm_kcalloc(&pdev->dev, pdata->nr_sensor_groups, sizeof(*sgrp_data), GFP_KERNEL); if (!sgrp_data) { ret = -ENOMEM; goto out; } for_each_child_of_node(groups, sgrp) { u32 gid; type = get_sensor_type(sgrp); if (type == MAX_SENSOR_TYPE) continue; if (of_property_read_u32(sgrp, "sensor-group-id", &gid)) continue; if (of_count_phandle_with_args(sgrp, "sensors", NULL) <= 0) continue; sensor_groups[type].attr_count++; sgrp_data[count].gid = gid; mutex_init(&sgrp_data[count].mutex); sgrp_data[count++].enable = false; } pdata->sgrp_data = sgrp_data; out: of_node_put(groups); return ret; } static struct sensor_group_data *get_sensor_group(struct platform_data *pdata, struct device_node *node, enum sensors gtype) { struct sensor_group_data *sgrp_data = pdata->sgrp_data; struct device_node *groups, *sgrp; groups = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group"); if (!groups) return NULL; for_each_child_of_node(groups, sgrp) { struct of_phandle_iterator it; u32 gid; int rc, i; enum sensors type; type = get_sensor_type(sgrp); if (type != gtype) continue; if (of_property_read_u32(sgrp, "sensor-group-id", &gid)) continue; of_for_each_phandle(&it, rc, sgrp, "sensors", NULL, 0) if (it.phandle == node->phandle) { of_node_put(it.node); break; } if (rc) continue; for (i = 0; i < pdata->nr_sensor_groups; i++) if (gid == sgrp_data[i].gid) { of_node_put(sgrp); of_node_put(groups); return &sgrp_data[i]; } } of_node_put(groups); return NULL; } |
8de303bae hwmon: (ibmpowern... |
414 |
static int populate_attr_groups(struct platform_device *pdev) |
24c1aa858 hwmon: (powerpc/p... |
415 416 417 418 419 |
{ struct platform_data *pdata = platform_get_drvdata(pdev); const struct attribute_group **pgroups = pdata->attr_groups; struct device_node *opal, *np; enum sensors type; |
e0da99123 hwmon: (ibmpowern... |
420 421 422 423 424 |
int ret; ret = init_sensor_group_data(pdev, pdata); if (ret) return ret; |
24c1aa858 hwmon: (powerpc/p... |
425 426 |
opal = of_find_node_by_path("/ibm,opal/sensors"); |
24c1aa858 hwmon: (powerpc/p... |
427 |
for_each_child_of_node(opal, np) { |
2bcd3787b hwmon: (ibmpowern... |
428 |
const char *label; |
c4ad47206 hwmon: (ibmpowern... |
429 |
type = get_sensor_type(np); |
2bcd3787b hwmon: (ibmpowern... |
430 431 432 433 434 435 |
if (type == MAX_SENSOR_TYPE) continue; sensor_groups[type].attr_count++; /* |
996cf5a5e hwmon: (ibmpowern... |
436 |
* add attributes for labels, min and max |
2bcd3787b hwmon: (ibmpowern... |
437 438 |
*/ if (!of_property_read_string(np, "label", &label)) |
c4ad47206 hwmon: (ibmpowern... |
439 |
sensor_groups[type].attr_count++; |
996cf5a5e hwmon: (ibmpowern... |
440 441 442 443 |
if (of_find_property(np, "sensor-data-min", NULL)) sensor_groups[type].attr_count++; if (of_find_property(np, "sensor-data-max", NULL)) sensor_groups[type].attr_count++; |
24c1aa858 hwmon: (powerpc/p... |
444 445 446 447 448 |
} of_node_put(opal); for (type = 0; type < MAX_SENSOR_TYPE; type++) { |
a86854d0c treewide: devm_kz... |
449 450 451 |
sensor_groups[type].group.attrs = devm_kcalloc(&pdev->dev, sensor_groups[type].attr_count + 1, sizeof(struct attribute *), |
24c1aa858 hwmon: (powerpc/p... |
452 453 454 455 456 457 458 459 460 461 462 |
GFP_KERNEL); if (!sensor_groups[type].group.attrs) return -ENOMEM; pgroups[type] = &sensor_groups[type].group; pdata->sensors_count += sensor_groups[type].attr_count; sensor_groups[type].attr_count = 0; } return 0; } |
9e4f74b11 hwmon: (ibmpowern... |
463 464 465 |
static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name, ssize_t (*show)(struct device *dev, struct device_attribute *attr, |
e0da99123 hwmon: (ibmpowern... |
466 467 468 469 |
char *buf), ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)) |
9e4f74b11 hwmon: (ibmpowern... |
470 471 472 473 474 475 476 |
{ snprintf(sdata->name, MAX_ATTR_LEN, "%s%d_%s", sensor_groups[sdata->type].name, sdata->hwmon_index, attr_name); sysfs_attr_init(&sdata->dev_attr.attr); sdata->dev_attr.attr.name = sdata->name; |
9e4f74b11 hwmon: (ibmpowern... |
477 |
sdata->dev_attr.show = show; |
e0da99123 hwmon: (ibmpowern... |
478 479 480 481 482 483 |
if (store) { sdata->dev_attr.store = store; sdata->dev_attr.attr.mode = 0664; } else { sdata->dev_attr.attr.mode = 0444; } |
9e4f74b11 hwmon: (ibmpowern... |
484 |
} |
996cf5a5e hwmon: (ibmpowern... |
485 486 487 |
static void populate_sensor(struct sensor_data *sdata, int od, int hd, int sid, const char *attr_name, enum sensors type, const struct attribute_group *pgroup, |
e0da99123 hwmon: (ibmpowern... |
488 |
struct sensor_group_data *sgrp_data, |
996cf5a5e hwmon: (ibmpowern... |
489 490 |
ssize_t (*show)(struct device *dev, struct device_attribute *attr, |
e0da99123 hwmon: (ibmpowern... |
491 492 493 494 |
char *buf), ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)) |
996cf5a5e hwmon: (ibmpowern... |
495 496 497 498 499 |
{ sdata->id = sid; sdata->type = type; sdata->opal_index = od; sdata->hwmon_index = hd; |
e0da99123 hwmon: (ibmpowern... |
500 |
create_hwmon_attr(sdata, attr_name, show, store); |
996cf5a5e hwmon: (ibmpowern... |
501 |
pgroup->attrs[sensor_groups[type].attr_count++] = &sdata->dev_attr.attr; |
e0da99123 hwmon: (ibmpowern... |
502 |
sdata->sgrp_data = sgrp_data; |
996cf5a5e hwmon: (ibmpowern... |
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 |
} static char *get_max_attr(enum sensors type) { switch (type) { case POWER_INPUT: return "input_highest"; default: return "highest"; } } static char *get_min_attr(enum sensors type) { switch (type) { case POWER_INPUT: return "input_lowest"; default: return "lowest"; } } |
24c1aa858 hwmon: (powerpc/p... |
524 525 526 527 528 529 |
/* * Iterate through the device tree for each child of 'sensors' node, create * a sysfs attribute file, the file is named by translating the DT node name * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max * etc.. */ |
8de303bae hwmon: (ibmpowern... |
530 |
static int create_device_attrs(struct platform_device *pdev) |
24c1aa858 hwmon: (powerpc/p... |
531 532 533 534 535 |
{ struct platform_data *pdata = platform_get_drvdata(pdev); const struct attribute_group **pgroups = pdata->attr_groups; struct device_node *opal, *np; struct sensor_data *sdata; |
24c1aa858 hwmon: (powerpc/p... |
536 |
u32 count = 0; |
e0da99123 hwmon: (ibmpowern... |
537 |
u32 group_attr_id[MAX_SENSOR_TYPE] = {0}; |
24c1aa858 hwmon: (powerpc/p... |
538 |
|
a86854d0c treewide: devm_kz... |
539 540 |
sdata = devm_kcalloc(&pdev->dev, pdata->sensors_count, sizeof(*sdata), |
24c1aa858 hwmon: (powerpc/p... |
541 |
GFP_KERNEL); |
e0da99123 hwmon: (ibmpowern... |
542 543 |
if (!sdata) return -ENOMEM; |
24c1aa858 hwmon: (powerpc/p... |
544 |
|
e0da99123 hwmon: (ibmpowern... |
545 |
opal = of_find_node_by_path("/ibm,opal/sensors"); |
24c1aa858 hwmon: (powerpc/p... |
546 |
for_each_child_of_node(opal, np) { |
e0da99123 hwmon: (ibmpowern... |
547 |
struct sensor_group_data *sgrp_data; |
f9f54f16b hwmon: (ibmpowern... |
548 |
const char *attr_name; |
e0da99123 hwmon: (ibmpowern... |
549 550 |
u32 opal_index, hw_id; u32 sensor_id; |
2bcd3787b hwmon: (ibmpowern... |
551 |
const char *label; |
e0da99123 hwmon: (ibmpowern... |
552 |
enum sensors type; |
f9f54f16b hwmon: (ibmpowern... |
553 |
|
c4ad47206 hwmon: (ibmpowern... |
554 |
type = get_sensor_type(np); |
24c1aa858 hwmon: (powerpc/p... |
555 556 |
if (type == MAX_SENSOR_TYPE) continue; |
14681637a hwmon: (ibmpowern... |
557 558 559 560 561 562 |
/* * Newer device trees use a "sensor-data" property * name for input. */ if (of_property_read_u32(np, "sensor-id", &sensor_id) && of_property_read_u32(np, "sensor-data", &sensor_id)) { |
24c1aa858 hwmon: (powerpc/p... |
563 |
dev_info(&pdev->dev, |
0debe4d0b hwmon: Convert to... |
564 565 566 |
"'sensor-id' missing in the node '%pOFn' ", np); |
24c1aa858 hwmon: (powerpc/p... |
567 568 |
continue; } |
18d03f3cb hwmon: (ibmpowern... |
569 |
sdata[count].id = sensor_id; |
24c1aa858 hwmon: (powerpc/p... |
570 |
sdata[count].type = type; |
f9f54f16b hwmon: (ibmpowern... |
571 |
|
14681637a hwmon: (ibmpowern... |
572 573 574 575 576 577 |
/* * If we can not parse the node name, it means we are * running on a newer device tree. We can just forget * about the OPAL index and use a defaut value for the * hwmon attribute name */ |
f9f54f16b hwmon: (ibmpowern... |
578 579 |
attr_name = parse_opal_node_name(np->name, type, &opal_index); if (IS_ERR(attr_name)) { |
14681637a hwmon: (ibmpowern... |
580 581 |
attr_name = "input"; opal_index = INVALID_INDEX; |
f9f54f16b hwmon: (ibmpowern... |
582 |
} |
e0da99123 hwmon: (ibmpowern... |
583 584 585 586 587 588 |
hw_id = get_sensor_hwmon_index(&sdata[count], sdata, count); sgrp_data = get_sensor_group(pdata, np, type); populate_sensor(&sdata[count], opal_index, hw_id, sensor_id, attr_name, type, pgroups[type], sgrp_data, show_sensor, NULL); count++; |
2bcd3787b hwmon: (ibmpowern... |
589 590 591 592 593 594 595 596 |
if (!of_property_read_string(np, "label", &label)) { /* * For the label attribute, we can reuse the * "properties" of the previous "input" * attribute. They are related to the same * sensor. */ |
2bcd3787b hwmon: (ibmpowern... |
597 598 |
make_sensor_label(np, &sdata[count], label); |
e0da99123 hwmon: (ibmpowern... |
599 |
populate_sensor(&sdata[count], opal_index, hw_id, |
996cf5a5e hwmon: (ibmpowern... |
600 |
sensor_id, "label", type, pgroups[type], |
e0da99123 hwmon: (ibmpowern... |
601 |
NULL, show_label, NULL); |
996cf5a5e hwmon: (ibmpowern... |
602 603 |
count++; } |
2bcd3787b hwmon: (ibmpowern... |
604 |
|
996cf5a5e hwmon: (ibmpowern... |
605 606 |
if (!of_property_read_u32(np, "sensor-data-max", &sensor_id)) { attr_name = get_max_attr(type); |
e0da99123 hwmon: (ibmpowern... |
607 |
populate_sensor(&sdata[count], opal_index, hw_id, |
996cf5a5e hwmon: (ibmpowern... |
608 |
sensor_id, attr_name, type, |
e0da99123 hwmon: (ibmpowern... |
609 610 |
pgroups[type], sgrp_data, show_sensor, NULL); |
996cf5a5e hwmon: (ibmpowern... |
611 612 |
count++; } |
2bcd3787b hwmon: (ibmpowern... |
613 |
|
996cf5a5e hwmon: (ibmpowern... |
614 615 |
if (!of_property_read_u32(np, "sensor-data-min", &sensor_id)) { attr_name = get_min_attr(type); |
e0da99123 hwmon: (ibmpowern... |
616 |
populate_sensor(&sdata[count], opal_index, hw_id, |
996cf5a5e hwmon: (ibmpowern... |
617 |
sensor_id, attr_name, type, |
e0da99123 hwmon: (ibmpowern... |
618 619 620 621 622 623 624 625 626 627 628 629 |
pgroups[type], sgrp_data, show_sensor, NULL); count++; } if (sgrp_data && !sgrp_data->enable) { sgrp_data->enable = true; hw_id = ++group_attr_id[type]; populate_sensor(&sdata[count], opal_index, hw_id, sgrp_data->gid, "enable", type, pgroups[type], sgrp_data, show_enable, store_enable); |
996cf5a5e hwmon: (ibmpowern... |
630 |
count++; |
2bcd3787b hwmon: (ibmpowern... |
631 |
} |
24c1aa858 hwmon: (powerpc/p... |
632 |
} |
24c1aa858 hwmon: (powerpc/p... |
633 |
of_node_put(opal); |
e0da99123 hwmon: (ibmpowern... |
634 |
return 0; |
24c1aa858 hwmon: (powerpc/p... |
635 |
} |
8de303bae hwmon: (ibmpowern... |
636 |
static int ibmpowernv_probe(struct platform_device *pdev) |
24c1aa858 hwmon: (powerpc/p... |
637 638 639 640 641 642 643 644 645 646 647 |
{ struct platform_data *pdata; struct device *hwmon_dev; int err; pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; platform_set_drvdata(pdev, pdata); pdata->sensors_count = 0; |
e0da99123 hwmon: (ibmpowern... |
648 |
pdata->nr_sensor_groups = 0; |
24c1aa858 hwmon: (powerpc/p... |
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 |
err = populate_attr_groups(pdev); if (err) return err; /* Create sysfs attribute data for each sensor found in the DT */ err = create_device_attrs(pdev); if (err) return err; /* Finally, register with hwmon */ hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, DRVNAME, pdata, pdata->attr_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } |
8de303bae hwmon: (ibmpowern... |
665 666 667 668 669 670 671 |
static const struct platform_device_id opal_sensor_driver_ids[] = { { .name = "opal-sensor", }, { } }; MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids); |
0b056b29f hwmon: (ibmpowern... |
672 673 674 675 676 |
static const struct of_device_id opal_sensor_match[] = { { .compatible = "ibm,opal-sensor" }, { }, }; MODULE_DEVICE_TABLE(of, opal_sensor_match); |
24c1aa858 hwmon: (powerpc/p... |
677 |
static struct platform_driver ibmpowernv_driver = { |
8de303bae hwmon: (ibmpowern... |
678 679 680 |
.probe = ibmpowernv_probe, .id_table = opal_sensor_driver_ids, .driver = { |
8de303bae hwmon: (ibmpowern... |
681 |
.name = DRVNAME, |
0b056b29f hwmon: (ibmpowern... |
682 |
.of_match_table = opal_sensor_match, |
24c1aa858 hwmon: (powerpc/p... |
683 684 |
}, }; |
3bdec670d hwmon: (ibmpowern... |
685 |
module_platform_driver(ibmpowernv_driver); |
24c1aa858 hwmon: (powerpc/p... |
686 687 688 689 |
MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>"); MODULE_DESCRIPTION("IBM POWERNV platform sensors"); MODULE_LICENSE("GPL"); |