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 49ef2ee5d0SMichal Meloun enum therm_info { 50ef2ee5d0SMichal Meloun CORETEMP_TEMP, 51ef2ee5d0SMichal Meloun CORETEMP_DELTA, 52ef2ee5d0SMichal Meloun CORETEMP_RESOLUTION, 53ef2ee5d0SMichal Meloun CORETEMP_TJMAX, 54ef2ee5d0SMichal Meloun }; 55ef2ee5d0SMichal Meloun 56ef2ee5d0SMichal Meloun struct tegra124_coretemp_softc { 57ef2ee5d0SMichal Meloun device_t dev; 58ef2ee5d0SMichal Meloun int overheat_log; 59ef2ee5d0SMichal Meloun int core_max_temp; 60ef2ee5d0SMichal Meloun int cpu_id; 61ef2ee5d0SMichal Meloun device_t tsens_dev; 62ef2ee5d0SMichal Meloun intptr_t tsens_id; 63ef2ee5d0SMichal Meloun }; 64ef2ee5d0SMichal Meloun 65ef2ee5d0SMichal Meloun static int 66ef2ee5d0SMichal Meloun coretemp_get_val_sysctl(SYSCTL_HANDLER_ARGS) 67ef2ee5d0SMichal Meloun { 68ef2ee5d0SMichal Meloun device_t dev; 69ef2ee5d0SMichal Meloun int val, temp, rv; 70ef2ee5d0SMichal Meloun struct tegra124_coretemp_softc *sc; 71ef2ee5d0SMichal Meloun enum therm_info type; 72ef2ee5d0SMichal Meloun char stemp[16]; 73ef2ee5d0SMichal Meloun 74ef2ee5d0SMichal Meloun 75ef2ee5d0SMichal Meloun dev = (device_t) arg1; 76ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 77ef2ee5d0SMichal Meloun type = arg2; 78ef2ee5d0SMichal Meloun 79ef2ee5d0SMichal Meloun 80ef2ee5d0SMichal Meloun rv = TEGRA_SOCTHERM_GET_TEMPERATURE(sc->tsens_dev, sc->dev, 81ef2ee5d0SMichal Meloun sc->tsens_id, &temp); 82ef2ee5d0SMichal Meloun if (rv != 0) { 83ef2ee5d0SMichal Meloun device_printf(sc->dev, 84ef2ee5d0SMichal Meloun "Cannot read temperature sensor %d: %d\n", 85ef2ee5d0SMichal Meloun sc->tsens_id, rv); 86ef2ee5d0SMichal Meloun return (rv); 87ef2ee5d0SMichal Meloun } 88ef2ee5d0SMichal Meloun 89ef2ee5d0SMichal Meloun switch (type) { 90ef2ee5d0SMichal Meloun case CORETEMP_TEMP: 91ef2ee5d0SMichal Meloun val = temp / 100; 92ef2ee5d0SMichal Meloun val += 2731; 93ef2ee5d0SMichal Meloun break; 94ef2ee5d0SMichal Meloun case CORETEMP_DELTA: 95ef2ee5d0SMichal Meloun val = (sc->core_max_temp - temp) / 1000; 96ef2ee5d0SMichal Meloun break; 97ef2ee5d0SMichal Meloun case CORETEMP_RESOLUTION: 98ef2ee5d0SMichal Meloun val = 1; 99ef2ee5d0SMichal Meloun break; 100ef2ee5d0SMichal Meloun case CORETEMP_TJMAX: 101ef2ee5d0SMichal Meloun val = sc->core_max_temp / 100; 102ef2ee5d0SMichal Meloun val += 2731; 103ef2ee5d0SMichal Meloun break; 104ef2ee5d0SMichal Meloun } 105ef2ee5d0SMichal Meloun 106ef2ee5d0SMichal Meloun 107ef2ee5d0SMichal Meloun if ((temp > sc->core_max_temp) && !sc->overheat_log) { 108ef2ee5d0SMichal Meloun sc->overheat_log = 1; 109ef2ee5d0SMichal Meloun 110ef2ee5d0SMichal Meloun /* 111ef2ee5d0SMichal Meloun * Check for Critical Temperature Status and Critical 112ef2ee5d0SMichal Meloun * Temperature Log. It doesn't really matter if the 113ef2ee5d0SMichal Meloun * current temperature is invalid because the "Critical 114ef2ee5d0SMichal Meloun * Temperature Log" bit will tell us if the Critical 115ef2ee5d0SMichal Meloun * Temperature has * been reached in past. It's not 116ef2ee5d0SMichal Meloun * directly related to the current temperature. 117ef2ee5d0SMichal Meloun * 118ef2ee5d0SMichal Meloun * If we reach a critical level, allow devctl(4) 119ef2ee5d0SMichal Meloun * to catch this and shutdown the system. 120ef2ee5d0SMichal Meloun */ 121ef2ee5d0SMichal Meloun device_printf(dev, "critical temperature detected, " 122ef2ee5d0SMichal Meloun "suggest system shutdown\n"); 123ef2ee5d0SMichal Meloun snprintf(stemp, sizeof(stemp), "%d", val); 124ef2ee5d0SMichal Meloun devctl_notify("coretemp", "Thermal", stemp, 125ef2ee5d0SMichal Meloun "notify=0xcc"); 126ef2ee5d0SMichal Meloun } else { 127ef2ee5d0SMichal Meloun sc->overheat_log = 0; 128ef2ee5d0SMichal Meloun } 129ef2ee5d0SMichal Meloun 130ef2ee5d0SMichal Meloun return (sysctl_handle_int(oidp, 0, val, req)); 131ef2ee5d0SMichal Meloun } 132ef2ee5d0SMichal Meloun 133ef2ee5d0SMichal Meloun static int 134ef2ee5d0SMichal Meloun tegra124_coretemp_ofw_parse(struct tegra124_coretemp_softc *sc) 135ef2ee5d0SMichal Meloun { 136ef2ee5d0SMichal Meloun int rv, ncells; 137ef2ee5d0SMichal Meloun phandle_t node, xnode; 138ef2ee5d0SMichal Meloun pcell_t *cells; 139ef2ee5d0SMichal Meloun 140ef2ee5d0SMichal Meloun node = OF_peer(0); 141ef2ee5d0SMichal Meloun node = ofw_bus_find_child(node, "thermal-zones"); 142ef2ee5d0SMichal Meloun if (node <= 0) { 143ef2ee5d0SMichal Meloun device_printf(sc->dev, "Cannot find 'thermal-zones'.\n"); 144ef2ee5d0SMichal Meloun return (ENXIO); 145ef2ee5d0SMichal Meloun } 146ef2ee5d0SMichal Meloun 147ef2ee5d0SMichal Meloun node = ofw_bus_find_child(node, "cpu"); 148ef2ee5d0SMichal Meloun if (node <= 0) { 149ef2ee5d0SMichal Meloun device_printf(sc->dev, "Cannot find 'cpu'\n"); 150ef2ee5d0SMichal Meloun return (ENXIO); 151ef2ee5d0SMichal Meloun } 152ef2ee5d0SMichal Meloun rv = ofw_bus_parse_xref_list_alloc(node, "thermal-sensors", 153ef2ee5d0SMichal Meloun "#thermal-sensor-cells", 0, &xnode, &ncells, &cells); 154ef2ee5d0SMichal Meloun if (rv != 0) { 155ef2ee5d0SMichal Meloun device_printf(sc->dev, 156ef2ee5d0SMichal Meloun "Cannot parse 'thermal-sensors' property.\n"); 157ef2ee5d0SMichal Meloun return (ENXIO); 158ef2ee5d0SMichal Meloun } 159ef2ee5d0SMichal Meloun if (ncells != 1) { 160ef2ee5d0SMichal Meloun device_printf(sc->dev, 161ef2ee5d0SMichal Meloun "Invalid format of 'thermal-sensors' property(%d).\n", 162ef2ee5d0SMichal Meloun ncells); 163ef2ee5d0SMichal Meloun return (ENXIO); 164ef2ee5d0SMichal Meloun } 165ef2ee5d0SMichal Meloun 166ef2ee5d0SMichal Meloun sc->tsens_id = 0x100 + sc->cpu_id; //cells[0]; 167bebd5269SOleksandr Tymoshenko OF_prop_free(cells); 168ef2ee5d0SMichal Meloun 169ef2ee5d0SMichal Meloun sc->tsens_dev = OF_device_from_xref(xnode); 170ef2ee5d0SMichal Meloun if (sc->tsens_dev == NULL) { 171ef2ee5d0SMichal Meloun device_printf(sc->dev, 172ef2ee5d0SMichal Meloun "Cannot find thermal sensors device."); 173ef2ee5d0SMichal Meloun return (ENXIO); 174ef2ee5d0SMichal Meloun } 175ef2ee5d0SMichal Meloun return (0); 176ef2ee5d0SMichal Meloun } 177ef2ee5d0SMichal Meloun 178ef2ee5d0SMichal Meloun static void 179ef2ee5d0SMichal Meloun tegra124_coretemp_identify(driver_t *driver, device_t parent) 180ef2ee5d0SMichal Meloun { 181ef2ee5d0SMichal Meloun 182ef2ee5d0SMichal Meloun if (device_find_child(parent, "tegra124_coretemp", -1) != NULL) 183ef2ee5d0SMichal Meloun return; 184ef2ee5d0SMichal 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 192ef2ee5d0SMichal Meloun device_set_desc(dev, "CPU Frequency Control"); 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, 219ef2ee5d0SMichal Meloun "coretemp", CTLFLAG_RD, NULL, "Per-CPU thermal information"); 220ef2ee5d0SMichal Meloun 221ef2ee5d0SMichal Meloun /* 222ef2ee5d0SMichal Meloun * Add the MIBs to dev.cpu.N and dev.cpu.N.coretemp. 223ef2ee5d0SMichal Meloun */ 224ef2ee5d0SMichal Meloun SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), 225ef2ee5d0SMichal Meloun OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 226ef2ee5d0SMichal Meloun dev, CORETEMP_TEMP, coretemp_get_val_sysctl, "IK", 227ef2ee5d0SMichal Meloun "Current temperature"); 228ef2ee5d0SMichal Meloun SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "delta", 229ef2ee5d0SMichal Meloun CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_DELTA, 230ef2ee5d0SMichal Meloun coretemp_get_val_sysctl, "I", 231ef2ee5d0SMichal Meloun "Delta between TCC activation and current temperature"); 232ef2ee5d0SMichal Meloun SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "resolution", 233ef2ee5d0SMichal Meloun CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_RESOLUTION, 234ef2ee5d0SMichal Meloun coretemp_get_val_sysctl, "I", 235ef2ee5d0SMichal Meloun "Resolution of CPU thermal sensor"); 236ef2ee5d0SMichal Meloun SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "tjmax", 237ef2ee5d0SMichal Meloun CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_TJMAX, 238ef2ee5d0SMichal Meloun coretemp_get_val_sysctl, "IK", 239ef2ee5d0SMichal Meloun "TCC activation temperature"); 240ef2ee5d0SMichal Meloun 241ef2ee5d0SMichal Meloun return (0); 242ef2ee5d0SMichal Meloun } 243ef2ee5d0SMichal Meloun 244ef2ee5d0SMichal Meloun static int 245ef2ee5d0SMichal Meloun tegra124_coretemp_detach(device_t dev) 246ef2ee5d0SMichal Meloun { 247ef2ee5d0SMichal Meloun struct tegra124_coretemp_softc *sc; 248ef2ee5d0SMichal Meloun 249ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 250ef2ee5d0SMichal Meloun return (0); 251ef2ee5d0SMichal Meloun } 252ef2ee5d0SMichal Meloun 253ef2ee5d0SMichal Meloun static device_method_t tegra124_coretemp_methods[] = { 254ef2ee5d0SMichal Meloun /* Device interface */ 255ef2ee5d0SMichal Meloun DEVMETHOD(device_identify, tegra124_coretemp_identify), 256ef2ee5d0SMichal Meloun DEVMETHOD(device_probe, tegra124_coretemp_probe), 257ef2ee5d0SMichal Meloun DEVMETHOD(device_attach, tegra124_coretemp_attach), 258ef2ee5d0SMichal Meloun DEVMETHOD(device_detach, tegra124_coretemp_detach), 259ef2ee5d0SMichal Meloun 260ef2ee5d0SMichal Meloun 261ef2ee5d0SMichal Meloun DEVMETHOD_END 262ef2ee5d0SMichal Meloun }; 263ef2ee5d0SMichal Meloun 264ef2ee5d0SMichal Meloun static devclass_t tegra124_coretemp_devclass; 2654bda238aSMichal Meloun static DEFINE_CLASS_0(coretemp, tegra124_coretemp_driver, 2664bda238aSMichal Meloun tegra124_coretemp_methods, sizeof(struct tegra124_coretemp_softc)); 267ef2ee5d0SMichal Meloun DRIVER_MODULE(tegra124_coretemp, cpu, tegra124_coretemp_driver, 2684bda238aSMichal Meloun tegra124_coretemp_devclass, NULL, NULL); 269