xref: /freebsd/sys/arm64/qoriq/qoriq_therm.c (revision 716fd348)
1 /*-
2  *
3  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4  *
5  * Copyright  2020 Michal Meloun <mmel@FreeBSD.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 /*
33  * Thermometer driver for QorIQ  SoCs.
34  */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/endian.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41 #include <sys/malloc.h>
42 #include <sys/rman.h>
43 #include <sys/sysctl.h>
44 
45 #include <machine/bus.h>
46 
47 #include <dev/extres/clk/clk.h>
48 #include <dev/ofw/ofw_bus.h>
49 #include <dev/ofw/ofw_bus_subr.h>
50 
51 #include "qoriq_therm_if.h"
52 
53 #define	TMU_TMR		0x00
54 #define	TMU_TSR		0x04
55 #define TMUV1_TMTMIR	0x08
56 #define TMUV2_TMSR	0x08
57 #define TMUV2_TMTMIR	0x0C
58 #define	TMU_TIER	0x20
59 #define	TMU_TTCFGR	0x80
60 #define	TMU_TSCFGR	0x84
61 #define	TMU_TRITSR(x)	(0x100 + (16 * (x)))
62 #define	 TMU_TRITSR_VALID	(1U << 31)
63 #define	TMUV2_TMSAR(x)	(0x304 + (16 * (x)))
64 #define	TMU_VERSION	0xBF8			/* not in TRM */
65 #define	TMUV2_TEUMR(x)	(0xF00 + (4 * (x)))
66 #define	TMU_TTRCR(x)	(0xF10 + (4 * (x)))
67 
68 
69 struct tsensor {
70 	int			site_id;
71 	char 			*name;
72 	int			id;
73 };
74 
75 struct qoriq_therm_softc {
76 	device_t		dev;
77 	struct resource		*mem_res;
78 	struct resource		*irq_res;
79 	void			*irq_ih;
80 	int			ntsensors;
81 	struct tsensor		*tsensors;
82 	bool			little_endian;
83 	clk_t			clk;
84 	int			ver;
85 };
86 
87 static struct sysctl_ctx_list qoriq_therm_sysctl_ctx;
88 
89 struct tsensor default_sensors[] =
90 {
91 	{ 0, "site0", 0},
92 	{ 1, "site1", 1},
93 	{ 2, "site2", 2},
94 	{ 3, "site3", 3},
95 	{ 4, "site4", 4},
96 	{ 5, "site5", 5},
97 	{ 6, "site6", 6},
98 };
99 
100 static struct ofw_compat_data compat_data[] = {
101 	{"fsl,qoriq-tmu",	1},
102 	{"fsl,imx8mq-tmu",	1},
103 	{NULL,			0},
104 };
105 
106 static inline void
107 WR4(struct qoriq_therm_softc *sc, bus_size_t addr, uint32_t val)
108 {
109 
110 	val = sc->little_endian ? htole32(val): htobe32(val);
111 	bus_write_4(sc->mem_res, addr, val);
112 }
113 
114 static inline uint32_t
115 RD4(struct qoriq_therm_softc *sc, bus_size_t addr)
116 {
117 	uint32_t val;
118 
119 	val = bus_read_4(sc->mem_res, addr);
120 	return (sc->little_endian ? le32toh(val): be32toh(val));
121 }
122 
123 static int
124 qoriq_therm_read_temp(struct qoriq_therm_softc *sc, struct tsensor *sensor,
125     int *temp)
126 {
127 	int timeout;
128 	uint32_t val;
129 
130 	/* wait for valid sample */
131 	for (timeout = 1000; timeout > 0; timeout--) {
132 		val = RD4(sc, TMU_TRITSR(sensor->site_id));
133 		if (val & TMU_TRITSR_VALID)
134 			break;
135 		DELAY(100);
136 	}
137 	if (timeout <= 0)
138 		device_printf(sc->dev, "Sensor %s timeouted\n", sensor->name);
139 
140 	*temp = (int)(val & 0x1FF) * 1000;
141 	if (sc->ver == 1)
142 		*temp = (int)(val & 0xFF) * 1000;
143 	else
144 		*temp = (int)(val & 0x1FF) * 1000 - 273100;
145 
146 	return (0);
147 }
148 
149 static int
150 qoriq_therm_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val)
151 {
152 	struct qoriq_therm_softc *sc;
153 
154 	sc = device_get_softc(dev);
155 	if (id >= sc->ntsensors)
156 		return (ERANGE);
157 	return(qoriq_therm_read_temp(sc, sc->tsensors + id, val));
158 }
159 
160 static int
161 qoriq_therm_sysctl_temperature(SYSCTL_HANDLER_ARGS)
162 {
163 	struct qoriq_therm_softc *sc;
164 	int val;
165 	int rv;
166 	int id;
167 
168 	/* Write request */
169 	if (req->newptr != NULL)
170 		return (EINVAL);
171 
172 	sc = arg1;
173 	id = arg2;
174 
175 	if (id >= sc->ntsensors)
176 		return (ERANGE);
177 	rv =  qoriq_therm_read_temp(sc, sc->tsensors + id, &val);
178 	if (rv != 0)
179 		return (rv);
180 
181 	val = val / 100;
182 	val +=  2731;
183 	rv = sysctl_handle_int(oidp, &val, 0, req);
184 	return (rv);
185 }
186 
187 static int
188 qoriq_therm_init_sysctl(struct qoriq_therm_softc *sc)
189 {
190 	int i;
191 	struct sysctl_oid *oid, *tmp;
192 
193 	/* create node for hw.temp */
194 	oid = SYSCTL_ADD_NODE(&qoriq_therm_sysctl_ctx,
195 	    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature",
196 	    CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
197 	if (oid == NULL)
198 		return (ENXIO);
199 
200 	/* add sensors */
201 	for (i = sc->ntsensors  - 1; i >= 0; i--) {
202 		tmp = SYSCTL_ADD_PROC(&qoriq_therm_sysctl_ctx,
203 		    SYSCTL_CHILDREN(oid), OID_AUTO, sc->tsensors[i].name,
204 		    CTLTYPE_INT | CTLFLAG_RD , sc, i,
205 		    qoriq_therm_sysctl_temperature, "IK", "SoC Temperature");
206 		if (tmp == NULL)
207 			return (ENXIO);
208 	}
209 	return (0);
210 }
211 
212 static int
213 qoriq_therm_fdt_calib(struct qoriq_therm_softc *sc, phandle_t node)
214 {
215 	int 	nranges, ncalibs, i;
216 	int	*ranges, *calibs;
217 
218 	/* initialize temperature range control registes */
219 	nranges = OF_getencprop_alloc_multi(node, "fsl,tmu-range",
220 	    sizeof(*ranges), (void **)&ranges);
221 	if (nranges < 2 || nranges > 4) {
222 		device_printf(sc->dev, "Invalid 'tmu-range' property\n");
223 		return (ERANGE);
224 	}
225 	for (i = 0; i < nranges; i++) {
226 		WR4(sc, TMU_TTRCR(i), ranges[i]);
227 	}
228 
229 	/* initialize calibration data for above ranges */
230 	ncalibs = OF_getencprop_alloc_multi(node, "fsl,tmu-calibration",
231 	    sizeof(*calibs),(void **)&calibs);
232 	if (ncalibs <= 0 || (ncalibs % 2) != 0) {
233 		device_printf(sc->dev, "Invalid 'tmu-calibration' property\n");
234 		return (ERANGE);
235 	}
236 	for (i = 0; i < ncalibs; i +=2) {
237 		WR4(sc, TMU_TTCFGR, calibs[i]);
238 		WR4(sc, TMU_TSCFGR, calibs[i + 1]);
239 	}
240 
241 	return (0);
242 }
243 
244 static int
245 qoriq_therm_probe(device_t dev)
246 {
247 
248 	if (!ofw_bus_status_okay(dev))
249 		return (ENXIO);
250 
251 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
252 		return (ENXIO);
253 
254 	device_set_desc(dev, "QorIQ temperature sensors");
255 	return (BUS_PROBE_DEFAULT);
256 }
257 
258 static int
259 qoriq_therm_attach(device_t dev)
260 {
261 	struct qoriq_therm_softc *sc;
262 	phandle_t node;
263 	uint32_t sites;
264 	int rid, rv;
265 
266 	sc = device_get_softc(dev);
267 	sc->dev = dev;
268 	node = ofw_bus_get_node(sc->dev);
269 	sc->little_endian = OF_hasprop(node, "little-endian");
270 
271 	sysctl_ctx_init(&qoriq_therm_sysctl_ctx);
272 
273 	rid = 0;
274 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
275 	    RF_ACTIVE);
276 	if (sc->mem_res == NULL) {
277 		device_printf(dev, "Cannot allocate memory resources\n");
278 		goto fail;
279 	}
280 
281 	rid = 0;
282 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
283 	if (sc->irq_res == NULL) {
284 		device_printf(dev, "Cannot allocate IRQ resources\n");
285 		goto fail;
286 	}
287 
288 /*
289 	if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
290 	    qoriq_therm_intr, NULL, sc, &sc->irq_ih))) {
291 		device_printf(dev,
292 		    "WARNING: unable to register interrupt handler\n");
293 		goto fail;
294 	}
295 */
296 	rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
297 	if (rv != 0 && rv != ENOENT) {
298 		device_printf(dev, "Cannot get clock: %d %d\n", rv, ENOENT);
299 		goto fail;
300 	}
301 	if (sc->clk != NULL) {
302 		rv = clk_enable(sc->clk);
303 		if (rv != 0) {
304 			device_printf(dev, "Cannot enable clock: %d\n", rv);
305 			goto fail;
306 		}
307 	}
308 
309 	sc->ver = (RD4(sc, TMU_VERSION) >> 8) & 0xFF;
310 
311 	/* XXX add per SoC customization */
312 	sc->ntsensors = nitems(default_sensors);
313 	sc->tsensors = default_sensors;
314 
315 	/* stop monitoring */
316 	WR4(sc, TMU_TMR, 0);
317 	RD4(sc, TMU_TMR);
318 
319 	/* disable all interrupts */
320 	WR4(sc, TMU_TIER, 0);
321 
322 	/* setup measurement interval */
323 	if (sc->ver == 1) {
324 		WR4(sc, TMUV1_TMTMIR, 0x0F);
325 	} else {
326 		WR4(sc, TMUV2_TMTMIR, 0x0F);	/* disable */
327 		/* these registers are not of settings is not in TRM */
328 		WR4(sc, TMUV2_TEUMR(0), 0x51009c00);
329 		for (int i = 0; i < 7; i++)
330 			WR4(sc, TMUV2_TMSAR(0), 0xE);
331 	}
332 
333 	/* prepare calibration tables */
334 	rv = qoriq_therm_fdt_calib(sc, node);
335 	if (rv != 0) {
336 		device_printf(sc->dev,
337 		    "Cannot initialize calibration tables\n");
338 		goto fail;
339 	}
340 	/* start monitoring */
341 	sites = (1U << sc->ntsensors) - 1;
342 	if (sc->ver == 1) {
343 		WR4(sc, TMU_TMR, 0x8C000000 | sites);
344 	} else {
345 		WR4(sc, TMUV2_TMSR, sites);
346 		WR4(sc, TMU_TMR, 0x83000000);
347 	}
348 
349 	rv = qoriq_therm_init_sysctl(sc);
350 	if (rv != 0) {
351 		device_printf(sc->dev, "Cannot initialize sysctls\n");
352 		goto fail;
353 	}
354 
355 	OF_device_register_xref(OF_xref_from_node(node), dev);
356 	return (bus_generic_attach(dev));
357 
358 fail:
359 	if (sc->irq_ih != NULL)
360 		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
361 	sysctl_ctx_free(&qoriq_therm_sysctl_ctx);
362 	if (sc->clk != NULL)
363 		clk_release(sc->clk);
364 	if (sc->irq_res != NULL)
365 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
366 	if (sc->mem_res != NULL)
367 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
368 
369 	return (ENXIO);
370 }
371 
372 static int
373 qoriq_therm_detach(device_t dev)
374 {
375 	struct qoriq_therm_softc *sc;
376 	sc = device_get_softc(dev);
377 
378 	if (sc->irq_ih != NULL)
379 		bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
380 	sysctl_ctx_free(&qoriq_therm_sysctl_ctx);
381 	if (sc->clk != NULL)
382 		clk_release(sc->clk);
383 	if (sc->irq_res != NULL)
384 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
385 	if (sc->mem_res != NULL)
386 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
387 
388 	return (0);
389 }
390 
391 static device_method_t qoriq_qoriq_therm_methods[] = {
392 	/* Device interface */
393 	DEVMETHOD(device_probe,			qoriq_therm_probe),
394 	DEVMETHOD(device_attach,		qoriq_therm_attach),
395 	DEVMETHOD(device_detach,		qoriq_therm_detach),
396 
397 	/* SOCTHERM interface */
398 	DEVMETHOD(qoriq_therm_get_temperature,	qoriq_therm_get_temp),
399 
400 	DEVMETHOD_END
401 };
402 
403 static DEFINE_CLASS_0(soctherm, qoriq_qoriq_therm_driver, qoriq_qoriq_therm_methods,
404     sizeof(struct qoriq_therm_softc));
405 DRIVER_MODULE(qoriq_soctherm, simplebus, qoriq_qoriq_therm_driver, NULL, NULL);
406