Blame view
drivers/thermal/imx_thermal.c
7.89 KB
83d290c56
|
1 |
// SPDX-License-Identifier: GPL-2.0+ |
e3568d2ec
|
2 3 4 5 |
/* * (C) Copyright 2014 Freescale Semiconductor, Inc. * Author: Nitin Garg <nitin.garg@freescale.com> * Ye Li <Ye.Li@freescale.com> |
e3568d2ec
|
6 7 8 9 10 11 12 13 |
*/ #include <config.h> #include <common.h> #include <div64.h> #include <fuse.h> #include <asm/io.h> #include <asm/arch/clock.h> |
a91db9547
|
14 |
#include <asm/arch/sys_proto.h> |
e3568d2ec
|
15 16 17 |
#include <dm.h> #include <errno.h> #include <malloc.h> |
80512547b
|
18 |
#include <linux/math64.h> |
e3568d2ec
|
19 20 |
#include <thermal.h> #include <imx_thermal.h> |
be56de6f3
|
21 22 |
/* board will busyloop until this many degrees C below CPU max temperature */ #define TEMPERATURE_HOT_DELTA 5 /* CPU maxT - 5C */ |
e3568d2ec
|
23 |
#define FACTOR0 10000000 |
4fac41716
|
24 25 26 |
#define FACTOR1 15423 #define FACTOR2 4148468 #define OFFSET 3580661 |
e3568d2ec
|
27 |
#define MEASURE_FREQ 327 |
d1aa6f2d5
|
28 29 30 |
#define TEMPERATURE_MIN -40 #define TEMPERATURE_HOT 85 #define TEMPERATURE_MAX 125 |
e3568d2ec
|
31 32 33 34 35 36 37 38 |
#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 |
a91db9547
|
39 40 |
struct thermal_data { unsigned int fuse; |
be56de6f3
|
41 |
int critical; |
a91db9547
|
42 43 44 |
int minc; int maxc; }; |
d1aa6f2d5
|
45 46 |
#if defined(CONFIG_MX6) static int read_cpu_temperature(struct udevice *dev) |
e3568d2ec
|
47 48 49 50 51 |
{ 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; |
a91db9547
|
52 53 |
struct thermal_data *priv = dev_get_priv(dev); u32 fuse = priv->fuse; |
e3568d2ec
|
54 |
int t1, n1; |
80512547b
|
55 56 57 |
s64 c1, c2; s64 temp64; s32 rem; |
e3568d2ec
|
58 59 60 61 62 |
/* * Sensor data layout: * [31:20] - sensor value @ 25C * We use universal formula now and only need sensor value @ 25C |
4fac41716
|
63 |
* slope = 0.4445388 - (0.0016549 * 25C fuse) |
e3568d2ec
|
64 65 66 67 68 69 |
*/ n1 = fuse >> 20; t1 = 25; /* t1 always 25C */ /* * Derived from linear interpolation: |
4fac41716
|
70 |
* slope = 0.4445388 - (0.0016549 * 25C fuse) |
e3568d2ec
|
71 |
* slope = (FACTOR2 - FACTOR1 * n1) / FACTOR0 |
4fac41716
|
72 73 74 |
* offset = 3.580661 * offset = OFFSET / 1000000 * (Nmeas - n1) / (Tmeas - t1 - offset) = slope |
e3568d2ec
|
75 76 77 |
* 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... |
4fac41716
|
78 79 80 81 82 83 84 85 |
* Tmeas = (Nmeas - n1) / slope + t1 + offset * milli_Tmeas = 1000000 * (Nmeas - n1) / slope + 1000000 * t1 + OFFSET * milli_Tmeas = -1000000 * (n1 - Nmeas) / slope + 1000000 * t1 + OFFSET * Let constant c1 = (-1000000 / slope) * milli_Tmeas = (n1 - Nmeas) * c1 + 1000000 * t1 + OFFSET * Let constant c2 = n1 *c1 + 1000000 * t1 * milli_Tmeas = (c2 - Nmeas * c1) + OFFSET * Tmeas = ((c2 - Nmeas * c1) + OFFSET) / 1000000 |
e3568d2ec
|
86 87 |
*/ temp64 = FACTOR0; |
4fac41716
|
88 |
temp64 *= 1000000; |
80512547b
|
89 |
temp64 = div_s64_rem(temp64, FACTOR1 * n1 - FACTOR2, &rem); |
e3568d2ec
|
90 |
c1 = temp64; |
4fac41716
|
91 |
c2 = n1 * c1 + 1000000 * t1; |
e3568d2ec
|
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
/* * 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); |
4fac41716
|
122 |
/* Tmeas = (c2 - Nmeas * c1 + OFFSET) / 1000000 */ |
80512547b
|
123 |
temperature = div_s64_rem(c2 - n_meas * c1 + OFFSET, 1000000, &rem); |
e3568d2ec
|
124 125 126 127 128 129 130 |
/* power down anatop thermal sensor */ writel(TEMPSENSE0_POWER_DOWN, &anatop->tempsense0_set); writel(MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_clr); return temperature; } |
d1aa6f2d5
|
131 132 133 |
#elif defined(CONFIG_MX7) static int read_cpu_temperature(struct udevice *dev) { |
fcbe8c567
|
134 |
unsigned int reg, tmp; |
d1aa6f2d5
|
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
unsigned int raw_25c, te1; int temperature; unsigned int *priv = dev_get_priv(dev); u32 fuse = *priv; struct mxc_ccm_anatop_reg *ccm_anatop = (struct mxc_ccm_anatop_reg *) ANATOP_BASE_ADDR; /* * fuse data layout: * [31:21] sensor value @ 25C * [20:18] hot temperature value * [17:9] sensor value of room * [8:0] sensor value of hot */ raw_25c = fuse >> 21; if (raw_25c == 0) raw_25c = 25; te1 = (fuse >> 9) & 0x1ff; /* * now we only use single measure, every time we read * the temperature, we will power on/down anadig thermal * module */ writel(TEMPMON_HW_ANADIG_TEMPSENSE1_POWER_DOWN_MASK, &ccm_anatop->tempsense1_clr); writel(PMU_REF_REFTOP_SELFBIASOFF_MASK, &ccm_anatop->ref_set); /* write measure freq */ reg = readl(&ccm_anatop->tempsense1); reg &= ~TEMPMON_HW_ANADIG_TEMPSENSE1_MEASURE_FREQ_MASK; reg |= TEMPMON_HW_ANADIG_TEMPSENSE1_MEASURE_FREQ(MEASURE_FREQ); writel(reg, &ccm_anatop->tempsense1); writel(TEMPMON_HW_ANADIG_TEMPSENSE1_MEASURE_TEMP_MASK, &ccm_anatop->tempsense1_clr); writel(TEMPMON_HW_ANADIG_TEMPSENSE1_FINISHED_MASK, &ccm_anatop->tempsense1_clr); writel(TEMPMON_HW_ANADIG_TEMPSENSE1_MEASURE_TEMP_MASK, &ccm_anatop->tempsense1_set); |
fcbe8c567
|
172 173 174 175 176 177 178 179 |
if (soc_rev() >= CHIP_REV_1_1) { while ((readl(&ccm_anatop->tempsense1) & TEMPMON_HW_ANADIG_TEMPSENSE1_FINISHED_MASK) == 0) ; reg = readl(&ccm_anatop->tempsense1); tmp = (reg & TEMPMON_HW_ANADIG_TEMPSENSE1_TEMP_VALUE_MASK) >> TEMPMON_HW_ANADIG_TEMPSENSE1_TEMP_VALUE_SHIFT; } else { |
d1aa6f2d5
|
180 |
/* |
fcbe8c567
|
181 182 183 184 |
* Since we can not rely on finish bit, use 10ms * delay to get temperature. From RM, 17us is * enough to get data, but to gurantee to get * the data, delay 10ms here. |
d1aa6f2d5
|
185 |
*/ |
fcbe8c567
|
186 |
udelay(10000); |
d1aa6f2d5
|
187 188 189 |
reg = readl(&ccm_anatop->tempsense1); tmp = (reg & TEMPMON_HW_ANADIG_TEMPSENSE1_TEMP_VALUE_MASK) >> TEMPMON_HW_ANADIG_TEMPSENSE1_TEMP_VALUE_SHIFT; |
fcbe8c567
|
190 |
} |
d1aa6f2d5
|
191 192 193 194 195 196 197 198 199 200 201 202 203 |
writel(TEMPMON_HW_ANADIG_TEMPSENSE1_FINISHED_MASK, &ccm_anatop->tempsense1_clr); /* power down anatop thermal sensor */ writel(TEMPMON_HW_ANADIG_TEMPSENSE1_POWER_DOWN_MASK, &ccm_anatop->tempsense1_set); writel(PMU_REF_REFTOP_SELFBIASOFF_MASK, &ccm_anatop->ref_clr); /* Single point */ temperature = tmp - (te1 - raw_25c); return temperature; } #endif |
e3568d2ec
|
204 205 206 |
int imx_thermal_get_temp(struct udevice *dev, int *temp) { |
a91db9547
|
207 |
struct thermal_data *priv = dev_get_priv(dev); |
e3568d2ec
|
208 |
int cpu_tmp = 0; |
d1aa6f2d5
|
209 |
cpu_tmp = read_cpu_temperature(dev); |
3b7ad216e
|
210 211 212 213 214 215 |
while (cpu_tmp >= priv->critical) { printf("CPU Temperature (%dC) too close to max (%dC)", cpu_tmp, priv->maxc); puts(" waiting... "); udelay(5000000); |
d1aa6f2d5
|
216 |
cpu_tmp = read_cpu_temperature(dev); |
e3568d2ec
|
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
} *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); |
a91db9547
|
233 |
struct thermal_data *priv = dev_get_priv(dev); |
e3568d2ec
|
234 235 236 |
/* Read Temperature calibration data fuse */ fuse_read(pdata->fuse_bank, pdata->fuse_word, &fuse); |
1368f9934
|
237 238 239 |
if (is_soc_type(MXC_SOC_MX6)) { /* Check for valid fuse */ if (fuse == 0 || fuse == ~0) { |
c8434ccac
|
240 241 |
debug("CPU: Thermal invalid data, fuse: 0x%x ", |
d1aa6f2d5
|
242 |
fuse); |
1368f9934
|
243 244 |
return -EPERM; } |
d1aa6f2d5
|
245 246 247 248 249 250 251 |
} else if (is_soc_type(MXC_SOC_MX7)) { /* No Calibration data in FUSE? */ if ((fuse & 0x3ffff) == 0) return -EPERM; /* We do not support 105C TE2 */ if (((fuse & 0x1c0000) >> 18) == 0x6) return -EPERM; |
e3568d2ec
|
252 |
} |
be56de6f3
|
253 |
/* set critical cooling temp */ |
a91db9547
|
254 |
get_cpu_temp_grade(&priv->minc, &priv->maxc); |
be56de6f3
|
255 |
priv->critical = priv->maxc - TEMPERATURE_HOT_DELTA; |
a91db9547
|
256 |
priv->fuse = fuse; |
e3568d2ec
|
257 258 259 260 261 262 263 264 265 266 267 |
enable_thermal_clk(); return 0; } U_BOOT_DRIVER(imx_thermal) = { .name = "imx_thermal", .id = UCLASS_THERMAL, .ops = &imx_thermal_ops, .probe = imx_thermal_probe, |
a91db9547
|
268 |
.priv_auto_alloc_size = sizeof(struct thermal_data), |
e3568d2ec
|
269 270 |
.flags = DM_FLAG_PRE_RELOC, }; |