1 /* 2 * System Control and Power Interface(SCPI) based hwmon sensor driver 3 * 4 * Copyright (C) 2015 ARM Ltd. 5 * Punit Agrawal <punit.agrawal@arm.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any 12 * kind, whether express or implied; without even the implied warranty 13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17 #include <linux/hwmon.h> 18 #include <linux/module.h> 19 #include <linux/platform_device.h> 20 #include <linux/scpi_protocol.h> 21 #include <linux/slab.h> 22 #include <linux/sysfs.h> 23 #include <linux/thermal.h> 24 25 struct sensor_data { 26 struct scpi_sensor_info info; 27 struct device_attribute dev_attr_input; 28 struct device_attribute dev_attr_label; 29 char input[20]; 30 char label[20]; 31 }; 32 33 struct scpi_thermal_zone { 34 int sensor_id; 35 struct scpi_sensors *scpi_sensors; 36 }; 37 38 struct scpi_sensors { 39 struct scpi_ops *scpi_ops; 40 struct sensor_data *data; 41 struct list_head thermal_zones; 42 struct attribute **attrs; 43 struct attribute_group group; 44 const struct attribute_group *groups[2]; 45 }; 46 47 static int scpi_read_temp(void *dev, int *temp) 48 { 49 struct scpi_thermal_zone *zone = dev; 50 struct scpi_sensors *scpi_sensors = zone->scpi_sensors; 51 struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops; 52 struct sensor_data *sensor = &scpi_sensors->data[zone->sensor_id]; 53 u64 value; 54 int ret; 55 56 ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value); 57 if (ret) 58 return ret; 59 60 *temp = value; 61 return 0; 62 } 63 64 /* hwmon callback functions */ 65 static ssize_t 66 scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf) 67 { 68 struct scpi_sensors *scpi_sensors = dev_get_drvdata(dev); 69 struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops; 70 struct sensor_data *sensor; 71 u64 value; 72 int ret; 73 74 sensor = container_of(attr, struct sensor_data, dev_attr_input); 75 76 ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value); 77 if (ret) 78 return ret; 79 80 return sprintf(buf, "%llu\n", value); 81 } 82 83 static ssize_t 84 scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf) 85 { 86 struct sensor_data *sensor; 87 88 sensor = container_of(attr, struct sensor_data, dev_attr_label); 89 90 return sprintf(buf, "%s\n", sensor->info.name); 91 } 92 93 static struct thermal_zone_of_device_ops scpi_sensor_ops = { 94 .get_temp = scpi_read_temp, 95 }; 96 97 static int scpi_hwmon_probe(struct platform_device *pdev) 98 { 99 u16 nr_sensors, i; 100 int num_temp = 0, num_volt = 0, num_current = 0, num_power = 0; 101 int num_energy = 0; 102 struct scpi_ops *scpi_ops; 103 struct device *hwdev, *dev = &pdev->dev; 104 struct scpi_sensors *scpi_sensors; 105 int idx, ret; 106 107 scpi_ops = get_scpi_ops(); 108 if (!scpi_ops) 109 return -EPROBE_DEFER; 110 111 ret = scpi_ops->sensor_get_capability(&nr_sensors); 112 if (ret) 113 return ret; 114 115 if (!nr_sensors) 116 return -ENODEV; 117 118 scpi_sensors = devm_kzalloc(dev, sizeof(*scpi_sensors), GFP_KERNEL); 119 if (!scpi_sensors) 120 return -ENOMEM; 121 122 scpi_sensors->data = devm_kcalloc(dev, nr_sensors, 123 sizeof(*scpi_sensors->data), GFP_KERNEL); 124 if (!scpi_sensors->data) 125 return -ENOMEM; 126 127 scpi_sensors->attrs = devm_kcalloc(dev, (nr_sensors * 2) + 1, 128 sizeof(*scpi_sensors->attrs), GFP_KERNEL); 129 if (!scpi_sensors->attrs) 130 return -ENOMEM; 131 132 scpi_sensors->scpi_ops = scpi_ops; 133 134 for (i = 0, idx = 0; i < nr_sensors; i++) { 135 struct sensor_data *sensor = &scpi_sensors->data[idx]; 136 137 ret = scpi_ops->sensor_get_info(i, &sensor->info); 138 if (ret) 139 return ret; 140 141 switch (sensor->info.class) { 142 case TEMPERATURE: 143 snprintf(sensor->input, sizeof(sensor->input), 144 "temp%d_input", num_temp + 1); 145 snprintf(sensor->label, sizeof(sensor->input), 146 "temp%d_label", num_temp + 1); 147 num_temp++; 148 break; 149 case VOLTAGE: 150 snprintf(sensor->input, sizeof(sensor->input), 151 "in%d_input", num_volt); 152 snprintf(sensor->label, sizeof(sensor->input), 153 "in%d_label", num_volt); 154 num_volt++; 155 break; 156 case CURRENT: 157 snprintf(sensor->input, sizeof(sensor->input), 158 "curr%d_input", num_current + 1); 159 snprintf(sensor->label, sizeof(sensor->input), 160 "curr%d_label", num_current + 1); 161 num_current++; 162 break; 163 case POWER: 164 snprintf(sensor->input, sizeof(sensor->input), 165 "power%d_input", num_power + 1); 166 snprintf(sensor->label, sizeof(sensor->input), 167 "power%d_label", num_power + 1); 168 num_power++; 169 break; 170 case ENERGY: 171 snprintf(sensor->input, sizeof(sensor->input), 172 "energy%d_input", num_energy + 1); 173 snprintf(sensor->label, sizeof(sensor->input), 174 "energy%d_label", num_energy + 1); 175 num_energy++; 176 break; 177 default: 178 continue; 179 } 180 181 sensor->dev_attr_input.attr.mode = S_IRUGO; 182 sensor->dev_attr_input.show = scpi_show_sensor; 183 sensor->dev_attr_input.attr.name = sensor->input; 184 185 sensor->dev_attr_label.attr.mode = S_IRUGO; 186 sensor->dev_attr_label.show = scpi_show_label; 187 sensor->dev_attr_label.attr.name = sensor->label; 188 189 scpi_sensors->attrs[idx << 1] = &sensor->dev_attr_input.attr; 190 scpi_sensors->attrs[(idx << 1) + 1] = &sensor->dev_attr_label.attr; 191 192 sysfs_attr_init(scpi_sensors->attrs[idx << 1]); 193 sysfs_attr_init(scpi_sensors->attrs[(idx << 1) + 1]); 194 idx++; 195 } 196 197 scpi_sensors->group.attrs = scpi_sensors->attrs; 198 scpi_sensors->groups[0] = &scpi_sensors->group; 199 200 platform_set_drvdata(pdev, scpi_sensors); 201 202 hwdev = devm_hwmon_device_register_with_groups(dev, 203 "scpi_sensors", scpi_sensors, scpi_sensors->groups); 204 205 if (IS_ERR(hwdev)) 206 return PTR_ERR(hwdev); 207 208 /* 209 * Register the temperature sensors with the thermal framework 210 * to allow their usage in setting up the thermal zones from 211 * device tree. 212 * 213 * NOTE: Not all temperature sensors maybe used for thermal 214 * control 215 */ 216 INIT_LIST_HEAD(&scpi_sensors->thermal_zones); 217 for (i = 0; i < nr_sensors; i++) { 218 struct sensor_data *sensor = &scpi_sensors->data[i]; 219 struct thermal_zone_device *z; 220 struct scpi_thermal_zone *zone; 221 222 if (sensor->info.class != TEMPERATURE) 223 continue; 224 225 zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL); 226 if (!zone) 227 return -ENOMEM; 228 229 zone->sensor_id = i; 230 zone->scpi_sensors = scpi_sensors; 231 z = devm_thermal_zone_of_sensor_register(dev, 232 sensor->info.sensor_id, 233 zone, 234 &scpi_sensor_ops); 235 /* 236 * The call to thermal_zone_of_sensor_register returns 237 * an error for sensors that are not associated with 238 * any thermal zones or if the thermal subsystem is 239 * not configured. 240 */ 241 if (IS_ERR(z)) { 242 devm_kfree(dev, zone); 243 continue; 244 } 245 } 246 247 return 0; 248 } 249 250 static const struct of_device_id scpi_of_match[] = { 251 {.compatible = "arm,scpi-sensors"}, 252 {}, 253 }; 254 MODULE_DEVICE_TABLE(of, scpi_of_match); 255 256 static struct platform_driver scpi_hwmon_platdrv = { 257 .driver = { 258 .name = "scpi-hwmon", 259 .of_match_table = scpi_of_match, 260 }, 261 .probe = scpi_hwmon_probe, 262 }; 263 module_platform_driver(scpi_hwmon_platdrv); 264 265 MODULE_AUTHOR("Punit Agrawal <punit.agrawal@arm.com>"); 266 MODULE_DESCRIPTION("ARM SCPI HWMON interface driver"); 267 MODULE_LICENSE("GPL v2"); 268