1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Copyright (C) 2012 ARM Limited |
5 | */ |
6 | |
7 | #define DRVNAME "vexpress-hwmon" |
8 | #define pr_fmt(fmt) DRVNAME ": " fmt |
9 | |
10 | #include <linux/device.h> |
11 | #include <linux/err.h> |
12 | #include <linux/hwmon.h> |
13 | #include <linux/hwmon-sysfs.h> |
14 | #include <linux/module.h> |
15 | #include <linux/of.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/vexpress.h> |
18 | |
19 | struct vexpress_hwmon_data { |
20 | struct device *hwmon_dev; |
21 | struct regmap *reg; |
22 | }; |
23 | |
24 | static ssize_t vexpress_hwmon_label_show(struct device *dev, |
25 | struct device_attribute *dev_attr, char *buffer) |
26 | { |
27 | const char *label = of_get_property(node: dev->of_node, name: "label" , NULL); |
28 | |
29 | return sysfs_emit(buf: buffer, fmt: "%s\n" , label); |
30 | } |
31 | |
32 | static ssize_t vexpress_hwmon_u32_show(struct device *dev, |
33 | struct device_attribute *dev_attr, char *buffer) |
34 | { |
35 | struct vexpress_hwmon_data *data = dev_get_drvdata(dev); |
36 | int err; |
37 | u32 value; |
38 | |
39 | err = regmap_read(map: data->reg, reg: 0, val: &value); |
40 | if (err) |
41 | return err; |
42 | |
43 | return sysfs_emit(buf: buffer, fmt: "%u\n" , value / |
44 | to_sensor_dev_attr(dev_attr)->index); |
45 | } |
46 | |
47 | static ssize_t vexpress_hwmon_u64_show(struct device *dev, |
48 | struct device_attribute *dev_attr, char *buffer) |
49 | { |
50 | struct vexpress_hwmon_data *data = dev_get_drvdata(dev); |
51 | int err; |
52 | u32 value_hi, value_lo; |
53 | |
54 | err = regmap_read(map: data->reg, reg: 0, val: &value_lo); |
55 | if (err) |
56 | return err; |
57 | |
58 | err = regmap_read(map: data->reg, reg: 1, val: &value_hi); |
59 | if (err) |
60 | return err; |
61 | |
62 | return sysfs_emit(buf: buffer, fmt: "%llu\n" , |
63 | div_u64(dividend: ((u64)value_hi << 32) | value_lo, |
64 | to_sensor_dev_attr(dev_attr)->index)); |
65 | } |
66 | |
67 | static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj, |
68 | struct attribute *attr, int index) |
69 | { |
70 | struct device *dev = kobj_to_dev(kobj); |
71 | struct device_attribute *dev_attr = container_of(attr, |
72 | struct device_attribute, attr); |
73 | |
74 | if (dev_attr->show == vexpress_hwmon_label_show && |
75 | !of_get_property(node: dev->of_node, name: "label" , NULL)) |
76 | return 0; |
77 | |
78 | return attr->mode; |
79 | } |
80 | |
81 | struct vexpress_hwmon_type { |
82 | const char *name; |
83 | const struct attribute_group **attr_groups; |
84 | }; |
85 | |
86 | #if !defined(CONFIG_REGULATOR_VEXPRESS) |
87 | static DEVICE_ATTR(in1_label, 0444, vexpress_hwmon_label_show, NULL); |
88 | static SENSOR_DEVICE_ATTR_RO(in1_input, vexpress_hwmon_u32, 1000); |
89 | static struct attribute *vexpress_hwmon_attrs_volt[] = { |
90 | &dev_attr_in1_label.attr, |
91 | &sensor_dev_attr_in1_input.dev_attr.attr, |
92 | NULL |
93 | }; |
94 | static struct attribute_group vexpress_hwmon_group_volt = { |
95 | .is_visible = vexpress_hwmon_attr_is_visible, |
96 | .attrs = vexpress_hwmon_attrs_volt, |
97 | }; |
98 | static struct vexpress_hwmon_type vexpress_hwmon_volt = { |
99 | .name = "vexpress_volt" , |
100 | .attr_groups = (const struct attribute_group *[]) { |
101 | &vexpress_hwmon_group_volt, |
102 | NULL, |
103 | }, |
104 | }; |
105 | #endif |
106 | |
107 | static DEVICE_ATTR(curr1_label, 0444, vexpress_hwmon_label_show, NULL); |
108 | static SENSOR_DEVICE_ATTR_RO(curr1_input, vexpress_hwmon_u32, 1000); |
109 | static struct attribute *vexpress_hwmon_attrs_amp[] = { |
110 | &dev_attr_curr1_label.attr, |
111 | &sensor_dev_attr_curr1_input.dev_attr.attr, |
112 | NULL |
113 | }; |
114 | static struct attribute_group vexpress_hwmon_group_amp = { |
115 | .is_visible = vexpress_hwmon_attr_is_visible, |
116 | .attrs = vexpress_hwmon_attrs_amp, |
117 | }; |
118 | static struct vexpress_hwmon_type vexpress_hwmon_amp = { |
119 | .name = "vexpress_amp" , |
120 | .attr_groups = (const struct attribute_group *[]) { |
121 | &vexpress_hwmon_group_amp, |
122 | NULL |
123 | }, |
124 | }; |
125 | |
126 | static DEVICE_ATTR(temp1_label, 0444, vexpress_hwmon_label_show, NULL); |
127 | static SENSOR_DEVICE_ATTR_RO(temp1_input, vexpress_hwmon_u32, 1000); |
128 | static struct attribute *vexpress_hwmon_attrs_temp[] = { |
129 | &dev_attr_temp1_label.attr, |
130 | &sensor_dev_attr_temp1_input.dev_attr.attr, |
131 | NULL |
132 | }; |
133 | static struct attribute_group vexpress_hwmon_group_temp = { |
134 | .is_visible = vexpress_hwmon_attr_is_visible, |
135 | .attrs = vexpress_hwmon_attrs_temp, |
136 | }; |
137 | static struct vexpress_hwmon_type vexpress_hwmon_temp = { |
138 | .name = "vexpress_temp" , |
139 | .attr_groups = (const struct attribute_group *[]) { |
140 | &vexpress_hwmon_group_temp, |
141 | NULL |
142 | }, |
143 | }; |
144 | |
145 | static DEVICE_ATTR(power1_label, 0444, vexpress_hwmon_label_show, NULL); |
146 | static SENSOR_DEVICE_ATTR_RO(power1_input, vexpress_hwmon_u32, 1); |
147 | static struct attribute *vexpress_hwmon_attrs_power[] = { |
148 | &dev_attr_power1_label.attr, |
149 | &sensor_dev_attr_power1_input.dev_attr.attr, |
150 | NULL |
151 | }; |
152 | static struct attribute_group vexpress_hwmon_group_power = { |
153 | .is_visible = vexpress_hwmon_attr_is_visible, |
154 | .attrs = vexpress_hwmon_attrs_power, |
155 | }; |
156 | static struct vexpress_hwmon_type vexpress_hwmon_power = { |
157 | .name = "vexpress_power" , |
158 | .attr_groups = (const struct attribute_group *[]) { |
159 | &vexpress_hwmon_group_power, |
160 | NULL |
161 | }, |
162 | }; |
163 | |
164 | static DEVICE_ATTR(energy1_label, 0444, vexpress_hwmon_label_show, NULL); |
165 | static SENSOR_DEVICE_ATTR_RO(energy1_input, vexpress_hwmon_u64, 1); |
166 | static struct attribute *vexpress_hwmon_attrs_energy[] = { |
167 | &dev_attr_energy1_label.attr, |
168 | &sensor_dev_attr_energy1_input.dev_attr.attr, |
169 | NULL |
170 | }; |
171 | static struct attribute_group vexpress_hwmon_group_energy = { |
172 | .is_visible = vexpress_hwmon_attr_is_visible, |
173 | .attrs = vexpress_hwmon_attrs_energy, |
174 | }; |
175 | static struct vexpress_hwmon_type vexpress_hwmon_energy = { |
176 | .name = "vexpress_energy" , |
177 | .attr_groups = (const struct attribute_group *[]) { |
178 | &vexpress_hwmon_group_energy, |
179 | NULL |
180 | }, |
181 | }; |
182 | |
183 | static const struct of_device_id vexpress_hwmon_of_match[] = { |
184 | #if !defined(CONFIG_REGULATOR_VEXPRESS) |
185 | { |
186 | .compatible = "arm,vexpress-volt" , |
187 | .data = &vexpress_hwmon_volt, |
188 | }, |
189 | #endif |
190 | { |
191 | .compatible = "arm,vexpress-amp" , |
192 | .data = &vexpress_hwmon_amp, |
193 | }, { |
194 | .compatible = "arm,vexpress-temp" , |
195 | .data = &vexpress_hwmon_temp, |
196 | }, { |
197 | .compatible = "arm,vexpress-power" , |
198 | .data = &vexpress_hwmon_power, |
199 | }, { |
200 | .compatible = "arm,vexpress-energy" , |
201 | .data = &vexpress_hwmon_energy, |
202 | }, |
203 | {} |
204 | }; |
205 | MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match); |
206 | |
207 | static int vexpress_hwmon_probe(struct platform_device *pdev) |
208 | { |
209 | struct vexpress_hwmon_data *data; |
210 | const struct vexpress_hwmon_type *type; |
211 | |
212 | data = devm_kzalloc(dev: &pdev->dev, size: sizeof(*data), GFP_KERNEL); |
213 | if (!data) |
214 | return -ENOMEM; |
215 | platform_set_drvdata(pdev, data); |
216 | |
217 | type = of_device_get_match_data(dev: &pdev->dev); |
218 | if (!type) |
219 | return -ENODEV; |
220 | |
221 | data->reg = devm_regmap_init_vexpress_config(dev: &pdev->dev); |
222 | if (IS_ERR(ptr: data->reg)) |
223 | return PTR_ERR(ptr: data->reg); |
224 | |
225 | data->hwmon_dev = devm_hwmon_device_register_with_groups(dev: &pdev->dev, |
226 | name: type->name, drvdata: data, groups: type->attr_groups); |
227 | |
228 | return PTR_ERR_OR_ZERO(ptr: data->hwmon_dev); |
229 | } |
230 | |
231 | static struct platform_driver vexpress_hwmon_driver = { |
232 | .probe = vexpress_hwmon_probe, |
233 | .driver = { |
234 | .name = DRVNAME, |
235 | .of_match_table = vexpress_hwmon_of_match, |
236 | }, |
237 | }; |
238 | |
239 | module_platform_driver(vexpress_hwmon_driver); |
240 | |
241 | MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>" ); |
242 | MODULE_DESCRIPTION("Versatile Express hwmon sensors driver" ); |
243 | MODULE_LICENSE("GPL" ); |
244 | MODULE_ALIAS("platform:vexpress-hwmon" ); |
245 | |