ibmpowernv.c 17 KB

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