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