1 /* 2 * (C) Copyright 2014 Freescale Semiconductor, Inc. 3 * Author: Nitin Garg <nitin.garg@freescale.com> 4 * Ye Li <Ye.Li@freescale.com> 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 9 #include <config.h> 10 #include <common.h> 11 #include <div64.h> 12 #include <fuse.h> 13 #include <asm/io.h> 14 #include <asm/arch/clock.h> 15 #include <dm.h> 16 #include <errno.h> 17 #include <malloc.h> 18 #include <thermal.h> 19 #include <imx_thermal.h> 20 21 #define TEMPERATURE_MIN -40 22 #define TEMPERATURE_HOT 80 23 #define TEMPERATURE_MAX 125 24 #define FACTOR0 10000000 25 #define FACTOR1 15976 26 #define FACTOR2 4297157 27 #define MEASURE_FREQ 327 28 29 #define TEMPSENSE0_TEMP_CNT_SHIFT 8 30 #define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT) 31 #define TEMPSENSE0_FINISHED (1 << 2) 32 #define TEMPSENSE0_MEASURE_TEMP (1 << 1) 33 #define TEMPSENSE0_POWER_DOWN (1 << 0) 34 #define MISC0_REFTOP_SELBIASOFF (1 << 3) 35 #define TEMPSENSE1_MEASURE_FREQ 0xffff 36 37 static int read_cpu_temperature(struct udevice *dev) 38 { 39 int temperature; 40 unsigned int reg, n_meas; 41 const struct imx_thermal_plat *pdata = dev_get_platdata(dev); 42 struct anatop_regs *anatop = (struct anatop_regs *)pdata->regs; 43 unsigned int *priv = dev_get_priv(dev); 44 u32 fuse = *priv; 45 int t1, n1; 46 u32 c1, c2; 47 u64 temp64; 48 49 /* 50 * Sensor data layout: 51 * [31:20] - sensor value @ 25C 52 * We use universal formula now and only need sensor value @ 25C 53 * slope = 0.4297157 - (0.0015976 * 25C fuse) 54 */ 55 n1 = fuse >> 20; 56 t1 = 25; /* t1 always 25C */ 57 58 /* 59 * Derived from linear interpolation: 60 * slope = 0.4297157 - (0.0015976 * 25C fuse) 61 * slope = (FACTOR2 - FACTOR1 * n1) / FACTOR0 62 * (Nmeas - n1) / (Tmeas - t1) = slope 63 * We want to reduce this down to the minimum computation necessary 64 * for each temperature read. Also, we want Tmeas in millicelsius 65 * and we don't want to lose precision from integer division. So... 66 * Tmeas = (Nmeas - n1) / slope + t1 67 * milli_Tmeas = 1000 * (Nmeas - n1) / slope + 1000 * t1 68 * milli_Tmeas = -1000 * (n1 - Nmeas) / slope + 1000 * t1 69 * Let constant c1 = (-1000 / slope) 70 * milli_Tmeas = (n1 - Nmeas) * c1 + 1000 * t1 71 * Let constant c2 = n1 *c1 + 1000 * t1 72 * milli_Tmeas = c2 - Nmeas * c1 73 */ 74 temp64 = FACTOR0; 75 temp64 *= 1000; 76 do_div(temp64, FACTOR1 * n1 - FACTOR2); 77 c1 = temp64; 78 c2 = n1 * c1 + 1000 * t1; 79 80 /* 81 * now we only use single measure, every time we read 82 * the temperature, we will power on/down anadig thermal 83 * module 84 */ 85 writel(TEMPSENSE0_POWER_DOWN, &anatop->tempsense0_clr); 86 writel(MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_set); 87 88 /* setup measure freq */ 89 reg = readl(&anatop->tempsense1); 90 reg &= ~TEMPSENSE1_MEASURE_FREQ; 91 reg |= MEASURE_FREQ; 92 writel(reg, &anatop->tempsense1); 93 94 /* start the measurement process */ 95 writel(TEMPSENSE0_MEASURE_TEMP, &anatop->tempsense0_clr); 96 writel(TEMPSENSE0_FINISHED, &anatop->tempsense0_clr); 97 writel(TEMPSENSE0_MEASURE_TEMP, &anatop->tempsense0_set); 98 99 /* make sure that the latest temp is valid */ 100 while ((readl(&anatop->tempsense0) & 101 TEMPSENSE0_FINISHED) == 0) 102 udelay(10000); 103 104 /* read temperature count */ 105 reg = readl(&anatop->tempsense0); 106 n_meas = (reg & TEMPSENSE0_TEMP_CNT_MASK) 107 >> TEMPSENSE0_TEMP_CNT_SHIFT; 108 writel(TEMPSENSE0_FINISHED, &anatop->tempsense0_clr); 109 110 /* milli_Tmeas = c2 - Nmeas * c1 */ 111 temperature = (c2 - n_meas * c1)/1000; 112 113 /* power down anatop thermal sensor */ 114 writel(TEMPSENSE0_POWER_DOWN, &anatop->tempsense0_set); 115 writel(MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_clr); 116 117 return temperature; 118 } 119 120 int imx_thermal_get_temp(struct udevice *dev, int *temp) 121 { 122 int cpu_tmp = 0; 123 124 cpu_tmp = read_cpu_temperature(dev); 125 while (cpu_tmp > TEMPERATURE_MIN && cpu_tmp < TEMPERATURE_MAX) { 126 if (cpu_tmp >= TEMPERATURE_HOT) { 127 printf("CPU Temperature is %d C, too hot to boot, waiting...\n", 128 cpu_tmp); 129 udelay(5000000); 130 cpu_tmp = read_cpu_temperature(dev); 131 } else { 132 break; 133 } 134 } 135 136 *temp = cpu_tmp; 137 138 return 0; 139 } 140 141 static const struct dm_thermal_ops imx_thermal_ops = { 142 .get_temp = imx_thermal_get_temp, 143 }; 144 145 static int imx_thermal_probe(struct udevice *dev) 146 { 147 unsigned int fuse = ~0; 148 149 const struct imx_thermal_plat *pdata = dev_get_platdata(dev); 150 unsigned int *priv = dev_get_priv(dev); 151 152 /* Read Temperature calibration data fuse */ 153 fuse_read(pdata->fuse_bank, pdata->fuse_word, &fuse); 154 155 /* Check for valid fuse */ 156 if (fuse == 0 || fuse == ~0) { 157 printf("CPU: Thermal invalid data, fuse: 0x%x\n", fuse); 158 return -EPERM; 159 } 160 161 *priv = fuse; 162 163 enable_thermal_clk(); 164 165 return 0; 166 } 167 168 U_BOOT_DRIVER(imx_thermal) = { 169 .name = "imx_thermal", 170 .id = UCLASS_THERMAL, 171 .ops = &imx_thermal_ops, 172 .probe = imx_thermal_probe, 173 .priv_auto_alloc_size = sizeof(unsigned int), 174 .flags = DM_FLAG_PRE_RELOC, 175 }; 176