1ef2ee5d0SMichal Meloun /*- 2ef2ee5d0SMichal Meloun * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3ef2ee5d0SMichal Meloun * All rights reserved. 4ef2ee5d0SMichal Meloun * 5ef2ee5d0SMichal Meloun * Redistribution and use in source and binary forms, with or without 6ef2ee5d0SMichal Meloun * modification, are permitted provided that the following conditions 7ef2ee5d0SMichal Meloun * are met: 8ef2ee5d0SMichal Meloun * 1. Redistributions of source code must retain the above copyright 9ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer. 10ef2ee5d0SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright 11ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer in the 12ef2ee5d0SMichal Meloun * documentation and/or other materials provided with the distribution. 13ef2ee5d0SMichal Meloun * 14ef2ee5d0SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15ef2ee5d0SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16ef2ee5d0SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ef2ee5d0SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18ef2ee5d0SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19ef2ee5d0SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20ef2ee5d0SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21ef2ee5d0SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22ef2ee5d0SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ef2ee5d0SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ef2ee5d0SMichal Meloun * SUCH DAMAGE. 25ef2ee5d0SMichal Meloun */ 26ef2ee5d0SMichal Meloun 27ef2ee5d0SMichal Meloun #include <sys/cdefs.h> 28ef2ee5d0SMichal Meloun __FBSDID("$FreeBSD$"); 29ef2ee5d0SMichal Meloun 30ef2ee5d0SMichal Meloun #include <sys/param.h> 31ef2ee5d0SMichal Meloun #include <sys/systm.h> 32ef2ee5d0SMichal Meloun #include <sys/bus.h> 33ef2ee5d0SMichal Meloun #include <sys/cpu.h> 34ef2ee5d0SMichal Meloun #include <sys/kernel.h> 35ef2ee5d0SMichal Meloun #include <sys/lock.h> 36ef2ee5d0SMichal Meloun #include <sys/malloc.h> 37ef2ee5d0SMichal Meloun #include <sys/module.h> 38ef2ee5d0SMichal Meloun #include <sys/sysctl.h> 39ef2ee5d0SMichal Meloun 40ef2ee5d0SMichal Meloun #include <machine/bus.h> 41ef2ee5d0SMichal Meloun #include <machine/cpu.h> 42ef2ee5d0SMichal Meloun 43ef2ee5d0SMichal Meloun #include <dev/extres/clk/clk.h> 44ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus_subr.h> 45ef2ee5d0SMichal Meloun 46ef2ee5d0SMichal Meloun #include "tegra_soctherm_if.h" 47ef2ee5d0SMichal Meloun 48ef2ee5d0SMichal Meloun enum therm_info { 49ef2ee5d0SMichal Meloun CORETEMP_TEMP, 50ef2ee5d0SMichal Meloun CORETEMP_DELTA, 51ef2ee5d0SMichal Meloun CORETEMP_RESOLUTION, 52ef2ee5d0SMichal Meloun CORETEMP_TJMAX, 53ef2ee5d0SMichal Meloun }; 54ef2ee5d0SMichal Meloun 55ef2ee5d0SMichal Meloun struct tegra124_coretemp_softc { 56ef2ee5d0SMichal Meloun device_t dev; 57ef2ee5d0SMichal Meloun int overheat_log; 58ef2ee5d0SMichal Meloun int core_max_temp; 59ef2ee5d0SMichal Meloun int cpu_id; 60ef2ee5d0SMichal Meloun device_t tsens_dev; 61ef2ee5d0SMichal Meloun intptr_t tsens_id; 62ef2ee5d0SMichal Meloun }; 63ef2ee5d0SMichal Meloun 64ef2ee5d0SMichal Meloun static int 65ef2ee5d0SMichal Meloun coretemp_get_val_sysctl(SYSCTL_HANDLER_ARGS) 66ef2ee5d0SMichal Meloun { 67ef2ee5d0SMichal Meloun device_t dev; 68ef2ee5d0SMichal Meloun int val, temp, rv; 69ef2ee5d0SMichal Meloun struct tegra124_coretemp_softc *sc; 70ef2ee5d0SMichal Meloun enum therm_info type; 71ef2ee5d0SMichal Meloun char stemp[16]; 72ef2ee5d0SMichal Meloun 73ef2ee5d0SMichal Meloun dev = (device_t) arg1; 74ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 75ef2ee5d0SMichal Meloun type = arg2; 76ef2ee5d0SMichal Meloun 77ef2ee5d0SMichal Meloun rv = TEGRA_SOCTHERM_GET_TEMPERATURE(sc->tsens_dev, sc->dev, 78ef2ee5d0SMichal Meloun sc->tsens_id, &temp); 79ef2ee5d0SMichal Meloun if (rv != 0) { 80ef2ee5d0SMichal Meloun device_printf(sc->dev, 81ef2ee5d0SMichal Meloun "Cannot read temperature sensor %d: %d\n", 82ef2ee5d0SMichal Meloun sc->tsens_id, rv); 83ef2ee5d0SMichal Meloun return (rv); 84ef2ee5d0SMichal Meloun } 85ef2ee5d0SMichal Meloun 86ef2ee5d0SMichal Meloun switch (type) { 87ef2ee5d0SMichal Meloun case CORETEMP_TEMP: 88ef2ee5d0SMichal Meloun val = temp / 100; 89ef2ee5d0SMichal Meloun val += 2731; 90ef2ee5d0SMichal Meloun break; 91ef2ee5d0SMichal Meloun case CORETEMP_DELTA: 92ef2ee5d0SMichal Meloun val = (sc->core_max_temp - temp) / 1000; 93ef2ee5d0SMichal Meloun break; 94ef2ee5d0SMichal Meloun case CORETEMP_RESOLUTION: 95ef2ee5d0SMichal Meloun val = 1; 96ef2ee5d0SMichal Meloun break; 97ef2ee5d0SMichal Meloun case CORETEMP_TJMAX: 98ef2ee5d0SMichal Meloun val = sc->core_max_temp / 100; 99ef2ee5d0SMichal Meloun val += 2731; 100ef2ee5d0SMichal Meloun break; 101ef2ee5d0SMichal Meloun } 102ef2ee5d0SMichal Meloun 103ef2ee5d0SMichal Meloun if ((temp > sc->core_max_temp) && !sc->overheat_log) { 104ef2ee5d0SMichal Meloun sc->overheat_log = 1; 105ef2ee5d0SMichal Meloun 106ef2ee5d0SMichal Meloun /* 107ef2ee5d0SMichal Meloun * Check for Critical Temperature Status and Critical 108ef2ee5d0SMichal Meloun * Temperature Log. It doesn't really matter if the 109ef2ee5d0SMichal Meloun * current temperature is invalid because the "Critical 110ef2ee5d0SMichal Meloun * Temperature Log" bit will tell us if the Critical 111ef2ee5d0SMichal Meloun * Temperature has * been reached in past. It's not 112ef2ee5d0SMichal Meloun * directly related to the current temperature. 113ef2ee5d0SMichal Meloun * 114ef2ee5d0SMichal Meloun * If we reach a critical level, allow devctl(4) 115ef2ee5d0SMichal Meloun * to catch this and shutdown the system. 116ef2ee5d0SMichal Meloun */ 117ef2ee5d0SMichal Meloun device_printf(dev, "critical temperature detected, " 118ef2ee5d0SMichal Meloun "suggest system shutdown\n"); 119ef2ee5d0SMichal Meloun snprintf(stemp, sizeof(stemp), "%d", val); 120ef2ee5d0SMichal Meloun devctl_notify("coretemp", "Thermal", stemp, 121ef2ee5d0SMichal Meloun "notify=0xcc"); 122ef2ee5d0SMichal Meloun } else { 123ef2ee5d0SMichal Meloun sc->overheat_log = 0; 124ef2ee5d0SMichal Meloun } 125ef2ee5d0SMichal Meloun 126ef2ee5d0SMichal Meloun return (sysctl_handle_int(oidp, 0, val, req)); 127ef2ee5d0SMichal Meloun } 128ef2ee5d0SMichal Meloun 129ef2ee5d0SMichal Meloun static int 130ef2ee5d0SMichal Meloun tegra124_coretemp_ofw_parse(struct tegra124_coretemp_softc *sc) 131ef2ee5d0SMichal Meloun { 132ef2ee5d0SMichal Meloun int rv, ncells; 133ef2ee5d0SMichal Meloun phandle_t node, xnode; 134ef2ee5d0SMichal Meloun pcell_t *cells; 135ef2ee5d0SMichal Meloun 136ef2ee5d0SMichal Meloun node = OF_peer(0); 137ef2ee5d0SMichal Meloun node = ofw_bus_find_child(node, "thermal-zones"); 138ef2ee5d0SMichal Meloun if (node <= 0) { 139ef2ee5d0SMichal Meloun device_printf(sc->dev, "Cannot find 'thermal-zones'.\n"); 140ef2ee5d0SMichal Meloun return (ENXIO); 141ef2ee5d0SMichal Meloun } 142ef2ee5d0SMichal Meloun 143ef2ee5d0SMichal Meloun node = ofw_bus_find_child(node, "cpu"); 144ef2ee5d0SMichal Meloun if (node <= 0) { 145ef2ee5d0SMichal Meloun device_printf(sc->dev, "Cannot find 'cpu'\n"); 146ef2ee5d0SMichal Meloun return (ENXIO); 147ef2ee5d0SMichal Meloun } 148ef2ee5d0SMichal Meloun rv = ofw_bus_parse_xref_list_alloc(node, "thermal-sensors", 149ef2ee5d0SMichal Meloun "#thermal-sensor-cells", 0, &xnode, &ncells, &cells); 150ef2ee5d0SMichal Meloun if (rv != 0) { 151ef2ee5d0SMichal Meloun device_printf(sc->dev, 152ef2ee5d0SMichal Meloun "Cannot parse 'thermal-sensors' property.\n"); 153ef2ee5d0SMichal Meloun return (ENXIO); 154ef2ee5d0SMichal Meloun } 155ef2ee5d0SMichal Meloun if (ncells != 1) { 156ef2ee5d0SMichal Meloun device_printf(sc->dev, 157ef2ee5d0SMichal Meloun "Invalid format of 'thermal-sensors' property(%d).\n", 158ef2ee5d0SMichal Meloun ncells); 159ef2ee5d0SMichal Meloun return (ENXIO); 160ef2ee5d0SMichal Meloun } 161ef2ee5d0SMichal Meloun 162ef2ee5d0SMichal Meloun sc->tsens_id = 0x100 + sc->cpu_id; //cells[0]; 163bebd5269SOleksandr Tymoshenko OF_prop_free(cells); 164ef2ee5d0SMichal Meloun 165ef2ee5d0SMichal Meloun sc->tsens_dev = OF_device_from_xref(xnode); 166ef2ee5d0SMichal Meloun if (sc->tsens_dev == NULL) { 167ef2ee5d0SMichal Meloun device_printf(sc->dev, 168ef2ee5d0SMichal Meloun "Cannot find thermal sensors device."); 169ef2ee5d0SMichal Meloun return (ENXIO); 170ef2ee5d0SMichal Meloun } 171ef2ee5d0SMichal Meloun return (0); 172ef2ee5d0SMichal Meloun } 173ef2ee5d0SMichal Meloun 174ef2ee5d0SMichal Meloun static void 175ef2ee5d0SMichal Meloun tegra124_coretemp_identify(driver_t *driver, device_t parent) 176ef2ee5d0SMichal Meloun { 1774d517b66SMichal Meloun phandle_t root; 178ef2ee5d0SMichal Meloun 1794d517b66SMichal Meloun root = OF_finddevice("/"); 1804d517b66SMichal Meloun if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124")) 181ef2ee5d0SMichal Meloun return; 1824d517b66SMichal Meloun if (device_find_child(parent, "tegra124_coretemp", -1) != NULL) 1834d517b66SMichal Meloun return; 1844d517b66SMichal Meloun if (BUS_ADD_CHILD(parent, 0, "tegra124_coretemp", -1) == NULL) 185ef2ee5d0SMichal Meloun device_printf(parent, "add child failed\n"); 186ef2ee5d0SMichal Meloun } 187ef2ee5d0SMichal Meloun 188ef2ee5d0SMichal Meloun static int 189ef2ee5d0SMichal Meloun tegra124_coretemp_probe(device_t dev) 190ef2ee5d0SMichal Meloun { 191ef2ee5d0SMichal Meloun 192a7b380d7SMichal Meloun device_set_desc(dev, "CPU Thermal Sensor"); 193ef2ee5d0SMichal Meloun return (0); 194ef2ee5d0SMichal Meloun } 195ef2ee5d0SMichal Meloun 196ef2ee5d0SMichal Meloun static int 197ef2ee5d0SMichal Meloun tegra124_coretemp_attach(device_t dev) 198ef2ee5d0SMichal Meloun { 199ef2ee5d0SMichal Meloun struct tegra124_coretemp_softc *sc; 200ef2ee5d0SMichal Meloun device_t pdev; 201ef2ee5d0SMichal Meloun struct sysctl_oid *oid; 202ef2ee5d0SMichal Meloun struct sysctl_ctx_list *ctx; 203ef2ee5d0SMichal Meloun int rv; 204ef2ee5d0SMichal Meloun 205ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 206ef2ee5d0SMichal Meloun sc->dev = dev; 207ef2ee5d0SMichal Meloun sc->cpu_id = device_get_unit(dev); 208ef2ee5d0SMichal Meloun sc->core_max_temp = 102000; 209ef2ee5d0SMichal Meloun pdev = device_get_parent(dev); 210ef2ee5d0SMichal Meloun 211ef2ee5d0SMichal Meloun rv = tegra124_coretemp_ofw_parse(sc); 212ef2ee5d0SMichal Meloun if (rv != 0) 213ef2ee5d0SMichal Meloun return (rv); 214ef2ee5d0SMichal Meloun 215ef2ee5d0SMichal Meloun ctx = device_get_sysctl_ctx(dev); 216ef2ee5d0SMichal Meloun 217ef2ee5d0SMichal Meloun oid = SYSCTL_ADD_NODE(ctx, 218ef2ee5d0SMichal Meloun SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), OID_AUTO, 2197029da5cSPawel Biernacki "coretemp", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 2207029da5cSPawel Biernacki "Per-CPU thermal information"); 221ef2ee5d0SMichal Meloun 222ef2ee5d0SMichal Meloun /* 223ef2ee5d0SMichal Meloun * Add the MIBs to dev.cpu.N and dev.cpu.N.coretemp. 224ef2ee5d0SMichal Meloun */ 225ef2ee5d0SMichal Meloun SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), 226ef2ee5d0SMichal Meloun OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 227ef2ee5d0SMichal Meloun dev, CORETEMP_TEMP, coretemp_get_val_sysctl, "IK", 228ef2ee5d0SMichal Meloun "Current temperature"); 229ef2ee5d0SMichal Meloun SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "delta", 230ef2ee5d0SMichal Meloun CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_DELTA, 231ef2ee5d0SMichal Meloun coretemp_get_val_sysctl, "I", 232ef2ee5d0SMichal Meloun "Delta between TCC activation and current temperature"); 233ef2ee5d0SMichal Meloun SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "resolution", 234ef2ee5d0SMichal Meloun CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_RESOLUTION, 235ef2ee5d0SMichal Meloun coretemp_get_val_sysctl, "I", 236ef2ee5d0SMichal Meloun "Resolution of CPU thermal sensor"); 237ef2ee5d0SMichal Meloun SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "tjmax", 238ef2ee5d0SMichal Meloun CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_TJMAX, 239ef2ee5d0SMichal Meloun coretemp_get_val_sysctl, "IK", 240ef2ee5d0SMichal Meloun "TCC activation temperature"); 241ef2ee5d0SMichal Meloun 242ef2ee5d0SMichal Meloun return (0); 243ef2ee5d0SMichal Meloun } 244ef2ee5d0SMichal Meloun 245ef2ee5d0SMichal Meloun static int 246ef2ee5d0SMichal Meloun tegra124_coretemp_detach(device_t dev) 247ef2ee5d0SMichal Meloun { 248ef2ee5d0SMichal Meloun return (0); 249ef2ee5d0SMichal Meloun } 250ef2ee5d0SMichal Meloun 251ef2ee5d0SMichal Meloun static device_method_t tegra124_coretemp_methods[] = { 252ef2ee5d0SMichal Meloun /* Device interface */ 253ef2ee5d0SMichal Meloun DEVMETHOD(device_identify, tegra124_coretemp_identify), 254ef2ee5d0SMichal Meloun DEVMETHOD(device_probe, tegra124_coretemp_probe), 255ef2ee5d0SMichal Meloun DEVMETHOD(device_attach, tegra124_coretemp_attach), 256ef2ee5d0SMichal Meloun DEVMETHOD(device_detach, tegra124_coretemp_detach), 257ef2ee5d0SMichal Meloun 258ef2ee5d0SMichal Meloun DEVMETHOD_END 259ef2ee5d0SMichal Meloun }; 260ef2ee5d0SMichal Meloun 2614d517b66SMichal Meloun static DEFINE_CLASS_0(tegra124_coretemp, tegra124_coretemp_driver, 2624bda238aSMichal Meloun tegra124_coretemp_methods, sizeof(struct tegra124_coretemp_softc)); 263289f133bSJohn Baldwin DRIVER_MODULE(tegra124_coretemp, cpu, tegra124_coretemp_driver, NULL, NULL); 264