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