1 /*-
2 *
3 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 *
5 * Copyright 2020 Michal Meloun <[email protected]>
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
WR4(struct qoriq_therm_softc * sc,bus_size_t addr,uint32_t val)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
RD4(struct qoriq_therm_softc * sc,bus_size_t addr)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
qoriq_therm_read_temp(struct qoriq_therm_softc * sc,struct tsensor * sensor,int * temp)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
qoriq_therm_get_temp(device_t dev,device_t cdev,uintptr_t id,int * val)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
qoriq_therm_sysctl_temperature(SYSCTL_HANDLER_ARGS)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
qoriq_therm_init_sysctl(struct qoriq_therm_softc * sc)188 qoriq_therm_init_sysctl(struct qoriq_therm_softc *sc)
189 {
190 int i;
191 struct sysctl_oid *oid, *tmp;
192
193 sysctl_ctx_init(&qoriq_therm_sysctl_ctx);
194 /* create node for hw.temp */
195 oid = SYSCTL_ADD_NODE(&qoriq_therm_sysctl_ctx,
196 SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature",
197 CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
198 if (oid == NULL)
199 return (ENXIO);
200
201 /* add sensors */
202 for (i = sc->ntsensors - 1; i >= 0; i--) {
203 tmp = SYSCTL_ADD_PROC(&qoriq_therm_sysctl_ctx,
204 SYSCTL_CHILDREN(oid), OID_AUTO, sc->tsensors[i].name,
205 CTLTYPE_INT | CTLFLAG_RD , sc, i,
206 qoriq_therm_sysctl_temperature, "IK", "SoC Temperature");
207 if (tmp == NULL)
208 return (ENXIO);
209 }
210 return (0);
211 }
212
213 static int
qoriq_therm_fdt_calib(struct qoriq_therm_softc * sc,phandle_t node)214 qoriq_therm_fdt_calib(struct qoriq_therm_softc *sc, phandle_t node)
215 {
216 int nranges, ncalibs, i;
217 int *ranges, *calibs;
218
219 /* initialize temperature range control registes */
220 nranges = OF_getencprop_alloc_multi(node, "fsl,tmu-range",
221 sizeof(*ranges), (void **)&ranges);
222 if (nranges < 2 || nranges > 4) {
223 device_printf(sc->dev, "Invalid 'tmu-range' property\n");
224 return (ERANGE);
225 }
226 for (i = 0; i < nranges; i++) {
227 WR4(sc, TMU_TTRCR(i), ranges[i]);
228 }
229
230 /* initialize calibration data for above ranges */
231 ncalibs = OF_getencprop_alloc_multi(node, "fsl,tmu-calibration",
232 sizeof(*calibs),(void **)&calibs);
233 if (ncalibs <= 0 || (ncalibs % 2) != 0) {
234 device_printf(sc->dev, "Invalid 'tmu-calibration' property\n");
235 return (ERANGE);
236 }
237 for (i = 0; i < ncalibs; i +=2) {
238 WR4(sc, TMU_TTCFGR, calibs[i]);
239 WR4(sc, TMU_TSCFGR, calibs[i + 1]);
240 }
241
242 return (0);
243 }
244
245 static int
qoriq_therm_probe(device_t dev)246 qoriq_therm_probe(device_t dev)
247 {
248
249 if (!ofw_bus_status_okay(dev))
250 return (ENXIO);
251
252 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
253 return (ENXIO);
254
255 device_set_desc(dev, "QorIQ temperature sensors");
256 return (BUS_PROBE_DEFAULT);
257 }
258
259 static int
qoriq_therm_attach(device_t dev)260 qoriq_therm_attach(device_t dev)
261 {
262 struct qoriq_therm_softc *sc;
263 phandle_t node;
264 uint32_t sites;
265 int rid, rv;
266
267 sc = device_get_softc(dev);
268 sc->dev = dev;
269 node = ofw_bus_get_node(sc->dev);
270 sc->little_endian = OF_hasprop(node, "little-endian");
271
272 rid = 0;
273 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
274 RF_ACTIVE);
275 if (sc->mem_res == NULL) {
276 device_printf(dev, "Cannot allocate memory resources\n");
277 goto fail;
278 }
279
280 rid = 0;
281 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
282 if (sc->irq_res == NULL) {
283 device_printf(dev, "Cannot allocate IRQ resources\n");
284 goto fail;
285 }
286
287 /*
288 if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
289 qoriq_therm_intr, NULL, sc, &sc->irq_ih))) {
290 device_printf(dev,
291 "WARNING: unable to register interrupt handler\n");
292 goto fail;
293 }
294 */
295 rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
296 if (rv != 0 && rv != ENOENT) {
297 device_printf(dev, "Cannot get clock: %d %d\n", rv, ENOENT);
298 goto fail;
299 }
300 if (sc->clk != NULL) {
301 rv = clk_enable(sc->clk);
302 if (rv != 0) {
303 device_printf(dev, "Cannot enable clock: %d\n", rv);
304 goto fail;
305 }
306 }
307
308 sc->ver = (RD4(sc, TMU_VERSION) >> 8) & 0xFF;
309
310 /* XXX add per SoC customization */
311 sc->ntsensors = nitems(default_sensors);
312 sc->tsensors = default_sensors;
313
314 /* stop monitoring */
315 WR4(sc, TMU_TMR, 0);
316 RD4(sc, TMU_TMR);
317
318 /* disable all interrupts */
319 WR4(sc, TMU_TIER, 0);
320
321 /* setup measurement interval */
322 if (sc->ver == 1) {
323 WR4(sc, TMUV1_TMTMIR, 0x0F);
324 } else {
325 WR4(sc, TMUV2_TMTMIR, 0x0F); /* disable */
326 /* these registers are not of settings is not in TRM */
327 WR4(sc, TMUV2_TEUMR(0), 0x51009c00);
328 for (int i = 0; i < 7; i++)
329 WR4(sc, TMUV2_TMSAR(0), 0xE);
330 }
331
332 /* prepare calibration tables */
333 rv = qoriq_therm_fdt_calib(sc, node);
334 if (rv != 0) {
335 device_printf(sc->dev,
336 "Cannot initialize calibration tables\n");
337 goto fail;
338 }
339 /* start monitoring */
340 sites = (1U << sc->ntsensors) - 1;
341 if (sc->ver == 1) {
342 WR4(sc, TMU_TMR, 0x8C000000 | sites);
343 } else {
344 WR4(sc, TMUV2_TMSR, sites);
345 WR4(sc, TMU_TMR, 0x83000000);
346 }
347
348 rv = qoriq_therm_init_sysctl(sc);
349 if (rv != 0) {
350 device_printf(sc->dev, "Cannot initialize sysctls\n");
351 goto fail;
352 }
353
354 OF_device_register_xref(OF_xref_from_node(node), dev);
355 return (bus_generic_attach(dev));
356
357 fail:
358 if (sc->irq_ih != NULL)
359 bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
360 sysctl_ctx_free(&qoriq_therm_sysctl_ctx);
361 if (sc->clk != NULL)
362 clk_release(sc->clk);
363 if (sc->irq_res != NULL)
364 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
365 if (sc->mem_res != NULL)
366 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
367
368 return (ENXIO);
369 }
370
371 static int
qoriq_therm_detach(device_t dev)372 qoriq_therm_detach(device_t dev)
373 {
374 struct qoriq_therm_softc *sc;
375 sc = device_get_softc(dev);
376
377 if (sc->irq_ih != NULL)
378 bus_teardown_intr(dev, sc->irq_res, sc->irq_ih);
379 sysctl_ctx_free(&qoriq_therm_sysctl_ctx);
380 if (sc->clk != NULL)
381 clk_release(sc->clk);
382 if (sc->irq_res != NULL)
383 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
384 if (sc->mem_res != NULL)
385 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
386
387 return (0);
388 }
389
390 static device_method_t qoriq_qoriq_therm_methods[] = {
391 /* Device interface */
392 DEVMETHOD(device_probe, qoriq_therm_probe),
393 DEVMETHOD(device_attach, qoriq_therm_attach),
394 DEVMETHOD(device_detach, qoriq_therm_detach),
395
396 /* SOCTHERM interface */
397 DEVMETHOD(qoriq_therm_get_temperature, qoriq_therm_get_temp),
398
399 DEVMETHOD_END
400 };
401
402 static devclass_t qoriq_qoriq_therm_devclass;
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,
406 qoriq_qoriq_therm_devclass, NULL, NULL);
407