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