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
, "%u\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;
117 struct scpi_ops
*scpi_ops
;
118 struct device
*hwdev
, *dev
= &pdev
->dev
;
119 struct scpi_sensors
*scpi_sensors
;
122 scpi_ops
= get_scpi_ops();
124 return -EPROBE_DEFER
;
126 ret
= scpi_ops
->sensor_get_capability(&nr_sensors
);
133 scpi_sensors
= devm_kzalloc(dev
, sizeof(*scpi_sensors
), GFP_KERNEL
);
137 scpi_sensors
->data
= devm_kcalloc(dev
, nr_sensors
,
138 sizeof(*scpi_sensors
->data
), GFP_KERNEL
);
139 if (!scpi_sensors
->data
)
142 scpi_sensors
->attrs
= devm_kcalloc(dev
, (nr_sensors
* 2) + 1,
143 sizeof(*scpi_sensors
->attrs
), GFP_KERNEL
);
144 if (!scpi_sensors
->attrs
)
147 scpi_sensors
->scpi_ops
= scpi_ops
;
149 for (i
= 0, idx
= 0; i
< nr_sensors
; i
++) {
150 struct sensor_data
*sensor
= &scpi_sensors
->data
[idx
];
152 ret
= scpi_ops
->sensor_get_info(i
, &sensor
->info
);
156 switch (sensor
->info
.class) {
158 snprintf(sensor
->input
, sizeof(sensor
->input
),
159 "temp%d_input", num_temp
+ 1);
160 snprintf(sensor
->label
, sizeof(sensor
->input
),
161 "temp%d_label", num_temp
+ 1);
165 snprintf(sensor
->input
, sizeof(sensor
->input
),
166 "in%d_input", num_volt
);
167 snprintf(sensor
->label
, sizeof(sensor
->input
),
168 "in%d_label", num_volt
);
172 snprintf(sensor
->input
, sizeof(sensor
->input
),
173 "curr%d_input", num_current
+ 1);
174 snprintf(sensor
->label
, sizeof(sensor
->input
),
175 "curr%d_label", num_current
+ 1);
179 snprintf(sensor
->input
, sizeof(sensor
->input
),
180 "power%d_input", num_power
+ 1);
181 snprintf(sensor
->label
, sizeof(sensor
->input
),
182 "power%d_label", num_power
+ 1);
189 sensor
->dev_attr_input
.attr
.mode
= S_IRUGO
;
190 sensor
->dev_attr_input
.show
= scpi_show_sensor
;
191 sensor
->dev_attr_input
.attr
.name
= sensor
->input
;
193 sensor
->dev_attr_label
.attr
.mode
= S_IRUGO
;
194 sensor
->dev_attr_label
.show
= scpi_show_label
;
195 sensor
->dev_attr_label
.attr
.name
= sensor
->label
;
197 scpi_sensors
->attrs
[idx
<< 1] = &sensor
->dev_attr_input
.attr
;
198 scpi_sensors
->attrs
[(idx
<< 1) + 1] = &sensor
->dev_attr_label
.attr
;
200 sysfs_attr_init(scpi_sensors
->attrs
[idx
<< 1]);
201 sysfs_attr_init(scpi_sensors
->attrs
[(idx
<< 1) + 1]);
205 scpi_sensors
->group
.attrs
= scpi_sensors
->attrs
;
206 scpi_sensors
->groups
[0] = &scpi_sensors
->group
;
208 platform_set_drvdata(pdev
, scpi_sensors
);
210 hwdev
= devm_hwmon_device_register_with_groups(dev
,
211 "scpi_sensors", scpi_sensors
, scpi_sensors
->groups
);
214 return PTR_ERR(hwdev
);
217 * Register the temperature sensors with the thermal framework
218 * to allow their usage in setting up the thermal zones from
221 * NOTE: Not all temperature sensors maybe used for thermal
224 INIT_LIST_HEAD(&scpi_sensors
->thermal_zones
);
225 for (i
= 0; i
< nr_sensors
; i
++) {
226 struct sensor_data
*sensor
= &scpi_sensors
->data
[i
];
227 struct scpi_thermal_zone
*zone
;
229 if (sensor
->info
.class != TEMPERATURE
)
232 zone
= devm_kzalloc(dev
, sizeof(*zone
), GFP_KERNEL
);
239 zone
->scpi_sensors
= scpi_sensors
;
240 zone
->tzd
= thermal_zone_of_sensor_register(dev
,
241 sensor
->info
.sensor_id
, zone
, &scpi_sensor_ops
);
243 * The call to thermal_zone_of_sensor_register returns
244 * an error for sensors that are not associated with
245 * any thermal zones or if the thermal subsystem is
248 if (IS_ERR(zone
->tzd
)) {
249 devm_kfree(dev
, zone
);
252 list_add(&zone
->list
, &scpi_sensors
->thermal_zones
);
258 unregister_thermal_zones(pdev
, scpi_sensors
);
262 static int scpi_hwmon_remove(struct platform_device
*pdev
)
264 struct scpi_sensors
*scpi_sensors
= platform_get_drvdata(pdev
);
266 unregister_thermal_zones(pdev
, scpi_sensors
);
271 static const struct of_device_id scpi_of_match
[] = {
272 {.compatible
= "arm,scpi-sensors"},
276 static struct platform_driver scpi_hwmon_platdrv
= {
278 .name
= "scpi-hwmon",
279 .owner
= THIS_MODULE
,
280 .of_match_table
= scpi_of_match
,
282 .probe
= scpi_hwmon_probe
,
283 .remove
= scpi_hwmon_remove
,
285 module_platform_driver(scpi_hwmon_platdrv
);
287 MODULE_AUTHOR("Punit Agrawal <punit.agrawal@arm.com>");
288 MODULE_DESCRIPTION("ARM SCPI HWMON interface driver");
289 MODULE_LICENSE("GPL v2");