Path: blob/main/sys/arm64/nvidia/tegra210/tegra210_coretemp.c
48266 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright 2020 Michal Meloun <[email protected]>4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/param.h>28#include <sys/systm.h>29#include <sys/bus.h>30#include <sys/cpu.h>31#include <sys/kernel.h>32#include <sys/lock.h>33#include <sys/malloc.h>34#include <sys/module.h>35#include <sys/sysctl.h>3637#include <machine/bus.h>38#include <machine/cpu.h>3940#include <dev/clk/clk.h>41#include <dev/ofw/ofw_bus_subr.h>4243#include "tegra_soctherm_if.h"444546enum therm_info {47CORETEMP_TEMP,48CORETEMP_DELTA,49CORETEMP_RESOLUTION,50CORETEMP_TJMAX,51};5253struct tegra210_coretemp_softc {54device_t dev;55int overheat_log;56int core_max_temp;57int cpu_id;58device_t tsens_dev;59intptr_t tsens_id;60};6162static int63coretemp_get_val_sysctl(SYSCTL_HANDLER_ARGS)64{65device_t dev;66int val, temp, rv;67struct tegra210_coretemp_softc *sc;68enum therm_info type;69char stemp[16];707172dev = (device_t) arg1;73sc = device_get_softc(dev);74type = arg2;757677rv = TEGRA_SOCTHERM_GET_TEMPERATURE(sc->tsens_dev, sc->dev,78sc->tsens_id, &temp);79if (rv != 0) {80device_printf(sc->dev,81"Cannot read temperature sensor %u: %d\n",82(unsigned int)sc->tsens_id, rv);83return (rv);84}8586switch (type) {87case CORETEMP_TEMP:88val = temp / 100;89val += 2731;90break;91case CORETEMP_DELTA:92val = (sc->core_max_temp - temp) / 1000;93break;94case CORETEMP_RESOLUTION:95val = 1;96break;97case CORETEMP_TJMAX:98val = sc->core_max_temp / 100;99val += 2731;100break;101}102103104if ((temp > sc->core_max_temp) && !sc->overheat_log) {105sc->overheat_log = 1;106107/*108* Check for Critical Temperature Status and Critical109* Temperature Log. It doesn't really matter if the110* current temperature is invalid because the "Critical111* Temperature Log" bit will tell us if the Critical112* Temperature has * been reached in past. It's not113* directly related to the current temperature.114*115* If we reach a critical level, allow devctl(4)116* to catch this and shutdown the system.117*/118device_printf(dev, "critical temperature detected, "119"suggest system shutdown\n");120snprintf(stemp, sizeof(stemp), "%d", val);121devctl_notify("coretemp", "Thermal", stemp,122"notify=0xcc");123} else {124sc->overheat_log = 0;125}126127return (sysctl_handle_int(oidp, 0, val, req));128}129130static int131tegra210_coretemp_ofw_parse(struct tegra210_coretemp_softc *sc)132{133int rv, ncells;134phandle_t node, xnode;135pcell_t *cells;136137node = OF_peer(0);138node = ofw_bus_find_child(node, "thermal-zones");139if (node <= 0) {140device_printf(sc->dev, "Cannot find 'thermal-zones'.\n");141return (ENXIO);142}143144node = ofw_bus_find_child(node, "cpu");145if (node <= 0) {146device_printf(sc->dev, "Cannot find 'cpu'\n");147return (ENXIO);148}149rv = ofw_bus_parse_xref_list_alloc(node, "thermal-sensors",150"#thermal-sensor-cells", 0, &xnode, &ncells, &cells);151if (rv != 0) {152device_printf(sc->dev,153"Cannot parse 'thermal-sensors' property.\n");154return (ENXIO);155}156if (ncells != 1) {157device_printf(sc->dev,158"Invalid format of 'thermal-sensors' property(%d).\n",159ncells);160return (ENXIO);161}162163sc->tsens_id = 0x100 + sc->cpu_id;164OF_prop_free(cells);165166sc->tsens_dev = OF_device_from_xref(xnode);167if (sc->tsens_dev == NULL) {168device_printf(sc->dev,169"Cannot find thermal sensors device.");170return (ENXIO);171}172return (0);173}174175static void176tegra210_coretemp_identify(driver_t *driver, device_t parent)177{178phandle_t root;179180root = OF_finddevice("/");181if (!ofw_bus_node_is_compatible(root, "nvidia,tegra210"))182return;183if (device_find_child(parent, "tegra210_coretemp", DEVICE_UNIT_ANY) != NULL)184return;185if (BUS_ADD_CHILD(parent, 0, "tegra210_coretemp", DEVICE_UNIT_ANY) == NULL)186device_printf(parent, "add child failed\n");187}188189static int190tegra210_coretemp_probe(device_t dev)191{192193device_set_desc(dev, "CPU Thermal Sensor");194return (0);195}196197static int198tegra210_coretemp_attach(device_t dev)199{200struct tegra210_coretemp_softc *sc;201device_t pdev;202struct sysctl_oid *oid;203struct sysctl_ctx_list *ctx;204int rv;205206sc = device_get_softc(dev);207sc->dev = dev;208sc->cpu_id = device_get_unit(dev);209sc->core_max_temp = 102000;210pdev = device_get_parent(dev);211212rv = tegra210_coretemp_ofw_parse(sc);213if (rv != 0)214return (rv);215216ctx = device_get_sysctl_ctx(dev);217218oid = SYSCTL_ADD_NODE(ctx,219SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)), OID_AUTO,220"coretemp", CTLFLAG_RD, NULL, "Per-CPU thermal information");221222/*223* Add the MIBs to dev.cpu.N and dev.cpu.N.coretemp.224*/225SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(pdev)),226OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,227dev, CORETEMP_TEMP, coretemp_get_val_sysctl, "IK",228"Current temperature");229SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "delta",230CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_DELTA,231coretemp_get_val_sysctl, "I",232"Delta between TCC activation and current temperature");233SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "resolution",234CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_RESOLUTION,235coretemp_get_val_sysctl, "I",236"Resolution of CPU thermal sensor");237SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "tjmax",238CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, CORETEMP_TJMAX,239coretemp_get_val_sysctl, "IK",240"TCC activation temperature");241242return (0);243}244245static int246tegra210_coretemp_detach(device_t dev)247{248return (0);249}250251static device_method_t tegra210_coretemp_methods[] = {252/* Device interface */253DEVMETHOD(device_identify, tegra210_coretemp_identify),254DEVMETHOD(device_probe, tegra210_coretemp_probe),255DEVMETHOD(device_attach, tegra210_coretemp_attach),256DEVMETHOD(device_detach, tegra210_coretemp_detach),257258DEVMETHOD_END259};260261static DEFINE_CLASS_0(tegra210_coretemp, tegra210_coretemp_driver,262tegra210_coretemp_methods, sizeof(struct tegra210_coretemp_softc));263DRIVER_MODULE(tegra210_coretemp, cpu, tegra210_coretemp_driver, NULL, NULL);264265266