1*21eb0be9SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 208bad5a8SMark Brown /* 308bad5a8SMark Brown * drivers/hwmon/wm831x-hwmon.c - Wolfson Microelectronics WM831x PMIC 408bad5a8SMark Brown * hardware monitoring features. 508bad5a8SMark Brown * 608bad5a8SMark Brown * Copyright (C) 2009 Wolfson Microelectronics plc 708bad5a8SMark Brown */ 808bad5a8SMark Brown 908bad5a8SMark Brown #include <linux/kernel.h> 1008bad5a8SMark Brown #include <linux/module.h> 1108bad5a8SMark Brown #include <linux/platform_device.h> 1208bad5a8SMark Brown #include <linux/err.h> 1308bad5a8SMark Brown #include <linux/hwmon.h> 1408bad5a8SMark Brown #include <linux/hwmon-sysfs.h> 155a0e3ad6STejun Heo #include <linux/slab.h> 1608bad5a8SMark Brown 1708bad5a8SMark Brown #include <linux/mfd/wm831x/core.h> 1808bad5a8SMark Brown #include <linux/mfd/wm831x/auxadc.h> 1908bad5a8SMark Brown 20a6100f6bSFrans Meulenbroeks static const char * const input_names[] = { 2108bad5a8SMark Brown [WM831X_AUX_SYSVDD] = "SYSVDD", 2208bad5a8SMark Brown [WM831X_AUX_USB] = "USB", 2308bad5a8SMark Brown [WM831X_AUX_BKUP_BATT] = "Backup battery", 2408bad5a8SMark Brown [WM831X_AUX_BATT] = "Battery", 2508bad5a8SMark Brown [WM831X_AUX_WALL] = "WALL", 2608bad5a8SMark Brown [WM831X_AUX_CHIP_TEMP] = "PMIC", 2708bad5a8SMark Brown [WM831X_AUX_BATT_TEMP] = "Battery", 2808bad5a8SMark Brown }; 2908bad5a8SMark Brown 3008bad5a8SMark Brown static ssize_t show_voltage(struct device *dev, 3108bad5a8SMark Brown struct device_attribute *attr, char *buf) 3208bad5a8SMark Brown { 339a70c97bSAxel Lin struct wm831x *wm831x = dev_get_drvdata(dev); 3408bad5a8SMark Brown int channel = to_sensor_dev_attr(attr)->index; 3508bad5a8SMark Brown int ret; 3608bad5a8SMark Brown 379a70c97bSAxel Lin ret = wm831x_auxadc_read_uv(wm831x, channel); 3808bad5a8SMark Brown if (ret < 0) 3908bad5a8SMark Brown return ret; 4008bad5a8SMark Brown 4108bad5a8SMark Brown return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(ret, 1000)); 4208bad5a8SMark Brown } 4308bad5a8SMark Brown 4408bad5a8SMark Brown static ssize_t show_chip_temp(struct device *dev, 4508bad5a8SMark Brown struct device_attribute *attr, char *buf) 4608bad5a8SMark Brown { 479a70c97bSAxel Lin struct wm831x *wm831x = dev_get_drvdata(dev); 4808bad5a8SMark Brown int channel = to_sensor_dev_attr(attr)->index; 4908bad5a8SMark Brown int ret; 5008bad5a8SMark Brown 519a70c97bSAxel Lin ret = wm831x_auxadc_read(wm831x, channel); 5208bad5a8SMark Brown if (ret < 0) 5308bad5a8SMark Brown return ret; 5408bad5a8SMark Brown 5508bad5a8SMark Brown /* Degrees celsius = (512.18-ret) / 1.0983 */ 5608bad5a8SMark Brown ret = 512180 - (ret * 1000); 5708bad5a8SMark Brown ret = DIV_ROUND_CLOSEST(ret * 10000, 10983); 5808bad5a8SMark Brown 5908bad5a8SMark Brown return sprintf(buf, "%d\n", ret); 6008bad5a8SMark Brown } 6108bad5a8SMark Brown 6208bad5a8SMark Brown static ssize_t show_label(struct device *dev, 6308bad5a8SMark Brown struct device_attribute *attr, char *buf) 6408bad5a8SMark Brown { 6508bad5a8SMark Brown int channel = to_sensor_dev_attr(attr)->index; 6608bad5a8SMark Brown 6708bad5a8SMark Brown return sprintf(buf, "%s\n", input_names[channel]); 6808bad5a8SMark Brown } 6908bad5a8SMark Brown 7008bad5a8SMark Brown #define WM831X_VOLTAGE(id, name) \ 7108bad5a8SMark Brown static SENSOR_DEVICE_ATTR(in##id##_input, S_IRUGO, show_voltage, \ 7208bad5a8SMark Brown NULL, name) 7308bad5a8SMark Brown 7408bad5a8SMark Brown #define WM831X_NAMED_VOLTAGE(id, name) \ 7508bad5a8SMark Brown WM831X_VOLTAGE(id, name); \ 7608bad5a8SMark Brown static SENSOR_DEVICE_ATTR(in##id##_label, S_IRUGO, show_label, \ 7708bad5a8SMark Brown NULL, name) 7808bad5a8SMark Brown 7908bad5a8SMark Brown WM831X_VOLTAGE(0, WM831X_AUX_AUX1); 8008bad5a8SMark Brown WM831X_VOLTAGE(1, WM831X_AUX_AUX2); 8108bad5a8SMark Brown WM831X_VOLTAGE(2, WM831X_AUX_AUX3); 8208bad5a8SMark Brown WM831X_VOLTAGE(3, WM831X_AUX_AUX4); 8308bad5a8SMark Brown 8408bad5a8SMark Brown WM831X_NAMED_VOLTAGE(4, WM831X_AUX_SYSVDD); 8508bad5a8SMark Brown WM831X_NAMED_VOLTAGE(5, WM831X_AUX_USB); 8608bad5a8SMark Brown WM831X_NAMED_VOLTAGE(6, WM831X_AUX_BATT); 8708bad5a8SMark Brown WM831X_NAMED_VOLTAGE(7, WM831X_AUX_WALL); 8808bad5a8SMark Brown WM831X_NAMED_VOLTAGE(8, WM831X_AUX_BKUP_BATT); 8908bad5a8SMark Brown 9008bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_chip_temp, NULL, 9108bad5a8SMark Brown WM831X_AUX_CHIP_TEMP); 9208bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 9308bad5a8SMark Brown WM831X_AUX_CHIP_TEMP); 9407de3dfbSGuenter Roeck /* 9507de3dfbSGuenter Roeck * Report as a voltage since conversion depends on external components 9607de3dfbSGuenter Roeck * and that's what the ABI wants. 9707de3dfbSGuenter Roeck */ 9808bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_voltage, NULL, 9908bad5a8SMark Brown WM831X_AUX_BATT_TEMP); 10008bad5a8SMark Brown static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 10108bad5a8SMark Brown WM831X_AUX_BATT_TEMP); 10208bad5a8SMark Brown 1039a70c97bSAxel Lin static struct attribute *wm831x_attrs[] = { 10408bad5a8SMark Brown &sensor_dev_attr_in0_input.dev_attr.attr, 10508bad5a8SMark Brown &sensor_dev_attr_in1_input.dev_attr.attr, 10608bad5a8SMark Brown &sensor_dev_attr_in2_input.dev_attr.attr, 10708bad5a8SMark Brown &sensor_dev_attr_in3_input.dev_attr.attr, 10808bad5a8SMark Brown 10908bad5a8SMark Brown &sensor_dev_attr_in4_input.dev_attr.attr, 11008bad5a8SMark Brown &sensor_dev_attr_in4_label.dev_attr.attr, 11108bad5a8SMark Brown &sensor_dev_attr_in5_input.dev_attr.attr, 11208bad5a8SMark Brown &sensor_dev_attr_in5_label.dev_attr.attr, 11308bad5a8SMark Brown &sensor_dev_attr_in6_input.dev_attr.attr, 11408bad5a8SMark Brown &sensor_dev_attr_in6_label.dev_attr.attr, 11508bad5a8SMark Brown &sensor_dev_attr_in7_input.dev_attr.attr, 11608bad5a8SMark Brown &sensor_dev_attr_in7_label.dev_attr.attr, 11708bad5a8SMark Brown &sensor_dev_attr_in8_input.dev_attr.attr, 11808bad5a8SMark Brown &sensor_dev_attr_in8_label.dev_attr.attr, 11908bad5a8SMark Brown 12008bad5a8SMark Brown &sensor_dev_attr_temp1_input.dev_attr.attr, 12108bad5a8SMark Brown &sensor_dev_attr_temp1_label.dev_attr.attr, 12208bad5a8SMark Brown &sensor_dev_attr_temp2_input.dev_attr.attr, 12308bad5a8SMark Brown &sensor_dev_attr_temp2_label.dev_attr.attr, 12408bad5a8SMark Brown 12508bad5a8SMark Brown NULL 12608bad5a8SMark Brown }; 12708bad5a8SMark Brown 1289a70c97bSAxel Lin ATTRIBUTE_GROUPS(wm831x); 12908bad5a8SMark Brown 1306c931ae1SBill Pemberton static int wm831x_hwmon_probe(struct platform_device *pdev) 13108bad5a8SMark Brown { 13208bad5a8SMark Brown struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 1339a70c97bSAxel Lin struct device *hwmon_dev; 13408bad5a8SMark Brown 1359a70c97bSAxel Lin hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, "wm831x", 1369a70c97bSAxel Lin wm831x, 1379a70c97bSAxel Lin wm831x_groups); 1389a70c97bSAxel Lin return PTR_ERR_OR_ZERO(hwmon_dev); 13908bad5a8SMark Brown } 14008bad5a8SMark Brown 14108bad5a8SMark Brown static struct platform_driver wm831x_hwmon_driver = { 14208bad5a8SMark Brown .probe = wm831x_hwmon_probe, 14308bad5a8SMark Brown .driver = { 14408bad5a8SMark Brown .name = "wm831x-hwmon", 14508bad5a8SMark Brown }, 14608bad5a8SMark Brown }; 14708bad5a8SMark Brown 14825a236a5SAxel Lin module_platform_driver(wm831x_hwmon_driver); 14908bad5a8SMark Brown 15008bad5a8SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 15108bad5a8SMark Brown MODULE_DESCRIPTION("WM831x Hardware Monitoring"); 15208bad5a8SMark Brown MODULE_LICENSE("GPL"); 15308bad5a8SMark Brown MODULE_ALIAS("platform:wm831x-hwmon"); 154