1/* 2 * drivers/hwmon/wm831x-hwmon.c - Wolfson Microelectronics WM831x PMIC 3 * hardware monitoring features. 4 * 5 * Copyright (C) 2009 Wolfson Microelectronics plc 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License v2 as published by the 9 * Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * this program; if not, write to the Free Software Foundation, Inc., 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21#include <linux/kernel.h> 22#include <linux/module.h> 23#include <linux/platform_device.h> 24#include <linux/err.h> 25#include <linux/hwmon.h> 26#include <linux/hwmon-sysfs.h> 27#include <linux/slab.h> 28 29#include <linux/mfd/wm831x/core.h> 30#include <linux/mfd/wm831x/auxadc.h> 31 32static const char * const input_names[] = { 33 [WM831X_AUX_SYSVDD] = "SYSVDD", 34 [WM831X_AUX_USB] = "USB", 35 [WM831X_AUX_BKUP_BATT] = "Backup battery", 36 [WM831X_AUX_BATT] = "Battery", 37 [WM831X_AUX_WALL] = "WALL", 38 [WM831X_AUX_CHIP_TEMP] = "PMIC", 39 [WM831X_AUX_BATT_TEMP] = "Battery", 40}; 41 42static ssize_t show_voltage(struct device *dev, 43 struct device_attribute *attr, char *buf) 44{ 45 struct wm831x *wm831x = dev_get_drvdata(dev); 46 int channel = to_sensor_dev_attr(attr)->index; 47 int ret; 48 49 ret = wm831x_auxadc_read_uv(wm831x, channel); 50 if (ret < 0) 51 return ret; 52 53 return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret, 1000)); 54} 55 56static ssize_t show_chip_temp(struct device *dev, 57 struct device_attribute *attr, char *buf) 58{ 59 struct wm831x *wm831x = dev_get_drvdata(dev); 60 int channel = to_sensor_dev_attr(attr)->index; 61 int ret; 62 63 ret = wm831x_auxadc_read(wm831x, channel); 64 if (ret < 0) 65 return ret; 66 67 /* Degrees celsius = (512.18-ret) / 1.0983 */ 68 ret = 512180 - (ret * 1000); 69 ret = DIV_ROUND_CLOSEST(ret * 10000, 10983); 70 71 return sprintf(buf, "%d\n", ret); 72} 73 74static ssize_t show_label(struct device *dev, 75 struct device_attribute *attr, char *buf) 76{ 77 int channel = to_sensor_dev_attr(attr)->index; 78 79 return sprintf(buf, "%s\n", input_names[channel]); 80} 81 82#define WM831X_VOLTAGE(id, name) \ 83 static SENSOR_DEVICE_ATTR(in##id##_input, S_IRUGO, show_voltage, \ 84 NULL, name) 85 86#define WM831X_NAMED_VOLTAGE(id, name) \ 87 WM831X_VOLTAGE(id, name); \ 88 static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \ 89 NULL, name) 90 91WM831X_VOLTAGE(0, WM831X_AUX_AUX1); 92WM831X_VOLTAGE(1, WM831X_AUX_AUX2); 93WM831X_VOLTAGE(2, WM831X_AUX_AUX3); 94WM831X_VOLTAGE(3, WM831X_AUX_AUX4); 95 96WM831X_NAMED_VOLTAGE(4, WM831X_AUX_SYSVDD); 97WM831X_NAMED_VOLTAGE(5, WM831X_AUX_USB); 98WM831X_NAMED_VOLTAGE(6, WM831X_AUX_BATT); 99WM831X_NAMED_VOLTAGE(7, WM831X_AUX_WALL); 100WM831X_NAMED_VOLTAGE(8, WM831X_AUX_BKUP_BATT); 101 102static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_chip_temp, NULL, 103 WM831X_AUX_CHIP_TEMP); 104static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 105 WM831X_AUX_CHIP_TEMP); 106/* 107 * Report as a voltage since conversion depends on external components 108 * and that's what the ABI wants. 109 */ 110static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_voltage, NULL, 111 WM831X_AUX_BATT_TEMP); 112static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 113 WM831X_AUX_BATT_TEMP); 114 115static struct attribute *wm831x_attrs[] = { 116 &sensor_dev_attr_in0_input.dev_attr.attr, 117 &sensor_dev_attr_in1_input.dev_attr.attr, 118 &sensor_dev_attr_in2_input.dev_attr.attr, 119 &sensor_dev_attr_in3_input.dev_attr.attr, 120 121 &sensor_dev_attr_in4_input.dev_attr.attr, 122 &sensor_dev_attr_in4_label.dev_attr.attr, 123 &sensor_dev_attr_in5_input.dev_attr.attr, 124 &sensor_dev_attr_in5_label.dev_attr.attr, 125 &sensor_dev_attr_in6_input.dev_attr.attr, 126 &sensor_dev_attr_in6_label.dev_attr.attr, 127 &sensor_dev_attr_in7_input.dev_attr.attr, 128 &sensor_dev_attr_in7_label.dev_attr.attr, 129 &sensor_dev_attr_in8_input.dev_attr.attr, 130 &sensor_dev_attr_in8_label.dev_attr.attr, 131 132 &sensor_dev_attr_temp1_input.dev_attr.attr, 133 &sensor_dev_attr_temp1_label.dev_attr.attr, 134 &sensor_dev_attr_temp2_input.dev_attr.attr, 135 &sensor_dev_attr_temp2_label.dev_attr.attr, 136 137 NULL 138}; 139 140ATTRIBUTE_GROUPS(wm831x); 141 142static int wm831x_hwmon_probe(struct platform_device *pdev) 143{ 144 struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 145 struct device *hwmon_dev; 146 147 hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, "wm831x", 148 wm831x, 149 wm831x_groups); 150 return PTR_ERR_OR_ZERO(hwmon_dev); 151} 152 153static struct platform_driver wm831x_hwmon_driver = { 154 .probe = wm831x_hwmon_probe, 155 .driver = { 156 .name = "wm831x-hwmon", 157 }, 158}; 159 160module_platform_driver(wm831x_hwmon_driver); 161 162MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 163MODULE_DESCRIPTION("WM831x Hardware Monitoring"); 164MODULE_LICENSE("GPL"); 165MODULE_ALIAS("platform:wm831x-hwmon"); 166