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