aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/thermal/Makefile9
-rw-r--r--drivers/thermal/imx_thermal.c177
-rw-r--r--drivers/thermal/thermal-uclass.c30
-rw-r--r--include/dm/uclass-id.h1
-rw-r--r--include/imx_thermal.h17
-rw-r--r--include/thermal.h42
7 files changed, 277 insertions, 0 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 33227c8bd6d..7683c6139ff 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -21,3 +21,4 @@ obj-y += pwm/
obj-y += input/
# SOC specific infrastructure drivers.
obj-y += soc/
+obj-y += thermal/
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
new file mode 100644
index 00000000000..6d4cacdcf7f
--- /dev/null
+++ b/drivers/thermal/Makefile
@@ -0,0 +1,9 @@
+#
+# (C) Copyright 2014 Freescale Semiconductor, Inc.
+# Author: Nitin Garg <nitin.garg@freescale.com>
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_DM_THERMAL) += thermal-uclass.o
+obj-$(CONFIG_IMX6_THERMAL) += imx_thermal.o
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c
new file mode 100644
index 00000000000..116158511db
--- /dev/null
+++ b/drivers/thermal/imx_thermal.c
@@ -0,0 +1,177 @@
+/*
+ * (C) Copyright 2014 Freescale Semiconductor, Inc.
+ * Author: Nitin Garg <nitin.garg@freescale.com>
+ * Ye Li <Ye.Li@freescale.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+#include <common.h>
+#include <div64.h>
+#include <fuse.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <thermal.h>
+#include <imx_thermal.h>
+
+#define TEMPERATURE_MIN -40
+#define TEMPERATURE_HOT 80
+#define TEMPERATURE_MAX 125
+#define FACTOR0 10000000
+#define FACTOR1 15976
+#define FACTOR2 4297157
+#define MEASURE_FREQ 327
+
+#define TEMPSENSE0_TEMP_CNT_SHIFT 8
+#define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT)
+#define TEMPSENSE0_FINISHED (1 << 2)
+#define TEMPSENSE0_MEASURE_TEMP (1 << 1)
+#define TEMPSENSE0_POWER_DOWN (1 << 0)
+#define MISC0_REFTOP_SELBIASOFF (1 << 3)
+#define TEMPSENSE1_MEASURE_FREQ 0xffff
+
+static int read_cpu_temperature(struct udevice *dev)
+{
+ int temperature;
+ unsigned int reg, n_meas;
+ const struct imx_thermal_plat *pdata = dev_get_platdata(dev);
+ struct anatop_regs *anatop = (struct anatop_regs *)pdata->regs;
+ unsigned int *priv = dev_get_priv(dev);
+ u32 fuse = *priv;
+ int t1, n1;
+ u32 c1, c2;
+ u64 temp64;
+
+ /*
+ * Sensor data layout:
+ * [31:20] - sensor value @ 25C
+ * We use universal formula now and only need sensor value @ 25C
+ * slope = 0.4297157 - (0.0015976 * 25C fuse)
+ */
+ n1 = fuse >> 20;
+ t1 = 25; /* t1 always 25C */
+
+ /*
+ * Derived from linear interpolation:
+ * slope = 0.4297157 - (0.0015976 * 25C fuse)
+ * slope = (FACTOR2 - FACTOR1 * n1) / FACTOR0
+ * (Nmeas - n1) / (Tmeas - t1) = slope
+ * We want to reduce this down to the minimum computation necessary
+ * for each temperature read. Also, we want Tmeas in millicelsius
+ * and we don't want to lose precision from integer division. So...
+ * Tmeas = (Nmeas - n1) / slope + t1
+ * milli_Tmeas = 1000 * (Nmeas - n1) / slope + 1000 * t1
+ * milli_Tmeas = -1000 * (n1 - Nmeas) / slope + 1000 * t1
+ * Let constant c1 = (-1000 / slope)
+ * milli_Tmeas = (n1 - Nmeas) * c1 + 1000 * t1
+ * Let constant c2 = n1 *c1 + 1000 * t1
+ * milli_Tmeas = c2 - Nmeas * c1
+ */
+ temp64 = FACTOR0;
+ temp64 *= 1000;
+ do_div(temp64, FACTOR1 * n1 - FACTOR2);
+ c1 = temp64;
+ c2 = n1 * c1 + 1000 * t1;
+
+ /*
+ * now we only use single measure, every time we read
+ * the temperature, we will power on/down anadig thermal
+ * module
+ */
+ writel(TEMPSENSE0_POWER_DOWN, &anatop->tempsense0_clr);
+ writel(MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_set);
+
+ /* setup measure freq */
+ reg = readl(&anatop->tempsense1);
+ reg &= ~TEMPSENSE1_MEASURE_FREQ;
+ reg |= MEASURE_FREQ;
+ writel(reg, &anatop->tempsense1);
+
+ /* start the measurement process */
+ writel(TEMPSENSE0_MEASURE_TEMP, &anatop->tempsense0_clr);
+ writel(TEMPSENSE0_FINISHED, &anatop->tempsense0_clr);
+ writel(TEMPSENSE0_MEASURE_TEMP, &anatop->tempsense0_set);
+
+ /* make sure that the latest temp is valid */
+ while ((readl(&anatop->tempsense0) &
+ TEMPSENSE0_FINISHED) == 0)
+ udelay(10000);
+
+ /* read temperature count */
+ reg = readl(&anatop->tempsense0);
+ n_meas = (reg & TEMPSENSE0_TEMP_CNT_MASK)
+ >> TEMPSENSE0_TEMP_CNT_SHIFT;
+ writel(TEMPSENSE0_FINISHED, &anatop->tempsense0_clr);
+
+ /* milli_Tmeas = c2 - Nmeas * c1 */
+ temperature = (c2 - n_meas * c1)/1000;
+
+ /* power down anatop thermal sensor */
+ writel(TEMPSENSE0_POWER_DOWN, &anatop->tempsense0_set);
+ writel(MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_clr);
+
+ return temperature;
+}
+
+int imx_thermal_get_temp(struct udevice *dev, int *temp)
+{
+ int cpu_tmp = 0;
+
+ cpu_tmp = read_cpu_temperature(dev);
+ while (cpu_tmp > TEMPERATURE_MIN && cpu_tmp < TEMPERATURE_MAX) {
+ if (cpu_tmp >= TEMPERATURE_HOT) {
+ printf("CPU Temperature is %d C, too hot to boot, waiting...\n",
+ cpu_tmp);
+ udelay(5000000);
+ cpu_tmp = read_cpu_temperature(dev);
+ } else {
+ break;
+ }
+ }
+
+ *temp = cpu_tmp;
+
+ return 0;
+}
+
+static const struct dm_thermal_ops imx_thermal_ops = {
+ .get_temp = imx_thermal_get_temp,
+};
+
+static int imx_thermal_probe(struct udevice *dev)
+{
+ unsigned int fuse = ~0;
+
+ const struct imx_thermal_plat *pdata = dev_get_platdata(dev);
+ unsigned int *priv = dev_get_priv(dev);
+
+ /* Read Temperature calibration data fuse */
+ fuse_read(pdata->fuse_bank, pdata->fuse_word, &fuse);
+
+ /* Check for valid fuse */
+ if (fuse == 0 || fuse == ~0) {
+ printf("CPU: Thermal invalid data, fuse: 0x%x\n", fuse);
+ return -EPERM;
+ } else {
+ printf("CPU: Thermal calibration data: 0x%x\n", fuse);
+ }
+
+ *priv = fuse;
+
+ enable_thermal_clk();
+
+ return 0;
+}
+
+U_BOOT_DRIVER(imx_thermal) = {
+ .name = "imx_thermal",
+ .id = UCLASS_THERMAL,
+ .ops = &imx_thermal_ops,
+ .probe = imx_thermal_probe,
+ .priv_auto_alloc_size = sizeof(unsigned int),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/thermal/thermal-uclass.c b/drivers/thermal/thermal-uclass.c
new file mode 100644
index 00000000000..3bee1a7dc7a
--- /dev/null
+++ b/drivers/thermal/thermal-uclass.c
@@ -0,0 +1,30 @@
+/*
+ * (C) Copyright 2014 Freescale Semiconductor, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <thermal.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <linux/list.h>
+
+
+int thermal_get_temp(struct udevice *dev, int *temp)
+{
+ const struct dm_thermal_ops *ops = device_get_ops(dev);
+
+ if (!ops->get_temp)
+ return -ENOSYS;
+
+ return ops->get_temp(dev, temp);
+}
+
+UCLASS_DRIVER(thermal) = {
+ .id = UCLASS_THERMAL,
+ .name = "thermal",
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index a8944c97d03..202f59b505d 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -28,6 +28,7 @@ enum uclass_id {
UCLASS_SPI_GENERIC, /* Generic SPI flash target */
UCLASS_SPI_FLASH, /* SPI flash */
UCLASS_CROS_EC, /* Chrome OS EC */
+ UCLASS_THERMAL, /* Thermal sensor */
UCLASS_COUNT,
UCLASS_INVALID = -1,
diff --git a/include/imx_thermal.h b/include/imx_thermal.h
new file mode 100644
index 00000000000..be1365288e5
--- /dev/null
+++ b/include/imx_thermal.h
@@ -0,0 +1,17 @@
+/*
+ *
+ * (C) Copyright 2014 Freescale Semiconductor, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _IMX_THERMAL_H_
+#define _IMX_THERMAL_H_
+
+struct imx_thermal_plat {
+ void *regs;
+ int fuse_bank;
+ int fuse_word;
+};
+
+#endif /* _IMX_THERMAL_H_ */
diff --git a/include/thermal.h b/include/thermal.h
new file mode 100644
index 00000000000..5d6101bec6e
--- /dev/null
+++ b/include/thermal.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * (C) Copyright 2014 Freescale Semiconductor, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _THERMAL_H_
+#define _THERMAL_H_
+
+#include <dm.h>
+
+int thermal_get_temp(struct udevice *dev, int *temp);
+
+/**
+ * struct struct dm_thermal_ops - Driver model Thermal operations
+ *
+ * The uclass interface is implemented by all Thermal devices which use
+ * driver model.
+ */
+struct dm_thermal_ops {
+ /**
+ * Get the current temperature
+ *
+ * The device provided is the slave device. It's parent controller
+ * will be used to provide the communication.
+ *
+ * This must be called before doing any transfers with a Thermal slave.
+ * It will enable and initialize any Thermal hardware as necessary,
+ * and make sure that the SCK line is in the correct idle state. It is
+ * not allowed to claim the same bus for several slaves without
+ * releasing the bus in between.
+ *
+ * @dev: The Thermal device
+ *
+ * Returns: 0 if the bus was claimed successfully, or a negative value
+ * if it wasn't.
+ */
+ int (*get_temp)(struct udevice *dev, int *temp);
+};
+
+#endif /* _THERMAL_H_ */