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