Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  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.h>
 23#include <linux/of_device.h>
 24#include <linux/platform_device.h>
 25#include <linux/vexpress.h>
 26
 27struct vexpress_hwmon_data {
 28	struct device *hwmon_dev;
 29	struct vexpress_config_func *func;
 30	const char *name;
 31};
 32
 33static ssize_t vexpress_hwmon_name_show(struct device *dev,
 34		struct device_attribute *dev_attr, char *buffer)
 35{
 36	struct vexpress_hwmon_data *data = dev_get_drvdata(dev);
 37
 38	return sprintf(buffer, "%s\n", data->name);
 39}
 40
 41static ssize_t vexpress_hwmon_label_show(struct device *dev,
 42		struct device_attribute *dev_attr, char *buffer)
 43{
 44	const char *label = of_get_property(dev->of_node, "label", NULL);
 45
 46	return snprintf(buffer, PAGE_SIZE, "%s\n", label);
 47}
 48
 49static ssize_t vexpress_hwmon_u32_show(struct device *dev,
 50		struct device_attribute *dev_attr, char *buffer)
 51{
 52	struct vexpress_hwmon_data *data = dev_get_drvdata(dev);
 53	int err;
 54	u32 value;
 55
 56	err = vexpress_config_read(data->func, 0, &value);
 57	if (err)
 58		return err;
 59
 60	return snprintf(buffer, PAGE_SIZE, "%u\n", value /
 61			to_sensor_dev_attr(dev_attr)->index);
 62}
 63
 64static ssize_t vexpress_hwmon_u64_show(struct device *dev,
 65		struct device_attribute *dev_attr, char *buffer)
 66{
 67	struct vexpress_hwmon_data *data = dev_get_drvdata(dev);
 68	int err;
 69	u32 value_hi, value_lo;
 70
 71	err = vexpress_config_read(data->func, 0, &value_lo);
 72	if (err)
 73		return err;
 74
 75	err = vexpress_config_read(data->func, 1, &value_hi);
 76	if (err)
 77		return err;
 78
 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));
 82}
 83
 84static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj,
 85		struct attribute *attr, int index)
 86{
 87	struct device *dev = kobj_to_dev(kobj);
 88	struct device_attribute *dev_attr = container_of(attr,
 89				struct device_attribute, attr);
 90
 91	if (dev_attr->show == vexpress_hwmon_label_show &&
 92			!of_get_property(dev->of_node, "label", NULL))
 93		return 0;
 94
 95	return attr->mode;
 96}
 97
 98static DEVICE_ATTR(name, S_IRUGO, vexpress_hwmon_name_show, NULL);
 99
100#define VEXPRESS_HWMON_ATTRS(_name, _label_attr, _input_attr)	\
101struct 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,		\
105	NULL							\
106}
107
108struct vexpress_hwmon_type {
109	const char *name;
110	const struct attribute_group **attr_groups;
111};
112
113#if !defined(CONFIG_REGULATOR_VEXPRESS)
114static DEVICE_ATTR(in1_label, S_IRUGO, vexpress_hwmon_label_show, NULL);
115static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, vexpress_hwmon_u32_show,
116		NULL, 1000);
117static VEXPRESS_HWMON_ATTRS(volt, in1_label, in1_input);
118static struct attribute_group vexpress_hwmon_group_volt = {
119	.is_visible = vexpress_hwmon_attr_is_visible,
120	.attrs = vexpress_hwmon_attrs_volt,
121};
122static struct vexpress_hwmon_type vexpress_hwmon_volt = {
123	.name = "vexpress_volt",
124	.attr_groups = (const struct attribute_group *[]) {
125		&vexpress_hwmon_group_volt,
126		NULL,
127	},
128};
129#endif
130
131static DEVICE_ATTR(curr1_label, S_IRUGO, vexpress_hwmon_label_show, NULL);
132static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, vexpress_hwmon_u32_show,
133		NULL, 1000);
134static VEXPRESS_HWMON_ATTRS(amp, curr1_label, curr1_input);
135static struct attribute_group vexpress_hwmon_group_amp = {
136	.is_visible = vexpress_hwmon_attr_is_visible,
137	.attrs = vexpress_hwmon_attrs_amp,
138};
139static struct vexpress_hwmon_type vexpress_hwmon_amp = {
140	.name = "vexpress_amp",
141	.attr_groups = (const struct attribute_group *[]) {
142		&vexpress_hwmon_group_amp,
143		NULL
144	},
145};
146
147static DEVICE_ATTR(temp1_label, S_IRUGO, vexpress_hwmon_label_show, NULL);
148static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, vexpress_hwmon_u32_show,
149		NULL, 1000);
150static VEXPRESS_HWMON_ATTRS(temp, temp1_label, temp1_input);
151static struct attribute_group vexpress_hwmon_group_temp = {
152	.is_visible = vexpress_hwmon_attr_is_visible,
153	.attrs = vexpress_hwmon_attrs_temp,
154};
155static struct vexpress_hwmon_type vexpress_hwmon_temp = {
156	.name = "vexpress_temp",
157	.attr_groups = (const struct attribute_group *[]) {
158		&vexpress_hwmon_group_temp,
159		NULL
160	},
161};
162
163static DEVICE_ATTR(power1_label, S_IRUGO, vexpress_hwmon_label_show, NULL);
164static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, vexpress_hwmon_u32_show,
165		NULL, 1);
166static VEXPRESS_HWMON_ATTRS(power, power1_label, power1_input);
167static struct attribute_group vexpress_hwmon_group_power = {
168	.is_visible = vexpress_hwmon_attr_is_visible,
169	.attrs = vexpress_hwmon_attrs_power,
170};
171static struct vexpress_hwmon_type vexpress_hwmon_power = {
172	.name = "vexpress_power",
173	.attr_groups = (const struct attribute_group *[]) {
174		&vexpress_hwmon_group_power,
175		NULL
176	},
177};
178
179static DEVICE_ATTR(energy1_label, S_IRUGO, vexpress_hwmon_label_show, NULL);
180static SENSOR_DEVICE_ATTR(energy1_input, S_IRUGO, vexpress_hwmon_u64_show,
181		NULL, 1);
182static VEXPRESS_HWMON_ATTRS(energy, energy1_label, energy1_input);
183static struct attribute_group vexpress_hwmon_group_energy = {
184	.is_visible = vexpress_hwmon_attr_is_visible,
185	.attrs = vexpress_hwmon_attrs_energy,
186};
187static struct vexpress_hwmon_type vexpress_hwmon_energy = {
188	.name = "vexpress_energy",
189	.attr_groups = (const struct attribute_group *[]) {
190		&vexpress_hwmon_group_energy,
191		NULL
192	},
193};
194
195static struct of_device_id vexpress_hwmon_of_match[] = {
196#if !defined(CONFIG_REGULATOR_VEXPRESS)
197	{
198		.compatible = "arm,vexpress-volt",
199		.data = &vexpress_hwmon_volt,
200	},
201#endif
202	{
203		.compatible = "arm,vexpress-amp",
204		.data = &vexpress_hwmon_amp,
205	}, {
206		.compatible = "arm,vexpress-temp",
207		.data = &vexpress_hwmon_temp,
208	}, {
209		.compatible = "arm,vexpress-power",
210		.data = &vexpress_hwmon_power,
211	}, {
212		.compatible = "arm,vexpress-energy",
213		.data = &vexpress_hwmon_energy,
214	},
215	{}
216};
217MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match);
218
219static int vexpress_hwmon_probe(struct platform_device *pdev)
220{
221	int err;
222	const struct of_device_id *match;
223	struct vexpress_hwmon_data *data;
224	const struct vexpress_hwmon_type *type;
225
226	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
227	if (!data)
228		return -ENOMEM;
229	platform_set_drvdata(pdev, data);
230
231	match = of_match_device(vexpress_hwmon_of_match, &pdev->dev);
232	if (!match)
233		return -ENODEV;
234	type = match->data;
235	data->name = type->name;
236
237	data->func = vexpress_config_func_get_by_dev(&pdev->dev);
238	if (!data->func)
239		return -ENODEV;
240
241	err = sysfs_create_groups(&pdev->dev.kobj, type->attr_groups);
242	if (err)
243		goto error;
244
245	data->hwmon_dev = hwmon_device_register(&pdev->dev);
246	if (IS_ERR(data->hwmon_dev)) {
247		err = PTR_ERR(data->hwmon_dev);
248		goto error;
249	}
250
251	return 0;
252
253error:
254	sysfs_remove_group(&pdev->dev.kobj, match->data);
255	vexpress_config_func_put(data->func);
256	return err;
257}
258
259static int vexpress_hwmon_remove(struct platform_device *pdev)
260{
261	struct vexpress_hwmon_data *data = platform_get_drvdata(pdev);
262	const struct of_device_id *match;
263
264	hwmon_device_unregister(data->hwmon_dev);
265
266	match = of_match_device(vexpress_hwmon_of_match, &pdev->dev);
267	sysfs_remove_group(&pdev->dev.kobj, match->data);
268
269	vexpress_config_func_put(data->func);
270
271	return 0;
272}
273
274static struct platform_driver vexpress_hwmon_driver = {
275	.probe = vexpress_hwmon_probe,
276	.remove = vexpress_hwmon_remove,
277	.driver	= {
278		.name = DRVNAME,
279		.owner = THIS_MODULE,
280		.of_match_table = vexpress_hwmon_of_match,
281	},
282};
283
284module_platform_driver(vexpress_hwmon_driver);
285
286MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>");
287MODULE_DESCRIPTION("Versatile Express hwmon sensors driver");
288MODULE_LICENSE("GPL");
289MODULE_ALIAS("platform:vexpress-hwmon");