1ef2ee5d0SMichal Meloun /*-
2ef2ee5d0SMichal Meloun * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3ef2ee5d0SMichal Meloun * All rights reserved.
4ef2ee5d0SMichal Meloun *
5ef2ee5d0SMichal Meloun * Redistribution and use in source and binary forms, with or without
6ef2ee5d0SMichal Meloun * modification, are permitted provided that the following conditions
7ef2ee5d0SMichal Meloun * are met:
8ef2ee5d0SMichal Meloun * 1. Redistributions of source code must retain the above copyright
9ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer.
10ef2ee5d0SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright
11ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer in the
12ef2ee5d0SMichal Meloun * documentation and/or other materials provided with the distribution.
13ef2ee5d0SMichal Meloun *
14ef2ee5d0SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15ef2ee5d0SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16ef2ee5d0SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ef2ee5d0SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18ef2ee5d0SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19ef2ee5d0SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20ef2ee5d0SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21ef2ee5d0SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22ef2ee5d0SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23ef2ee5d0SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24ef2ee5d0SMichal Meloun * SUCH DAMAGE.
25ef2ee5d0SMichal Meloun */
26ef2ee5d0SMichal Meloun
27ef2ee5d0SMichal Meloun #include <sys/cdefs.h>
28ef2ee5d0SMichal Meloun /*
29ef2ee5d0SMichal Meloun * Thermometer and thermal zones driver for Tegra SoCs.
30ef2ee5d0SMichal Meloun * Calibration data and algo are taken from Linux, because this part of SoC
31ef2ee5d0SMichal Meloun * is undocumented in TRM.
32ef2ee5d0SMichal Meloun */
33ef2ee5d0SMichal Meloun
34ef2ee5d0SMichal Meloun #include <sys/param.h>
35ef2ee5d0SMichal Meloun #include <sys/systm.h>
36ef2ee5d0SMichal Meloun #include <sys/bus.h>
37ef2ee5d0SMichal Meloun #include <sys/gpio.h>
38ef2ee5d0SMichal Meloun #include <sys/kernel.h>
39ef2ee5d0SMichal Meloun #include <sys/module.h>
40ef2ee5d0SMichal Meloun #include <sys/malloc.h>
41ef2ee5d0SMichal Meloun #include <sys/rman.h>
42ef2ee5d0SMichal Meloun #include <sys/sysctl.h>
43ef2ee5d0SMichal Meloun
44ef2ee5d0SMichal Meloun #include <machine/bus.h>
45ef2ee5d0SMichal Meloun
46be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
47*1f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
48ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus.h>
49ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
50ef2ee5d0SMichal Meloun
51ef2ee5d0SMichal Meloun #include <arm/nvidia/tegra_efuse.h>
528a7a4683SEmmanuel Vadot #include <dt-bindings/thermal/tegra124-soctherm.h>
53ef2ee5d0SMichal Meloun #include "tegra_soctherm_if.h"
54ef2ee5d0SMichal Meloun
55ef2ee5d0SMichal Meloun /* Per sensors registers - base is 0x0c0*/
56ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG0 0x000
57ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG0_TALL(x) (((x) & 0xFFFFF) << 8)
58ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG0_STATUS_CLR (1 << 5)
59ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG0_TCALC_OVERFLOW (1 << 4)
60ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG0_OVERFLOW (1 << 3)
61ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG0_CPTR_OVERFLOW (1 << 2)
62ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG0_RO_SEL (1 << 1)
63ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG0_STOP (1 << 0)
64ef2ee5d0SMichal Meloun
65ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG1 0x004
66ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG1_TEMP_ENABLE (1U << 31)
67ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG1_TEN_COUNT(x) (((x) & 0x3F) << 24)
68ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG1_TIDDQ_EN(x) (((x) & 0x3F) << 15)
69ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG1_TSAMPLE(x) (((x) & 0x3FF) << 0)
70ef2ee5d0SMichal Meloun
71ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG2 0x008
72ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG2_THERMA(x) (((x) & 0xFFFF) << 16)
73ef2ee5d0SMichal Meloun #define TSENSOR_CONFIG2_THERMB(x) (((x) & 0xFFFF) << 0)
74ef2ee5d0SMichal Meloun
75ef2ee5d0SMichal Meloun #define TSENSOR_STATUS0 0x00c
76ef2ee5d0SMichal Meloun #define TSENSOR_STATUS0_CAPTURE_VALID (1U << 31)
77ef2ee5d0SMichal Meloun #define TSENSOR_STATUS0_CAPTURE(x) (((x) >> 0) & 0xffff)
78ef2ee5d0SMichal Meloun
79ef2ee5d0SMichal Meloun #define TSENSOR_STATUS1 0x010
80ef2ee5d0SMichal Meloun #define TSENSOR_STATUS1_TEMP_VALID (1U << 31)
81ef2ee5d0SMichal Meloun #define TSENSOR_STATUS1_TEMP(x) (((x) >> 0) & 0xffff)
82ef2ee5d0SMichal Meloun
83ef2ee5d0SMichal Meloun #define TSENSOR_STATUS2 0x014
84ef2ee5d0SMichal Meloun #define TSENSOR_STATUS2_TEMP_MAX(x) (((x) >> 16) & 0xffff)
85ef2ee5d0SMichal Meloun #define TSENSOR_STATUS2_TEMP_MIN(x) (((x) >> 0) & 0xffff)
86ef2ee5d0SMichal Meloun
87ef2ee5d0SMichal Meloun
88ef2ee5d0SMichal Meloun /* Readbacks */
89b9cbd68dSMichal Meloun #define READBACK_VALUE(x) (((x) >> 8) & 0xff)
90ef2ee5d0SMichal Meloun #define READBACK_ADD_HALF (1 << 7)
91ef2ee5d0SMichal Meloun #define READBACK_NEGATE (1 << 0)
92ef2ee5d0SMichal Meloun
93b9cbd68dSMichal Meloun /* Global registers */
94b9cbd68dSMichal Meloun #define TSENSOR_PDIV 0x1c0
95b9cbd68dSMichal Meloun #define TSENSOR_HOTSPOT_OFF 0x1c4
96b9cbd68dSMichal Meloun #define TSENSOR_TEMP1 0x1c8
97b9cbd68dSMichal Meloun #define TSENSOR_TEMP2 0x1cc
98b9cbd68dSMichal Meloun
99ef2ee5d0SMichal Meloun /* Fuses */
100ef2ee5d0SMichal Meloun #define FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT 0
101ef2ee5d0SMichal Meloun #define FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS 13
102ef2ee5d0SMichal Meloun #define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
103ef2ee5d0SMichal Meloun #define FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS 13
104ef2ee5d0SMichal Meloun
105b9cbd68dSMichal Meloun /* Layout is different for Tegra124 and Tegra210 */
106b9cbd68dSMichal Meloun #define FUSE_TSENSOR_COMMON 0x180
107b9cbd68dSMichal Meloun #define TEGRA124_FUSE_COMMON_CP_TS_BASE(x) (((x) >> 0) & 0x3ff)
108b9cbd68dSMichal Meloun #define TEGRA124_FUSE_COMMON_FT_TS_BASE(x) (((x) >> 10) & 0x7ff)
109b9cbd68dSMichal Meloun #define TEGRA124_FUSE_COMMON_SHIFT_FT_SHIFT 21
110b9cbd68dSMichal Meloun #define TEGRA124_FUSE_COMMON_SHIFT_FT_BITS 5
111ef2ee5d0SMichal Meloun
112b9cbd68dSMichal Meloun #define TEGRA210_FUSE_COMMON_CP_TS_BASE(x) (((x) >> 11) & 0x3ff)
113b9cbd68dSMichal Meloun #define TEGRA210_FUSE_COMMON_FT_TS_BASE(x) (((x) >> 21) & 0x7ff)
114b9cbd68dSMichal Meloun #define TEGRA210_FUSE_COMMON_SHIFT_CP_SHIFT 0
115b9cbd68dSMichal Meloun #define TEGRA210_FUSE_COMMON_SHIFT_CP_BITS 6
116b9cbd68dSMichal Meloun #define TEGRA210_FUSE_COMMON_SHIFT_FT_SHIFT 6
117b9cbd68dSMichal Meloun #define TEGRA210_FUSE_COMMON_SHIFT_FT_BITS 5
118b9cbd68dSMichal Meloun
119b9cbd68dSMichal Meloun
120b9cbd68dSMichal Meloun /* Only for Tegra124 */
121ef2ee5d0SMichal Meloun #define FUSE_SPARE_REALIGNMENT_REG 0x1fc
122ef2ee5d0SMichal Meloun #define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT 0
123ef2ee5d0SMichal Meloun #define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS 6
124ef2ee5d0SMichal Meloun
125b9cbd68dSMichal Meloun #define TEGRA124_NOMINAL_CALIB_FT 105
126b9cbd68dSMichal Meloun #define TEGRA124_NOMINAL_CALIB_CP 25
127b9cbd68dSMichal Meloun
128b9cbd68dSMichal Meloun #define TEGRA210_NOMINAL_CALIB_FT 105
129b9cbd68dSMichal Meloun #define TEGRA210_NOMINAL_CALIB_CP 25
130ef2ee5d0SMichal Meloun
131ef2ee5d0SMichal Meloun #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
132ef2ee5d0SMichal Meloun #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
133ef2ee5d0SMichal Meloun
134ef2ee5d0SMichal Meloun static struct sysctl_ctx_list soctherm_sysctl_ctx;
135ef2ee5d0SMichal Meloun
136ef2ee5d0SMichal Meloun struct tsensor_cfg {
137ef2ee5d0SMichal Meloun uint32_t tall;
138ef2ee5d0SMichal Meloun uint32_t tsample;
139ef2ee5d0SMichal Meloun uint32_t tiddq_en;
140ef2ee5d0SMichal Meloun uint32_t ten_count;
141ef2ee5d0SMichal Meloun uint32_t pdiv;
142ef2ee5d0SMichal Meloun uint32_t tsample_ate;
143ef2ee5d0SMichal Meloun uint32_t pdiv_ate;
144ef2ee5d0SMichal Meloun };
145ef2ee5d0SMichal Meloun
146b9cbd68dSMichal Meloun struct soctherm_shared_cal {
147b9cbd68dSMichal Meloun uint32_t base_cp;
148b9cbd68dSMichal Meloun uint32_t base_ft;
149b9cbd68dSMichal Meloun int32_t actual_temp_cp;
150b9cbd68dSMichal Meloun int32_t actual_temp_ft;
151b9cbd68dSMichal Meloun };
152b9cbd68dSMichal Meloun
153ef2ee5d0SMichal Meloun struct tsensor {
154ef2ee5d0SMichal Meloun char *name;
155ef2ee5d0SMichal Meloun int id;
156ef2ee5d0SMichal Meloun bus_addr_t sensor_base;
157ef2ee5d0SMichal Meloun bus_addr_t calib_fuse;
158ef2ee5d0SMichal Meloun int fuse_corr_alpha;
159ef2ee5d0SMichal Meloun int fuse_corr_beta;
160ef2ee5d0SMichal Meloun
161ef2ee5d0SMichal Meloun int16_t therm_a;
162ef2ee5d0SMichal Meloun int16_t therm_b;
163ef2ee5d0SMichal Meloun };
164ef2ee5d0SMichal Meloun
165b9cbd68dSMichal Meloun struct soctherm_soc;
166ef2ee5d0SMichal Meloun struct soctherm_softc {
167ef2ee5d0SMichal Meloun device_t dev;
168ef2ee5d0SMichal Meloun struct resource *mem_res;
169ef2ee5d0SMichal Meloun struct resource *irq_res;
170ef2ee5d0SMichal Meloun void *irq_ih;
171ef2ee5d0SMichal Meloun
172ef2ee5d0SMichal Meloun clk_t tsensor_clk;
173ef2ee5d0SMichal Meloun clk_t soctherm_clk;
174ef2ee5d0SMichal Meloun hwreset_t reset;
175ef2ee5d0SMichal Meloun
176b9cbd68dSMichal Meloun struct soctherm_soc *soc;
177b9cbd68dSMichal Meloun struct soctherm_shared_cal shared_cal;
178ef2ee5d0SMichal Meloun };
179ef2ee5d0SMichal Meloun
180b9cbd68dSMichal Meloun struct soctherm_soc {
181b9cbd68dSMichal Meloun void (*shared_cal)(struct soctherm_softc *sc);
182b9cbd68dSMichal Meloun uint32_t tsensor_pdiv;
183b9cbd68dSMichal Meloun uint32_t tsensor_hotspot_off;
184b9cbd68dSMichal Meloun struct tsensor_cfg *tsensor_cfg;
185b9cbd68dSMichal Meloun struct tsensor *tsensors;
186b9cbd68dSMichal Meloun int ntsensors;
187ef2ee5d0SMichal Meloun };
188ef2ee5d0SMichal Meloun
189b9cbd68dSMichal Meloun /* Tegra124 config */
190b9cbd68dSMichal Meloun
191ef2ee5d0SMichal Meloun static struct tsensor_cfg t124_tsensor_config = {
192ef2ee5d0SMichal Meloun .tall = 16300,
193ef2ee5d0SMichal Meloun .tsample = 120,
194ef2ee5d0SMichal Meloun .tiddq_en = 1,
195ef2ee5d0SMichal Meloun .ten_count = 1,
196ef2ee5d0SMichal Meloun .pdiv = 8,
197ef2ee5d0SMichal Meloun .tsample_ate = 480,
198ef2ee5d0SMichal Meloun .pdiv_ate = 8
199ef2ee5d0SMichal Meloun };
200ef2ee5d0SMichal Meloun
201ef2ee5d0SMichal Meloun static struct tsensor t124_tsensors[] = {
202ef2ee5d0SMichal Meloun {
203ef2ee5d0SMichal Meloun .name = "cpu0",
204ef2ee5d0SMichal Meloun .id = TEGRA124_SOCTHERM_SENSOR_CPU,
205ef2ee5d0SMichal Meloun .sensor_base = 0x0c0,
206ef2ee5d0SMichal Meloun .calib_fuse = 0x098,
207ef2ee5d0SMichal Meloun .fuse_corr_alpha = 1135400,
208ef2ee5d0SMichal Meloun .fuse_corr_beta = -6266900,
209ef2ee5d0SMichal Meloun },
210ef2ee5d0SMichal Meloun {
211ef2ee5d0SMichal Meloun .name = "cpu1",
212ef2ee5d0SMichal Meloun .id = -1,
213ef2ee5d0SMichal Meloun .sensor_base = 0x0e0,
214ef2ee5d0SMichal Meloun .calib_fuse = 0x084,
215ef2ee5d0SMichal Meloun .fuse_corr_alpha = 1122220,
216ef2ee5d0SMichal Meloun .fuse_corr_beta = -5700700,
217ef2ee5d0SMichal Meloun },
218ef2ee5d0SMichal Meloun {
219ef2ee5d0SMichal Meloun .name = "cpu2",
220ef2ee5d0SMichal Meloun .id = -1,
221ef2ee5d0SMichal Meloun .sensor_base = 0x100,
222ef2ee5d0SMichal Meloun .calib_fuse = 0x088,
223ef2ee5d0SMichal Meloun .fuse_corr_alpha = 1127000,
224ef2ee5d0SMichal Meloun .fuse_corr_beta = -6768200,
225ef2ee5d0SMichal Meloun },
226ef2ee5d0SMichal Meloun {
227ef2ee5d0SMichal Meloun .name = "cpu3",
228ef2ee5d0SMichal Meloun .id = -1,
229ef2ee5d0SMichal Meloun .sensor_base = 0x120,
230ef2ee5d0SMichal Meloun .calib_fuse = 0x12c,
231ef2ee5d0SMichal Meloun .fuse_corr_alpha = 1110900,
232ef2ee5d0SMichal Meloun .fuse_corr_beta = -6232000,
233ef2ee5d0SMichal Meloun },
234ef2ee5d0SMichal Meloun {
235ef2ee5d0SMichal Meloun .name = "mem0",
236ef2ee5d0SMichal Meloun .id = TEGRA124_SOCTHERM_SENSOR_MEM,
237ef2ee5d0SMichal Meloun .sensor_base = 0x140,
238ef2ee5d0SMichal Meloun .calib_fuse = 0x158,
239ef2ee5d0SMichal Meloun .fuse_corr_alpha = 1122300,
240ef2ee5d0SMichal Meloun .fuse_corr_beta = -5936400,
241ef2ee5d0SMichal Meloun },
242ef2ee5d0SMichal Meloun {
243ef2ee5d0SMichal Meloun .name = "mem1",
244ef2ee5d0SMichal Meloun .id = -1,
245ef2ee5d0SMichal Meloun .sensor_base = 0x160,
246ef2ee5d0SMichal Meloun .calib_fuse = 0x15c,
247ef2ee5d0SMichal Meloun .fuse_corr_alpha = 1145700,
248ef2ee5d0SMichal Meloun .fuse_corr_beta = -7124600,
249ef2ee5d0SMichal Meloun },
250ef2ee5d0SMichal Meloun {
251ef2ee5d0SMichal Meloun .name = "gpu",
252ef2ee5d0SMichal Meloun .id = TEGRA124_SOCTHERM_SENSOR_GPU,
253ef2ee5d0SMichal Meloun .sensor_base = 0x180,
254ef2ee5d0SMichal Meloun .calib_fuse = 0x154,
255ef2ee5d0SMichal Meloun .fuse_corr_alpha = 1120100,
256ef2ee5d0SMichal Meloun .fuse_corr_beta = -6000500,
257ef2ee5d0SMichal Meloun },
258ef2ee5d0SMichal Meloun {
259ef2ee5d0SMichal Meloun .name = "pllX",
260ef2ee5d0SMichal Meloun .id = TEGRA124_SOCTHERM_SENSOR_PLLX,
261ef2ee5d0SMichal Meloun .sensor_base = 0x1a0,
262ef2ee5d0SMichal Meloun .calib_fuse = 0x160,
263ef2ee5d0SMichal Meloun .fuse_corr_alpha = 1106500,
264ef2ee5d0SMichal Meloun .fuse_corr_beta = -6729300,
265ef2ee5d0SMichal Meloun },
266ef2ee5d0SMichal Meloun };
267ef2ee5d0SMichal Meloun
268b9cbd68dSMichal Meloun static void tegra124_shared_cal(struct soctherm_softc *sc);
269b9cbd68dSMichal Meloun
270b9cbd68dSMichal Meloun static struct soctherm_soc tegra124_soc = {
271b9cbd68dSMichal Meloun .shared_cal = tegra124_shared_cal,
272b9cbd68dSMichal Meloun .tsensor_pdiv = 0x8888,
273b9cbd68dSMichal Meloun .tsensor_hotspot_off = 0x00060600 ,
274b9cbd68dSMichal Meloun .tsensor_cfg = &t124_tsensor_config,
275b9cbd68dSMichal Meloun .tsensors = t124_tsensors,
276b9cbd68dSMichal Meloun .ntsensors = nitems(t124_tsensors),
277b9cbd68dSMichal Meloun };
278b9cbd68dSMichal Meloun
279b9cbd68dSMichal Meloun /* Tegra210 config */
280b9cbd68dSMichal Meloun static struct tsensor_cfg t210_tsensor_config = {
281b9cbd68dSMichal Meloun .tall = 16300,
282b9cbd68dSMichal Meloun .tsample = 120,
283b9cbd68dSMichal Meloun .tiddq_en = 1,
284b9cbd68dSMichal Meloun .ten_count = 1,
285b9cbd68dSMichal Meloun .pdiv = 8,
286b9cbd68dSMichal Meloun .tsample_ate = 480,
287b9cbd68dSMichal Meloun .pdiv_ate = 8
288b9cbd68dSMichal Meloun };
289b9cbd68dSMichal Meloun
290b9cbd68dSMichal Meloun static struct tsensor t210_tsensors[] = {
291b9cbd68dSMichal Meloun {
292b9cbd68dSMichal Meloun .name = "cpu0",
293b9cbd68dSMichal Meloun .id = TEGRA124_SOCTHERM_SENSOR_CPU,
294b9cbd68dSMichal Meloun .sensor_base = 0x0c0,
295b9cbd68dSMichal Meloun .calib_fuse = 0x098,
296b9cbd68dSMichal Meloun .fuse_corr_alpha = 1085000,
297b9cbd68dSMichal Meloun .fuse_corr_beta = 3244200,
298b9cbd68dSMichal Meloun },
299b9cbd68dSMichal Meloun {
300b9cbd68dSMichal Meloun .name = "cpu1",
301b9cbd68dSMichal Meloun .id = -1,
302b9cbd68dSMichal Meloun .sensor_base = 0x0e0,
303b9cbd68dSMichal Meloun .calib_fuse = 0x084,
304b9cbd68dSMichal Meloun .fuse_corr_alpha = 1126200,
305b9cbd68dSMichal Meloun .fuse_corr_beta = -67500,
306b9cbd68dSMichal Meloun },
307b9cbd68dSMichal Meloun {
308b9cbd68dSMichal Meloun .name = "cpu2",
309b9cbd68dSMichal Meloun .id = -1,
310b9cbd68dSMichal Meloun .sensor_base = 0x100,
311b9cbd68dSMichal Meloun .calib_fuse = 0x088,
312b9cbd68dSMichal Meloun .fuse_corr_alpha = 1098400,
313b9cbd68dSMichal Meloun .fuse_corr_beta = 2251100,
314b9cbd68dSMichal Meloun },
315b9cbd68dSMichal Meloun {
316b9cbd68dSMichal Meloun .name = "cpu3",
317b9cbd68dSMichal Meloun .id = -1,
318b9cbd68dSMichal Meloun .sensor_base = 0x120,
319b9cbd68dSMichal Meloun .calib_fuse = 0x12c,
320b9cbd68dSMichal Meloun .fuse_corr_alpha = 1108000,
321b9cbd68dSMichal Meloun .fuse_corr_beta = 602700,
322b9cbd68dSMichal Meloun },
323b9cbd68dSMichal Meloun {
324b9cbd68dSMichal Meloun .name = "mem0",
325b9cbd68dSMichal Meloun .id = TEGRA124_SOCTHERM_SENSOR_MEM,
326b9cbd68dSMichal Meloun .sensor_base = 0x140,
327b9cbd68dSMichal Meloun .calib_fuse = 0x158,
328b9cbd68dSMichal Meloun .fuse_corr_alpha = 1069200,
329b9cbd68dSMichal Meloun .fuse_corr_beta = 3549900,
330b9cbd68dSMichal Meloun },
331b9cbd68dSMichal Meloun {
332b9cbd68dSMichal Meloun .name = "mem1",
333b9cbd68dSMichal Meloun .id = -1,
334b9cbd68dSMichal Meloun .sensor_base = 0x160,
335b9cbd68dSMichal Meloun .calib_fuse = 0x15c,
336b9cbd68dSMichal Meloun .fuse_corr_alpha = 1173700,
337b9cbd68dSMichal Meloun .fuse_corr_beta = -6263600,
338b9cbd68dSMichal Meloun },
339b9cbd68dSMichal Meloun {
340b9cbd68dSMichal Meloun .name = "gpu",
341b9cbd68dSMichal Meloun .id = TEGRA124_SOCTHERM_SENSOR_GPU,
342b9cbd68dSMichal Meloun .sensor_base = 0x180,
343b9cbd68dSMichal Meloun .calib_fuse = 0x154,
344b9cbd68dSMichal Meloun .fuse_corr_alpha = 1074300,
345b9cbd68dSMichal Meloun .fuse_corr_beta = 2734900,
346b9cbd68dSMichal Meloun },
347b9cbd68dSMichal Meloun {
348b9cbd68dSMichal Meloun .name = "pllX",
349b9cbd68dSMichal Meloun .id = TEGRA124_SOCTHERM_SENSOR_PLLX,
350b9cbd68dSMichal Meloun .sensor_base = 0x1a0,
351b9cbd68dSMichal Meloun .calib_fuse = 0x160,
352b9cbd68dSMichal Meloun .fuse_corr_alpha = 1039700,
353b9cbd68dSMichal Meloun .fuse_corr_beta = 6829100,
354b9cbd68dSMichal Meloun },
355b9cbd68dSMichal Meloun };
356b9cbd68dSMichal Meloun
357b9cbd68dSMichal Meloun static void tegra210_shared_cal(struct soctherm_softc *sc);
358b9cbd68dSMichal Meloun
359b9cbd68dSMichal Meloun static struct soctherm_soc tegra210_soc = {
360b9cbd68dSMichal Meloun .shared_cal = tegra210_shared_cal,
361b9cbd68dSMichal Meloun .tsensor_pdiv = 0x8888,
362b9cbd68dSMichal Meloun .tsensor_hotspot_off = 0x000A0500 ,
363b9cbd68dSMichal Meloun .tsensor_cfg = &t210_tsensor_config,
364b9cbd68dSMichal Meloun .tsensors = t210_tsensors,
365b9cbd68dSMichal Meloun .ntsensors = nitems(t210_tsensors),
366b9cbd68dSMichal Meloun };
367b9cbd68dSMichal Meloun
368b9cbd68dSMichal Meloun static struct ofw_compat_data compat_data[] = {
369b9cbd68dSMichal Meloun {"nvidia,tegra124-soctherm", (uintptr_t)&tegra124_soc},
370b9cbd68dSMichal Meloun {"nvidia,tegra210-soctherm", (uintptr_t)&tegra210_soc},
371b9cbd68dSMichal Meloun {NULL, 0},
372b9cbd68dSMichal Meloun };
373b9cbd68dSMichal Meloun
374ef2ee5d0SMichal Meloun /* Extract signed integer bitfield from register */
375ef2ee5d0SMichal Meloun static int
extract_signed(uint32_t reg,int shift,int bits)376ef2ee5d0SMichal Meloun extract_signed(uint32_t reg, int shift, int bits)
377ef2ee5d0SMichal Meloun {
378ef2ee5d0SMichal Meloun int32_t val;
379ef2ee5d0SMichal Meloun uint32_t mask;
380ef2ee5d0SMichal Meloun
381ef2ee5d0SMichal Meloun mask = (1 << bits) - 1;
382ef2ee5d0SMichal Meloun val = ((reg >> shift) & mask) << (32 - bits);
383ef2ee5d0SMichal Meloun val >>= 32 - bits;
384ef2ee5d0SMichal Meloun return ((int32_t)val);
385ef2ee5d0SMichal Meloun }
386ef2ee5d0SMichal Meloun
387b9cbd68dSMichal Meloun static inline
div64_s64_precise(int64_t a,int64_t b)388b9cbd68dSMichal Meloun int64_t div64_s64_precise(int64_t a, int64_t b)
389ef2ee5d0SMichal Meloun {
390ef2ee5d0SMichal Meloun int64_t r, al;
391ef2ee5d0SMichal Meloun
392ef2ee5d0SMichal Meloun al = a << 16;
393ef2ee5d0SMichal Meloun r = (al * 2 + 1) / (2 * b);
394b9cbd68dSMichal Meloun return (r >> 16);
395ef2ee5d0SMichal Meloun }
396ef2ee5d0SMichal Meloun
397ef2ee5d0SMichal Meloun static void
tegra124_shared_cal(struct soctherm_softc * sc)398b9cbd68dSMichal Meloun tegra124_shared_cal(struct soctherm_softc *sc)
399ef2ee5d0SMichal Meloun {
400ef2ee5d0SMichal Meloun uint32_t val;
401ef2ee5d0SMichal Meloun int calib_cp, calib_ft;
402b9cbd68dSMichal Meloun struct soctherm_shared_cal *cal;
403ef2ee5d0SMichal Meloun
404b9cbd68dSMichal Meloun cal = &sc->shared_cal;
405b9cbd68dSMichal Meloun val = tegra_fuse_read_4(FUSE_TSENSOR_COMMON);
406b9cbd68dSMichal Meloun cal->base_cp = TEGRA124_FUSE_COMMON_CP_TS_BASE(val);
407b9cbd68dSMichal Meloun cal->base_ft = TEGRA124_FUSE_COMMON_FT_TS_BASE(val);
408b9cbd68dSMichal Meloun
409b9cbd68dSMichal Meloun calib_ft = extract_signed(val,
410b9cbd68dSMichal Meloun TEGRA124_FUSE_COMMON_SHIFT_FT_SHIFT,
411b9cbd68dSMichal Meloun TEGRA124_FUSE_COMMON_SHIFT_FT_BITS);
412ef2ee5d0SMichal Meloun
413ef2ee5d0SMichal Meloun val = tegra_fuse_read_4(FUSE_SPARE_REALIGNMENT_REG);
414ef2ee5d0SMichal Meloun calib_cp = extract_signed(val,
415ef2ee5d0SMichal Meloun FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_SHIFT,
416ef2ee5d0SMichal Meloun FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_BITS);
417ef2ee5d0SMichal Meloun
418b9cbd68dSMichal Meloun cal->actual_temp_cp = 2 * TEGRA124_NOMINAL_CALIB_CP + calib_cp;
419b9cbd68dSMichal Meloun cal->actual_temp_ft = 2 * TEGRA124_NOMINAL_CALIB_FT + calib_ft;
420ef2ee5d0SMichal Meloun #ifdef DEBUG
421ef2ee5d0SMichal Meloun printf("%s: base_cp: %u, base_ft: %d,"
422ef2ee5d0SMichal Meloun " actual_temp_cp: %d, actual_temp_ft: %d\n",
423ef2ee5d0SMichal Meloun __func__, cal->base_cp, cal->base_ft,
424ef2ee5d0SMichal Meloun cal->actual_temp_cp, cal->actual_temp_ft);
425ef2ee5d0SMichal Meloun #endif
426ef2ee5d0SMichal Meloun }
427ef2ee5d0SMichal Meloun
428ef2ee5d0SMichal Meloun static void
tegra210_shared_cal(struct soctherm_softc * sc)429b9cbd68dSMichal Meloun tegra210_shared_cal(struct soctherm_softc *sc)
430b9cbd68dSMichal Meloun {
431b9cbd68dSMichal Meloun uint32_t val;
432b9cbd68dSMichal Meloun int calib_cp, calib_ft;
433b9cbd68dSMichal Meloun struct soctherm_shared_cal *cal;
434b9cbd68dSMichal Meloun
435b9cbd68dSMichal Meloun cal = &sc->shared_cal;
436b9cbd68dSMichal Meloun
437b9cbd68dSMichal Meloun val = tegra_fuse_read_4(FUSE_TSENSOR_COMMON);
438b9cbd68dSMichal Meloun cal->base_cp = TEGRA210_FUSE_COMMON_CP_TS_BASE(val);
439b9cbd68dSMichal Meloun cal->base_ft = TEGRA210_FUSE_COMMON_FT_TS_BASE(val);
440b9cbd68dSMichal Meloun
441b9cbd68dSMichal Meloun calib_ft = extract_signed(val,
442b9cbd68dSMichal Meloun TEGRA210_FUSE_COMMON_SHIFT_FT_SHIFT,
443b9cbd68dSMichal Meloun TEGRA210_FUSE_COMMON_SHIFT_FT_BITS);
444b9cbd68dSMichal Meloun calib_cp = extract_signed(val,
445b9cbd68dSMichal Meloun TEGRA210_FUSE_COMMON_SHIFT_CP_SHIFT,
446b9cbd68dSMichal Meloun TEGRA210_FUSE_COMMON_SHIFT_CP_BITS);
447b9cbd68dSMichal Meloun
448b9cbd68dSMichal Meloun cal->actual_temp_cp = 2 * TEGRA210_NOMINAL_CALIB_CP + calib_cp;
449b9cbd68dSMichal Meloun cal->actual_temp_ft = 2 * TEGRA210_NOMINAL_CALIB_FT + calib_ft;
450b9cbd68dSMichal Meloun #ifdef DEBUG
451b9cbd68dSMichal Meloun printf("%s: base_cp: %u, base_ft: %d,"
452b9cbd68dSMichal Meloun " actual_temp_cp: %d, actual_temp_ft: %d\n",
453b9cbd68dSMichal Meloun __func__, cal->base_cp, cal->base_ft,
454b9cbd68dSMichal Meloun cal->actual_temp_cp, cal->actual_temp_ft);
455b9cbd68dSMichal Meloun #endif
456b9cbd68dSMichal Meloun }
457b9cbd68dSMichal Meloun
458b9cbd68dSMichal Meloun static void
tsensor_calibration(struct soctherm_softc * sc,struct tsensor * sensor)459b9cbd68dSMichal Meloun tsensor_calibration(struct soctherm_softc *sc, struct tsensor *sensor)
460ef2ee5d0SMichal Meloun {
461ef2ee5d0SMichal Meloun uint32_t val;
462ef2ee5d0SMichal Meloun int mult, div, calib_cp, calib_ft;
463ef2ee5d0SMichal Meloun int actual_tsensor_ft, actual_tsensor_cp, delta_sens, delta_temp;
464ef2ee5d0SMichal Meloun int temp_a, temp_b;
465b9cbd68dSMichal Meloun struct tsensor_cfg *cfg;
466b9cbd68dSMichal Meloun struct soctherm_shared_cal *cal;
467ef2ee5d0SMichal Meloun int64_t tmp;
468ef2ee5d0SMichal Meloun
469b9cbd68dSMichal Meloun cfg = sc->soc->tsensor_cfg;
470b9cbd68dSMichal Meloun cal = &sc->shared_cal;
471b9cbd68dSMichal Meloun
472ef2ee5d0SMichal Meloun val = tegra_fuse_read_4(sensor->calib_fuse);
473ef2ee5d0SMichal Meloun calib_cp = extract_signed(val,
474ef2ee5d0SMichal Meloun FUSE_TSENSOR_CALIB_CP_TS_BASE_SHIFT,
475ef2ee5d0SMichal Meloun FUSE_TSENSOR_CALIB_CP_TS_BASE_BITS);
476b9cbd68dSMichal Meloun actual_tsensor_cp = cal->base_cp * 64 + calib_cp;
477ef2ee5d0SMichal Meloun
478ef2ee5d0SMichal Meloun calib_ft = extract_signed(val,
479ef2ee5d0SMichal Meloun FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT,
480ef2ee5d0SMichal Meloun FUSE_TSENSOR_CALIB_FT_TS_BASE_BITS);
481b9cbd68dSMichal Meloun actual_tsensor_ft = cal->base_ft * 32 + calib_ft;
482ef2ee5d0SMichal Meloun
483ef2ee5d0SMichal Meloun delta_sens = actual_tsensor_ft - actual_tsensor_cp;
484b9cbd68dSMichal Meloun delta_temp = cal->actual_temp_ft - cal->actual_temp_cp;
485b9cbd68dSMichal Meloun mult = cfg->pdiv * cfg->tsample_ate;
486b9cbd68dSMichal Meloun div = cfg->tsample * cfg->pdiv_ate;
487ef2ee5d0SMichal Meloun
488ef2ee5d0SMichal Meloun temp_a = div64_s64_precise((int64_t) delta_temp * (1LL << 13) * mult,
489ef2ee5d0SMichal Meloun (int64_t) delta_sens * div);
490ef2ee5d0SMichal Meloun
491b9cbd68dSMichal Meloun tmp = (int64_t)actual_tsensor_ft * cal->actual_temp_cp -
492b9cbd68dSMichal Meloun (int64_t)actual_tsensor_cp * cal->actual_temp_ft;
493ef2ee5d0SMichal Meloun temp_b = div64_s64_precise(tmp, (int64_t)delta_sens);
494ef2ee5d0SMichal Meloun
495ef2ee5d0SMichal Meloun temp_a = div64_s64_precise((int64_t)temp_a * sensor->fuse_corr_alpha,
496ef2ee5d0SMichal Meloun 1000000);
497ef2ee5d0SMichal Meloun temp_b = div64_s64_precise((int64_t)temp_b * sensor->fuse_corr_alpha +
498ef2ee5d0SMichal Meloun sensor->fuse_corr_beta, 1000000);
499ef2ee5d0SMichal Meloun sensor->therm_a = (int16_t)temp_a;
500ef2ee5d0SMichal Meloun sensor->therm_b = (int16_t)temp_b;
501ef2ee5d0SMichal Meloun #ifdef DEBUG
502ef2ee5d0SMichal Meloun printf("%s: sensor %s fuse: 0x%08X (0x%04X, 0x%04X)"
503ef2ee5d0SMichal Meloun " calib_cp: %d(0x%04X), calib_ft: %d(0x%04X)\n",
504ef2ee5d0SMichal Meloun __func__, sensor->name, val, val & 0x1FFF, (val >> 13) & 0x1FFF,
505ef2ee5d0SMichal Meloun calib_cp, calib_cp, calib_ft, calib_ft);
506ef2ee5d0SMichal Meloun printf("therma: 0x%04X(%d), thermb: 0x%04X(%d)\n",
507b9cbd68dSMichal Meloun (uint16_t)sensor->therm_a, sensor->therm_a,
508ef2ee5d0SMichal Meloun (uint16_t)sensor->therm_b, sensor->therm_b);
509ef2ee5d0SMichal Meloun #endif
510ef2ee5d0SMichal Meloun }
511ef2ee5d0SMichal Meloun
512ef2ee5d0SMichal Meloun static void
soctherm_init_tsensor(struct soctherm_softc * sc,struct tsensor * sensor)513b9cbd68dSMichal Meloun soctherm_init_tsensor(struct soctherm_softc *sc, struct tsensor *sensor)
514ef2ee5d0SMichal Meloun {
515b9cbd68dSMichal Meloun struct tsensor_cfg *cfg;
516ef2ee5d0SMichal Meloun uint32_t val;
517ef2ee5d0SMichal Meloun
518b9cbd68dSMichal Meloun cfg = sc->soc->tsensor_cfg;
519b9cbd68dSMichal Meloun tsensor_calibration(sc, sensor);
520ef2ee5d0SMichal Meloun
521ef2ee5d0SMichal Meloun val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0);
522ef2ee5d0SMichal Meloun val |= TSENSOR_CONFIG0_STOP;
523ef2ee5d0SMichal Meloun val |= TSENSOR_CONFIG0_STATUS_CLR;
524ef2ee5d0SMichal Meloun WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
525ef2ee5d0SMichal Meloun
526b9cbd68dSMichal Meloun val = TSENSOR_CONFIG0_TALL(cfg->tall);
527ef2ee5d0SMichal Meloun val |= TSENSOR_CONFIG0_STOP;
528ef2ee5d0SMichal Meloun WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
529ef2ee5d0SMichal Meloun
530b9cbd68dSMichal Meloun val = TSENSOR_CONFIG1_TSAMPLE(cfg->tsample - 1);
531b9cbd68dSMichal Meloun val |= TSENSOR_CONFIG1_TIDDQ_EN(cfg->tiddq_en);
532b9cbd68dSMichal Meloun val |= TSENSOR_CONFIG1_TEN_COUNT(cfg->ten_count);
533ef2ee5d0SMichal Meloun val |= TSENSOR_CONFIG1_TEMP_ENABLE;
534ef2ee5d0SMichal Meloun WR4(sc, sensor->sensor_base + TSENSOR_CONFIG1, val);
535ef2ee5d0SMichal Meloun
536ef2ee5d0SMichal Meloun val = TSENSOR_CONFIG2_THERMA((uint16_t)sensor->therm_a) |
537ef2ee5d0SMichal Meloun TSENSOR_CONFIG2_THERMB((uint16_t)sensor->therm_b);
538ef2ee5d0SMichal Meloun WR4(sc, sensor->sensor_base + TSENSOR_CONFIG2, val);
539ef2ee5d0SMichal Meloun
540ef2ee5d0SMichal Meloun val = RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0);
541ef2ee5d0SMichal Meloun val &= ~TSENSOR_CONFIG0_STOP;
542ef2ee5d0SMichal Meloun WR4(sc, sensor->sensor_base + TSENSOR_CONFIG0, val);
543ef2ee5d0SMichal Meloun #ifdef DEBUG
544ef2ee5d0SMichal Meloun printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X,"
545ef2ee5d0SMichal Meloun " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name,
546ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0),
547ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1),
548ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2),
549ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_STATUS0),
550ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_STATUS1),
551ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_STATUS2)
552ef2ee5d0SMichal Meloun );
553ef2ee5d0SMichal Meloun #endif
554ef2ee5d0SMichal Meloun }
555ef2ee5d0SMichal Meloun
556ef2ee5d0SMichal Meloun static int
soctherm_convert_raw(uint32_t val)557ef2ee5d0SMichal Meloun soctherm_convert_raw(uint32_t val)
558ef2ee5d0SMichal Meloun {
559ef2ee5d0SMichal Meloun int32_t t;
560ef2ee5d0SMichal Meloun
561b9cbd68dSMichal Meloun t = READBACK_VALUE(val) * 1000;
562ef2ee5d0SMichal Meloun if (val & READBACK_ADD_HALF)
563ef2ee5d0SMichal Meloun t += 500;
564ef2ee5d0SMichal Meloun if (val & READBACK_NEGATE)
565ef2ee5d0SMichal Meloun t *= -1;
566ef2ee5d0SMichal Meloun
567b9cbd68dSMichal Meloun return (t);
568ef2ee5d0SMichal Meloun }
569ef2ee5d0SMichal Meloun
570ef2ee5d0SMichal Meloun static int
soctherm_read_temp(struct soctherm_softc * sc,struct tsensor * sensor,int * temp)571ef2ee5d0SMichal Meloun soctherm_read_temp(struct soctherm_softc *sc, struct tsensor *sensor, int *temp)
572ef2ee5d0SMichal Meloun {
573ef2ee5d0SMichal Meloun int timeout;
574ef2ee5d0SMichal Meloun uint32_t val;
575ef2ee5d0SMichal Meloun
576ef2ee5d0SMichal Meloun /* wait for valid sample */
577b9cbd68dSMichal Meloun for (timeout = 100; timeout > 0; timeout--) {
578ef2ee5d0SMichal Meloun val = RD4(sc, sensor->sensor_base + TSENSOR_STATUS1);
579ef2ee5d0SMichal Meloun if ((val & TSENSOR_STATUS1_TEMP_VALID) != 0)
580ef2ee5d0SMichal Meloun break;
581ef2ee5d0SMichal Meloun DELAY(100);
582ef2ee5d0SMichal Meloun }
583ef2ee5d0SMichal Meloun if (timeout <= 0)
584ef2ee5d0SMichal Meloun device_printf(sc->dev, "Sensor %s timeouted\n", sensor->name);
585ef2ee5d0SMichal Meloun *temp = soctherm_convert_raw(val);
586ef2ee5d0SMichal Meloun #ifdef DEBUG
587ef2ee5d0SMichal Meloun printf("%s: Raw: 0x%08X, temp: %d\n", __func__, val, *temp);
588ef2ee5d0SMichal Meloun printf(" Sensor: %s cfg:0x%08X, 0x%08X, 0x%08X,"
589ef2ee5d0SMichal Meloun " sts:0x%08X, 0x%08X, 0x%08X\n", sensor->name,
590ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_CONFIG0),
591ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_CONFIG1),
592ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_CONFIG2),
593ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_STATUS0),
594ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_STATUS1),
595ef2ee5d0SMichal Meloun RD4(sc, sensor->sensor_base + TSENSOR_STATUS2)
596ef2ee5d0SMichal Meloun );
597ef2ee5d0SMichal Meloun #endif
598b9cbd68dSMichal Meloun return (0);
599ef2ee5d0SMichal Meloun }
600ef2ee5d0SMichal Meloun
601ef2ee5d0SMichal Meloun static int
soctherm_get_temp(device_t dev,device_t cdev,uintptr_t id,int * val)602ef2ee5d0SMichal Meloun soctherm_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val)
603ef2ee5d0SMichal Meloun {
604ef2ee5d0SMichal Meloun struct soctherm_softc *sc;
605ef2ee5d0SMichal Meloun int i;
606ef2ee5d0SMichal Meloun
607ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
608ef2ee5d0SMichal Meloun /* The direct sensor map starts at 0x100 */
609ef2ee5d0SMichal Meloun if (id >= 0x100) {
610ef2ee5d0SMichal Meloun id -= 0x100;
611b9cbd68dSMichal Meloun if (id >= sc->soc->ntsensors)
612ef2ee5d0SMichal Meloun return (ERANGE);
613b9cbd68dSMichal Meloun return(soctherm_read_temp(sc, sc->soc->tsensors + id, val));
614ef2ee5d0SMichal Meloun }
615ef2ee5d0SMichal Meloun /* Linux (DT) compatible thermal zones */
616b9cbd68dSMichal Meloun for (i = 0; i < sc->soc->ntsensors; i++) {
617b9cbd68dSMichal Meloun if (sc->soc->tsensors->id == id) {
618b9cbd68dSMichal Meloun return(soctherm_read_temp(sc, sc->soc->tsensors + id,
619b9cbd68dSMichal Meloun val));
620b9cbd68dSMichal Meloun }
621ef2ee5d0SMichal Meloun }
622ef2ee5d0SMichal Meloun return (ERANGE);
623ef2ee5d0SMichal Meloun }
624ef2ee5d0SMichal Meloun
625ef2ee5d0SMichal Meloun static int
soctherm_sysctl_temperature(SYSCTL_HANDLER_ARGS)626ef2ee5d0SMichal Meloun soctherm_sysctl_temperature(SYSCTL_HANDLER_ARGS)
627ef2ee5d0SMichal Meloun {
628ef2ee5d0SMichal Meloun struct soctherm_softc *sc;
629ef2ee5d0SMichal Meloun int val;
630ef2ee5d0SMichal Meloun int rv;
631ef2ee5d0SMichal Meloun int id;
632ef2ee5d0SMichal Meloun
633ef2ee5d0SMichal Meloun /* Write request */
634ef2ee5d0SMichal Meloun if (req->newptr != NULL)
635ef2ee5d0SMichal Meloun return (EINVAL);
636ef2ee5d0SMichal Meloun
637ef2ee5d0SMichal Meloun sc = arg1;
638ef2ee5d0SMichal Meloun id = arg2;
639ef2ee5d0SMichal Meloun
640b9cbd68dSMichal Meloun if (id >= sc->soc->ntsensors)
641ef2ee5d0SMichal Meloun return (ERANGE);
642b9cbd68dSMichal Meloun rv = soctherm_read_temp(sc, sc->soc->tsensors + id, &val);
643ef2ee5d0SMichal Meloun if (rv != 0)
644ef2ee5d0SMichal Meloun return (rv);
645ef2ee5d0SMichal Meloun
646ef2ee5d0SMichal Meloun val = val / 100;
647ef2ee5d0SMichal Meloun val += 2731;
648ef2ee5d0SMichal Meloun rv = sysctl_handle_int(oidp, &val, 0, req);
649ef2ee5d0SMichal Meloun return (rv);
650ef2ee5d0SMichal Meloun }
651ef2ee5d0SMichal Meloun
652ef2ee5d0SMichal Meloun static int
soctherm_init_sysctl(struct soctherm_softc * sc)653ef2ee5d0SMichal Meloun soctherm_init_sysctl(struct soctherm_softc *sc)
654ef2ee5d0SMichal Meloun {
655ef2ee5d0SMichal Meloun int i;
656ef2ee5d0SMichal Meloun struct sysctl_oid *oid, *tmp;
657ef2ee5d0SMichal Meloun
658ef2ee5d0SMichal Meloun sysctl_ctx_init(&soctherm_sysctl_ctx);
659ef2ee5d0SMichal Meloun /* create node for hw.temp */
660ef2ee5d0SMichal Meloun oid = SYSCTL_ADD_NODE(&soctherm_sysctl_ctx,
661ef2ee5d0SMichal Meloun SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature",
6627029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
663ef2ee5d0SMichal Meloun if (oid == NULL)
664ef2ee5d0SMichal Meloun return (ENXIO);
665ef2ee5d0SMichal Meloun
666ef2ee5d0SMichal Meloun /* Add sensors */
667b9cbd68dSMichal Meloun for (i = sc->soc->ntsensors - 1; i >= 0; i--) {
668ef2ee5d0SMichal Meloun tmp = SYSCTL_ADD_PROC(&soctherm_sysctl_ctx,
669b9cbd68dSMichal Meloun SYSCTL_CHILDREN(oid), OID_AUTO, sc->soc->tsensors[i].name,
6707029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, i,
671ef2ee5d0SMichal Meloun soctherm_sysctl_temperature, "IK", "SoC Temperature");
672ef2ee5d0SMichal Meloun if (tmp == NULL)
673ef2ee5d0SMichal Meloun return (ENXIO);
674ef2ee5d0SMichal Meloun }
675ef2ee5d0SMichal Meloun
676ef2ee5d0SMichal Meloun return (0);
677ef2ee5d0SMichal Meloun }
678ef2ee5d0SMichal Meloun
679ef2ee5d0SMichal Meloun static int
soctherm_probe(device_t dev)680ef2ee5d0SMichal Meloun soctherm_probe(device_t dev)
681ef2ee5d0SMichal Meloun {
682ef2ee5d0SMichal Meloun
683ef2ee5d0SMichal Meloun if (!ofw_bus_status_okay(dev))
684ef2ee5d0SMichal Meloun return (ENXIO);
685ef2ee5d0SMichal Meloun
686ef2ee5d0SMichal Meloun if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
687ef2ee5d0SMichal Meloun return (ENXIO);
688ef2ee5d0SMichal Meloun
689ef2ee5d0SMichal Meloun device_set_desc(dev, "Tegra temperature sensors");
690ef2ee5d0SMichal Meloun return (BUS_PROBE_DEFAULT);
691ef2ee5d0SMichal Meloun }
692ef2ee5d0SMichal Meloun
693ef2ee5d0SMichal Meloun static int
soctherm_attach(device_t dev)694ef2ee5d0SMichal Meloun soctherm_attach(device_t dev)
695ef2ee5d0SMichal Meloun {
696ef2ee5d0SMichal Meloun struct soctherm_softc *sc;
697ef2ee5d0SMichal Meloun phandle_t node;
698ef2ee5d0SMichal Meloun int i, rid, rv;
699ef2ee5d0SMichal Meloun
700ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
701ef2ee5d0SMichal Meloun sc->dev = dev;
702b9cbd68dSMichal Meloun sc->soc = (struct soctherm_soc *)ofw_bus_search_compatible(dev,
703b9cbd68dSMichal Meloun compat_data)->ocd_data;
704ef2ee5d0SMichal Meloun node = ofw_bus_get_node(sc->dev);
705ef2ee5d0SMichal Meloun
706ef2ee5d0SMichal Meloun rid = 0;
707ef2ee5d0SMichal Meloun sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
708ef2ee5d0SMichal Meloun RF_ACTIVE);
709ef2ee5d0SMichal Meloun if (sc->mem_res == NULL) {
710ef2ee5d0SMichal Meloun device_printf(dev, "Cannot allocate memory resources\n");
711ef2ee5d0SMichal Meloun goto fail;
712ef2ee5d0SMichal Meloun }
713ef2ee5d0SMichal Meloun
714ef2ee5d0SMichal Meloun rid = 0;
715ef2ee5d0SMichal Meloun sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
716ef2ee5d0SMichal Meloun if (sc->irq_res == NULL) {
717ef2ee5d0SMichal Meloun device_printf(dev, "Cannot allocate IRQ resources\n");
718ef2ee5d0SMichal Meloun goto fail;
719ef2ee5d0SMichal Meloun }
720ef2ee5d0SMichal Meloun
721ef2ee5d0SMichal Meloun /*
722ef2ee5d0SMichal Meloun if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC,
723ef2ee5d0SMichal Meloun soctherm_intr, NULL, sc, &sc->irq_ih))) {
724ef2ee5d0SMichal Meloun device_printf(dev,
725ef2ee5d0SMichal Meloun "WARNING: unable to register interrupt handler\n");
726ef2ee5d0SMichal Meloun goto fail;
727ef2ee5d0SMichal Meloun }
728ef2ee5d0SMichal Meloun */
729ef2ee5d0SMichal Meloun
730ef2ee5d0SMichal Meloun /* OWF resources */
731dac93553SMichal Meloun rv = hwreset_get_by_ofw_name(dev, 0, "soctherm", &sc->reset);
732ef2ee5d0SMichal Meloun if (rv != 0) {
733ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get fuse reset\n");
734ef2ee5d0SMichal Meloun goto fail;
735ef2ee5d0SMichal Meloun }
736dac93553SMichal Meloun rv = clk_get_by_ofw_name(dev, 0, "tsensor", &sc->tsensor_clk);
737ef2ee5d0SMichal Meloun if (rv != 0) {
738ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get 'tsensor' clock: %d\n", rv);
739ef2ee5d0SMichal Meloun goto fail;
740ef2ee5d0SMichal Meloun }
741dac93553SMichal Meloun rv = clk_get_by_ofw_name(dev, 0, "soctherm", &sc->soctherm_clk);
742ef2ee5d0SMichal Meloun if (rv != 0) {
743ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get 'soctherm' clock: %d\n", rv);
744ef2ee5d0SMichal Meloun goto fail;
745ef2ee5d0SMichal Meloun }
746ef2ee5d0SMichal Meloun
747ef2ee5d0SMichal Meloun rv = hwreset_assert(sc->reset);
748ef2ee5d0SMichal Meloun if (rv != 0) {
749ef2ee5d0SMichal Meloun device_printf(dev, "Cannot assert reset\n");
750ef2ee5d0SMichal Meloun goto fail;
751ef2ee5d0SMichal Meloun }
752ef2ee5d0SMichal Meloun rv = clk_enable(sc->tsensor_clk);
753ef2ee5d0SMichal Meloun if (rv != 0) {
754ef2ee5d0SMichal Meloun device_printf(dev, "Cannot enable 'tsensor' clock: %d\n", rv);
755ef2ee5d0SMichal Meloun goto fail;
756ef2ee5d0SMichal Meloun }
757ef2ee5d0SMichal Meloun rv = clk_enable(sc->soctherm_clk);
758ef2ee5d0SMichal Meloun if (rv != 0) {
759ef2ee5d0SMichal Meloun device_printf(dev, "Cannot enable 'soctherm' clock: %d\n", rv);
760ef2ee5d0SMichal Meloun goto fail;
761ef2ee5d0SMichal Meloun }
762ef2ee5d0SMichal Meloun rv = hwreset_deassert(sc->reset);
763ef2ee5d0SMichal Meloun if (rv != 0) {
764ef2ee5d0SMichal Meloun device_printf(dev, "Cannot clear reset\n");
765ef2ee5d0SMichal Meloun goto fail;
766ef2ee5d0SMichal Meloun }
767ef2ee5d0SMichal Meloun
768b9cbd68dSMichal Meloun sc->soc->shared_cal(sc);
769ef2ee5d0SMichal Meloun
770b9cbd68dSMichal Meloun WR4(sc, TSENSOR_PDIV, sc->soc->tsensor_pdiv);
771b9cbd68dSMichal Meloun WR4(sc, TSENSOR_HOTSPOT_OFF, sc->soc->tsensor_hotspot_off);
772ef2ee5d0SMichal Meloun
773b9cbd68dSMichal Meloun for (i = 0; i < sc->soc->ntsensors; i++)
774b9cbd68dSMichal Meloun soctherm_init_tsensor(sc, sc->soc->tsensors + i);
775ef2ee5d0SMichal Meloun
776ef2ee5d0SMichal Meloun rv = soctherm_init_sysctl(sc);
777ef2ee5d0SMichal Meloun if (rv != 0) {
778ef2ee5d0SMichal Meloun device_printf(sc->dev, "Cannot initialize sysctls\n");
779ef2ee5d0SMichal Meloun goto fail;
780ef2ee5d0SMichal Meloun }
781ef2ee5d0SMichal Meloun
782ef2ee5d0SMichal Meloun OF_device_register_xref(OF_xref_from_node(node), dev);
783ef2ee5d0SMichal Meloun return (bus_generic_attach(dev));
784ef2ee5d0SMichal Meloun
785ef2ee5d0SMichal Meloun fail:
786ef2ee5d0SMichal Meloun if (sc->irq_ih != NULL)
787ef2ee5d0SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
788ef2ee5d0SMichal Meloun sysctl_ctx_free(&soctherm_sysctl_ctx);
789ef2ee5d0SMichal Meloun if (sc->tsensor_clk != NULL)
790ef2ee5d0SMichal Meloun clk_release(sc->tsensor_clk);
791ef2ee5d0SMichal Meloun if (sc->soctherm_clk != NULL)
792ef2ee5d0SMichal Meloun clk_release(sc->soctherm_clk);
793ef2ee5d0SMichal Meloun if (sc->reset != NULL)
794ef2ee5d0SMichal Meloun hwreset_release(sc->reset);
795ef2ee5d0SMichal Meloun if (sc->irq_res != NULL)
796ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
797ef2ee5d0SMichal Meloun if (sc->mem_res != NULL)
798ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
799ef2ee5d0SMichal Meloun
800ef2ee5d0SMichal Meloun return (ENXIO);
801ef2ee5d0SMichal Meloun }
802ef2ee5d0SMichal Meloun
803ef2ee5d0SMichal Meloun static int
soctherm_detach(device_t dev)804ef2ee5d0SMichal Meloun soctherm_detach(device_t dev)
805ef2ee5d0SMichal Meloun {
806ef2ee5d0SMichal Meloun struct soctherm_softc *sc;
807ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
808ef2ee5d0SMichal Meloun
809ef2ee5d0SMichal Meloun if (sc->irq_ih != NULL)
810ef2ee5d0SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
811ef2ee5d0SMichal Meloun sysctl_ctx_free(&soctherm_sysctl_ctx);
812ef2ee5d0SMichal Meloun if (sc->tsensor_clk != NULL)
813ef2ee5d0SMichal Meloun clk_release(sc->tsensor_clk);
814ef2ee5d0SMichal Meloun if (sc->soctherm_clk != NULL)
815ef2ee5d0SMichal Meloun clk_release(sc->soctherm_clk);
816ef2ee5d0SMichal Meloun if (sc->reset != NULL)
817ef2ee5d0SMichal Meloun hwreset_release(sc->reset);
818ef2ee5d0SMichal Meloun if (sc->irq_res != NULL)
819ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
820ef2ee5d0SMichal Meloun if (sc->mem_res != NULL)
821ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
822ef2ee5d0SMichal Meloun
823ef2ee5d0SMichal Meloun return (ENXIO);
824ef2ee5d0SMichal Meloun }
825ef2ee5d0SMichal Meloun
826ef2ee5d0SMichal Meloun static device_method_t tegra_soctherm_methods[] = {
827ef2ee5d0SMichal Meloun /* Device interface */
828ef2ee5d0SMichal Meloun DEVMETHOD(device_probe, soctherm_probe),
829ef2ee5d0SMichal Meloun DEVMETHOD(device_attach, soctherm_attach),
830ef2ee5d0SMichal Meloun DEVMETHOD(device_detach, soctherm_detach),
831ef2ee5d0SMichal Meloun
832ef2ee5d0SMichal Meloun /* SOCTHERM interface */
833ef2ee5d0SMichal Meloun DEVMETHOD(tegra_soctherm_get_temperature, soctherm_get_temp),
834ef2ee5d0SMichal Meloun
835ef2ee5d0SMichal Meloun DEVMETHOD_END
836ef2ee5d0SMichal Meloun };
837ef2ee5d0SMichal Meloun
8384bda238aSMichal Meloun static DEFINE_CLASS_0(soctherm, tegra_soctherm_driver, tegra_soctherm_methods,
839ef2ee5d0SMichal Meloun sizeof(struct soctherm_softc));
840ef2ee5d0SMichal Meloun EARLY_DRIVER_MODULE(tegra_soctherm, simplebus, tegra_soctherm_driver,
841289f133bSJohn Baldwin NULL, NULL, 79);
842