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