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