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/of_device.h>
20 #include <linux/platform_device.h>
21 #include <linux/scpi_protocol.h>
22 #include <linux/slab.h>
23 #include <linux/sysfs.h>
24 #include <linux/thermal.h>
28 struct scpi_sensor_info info
;
29 struct device_attribute dev_attr_input
;
30 struct device_attribute dev_attr_label
;
35 struct scpi_thermal_zone
{
37 struct scpi_sensors
*scpi_sensors
;
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 const u32 gxbb_scpi_scale
[] = {
50 [TEMPERATURE
] = 1, /* (celsius) */
51 [VOLTAGE
] = 1000, /* (millivolts) */
52 [CURRENT
] = 1000, /* (milliamperes) */
53 [POWER
] = 1000000, /* (microwatts) */
54 [ENERGY
] = 1000000, /* (microjoules) */
57 static const u32 scpi_scale
[] = {
58 [TEMPERATURE
] = 1000, /* (millicelsius) */
59 [VOLTAGE
] = 1000, /* (millivolts) */
60 [CURRENT
] = 1000, /* (milliamperes) */
61 [POWER
] = 1000000, /* (microwatts) */
62 [ENERGY
] = 1000000, /* (microjoules) */
65 static void scpi_scale_reading(u64
*value
, struct sensor_data
*sensor
)
67 if (scpi_scale
[sensor
->info
.class] != sensor
->scale
) {
68 *value
*= scpi_scale
[sensor
->info
.class];
69 do_div(*value
, sensor
->scale
);
73 static int scpi_read_temp(void *dev
, int *temp
)
75 struct scpi_thermal_zone
*zone
= dev
;
76 struct scpi_sensors
*scpi_sensors
= zone
->scpi_sensors
;
77 struct scpi_ops
*scpi_ops
= scpi_sensors
->scpi_ops
;
78 struct sensor_data
*sensor
= &scpi_sensors
->data
[zone
->sensor_id
];
82 ret
= scpi_ops
->sensor_get_value(sensor
->info
.sensor_id
, &value
);
86 scpi_scale_reading(&value
, sensor
);
92 /* hwmon callback functions */
94 scpi_show_sensor(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
96 struct scpi_sensors
*scpi_sensors
= dev_get_drvdata(dev
);
97 struct scpi_ops
*scpi_ops
= scpi_sensors
->scpi_ops
;
98 struct sensor_data
*sensor
;
102 sensor
= container_of(attr
, struct sensor_data
, dev_attr_input
);
104 ret
= scpi_ops
->sensor_get_value(sensor
->info
.sensor_id
, &value
);
108 scpi_scale_reading(&value
, sensor
);
110 return sprintf(buf
, "%llu\n", value
);
114 scpi_show_label(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
116 struct sensor_data
*sensor
;
118 sensor
= container_of(attr
, struct sensor_data
, dev_attr_label
);
120 return sprintf(buf
, "%s\n", sensor
->info
.name
);
123 static const struct thermal_zone_of_device_ops scpi_sensor_ops
= {
124 .get_temp
= scpi_read_temp
,
127 static const struct of_device_id scpi_of_match
[] = {
128 {.compatible
= "arm,scpi-sensors", .data
= &scpi_scale
},
129 {.compatible
= "amlogic,meson-gxbb-scpi-sensors", .data
= &gxbb_scpi_scale
},
132 MODULE_DEVICE_TABLE(of
, scpi_of_match
);
134 static int scpi_hwmon_probe(struct platform_device
*pdev
)
138 int num_temp
= 0, num_volt
= 0, num_current
= 0, num_power
= 0;
140 struct scpi_ops
*scpi_ops
;
141 struct device
*hwdev
, *dev
= &pdev
->dev
;
142 struct scpi_sensors
*scpi_sensors
;
143 const struct of_device_id
*of_id
;
146 scpi_ops
= get_scpi_ops();
148 return -EPROBE_DEFER
;
150 ret
= scpi_ops
->sensor_get_capability(&nr_sensors
);
157 scpi_sensors
= devm_kzalloc(dev
, sizeof(*scpi_sensors
), GFP_KERNEL
);
161 scpi_sensors
->data
= devm_kcalloc(dev
, nr_sensors
,
162 sizeof(*scpi_sensors
->data
), GFP_KERNEL
);
163 if (!scpi_sensors
->data
)
166 scpi_sensors
->attrs
= devm_kcalloc(dev
, (nr_sensors
* 2) + 1,
167 sizeof(*scpi_sensors
->attrs
), GFP_KERNEL
);
168 if (!scpi_sensors
->attrs
)
171 scpi_sensors
->scpi_ops
= scpi_ops
;
173 of_id
= of_match_device(scpi_of_match
, &pdev
->dev
);
175 dev_err(&pdev
->dev
, "Unable to initialize scpi-hwmon data\n");
180 for (i
= 0, idx
= 0; i
< nr_sensors
; i
++) {
181 struct sensor_data
*sensor
= &scpi_sensors
->data
[idx
];
183 ret
= scpi_ops
->sensor_get_info(i
, &sensor
->info
);
187 switch (sensor
->info
.class) {
189 snprintf(sensor
->input
, sizeof(sensor
->input
),
190 "temp%d_input", num_temp
+ 1);
191 snprintf(sensor
->label
, sizeof(sensor
->input
),
192 "temp%d_label", num_temp
+ 1);
196 snprintf(sensor
->input
, sizeof(sensor
->input
),
197 "in%d_input", num_volt
);
198 snprintf(sensor
->label
, sizeof(sensor
->input
),
199 "in%d_label", num_volt
);
203 snprintf(sensor
->input
, sizeof(sensor
->input
),
204 "curr%d_input", num_current
+ 1);
205 snprintf(sensor
->label
, sizeof(sensor
->input
),
206 "curr%d_label", num_current
+ 1);
210 snprintf(sensor
->input
, sizeof(sensor
->input
),
211 "power%d_input", num_power
+ 1);
212 snprintf(sensor
->label
, sizeof(sensor
->input
),
213 "power%d_label", num_power
+ 1);
217 snprintf(sensor
->input
, sizeof(sensor
->input
),
218 "energy%d_input", num_energy
+ 1);
219 snprintf(sensor
->label
, sizeof(sensor
->input
),
220 "energy%d_label", num_energy
+ 1);
227 sensor
->scale
= scale
[sensor
->info
.class];
229 sensor
->dev_attr_input
.attr
.mode
= S_IRUGO
;
230 sensor
->dev_attr_input
.show
= scpi_show_sensor
;
231 sensor
->dev_attr_input
.attr
.name
= sensor
->input
;
233 sensor
->dev_attr_label
.attr
.mode
= S_IRUGO
;
234 sensor
->dev_attr_label
.show
= scpi_show_label
;
235 sensor
->dev_attr_label
.attr
.name
= sensor
->label
;
237 scpi_sensors
->attrs
[idx
<< 1] = &sensor
->dev_attr_input
.attr
;
238 scpi_sensors
->attrs
[(idx
<< 1) + 1] = &sensor
->dev_attr_label
.attr
;
240 sysfs_attr_init(scpi_sensors
->attrs
[idx
<< 1]);
241 sysfs_attr_init(scpi_sensors
->attrs
[(idx
<< 1) + 1]);
245 scpi_sensors
->group
.attrs
= scpi_sensors
->attrs
;
246 scpi_sensors
->groups
[0] = &scpi_sensors
->group
;
248 platform_set_drvdata(pdev
, scpi_sensors
);
250 hwdev
= devm_hwmon_device_register_with_groups(dev
,
251 "scpi_sensors", scpi_sensors
, scpi_sensors
->groups
);
254 return PTR_ERR(hwdev
);
257 * Register the temperature sensors with the thermal framework
258 * to allow their usage in setting up the thermal zones from
261 * NOTE: Not all temperature sensors maybe used for thermal
264 INIT_LIST_HEAD(&scpi_sensors
->thermal_zones
);
265 for (i
= 0; i
< nr_sensors
; i
++) {
266 struct sensor_data
*sensor
= &scpi_sensors
->data
[i
];
267 struct thermal_zone_device
*z
;
268 struct scpi_thermal_zone
*zone
;
270 if (sensor
->info
.class != TEMPERATURE
)
273 zone
= devm_kzalloc(dev
, sizeof(*zone
), GFP_KERNEL
);
278 zone
->scpi_sensors
= scpi_sensors
;
279 z
= devm_thermal_zone_of_sensor_register(dev
,
280 sensor
->info
.sensor_id
,
284 * The call to thermal_zone_of_sensor_register returns
285 * an error for sensors that are not associated with
286 * any thermal zones or if the thermal subsystem is
290 devm_kfree(dev
, zone
);
298 static struct platform_driver scpi_hwmon_platdrv
= {
300 .name
= "scpi-hwmon",
301 .of_match_table
= scpi_of_match
,
303 .probe
= scpi_hwmon_probe
,
305 module_platform_driver(scpi_hwmon_platdrv
);
307 MODULE_AUTHOR("Punit Agrawal <punit.agrawal@arm.com>");
308 MODULE_DESCRIPTION("ARM SCPI HWMON interface driver");
309 MODULE_LICENSE("GPL v2");