root/drivers/hwmon/scmi-hwmon.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __pow10
  2. scmi_hwmon_scale
  3. scmi_hwmon_read
  4. scmi_hwmon_read_string
  5. scmi_hwmon_is_visible
  6. scmi_hwmon_add_chan_info
  7. scmi_hwmon_probe

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

/* [<][>][^][v][top][bottom][index][help] */