xref: /openbsd/sys/dev/fdt/sxitemp.c (revision 9fdf0c62)
1 /*	$OpenBSD: sxitemp.c,v 1.9 2021/10/24 17:52:27 mpi 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 
65 struct 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 
82 int	sxitemp_match(struct device *, void *, void *);
83 void	sxitemp_attach(struct device *, struct device *, void *);
84 
85 const struct cfattach	sxitemp_ca = {
86 	sizeof (struct sxitemp_softc), sxitemp_match, sxitemp_attach
87 };
88 
89 struct cfdriver sxitemp_cd = {
90 	NULL, "sxitemp", DV_DULL
91 };
92 
93 void	sxitemp_setup_calib(struct sxitemp_softc *, int);
94 int	sxitemp_intr(void *);
95 uint64_t sxitemp_h3_calc_temp(int64_t);
96 uint64_t sxitemp_r40_calc_temp(int64_t);
97 uint64_t sxitemp_a64_calc_temp(int64_t);
98 uint64_t sxitemp_h5_calc_temp0(int64_t);
99 uint64_t sxitemp_h5_calc_temp1(int64_t);
100 void	sxitemp_refresh_sensors(void *);
101 int32_t sxitemp_get_temperature(void *, uint32_t *);
102 
103 int
sxitemp_match(struct device * parent,void * match,void * aux)104 sxitemp_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 
114 void
sxitemp_attach(struct device * parent,struct device * self,void * aux)115 sxitemp_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 
217 void
sxitemp_setup_calib(struct sxitemp_softc * sc,int node)218 sxitemp_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 
240 int
sxitemp_intr(void * arg)241 sxitemp_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 
269 uint64_t
sxitemp_h3_calc_temp(int64_t data)270 sxitemp_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 
276 uint64_t
sxitemp_r40_calc_temp(int64_t data)277 sxitemp_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 
283 uint64_t
sxitemp_a64_calc_temp(int64_t data)284 sxitemp_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 
290 uint64_t
sxitemp_h5_calc_temp0(int64_t data)291 sxitemp_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 
299 uint64_t
sxitemp_h5_calc_temp1(int64_t data)300 sxitemp_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 
308 void
sxitemp_refresh_sensors(void * arg)309 sxitemp_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 
333 int32_t
sxitemp_get_temperature(void * cookie,uint32_t * cells)334 sxitemp_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