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