xref: /openbmc/linux/drivers/hwmon/wm831x-hwmon.c (revision a6100f6b)
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 
3208bad5a8SMark Brown struct wm831x_hwmon {
3308bad5a8SMark Brown 	struct wm831x *wm831x;
3408bad5a8SMark Brown 	struct device *classdev;
3508bad5a8SMark Brown };
3608bad5a8SMark Brown 
3708bad5a8SMark Brown static ssize_t show_name(struct device *dev,
3808bad5a8SMark Brown 			 struct device_attribute *attr, char *buf)
3908bad5a8SMark Brown {
4008bad5a8SMark Brown 	return sprintf(buf, "wm831x\n");
4108bad5a8SMark Brown }
4208bad5a8SMark Brown 
43a6100f6bSFrans Meulenbroeks static const char * const input_names[] = {
4408bad5a8SMark Brown 	[WM831X_AUX_SYSVDD]    = "SYSVDD",
4508bad5a8SMark Brown 	[WM831X_AUX_USB]       = "USB",
4608bad5a8SMark Brown 	[WM831X_AUX_BKUP_BATT] = "Backup battery",
4708bad5a8SMark Brown 	[WM831X_AUX_BATT]      = "Battery",
4808bad5a8SMark Brown 	[WM831X_AUX_WALL]      = "WALL",
4908bad5a8SMark Brown 	[WM831X_AUX_CHIP_TEMP] = "PMIC",
5008bad5a8SMark Brown 	[WM831X_AUX_BATT_TEMP] = "Battery",
5108bad5a8SMark Brown };
5208bad5a8SMark Brown 
5308bad5a8SMark Brown 
5408bad5a8SMark Brown static ssize_t show_voltage(struct device *dev,
5508bad5a8SMark Brown 			    struct device_attribute *attr, char *buf)
5608bad5a8SMark Brown {
5708bad5a8SMark Brown 	struct wm831x_hwmon *hwmon = dev_get_drvdata(dev);
5808bad5a8SMark Brown 	int channel = to_sensor_dev_attr(attr)->index;
5908bad5a8SMark Brown 	int ret;
6008bad5a8SMark Brown 
6108bad5a8SMark Brown 	ret = wm831x_auxadc_read_uv(hwmon->wm831x, channel);
6208bad5a8SMark Brown 	if (ret < 0)
6308bad5a8SMark Brown 		return ret;
6408bad5a8SMark Brown 
6508bad5a8SMark Brown 	return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret, 1000));
6608bad5a8SMark Brown }
6708bad5a8SMark Brown 
6808bad5a8SMark Brown static ssize_t show_chip_temp(struct device *dev,
6908bad5a8SMark Brown 			      struct device_attribute *attr, char *buf)
7008bad5a8SMark Brown {
7108bad5a8SMark Brown 	struct wm831x_hwmon *hwmon = dev_get_drvdata(dev);
7208bad5a8SMark Brown 	int channel = to_sensor_dev_attr(attr)->index;
7308bad5a8SMark Brown 	int ret;
7408bad5a8SMark Brown 
7508bad5a8SMark Brown 	ret = wm831x_auxadc_read(hwmon->wm831x, channel);
7608bad5a8SMark Brown 	if (ret < 0)
7708bad5a8SMark Brown 		return ret;
7808bad5a8SMark Brown 
7908bad5a8SMark Brown 	/* Degrees celsius = (512.18-ret) / 1.0983 */
8008bad5a8SMark Brown 	ret = 512180 - (ret * 1000);
8108bad5a8SMark Brown 	ret = DIV_ROUND_CLOSEST(ret * 10000, 10983);
8208bad5a8SMark Brown 
8308bad5a8SMark Brown 	return sprintf(buf, "%d\n", ret);
8408bad5a8SMark Brown }
8508bad5a8SMark Brown 
8608bad5a8SMark Brown static ssize_t show_label(struct device *dev,
8708bad5a8SMark Brown 			  struct device_attribute *attr, char *buf)
8808bad5a8SMark Brown {
8908bad5a8SMark Brown 	int channel = to_sensor_dev_attr(attr)->index;
9008bad5a8SMark Brown 
9108bad5a8SMark Brown 	return sprintf(buf, "%s\n", input_names[channel]);
9208bad5a8SMark Brown }
9308bad5a8SMark Brown 
9408bad5a8SMark Brown #define WM831X_VOLTAGE(id, name) \
9508bad5a8SMark Brown 	static SENSOR_DEVICE_ATTR(in##id##_input, S_IRUGO, show_voltage, \
9608bad5a8SMark Brown 				  NULL, name)
9708bad5a8SMark Brown 
9808bad5a8SMark Brown #define WM831X_NAMED_VOLTAGE(id, name) \
9908bad5a8SMark Brown 	WM831X_VOLTAGE(id, name); \
10008bad5a8SMark Brown 	static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label,	\
10108bad5a8SMark Brown 				  NULL, name)
10208bad5a8SMark Brown 
10308bad5a8SMark Brown static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
10408bad5a8SMark Brown 
10508bad5a8SMark Brown WM831X_VOLTAGE(0, WM831X_AUX_AUX1);
10608bad5a8SMark Brown WM831X_VOLTAGE(1, WM831X_AUX_AUX2);
10708bad5a8SMark Brown WM831X_VOLTAGE(2, WM831X_AUX_AUX3);
10808bad5a8SMark Brown WM831X_VOLTAGE(3, WM831X_AUX_AUX4);
10908bad5a8SMark Brown 
11008bad5a8SMark Brown WM831X_NAMED_VOLTAGE(4, WM831X_AUX_SYSVDD);
11108bad5a8SMark Brown WM831X_NAMED_VOLTAGE(5, WM831X_AUX_USB);
11208bad5a8SMark Brown WM831X_NAMED_VOLTAGE(6, WM831X_AUX_BATT);
11308bad5a8SMark Brown WM831X_NAMED_VOLTAGE(7, WM831X_AUX_WALL);
11408bad5a8SMark Brown WM831X_NAMED_VOLTAGE(8, WM831X_AUX_BKUP_BATT);
11508bad5a8SMark Brown 
11608bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_chip_temp, NULL,
11708bad5a8SMark Brown 			  WM831X_AUX_CHIP_TEMP);
11808bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL,
11908bad5a8SMark Brown 			  WM831X_AUX_CHIP_TEMP);
12008bad5a8SMark Brown /* Report as a voltage since conversion depends on external components
12108bad5a8SMark Brown  * and that's what the ABI wants. */
12208bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_voltage, NULL,
12308bad5a8SMark Brown 			  WM831X_AUX_BATT_TEMP);
12408bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL,
12508bad5a8SMark Brown 			  WM831X_AUX_BATT_TEMP);
12608bad5a8SMark Brown 
12708bad5a8SMark Brown static struct attribute *wm831x_attributes[] = {
12808bad5a8SMark Brown 	&dev_attr_name.attr,
12908bad5a8SMark Brown 
13008bad5a8SMark Brown 	&sensor_dev_attr_in0_input.dev_attr.attr,
13108bad5a8SMark Brown 	&sensor_dev_attr_in1_input.dev_attr.attr,
13208bad5a8SMark Brown 	&sensor_dev_attr_in2_input.dev_attr.attr,
13308bad5a8SMark Brown 	&sensor_dev_attr_in3_input.dev_attr.attr,
13408bad5a8SMark Brown 
13508bad5a8SMark Brown 	&sensor_dev_attr_in4_input.dev_attr.attr,
13608bad5a8SMark Brown 	&sensor_dev_attr_in4_label.dev_attr.attr,
13708bad5a8SMark Brown 	&sensor_dev_attr_in5_input.dev_attr.attr,
13808bad5a8SMark Brown 	&sensor_dev_attr_in5_label.dev_attr.attr,
13908bad5a8SMark Brown 	&sensor_dev_attr_in6_input.dev_attr.attr,
14008bad5a8SMark Brown 	&sensor_dev_attr_in6_label.dev_attr.attr,
14108bad5a8SMark Brown 	&sensor_dev_attr_in7_input.dev_attr.attr,
14208bad5a8SMark Brown 	&sensor_dev_attr_in7_label.dev_attr.attr,
14308bad5a8SMark Brown 	&sensor_dev_attr_in8_input.dev_attr.attr,
14408bad5a8SMark Brown 	&sensor_dev_attr_in8_label.dev_attr.attr,
14508bad5a8SMark Brown 
14608bad5a8SMark Brown 	&sensor_dev_attr_temp1_input.dev_attr.attr,
14708bad5a8SMark Brown 	&sensor_dev_attr_temp1_label.dev_attr.attr,
14808bad5a8SMark Brown 	&sensor_dev_attr_temp2_input.dev_attr.attr,
14908bad5a8SMark Brown 	&sensor_dev_attr_temp2_label.dev_attr.attr,
15008bad5a8SMark Brown 
15108bad5a8SMark Brown 	NULL
15208bad5a8SMark Brown };
15308bad5a8SMark Brown 
15408bad5a8SMark Brown static const struct attribute_group wm831x_attr_group = {
15508bad5a8SMark Brown 	.attrs	= wm831x_attributes,
15608bad5a8SMark Brown };
15708bad5a8SMark Brown 
15808bad5a8SMark Brown static int __devinit wm831x_hwmon_probe(struct platform_device *pdev)
15908bad5a8SMark Brown {
16008bad5a8SMark Brown 	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
16108bad5a8SMark Brown 	struct wm831x_hwmon *hwmon;
16208bad5a8SMark Brown 	int ret;
16308bad5a8SMark Brown 
16408bad5a8SMark Brown 	hwmon = kzalloc(sizeof(struct wm831x_hwmon), GFP_KERNEL);
16508bad5a8SMark Brown 	if (!hwmon)
16608bad5a8SMark Brown 		return -ENOMEM;
16708bad5a8SMark Brown 
16808bad5a8SMark Brown 	hwmon->wm831x = wm831x;
16908bad5a8SMark Brown 
17008bad5a8SMark Brown 	ret = sysfs_create_group(&pdev->dev.kobj, &wm831x_attr_group);
17108bad5a8SMark Brown 	if (ret)
17208bad5a8SMark Brown 		goto err;
17308bad5a8SMark Brown 
17408bad5a8SMark Brown 	hwmon->classdev = hwmon_device_register(&pdev->dev);
17508bad5a8SMark Brown 	if (IS_ERR(hwmon->classdev)) {
17608bad5a8SMark Brown 		ret = PTR_ERR(hwmon->classdev);
17708bad5a8SMark Brown 		goto err_sysfs;
17808bad5a8SMark Brown 	}
17908bad5a8SMark Brown 
18008bad5a8SMark Brown 	platform_set_drvdata(pdev, hwmon);
18108bad5a8SMark Brown 
18208bad5a8SMark Brown 	return 0;
18308bad5a8SMark Brown 
18408bad5a8SMark Brown err_sysfs:
18508bad5a8SMark Brown 	sysfs_remove_group(&pdev->dev.kobj, &wm831x_attr_group);
18608bad5a8SMark Brown err:
18708bad5a8SMark Brown 	kfree(hwmon);
18808bad5a8SMark Brown 	return ret;
18908bad5a8SMark Brown }
19008bad5a8SMark Brown 
19108bad5a8SMark Brown static int __devexit wm831x_hwmon_remove(struct platform_device *pdev)
19208bad5a8SMark Brown {
19308bad5a8SMark Brown 	struct wm831x_hwmon *hwmon = platform_get_drvdata(pdev);
19408bad5a8SMark Brown 
19508bad5a8SMark Brown 	hwmon_device_unregister(hwmon->classdev);
19608bad5a8SMark Brown 	sysfs_remove_group(&pdev->dev.kobj, &wm831x_attr_group);
19708bad5a8SMark Brown 	platform_set_drvdata(pdev, NULL);
19808bad5a8SMark Brown 	kfree(hwmon);
19908bad5a8SMark Brown 
20008bad5a8SMark Brown 	return 0;
20108bad5a8SMark Brown }
20208bad5a8SMark Brown 
20308bad5a8SMark Brown static struct platform_driver wm831x_hwmon_driver = {
20408bad5a8SMark Brown 	.probe = wm831x_hwmon_probe,
20508bad5a8SMark Brown 	.remove = __devexit_p(wm831x_hwmon_remove),
20608bad5a8SMark Brown 	.driver = {
20708bad5a8SMark Brown 		.name = "wm831x-hwmon",
20808bad5a8SMark Brown 		.owner = THIS_MODULE,
20908bad5a8SMark Brown 	},
21008bad5a8SMark Brown };
21108bad5a8SMark Brown 
21225a236a5SAxel Lin module_platform_driver(wm831x_hwmon_driver);
21308bad5a8SMark Brown 
21408bad5a8SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
21508bad5a8SMark Brown MODULE_DESCRIPTION("WM831x Hardware Monitoring");
21608bad5a8SMark Brown MODULE_LICENSE("GPL");
21708bad5a8SMark Brown MODULE_ALIAS("platform:wm831x-hwmon");
218