Blame view

drivers/thermal/imx_thermal.c 7.89 KB
83d290c56   Tom Rini   SPDX: Convert all...
1
  // SPDX-License-Identifier: GPL-2.0+
e3568d2ec   Ye.Li   DM: thermal: Add ...
2
3
4
5
  /*
   * (C) Copyright 2014 Freescale Semiconductor, Inc.
   * Author: Nitin Garg <nitin.garg@freescale.com>
   *             Ye Li <Ye.Li@freescale.com>
e3568d2ec   Ye.Li   DM: thermal: Add ...
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   Tim Harvey   thermal: imx_ther...
14
  #include <asm/arch/sys_proto.h>
e3568d2ec   Ye.Li   DM: thermal: Add ...
15
16
17
  #include <dm.h>
  #include <errno.h>
  #include <malloc.h>
80512547b   Peng Fan   thermal: imx: fix...
18
  #include <linux/math64.h>
e3568d2ec   Ye.Li   DM: thermal: Add ...
19
20
  #include <thermal.h>
  #include <imx_thermal.h>
be56de6f3   Tim Harvey   thermal: imx_ther...
21
22
  /* board will busyloop until this many degrees C below CPU max temperature */
  #define TEMPERATURE_HOT_DELTA   5 /* CPU maxT - 5C */
e3568d2ec   Ye.Li   DM: thermal: Add ...
23
  #define FACTOR0			10000000
4fac41716   Peng Fan   imx: thermal: upd...
24
25
26
  #define FACTOR1			15423
  #define FACTOR2			4148468
  #define OFFSET			3580661
e3568d2ec   Ye.Li   DM: thermal: Add ...
27
  #define MEASURE_FREQ		327
d1aa6f2d5   Adrian Alonso   thermal: imx: add...
28
29
30
  #define TEMPERATURE_MIN         -40
  #define TEMPERATURE_HOT         85
  #define TEMPERATURE_MAX         125
e3568d2ec   Ye.Li   DM: thermal: Add ...
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   Tim Harvey   thermal: imx_ther...
39
40
  struct thermal_data {
  	unsigned int fuse;
be56de6f3   Tim Harvey   thermal: imx_ther...
41
  	int critical;
a91db9547   Tim Harvey   thermal: imx_ther...
42
43
44
  	int minc;
  	int maxc;
  };
d1aa6f2d5   Adrian Alonso   thermal: imx: add...
45
46
  #if defined(CONFIG_MX6)
  static int read_cpu_temperature(struct udevice *dev)
e3568d2ec   Ye.Li   DM: thermal: Add ...
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   Tim Harvey   thermal: imx_ther...
52
53
  	struct thermal_data *priv = dev_get_priv(dev);
  	u32 fuse = priv->fuse;
e3568d2ec   Ye.Li   DM: thermal: Add ...
54
  	int t1, n1;
80512547b   Peng Fan   thermal: imx: fix...
55
56
57
  	s64 c1, c2;
  	s64 temp64;
  	s32 rem;
e3568d2ec   Ye.Li   DM: thermal: Add ...
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   Peng Fan   imx: thermal: upd...
63
  	 * slope = 0.4445388 - (0.0016549 * 25C fuse)
e3568d2ec   Ye.Li   DM: thermal: Add ...
64
65
66
67
68
69
  	 */
  	n1 = fuse >> 20;
  	t1 = 25; /* t1 always 25C */
  
  	/*
  	 * Derived from linear interpolation:
4fac41716   Peng Fan   imx: thermal: upd...
70
  	 * slope = 0.4445388 - (0.0016549 * 25C fuse)
e3568d2ec   Ye.Li   DM: thermal: Add ...
71
  	 * slope = (FACTOR2 - FACTOR1 * n1) / FACTOR0
4fac41716   Peng Fan   imx: thermal: upd...
72
73
74
  	 * offset = 3.580661
  	 * offset = OFFSET / 1000000
  	 * (Nmeas - n1) / (Tmeas - t1 - offset) = slope
e3568d2ec   Ye.Li   DM: thermal: Add ...
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   Peng Fan   imx: thermal: upd...
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   Ye.Li   DM: thermal: Add ...
86
87
  	 */
  	temp64 = FACTOR0;
4fac41716   Peng Fan   imx: thermal: upd...
88
  	temp64 *= 1000000;
80512547b   Peng Fan   thermal: imx: fix...
89
  	temp64 = div_s64_rem(temp64, FACTOR1 * n1 - FACTOR2, &rem);
e3568d2ec   Ye.Li   DM: thermal: Add ...
90
  	c1 = temp64;
4fac41716   Peng Fan   imx: thermal: upd...
91
  	c2 = n1 * c1 + 1000000 * t1;
e3568d2ec   Ye.Li   DM: thermal: Add ...
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   Peng Fan   imx: thermal: upd...
122
  	/* Tmeas = (c2 - Nmeas * c1 + OFFSET) / 1000000 */
80512547b   Peng Fan   thermal: imx: fix...
123
  	temperature = div_s64_rem(c2 - n_meas * c1 + OFFSET, 1000000, &rem);
e3568d2ec   Ye.Li   DM: thermal: Add ...
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   Adrian Alonso   thermal: imx: add...
131
132
133
  #elif defined(CONFIG_MX7)
  static int read_cpu_temperature(struct udevice *dev)
  {
fcbe8c567   Peng Fan   imx: mx7: fix the...
134
  	unsigned int reg, tmp;
d1aa6f2d5   Adrian Alonso   thermal: imx: add...
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   Peng Fan   imx: mx7: fix the...
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   Adrian Alonso   thermal: imx: add...
180
  		/*
fcbe8c567   Peng Fan   imx: mx7: fix the...
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   Adrian Alonso   thermal: imx: add...
185
  		 */
fcbe8c567   Peng Fan   imx: mx7: fix the...
186
  		udelay(10000);
d1aa6f2d5   Adrian Alonso   thermal: imx: add...
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   Peng Fan   imx: mx7: fix the...
190
  	}
d1aa6f2d5   Adrian Alonso   thermal: imx: add...
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   Ye.Li   DM: thermal: Add ...
204
205
206
  
  int imx_thermal_get_temp(struct udevice *dev, int *temp)
  {
a91db9547   Tim Harvey   thermal: imx_ther...
207
  	struct thermal_data *priv = dev_get_priv(dev);
e3568d2ec   Ye.Li   DM: thermal: Add ...
208
  	int cpu_tmp = 0;
d1aa6f2d5   Adrian Alonso   thermal: imx: add...
209
  	cpu_tmp = read_cpu_temperature(dev);
3b7ad216e   Tim Harvey   thermal:imx_therm...
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   Adrian Alonso   thermal: imx: add...
216
  		cpu_tmp = read_cpu_temperature(dev);
e3568d2ec   Ye.Li   DM: thermal: Add ...
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   Tim Harvey   thermal: imx_ther...
233
  	struct thermal_data *priv = dev_get_priv(dev);
e3568d2ec   Ye.Li   DM: thermal: Add ...
234
235
236
  
  	/* Read Temperature calibration data fuse */
  	fuse_read(pdata->fuse_bank, pdata->fuse_word, &fuse);
1368f9934   Adrian Alonso   thermal: imx_ther...
237
238
239
  	if (is_soc_type(MXC_SOC_MX6)) {
  		/* Check for valid fuse */
  		if (fuse == 0 || fuse == ~0) {
c8434ccac   Fabio Estevam   thermal: imx_ther...
240
241
  			debug("CPU:   Thermal invalid data, fuse: 0x%x
  ",
d1aa6f2d5   Adrian Alonso   thermal: imx: add...
242
  				fuse);
1368f9934   Adrian Alonso   thermal: imx_ther...
243
244
  			return -EPERM;
  		}
d1aa6f2d5   Adrian Alonso   thermal: imx: add...
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   Ye.Li   DM: thermal: Add ...
252
  	}
be56de6f3   Tim Harvey   thermal: imx_ther...
253
  	/* set critical cooling temp */
a91db9547   Tim Harvey   thermal: imx_ther...
254
  	get_cpu_temp_grade(&priv->minc, &priv->maxc);
be56de6f3   Tim Harvey   thermal: imx_ther...
255
  	priv->critical = priv->maxc - TEMPERATURE_HOT_DELTA;
a91db9547   Tim Harvey   thermal: imx_ther...
256
  	priv->fuse = fuse;
e3568d2ec   Ye.Li   DM: thermal: Add ...
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   Tim Harvey   thermal: imx_ther...
268
  	.priv_auto_alloc_size = sizeof(struct thermal_data),
e3568d2ec   Ye.Li   DM: thermal: Add ...
269
270
  	.flags  = DM_FLAG_PRE_RELOC,
  };