linux/drivers/hwmon/scmi-hwmon.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * System Control and Management Interface(SCMI) based hwmon sensor driver
   4 *
   5 * Copyright (C) 2018-2021 ARM Ltd.
   6 * Sudeep Holla <sudeep.holla@arm.com>
   7 */
   8
   9#include <linux/hwmon.h>
  10#include <linux/module.h>
  11#include <linux/scmi_protocol.h>
  12#include <linux/slab.h>
  13#include <linux/sysfs.h>
  14#include <linux/thermal.h>
  15
  16static const struct scmi_sensor_proto_ops *sensor_ops;
  17
  18struct scmi_sensors {
  19        const struct scmi_protocol_handle *ph;
  20        const struct scmi_sensor_info **info[hwmon_max];
  21};
  22
  23static inline u64 __pow10(u8 x)
  24{
  25        u64 r = 1;
  26
  27        while (x--)
  28                r *= 10;
  29
  30        return r;
  31}
  32
  33static int scmi_hwmon_scale(const struct scmi_sensor_info *sensor, u64 *value)
  34{
  35        int scale = sensor->scale;
  36        u64 f;
  37
  38        switch (sensor->type) {
  39        case TEMPERATURE_C:
  40        case VOLTAGE:
  41        case CURRENT:
  42                scale += 3;
  43                break;
  44        case POWER:
  45        case ENERGY:
  46                scale += 6;
  47                break;
  48        default:
  49                break;
  50        }
  51
  52        if (scale == 0)
  53                return 0;
  54
  55        if (abs(scale) > 19)
  56                return -E2BIG;
  57
  58        f = __pow10(abs(scale));
  59        if (scale > 0)
  60                *value *= f;
  61        else
  62                *value = div64_u64(*value, f);
  63
  64        return 0;
  65}
  66
  67static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
  68                           u32 attr, int channel, long *val)
  69{
  70        int ret;
  71        u64 value;
  72        const struct scmi_sensor_info *sensor;
  73        struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
  74
  75        sensor = *(scmi_sensors->info[type] + channel);
  76        ret = sensor_ops->reading_get(scmi_sensors->ph, sensor->id, &value);
  77        if (ret)
  78                return ret;
  79
  80        ret = scmi_hwmon_scale(sensor, &value);
  81        if (!ret)
  82                *val = value;
  83
  84        return ret;
  85}
  86
  87static int
  88scmi_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
  89                       u32 attr, int channel, const char **str)
  90{
  91        const struct scmi_sensor_info *sensor;
  92        struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
  93
  94        sensor = *(scmi_sensors->info[type] + channel);
  95        *str = sensor->name;
  96
  97        return 0;
  98}
  99
 100static umode_t
 101scmi_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
 102                      u32 attr, int channel)
 103{
 104        const struct scmi_sensor_info *sensor;
 105        const struct scmi_sensors *scmi_sensors = drvdata;
 106
 107        sensor = *(scmi_sensors->info[type] + channel);
 108        if (sensor)
 109                return 0444;
 110
 111        return 0;
 112}
 113
 114static const struct hwmon_ops scmi_hwmon_ops = {
 115        .is_visible = scmi_hwmon_is_visible,
 116        .read = scmi_hwmon_read,
 117        .read_string = scmi_hwmon_read_string,
 118};
 119
 120static struct hwmon_chip_info scmi_chip_info = {
 121        .ops = &scmi_hwmon_ops,
 122        .info = NULL,
 123};
 124
 125static int scmi_hwmon_add_chan_info(struct hwmon_channel_info *scmi_hwmon_chan,
 126                                    struct device *dev, int num,
 127                                    enum hwmon_sensor_types type, u32 config)
 128{
 129        int i;
 130        u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL);
 131
 132        if (!cfg)
 133                return -ENOMEM;
 134
 135        scmi_hwmon_chan->type = type;
 136        scmi_hwmon_chan->config = cfg;
 137        for (i = 0; i < num; i++, cfg++)
 138                *cfg = config;
 139
 140        return 0;
 141}
 142
 143static enum hwmon_sensor_types scmi_types[] = {
 144        [TEMPERATURE_C] = hwmon_temp,
 145        [VOLTAGE] = hwmon_in,
 146        [CURRENT] = hwmon_curr,
 147        [POWER] = hwmon_power,
 148        [ENERGY] = hwmon_energy,
 149};
 150
 151static u32 hwmon_attributes[hwmon_max] = {
 152        [hwmon_chip] = HWMON_C_REGISTER_TZ,
 153        [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL,
 154        [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL,
 155        [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL,
 156        [hwmon_power] = HWMON_P_INPUT | HWMON_P_LABEL,
 157        [hwmon_energy] = HWMON_E_INPUT | HWMON_E_LABEL,
 158};
 159
 160static int scmi_hwmon_probe(struct scmi_device *sdev)
 161{
 162        int i, idx;
 163        u16 nr_sensors;
 164        enum hwmon_sensor_types type;
 165        struct scmi_sensors *scmi_sensors;
 166        const struct scmi_sensor_info *sensor;
 167        int nr_count[hwmon_max] = {0}, nr_types = 0;
 168        const struct hwmon_chip_info *chip_info;
 169        struct device *hwdev, *dev = &sdev->dev;
 170        struct hwmon_channel_info *scmi_hwmon_chan;
 171        const struct hwmon_channel_info **ptr_scmi_ci;
 172        const struct scmi_handle *handle = sdev->handle;
 173        struct scmi_protocol_handle *ph;
 174
 175        if (!handle)
 176                return -ENODEV;
 177
 178        sensor_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_SENSOR, &ph);
 179        if (IS_ERR(sensor_ops))
 180                return PTR_ERR(sensor_ops);
 181
 182        nr_sensors = sensor_ops->count_get(ph);
 183        if (!nr_sensors)
 184                return -EIO;
 185
 186        scmi_sensors = devm_kzalloc(dev, sizeof(*scmi_sensors), GFP_KERNEL);
 187        if (!scmi_sensors)
 188                return -ENOMEM;
 189
 190        scmi_sensors->ph = ph;
 191
 192        for (i = 0; i < nr_sensors; i++) {
 193                sensor = sensor_ops->info_get(ph, i);
 194                if (!sensor)
 195                        return -EINVAL;
 196
 197                switch (sensor->type) {
 198                case TEMPERATURE_C:
 199                case VOLTAGE:
 200                case CURRENT:
 201                case POWER:
 202                case ENERGY:
 203                        type = scmi_types[sensor->type];
 204                        if (!nr_count[type])
 205                                nr_types++;
 206                        nr_count[type]++;
 207                        break;
 208                }
 209        }
 210
 211        if (nr_count[hwmon_temp]) {
 212                nr_count[hwmon_chip]++;
 213                nr_types++;
 214        }
 215
 216        scmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*scmi_hwmon_chan),
 217                                       GFP_KERNEL);
 218        if (!scmi_hwmon_chan)
 219                return -ENOMEM;
 220
 221        ptr_scmi_ci = devm_kcalloc(dev, nr_types + 1, sizeof(*ptr_scmi_ci),
 222                                   GFP_KERNEL);
 223        if (!ptr_scmi_ci)
 224                return -ENOMEM;
 225
 226        scmi_chip_info.info = ptr_scmi_ci;
 227        chip_info = &scmi_chip_info;
 228
 229        for (type = 0; type < hwmon_max; type++) {
 230                if (!nr_count[type])
 231                        continue;
 232
 233                scmi_hwmon_add_chan_info(scmi_hwmon_chan, dev, nr_count[type],
 234                                         type, hwmon_attributes[type]);
 235                *ptr_scmi_ci++ = scmi_hwmon_chan++;
 236
 237                scmi_sensors->info[type] =
 238                        devm_kcalloc(dev, nr_count[type],
 239                                     sizeof(*scmi_sensors->info), GFP_KERNEL);
 240                if (!scmi_sensors->info[type])
 241                        return -ENOMEM;
 242        }
 243
 244        for (i = nr_sensors - 1; i >= 0 ; i--) {
 245                sensor = sensor_ops->info_get(ph, i);
 246                if (!sensor)
 247                        continue;
 248
 249                switch (sensor->type) {
 250                case TEMPERATURE_C:
 251                case VOLTAGE:
 252                case CURRENT:
 253                case POWER:
 254                case ENERGY:
 255                        type = scmi_types[sensor->type];
 256                        idx = --nr_count[type];
 257                        *(scmi_sensors->info[type] + idx) = sensor;
 258                        break;
 259                }
 260        }
 261
 262        hwdev = devm_hwmon_device_register_with_info(dev, "scmi_sensors",
 263                                                     scmi_sensors, chip_info,
 264                                                     NULL);
 265
 266        return PTR_ERR_OR_ZERO(hwdev);
 267}
 268
 269static const struct scmi_device_id scmi_id_table[] = {
 270        { SCMI_PROTOCOL_SENSOR, "hwmon" },
 271        { },
 272};
 273MODULE_DEVICE_TABLE(scmi, scmi_id_table);
 274
 275static struct scmi_driver scmi_hwmon_drv = {
 276        .name           = "scmi-hwmon",
 277        .probe          = scmi_hwmon_probe,
 278        .id_table       = scmi_id_table,
 279};
 280module_scmi_driver(scmi_hwmon_drv);
 281
 282MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
 283MODULE_DESCRIPTION("ARM SCMI HWMON interface driver");
 284MODULE_LICENSE("GPL v2");
 285