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 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 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 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 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 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