-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Pawel Moll
committed
Oct 16, 2012
1 parent
ddffeb8
commit 48ed887
Showing
5 changed files
with
295 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
Versatile Express hwmon sensors | ||
------------------------------- | ||
|
||
Requires node properties: | ||
- "compatible" value : one of | ||
"arm,vexpress-volt" | ||
"arm,vexpress-amp" | ||
"arm,vexpress-temp" | ||
"arm,vexpress-power" | ||
"arm,vexpress-energy" | ||
- "arm,vexpress-sysreg,func" when controlled via vexpress-sysreg | ||
(see Documentation/devicetree/bindings/arm/vexpress-sysreg.txt | ||
for more details) | ||
|
||
Optional node properties: | ||
- label : string describing the monitored value | ||
|
||
Example: | ||
energy@0 { | ||
compatible = "arm,vexpress-energy"; | ||
arm,vexpress-sysreg,func = <13 0>; | ||
label = "A15 Jcore"; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
Kernel driver vexpress | ||
====================== | ||
|
||
Supported systems: | ||
* ARM Ltd. Versatile Express platform | ||
Prefix: 'vexpress' | ||
Datasheets: | ||
* "Hardware Description" sections of the Technical Reference Manuals | ||
for the Versatile Express boards: | ||
http://infocenter.arm.com/help/topic/com.arm.doc.subset.boards.express/index.html | ||
* Section "4.4.14. System Configuration registers" of the V2M-P1 TRM: | ||
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0447-/index.html | ||
|
||
Author: Pawel Moll | ||
|
||
Description | ||
----------- | ||
|
||
Versatile Express platform (http://www.arm.com/versatileexpress/) is a | ||
reference & prototyping system for ARM Ltd. processors. It can be set up | ||
from a wide range of boards, each of them containing (apart of the main | ||
chip/FPGA) a number of microcontrollers responsible for platform | ||
configuration and control. Theses microcontrollers can also monitor the | ||
board and its environment by a number of internal and external sensors, | ||
providing information about power lines voltages and currents, board | ||
temperature and power usage. Some of them also calculate consumed energy | ||
and provide a cumulative use counter. | ||
|
||
The configuration devices are _not_ memory mapped and must be accessed | ||
via a custom interface, abstracted by the "vexpress_config" API. | ||
|
||
As these devices are non-discoverable, they must be described in a Device | ||
Tree passed to the kernel. Details of the DT binding for them can be found | ||
in Documentation/devicetree/bindings/hwmon/vexpress.txt. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
/* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* Copyright (C) 2012 ARM Limited | ||
*/ | ||
|
||
#define DRVNAME "vexpress-hwmon" | ||
#define pr_fmt(fmt) DRVNAME ": " fmt | ||
|
||
#include <linux/device.h> | ||
#include <linux/err.h> | ||
#include <linux/hwmon.h> | ||
#include <linux/hwmon-sysfs.h> | ||
#include <linux/module.h> | ||
#include <linux/of_device.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/vexpress.h> | ||
|
||
struct vexpress_hwmon_data { | ||
struct device *hwmon_dev; | ||
struct vexpress_config_func *func; | ||
}; | ||
|
||
static ssize_t vexpress_hwmon_name_show(struct device *dev, | ||
struct device_attribute *dev_attr, char *buffer) | ||
{ | ||
const char *compatible = of_get_property(dev->of_node, "compatible", | ||
NULL); | ||
|
||
return sprintf(buffer, "%s\n", compatible); | ||
} | ||
|
||
static ssize_t vexpress_hwmon_label_show(struct device *dev, | ||
struct device_attribute *dev_attr, char *buffer) | ||
{ | ||
const char *label = of_get_property(dev->of_node, "label", NULL); | ||
|
||
if (!label) | ||
return -ENOENT; | ||
|
||
return snprintf(buffer, PAGE_SIZE, "%s\n", label); | ||
} | ||
|
||
static ssize_t vexpress_hwmon_u32_show(struct device *dev, | ||
struct device_attribute *dev_attr, char *buffer) | ||
{ | ||
struct vexpress_hwmon_data *data = dev_get_drvdata(dev); | ||
int err; | ||
u32 value; | ||
|
||
err = vexpress_config_read(data->func, 0, &value); | ||
if (err) | ||
return err; | ||
|
||
return snprintf(buffer, PAGE_SIZE, "%u\n", value / | ||
to_sensor_dev_attr(dev_attr)->index); | ||
} | ||
|
||
static ssize_t vexpress_hwmon_u64_show(struct device *dev, | ||
struct device_attribute *dev_attr, char *buffer) | ||
{ | ||
struct vexpress_hwmon_data *data = dev_get_drvdata(dev); | ||
int err; | ||
u32 value_hi, value_lo; | ||
|
||
err = vexpress_config_read(data->func, 0, &value_lo); | ||
if (err) | ||
return err; | ||
|
||
err = vexpress_config_read(data->func, 1, &value_hi); | ||
if (err) | ||
return err; | ||
|
||
return snprintf(buffer, PAGE_SIZE, "%llu\n", | ||
div_u64(((u64)value_hi << 32) | value_lo, | ||
to_sensor_dev_attr(dev_attr)->index)); | ||
} | ||
|
||
static DEVICE_ATTR(name, S_IRUGO, vexpress_hwmon_name_show, NULL); | ||
|
||
#define VEXPRESS_HWMON_ATTRS(_name, _label_attr, _input_attr) \ | ||
struct attribute *vexpress_hwmon_attrs_##_name[] = { \ | ||
&dev_attr_name.attr, \ | ||
&dev_attr_##_label_attr.attr, \ | ||
&sensor_dev_attr_##_input_attr.dev_attr.attr, \ | ||
NULL \ | ||
} | ||
|
||
#if !defined(CONFIG_REGULATOR_VEXPRESS) | ||
static DEVICE_ATTR(in1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); | ||
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, vexpress_hwmon_u32_show, | ||
NULL, 1000); | ||
static VEXPRESS_HWMON_ATTRS(volt, in1_label, in1_input); | ||
static struct attribute_group vexpress_hwmon_group_volt = { | ||
.attrs = vexpress_hwmon_attrs_volt, | ||
}; | ||
#endif | ||
|
||
static DEVICE_ATTR(curr1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); | ||
static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, vexpress_hwmon_u32_show, | ||
NULL, 1000); | ||
static VEXPRESS_HWMON_ATTRS(amp, curr1_label, curr1_input); | ||
static struct attribute_group vexpress_hwmon_group_amp = { | ||
.attrs = vexpress_hwmon_attrs_amp, | ||
}; | ||
|
||
static DEVICE_ATTR(temp1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); | ||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, vexpress_hwmon_u32_show, | ||
NULL, 1000); | ||
static VEXPRESS_HWMON_ATTRS(temp, temp1_label, temp1_input); | ||
static struct attribute_group vexpress_hwmon_group_temp = { | ||
.attrs = vexpress_hwmon_attrs_temp, | ||
}; | ||
|
||
static DEVICE_ATTR(power1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); | ||
static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, vexpress_hwmon_u32_show, | ||
NULL, 1); | ||
static VEXPRESS_HWMON_ATTRS(power, power1_label, power1_input); | ||
static struct attribute_group vexpress_hwmon_group_power = { | ||
.attrs = vexpress_hwmon_attrs_power, | ||
}; | ||
|
||
static DEVICE_ATTR(energy1_label, S_IRUGO, vexpress_hwmon_label_show, NULL); | ||
static SENSOR_DEVICE_ATTR(energy1_input, S_IRUGO, vexpress_hwmon_u64_show, | ||
NULL, 1); | ||
static VEXPRESS_HWMON_ATTRS(energy, energy1_label, energy1_input); | ||
static struct attribute_group vexpress_hwmon_group_energy = { | ||
.attrs = vexpress_hwmon_attrs_energy, | ||
}; | ||
|
||
static struct of_device_id vexpress_hwmon_of_match[] = { | ||
#if !defined(CONFIG_REGULATOR_VEXPRESS) | ||
{ | ||
.compatible = "arm,vexpress-volt", | ||
.data = &vexpress_hwmon_group_volt, | ||
}, | ||
#endif | ||
{ | ||
.compatible = "arm,vexpress-amp", | ||
.data = &vexpress_hwmon_group_amp, | ||
}, { | ||
.compatible = "arm,vexpress-temp", | ||
.data = &vexpress_hwmon_group_temp, | ||
}, { | ||
.compatible = "arm,vexpress-power", | ||
.data = &vexpress_hwmon_group_power, | ||
}, { | ||
.compatible = "arm,vexpress-energy", | ||
.data = &vexpress_hwmon_group_energy, | ||
}, | ||
{} | ||
}; | ||
MODULE_DEVICE_TABLE(of, vexpress_hwmon_of_match); | ||
|
||
static int vexpress_hwmon_probe(struct platform_device *pdev) | ||
{ | ||
int err; | ||
const struct of_device_id *match; | ||
struct vexpress_hwmon_data *data; | ||
|
||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||
if (!data) | ||
return -ENOMEM; | ||
platform_set_drvdata(pdev, data); | ||
|
||
match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); | ||
if (!match) | ||
return -ENODEV; | ||
|
||
data->func = vexpress_config_func_get_by_dev(&pdev->dev); | ||
if (!data->func) | ||
return -ENODEV; | ||
|
||
err = sysfs_create_group(&pdev->dev.kobj, match->data); | ||
if (err) | ||
goto error; | ||
|
||
data->hwmon_dev = hwmon_device_register(&pdev->dev); | ||
if (IS_ERR(data->hwmon_dev)) { | ||
err = PTR_ERR(data->hwmon_dev); | ||
goto error; | ||
} | ||
|
||
return 0; | ||
|
||
error: | ||
sysfs_remove_group(&pdev->dev.kobj, match->data); | ||
vexpress_config_func_put(data->func); | ||
return err; | ||
} | ||
|
||
static int __devexit vexpress_hwmon_remove(struct platform_device *pdev) | ||
{ | ||
struct vexpress_hwmon_data *data = platform_get_drvdata(pdev); | ||
const struct of_device_id *match; | ||
|
||
hwmon_device_unregister(data->hwmon_dev); | ||
|
||
match = of_match_device(vexpress_hwmon_of_match, &pdev->dev); | ||
sysfs_remove_group(&pdev->dev.kobj, match->data); | ||
|
||
vexpress_config_func_put(data->func); | ||
|
||
return 0; | ||
} | ||
|
||
static struct platform_driver vexpress_hwmon_driver = { | ||
.probe = vexpress_hwmon_probe, | ||
.remove = __devexit_p(vexpress_hwmon_remove), | ||
.driver = { | ||
.name = DRVNAME, | ||
.owner = THIS_MODULE, | ||
.of_match_table = vexpress_hwmon_of_match, | ||
}, | ||
}; | ||
|
||
module_platform_driver(vexpress_hwmon_driver); | ||
|
||
MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>"); | ||
MODULE_DESCRIPTION("Versatile Express hwmon sensors driver"); | ||
MODULE_LICENSE("GPL"); | ||
MODULE_ALIAS("platform:vexpress-hwmon"); |