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> 42343044c4SJared McNeill #include <sys/module.h> 43343044c4SJared McNeill #include <machine/bus.h> 44343044c4SJared McNeill 45343044c4SJared McNeill #include <dev/ofw/ofw_bus.h> 46343044c4SJared McNeill #include <dev/ofw/ofw_bus_subr.h> 47343044c4SJared McNeill 48343044c4SJared McNeill #include <arm/allwinner/aw_sid.h> 49343044c4SJared McNeill 50343044c4SJared McNeill #define THS_CTRL0 0x00 51343044c4SJared McNeill #define THS_CTRL2 0x40 52343044c4SJared McNeill #define SENSOR_ACQ1_SHIFT 16 53343044c4SJared McNeill #define SENSOR2_EN (1 << 2) 54343044c4SJared McNeill #define SENSOR1_EN (1 << 1) 55343044c4SJared McNeill #define SENSOR0_EN (1 << 0) 56343044c4SJared McNeill #define THS_INTC 0x44 57343044c4SJared McNeill #define THS_INTS 0x48 58343044c4SJared McNeill #define THS_FILTER 0x70 59343044c4SJared McNeill #define FILTER_EN (1 << 2) 60343044c4SJared McNeill #define THS_CALIB0 0x74 61343044c4SJared McNeill #define THS_CALIB1 0x78 62343044c4SJared McNeill #define THS_DATA0 0x80 63343044c4SJared McNeill #define THS_DATA1 0x84 64343044c4SJared McNeill #define THS_DATA2 0x88 65343044c4SJared McNeill #define DATA_MASK 0xfff 66343044c4SJared McNeill 67343044c4SJared McNeill #define TEMP_BASE 2719 68343044c4SJared McNeill #define TEMP_MUL 1000 69343044c4SJared McNeill #define TEMP_DIV 14186 70343044c4SJared McNeill #define TEMP_TO_K 273 71343044c4SJared McNeill #define ADC_ACQUIRE_TIME (24 - 1) 72343044c4SJared McNeill #define SENSOR_ENABLE_ALL (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN) 73343044c4SJared McNeill 74343044c4SJared McNeill enum aw_thermal_sensor { 75343044c4SJared McNeill THS_SENSOR_CPU_CLUSTER0, 76343044c4SJared McNeill THS_SENSOR_CPU_CLUSTER1, 77343044c4SJared McNeill THS_SENSOR_GPU, 78343044c4SJared McNeill THS_SENSOR_END = -1 79343044c4SJared McNeill }; 80343044c4SJared McNeill 81343044c4SJared McNeill struct aw_thermal_sensor_config { 82343044c4SJared McNeill enum aw_thermal_sensor sensor; 83343044c4SJared McNeill const char *name; 84343044c4SJared McNeill const char *desc; 85343044c4SJared McNeill }; 86343044c4SJared McNeill 87343044c4SJared McNeill static const struct aw_thermal_sensor_config a83t_sensor_config[] = { 88343044c4SJared McNeill { .sensor = THS_SENSOR_CPU_CLUSTER0, 89343044c4SJared McNeill .name = "cluster0", .desc = "CPU cluster 0 temperature" }, 90343044c4SJared McNeill { .sensor = THS_SENSOR_CPU_CLUSTER1, 91343044c4SJared McNeill .name = "cluster1", .desc = "CPU cluster 1 temperature" }, 92343044c4SJared McNeill { .sensor = THS_SENSOR_GPU, 93343044c4SJared McNeill .name = "gpu", .desc = "GPU temperature" }, 94343044c4SJared McNeill { .sensor = THS_SENSOR_END } 95343044c4SJared McNeill }; 96343044c4SJared McNeill 97343044c4SJared McNeill static struct ofw_compat_data compat_data[] = { 98343044c4SJared McNeill { "allwinner,sun8i-a83t-ts", (uintptr_t)&a83t_sensor_config }, 99343044c4SJared McNeill { NULL, (uintptr_t)NULL } 100343044c4SJared McNeill }; 101343044c4SJared McNeill 102343044c4SJared McNeill #define THS_CONF(d) \ 103343044c4SJared McNeill (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data 104343044c4SJared McNeill 105343044c4SJared McNeill struct aw_thermal_softc { 106343044c4SJared McNeill struct resource *res; 107343044c4SJared McNeill struct aw_thermal_sensor_config *conf; 108343044c4SJared McNeill }; 109343044c4SJared McNeill 110343044c4SJared McNeill static struct resource_spec aw_thermal_spec[] = { 111343044c4SJared McNeill { SYS_RES_MEMORY, 0, RF_ACTIVE }, 112343044c4SJared McNeill { -1, 0 } 113343044c4SJared McNeill }; 114343044c4SJared McNeill 115343044c4SJared McNeill #define RD4(sc, reg) bus_read_4((sc)->res, (reg)) 116343044c4SJared McNeill #define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) 117343044c4SJared McNeill 118343044c4SJared McNeill static int 119343044c4SJared McNeill aw_thermal_init(struct aw_thermal_softc *sc) 120343044c4SJared McNeill { 121343044c4SJared McNeill uint32_t calib0, calib1; 122343044c4SJared McNeill int error; 123343044c4SJared McNeill 124343044c4SJared McNeill /* Read calibration settings from SRAM */ 125343044c4SJared McNeill error = aw_sid_read_tscalib(&calib0, &calib1); 126343044c4SJared McNeill if (error != 0) 127343044c4SJared McNeill return (error); 128343044c4SJared McNeill 129343044c4SJared McNeill /* Write calibration settings to thermal controller */ 130343044c4SJared McNeill WR4(sc, THS_CALIB0, calib0); 131343044c4SJared McNeill WR4(sc, THS_CALIB1, calib1); 132343044c4SJared McNeill 133343044c4SJared McNeill /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */ 134343044c4SJared McNeill WR4(sc, THS_CTRL0, ADC_ACQUIRE_TIME); 135343044c4SJared McNeill WR4(sc, THS_CTRL2, (ADC_ACQUIRE_TIME << SENSOR_ACQ1_SHIFT) | 136343044c4SJared McNeill SENSOR_ENABLE_ALL); 137343044c4SJared McNeill 138343044c4SJared McNeill /* Disable interrupts */ 139343044c4SJared McNeill WR4(sc, THS_INTC, 0); 140343044c4SJared McNeill WR4(sc, THS_INTS, RD4(sc, THS_INTS)); 141343044c4SJared McNeill 142343044c4SJared McNeill /* Enable average filter */ 143343044c4SJared McNeill WR4(sc, THS_FILTER, RD4(sc, THS_FILTER) | FILTER_EN); 144343044c4SJared McNeill 145343044c4SJared McNeill return (0); 146343044c4SJared McNeill } 147343044c4SJared McNeill 148343044c4SJared McNeill static int 149343044c4SJared McNeill aw_thermal_gettemp(uint32_t val) 150343044c4SJared McNeill { 151343044c4SJared McNeill int raw; 152343044c4SJared McNeill 153343044c4SJared McNeill raw = val & DATA_MASK; 154343044c4SJared McNeill return (((TEMP_BASE - raw) * TEMP_MUL) / TEMP_DIV) + TEMP_TO_K; 155343044c4SJared McNeill } 156343044c4SJared McNeill 157343044c4SJared McNeill static int 158343044c4SJared McNeill aw_thermal_sysctl(SYSCTL_HANDLER_ARGS) 159343044c4SJared McNeill { 160343044c4SJared McNeill struct aw_thermal_softc *sc; 161343044c4SJared McNeill enum aw_thermal_sensor sensor; 162343044c4SJared McNeill int val; 163343044c4SJared McNeill 164343044c4SJared McNeill sc = arg1; 165343044c4SJared McNeill sensor = arg2; 166343044c4SJared McNeill 167343044c4SJared McNeill val = aw_thermal_gettemp(RD4(sc, THS_DATA0 + (sensor * 4))); 168343044c4SJared McNeill 169343044c4SJared McNeill return sysctl_handle_opaque(oidp, &val, sizeof(val), req); 170343044c4SJared McNeill } 171343044c4SJared McNeill 172343044c4SJared McNeill static int 173343044c4SJared McNeill aw_thermal_probe(device_t dev) 174343044c4SJared McNeill { 175343044c4SJared McNeill if (!ofw_bus_status_okay(dev)) 176343044c4SJared McNeill return (ENXIO); 177343044c4SJared McNeill 178343044c4SJared McNeill if (THS_CONF(dev) == NULL) 179343044c4SJared McNeill return (ENXIO); 180343044c4SJared McNeill 181343044c4SJared McNeill device_set_desc(dev, "Allwinner Thermal Sensor Controller"); 182343044c4SJared McNeill return (BUS_PROBE_DEFAULT); 183343044c4SJared McNeill } 184343044c4SJared McNeill 185343044c4SJared McNeill static int 186343044c4SJared McNeill aw_thermal_attach(device_t dev) 187343044c4SJared McNeill { 188343044c4SJared McNeill struct aw_thermal_softc *sc; 189343044c4SJared McNeill int i; 190343044c4SJared McNeill 191343044c4SJared McNeill sc = device_get_softc(dev); 192343044c4SJared McNeill 193343044c4SJared McNeill sc->conf = THS_CONF(dev); 194343044c4SJared McNeill 195343044c4SJared McNeill if (bus_alloc_resources(dev, aw_thermal_spec, &sc->res) != 0) { 196343044c4SJared McNeill device_printf(dev, "cannot allocate resources for device\n"); 197343044c4SJared McNeill return (ENXIO); 198343044c4SJared McNeill } 199343044c4SJared McNeill 200343044c4SJared McNeill if (aw_thermal_init(sc) != 0) 201343044c4SJared McNeill return (ENXIO); 202343044c4SJared McNeill 203343044c4SJared McNeill for (i = 0; sc->conf[i].sensor != THS_SENSOR_END; i++) 204343044c4SJared McNeill SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 205343044c4SJared McNeill SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 206343044c4SJared McNeill OID_AUTO, sc->conf[i].name, 207343044c4SJared McNeill CTLTYPE_INT | CTLFLAG_RD, 208343044c4SJared McNeill sc, sc->conf[i].sensor, aw_thermal_sysctl, "IK0", 209343044c4SJared McNeill sc->conf[i].desc); 210343044c4SJared McNeill 211343044c4SJared McNeill return (0); 212343044c4SJared McNeill } 213343044c4SJared McNeill 214343044c4SJared McNeill static device_method_t aw_thermal_methods[] = { 215343044c4SJared McNeill /* Device interface */ 216343044c4SJared McNeill DEVMETHOD(device_probe, aw_thermal_probe), 217343044c4SJared McNeill DEVMETHOD(device_attach, aw_thermal_attach), 218343044c4SJared McNeill 219343044c4SJared McNeill DEVMETHOD_END 220343044c4SJared McNeill }; 221343044c4SJared McNeill 222343044c4SJared McNeill static driver_t aw_thermal_driver = { 223343044c4SJared McNeill "aw_thermal", 224343044c4SJared McNeill aw_thermal_methods, 225343044c4SJared McNeill sizeof(struct aw_thermal_softc), 226343044c4SJared McNeill }; 227343044c4SJared McNeill 228343044c4SJared McNeill static devclass_t aw_thermal_devclass; 229343044c4SJared McNeill 230343044c4SJared McNeill DRIVER_MODULE(aw_thermal, simplebus, aw_thermal_driver, aw_thermal_devclass, 231343044c4SJared McNeill 0, 0); 232343044c4SJared McNeill MODULE_VERSION(aw_thermal, 1); 233