diff options
Diffstat (limited to 'sys/arm/allwinner/aw_thermal.c')
-rw-r--r-- | sys/arm/allwinner/aw_thermal.c | 248 |
1 files changed, 200 insertions, 48 deletions
diff --git a/sys/arm/allwinner/aw_thermal.c b/sys/arm/allwinner/aw_thermal.c index 665102f72021..ae61aa28bf47 100644 --- a/sys/arm/allwinner/aw_thermal.c +++ b/sys/arm/allwinner/aw_thermal.c @@ -39,15 +39,21 @@ __FBSDID("$FreeBSD$"); #include <sys/rman.h> #include <sys/kernel.h> #include <sys/sysctl.h> +#include <sys/reboot.h> #include <sys/module.h> #include <machine/bus.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#include <dev/extres/clk/clk.h> +#include <dev/extres/hwreset/hwreset.h> + #include <arm/allwinner/aw_sid.h> #define THS_CTRL0 0x00 +#define THS_CTRL1 0x04 +#define ADC_CALI_EN (1 << 17) #define THS_CTRL2 0x40 #define SENSOR_ACQ1_SHIFT 16 #define SENSOR2_EN (1 << 2) @@ -55,8 +61,16 @@ __FBSDID("$FreeBSD$"); #define SENSOR0_EN (1 << 0) #define THS_INTC 0x44 #define THS_INTS 0x48 +#define THS2_DATA_IRQ_STS (1 << 10) +#define THS1_DATA_IRQ_STS (1 << 9) +#define THS0_DATA_IRQ_STS (1 << 8) +#define SHUT_INT2_STS (1 << 6) +#define SHUT_INT1_STS (1 << 5) +#define SHUT_INT0_STS (1 << 4) +#define ALARM_INT2_STS (1 << 2) +#define ALARM_INT1_STS (1 << 1) +#define ALARM_INT0_STS (1 << 0) #define THS_FILTER 0x70 -#define FILTER_EN (1 << 2) #define THS_CALIB0 0x74 #define THS_CALIB1 0x78 #define THS_DATA0 0x80 @@ -64,38 +78,93 @@ __FBSDID("$FreeBSD$"); #define THS_DATA2 0x88 #define DATA_MASK 0xfff -#define TEMP_BASE 2719 -#define TEMP_MUL 1000 -#define TEMP_DIV 14186 -#define TEMP_TO_K 273 -#define ADC_ACQUIRE_TIME (24 - 1) +#define A83T_ADC_ACQUIRE_TIME 0x17 +#define A83T_FILTER 0x4 +#define A83T_INTC 0x1000 +#define A83T_TEMP_BASE 2719000 +#define A83T_TEMP_DIV 14186 +#define A83T_CLK_RATE 24000000 + +#define A64_ADC_ACQUIRE_TIME 0x190 +#define A64_FILTER 0x6 +#define A64_INTC 0x18000 +#define A64_TEMP_BASE 2170000 +#define A64_TEMP_DIV 8560 +#define A64_CLK_RATE 4000000 + +#define TEMP_C_TO_K 273 #define SENSOR_ENABLE_ALL (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN) +#define SHUT_INT_ALL (SHUT_INT0_STS|SHUT_INT1_STS|SHUT_INT2_STS) -enum aw_thermal_sensor { - THS_SENSOR_CPU_CLUSTER0, - THS_SENSOR_CPU_CLUSTER1, - THS_SENSOR_GPU, - THS_SENSOR_END = -1 -}; +#define MAX_SENSORS 3 -struct aw_thermal_sensor_config { - enum aw_thermal_sensor sensor; +struct aw_thermal_sensor { const char *name; const char *desc; }; -static const struct aw_thermal_sensor_config a83t_sensor_config[] = { - { .sensor = THS_SENSOR_CPU_CLUSTER0, - .name = "cluster0", .desc = "CPU cluster 0 temperature" }, - { .sensor = THS_SENSOR_CPU_CLUSTER1, - .name = "cluster1", .desc = "CPU cluster 1 temperature" }, - { .sensor = THS_SENSOR_GPU, - .name = "gpu", .desc = "GPU temperature" }, - { .sensor = THS_SENSOR_END } +struct aw_thermal_config { + struct aw_thermal_sensor sensors[MAX_SENSORS]; + int nsensors; + uint64_t clk_rate; + uint32_t adc_acquire_time; + uint32_t filter; + uint32_t intc; + uint32_t temp_base; + uint32_t temp_div; +}; + +static const struct aw_thermal_config a83t_config = { + .nsensors = 3, + .sensors = { + [0] = { + .name = "cluster0", + .desc = "CPU cluster 0 temperature", + }, + [1] = { + .name = "cluster1", + .desc = "CPU cluster 1 temperature", + }, + [2] = { + .name = "gpu", + .desc = "GPU temperature", + }, + }, + .clk_rate = A83T_CLK_RATE, + .adc_acquire_time = A83T_ADC_ACQUIRE_TIME, + .filter = A83T_FILTER, + .intc = A83T_INTC, + .temp_base = A83T_TEMP_BASE, + .temp_div = A83T_TEMP_DIV, +}; + +static const struct aw_thermal_config a64_config = { + .nsensors = 3, + .sensors = { + [0] = { + .name = "cpu", + .desc = "CPU temperature", + }, + [1] = { + .name = "gpu1", + .desc = "GPU temperature 1", + }, + [2] = { + .name = "gpu2", + .desc = "GPU temperature 2", + }, + }, + .clk_rate = A64_CLK_RATE, + .adc_acquire_time = A64_ADC_ACQUIRE_TIME, + .filter = A64_FILTER, + .intc = A64_INTC, + .temp_base = A64_TEMP_BASE, + .temp_div = A64_TEMP_DIV, }; static struct ofw_compat_data compat_data[] = { - { "allwinner,sun8i-a83t-ts", (uintptr_t)&a83t_sensor_config }, + { "allwinner,sun8i-a83t-ts", (uintptr_t)&a83t_config }, + { "allwinner,sun50i-a64-ts", (uintptr_t)&a64_config }, { NULL, (uintptr_t)NULL } }; @@ -103,17 +172,18 @@ static struct ofw_compat_data compat_data[] = { (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data struct aw_thermal_softc { - struct resource *res; - struct aw_thermal_sensor_config *conf; + struct resource *res[2]; + struct aw_thermal_config *conf; }; static struct resource_spec aw_thermal_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; -#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) -#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) +#define RD4(sc, reg) bus_read_4((sc)->res[0], (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) static int aw_thermal_init(struct aw_thermal_softc *sc) @@ -131,44 +201,73 @@ aw_thermal_init(struct aw_thermal_softc *sc) WR4(sc, THS_CALIB1, calib1); /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */ - WR4(sc, THS_CTRL0, ADC_ACQUIRE_TIME); - WR4(sc, THS_CTRL2, (ADC_ACQUIRE_TIME << SENSOR_ACQ1_SHIFT) | - SENSOR_ENABLE_ALL); + WR4(sc, THS_CTRL1, ADC_CALI_EN); + WR4(sc, THS_CTRL0, sc->conf->adc_acquire_time); + WR4(sc, THS_CTRL2, sc->conf->adc_acquire_time << SENSOR_ACQ1_SHIFT); + + /* Enable average filter */ + WR4(sc, THS_FILTER, sc->conf->filter); - /* Disable interrupts */ - WR4(sc, THS_INTC, 0); + /* Enable interrupts */ WR4(sc, THS_INTS, RD4(sc, THS_INTS)); + WR4(sc, THS_INTC, sc->conf->intc | SHUT_INT_ALL); - /* Enable average filter */ - WR4(sc, THS_FILTER, RD4(sc, THS_FILTER) | FILTER_EN); + /* Enable sensors */ + WR4(sc, THS_CTRL2, RD4(sc, THS_CTRL2) | SENSOR_ENABLE_ALL); return (0); } static int -aw_thermal_gettemp(uint32_t val) +aw_thermal_reg_to_temp(struct aw_thermal_softc *sc, uint32_t val) +{ + return ((sc->conf->temp_base - val * 1000) / sc->conf->temp_div); +} + +static int +aw_thermal_gettemp(struct aw_thermal_softc *sc, int sensor) { - int raw; + uint32_t val; + + val = RD4(sc, THS_DATA0 + (sensor * 4)); - raw = val & DATA_MASK; - return (((TEMP_BASE - raw) * TEMP_MUL) / TEMP_DIV) + TEMP_TO_K; + return (aw_thermal_reg_to_temp(sc, val) + TEMP_C_TO_K); } static int aw_thermal_sysctl(SYSCTL_HANDLER_ARGS) { struct aw_thermal_softc *sc; - enum aw_thermal_sensor sensor; - int val; + int sensor, val; sc = arg1; sensor = arg2; - val = aw_thermal_gettemp(RD4(sc, THS_DATA0 + (sensor * 4))); + val = aw_thermal_gettemp(sc, sensor); return sysctl_handle_opaque(oidp, &val, sizeof(val), req); } +static void +aw_thermal_intr(void *arg) +{ + struct aw_thermal_softc *sc; + device_t dev; + uint32_t ints; + + dev = arg; + sc = device_get_softc(dev); + + ints = RD4(sc, THS_INTS); + WR4(sc, THS_INTS, ints); + + if ((ints & SHUT_INT_ALL) != 0) { + device_printf(dev, + "WARNING - current temperature exceeds safe limits\n"); + shutdown_nice(RB_POWEROFF); + } +} + static int aw_thermal_probe(device_t dev) { @@ -186,29 +285,82 @@ static int aw_thermal_attach(device_t dev) { struct aw_thermal_softc *sc; - int i; + clk_t clk_ahb, clk_ths; + hwreset_t rst; + int i, error; + void *ih; sc = device_get_softc(dev); + clk_ahb = clk_ths = NULL; + rst = NULL; + ih = NULL; sc->conf = THS_CONF(dev); - if (bus_alloc_resources(dev, aw_thermal_spec, &sc->res) != 0) { + if (bus_alloc_resources(dev, aw_thermal_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); return (ENXIO); } + if (clk_get_by_ofw_name(dev, 0, "ahb", &clk_ahb) == 0) { + error = clk_enable(clk_ahb); + if (error != 0) { + device_printf(dev, "cannot enable ahb clock\n"); + goto fail; + } + } + if (clk_get_by_ofw_name(dev, 0, "ths", &clk_ths) == 0) { + error = clk_set_freq(clk_ths, sc->conf->clk_rate, 0); + if (error != 0) { + device_printf(dev, "cannot set ths clock rate\n"); + goto fail; + } + error = clk_enable(clk_ths); + if (error != 0) { + device_printf(dev, "cannot enable ths clock\n"); + goto fail; + } + } + if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) { + error = hwreset_deassert(rst); + if (error != 0) { + device_printf(dev, "cannot de-assert reset\n"); + goto fail; + } + } + + error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, + NULL, aw_thermal_intr, dev, &ih); + if (error != 0) { + device_printf(dev, "cannot setup interrupt handler\n"); + goto fail; + } + if (aw_thermal_init(sc) != 0) - return (ENXIO); + goto fail; - for (i = 0; sc->conf[i].sensor != THS_SENSOR_END; i++) + for (i = 0; i < sc->conf->nsensors; i++) SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), - OID_AUTO, sc->conf[i].name, + OID_AUTO, sc->conf->sensors[i].name, CTLTYPE_INT | CTLFLAG_RD, - sc, sc->conf[i].sensor, aw_thermal_sysctl, "IK0", - sc->conf[i].desc); + sc, i, aw_thermal_sysctl, "IK0", + sc->conf->sensors[i].desc); return (0); + +fail: + if (ih != NULL) + bus_teardown_intr(dev, sc->res[1], ih); + if (rst != NULL) + hwreset_release(rst); + if (clk_ahb != NULL) + clk_release(clk_ahb); + if (clk_ths != NULL) + clk_release(clk_ths); + bus_release_resources(dev, aw_thermal_spec, sc->res); + + return (ENXIO); } static device_method_t aw_thermal_methods[] = { |