diff options
author | Pawel Moll <pawel.moll@arm.com> | 2012-09-17 13:40:09 -0400 |
---|---|---|
committer | Pawel Moll <pawel.moll@arm.com> | 2012-10-16 12:12:35 -0400 |
commit | 48ed8877244637b52aec0a114cdccd8ec26e66b1 (patch) | |
tree | 56e04096ead15bd84d1b768f2630a36c9c832a70 /drivers/hwmon | |
parent | ddffeb8c4d0331609ef2581d84de4d763607bd37 (diff) |
hwmon: Versatile Express hwmon driver
hwmon framework driver for Versatile Express sensors, providing
information about board level voltage (only when regulator driver
is not configured), currents, temperature and power/energy usage.
Labels for the values can be defined as DT properties.
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 8 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/vexpress.c | 229 |
3 files changed, 238 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c4633de64465..db213fe958a5 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -1197,6 +1197,14 @@ config SENSORS_TWL4030_MADC | |||
1197 | This driver can also be built as a module. If so it will be called | 1197 | This driver can also be built as a module. If so it will be called |
1198 | twl4030-madc-hwmon. | 1198 | twl4030-madc-hwmon. |
1199 | 1199 | ||
1200 | config SENSORS_VEXPRESS | ||
1201 | tristate "Versatile Express" | ||
1202 | depends on VEXPRESS_CONFIG | ||
1203 | help | ||
1204 | This driver provides support for hardware sensors available on | ||
1205 | the ARM Ltd's Versatile Express platform. It can provide wide | ||
1206 | range of information like temperature, power, energy. | ||
1207 | |||
1200 | config SENSORS_VIA_CPUTEMP | 1208 | config SENSORS_VIA_CPUTEMP |
1201 | tristate "VIA CPU temperature sensor" | 1209 | tristate "VIA CPU temperature sensor" |
1202 | depends on X86 | 1210 | depends on X86 |
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8d5fcb5e8e9f..aac8b7c619d6 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile | |||
@@ -121,6 +121,7 @@ obj-$(CONFIG_SENSORS_TMP102) += tmp102.o | |||
121 | obj-$(CONFIG_SENSORS_TMP401) += tmp401.o | 121 | obj-$(CONFIG_SENSORS_TMP401) += tmp401.o |
122 | obj-$(CONFIG_SENSORS_TMP421) += tmp421.o | 122 | obj-$(CONFIG_SENSORS_TMP421) += tmp421.o |
123 | obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o | 123 | obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o |
124 | obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress.o | ||
124 | obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o | 125 | obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o |
125 | obj-$(CONFIG_SENSORS_VIA686A) += via686a.o | 126 | obj-$(CONFIG_SENSORS_VIA686A) += via686a.o |
126 | obj-$(CONFIG_SENSORS_VT1211) += vt1211.o | 127 | obj-$(CONFIG_SENSORS_VT1211) += vt1211.o |
diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c new file mode 100644 index 000000000000..59fd1268e58a --- /dev/null +++ b/drivers/hwmon/vexpress.c | |||
@@ -0,0 +1,229 @@ | |||
1 | /* | ||
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. | ||
5 | * | ||
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. | ||
10 | * | ||
11 | * Copyright (C) 2012 ARM Limited | ||
12 | */ | ||
13 | |||
14 | #define DRVNAME "vexpress-hwmon" | ||
15 | #define pr_fmt(fmt) DRVNAME ": " fmt | ||
16 | |||
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> | ||
22 | #include <linux/of_device.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/vexpress.h> | ||
25 | |||
26 | struct vexpress_hwmon_data { | ||
27 | struct device *hwmon_dev; | ||
28 | struct vexpress_config_func *func; | ||
29 | }; | ||
30 | |||
31 | static ssize_t vexpress_hwmon_name_show(struct device *dev, | ||
32 | struct device_attribute *dev_attr, char *buffer) | ||
33 | { | ||
34 | const char *compatible = of_get_property(dev->of_node, "compatible", | ||
35 | NULL); | ||
36 | |||
37 | return sprintf(buffer, "%s\n", compatible); | ||
38 | } | ||
39 | |||
40 | static ssize_t vexpress_hwmon_label_show(struct device *dev, | ||
41 | struct device_attribute *dev_attr, char *buffer) | ||
42 | { | ||
43 | const char *label = of_get_property(dev->of_node, "label", NULL); | ||
44 | |||
45 | if (!label) | ||
46 | return -ENOENT; | ||
47 | |||
48 | return snprintf(buffer, PAGE_SIZE, "%s\n", label); | ||
49 | } | ||
50 | |||
51 | static ssize_t vexpress_hwmon_u32_show(struct device *dev, | ||
52 | struct device_attribute *dev_attr, char *buffer) | ||
53 | { | ||
54 | struct vexpress_hwmon_data *data = dev_get_drvdata(dev); | ||
55 | int err; | ||
56 | u32 value; | ||
57 | |||
58 | err = vexpress_config_read(data->func, 0, &value); | ||
59 | if (err) | ||
60 | return err; | ||
61 | |||
62 | return snprintf(buffer, PAGE_SIZE, "%u\n", value / | ||
63 | to_sensor_dev_attr(dev_attr)->index); | ||
64 | } | ||
65 | |||
66 | static ssize_t vexpress_hwmon_u64_show(struct device *dev, | ||
67 | struct device_attribute *dev_attr, char *buffer) | ||
68 | { | ||
69 | struct vexpress_hwmon_data *data = dev_get_drvdata(dev); | ||
70 | int err; | ||
71 | u32 value_hi, value_lo; | ||
72 | |||
73 | err = vexpress_config_read(data->func, 0, &value_lo); | ||
74 | if (err) | ||
75 | return err; | ||
76 | |||
77 | err = vexpress_config_read(data->func, 1, &value_hi); | ||
78 | if (err) | ||
79 | return err; | ||
80 | |||
81 | return snprintf(buffer, PAGE_SIZE, "%llu\n", | ||
82 | div_u64(((u64)value_hi << 32) | value_lo, | ||
83 | to_sensor_dev_attr(dev_attr)->index)); | ||
84 | } | ||
85 | |||
86 | static DEVICE_ATTR(name, S_IRUGO, vexpress_hwmon_name_show, NULL); | ||
87 | |||
88 | #define VEXPRESS_HWMON_ATTRS(_name, _label_attr, _input_attr) \ | ||
89 | struct attribute *vexpress_hwmon_attrs_##_name[] = { \ | ||
90 | &dev_attr_name.attr, \ | ||
91 | &dev_attr_##_label_attr.attr, \ | ||
92 | &sensor_dev_attr_##_input_attr.dev_attr.attr, \ | ||
93 | NULL \ | ||
94 | } | ||
95 | |||
96 | #if !defined(CONFIG_REGULATOR_VEXPRESS) | ||
97 | static DEVICE_ATTR(in1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); | ||
98 | static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, vexpress_hwmon_u32_show, | ||
99 | NULL, 1000); | ||
100 | static VEXPRESS_HWMON_ATTRS(volt, in1_label, in1_input); | ||
101 | static struct attribute_group vexpress_hwmon_group_volt = { | ||
102 | .attrs = vexpress_hwmon_attrs_volt, | ||
103 | }; | ||
104 | #endif | ||
105 | |||
106 | static DEVICE_ATTR(curr1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); | ||
107 | static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, vexpress_hwmon_u32_show, | ||
108 | NULL, 1000); | ||
109 | static VEXPRESS_HWMON_ATTRS(amp, curr1_label, curr1_input); | ||
110 | static struct attribute_group vexpress_hwmon_group_amp = { | ||
111 | .attrs = vexpress_hwmon_attrs_amp, | ||
112 | }; | ||
113 | |||
114 | static DEVICE_ATTR(temp1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); | ||
115 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, vexpress_hwmon_u32_show, | ||
116 | NULL, 1000); | ||
117 | static VEXPRESS_HWMON_ATTRS(temp, temp1_label, temp1_input); | ||
118 | static struct attribute_group vexpress_hwmon_group_temp = { | ||
119 | .attrs = vexpress_hwmon_attrs_temp, | ||
120 | }; | ||
121 | |||
122 | static DEVICE_ATTR(power1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); | ||
123 | static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, vexpress_hwmon_u32_show, | ||
124 | NULL, 1); | ||
125 | static VEXPRESS_HWMON_ATTRS(power, power1_label, power1_input); | ||
126 | static struct attribute_group vexpress_hwmon_group_power = { | ||
127 | .attrs = vexpress_hwmon_attrs_power, | ||
128 | }; | ||
129 | |||
130 | static DEVICE_ATTR(energy1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); | ||
131 | static SENSOR_DEVICE_ATTR(energy1_input, S_IRUGO, vexpress_hwmon_u64_show, | ||
132 | NULL, 1); | ||
133 | static VEXPRESS_HWMON_ATTRS(energy, energy1_label, energy1_input); | ||
134 | static struct attribute_group vexpress_hwmon_group_energy = { | ||
135 | .attrs = vexpress_hwmon_attrs_energy, | ||
136 | }; | ||
137 | |||
138 | static struct of_device_id vexpress_hwmon_of_match[] = { | ||
139 | #if !defined(CONFIG_REGULATOR_VEXPRESS) | ||
140 | { | ||
141 | .compatible = "arm,vexpress-volt", | ||
142 | .data = &vexpress_hwmon_group_volt, | ||
143 | }, | ||
144 | #endif | ||
145 | { | ||
146 | .compatible = "arm,vexpress-amp", | ||
147 | .data = &vexpress_hwmon_group_amp, | ||
148 | }, { | ||
149 | .compatible = "arm,vexpress-temp", | ||
150 | .data = &vexpress_hwmon_group_temp, | ||
151 | }, { | ||
152 | .compatible = "arm,vexpress-power", | ||
153 | .data = &vexpress_hwmon_group_power, | ||
154 | }, { | ||
155 | .compatible = "arm,vexpress-energy", | ||
156 | .data = &vexpress_hwmon_group_energy, | ||
157 | }, | ||
158 | {} | ||
159 | }; | ||
160 | MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match); | ||
161 | |||
162 | static int vexpress_hwmon_probe(struct platform_device *pdev) | ||
163 | { | ||
164 | int err; | ||
165 | const struct of_device_id *match; | ||
166 | struct vexpress_hwmon_data *data; | ||
167 | |||
168 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||
169 | if (!data) | ||
170 | return -ENOMEM; | ||
171 | platform_set_drvdata(pdev, data); | ||
172 | |||
173 | match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); | ||
174 | if (!match) | ||
175 | return -ENODEV; | ||
176 | |||
177 | data->func = vexpress_config_func_get_by_dev(&pdev->dev); | ||
178 | if (!data->func) | ||
179 | return -ENODEV; | ||
180 | |||
181 | err = sysfs_create_group(&pdev->dev.kobj, match->data); | ||
182 | if (err) | ||
183 | goto error; | ||
184 | |||
185 | data->hwmon_dev = hwmon_device_register(&pdev->dev); | ||
186 | if (IS_ERR(data->hwmon_dev)) { | ||
187 | err = PTR_ERR(data->hwmon_dev); | ||
188 | goto error; | ||
189 | } | ||
190 | |||
191 | return 0; | ||
192 | |||
193 | error: | ||
194 | sysfs_remove_group(&pdev->dev.kobj, match->data); | ||
195 | vexpress_config_func_put(data->func); | ||
196 | return err; | ||
197 | } | ||
198 | |||
199 | static int __devexit vexpress_hwmon_remove(struct platform_device *pdev) | ||
200 | { | ||
201 | struct vexpress_hwmon_data *data = platform_get_drvdata(pdev); | ||
202 | const struct of_device_id *match; | ||
203 | |||
204 | hwmon_device_unregister(data->hwmon_dev); | ||
205 | |||
206 | match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); | ||
207 | sysfs_remove_group(&pdev->dev.kobj, match->data); | ||
208 | |||
209 | vexpress_config_func_put(data->func); | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static struct platform_driver vexpress_hwmon_driver = { | ||
215 | .probe = vexpress_hwmon_probe, | ||
216 | .remove = __devexit_p(vexpress_hwmon_remove), | ||
217 | .driver = { | ||
218 | .name = DRVNAME, | ||
219 | .owner = THIS_MODULE, | ||
220 | .of_match_table = vexpress_hwmon_of_match, | ||
221 | }, | ||
222 | }; | ||
223 | |||
224 | module_platform_driver(vexpress_hwmon_driver); | ||
225 | |||
226 | MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>"); | ||
227 | MODULE_DESCRIPTION("Versatile Express hwmon sensors driver"); | ||
228 | MODULE_LICENSE("GPL"); | ||
229 | MODULE_ALIAS("platform:vexpress-hwmon"); | ||