[PATCH 2/3] hwmon: Driver for OMAP3 temperature sensor

Sebastian Reichel sre at kernel.org
Fri Dec 26 04:34:53 PST 2014


OMAP34xx and OMAP36xx processors contain a register in the syscon area,
which can be used to determine the SoCs temperature. This patch provides
a DT based driver for the temperature sensor based on an older driver
written by Peter De Schrijver for the Nokia N900 and N9.

Signed-off-by: Sebastian Reichel <sre at kernel.org>
---
 drivers/hwmon/Kconfig      |   8 ++
 drivers/hwmon/Makefile     |   1 +
 drivers/hwmon/omap3-temp.c | 307 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 316 insertions(+)
 create mode 100644 drivers/hwmon/omap3-temp.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 6529c09..749748d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1135,6 +1135,14 @@ config SENSORS_NCT7802
 	  This driver can also be built as a module.  If so, the module
 	  will be called nct7802.
 
+config SENSORS_OMAP3_TEMP
+	tristate "OMAP3 Temperature Sensor"
+	depends on OF && (ARCH_OMAP3 || COMPILE_TEST)
+	select MFD_SYSCON
+	help
+	  If you say yes here you get support for the temperature sensor
+	  built into OMAP3 processors.
+
 config SENSORS_PCF8591
 	tristate "Philips PCF8591 ADC/DAC"
 	depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 6728064..5a69773 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_SENSORS_NCT6683)	+= nct6683.o
 obj-$(CONFIG_SENSORS_NCT6775)	+= nct6775.o
 obj-$(CONFIG_SENSORS_NCT7802)	+= nct7802.o
 obj-$(CONFIG_SENSORS_NTC_THERMISTOR)	+= ntc_thermistor.o
+obj-$(CONFIG_SENSORS_OMAP3_TEMP)	+= omap3-temp.o
 obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
diff --git a/drivers/hwmon/omap3-temp.c b/drivers/hwmon/omap3-temp.c
new file mode 100644
index 0000000..5c331c5
--- /dev/null
+++ b/drivers/hwmon/omap3-temp.c
@@ -0,0 +1,307 @@
+/*
+ * omap3-temp.c - driver for OMAP34xx and OMAP36xx temperature sensor
+ *
+ * Copyright (c) 2014 Sebastian Reichel <sre at kernel.org>
+ * Copyright (C) 2008, 2009, 2010 Nokia Corporation
+ *
+ * based on Peter De Schrijver's driver for N9
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/clk.h>
+#include <linux/hrtimer.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+
+/* 32.768Khz clock speed in nano seconds */
+#define CLOCK_32K_SPEED_NS 30518
+
+/* minimum delay for EOCZ rise after SOC rise is
+ * 11 cycles of the 32.768Khz clock */
+#define EOCZ_MIN_RISING_DELAY (11 * CLOCK_32K_SPEED_NS)
+
+/* From docs, maximum delay for EOCZ rise after SOC rise is
+ * 14 cycles of the 32.768Khz clock. But after some experiments,
+ * 24 cycles as maximum is safer. */
+#define EOCZ_MAX_RISING_DELAY (24 * CLOCK_32K_SPEED_NS)
+
+/* minimum delay for EOCZ falling is
+ * 36 cycles of the 32.768Khz clock */
+#define EOCZ_MIN_FALLING_DELAY (36 * CLOCK_32K_SPEED_NS)
+
+/* maximum delay for EOCZ falling is
+ * 40 cycles of the 32.768Khz clock */
+#define EOCZ_MAX_FALLING_DELAY (40 * CLOCK_32K_SPEED_NS)
+
+/* temperature register offset in the syscon register area */
+#define SYSCON_TEMP_REG 0x02B4
+
+/* TRM: Table 7-11. ADC Codes Versus Temperature */
+static const int adc_to_temp_3430[] = {
+	-400, -400, -400, -400, -400, -390, -380, -360, -340, -320, -310,
+	-290, -280, -260, -250, -240, -220, -210, -190, -180, -170, -150,
+	-140, -120, -110, -90, -80, -70, -50, -40, -20, -10, 00, 10, 30,
+	40, 50, 70, 80, 100, 110, 130, 140, 150, 170, 180, 200, 210, 220,
+	240, 250, 270, 280, 300, 310, 320, 340, 350, 370, 380, 390, 410, 420,
+	440, 450, 470, 480, 490, 510, 520, 530, 550, 560, 580, 590, 600, 620,
+	630, 650, 660, 670, 690, 700, 720, 730, 740, 760, 770, 790, 800, 810,
+	830, 840, 850, 870, 880, 890, 910, 920, 940, 950, 960, 980, 990, 1000,
+	1020, 1030, 1050, 1060, 1070, 1090, 1100, 1110, 1130, 1140, 1160,
+	1170, 1180, 1200, 1210, 1220, 1240, 1240, 1250, 1250, 1250, 1250,
+	1250};
+
+/* TRM: Table 13-11. ADC Code Versus Temperature */
+static const int adc_to_temp_3630[] = {
+	-400, -400, -400, -400, -400, -400, -400, -400, -400, -400, -400,
+	-400, -400, -400, -380, -350, -340, -320, -300, -280, -260, -240,
+	-220, -200, -185, -170, -150, -135, -120, -100, -80, -65, -50, -35,
+	-15, 0, 20, 35, 50, 65, 85, 100, 120, 135, 150, 170, 190, 210, 230,
+	250, 270, 285, 300, 320, 335, 350, 370, 385, 400, 420, 435, 450, 470,
+	485, 500, 520, 535, 550, 570, 585, 600, 620, 640, 660, 680, 700, 715,
+	735, 750, 770, 785, 800, 820, 835, 850, 870, 885, 900, 920, 935, 950,
+	970, 985, 1000, 1020, 1035, 1050, 1070, 1090, 1110, 1130, 1150, 1170,
+	1185, 1200, 1220, 1235, 1250, 1250, 1250, 1250, 1250, 1250, 1250,
+	1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250,
+	1250, 1250, 1250};
+
+struct omap3_temp_type {
+	const int *adc_to_temp;
+	u8 soc_bit;
+	u8 eocz_bit;
+};
+
+/* TRM: Table 7-228. CONTROL_TEMP_SENSOR */
+static const struct omap3_temp_type omap34xx_temp_type = {
+	.eocz_bit = 7,
+	.soc_bit = 8,
+	.adc_to_temp = adc_to_temp_3430,
+};
+
+/* TRM: Table 13-239. CONTROL_TEMP_SENSOR */
+static const struct omap3_temp_type omap36xx_temp_type = {
+	.eocz_bit = 8,
+	.soc_bit = 9,
+	.adc_to_temp = adc_to_temp_3630,
+};
+
+struct omap3_temp_data {
+	struct device *hwmon_dev;
+	struct regmap *syscon;
+	struct clk *clk_32k;
+	struct omap3_temp_type *hwdata;
+	/* mutex to protect the update procedure while reading from sensor */
+	struct mutex update_lock;
+	const char *name;
+	unsigned long last_updated;
+	u32 temperature;
+	bool valid;
+};
+
+static inline u32 wait_for_eocz(int min_delay, int max_delay, u32 level,
+				struct omap3_temp_data *data)
+{
+	ktime_t timeout, expire;
+	u32 temp_sensor_reg, eocz_mask;
+
+	eocz_mask = BIT(data->hwdata->eocz_bit);
+	level &= 1;
+	level *= eocz_mask;
+
+	expire = ktime_add_ns(ktime_get(), max_delay);
+	timeout = ktime_set(0, min_delay);
+	__set_current_state(TASK_INTERRUPTIBLE);
+	schedule_hrtimeout(&timeout, HRTIMER_MODE_REL);
+	do {
+		regmap_read(data->syscon, SYSCON_TEMP_REG, &temp_sensor_reg);
+		if ((temp_sensor_reg & eocz_mask) == level)
+			break;
+	} while (ktime_us_delta(expire, ktime_get()) > 0);
+
+	return (temp_sensor_reg & eocz_mask) == level;
+}
+
+static int omap3_temp_update(struct omap3_temp_data *data)
+{
+	int e = 0;
+	u32 temp_sensor_reg;
+	u32 soc_mask = BIT(data->hwdata->soc_bit);
+
+	mutex_lock(&data->update_lock);
+
+	if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
+		clk_enable(data->clk_32k);
+
+		regmap_update_bits(data->syscon, SYSCON_TEMP_REG,
+				   soc_mask, soc_mask);
+
+		if (!wait_for_eocz(EOCZ_MIN_RISING_DELAY,
+		    EOCZ_MAX_RISING_DELAY, 1, data)) {
+			e = -EIO;
+			goto err;
+		}
+
+		regmap_update_bits(data->syscon, SYSCON_TEMP_REG, soc_mask, 0);
+
+		if (!wait_for_eocz(EOCZ_MIN_FALLING_DELAY,
+		    EOCZ_MAX_FALLING_DELAY, 0, data)) {
+			e = -EIO;
+			goto err;
+		}
+
+		regmap_read(data->syscon, SYSCON_TEMP_REG, &temp_sensor_reg);
+		data->temperature = temp_sensor_reg & ((1<<7) - 1);
+		data->last_updated = jiffies;
+		data->valid = true;
+
+err:
+		clk_disable(data->clk_32k);
+	}
+
+	mutex_unlock(&data->update_lock);
+	return e;
+}
+
+static ssize_t show_temp(struct device *dev,
+			 struct device_attribute *devattr, char *buf)
+{
+	struct omap3_temp_data *data = dev_get_drvdata(dev);
+	int temp;
+	int ret;
+
+	ret = omap3_temp_update(data);
+	if (ret < 0)
+		return ret;
+
+	temp = data->hwdata->adc_to_temp[data->temperature];
+
+	return sprintf(buf, "%d.%d\n", temp / 10, temp % 10);
+}
+
+static ssize_t show_name(struct device *dev,
+			 struct device_attribute *devattr, char *buf)
+{
+	struct omap3_temp_data *data = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", data->name);
+}
+
+static SENSOR_DEVICE_ATTR_2(temp_input, S_IRUGO, show_temp, NULL, 0, 0);
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static const struct of_device_id omap3_temp_dt_ids[] = {
+	{
+		.compatible = "ti,omap34xx-temperature-sensor",
+		.data = &omap34xx_temp_type,
+	},
+	{
+		.compatible = "ti,omap36xx-temperature-sensor",
+		.data = &omap36xx_temp_type,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, omap3_temp_dt_ids);
+
+static int omap3_temp_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct omap3_temp_data *data;
+	const struct of_device_id *of_id;
+	int err;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	of_id = of_match_device(omap3_temp_dt_ids, &pdev->dev);
+	if (!of_id) {
+		dev_warn(&pdev->dev, "unsupported device!");
+		return -ENODEV;
+	}
+
+	mutex_init(&data->update_lock);
+	data->name = "omap3-temperature";
+
+	data->clk_32k = devm_clk_get(&pdev->dev, "fck");
+	if (IS_ERR(data->clk_32k))
+		return PTR_ERR(data->clk_32k);
+
+	data->hwdata = (struct omap3_temp_type *)of_id->data;
+
+	data->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
+	if (IS_ERR(data->syscon))
+		return PTR_ERR(data->syscon);
+
+	platform_set_drvdata(pdev, data);
+
+	err = device_create_file(&pdev->dev,
+				 &sensor_dev_attr_temp_input.dev_attr);
+	if (err)
+		goto fail_temp_file;
+
+	err = device_create_file(&pdev->dev, &dev_attr_name);
+	if (err)
+		goto fail_name_file;
+
+	data->hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto fail_hwmon_reg;
+	}
+
+	return 0;
+
+fail_hwmon_reg:
+	device_remove_file(&pdev->dev, &dev_attr_name);
+fail_name_file:
+	device_remove_file(&pdev->dev, &sensor_dev_attr_temp_input.dev_attr);
+fail_temp_file:
+	return err;
+}
+
+static int omap3_temp_remove(struct platform_device *pdev)
+{
+	struct omap3_temp_data *data = platform_get_drvdata(pdev);
+
+	if (!data)
+		return 0;
+
+	hwmon_device_unregister(data->hwmon_dev);
+	device_remove_file(&pdev->dev, &dev_attr_name);
+	device_remove_file(&pdev->dev, &sensor_dev_attr_temp_input.dev_attr);
+
+	return 0;
+}
+
+static struct platform_driver omap3_temp_driver = {
+	.probe	= omap3_temp_probe,
+	.remove	= omap3_temp_remove,
+	.driver = {
+		.name	= "omap3-temperature",
+		.of_match_table	= omap3_temp_dt_ids,
+	},
+};
+
+module_platform_driver(omap3_temp_driver);
+
+MODULE_AUTHOR("Sebastian Reichel");
+MODULE_DESCRIPTION("OMAP34xx/OMAP36xx temperature sensor");
+MODULE_LICENSE("GPL");
-- 
2.1.3




More information about the linux-arm-kernel mailing list