sxitemp.c revision 1.8
1/*	$OpenBSD: sxitemp.c,v 1.8 2020/07/15 11:33:12 dtucker Exp $	*/
2/*
3 * Copyright (c) 2017 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/param.h>
19#include <sys/systm.h>
20#include <sys/device.h>
21#include <sys/sensors.h>
22
23#include <machine/intr.h>
24#include <machine/bus.h>
25#include <machine/fdt.h>
26
27#include <dev/ofw/openfirm.h>
28#include <dev/ofw/ofw_clock.h>
29#include <dev/ofw/ofw_misc.h>
30#include <dev/ofw/ofw_pinctrl.h>
31#include <dev/ofw/ofw_thermal.h>
32#include <dev/ofw/fdt.h>
33
34/* Registers */
35#define THS_CTRL0			0x0000
36#define  THS_CTRL0_SENSOR_ACQ(x)	((x) & 0xffff)
37#define THS_CTRL2			0x0040
38#define  THS_CTRL2_ADC_ACQ(x)		(((x) & 0xffff) << 16)
39#define  THS_CTRL2_SENSE2_EN		(1 << 2)
40#define  THS_CTRL2_SENSE1_EN		(1 << 1)
41#define  THS_CTRL2_SENSE0_EN		(1 << 0)
42#define THS_INT_CTRL			0x0044
43#define  THS_INT_CTRL_THERMAL_PER(x)	(((x) & 0xfffff) << 12)
44#define  THS_INT_CTRL_THS0_DATA_IRQ_EN	(1 << 8)
45#define  THS_INT_CTRL_THS1_DATA_IRQ_EN	(1 << 9)
46#define  THS_INT_CTRL_THS2_DATA_IRQ_EN	(1 << 10)
47#define THS_STAT			0x0048
48#define  THS_STAT_THS0_DATA_IRQ_STS	(1 << 8)
49#define  THS_STAT_THS1_DATA_IRQ_STS	(1 << 9)
50#define  THS_STAT_THS2_DATA_IRQ_STS	(1 << 10)
51#define THS_FILTER			0x0070
52#define  THS_FILTER_EN			(1 << 2)
53#define  THS_FILTER_TYPE(x)		((x) & 0x3)
54#define THS0_1_CDATA			0x0074
55#define THS2_CDATA			0x0078
56#define THS0_DATA			0x0080
57#define THS1_DATA			0x0084
58#define THS2_DATA			0x0088
59
60#define HREAD4(sc, reg)							\
61	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
62#define HWRITE4(sc, reg, val)						\
63	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
64
65struct sxitemp_softc {
66	struct device		sc_dev;
67	bus_space_tag_t		sc_iot;
68	bus_space_handle_t	sc_ioh;
69
70	void			*sc_ih;
71
72	uint64_t		(*sc_calc_temp0)(int64_t);
73	uint64_t		(*sc_calc_temp1)(int64_t);
74	uint64_t		(*sc_calc_temp2)(int64_t);
75
76	struct ksensor		sc_sensors[3];
77	struct ksensordev	sc_sensordev;
78
79	struct thermal_sensor	sc_ts;
80};
81
82int	sxitemp_match(struct device *, void *, void *);
83void	sxitemp_attach(struct device *, struct device *, void *);
84
85struct cfattach	sxitemp_ca = {
86	sizeof (struct sxitemp_softc), sxitemp_match, sxitemp_attach
87};
88
89struct cfdriver sxitemp_cd = {
90	NULL, "sxitemp", DV_DULL
91};
92
93void	sxitemp_setup_calib(struct sxitemp_softc *, int);
94int	sxitemp_intr(void *);
95uint64_t sxitemp_h3_calc_temp(int64_t);
96uint64_t sxitemp_r40_calc_temp(int64_t);
97uint64_t sxitemp_a64_calc_temp(int64_t);
98uint64_t sxitemp_h5_calc_temp0(int64_t);
99uint64_t sxitemp_h5_calc_temp1(int64_t);
100void	sxitemp_refresh_sensors(void *);
101int32_t sxitemp_get_temperature(void *, uint32_t *);
102
103int
104sxitemp_match(struct device *parent, void *match, void *aux)
105{
106	struct fdt_attach_args *faa = aux;
107
108	return (OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-ths") ||
109	    OF_is_compatible(faa->fa_node, "allwinner,sun8i-r40-ths") ||
110	    OF_is_compatible(faa->fa_node, "allwinner,sun50i-a64-ths") ||
111	    OF_is_compatible(faa->fa_node, "allwinner,sun50i-h5-ths"));
112}
113
114void
115sxitemp_attach(struct device *parent, struct device *self, void *aux)
116{
117	struct sxitemp_softc *sc = (struct sxitemp_softc *)self;
118	struct fdt_attach_args *faa = aux;
119	int node = faa->fa_node;
120	uint32_t enable, irq;
121
122	if (faa->fa_nreg < 1) {
123		printf(": no registers\n");
124		return;
125	}
126
127	sc->sc_iot = faa->fa_iot;
128	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
129	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
130		printf(": can't map registers\n");
131		return;
132	}
133
134	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_SOFTCLOCK,
135	    sxitemp_intr, sc, sc->sc_dev.dv_xname);
136	if (sc->sc_ih == NULL) {
137		printf(": can't establish interrupt\n");
138		return;
139	}
140
141	printf("\n");
142
143	pinctrl_byname(node, "default");
144
145	clock_enable_all(node);
146	reset_deassert_all(node);
147
148	if (OF_is_compatible(faa->fa_node, "allwinner,sun8i-h3-ths")) {
149		sc->sc_calc_temp0 = sxitemp_h3_calc_temp;
150	} else if (OF_is_compatible(faa->fa_node, "allwinner,sun8i-r40-ths")) {
151		sc->sc_calc_temp0 = sxitemp_r40_calc_temp;
152		sc->sc_calc_temp1 = sxitemp_r40_calc_temp;
153	} else if (OF_is_compatible(faa->fa_node, "allwinner,sun50i-a64-ths")) {
154		sc->sc_calc_temp0 = sxitemp_a64_calc_temp;
155		sc->sc_calc_temp1 = sxitemp_a64_calc_temp;
156		sc->sc_calc_temp2 = sxitemp_a64_calc_temp;
157	} else {
158		sc->sc_calc_temp0 = sxitemp_h5_calc_temp0;
159		sc->sc_calc_temp1 = sxitemp_h5_calc_temp1;
160	}
161
162	enable = irq = 0;
163	if (sc->sc_calc_temp0) {
164		enable |= THS_CTRL2_SENSE0_EN;
165		irq |= THS_INT_CTRL_THS0_DATA_IRQ_EN;
166	}
167	if (sc->sc_calc_temp1) {
168		enable |= THS_CTRL2_SENSE1_EN;
169		irq |= THS_INT_CTRL_THS1_DATA_IRQ_EN;
170	}
171	if (sc->sc_calc_temp2) {
172		enable |= THS_CTRL2_SENSE2_EN;
173		irq |= THS_INT_CTRL_THS2_DATA_IRQ_EN;
174	}
175
176	sxitemp_setup_calib(sc, node);
177
178	/* Start data acquisition. */
179	HWRITE4(sc, THS_FILTER, THS_FILTER_EN | THS_FILTER_TYPE(1));
180	HWRITE4(sc, THS_INT_CTRL, THS_INT_CTRL_THERMAL_PER(800) | irq);
181	HWRITE4(sc, THS_CTRL0, THS_CTRL0_SENSOR_ACQ(31));
182	HWRITE4(sc, THS_CTRL2, THS_CTRL2_ADC_ACQ(31) | enable);
183
184	/* Register sensors. */
185	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
186	    sizeof(sc->sc_sensordev.xname));
187	if (sc->sc_calc_temp0) {
188		strlcpy(sc->sc_sensors[0].desc, "CPU",
189		    sizeof(sc->sc_sensors[0].desc));
190		sc->sc_sensors[0].type = SENSOR_TEMP;
191		sc->sc_sensors[0].flags = SENSOR_FINVALID;
192		sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[0]);
193	}
194	if (sc->sc_calc_temp1) {
195		strlcpy(sc->sc_sensors[1].desc, "GPU",
196		    sizeof(sc->sc_sensors[1].desc));
197		sc->sc_sensors[1].type = SENSOR_TEMP;
198		sc->sc_sensors[1].flags = SENSOR_FINVALID;
199		sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[1]);
200	}
201	if (sc->sc_calc_temp2) {
202		strlcpy(sc->sc_sensors[2].desc, "",
203		    sizeof(sc->sc_sensors[2].desc));
204		sc->sc_sensors[2].type = SENSOR_TEMP;
205		sc->sc_sensors[2].flags = SENSOR_FINVALID;
206		sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[2]);
207	}
208	sensordev_install(&sc->sc_sensordev);
209	sensor_task_register(sc, sxitemp_refresh_sensors, 5);
210
211	sc->sc_ts.ts_node = node;
212	sc->sc_ts.ts_cookie = sc;
213	sc->sc_ts.ts_get_temperature = sxitemp_get_temperature;
214	thermal_sensor_register(&sc->sc_ts);
215}
216
217void
218sxitemp_setup_calib(struct sxitemp_softc *sc, int node)
219{
220	uint32_t calib[2];
221	bus_size_t size = sizeof(calib);
222
223	/*
224	 * The size of the calibration data depends on the number of
225	 * sensors.  Instead of trying to be clever, just try the
226	 * possible sizes.
227	 */
228	while (size > 0) {
229		if (nvmem_read_cell(node, "calibration", &calib, size) == 0)
230			break;
231		size -= sizeof(calib[0]);
232	}
233
234	if (size > 0)
235		HWRITE4(sc, THS0_1_CDATA, calib[0]);
236	if (size > 4)
237		HWRITE4(sc, THS2_CDATA, calib[1]);
238}
239
240int
241sxitemp_intr(void *arg)
242{
243	struct sxitemp_softc *sc = arg;
244	uint32_t cell, stat;
245	int rc = 0;
246
247	stat = HREAD4(sc, THS_STAT);
248	HWRITE4(sc, THS_STAT, stat);
249
250	if (stat & THS_STAT_THS0_DATA_IRQ_STS) {
251		cell = 0;
252		thermal_sensor_update(&sc->sc_ts, &cell);
253		rc = 1;
254	}
255	if (stat & THS_STAT_THS1_DATA_IRQ_STS) {
256		cell = 1;
257		thermal_sensor_update(&sc->sc_ts, &cell);
258		rc = 1;
259	}
260	if (stat & THS_STAT_THS2_DATA_IRQ_STS) {
261		cell = 2;
262		thermal_sensor_update(&sc->sc_ts, &cell);
263		rc = 1;
264	}
265
266	return rc;
267}
268
269uint64_t
270sxitemp_h3_calc_temp(int64_t data)
271{
272	/* From BSP since the H3 Data Sheet isn't accurate. */
273	return 217000000 - data * 1000000000 / 8253;
274}
275
276uint64_t
277sxitemp_r40_calc_temp(int64_t data)
278{
279	/* From BSP as the R40 User Manual says T.B.D. */
280	return -112500 * data + 250000000;
281}
282
283uint64_t
284sxitemp_a64_calc_temp(int64_t data)
285{
286	/* From BSP as the A64 User Manual isn't correct. */
287	return (2170000000000 - data * 1000000000) / 8560;
288}
289
290uint64_t
291sxitemp_h5_calc_temp0(int64_t data)
292{
293	if (data > 0x500)
294		return -119100 * data + 223000000;
295	else
296		return -145200 * data + 259000000;
297}
298
299uint64_t
300sxitemp_h5_calc_temp1(int64_t data)
301{
302	if (data > 0x500)
303		return -119100 * data + 223000000;
304	else
305		return -159000 * data + 276000000;
306}
307
308void
309sxitemp_refresh_sensors(void *arg)
310{
311	struct sxitemp_softc *sc = arg;
312	uint32_t data;
313
314	if (sc->sc_calc_temp0) {
315		data = HREAD4(sc, THS0_DATA);
316		sc->sc_sensors[0].value = sc->sc_calc_temp0(data) + 273150000;
317		sc->sc_sensors[0].flags &= ~SENSOR_FINVALID;
318	}
319
320	if (sc->sc_calc_temp1) {
321		data = HREAD4(sc, THS1_DATA);
322		sc->sc_sensors[1].value = sc->sc_calc_temp1(data) + 273150000;
323		sc->sc_sensors[1].flags &= ~SENSOR_FINVALID;
324	}
325
326	if (sc->sc_calc_temp2) {
327		data = HREAD4(sc, THS2_DATA);
328		sc->sc_sensors[2].value = sc->sc_calc_temp2(data) + 273150000;
329		sc->sc_sensors[2].flags &= ~SENSOR_FINVALID;
330	}
331}
332
333int32_t
334sxitemp_get_temperature(void *cookie, uint32_t *cells)
335{
336	struct sxitemp_softc *sc = cookie;
337	uint32_t idx = cells[0];
338	uint32_t data;
339
340	if (idx == 0 && sc->sc_calc_temp0) {
341		data = HREAD4(sc, THS0_DATA);
342		return sc->sc_calc_temp0(data) / 1000;
343	} else if (idx == 1 && sc->sc_calc_temp1) {
344		data = HREAD4(sc, THS1_DATA);
345		return sc->sc_calc_temp1(data) / 1000;
346	} else if (idx == 2 && sc->sc_calc_temp2) {
347		data = HREAD4(sc, THS2_DATA);
348		return sc->sc_calc_temp2(data) / 1000;
349	}
350
351	return THERMAL_SENSOR_MAX;
352}
353