1296936Smmel/*- 2296936Smmel * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3296936Smmel * All rights reserved. 4296936Smmel * 5296936Smmel * Redistribution and use in source and binary forms, with or without 6296936Smmel * modification, are permitted provided that the following conditions 7296936Smmel * are met: 8296936Smmel * 1. Redistributions of source code must retain the above copyright 9296936Smmel * notice, this list of conditions and the following disclaimer. 10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright 11296936Smmel * notice, this list of conditions and the following disclaimer in the 12296936Smmel * documentation and/or other materials provided with the distribution. 13296936Smmel * 14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17296936Smmel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24296936Smmel * SUCH DAMAGE. 25296936Smmel */ 26296936Smmel 27296936Smmel#include <sys/cdefs.h> 28296936Smmel__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra124/tegra124_coretemp.c 308374 2016-11-06 15:25:46Z mmel $"); 29296936Smmel 30296936Smmel#include <sys/param.h> 31296936Smmel#include <sys/systm.h> 32296936Smmel#include <sys/bus.h> 33296936Smmel#include <sys/cpu.h> 34296936Smmel#include <sys/kernel.h> 35296936Smmel#include <sys/lock.h> 36296936Smmel#include <sys/malloc.h> 37296936Smmel#include <sys/module.h> 38296936Smmel#include <sys/sysctl.h> 39296936Smmel 40296936Smmel#include <machine/bus.h> 41296936Smmel#include <machine/cpu.h> 42296936Smmel 43296936Smmel#include <dev/extres/clk/clk.h> 44296936Smmel#include <dev/ofw/ofw_bus_subr.h> 45296936Smmel 46296936Smmel#include "tegra_soctherm_if.h" 47296936Smmel 48296936Smmel 49296936Smmelenum therm_info { 50296936Smmel CORETEMP_TEMP, 51296936Smmel CORETEMP_DELTA, 52296936Smmel CORETEMP_RESOLUTION, 53296936Smmel CORETEMP_TJMAX, 54296936Smmel}; 55296936Smmel 56296936Smmelstruct tegra124_coretemp_softc { 57296936Smmel device_t dev; 58296936Smmel int overheat_log; 59296936Smmel int core_max_temp; 60296936Smmel int cpu_id; 61296936Smmel device_t tsens_dev; 62296936Smmel intptr_t tsens_id; 63296936Smmel}; 64296936Smmel 65296936Smmelstatic int 66296936Smmelcoretemp_get_val_sysctl(SYSCTL_HANDLER_ARGS) 67296936Smmel{ 68296936Smmel device_t dev; 69296936Smmel int val, temp, rv; 70296936Smmel struct tegra124_coretemp_softc *sc; 71296936Smmel enum therm_info type; 72296936Smmel char stemp[16]; 73296936Smmel 74296936Smmel 75296936Smmel dev = (device_t) arg1; 76296936Smmel sc = device_get_softc(dev); 77296936Smmel type = arg2; 78296936Smmel 79296936Smmel 80296936Smmel rv = TEGRA_SOCTHERM_GET_TEMPERATURE(sc->tsens_dev, sc->dev, 81296936Smmel sc->tsens_id, &temp); 82296936Smmel if (rv != 0) { 83296936Smmel device_printf(sc->dev, 84296936Smmel "Cannot read temperature sensor %d: %d\n", 85296936Smmel sc->tsens_id, rv); 86296936Smmel return (rv); 87296936Smmel } 88296936Smmel 89296936Smmel switch (type) { 90296936Smmel case CORETEMP_TEMP: 91296936Smmel val = temp / 100; 92296936Smmel val += 2731; 93296936Smmel break; 94296936Smmel case CORETEMP_DELTA: 95296936Smmel val = (sc->core_max_temp - temp) / 1000; 96296936Smmel break; 97296936Smmel case CORETEMP_RESOLUTION: 98296936Smmel val = 1; 99296936Smmel break; 100296936Smmel case CORETEMP_TJMAX: 101296936Smmel val = sc->core_max_temp / 100; 102296936Smmel val += 2731; 103296936Smmel break; 104296936Smmel } 105296936Smmel 106296936Smmel 107296936Smmel if ((temp > sc->core_max_temp) && !sc->overheat_log) { 108296936Smmel sc->overheat_log = 1; 109296936Smmel 110296936Smmel /* 111296936Smmel * Check for Critical Temperature Status and Critical 112296936Smmel * Temperature Log. It doesn't really matter if the 113296936Smmel * current temperature is invalid because the "Critical 114296936Smmel * Temperature Log" bit will tell us if the Critical 115296936Smmel * Temperature has * been reached in past. It's not 116296936Smmel * directly related to the current temperature. 117296936Smmel * 118296936Smmel * If we reach a critical level, allow devctl(4) 119296936Smmel * to catch this and shutdown the system. 120296936Smmel */ 121296936Smmel device_printf(dev, "critical temperature detected, " 122296936Smmel "suggest system shutdown\n"); 123296936Smmel snprintf(stemp, sizeof(stemp), "%d", val); 124296936Smmel devctl_notify("coretemp", "Thermal", stemp, 125296936Smmel "notify=0xcc"); 126296936Smmel } else { 127296936Smmel sc->overheat_log = 0; 128296936Smmel } 129296936Smmel 130296936Smmel return (sysctl_handle_int(oidp, 0, val, req)); 131296936Smmel} 132296936Smmel 133296936Smmelstatic int 134296936Smmeltegra124_coretemp_ofw_parse(struct tegra124_coretemp_softc *sc) 135296936Smmel{ 136296936Smmel int rv, ncells; 137296936Smmel phandle_t node, xnode; 138296936Smmel pcell_t *cells; 139296936Smmel 140296936Smmel node = OF_peer(0); 141296936Smmel node = ofw_bus_find_child(node, "thermal-zones"); 142296936Smmel if (node <= 0) { 143296936Smmel device_printf(sc->dev, "Cannot find 'thermal-zones'.\n"); 144296936Smmel return (ENXIO); 145296936Smmel } 146296936Smmel 147296936Smmel node = ofw_bus_find_child(node, "cpu"); 148296936Smmel if (node <= 0) { 149296936Smmel device_printf(sc->dev, "Cannot find 'cpu'\n"); 150296936Smmel return (ENXIO); 151296936Smmel } 152296936Smmel rv = ofw_bus_parse_xref_list_alloc(node, "thermal-sensors", 153296936Smmel "#thermal-sensor-cells", 0, &xnode, &ncells, &cells); 154296936Smmel if (rv != 0) { 155296936Smmel device_printf(sc->dev, 156296936Smmel "Cannot parse 'thermal-sensors' property.\n"); 157296936Smmel return (ENXIO); 158296936Smmel } 159296936Smmel if (ncells != 1) { 160296936Smmel device_printf(sc->dev, 161296936Smmel "Invalid format of 'thermal-sensors' property(%d).\n", 162296936Smmel ncells); 163296936Smmel return (ENXIO); 164296936Smmel } 165296936Smmel 166296936Smmel sc->tsens_id = 0x100 + sc->cpu_id; //cells[0]; 167299715Sgonzo OF_prop_free(cells); 168296936Smmel 169296936Smmel sc->tsens_dev = OF_device_from_xref(xnode); 170296936Smmel if (sc->tsens_dev == NULL) { 171296936Smmel device_printf(sc->dev, 172296936Smmel "Cannot find thermal sensors device."); 173296936Smmel return (ENXIO); 174296936Smmel } 175296936Smmel return (0); 176296936Smmel} 177296936Smmel 178296936Smmelstatic void 179296936Smmeltegra124_coretemp_identify(driver_t *driver, device_t parent) 180296936Smmel{ 181308374Smmel phandle_t root; 182296936Smmel 183308374Smmel root = OF_finddevice("/"); 184308374Smmel if (!ofw_bus_node_is_compatible(root, "nvidia,tegra124")) 185308374Smmel return; 186296936Smmel if (device_find_child(parent, "tegra124_coretemp", -1) != NULL) 187296936Smmel return; 188296936Smmel if (BUS_ADD_CHILD(parent, 0, "tegra124_coretemp", -1) == NULL) 189296936Smmel device_printf(parent, "add child failed\n"); 190296936Smmel} 191296936Smmel 192296936Smmelstatic int 193296936Smmeltegra124_coretemp_probe(device_t dev) 194296936Smmel{ 195296936Smmel 196308374Smmel device_set_desc(dev, "CPU Thermal Sensor"); 197296936Smmel return (0); 198296936Smmel} 199296936Smmel 200296936Smmelstatic int 201296936Smmeltegra124_coretemp_attach(device_t dev) 202296936Smmel{ 203296936Smmel struct tegra124_coretemp_softc *sc; 204296936Smmel device_t pdev; 205296936Smmel struct sysctl_oid *oid; 206296936Smmel struct sysctl_ctx_list *ctx; 207296936Smmel int rv; 208296936Smmel 209296936Smmel sc = device_get_softc(dev); 210296936Smmel sc->dev = dev; 211296936Smmel sc->cpu_id = device_get_unit(dev); 212296936Smmel sc->core_max_temp = 102000; 213296936Smmel pdev = device_get_parent(dev); 214296936Smmel 215296936Smmel rv = tegra124_coretemp_ofw_parse(sc); 216296936Smmel if (rv != 0) 217296936Smmel return (rv); 218296936Smmel 219296936Smmel ctx = device_get_sysctl_ctx(dev); 220296936Smmel 221296936Smmel oid = SYSCTL_ADD_NODE(ctx, 222296936Smmel SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), OID_AUTO, 223296936Smmel "coretemp", CTLFLAG_RD, NULL, "Per-CPU thermal information"); 224296936Smmel 225296936Smmel /* 226296936Smmel * Add the MIBs to dev.cpu.N and dev.cpu.N.coretemp. 227296936Smmel */ 228296936Smmel SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), 229296936Smmel OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 230296936Smmel dev, CORETEMP_TEMP, coretemp_get_val_sysctl, "IK", 231296936Smmel "Current temperature"); 232296936Smmel SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "delta", 233296936Smmel CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_DELTA, 234296936Smmel coretemp_get_val_sysctl, "I", 235296936Smmel "Delta between TCC activation and current temperature"); 236296936Smmel SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "resolution", 237296936Smmel CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_RESOLUTION, 238296936Smmel coretemp_get_val_sysctl, "I", 239296936Smmel "Resolution of CPU thermal sensor"); 240296936Smmel SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "tjmax", 241296936Smmel CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_TJMAX, 242296936Smmel coretemp_get_val_sysctl, "IK", 243296936Smmel "TCC activation temperature"); 244296936Smmel 245296936Smmel return (0); 246296936Smmel} 247296936Smmel 248296936Smmelstatic int 249296936Smmeltegra124_coretemp_detach(device_t dev) 250296936Smmel{ 251296936Smmel struct tegra124_coretemp_softc *sc; 252296936Smmel 253296936Smmel sc = device_get_softc(dev); 254296936Smmel return (0); 255296936Smmel} 256296936Smmel 257296936Smmelstatic device_method_t tegra124_coretemp_methods[] = { 258296936Smmel /* Device interface */ 259296936Smmel DEVMETHOD(device_identify, tegra124_coretemp_identify), 260296936Smmel DEVMETHOD(device_probe, tegra124_coretemp_probe), 261296936Smmel DEVMETHOD(device_attach, tegra124_coretemp_attach), 262296936Smmel DEVMETHOD(device_detach, tegra124_coretemp_detach), 263296936Smmel 264296936Smmel 265296936Smmel DEVMETHOD_END 266296936Smmel}; 267296936Smmel 268296936Smmelstatic devclass_t tegra124_coretemp_devclass; 269308374Smmelstatic DEFINE_CLASS_0(tegra124_coretemp, tegra124_coretemp_driver, 270308335Smmel tegra124_coretemp_methods, sizeof(struct tegra124_coretemp_softc)); 271296936SmmelDRIVER_MODULE(tegra124_coretemp, cpu, tegra124_coretemp_driver, 272308335Smmel tegra124_coretemp_devclass, NULL, NULL); 273