2 * System Control and Power Interface(SCPI) based hwmon sensor driver
4 * Copyright (C) 2015 ARM Ltd.
5 * Punit Agrawal <punit.agrawal@arm.com>
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.
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.
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>
26 struct scpi_sensor_info info
;
27 struct device_attribute dev_attr_input
;
28 struct device_attribute dev_attr_label
;
33 struct scpi_thermal_zone
{
34 struct list_head list
;
36 struct scpi_sensors
*scpi_sensors
;
37 struct thermal_zone_device
*tzd
;
41 struct scpi_ops
*scpi_ops
;
42 struct sensor_data
*data
;
43 struct list_head thermal_zones
;
44 struct attribute
**attrs
;
45 struct attribute_group group
;
46 const struct attribute_group
*groups
[2];
49 static int scpi_read_temp(void *dev
, int *temp
)
51 struct scpi_thermal_zone
*zone
= dev
;
52 struct scpi_sensors
*scpi_sensors
= zone
->scpi_sensors
;
53 struct scpi_ops
*scpi_ops
= scpi_sensors
->scpi_ops
;
54 struct sensor_data
*sensor
= &scpi_sensors
->data
[zone
->sensor_id
];
58 ret
= scpi_ops
->sensor_get_value(sensor
->info
.sensor_id
, &value
);
66 /* hwmon callback functions */
68 scpi_show_sensor(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
70 struct scpi_sensors
*scpi_sensors
= dev_get_drvdata(dev
);
71 struct scpi_ops
*scpi_ops
= scpi_sensors
->scpi_ops
;
72 struct sensor_data
*sensor
;
76 sensor
= container_of(attr
, struct sensor_data
, dev_attr_input
);
78 ret
= scpi_ops
->sensor_get_value(sensor
->info
.sensor_id
, &value
);
82 return sprintf(buf
, "%llu\n", value
);
86 scpi_show_label(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
88 struct sensor_data
*sensor
;
90 sensor
= container_of(attr
, struct sensor_data
, dev_attr_label
);
92 return sprintf(buf
, "%s\n", sensor
->info
.name
);
96 unregister_thermal_zones(struct platform_device
*pdev
,
97 struct scpi_sensors
*scpi_sensors
)
99 struct list_head
*pos
;
101 list_for_each(pos
, &scpi_sensors
->thermal_zones
) {
102 struct scpi_thermal_zone
*zone
;
104 zone
= list_entry(pos
, struct scpi_thermal_zone
, list
);
105 thermal_zone_of_sensor_unregister(&pdev
->dev
, zone
->tzd
);
109 static struct thermal_zone_of_device_ops scpi_sensor_ops
= {
110 .get_temp
= scpi_read_temp
,
113 static int scpi_hwmon_probe(struct platform_device
*pdev
)
116 int num_temp
= 0, num_volt
= 0, num_current
= 0, num_power
= 0;
118 struct scpi_ops
*scpi_ops
;
119 struct device
*hwdev
, *dev
= &pdev
->dev
;
120 struct scpi_sensors
*scpi_sensors
;
123 scpi_ops
= get_scpi_ops();
125 return -EPROBE_DEFER
;
127 ret
= scpi_ops
->sensor_get_capability(&nr_sensors
);
134 scpi_sensors
= devm_kzalloc(dev
, sizeof(*scpi_sensors
), GFP_KERNEL
);
138 scpi_sensors
->data
= devm_kcalloc(dev
, nr_sensors
,
139 sizeof(*scpi_sensors
->data
), GFP_KERNEL
);
140 if (!scpi_sensors
->data
)
143 scpi_sensors
->attrs
= devm_kcalloc(dev
, (nr_sensors
* 2) + 1,
144 sizeof(*scpi_sensors
->attrs
), GFP_KERNEL
);
145 if (!scpi_sensors
->attrs
)
148 scpi_sensors
->scpi_ops
= scpi_ops
;
150 for (i
= 0, idx
= 0; i
< nr_sensors
; i
++) {
151 struct sensor_data
*sensor
= &scpi_sensors
->data
[idx
];
153 ret
= scpi_ops
->sensor_get_info(i
, &sensor
->info
);
157 switch (sensor
->info
.class) {
159 snprintf(sensor
->input
, sizeof(sensor
->input
),
160 "temp%d_input", num_temp
+ 1);
161 snprintf(sensor
->label
, sizeof(sensor
->input
),
162 "temp%d_label", num_temp
+ 1);
166 snprintf(sensor
->input
, sizeof(sensor
->input
),
167 "in%d_input", num_volt
);
168 snprintf(sensor
->label
, sizeof(sensor
->input
),
169 "in%d_label", num_volt
);
173 snprintf(sensor
->input
, sizeof(sensor
->input
),
174 "curr%d_input", num_current
+ 1);
175 snprintf(sensor
->label
, sizeof(sensor
->input
),
176 "curr%d_label", num_current
+ 1);
180 snprintf(sensor
->input
, sizeof(sensor
->input
),
181 "power%d_input", num_power
+ 1);
182 snprintf(sensor
->label
, sizeof(sensor
->input
),
183 "power%d_label", num_power
+ 1);
187 snprintf(sensor
->input
, sizeof(sensor
->input
),
188 "energy%d_input", num_energy
+ 1);
189 snprintf(sensor
->label
, sizeof(sensor
->input
),
190 "energy%d_label", num_energy
+ 1);
197 sensor
->dev_attr_input
.attr
.mode
= S_IRUGO
;
198 sensor
->dev_attr_input
.show
= scpi_show_sensor
;
199 sensor
->dev_attr_input
.attr
.name
= sensor
->input
;
201 sensor
->dev_attr_label
.attr
.mode
= S_IRUGO
;
202 sensor
->dev_attr_label
.show
= scpi_show_label
;
203 sensor
->dev_attr_label
.attr
.name
= sensor
->label
;
205 scpi_sensors
->attrs
[idx
<< 1] = &sensor
->dev_attr_input
.attr
;
206 scpi_sensors
->attrs
[(idx
<< 1) + 1] = &sensor
->dev_attr_label
.attr
;
208 sysfs_attr_init(scpi_sensors
->attrs
[idx
<< 1]);
209 sysfs_attr_init(scpi_sensors
->attrs
[(idx
<< 1) + 1]);
213 scpi_sensors
->group
.attrs
= scpi_sensors
->attrs
;
214 scpi_sensors
->groups
[0] = &scpi_sensors
->group
;
216 platform_set_drvdata(pdev
, scpi_sensors
);
218 hwdev
= devm_hwmon_device_register_with_groups(dev
,
219 "scpi_sensors", scpi_sensors
, scpi_sensors
->groups
);
222 return PTR_ERR(hwdev
);
225 * Register the temperature sensors with the thermal framework
226 * to allow their usage in setting up the thermal zones from
229 * NOTE: Not all temperature sensors maybe used for thermal
232 INIT_LIST_HEAD(&scpi_sensors
->thermal_zones
);
233 for (i
= 0; i
< nr_sensors
; i
++) {
234 struct sensor_data
*sensor
= &scpi_sensors
->data
[i
];
235 struct scpi_thermal_zone
*zone
;
237 if (sensor
->info
.class != TEMPERATURE
)
240 zone
= devm_kzalloc(dev
, sizeof(*zone
), GFP_KERNEL
);
247 zone
->scpi_sensors
= scpi_sensors
;
248 zone
->tzd
= thermal_zone_of_sensor_register(dev
,
249 sensor
->info
.sensor_id
, zone
, &scpi_sensor_ops
);
251 * The call to thermal_zone_of_sensor_register returns
252 * an error for sensors that are not associated with
253 * any thermal zones or if the thermal subsystem is
256 if (IS_ERR(zone
->tzd
)) {
257 devm_kfree(dev
, zone
);
260 list_add(&zone
->list
, &scpi_sensors
->thermal_zones
);
266 unregister_thermal_zones(pdev
, scpi_sensors
);
270 static int scpi_hwmon_remove(struct platform_device
*pdev
)
272 struct scpi_sensors
*scpi_sensors
= platform_get_drvdata(pdev
);
274 unregister_thermal_zones(pdev
, scpi_sensors
);
279 static const struct of_device_id scpi_of_match
[] = {
280 {.compatible
= "arm,scpi-sensors"},
284 static struct platform_driver scpi_hwmon_platdrv
= {
286 .name
= "scpi-hwmon",
287 .owner
= THIS_MODULE
,
288 .of_match_table
= scpi_of_match
,
290 .probe
= scpi_hwmon_probe
,
291 .remove
= scpi_hwmon_remove
,
293 module_platform_driver(scpi_hwmon_platdrv
);
295 MODULE_AUTHOR("Punit Agrawal <punit.agrawal@arm.com>");
296 MODULE_DESCRIPTION("ARM SCPI HWMON interface driver");
297 MODULE_LICENSE("GPL v2");