[U-Boot] [PATCH v6 2/4] mx6: thermal: Add i.MX6 CPU thermal sensor support

Stefano Babic sbabic at denx.de
Mon Nov 17 13:49:52 CET 2014


Hi Nitin,


On 14/11/2014 21:13, nitin.garg at freescale.com wrote:
> From: Nitin Garg <nitin.garg at freescale.com>
> 
> i.MX6 SoC has onchip temperature sensor. Add driver
> for this sensor.
> 
> Signed-off-by: Nitin Garg <nitin.garg at freescale.com>
> ---
>  drivers/Makefile              |    1 +
>  drivers/thermal/Makefile      |    8 +++
>  drivers/thermal/imx_thermal.c |  144 +++++++++++++++++++++++++++++++++++++++++
>  include/imx_thermal.h         |   15 +++++
>  4 files changed, 168 insertions(+)
>  create mode 100644 drivers/thermal/Makefile
>  create mode 100644 drivers/thermal/imx_thermal.c
>  create mode 100644 include/imx_thermal.h
> 

I have some conceptional question here. This introduces a new set or,
better, "class" of drivers, as we are used to see in kernel as
"thermal". As we discussed in last u-boot mini summit, the preferred way
to introduce new drivers into current u-boot is to use DM (Driver
Model), because this is the way u-boot will follow in the future. It
will take some time porting the current drivers, but it is easier for
drivers introducing new features.

This is exactly this case: there is at the moment no "thermal" drivers,
and it would be nice to introduce it using DM, else we will have the
problem in the near future to port this drivers, too, and maybe some
further drivers adding thermal monitoring.

This means you have to add a "thermal" class (you can take a look at the
spi driver, it was already ported to dm) and define the functions your
class exports. I think at the moment you define only a "monitor", but
feel free to add further functions, preferably in the same way as the
linux kernel does (see include/linux/thermal.h in kernel tree).

> diff --git a/drivers/Makefile b/drivers/Makefile
> index 33227c8..7683c61 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 0000000..04ae395
> --- /dev/null
> +++ b/drivers/thermal/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# (C) Copyright 2014 Freescale Semiconductor, Inc.
> +# Author: Nitin Garg <nitin.garg at freescale.com>
> +#
> +# SPDX-License-Identifier:	GPL-2.0+
> +#
> +
> +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 0000000..fe9dd85
> --- /dev/null
> +++ b/drivers/thermal/imx_thermal.c
> @@ -0,0 +1,144 @@
> +/*
> + * (C) Copyright 2014 Freescale Semiconductor, Inc.
> + * Author: Nitin Garg <nitin.garg at 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>
> +
> +#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(u32 fuse)
> +{
> +	int temperature;
> +	unsigned int reg, n_meas;
> +	struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR;
> +	int t1, n1;
> +	u32 c1, c2;
> +	u64 temp64;
> +
> +	/* make sure pll3 is enabled for thermal sensor */
> +	enable_pll3();
> +
> +	/*
> +	 * 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;
> +}
> +
> +void check_cpu_temperature(void)
> +{
> +	int cpu_tmp = 0;
> +	u32 fuse = ~0;
> +
> +	/* Read Temperature calibration data fuse */
> +	fuse_read(1, 6, &fuse);
> +
> +	/* Check for valid fuse */
> +	if (fuse == 0 || fuse == ~0) {
> +		printf("CPU:   Temperature: invalid data, fuse: 0x%x\n", fuse);
> +		return;
> +	}
> +
> +	cpu_tmp = read_cpu_temperature(fuse);
> +	while (cpu_tmp > TEMPERATURE_MIN && cpu_tmp < TEMPERATURE_MAX) {
> +		if (cpu_tmp >= TEMPERATURE_HOT) {
> +			printf("CPU is %d C, too hot to boot, waiting...\n",
> +			       cpu_tmp);
> +			udelay(5000000);
> +			cpu_tmp = read_cpu_temperature(fuse);
> +		} else {
> +			break;
> +		}
> +	}

I would split here the interface. We have a driver, and this should
return back its result - but without taking an action. This decision
should be done by the user of the driver (the cpu code).


> +
> +	if (cpu_tmp > TEMPERATURE_MIN && cpu_tmp < TEMPERATURE_MAX)
> +		printf("CPU:   Temperature %d C\n", cpu_tmp);
> +	else
> +		printf("CPU:   Temperature: invalid sensor data\n");
> +}
> diff --git a/include/imx_thermal.h b/include/imx_thermal.h
> new file mode 100644
> index 0000000..979664a
> --- /dev/null
> +++ b/include/imx_thermal.h
> @@ -0,0 +1,15 @@
> +/*
> + * IMX Thermal Defines
> + *-------------------------------------------------------------------
> + *
> + * Copyright 2014 Freescale Semiconductor, Inc
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#ifndef __IMX6_THERMAL_H__
> +#define	__IMX6_THERMAL_H__
> +
> +void check_cpu_temperature(void);
> +
> +#endif /* __IMX6_THERMAL_H__ */
> 

Best regards,
Stefano Babic

-- 
=====================================================================
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sbabic at denx.de
=====================================================================


More information about the U-Boot mailing list