xref: /linux/drivers/hwmon/wm831x-hwmon.c (revision 9a70c97b218f5b78b3a265c3ba797b18fa36bd02)
108bad5a8SMark Brown /*
208bad5a8SMark Brown  * drivers/hwmon/wm831x-hwmon.c - Wolfson Microelectronics WM831x PMIC
308bad5a8SMark Brown  *                                hardware monitoring features.
408bad5a8SMark Brown  *
508bad5a8SMark Brown  * Copyright (C) 2009 Wolfson Microelectronics plc
608bad5a8SMark Brown  *
708bad5a8SMark Brown  * This program is free software; you can redistribute it and/or modify it
808bad5a8SMark Brown  * under the terms of the GNU General Public License v2 as published by the
908bad5a8SMark Brown  * Free Software Foundation.
1008bad5a8SMark Brown  *
1108bad5a8SMark Brown  * This program is distributed in the hope that it will be useful, but WITHOUT
1208bad5a8SMark Brown  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1308bad5a8SMark Brown  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1408bad5a8SMark Brown  * more details.
1508bad5a8SMark Brown  *
1608bad5a8SMark Brown  * You should have received a copy of the GNU General Public License along with
1708bad5a8SMark Brown  * this program; if not, write to the Free Software Foundation, Inc.,
1808bad5a8SMark Brown  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
1908bad5a8SMark Brown  */
2008bad5a8SMark Brown 
2108bad5a8SMark Brown #include <linux/kernel.h>
2208bad5a8SMark Brown #include <linux/module.h>
2308bad5a8SMark Brown #include <linux/platform_device.h>
2408bad5a8SMark Brown #include <linux/err.h>
2508bad5a8SMark Brown #include <linux/hwmon.h>
2608bad5a8SMark Brown #include <linux/hwmon-sysfs.h>
275a0e3ad6STejun Heo #include <linux/slab.h>
2808bad5a8SMark Brown 
2908bad5a8SMark Brown #include <linux/mfd/wm831x/core.h>
3008bad5a8SMark Brown #include <linux/mfd/wm831x/auxadc.h>
3108bad5a8SMark Brown 
32a6100f6bSFrans Meulenbroeks static const char * const input_names[] = {
3308bad5a8SMark Brown 	[WM831X_AUX_SYSVDD]    = "SYSVDD",
3408bad5a8SMark Brown 	[WM831X_AUX_USB]       = "USB",
3508bad5a8SMark Brown 	[WM831X_AUX_BKUP_BATT] = "Backup battery",
3608bad5a8SMark Brown 	[WM831X_AUX_BATT]      = "Battery",
3708bad5a8SMark Brown 	[WM831X_AUX_WALL]      = "WALL",
3808bad5a8SMark Brown 	[WM831X_AUX_CHIP_TEMP] = "PMIC",
3908bad5a8SMark Brown 	[WM831X_AUX_BATT_TEMP] = "Battery",
4008bad5a8SMark Brown };
4108bad5a8SMark Brown 
4208bad5a8SMark Brown static ssize_t show_voltage(struct device *dev,
4308bad5a8SMark Brown 			    struct device_attribute *attr, char *buf)
4408bad5a8SMark Brown {
45*9a70c97bSAxel Lin 	struct wm831x *wm831x = dev_get_drvdata(dev);
4608bad5a8SMark Brown 	int channel = to_sensor_dev_attr(attr)->index;
4708bad5a8SMark Brown 	int ret;
4808bad5a8SMark Brown 
49*9a70c97bSAxel Lin 	ret = wm831x_auxadc_read_uv(wm831x, channel);
5008bad5a8SMark Brown 	if (ret < 0)
5108bad5a8SMark Brown 		return ret;
5208bad5a8SMark Brown 
5308bad5a8SMark Brown 	return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret, 1000));
5408bad5a8SMark Brown }
5508bad5a8SMark Brown 
5608bad5a8SMark Brown static ssize_t show_chip_temp(struct device *dev,
5708bad5a8SMark Brown 			      struct device_attribute *attr, char *buf)
5808bad5a8SMark Brown {
59*9a70c97bSAxel Lin 	struct wm831x *wm831x = dev_get_drvdata(dev);
6008bad5a8SMark Brown 	int channel = to_sensor_dev_attr(attr)->index;
6108bad5a8SMark Brown 	int ret;
6208bad5a8SMark Brown 
63*9a70c97bSAxel Lin 	ret = wm831x_auxadc_read(wm831x, channel);
6408bad5a8SMark Brown 	if (ret < 0)
6508bad5a8SMark Brown 		return ret;
6608bad5a8SMark Brown 
6708bad5a8SMark Brown 	/* Degrees celsius = (512.18-ret) / 1.0983 */
6808bad5a8SMark Brown 	ret = 512180 - (ret * 1000);
6908bad5a8SMark Brown 	ret = DIV_ROUND_CLOSEST(ret * 10000, 10983);
7008bad5a8SMark Brown 
7108bad5a8SMark Brown 	return sprintf(buf, "%d\n", ret);
7208bad5a8SMark Brown }
7308bad5a8SMark Brown 
7408bad5a8SMark Brown static ssize_t show_label(struct device *dev,
7508bad5a8SMark Brown 			  struct device_attribute *attr, char *buf)
7608bad5a8SMark Brown {
7708bad5a8SMark Brown 	int channel = to_sensor_dev_attr(attr)->index;
7808bad5a8SMark Brown 
7908bad5a8SMark Brown 	return sprintf(buf, "%s\n", input_names[channel]);
8008bad5a8SMark Brown }
8108bad5a8SMark Brown 
8208bad5a8SMark Brown #define WM831X_VOLTAGE(id, name) \
8308bad5a8SMark Brown 	static SENSOR_DEVICE_ATTR(in##id##_input, S_IRUGO, show_voltage, \
8408bad5a8SMark Brown 				  NULL, name)
8508bad5a8SMark Brown 
8608bad5a8SMark Brown #define WM831X_NAMED_VOLTAGE(id, name) \
8708bad5a8SMark Brown 	WM831X_VOLTAGE(id, name); \
8808bad5a8SMark Brown 	static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label,	\
8908bad5a8SMark Brown 				  NULL, name)
9008bad5a8SMark Brown 
9108bad5a8SMark Brown WM831X_VOLTAGE(0, WM831X_AUX_AUX1);
9208bad5a8SMark Brown WM831X_VOLTAGE(1, WM831X_AUX_AUX2);
9308bad5a8SMark Brown WM831X_VOLTAGE(2, WM831X_AUX_AUX3);
9408bad5a8SMark Brown WM831X_VOLTAGE(3, WM831X_AUX_AUX4);
9508bad5a8SMark Brown 
9608bad5a8SMark Brown WM831X_NAMED_VOLTAGE(4, WM831X_AUX_SYSVDD);
9708bad5a8SMark Brown WM831X_NAMED_VOLTAGE(5, WM831X_AUX_USB);
9808bad5a8SMark Brown WM831X_NAMED_VOLTAGE(6, WM831X_AUX_BATT);
9908bad5a8SMark Brown WM831X_NAMED_VOLTAGE(7, WM831X_AUX_WALL);
10008bad5a8SMark Brown WM831X_NAMED_VOLTAGE(8, WM831X_AUX_BKUP_BATT);
10108bad5a8SMark Brown 
10208bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_chip_temp, NULL,
10308bad5a8SMark Brown 			  WM831X_AUX_CHIP_TEMP);
10408bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL,
10508bad5a8SMark Brown 			  WM831X_AUX_CHIP_TEMP);
10607de3dfbSGuenter Roeck /*
10707de3dfbSGuenter Roeck  * Report as a voltage since conversion depends on external components
10807de3dfbSGuenter Roeck  * and that's what the ABI wants.
10907de3dfbSGuenter Roeck  */
11008bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_voltage, NULL,
11108bad5a8SMark Brown 			  WM831X_AUX_BATT_TEMP);
11208bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL,
11308bad5a8SMark Brown 			  WM831X_AUX_BATT_TEMP);
11408bad5a8SMark Brown 
115*9a70c97bSAxel Lin static struct attribute *wm831x_attrs[] = {
11608bad5a8SMark Brown 	&sensor_dev_attr_in0_input.dev_attr.attr,
11708bad5a8SMark Brown 	&sensor_dev_attr_in1_input.dev_attr.attr,
11808bad5a8SMark Brown 	&sensor_dev_attr_in2_input.dev_attr.attr,
11908bad5a8SMark Brown 	&sensor_dev_attr_in3_input.dev_attr.attr,
12008bad5a8SMark Brown 
12108bad5a8SMark Brown 	&sensor_dev_attr_in4_input.dev_attr.attr,
12208bad5a8SMark Brown 	&sensor_dev_attr_in4_label.dev_attr.attr,
12308bad5a8SMark Brown 	&sensor_dev_attr_in5_input.dev_attr.attr,
12408bad5a8SMark Brown 	&sensor_dev_attr_in5_label.dev_attr.attr,
12508bad5a8SMark Brown 	&sensor_dev_attr_in6_input.dev_attr.attr,
12608bad5a8SMark Brown 	&sensor_dev_attr_in6_label.dev_attr.attr,
12708bad5a8SMark Brown 	&sensor_dev_attr_in7_input.dev_attr.attr,
12808bad5a8SMark Brown 	&sensor_dev_attr_in7_label.dev_attr.attr,
12908bad5a8SMark Brown 	&sensor_dev_attr_in8_input.dev_attr.attr,
13008bad5a8SMark Brown 	&sensor_dev_attr_in8_label.dev_attr.attr,
13108bad5a8SMark Brown 
13208bad5a8SMark Brown 	&sensor_dev_attr_temp1_input.dev_attr.attr,
13308bad5a8SMark Brown 	&sensor_dev_attr_temp1_label.dev_attr.attr,
13408bad5a8SMark Brown 	&sensor_dev_attr_temp2_input.dev_attr.attr,
13508bad5a8SMark Brown 	&sensor_dev_attr_temp2_label.dev_attr.attr,
13608bad5a8SMark Brown 
13708bad5a8SMark Brown 	NULL
13808bad5a8SMark Brown };
13908bad5a8SMark Brown 
140*9a70c97bSAxel Lin ATTRIBUTE_GROUPS(wm831x);
14108bad5a8SMark Brown 
1426c931ae1SBill Pemberton static int wm831x_hwmon_probe(struct platform_device *pdev)
14308bad5a8SMark Brown {
14408bad5a8SMark Brown 	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
145*9a70c97bSAxel Lin 	struct device *hwmon_dev;
14608bad5a8SMark Brown 
147*9a70c97bSAxel Lin 	hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, "wm831x",
148*9a70c97bSAxel Lin 							   wm831x,
149*9a70c97bSAxel Lin 							   wm831x_groups);
150*9a70c97bSAxel Lin 	return PTR_ERR_OR_ZERO(hwmon_dev);
15108bad5a8SMark Brown }
15208bad5a8SMark Brown 
15308bad5a8SMark Brown static struct platform_driver wm831x_hwmon_driver = {
15408bad5a8SMark Brown 	.probe = wm831x_hwmon_probe,
15508bad5a8SMark Brown 	.driver = {
15608bad5a8SMark Brown 		.name = "wm831x-hwmon",
15708bad5a8SMark Brown 		.owner = THIS_MODULE,
15808bad5a8SMark Brown 	},
15908bad5a8SMark Brown };
16008bad5a8SMark Brown 
16125a236a5SAxel Lin module_platform_driver(wm831x_hwmon_driver);
16208bad5a8SMark Brown 
16308bad5a8SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
16408bad5a8SMark Brown MODULE_DESCRIPTION("WM831x Hardware Monitoring");
16508bad5a8SMark Brown MODULE_LICENSE("GPL");
16608bad5a8SMark Brown MODULE_ALIAS("platform:wm831x-hwmon");
167