1343044c4SJared McNeill /*- 2343044c4SJared McNeill * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3343044c4SJared McNeill * All rights reserved. 4343044c4SJared McNeill * 5343044c4SJared McNeill * Redistribution and use in source and binary forms, with or without 6343044c4SJared McNeill * modification, are permitted provided that the following conditions 7343044c4SJared McNeill * are met: 8343044c4SJared McNeill * 1. Redistributions of source code must retain the above copyright 9343044c4SJared McNeill * notice, this list of conditions and the following disclaimer. 10343044c4SJared McNeill * 2. Redistributions in binary form must reproduce the above copyright 11343044c4SJared McNeill * notice, this list of conditions and the following disclaimer in the 12343044c4SJared McNeill * documentation and/or other materials provided with the distribution. 13343044c4SJared McNeill * 14343044c4SJared McNeill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15343044c4SJared McNeill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16343044c4SJared McNeill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17343044c4SJared McNeill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18343044c4SJared McNeill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19343044c4SJared McNeill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20343044c4SJared McNeill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21343044c4SJared McNeill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22343044c4SJared McNeill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23343044c4SJared McNeill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24343044c4SJared McNeill * SUCH DAMAGE. 25343044c4SJared McNeill * 26343044c4SJared McNeill * $FreeBSD$ 27343044c4SJared McNeill */ 28343044c4SJared McNeill 29343044c4SJared McNeill /* 30343044c4SJared McNeill * Allwinner thermal sensor controller 31343044c4SJared McNeill */ 32343044c4SJared McNeill 33343044c4SJared McNeill #include <sys/cdefs.h> 34343044c4SJared McNeill __FBSDID("$FreeBSD$"); 35343044c4SJared McNeill 36343044c4SJared McNeill #include <sys/param.h> 37343044c4SJared McNeill #include <sys/systm.h> 38343044c4SJared McNeill #include <sys/bus.h> 39343044c4SJared McNeill #include <sys/rman.h> 40343044c4SJared McNeill #include <sys/kernel.h> 41343044c4SJared McNeill #include <sys/sysctl.h> 42d69d5ab0SJared McNeill #include <sys/reboot.h> 43343044c4SJared McNeill #include <sys/module.h> 443c2b90f1SJared McNeill #include <sys/cpu.h> 452e4f9347SJared McNeill #include <sys/taskqueue.h> 46343044c4SJared McNeill #include <machine/bus.h> 47343044c4SJared McNeill 48343044c4SJared McNeill #include <dev/ofw/ofw_bus.h> 49343044c4SJared McNeill #include <dev/ofw/ofw_bus_subr.h> 50343044c4SJared McNeill 51d69d5ab0SJared McNeill #include <dev/extres/clk/clk.h> 52d69d5ab0SJared McNeill #include <dev/extres/hwreset/hwreset.h> 5397eb836fSEmmanuel Vadot #include <dev/extres/nvmem/nvmem.h> 54d69d5ab0SJared McNeill 55343044c4SJared McNeill #include <arm/allwinner/aw_sid.h> 56343044c4SJared McNeill 573c2b90f1SJared McNeill #include "cpufreq_if.h" 5897eb836fSEmmanuel Vadot #include "nvmem_if.h" 593c2b90f1SJared McNeill 60343044c4SJared McNeill #define THS_CTRL0 0x00 61d69d5ab0SJared McNeill #define THS_CTRL1 0x04 62d69d5ab0SJared McNeill #define ADC_CALI_EN (1 << 17) 63343044c4SJared McNeill #define THS_CTRL2 0x40 64343044c4SJared McNeill #define SENSOR_ACQ1_SHIFT 16 65343044c4SJared McNeill #define SENSOR2_EN (1 << 2) 66343044c4SJared McNeill #define SENSOR1_EN (1 << 1) 67343044c4SJared McNeill #define SENSOR0_EN (1 << 0) 68343044c4SJared McNeill #define THS_INTC 0x44 69343044c4SJared McNeill #define THS_INTS 0x48 70d69d5ab0SJared McNeill #define THS2_DATA_IRQ_STS (1 << 10) 71d69d5ab0SJared McNeill #define THS1_DATA_IRQ_STS (1 << 9) 72d69d5ab0SJared McNeill #define THS0_DATA_IRQ_STS (1 << 8) 73d69d5ab0SJared McNeill #define SHUT_INT2_STS (1 << 6) 74d69d5ab0SJared McNeill #define SHUT_INT1_STS (1 << 5) 75d69d5ab0SJared McNeill #define SHUT_INT0_STS (1 << 4) 76d69d5ab0SJared McNeill #define ALARM_INT2_STS (1 << 2) 77d69d5ab0SJared McNeill #define ALARM_INT1_STS (1 << 1) 78d69d5ab0SJared McNeill #define ALARM_INT0_STS (1 << 0) 793c2b90f1SJared McNeill #define THS_ALARM0_CTRL 0x50 803c2b90f1SJared McNeill #define ALARM_T_HOT_MASK 0xfff 813c2b90f1SJared McNeill #define ALARM_T_HOT_SHIFT 16 823c2b90f1SJared McNeill #define ALARM_T_HYST_MASK 0xfff 833c2b90f1SJared McNeill #define ALARM_T_HYST_SHIFT 0 843c2b90f1SJared McNeill #define THS_SHUTDOWN0_CTRL 0x60 853c2b90f1SJared McNeill #define SHUT_T_HOT_MASK 0xfff 863c2b90f1SJared McNeill #define SHUT_T_HOT_SHIFT 16 87343044c4SJared McNeill #define THS_FILTER 0x70 88343044c4SJared McNeill #define THS_CALIB0 0x74 89343044c4SJared McNeill #define THS_CALIB1 0x78 90343044c4SJared McNeill #define THS_DATA0 0x80 91343044c4SJared McNeill #define THS_DATA1 0x84 92343044c4SJared McNeill #define THS_DATA2 0x88 93343044c4SJared McNeill #define DATA_MASK 0xfff 94343044c4SJared McNeill 95d69d5ab0SJared McNeill #define A83T_ADC_ACQUIRE_TIME 0x17 96d69d5ab0SJared McNeill #define A83T_FILTER 0x4 97d69d5ab0SJared McNeill #define A83T_INTC 0x1000 98d69d5ab0SJared McNeill #define A83T_TEMP_BASE 2719000 994e7f43baSJared McNeill #define A83T_TEMP_MUL 1000 100d69d5ab0SJared McNeill #define A83T_TEMP_DIV 14186 101d69d5ab0SJared McNeill #define A83T_CLK_RATE 24000000 102d69d5ab0SJared McNeill 103d69d5ab0SJared McNeill #define A64_ADC_ACQUIRE_TIME 0x190 104d69d5ab0SJared McNeill #define A64_FILTER 0x6 105d69d5ab0SJared McNeill #define A64_INTC 0x18000 106d69d5ab0SJared McNeill #define A64_TEMP_BASE 2170000 1074e7f43baSJared McNeill #define A64_TEMP_MUL 1000 108d69d5ab0SJared McNeill #define A64_TEMP_DIV 8560 109d69d5ab0SJared McNeill #define A64_CLK_RATE 4000000 110d69d5ab0SJared McNeill 1114e7f43baSJared McNeill #define H3_ADC_ACQUIRE_TIME 0x3f 1124e7f43baSJared McNeill #define H3_FILTER 0x6 1134e7f43baSJared McNeill #define H3_INTC 0x191000 1140a30b4b2SJared McNeill #define H3_TEMP_BASE 217 1153c2b90f1SJared McNeill #define H3_TEMP_MUL 1000 1160a30b4b2SJared McNeill #define H3_TEMP_DIV 8253 1170a30b4b2SJared McNeill #define H3_TEMP_MINUS 1794000 1184e7f43baSJared McNeill #define H3_CLK_RATE 4000000 1190a30b4b2SJared McNeill #define H3_INIT_ALARM 90 /* degC */ 1200a30b4b2SJared McNeill #define H3_INIT_SHUT 105 /* degC */ 1214e7f43baSJared McNeill 122d69d5ab0SJared McNeill #define TEMP_C_TO_K 273 123343044c4SJared McNeill #define SENSOR_ENABLE_ALL (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN) 124d69d5ab0SJared McNeill #define SHUT_INT_ALL (SHUT_INT0_STS|SHUT_INT1_STS|SHUT_INT2_STS) 1253c2b90f1SJared McNeill #define ALARM_INT_ALL (ALARM_INT0_STS) 126343044c4SJared McNeill 127d69d5ab0SJared McNeill #define MAX_SENSORS 3 1283c2b90f1SJared McNeill #define MAX_CF_LEVELS 64 1293c2b90f1SJared McNeill 1303c2b90f1SJared McNeill #define THROTTLE_ENABLE_DEFAULT 1 1313c2b90f1SJared McNeill 1323c2b90f1SJared McNeill /* Enable thermal throttling */ 1333c2b90f1SJared McNeill static int aw_thermal_throttle_enable = THROTTLE_ENABLE_DEFAULT; 1343c2b90f1SJared McNeill TUNABLE_INT("hw.aw_thermal.throttle_enable", &aw_thermal_throttle_enable); 135343044c4SJared McNeill 136d69d5ab0SJared McNeill struct aw_thermal_sensor { 137343044c4SJared McNeill const char *name; 138343044c4SJared McNeill const char *desc; 1390a30b4b2SJared McNeill int init_alarm; 1400a30b4b2SJared McNeill int init_shut; 141343044c4SJared McNeill }; 142343044c4SJared McNeill 143d69d5ab0SJared McNeill struct aw_thermal_config { 144d69d5ab0SJared McNeill struct aw_thermal_sensor sensors[MAX_SENSORS]; 145d69d5ab0SJared McNeill int nsensors; 146d69d5ab0SJared McNeill uint64_t clk_rate; 147d69d5ab0SJared McNeill uint32_t adc_acquire_time; 1483c2b90f1SJared McNeill int adc_cali_en; 149d69d5ab0SJared McNeill uint32_t filter; 150d69d5ab0SJared McNeill uint32_t intc; 1513c2b90f1SJared McNeill int (*to_temp)(uint32_t); 1520a30b4b2SJared McNeill uint32_t (*to_reg)(int); 1534e7f43baSJared McNeill int temp_base; 1544e7f43baSJared McNeill int temp_mul; 1554e7f43baSJared McNeill int temp_div; 1563c2b90f1SJared McNeill int calib0, calib1; 1573c2b90f1SJared McNeill uint32_t calib0_mask, calib1_mask; 158d69d5ab0SJared McNeill }; 159d69d5ab0SJared McNeill 1603c2b90f1SJared McNeill static int 1613c2b90f1SJared McNeill a83t_to_temp(uint32_t val) 1623c2b90f1SJared McNeill { 1633c2b90f1SJared McNeill return ((A83T_TEMP_BASE - (val * A83T_TEMP_MUL)) / A83T_TEMP_DIV); 1643c2b90f1SJared McNeill } 1653c2b90f1SJared McNeill 166d69d5ab0SJared McNeill static const struct aw_thermal_config a83t_config = { 167d69d5ab0SJared McNeill .nsensors = 3, 168d69d5ab0SJared McNeill .sensors = { 169d69d5ab0SJared McNeill [0] = { 170d69d5ab0SJared McNeill .name = "cluster0", 171d69d5ab0SJared McNeill .desc = "CPU cluster 0 temperature", 172d69d5ab0SJared McNeill }, 173d69d5ab0SJared McNeill [1] = { 174d69d5ab0SJared McNeill .name = "cluster1", 175d69d5ab0SJared McNeill .desc = "CPU cluster 1 temperature", 176d69d5ab0SJared McNeill }, 177d69d5ab0SJared McNeill [2] = { 178d69d5ab0SJared McNeill .name = "gpu", 179d69d5ab0SJared McNeill .desc = "GPU temperature", 180d69d5ab0SJared McNeill }, 181d69d5ab0SJared McNeill }, 182d69d5ab0SJared McNeill .clk_rate = A83T_CLK_RATE, 183d69d5ab0SJared McNeill .adc_acquire_time = A83T_ADC_ACQUIRE_TIME, 1843c2b90f1SJared McNeill .adc_cali_en = 1, 185d69d5ab0SJared McNeill .filter = A83T_FILTER, 186d69d5ab0SJared McNeill .intc = A83T_INTC, 1873c2b90f1SJared McNeill .to_temp = a83t_to_temp, 1883c2b90f1SJared McNeill .calib0_mask = 0xffffffff, 1893c2b90f1SJared McNeill .calib1_mask = 0xffffffff, 190d69d5ab0SJared McNeill }; 191d69d5ab0SJared McNeill 1923c2b90f1SJared McNeill static int 1933c2b90f1SJared McNeill a64_to_temp(uint32_t val) 1943c2b90f1SJared McNeill { 1953c2b90f1SJared McNeill return ((A64_TEMP_BASE - (val * A64_TEMP_MUL)) / A64_TEMP_DIV); 1963c2b90f1SJared McNeill } 1973c2b90f1SJared McNeill 198d69d5ab0SJared McNeill static const struct aw_thermal_config a64_config = { 199d69d5ab0SJared McNeill .nsensors = 3, 200d69d5ab0SJared McNeill .sensors = { 201d69d5ab0SJared McNeill [0] = { 202d69d5ab0SJared McNeill .name = "cpu", 203d69d5ab0SJared McNeill .desc = "CPU temperature", 204d69d5ab0SJared McNeill }, 205d69d5ab0SJared McNeill [1] = { 206d69d5ab0SJared McNeill .name = "gpu1", 207d69d5ab0SJared McNeill .desc = "GPU temperature 1", 208d69d5ab0SJared McNeill }, 209d69d5ab0SJared McNeill [2] = { 210d69d5ab0SJared McNeill .name = "gpu2", 211d69d5ab0SJared McNeill .desc = "GPU temperature 2", 212d69d5ab0SJared McNeill }, 213d69d5ab0SJared McNeill }, 214d69d5ab0SJared McNeill .clk_rate = A64_CLK_RATE, 215d69d5ab0SJared McNeill .adc_acquire_time = A64_ADC_ACQUIRE_TIME, 216d69d5ab0SJared McNeill .filter = A64_FILTER, 217d69d5ab0SJared McNeill .intc = A64_INTC, 2183c2b90f1SJared McNeill .to_temp = a64_to_temp, 219343044c4SJared McNeill }; 220343044c4SJared McNeill 2213c2b90f1SJared McNeill static int 2223c2b90f1SJared McNeill h3_to_temp(uint32_t val) 2233c2b90f1SJared McNeill { 2240a30b4b2SJared McNeill return (H3_TEMP_BASE - ((val * H3_TEMP_MUL) / H3_TEMP_DIV)); 2250a30b4b2SJared McNeill } 2260a30b4b2SJared McNeill 2270a30b4b2SJared McNeill static uint32_t 2280a30b4b2SJared McNeill h3_to_reg(int val) 2290a30b4b2SJared McNeill { 2300a30b4b2SJared McNeill return ((H3_TEMP_MINUS - (val * H3_TEMP_DIV)) / H3_TEMP_MUL); 2313c2b90f1SJared McNeill } 2323c2b90f1SJared McNeill 2334e7f43baSJared McNeill static const struct aw_thermal_config h3_config = { 2344e7f43baSJared McNeill .nsensors = 1, 2354e7f43baSJared McNeill .sensors = { 2364e7f43baSJared McNeill [0] = { 2374e7f43baSJared McNeill .name = "cpu", 2384e7f43baSJared McNeill .desc = "CPU temperature", 2390a30b4b2SJared McNeill .init_alarm = H3_INIT_ALARM, 2400a30b4b2SJared McNeill .init_shut = H3_INIT_SHUT, 2414e7f43baSJared McNeill }, 2424e7f43baSJared McNeill }, 2434e7f43baSJared McNeill .clk_rate = H3_CLK_RATE, 2444e7f43baSJared McNeill .adc_acquire_time = H3_ADC_ACQUIRE_TIME, 2454e7f43baSJared McNeill .filter = H3_FILTER, 2464e7f43baSJared McNeill .intc = H3_INTC, 2473c2b90f1SJared McNeill .to_temp = h3_to_temp, 2480a30b4b2SJared McNeill .to_reg = h3_to_reg, 2493c2b90f1SJared McNeill .calib0_mask = 0xfff, 2504e7f43baSJared McNeill }; 2514e7f43baSJared McNeill 252343044c4SJared McNeill static struct ofw_compat_data compat_data[] = { 253d69d5ab0SJared McNeill { "allwinner,sun8i-a83t-ts", (uintptr_t)&a83t_config }, 2544e7f43baSJared McNeill { "allwinner,sun8i-h3-ts", (uintptr_t)&h3_config }, 255d69d5ab0SJared McNeill { "allwinner,sun50i-a64-ts", (uintptr_t)&a64_config }, 256343044c4SJared McNeill { NULL, (uintptr_t)NULL } 257343044c4SJared McNeill }; 258343044c4SJared McNeill 259343044c4SJared McNeill #define THS_CONF(d) \ 260343044c4SJared McNeill (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data 261343044c4SJared McNeill 262343044c4SJared McNeill struct aw_thermal_softc { 2633c2b90f1SJared McNeill device_t dev; 264d69d5ab0SJared McNeill struct resource *res[2]; 265d69d5ab0SJared McNeill struct aw_thermal_config *conf; 2663c2b90f1SJared McNeill 2672e4f9347SJared McNeill struct task cf_task; 2683c2b90f1SJared McNeill int throttle; 2693c2b90f1SJared McNeill int min_freq; 2703c2b90f1SJared McNeill struct cf_level levels[MAX_CF_LEVELS]; 2713c2b90f1SJared McNeill eventhandler_tag cf_pre_tag; 272343044c4SJared McNeill }; 273343044c4SJared McNeill 274343044c4SJared McNeill static struct resource_spec aw_thermal_spec[] = { 275343044c4SJared McNeill { SYS_RES_MEMORY, 0, RF_ACTIVE }, 276d69d5ab0SJared McNeill { SYS_RES_IRQ, 0, RF_ACTIVE }, 277343044c4SJared McNeill { -1, 0 } 278343044c4SJared McNeill }; 279343044c4SJared McNeill 280d69d5ab0SJared McNeill #define RD4(sc, reg) bus_read_4((sc)->res[0], (reg)) 281d69d5ab0SJared McNeill #define WR4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) 282343044c4SJared McNeill 283343044c4SJared McNeill static int 284343044c4SJared McNeill aw_thermal_init(struct aw_thermal_softc *sc) 285343044c4SJared McNeill { 28697eb836fSEmmanuel Vadot phandle_t node; 28797eb836fSEmmanuel Vadot uint32_t calib[2]; 288343044c4SJared McNeill int error; 289343044c4SJared McNeill 29097eb836fSEmmanuel Vadot node = ofw_bus_get_node(sc->dev); 2910a30b4b2SJared McNeill if (sc->conf->calib0_mask != 0 || sc->conf->calib1_mask != 0) { 29297eb836fSEmmanuel Vadot error = nvmem_read_cell_by_name(node, "ths_calibration", 29397eb836fSEmmanuel Vadot (void *)calib, sizeof(calib)); 29497eb836fSEmmanuel Vadot /* Read calibration settings from EFUSE */ 29597eb836fSEmmanuel Vadot if (error != 0) { 29697eb836fSEmmanuel Vadot device_printf(sc->dev, "Cannot read THS efuse\n"); 297343044c4SJared McNeill return (error); 29897eb836fSEmmanuel Vadot } 299343044c4SJared McNeill 30097eb836fSEmmanuel Vadot device_printf(sc->dev, "calib0: %x\n", calib[0]); 30197eb836fSEmmanuel Vadot device_printf(sc->dev, "calib1: %x\n", calib[1]); 30297eb836fSEmmanuel Vadot 30397eb836fSEmmanuel Vadot calib[0] &= sc->conf->calib0_mask; 30497eb836fSEmmanuel Vadot calib[1] &= sc->conf->calib1_mask; 3053c2b90f1SJared McNeill 306343044c4SJared McNeill /* Write calibration settings to thermal controller */ 30797eb836fSEmmanuel Vadot if (calib[0] != 0) 30897eb836fSEmmanuel Vadot WR4(sc, THS_CALIB0, calib[0]); 30997eb836fSEmmanuel Vadot if (calib[1] != 0) 31097eb836fSEmmanuel Vadot WR4(sc, THS_CALIB1, calib[1]); 3114e7f43baSJared McNeill } 312343044c4SJared McNeill 313343044c4SJared McNeill /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */ 314d69d5ab0SJared McNeill WR4(sc, THS_CTRL1, ADC_CALI_EN); 315d69d5ab0SJared McNeill WR4(sc, THS_CTRL0, sc->conf->adc_acquire_time); 316d69d5ab0SJared McNeill WR4(sc, THS_CTRL2, sc->conf->adc_acquire_time << SENSOR_ACQ1_SHIFT); 317343044c4SJared McNeill 318343044c4SJared McNeill /* Enable average filter */ 319d69d5ab0SJared McNeill WR4(sc, THS_FILTER, sc->conf->filter); 320d69d5ab0SJared McNeill 321d69d5ab0SJared McNeill /* Enable interrupts */ 322d69d5ab0SJared McNeill WR4(sc, THS_INTS, RD4(sc, THS_INTS)); 3233c2b90f1SJared McNeill WR4(sc, THS_INTC, sc->conf->intc | SHUT_INT_ALL | ALARM_INT_ALL); 324d69d5ab0SJared McNeill 325d69d5ab0SJared McNeill /* Enable sensors */ 326d69d5ab0SJared McNeill WR4(sc, THS_CTRL2, RD4(sc, THS_CTRL2) | SENSOR_ENABLE_ALL); 327343044c4SJared McNeill 328343044c4SJared McNeill return (0); 329343044c4SJared McNeill } 330343044c4SJared McNeill 331343044c4SJared McNeill static int 332d69d5ab0SJared McNeill aw_thermal_gettemp(struct aw_thermal_softc *sc, int sensor) 333d69d5ab0SJared McNeill { 334d69d5ab0SJared McNeill uint32_t val; 335d69d5ab0SJared McNeill 336d69d5ab0SJared McNeill val = RD4(sc, THS_DATA0 + (sensor * 4)); 337d69d5ab0SJared McNeill 3380a30b4b2SJared McNeill return (sc->conf->to_temp(val)); 3393c2b90f1SJared McNeill } 3403c2b90f1SJared McNeill 3413c2b90f1SJared McNeill static int 3423c2b90f1SJared McNeill aw_thermal_getshut(struct aw_thermal_softc *sc, int sensor) 3433c2b90f1SJared McNeill { 3443c2b90f1SJared McNeill uint32_t val; 3453c2b90f1SJared McNeill 3463c2b90f1SJared McNeill val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4)); 3473c2b90f1SJared McNeill val = (val >> SHUT_T_HOT_SHIFT) & SHUT_T_HOT_MASK; 3483c2b90f1SJared McNeill 3490a30b4b2SJared McNeill return (sc->conf->to_temp(val)); 3500a30b4b2SJared McNeill } 3510a30b4b2SJared McNeill 3520a30b4b2SJared McNeill static void 3530a30b4b2SJared McNeill aw_thermal_setshut(struct aw_thermal_softc *sc, int sensor, int temp) 3540a30b4b2SJared McNeill { 3550a30b4b2SJared McNeill uint32_t val; 3560a30b4b2SJared McNeill 3570a30b4b2SJared McNeill val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4)); 3580a30b4b2SJared McNeill val &= ~(SHUT_T_HOT_MASK << SHUT_T_HOT_SHIFT); 3590a30b4b2SJared McNeill val |= (sc->conf->to_reg(temp) << SHUT_T_HOT_SHIFT); 3600a30b4b2SJared McNeill WR4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4), val); 3613c2b90f1SJared McNeill } 3623c2b90f1SJared McNeill 3633c2b90f1SJared McNeill static int 3643c2b90f1SJared McNeill aw_thermal_gethyst(struct aw_thermal_softc *sc, int sensor) 3653c2b90f1SJared McNeill { 3663c2b90f1SJared McNeill uint32_t val; 3673c2b90f1SJared McNeill 3683c2b90f1SJared McNeill val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4)); 3693c2b90f1SJared McNeill val = (val >> ALARM_T_HYST_SHIFT) & ALARM_T_HYST_MASK; 3703c2b90f1SJared McNeill 3710a30b4b2SJared McNeill return (sc->conf->to_temp(val)); 3723c2b90f1SJared McNeill } 3733c2b90f1SJared McNeill 3743c2b90f1SJared McNeill static int 3753c2b90f1SJared McNeill aw_thermal_getalarm(struct aw_thermal_softc *sc, int sensor) 3763c2b90f1SJared McNeill { 3773c2b90f1SJared McNeill uint32_t val; 3783c2b90f1SJared McNeill 3793c2b90f1SJared McNeill val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4)); 3803c2b90f1SJared McNeill val = (val >> ALARM_T_HOT_SHIFT) & ALARM_T_HOT_MASK; 3813c2b90f1SJared McNeill 3820a30b4b2SJared McNeill return (sc->conf->to_temp(val)); 3830a30b4b2SJared McNeill } 3840a30b4b2SJared McNeill 3850a30b4b2SJared McNeill static void 3860a30b4b2SJared McNeill aw_thermal_setalarm(struct aw_thermal_softc *sc, int sensor, int temp) 3870a30b4b2SJared McNeill { 3880a30b4b2SJared McNeill uint32_t val; 3890a30b4b2SJared McNeill 3900a30b4b2SJared McNeill val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4)); 3910a30b4b2SJared McNeill val &= ~(ALARM_T_HOT_MASK << ALARM_T_HOT_SHIFT); 3920a30b4b2SJared McNeill val |= (sc->conf->to_reg(temp) << ALARM_T_HOT_SHIFT); 3930a30b4b2SJared McNeill WR4(sc, THS_ALARM0_CTRL + (sensor * 4), val); 394343044c4SJared McNeill } 395343044c4SJared McNeill 396343044c4SJared McNeill static int 397343044c4SJared McNeill aw_thermal_sysctl(SYSCTL_HANDLER_ARGS) 398343044c4SJared McNeill { 399343044c4SJared McNeill struct aw_thermal_softc *sc; 400d69d5ab0SJared McNeill int sensor, val; 401343044c4SJared McNeill 402343044c4SJared McNeill sc = arg1; 403343044c4SJared McNeill sensor = arg2; 404343044c4SJared McNeill 4050a30b4b2SJared McNeill val = aw_thermal_gettemp(sc, sensor) + TEMP_C_TO_K; 406343044c4SJared McNeill 407343044c4SJared McNeill return sysctl_handle_opaque(oidp, &val, sizeof(val), req); 408343044c4SJared McNeill } 409343044c4SJared McNeill 410d69d5ab0SJared McNeill static void 4113c2b90f1SJared McNeill aw_thermal_throttle(struct aw_thermal_softc *sc, int enable) 4123c2b90f1SJared McNeill { 4133c2b90f1SJared McNeill device_t cf_dev; 4143c2b90f1SJared McNeill int count, error; 4153c2b90f1SJared McNeill 4163c2b90f1SJared McNeill if (enable == sc->throttle) 4173c2b90f1SJared McNeill return; 4183c2b90f1SJared McNeill 4193c2b90f1SJared McNeill if (enable != 0) { 4203c2b90f1SJared McNeill /* Set the lowest available frequency */ 4213c2b90f1SJared McNeill cf_dev = devclass_get_device(devclass_find("cpufreq"), 0); 4223c2b90f1SJared McNeill if (cf_dev == NULL) 4233c2b90f1SJared McNeill return; 4243c2b90f1SJared McNeill count = MAX_CF_LEVELS; 4253c2b90f1SJared McNeill error = CPUFREQ_LEVELS(cf_dev, sc->levels, &count); 4263c2b90f1SJared McNeill if (error != 0 || count == 0) 4273c2b90f1SJared McNeill return; 4283c2b90f1SJared McNeill sc->min_freq = sc->levels[count - 1].total_set.freq; 4293c2b90f1SJared McNeill error = CPUFREQ_SET(cf_dev, &sc->levels[count - 1], 4303c2b90f1SJared McNeill CPUFREQ_PRIO_USER); 4313c2b90f1SJared McNeill if (error != 0) 4323c2b90f1SJared McNeill return; 4333c2b90f1SJared McNeill } 4343c2b90f1SJared McNeill 4353c2b90f1SJared McNeill sc->throttle = enable; 4363c2b90f1SJared McNeill } 4373c2b90f1SJared McNeill 4383c2b90f1SJared McNeill static void 4392e4f9347SJared McNeill aw_thermal_cf_task(void *arg, int pending) 4402e4f9347SJared McNeill { 4412e4f9347SJared McNeill struct aw_thermal_softc *sc; 4422e4f9347SJared McNeill 4432e4f9347SJared McNeill sc = arg; 4442e4f9347SJared McNeill 4452e4f9347SJared McNeill aw_thermal_throttle(sc, 1); 4462e4f9347SJared McNeill } 4472e4f9347SJared McNeill 4482e4f9347SJared McNeill static void 4493c2b90f1SJared McNeill aw_thermal_cf_pre_change(void *arg, const struct cf_level *level, int *status) 4503c2b90f1SJared McNeill { 4513c2b90f1SJared McNeill struct aw_thermal_softc *sc; 4523c2b90f1SJared McNeill int temp_cur, temp_alarm; 4533c2b90f1SJared McNeill 4543c2b90f1SJared McNeill sc = arg; 4553c2b90f1SJared McNeill 4563c2b90f1SJared McNeill if (aw_thermal_throttle_enable == 0 || sc->throttle == 0 || 4573c2b90f1SJared McNeill level->total_set.freq == sc->min_freq) 4583c2b90f1SJared McNeill return; 4593c2b90f1SJared McNeill 4603c2b90f1SJared McNeill temp_cur = aw_thermal_gettemp(sc, 0); 4613c2b90f1SJared McNeill temp_alarm = aw_thermal_getalarm(sc, 0); 4623c2b90f1SJared McNeill 4633c2b90f1SJared McNeill if (temp_cur < temp_alarm) 4643c2b90f1SJared McNeill aw_thermal_throttle(sc, 0); 4653c2b90f1SJared McNeill else 4663c2b90f1SJared McNeill *status = ENXIO; 4673c2b90f1SJared McNeill } 4683c2b90f1SJared McNeill 4693c2b90f1SJared McNeill static void 470d69d5ab0SJared McNeill aw_thermal_intr(void *arg) 471d69d5ab0SJared McNeill { 472d69d5ab0SJared McNeill struct aw_thermal_softc *sc; 473d69d5ab0SJared McNeill device_t dev; 474d69d5ab0SJared McNeill uint32_t ints; 475d69d5ab0SJared McNeill 476d69d5ab0SJared McNeill dev = arg; 477d69d5ab0SJared McNeill sc = device_get_softc(dev); 478d69d5ab0SJared McNeill 479d69d5ab0SJared McNeill ints = RD4(sc, THS_INTS); 480d69d5ab0SJared McNeill WR4(sc, THS_INTS, ints); 481d69d5ab0SJared McNeill 482d69d5ab0SJared McNeill if ((ints & SHUT_INT_ALL) != 0) { 483d69d5ab0SJared McNeill device_printf(dev, 484d69d5ab0SJared McNeill "WARNING - current temperature exceeds safe limits\n"); 485d69d5ab0SJared McNeill shutdown_nice(RB_POWEROFF); 486d69d5ab0SJared McNeill } 4873c2b90f1SJared McNeill 4883c2b90f1SJared McNeill if ((ints & ALARM_INT_ALL) != 0) 4892e4f9347SJared McNeill taskqueue_enqueue(taskqueue_thread, &sc->cf_task); 490d69d5ab0SJared McNeill } 491d69d5ab0SJared McNeill 492343044c4SJared McNeill static int 493343044c4SJared McNeill aw_thermal_probe(device_t dev) 494343044c4SJared McNeill { 495343044c4SJared McNeill if (!ofw_bus_status_okay(dev)) 496343044c4SJared McNeill return (ENXIO); 497343044c4SJared McNeill 498343044c4SJared McNeill if (THS_CONF(dev) == NULL) 499343044c4SJared McNeill return (ENXIO); 500343044c4SJared McNeill 501343044c4SJared McNeill device_set_desc(dev, "Allwinner Thermal Sensor Controller"); 502343044c4SJared McNeill return (BUS_PROBE_DEFAULT); 503343044c4SJared McNeill } 504343044c4SJared McNeill 505343044c4SJared McNeill static int 506343044c4SJared McNeill aw_thermal_attach(device_t dev) 507343044c4SJared McNeill { 508343044c4SJared McNeill struct aw_thermal_softc *sc; 509d69d5ab0SJared McNeill clk_t clk_ahb, clk_ths; 510d69d5ab0SJared McNeill hwreset_t rst; 511d69d5ab0SJared McNeill int i, error; 512d69d5ab0SJared McNeill void *ih; 513343044c4SJared McNeill 514343044c4SJared McNeill sc = device_get_softc(dev); 515d69d5ab0SJared McNeill clk_ahb = clk_ths = NULL; 516d69d5ab0SJared McNeill rst = NULL; 517d69d5ab0SJared McNeill ih = NULL; 518343044c4SJared McNeill 519343044c4SJared McNeill sc->conf = THS_CONF(dev); 5202e4f9347SJared McNeill TASK_INIT(&sc->cf_task, 0, aw_thermal_cf_task, sc); 521343044c4SJared McNeill 522d69d5ab0SJared McNeill if (bus_alloc_resources(dev, aw_thermal_spec, sc->res) != 0) { 523343044c4SJared McNeill device_printf(dev, "cannot allocate resources for device\n"); 524343044c4SJared McNeill return (ENXIO); 525343044c4SJared McNeill } 526343044c4SJared McNeill 527d69d5ab0SJared McNeill if (clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb) == 0) { 528d69d5ab0SJared McNeill error = clk_enable(clk_ahb); 529d69d5ab0SJared McNeill if (error != 0) { 530d69d5ab0SJared McNeill device_printf(dev, "cannot enable ahb clock\n"); 531d69d5ab0SJared McNeill goto fail; 532d69d5ab0SJared McNeill } 533d69d5ab0SJared McNeill } 534d69d5ab0SJared McNeill if (clk_get_by_ofw_name(dev, 0, "ths", &clk_ths) == 0) { 535d69d5ab0SJared McNeill error = clk_set_freq(clk_ths, sc->conf->clk_rate, 0); 536d69d5ab0SJared McNeill if (error != 0) { 537d69d5ab0SJared McNeill device_printf(dev, "cannot set ths clock rate\n"); 538d69d5ab0SJared McNeill goto fail; 539d69d5ab0SJared McNeill } 540d69d5ab0SJared McNeill error = clk_enable(clk_ths); 541d69d5ab0SJared McNeill if (error != 0) { 542d69d5ab0SJared McNeill device_printf(dev, "cannot enable ths clock\n"); 543d69d5ab0SJared McNeill goto fail; 544d69d5ab0SJared McNeill } 545d69d5ab0SJared McNeill } 546d69d5ab0SJared McNeill if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { 547d69d5ab0SJared McNeill error = hwreset_deassert(rst); 548d69d5ab0SJared McNeill if (error != 0) { 549d69d5ab0SJared McNeill device_printf(dev, "cannot de-assert reset\n"); 550d69d5ab0SJared McNeill goto fail; 551d69d5ab0SJared McNeill } 552d69d5ab0SJared McNeill } 553343044c4SJared McNeill 554d69d5ab0SJared McNeill error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, 555d69d5ab0SJared McNeill NULL, aw_thermal_intr, dev, &ih); 556d69d5ab0SJared McNeill if (error != 0) { 557d69d5ab0SJared McNeill device_printf(dev, "cannot setup interrupt handler\n"); 558d69d5ab0SJared McNeill goto fail; 559d69d5ab0SJared McNeill } 560d69d5ab0SJared McNeill 5610a30b4b2SJared McNeill for (i = 0; i < sc->conf->nsensors; i++) { 5620a30b4b2SJared McNeill if (sc->conf->sensors[i].init_alarm > 0) 5630a30b4b2SJared McNeill aw_thermal_setalarm(sc, i, 5640a30b4b2SJared McNeill sc->conf->sensors[i].init_alarm); 5650a30b4b2SJared McNeill if (sc->conf->sensors[i].init_shut > 0) 5660a30b4b2SJared McNeill aw_thermal_setshut(sc, i, 5670a30b4b2SJared McNeill sc->conf->sensors[i].init_shut); 5680a30b4b2SJared McNeill } 5690a30b4b2SJared McNeill 570d69d5ab0SJared McNeill if (aw_thermal_init(sc) != 0) 571d69d5ab0SJared McNeill goto fail; 572d69d5ab0SJared McNeill 573d69d5ab0SJared McNeill for (i = 0; i < sc->conf->nsensors; i++) 574343044c4SJared McNeill SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 575343044c4SJared McNeill SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 576d69d5ab0SJared McNeill OID_AUTO, sc->conf->sensors[i].name, 577343044c4SJared McNeill CTLTYPE_INT | CTLFLAG_RD, 578d69d5ab0SJared McNeill sc, i, aw_thermal_sysctl, "IK0", 579d69d5ab0SJared McNeill sc->conf->sensors[i].desc); 580343044c4SJared McNeill 5813c2b90f1SJared McNeill if (bootverbose) 5823c2b90f1SJared McNeill for (i = 0; i < sc->conf->nsensors; i++) { 5833c2b90f1SJared McNeill device_printf(dev, 5843c2b90f1SJared McNeill "#%d: alarm %dC hyst %dC shut %dC\n", i, 5850a30b4b2SJared McNeill aw_thermal_getalarm(sc, i), 5860a30b4b2SJared McNeill aw_thermal_gethyst(sc, i), 5870a30b4b2SJared McNeill aw_thermal_getshut(sc, i)); 5883c2b90f1SJared McNeill } 5893c2b90f1SJared McNeill 5903c2b90f1SJared McNeill sc->cf_pre_tag = EVENTHANDLER_REGISTER(cpufreq_pre_change, 5913c2b90f1SJared McNeill aw_thermal_cf_pre_change, sc, EVENTHANDLER_PRI_FIRST); 5923c2b90f1SJared McNeill 593343044c4SJared McNeill return (0); 594d69d5ab0SJared McNeill 595d69d5ab0SJared McNeill fail: 596d69d5ab0SJared McNeill if (ih != NULL) 597d69d5ab0SJared McNeill bus_teardown_intr(dev, sc->res[1], ih); 598d69d5ab0SJared McNeill if (rst != NULL) 599d69d5ab0SJared McNeill hwreset_release(rst); 600d69d5ab0SJared McNeill if (clk_ahb != NULL) 601d69d5ab0SJared McNeill clk_release(clk_ahb); 602d69d5ab0SJared McNeill if (clk_ths != NULL) 603d69d5ab0SJared McNeill clk_release(clk_ths); 604d69d5ab0SJared McNeill bus_release_resources(dev, aw_thermal_spec, sc->res); 605d69d5ab0SJared McNeill 606d69d5ab0SJared McNeill return (ENXIO); 607343044c4SJared McNeill } 608343044c4SJared McNeill 609343044c4SJared McNeill static device_method_t aw_thermal_methods[] = { 610343044c4SJared McNeill /* Device interface */ 611343044c4SJared McNeill DEVMETHOD(device_probe, aw_thermal_probe), 612343044c4SJared McNeill DEVMETHOD(device_attach, aw_thermal_attach), 613343044c4SJared McNeill 614343044c4SJared McNeill DEVMETHOD_END 615343044c4SJared McNeill }; 616343044c4SJared McNeill 617343044c4SJared McNeill static driver_t aw_thermal_driver = { 618343044c4SJared McNeill "aw_thermal", 619343044c4SJared McNeill aw_thermal_methods, 620343044c4SJared McNeill sizeof(struct aw_thermal_softc), 621343044c4SJared McNeill }; 622343044c4SJared McNeill 623343044c4SJared McNeill static devclass_t aw_thermal_devclass; 624343044c4SJared McNeill 625343044c4SJared McNeill DRIVER_MODULE(aw_thermal, simplebus, aw_thermal_driver, aw_thermal_devclass, 626343044c4SJared McNeill 0, 0); 627343044c4SJared McNeill MODULE_VERSION(aw_thermal, 1); 628