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
show_voltage(struct device * dev,struct device_attribute * attr,char * buf)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
show_chip_temp(struct device * dev,struct device_attribute * attr,char * buf)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
show_label(struct device * dev,struct device_attribute * attr,char * buf)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
wm831x_hwmon_probe(struct platform_device * pdev)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