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