2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * Copyright (C) 2012 ARM Limited
14 #define DRVNAME "vexpress-hwmon"
15 #define pr_fmt(fmt) DRVNAME ": " fmt
17 #include <linux/device.h>
18 #include <linux/err.h>
19 #include <linux/hwmon.h>
20 #include <linux/hwmon-sysfs.h>
21 #include <linux/module.h>
23 #include <linux/of_device.h>
24 #include <linux/platform_device.h>
25 #include <linux/vexpress.h>
27 struct vexpress_hwmon_data
{
28 struct device
*hwmon_dev
;
29 struct vexpress_config_func
*func
;
33 static ssize_t
vexpress_hwmon_name_show(struct device
*dev
,
34 struct device_attribute
*dev_attr
, char *buffer
)
36 struct vexpress_hwmon_data
*data
= dev_get_drvdata(dev
);
38 return sprintf(buffer
, "%s\n", data
->name
);
41 static ssize_t
vexpress_hwmon_label_show(struct device
*dev
,
42 struct device_attribute
*dev_attr
, char *buffer
)
44 const char *label
= of_get_property(dev
->of_node
, "label", NULL
);
46 return snprintf(buffer
, PAGE_SIZE
, "%s\n", label
);
49 static ssize_t
vexpress_hwmon_u32_show(struct device
*dev
,
50 struct device_attribute
*dev_attr
, char *buffer
)
52 struct vexpress_hwmon_data
*data
= dev_get_drvdata(dev
);
56 err
= vexpress_config_read(data
->func
, 0, &value
);
60 return snprintf(buffer
, PAGE_SIZE
, "%u\n", value
/
61 to_sensor_dev_attr(dev_attr
)->index
);
64 static ssize_t
vexpress_hwmon_u64_show(struct device
*dev
,
65 struct device_attribute
*dev_attr
, char *buffer
)
67 struct vexpress_hwmon_data
*data
= dev_get_drvdata(dev
);
69 u32 value_hi
, value_lo
;
71 err
= vexpress_config_read(data
->func
, 0, &value_lo
);
75 err
= vexpress_config_read(data
->func
, 1, &value_hi
);
79 return snprintf(buffer
, PAGE_SIZE
, "%llu\n",
80 div_u64(((u64
)value_hi
<< 32) | value_lo
,
81 to_sensor_dev_attr(dev_attr
)->index
));
84 static umode_t
vexpress_hwmon_attr_is_visible(struct kobject
*kobj
,
85 struct attribute
*attr
, int index
)
87 struct device
*dev
= kobj_to_dev(kobj
);
88 struct device_attribute
*dev_attr
= container_of(attr
,
89 struct device_attribute
, attr
);
91 if (dev_attr
->show
== vexpress_hwmon_label_show
&&
92 !of_get_property(dev
->of_node
, "label", NULL
))
98 static DEVICE_ATTR(name
, S_IRUGO
, vexpress_hwmon_name_show
, NULL
);
100 #define VEXPRESS_HWMON_ATTRS(_name, _label_attr, _input_attr) \
101 struct attribute *vexpress_hwmon_attrs_##_name[] = { \
102 &dev_attr_name.attr, \
103 &dev_attr_##_label_attr.attr, \
104 &sensor_dev_attr_##_input_attr.dev_attr.attr, \
108 struct vexpress_hwmon_type
{
110 const struct attribute_group
**attr_groups
;
113 #if !defined(CONFIG_REGULATOR_VEXPRESS)
114 static DEVICE_ATTR(in1_label
, S_IRUGO
, vexpress_hwmon_label_show
, NULL
);
115 static SENSOR_DEVICE_ATTR(in1_input
, S_IRUGO
, vexpress_hwmon_u32_show
,
117 static VEXPRESS_HWMON_ATTRS(volt
, in1_label
, in1_input
);
118 static struct attribute_group vexpress_hwmon_group_volt
= {
119 .is_visible
= vexpress_hwmon_attr_is_visible
,
120 .attrs
= vexpress_hwmon_attrs_volt
,
122 static struct vexpress_hwmon_type vexpress_hwmon_volt
= {
123 .name
= "vexpress_volt",
124 .attr_groups
= (const struct attribute_group
*[]) {
125 &vexpress_hwmon_group_volt
,
131 static DEVICE_ATTR(curr1_label
, S_IRUGO
, vexpress_hwmon_label_show
, NULL
);
132 static SENSOR_DEVICE_ATTR(curr1_input
, S_IRUGO
, vexpress_hwmon_u32_show
,
134 static VEXPRESS_HWMON_ATTRS(amp
, curr1_label
, curr1_input
);
135 static struct attribute_group vexpress_hwmon_group_amp
= {
136 .is_visible
= vexpress_hwmon_attr_is_visible
,
137 .attrs
= vexpress_hwmon_attrs_amp
,
139 static struct vexpress_hwmon_type vexpress_hwmon_amp
= {
140 .name
= "vexpress_amp",
141 .attr_groups
= (const struct attribute_group
*[]) {
142 &vexpress_hwmon_group_amp
,
147 static DEVICE_ATTR(temp1_label
, S_IRUGO
, vexpress_hwmon_label_show
, NULL
);
148 static SENSOR_DEVICE_ATTR(temp1_input
, S_IRUGO
, vexpress_hwmon_u32_show
,
150 static VEXPRESS_HWMON_ATTRS(temp
, temp1_label
, temp1_input
);
151 static struct attribute_group vexpress_hwmon_group_temp
= {
152 .is_visible
= vexpress_hwmon_attr_is_visible
,
153 .attrs
= vexpress_hwmon_attrs_temp
,
155 static struct vexpress_hwmon_type vexpress_hwmon_temp
= {
156 .name
= "vexpress_temp",
157 .attr_groups
= (const struct attribute_group
*[]) {
158 &vexpress_hwmon_group_temp
,
163 static DEVICE_ATTR(power1_label
, S_IRUGO
, vexpress_hwmon_label_show
, NULL
);
164 static SENSOR_DEVICE_ATTR(power1_input
, S_IRUGO
, vexpress_hwmon_u32_show
,
166 static VEXPRESS_HWMON_ATTRS(power
, power1_label
, power1_input
);
167 static struct attribute_group vexpress_hwmon_group_power
= {
168 .is_visible
= vexpress_hwmon_attr_is_visible
,
169 .attrs
= vexpress_hwmon_attrs_power
,
171 static struct vexpress_hwmon_type vexpress_hwmon_power
= {
172 .name
= "vexpress_power",
173 .attr_groups
= (const struct attribute_group
*[]) {
174 &vexpress_hwmon_group_power
,
179 static DEVICE_ATTR(energy1_label
, S_IRUGO
, vexpress_hwmon_label_show
, NULL
);
180 static SENSOR_DEVICE_ATTR(energy1_input
, S_IRUGO
, vexpress_hwmon_u64_show
,
182 static VEXPRESS_HWMON_ATTRS(energy
, energy1_label
, energy1_input
);
183 static struct attribute_group vexpress_hwmon_group_energy
= {
184 .is_visible
= vexpress_hwmon_attr_is_visible
,
185 .attrs
= vexpress_hwmon_attrs_energy
,
187 static struct vexpress_hwmon_type vexpress_hwmon_energy
= {
188 .name
= "vexpress_energy",
189 .attr_groups
= (const struct attribute_group
*[]) {
190 &vexpress_hwmon_group_energy
,
195 static struct of_device_id vexpress_hwmon_of_match
[] = {
196 #if !defined(CONFIG_REGULATOR_VEXPRESS)
198 .compatible
= "arm,vexpress-volt",
199 .data
= &vexpress_hwmon_volt
,
203 .compatible
= "arm,vexpress-amp",
204 .data
= &vexpress_hwmon_amp
,
206 .compatible
= "arm,vexpress-temp",
207 .data
= &vexpress_hwmon_temp
,
209 .compatible
= "arm,vexpress-power",
210 .data
= &vexpress_hwmon_power
,
212 .compatible
= "arm,vexpress-energy",
213 .data
= &vexpress_hwmon_energy
,
217 MODULE_DEVICE_TABLE(of
, vexpress_hwmon_of_match
);
219 static int vexpress_hwmon_probe(struct platform_device
*pdev
)
222 const struct of_device_id
*match
;
223 struct vexpress_hwmon_data
*data
;
224 const struct vexpress_hwmon_type
*type
;
226 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
229 platform_set_drvdata(pdev
, data
);
231 match
= of_match_device(vexpress_hwmon_of_match
, &pdev
->dev
);
235 data
->name
= type
->name
;
237 data
->func
= vexpress_config_func_get_by_dev(&pdev
->dev
);
241 err
= sysfs_create_groups(&pdev
->dev
.kobj
, type
->attr_groups
);
245 data
->hwmon_dev
= hwmon_device_register(&pdev
->dev
);
246 if (IS_ERR(data
->hwmon_dev
)) {
247 err
= PTR_ERR(data
->hwmon_dev
);
254 sysfs_remove_group(&pdev
->dev
.kobj
, match
->data
);
255 vexpress_config_func_put(data
->func
);
259 static int vexpress_hwmon_remove(struct platform_device
*pdev
)
261 struct vexpress_hwmon_data
*data
= platform_get_drvdata(pdev
);
262 const struct of_device_id
*match
;
264 hwmon_device_unregister(data
->hwmon_dev
);
266 match
= of_match_device(vexpress_hwmon_of_match
, &pdev
->dev
);
267 sysfs_remove_group(&pdev
->dev
.kobj
, match
->data
);
269 vexpress_config_func_put(data
->func
);
274 static struct platform_driver vexpress_hwmon_driver
= {
275 .probe
= vexpress_hwmon_probe
,
276 .remove
= vexpress_hwmon_remove
,
279 .owner
= THIS_MODULE
,
280 .of_match_table
= vexpress_hwmon_of_match
,
284 module_platform_driver(vexpress_hwmon_driver
);
286 MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>");
287 MODULE_DESCRIPTION("Versatile Express hwmon sensors driver");
288 MODULE_LICENSE("GPL");
289 MODULE_ALIAS("platform:vexpress-hwmon");