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