[PATCH 3/4] hwmon: Add a simple driver to read the MXS SoC temperature
From: Alexandre Belloni <hidden>
Date: 2013-06-26 08:52:31
Also in:
linux-arm-kernel, lkml
Subsystem:
documentation, hardware monitoring, open firmware and flattened device tree bindings, the rest · Maintainers:
Jonathan Corbet, Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Torvalds
The low resolution ADC of the mxs is able to read an internal temperature sensor, expose that using hwmon. Signed-off-by: Alexandre Belloni <redacted> --- .../devicetree/bindings/hwmon/mxs-cputemp.txt | 18 +++ Documentation/hwmon/mxs-cputemp | 29 +++++ drivers/hwmon/Kconfig | 10 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/mxs-cputemp.c | 132 +++++++++++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/mxs-cputemp.txt create mode 100644 Documentation/hwmon/mxs-cputemp create mode 100644 drivers/hwmon/mxs-cputemp.c
diff --git a/Documentation/devicetree/bindings/hwmon/mxs-cputemp.txt b/Documentation/devicetree/bindings/hwmon/mxs-cputemp.txt
new file mode 100644
index 0000000..7d3ae47
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/mxs-cputemp.txt@@ -0,0 +1,18 @@ +mxs cputemp hwmon sensors +------------------------- + +See: Documentation/hwmon/mxs-cputemp + +Required properties: +- compatible: should be "fsl,mxs-internal-temp" +- io-channels: should list the two adc channels needed to calculate the + temperature +- io-channel-names: should map the previously listed adc channels to the "min" + and "max" value + +Example: + temp { + compatible = "fsl,mxs-internal-temp"; + io-channels = <&lradc 8>, <&lradc 9>; + io-channel-names = "min", "max"; + };
diff --git a/Documentation/hwmon/mxs-cputemp b/Documentation/hwmon/mxs-cputemp
new file mode 100644
index 0000000..6c6201f
--- /dev/null
+++ b/Documentation/hwmon/mxs-cputemp@@ -0,0 +1,29 @@ +Kernel driver mxs-cputemp +========================= + +Supported chips: + * Freescale i.mx28 + Datasheet: i.MX28 Applications Processor Reference Manual, Rev. 1, 2010 + http://cache.freescale.com/files/dsp/doc/ref_manual/MCIMX28RM.pdf + +Author: Alexandre Belloni + +Description +----------- +This driver permits reading the internal die temperature sensor embedded inside +Freescale i.mx28 SoCs. This sensor is read through two channels of the on chip +Low-Resolution ADC. After calculation, the three-sigma error of the temperature +sensor should be within ± 1.5% in degrees Kelvin. Additionally, the temperature +sampling has a three-sigma sample-to-sample variation of 2 degrees Kelvin. If +desired, this error can be removed by oversampling and averaging the temperature +result. + +The formula is: + (Channel9 – Channel8) * Gain_correction/4 + +As recommended by the datasheet, Gain_correction is equal to 1.012. + +sysfs entries +------------- +temp1_input Measured and corrected temperature in millidegrees Celsius +
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0428e8a..2daf794 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig@@ -929,6 +929,16 @@ config SENSORS_MCP3021 This driver can also be built as a module. If so, the module will be called mcp3021. +config SENSORS_MXS_CPU + tristate "MXS internal CPU temperature sensor" + depends on MXS_LRADC + help + If you say yes here you get support for the i.mx28 internal + temperature sensor. + + This driver can also be built as a module. If so, the module + will be called mxs-cputemp + config SENSORS_NCT6775 tristate "Nuvoton NCT6775F and compatibles" depends on !PPC
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index d17d3e6..366c92d 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile@@ -108,6 +108,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MAX6697) += max6697.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o +obj-$(CONFIG_SENSORS_MXS_CPU) += mxs-cputemp.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
diff --git a/drivers/hwmon/mxs-cputemp.c b/drivers/hwmon/mxs-cputemp.c
new file mode 100644
index 0000000..a312fb5
--- /dev/null
+++ b/drivers/hwmon/mxs-cputemp.c@@ -0,0 +1,132 @@ +/* + * Driver for the mxs internal temperature sensor + * + * Copyright 2013 Free Electrons + * + * Licensed under the GPLv2 or later. + */ + +#define DRVNAME "mxs-hwmon" + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/iio/consumer.h> + +#define GAIN_CORRECTION 1012 + +/* The value we calculate from the ADCs is in Kelvins, don't forget to convert + * it to Celsius */ +#define VALUES_TO_MILLIC(min, max) ((max - min) * GAIN_CORRECTION / 4 - 272150) + +struct mxs_hwmon_data { + struct device *hwmon_dev; + struct iio_channel *chan_min; + struct iio_channel *chan_max; +}; + +static int mxs_hwmon_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int err, val_min, val_max; + + struct mxs_hwmon_data *data = dev_get_drvdata(dev); + + err = iio_read_channel_raw(data->chan_min, &val_min); + if (err < 0) + return err; + + err = iio_read_channel_raw(data->chan_max, &val_max); + if (err < 0) + return err; + + return sprintf(buf, "%u\n", VALUES_TO_MILLIC(val_min, val_max)); +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, mxs_hwmon_show_temp, NULL); + +static int mxs_hwmon_probe(struct platform_device *pdev) +{ + int err; + struct mxs_hwmon_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + + err = device_create_file(&pdev->dev, &dev_attr_temp1_input); + if (err) + return err; + + data->chan_min = iio_channel_get(&pdev->dev, "min"); + if (IS_ERR(data->chan_min)) { + err = PTR_ERR(data->chan_min); + goto error_chan_min; + } + + data->chan_max = iio_channel_get(&pdev->dev, "max"); + if (IS_ERR(data->chan_max)) { + err = PTR_ERR(data->chan_max); + goto error_chan_max; + } + + data->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto error_hwmon; + } + + return 0; + +error_hwmon: + iio_channel_release(data->chan_max); +error_chan_max: + iio_channel_release(data->chan_min); +error_chan_min: + device_remove_file(&pdev->dev, &dev_attr_temp1_input); + + return err; +} + +static int mxs_hwmon_remove(struct platform_device *pdev) +{ + struct mxs_hwmon_data *data = platform_get_drvdata(pdev); + + iio_channel_release(data->chan_min); + iio_channel_release(data->chan_max); + hwmon_device_unregister(data->hwmon_dev); + + device_remove_file(&pdev->dev, &dev_attr_temp1_input); + + return 0; +} + +static struct of_device_id mxs_hwmon_of_match[] = { + { .compatible = "fsl,mxs-internal-temp", }, + {} +}; +MODULE_DEVICE_TABLE(of, mxs_hwmon_of_match); + +static struct platform_driver mxs_hwmon_driver = { + .probe = mxs_hwmon_probe, + .remove = mxs_hwmon_remove, + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + .of_match_table = mxs_hwmon_of_match, + }, +}; + +module_platform_driver(mxs_hwmon_driver); + +MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); +MODULE_DESCRIPTION("Freescale i.MX28 hwmon sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mxs-hwmon");
--
1.8.1.2