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