diff --git a/sys/arm64/qoriq/qoriq_therm.c b/sys/arm64/qoriq/qoriq_therm.c index 33237fc94c07..c880e68025bf 100644 --- a/sys/arm64/qoriq/qoriq_therm.c +++ b/sys/arm64/qoriq/qoriq_therm.c @@ -1,406 +1,407 @@ /*- * * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2020 Michal Meloun * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Thermometer driver for QorIQ SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qoriq_therm_if.h" #define TMU_TMR 0x00 #define TMU_TSR 0x04 #define TMUV1_TMTMIR 0x08 #define TMUV2_TMSR 0x08 #define TMUV2_TMTMIR 0x0C #define TMU_TIER 0x20 #define TMU_TTCFGR 0x80 #define TMU_TSCFGR 0x84 #define TMU_TRITSR(x) (0x100 + (16 * (x))) #define TMU_TRITSR_VALID (1U << 31) #define TMUV2_TMSAR(x) (0x304 + (16 * (x))) #define TMU_VERSION 0xBF8 /* not in TRM */ #define TMUV2_TEUMR(x) (0xF00 + (4 * (x))) #define TMU_TTRCR(x) (0xF10 + (4 * (x))) struct tsensor { int site_id; char *name; int id; }; struct qoriq_therm_softc { device_t dev; struct resource *mem_res; struct resource *irq_res; void *irq_ih; int ntsensors; struct tsensor *tsensors; bool little_endian; clk_t clk; int ver; }; static struct sysctl_ctx_list qoriq_therm_sysctl_ctx; struct tsensor default_sensors[] = { { 0, "site0", 0}, { 1, "site1", 1}, { 2, "site2", 2}, { 3, "site3", 3}, { 4, "site4", 4}, { 5, "site5", 5}, { 6, "site6", 6}, }; static struct ofw_compat_data compat_data[] = { {"fsl,qoriq-tmu", 1}, {"fsl,imx8mq-tmu", 1}, {NULL, 0}, }; static inline void WR4(struct qoriq_therm_softc *sc, bus_size_t addr, uint32_t val) { val = sc->little_endian ? htole32(val): htobe32(val); bus_write_4(sc->mem_res, addr, val); } static inline uint32_t RD4(struct qoriq_therm_softc *sc, bus_size_t addr) { uint32_t val; val = bus_read_4(sc->mem_res, addr); return (sc->little_endian ? le32toh(val): be32toh(val)); } static int qoriq_therm_read_temp(struct qoriq_therm_softc *sc, struct tsensor *sensor, int *temp) { int timeout; uint32_t val; /* wait for valid sample */ for (timeout = 1000; timeout > 0; timeout--) { val = RD4(sc, TMU_TRITSR(sensor->site_id)); if (val & TMU_TRITSR_VALID) break; DELAY(100); } if (timeout <= 0) device_printf(sc->dev, "Sensor %s timeouted\n", sensor->name); *temp = (int)(val & 0x1FF) * 1000; if (sc->ver == 1) *temp = (int)(val & 0xFF) * 1000; else *temp = (int)(val & 0x1FF) * 1000 - 273100; return (0); } static int qoriq_therm_get_temp(device_t dev, device_t cdev, uintptr_t id, int *val) { struct qoriq_therm_softc *sc; sc = device_get_softc(dev); if (id >= sc->ntsensors) return (ERANGE); return(qoriq_therm_read_temp(sc, sc->tsensors + id, val)); } static int qoriq_therm_sysctl_temperature(SYSCTL_HANDLER_ARGS) { struct qoriq_therm_softc *sc; int val; int rv; int id; /* Write request */ if (req->newptr != NULL) return (EINVAL); sc = arg1; id = arg2; if (id >= sc->ntsensors) return (ERANGE); rv = qoriq_therm_read_temp(sc, sc->tsensors + id, &val); if (rv != 0) return (rv); val = val / 100; val += 2731; rv = sysctl_handle_int(oidp, &val, 0, req); return (rv); } static int qoriq_therm_init_sysctl(struct qoriq_therm_softc *sc) { int i; struct sysctl_oid *oid, *tmp; - sysctl_ctx_init(&qoriq_therm_sysctl_ctx); /* create node for hw.temp */ oid = SYSCTL_ADD_NODE(&qoriq_therm_sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "temperature", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, ""); if (oid == NULL) return (ENXIO); /* add sensors */ for (i = sc->ntsensors - 1; i >= 0; i--) { tmp = SYSCTL_ADD_PROC(&qoriq_therm_sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, sc->tsensors[i].name, CTLTYPE_INT | CTLFLAG_RD , sc, i, qoriq_therm_sysctl_temperature, "IK", "SoC Temperature"); if (tmp == NULL) return (ENXIO); } return (0); } static int qoriq_therm_fdt_calib(struct qoriq_therm_softc *sc, phandle_t node) { int nranges, ncalibs, i; int *ranges, *calibs; /* initialize temperature range control registes */ nranges = OF_getencprop_alloc_multi(node, "fsl,tmu-range", sizeof(*ranges), (void **)&ranges); if (nranges < 2 || nranges > 4) { device_printf(sc->dev, "Invalid 'tmu-range' property\n"); return (ERANGE); } for (i = 0; i < nranges; i++) { WR4(sc, TMU_TTRCR(i), ranges[i]); } /* initialize calibration data for above ranges */ ncalibs = OF_getencprop_alloc_multi(node, "fsl,tmu-calibration", sizeof(*calibs),(void **)&calibs); if (ncalibs <= 0 || (ncalibs % 2) != 0) { device_printf(sc->dev, "Invalid 'tmu-calibration' property\n"); return (ERANGE); } for (i = 0; i < ncalibs; i +=2) { WR4(sc, TMU_TTCFGR, calibs[i]); WR4(sc, TMU_TSCFGR, calibs[i + 1]); } return (0); } static int qoriq_therm_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "QorIQ temperature sensors"); return (BUS_PROBE_DEFAULT); } static int qoriq_therm_attach(device_t dev) { struct qoriq_therm_softc *sc; phandle_t node; uint32_t sites; int rid, rv; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(sc->dev); sc->little_endian = OF_hasprop(node, "little-endian"); + sysctl_ctx_init(&qoriq_therm_sysctl_ctx); + rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { device_printf(dev, "Cannot allocate memory resources\n"); goto fail; } rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->irq_res == NULL) { device_printf(dev, "Cannot allocate IRQ resources\n"); goto fail; } /* if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, qoriq_therm_intr, NULL, sc, &sc->irq_ih))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); goto fail; } */ rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); if (rv != 0 && rv != ENOENT) { device_printf(dev, "Cannot get clock: %d %d\n", rv, ENOENT); goto fail; } if (sc->clk != NULL) { rv = clk_enable(sc->clk); if (rv != 0) { device_printf(dev, "Cannot enable clock: %d\n", rv); goto fail; } } sc->ver = (RD4(sc, TMU_VERSION) >> 8) & 0xFF; /* XXX add per SoC customization */ sc->ntsensors = nitems(default_sensors); sc->tsensors = default_sensors; /* stop monitoring */ WR4(sc, TMU_TMR, 0); RD4(sc, TMU_TMR); /* disable all interrupts */ WR4(sc, TMU_TIER, 0); /* setup measurement interval */ if (sc->ver == 1) { WR4(sc, TMUV1_TMTMIR, 0x0F); } else { WR4(sc, TMUV2_TMTMIR, 0x0F); /* disable */ /* these registers are not of settings is not in TRM */ WR4(sc, TMUV2_TEUMR(0), 0x51009c00); for (int i = 0; i < 7; i++) WR4(sc, TMUV2_TMSAR(0), 0xE); } /* prepare calibration tables */ rv = qoriq_therm_fdt_calib(sc, node); if (rv != 0) { device_printf(sc->dev, "Cannot initialize calibration tables\n"); goto fail; } /* start monitoring */ sites = (1U << sc->ntsensors) - 1; if (sc->ver == 1) { WR4(sc, TMU_TMR, 0x8C000000 | sites); } else { WR4(sc, TMUV2_TMSR, sites); WR4(sc, TMU_TMR, 0x83000000); } rv = qoriq_therm_init_sysctl(sc); if (rv != 0) { device_printf(sc->dev, "Cannot initialize sysctls\n"); goto fail; } OF_device_register_xref(OF_xref_from_node(node), dev); return (bus_generic_attach(dev)); fail: if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); sysctl_ctx_free(&qoriq_therm_sysctl_ctx); if (sc->clk != NULL) clk_release(sc->clk); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (ENXIO); } static int qoriq_therm_detach(device_t dev) { struct qoriq_therm_softc *sc; sc = device_get_softc(dev); if (sc->irq_ih != NULL) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); sysctl_ctx_free(&qoriq_therm_sysctl_ctx); if (sc->clk != NULL) clk_release(sc->clk); if (sc->irq_res != NULL) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); if (sc->mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); return (0); } static device_method_t qoriq_qoriq_therm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, qoriq_therm_probe), DEVMETHOD(device_attach, qoriq_therm_attach), DEVMETHOD(device_detach, qoriq_therm_detach), /* SOCTHERM interface */ DEVMETHOD(qoriq_therm_get_temperature, qoriq_therm_get_temp), DEVMETHOD_END }; static devclass_t qoriq_qoriq_therm_devclass; static DEFINE_CLASS_0(soctherm, qoriq_qoriq_therm_driver, qoriq_qoriq_therm_methods, sizeof(struct qoriq_therm_softc)); DRIVER_MODULE(qoriq_soctherm, simplebus, qoriq_qoriq_therm_driver, qoriq_qoriq_therm_devclass, NULL, NULL);