1*dad5cb8dSjmcneill /* $NetBSD: sunxi_thermal.c,v 1.16 2023/05/02 23:08:58 jmcneill Exp $ */
205641e30Sjmcneill
305641e30Sjmcneill /*-
405641e30Sjmcneill * Copyright (c) 2016-2017 Jared McNeill <jmcneill@invisible.ca>
505641e30Sjmcneill * All rights reserved.
605641e30Sjmcneill *
705641e30Sjmcneill * Redistribution and use in source and binary forms, with or without
805641e30Sjmcneill * modification, are permitted provided that the following conditions
905641e30Sjmcneill * are met:
1005641e30Sjmcneill * 1. Redistributions of source code must retain the above copyright
1105641e30Sjmcneill * notice, this list of conditions and the following disclaimer.
1205641e30Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
1305641e30Sjmcneill * notice, this list of conditions and the following disclaimer in the
1405641e30Sjmcneill * documentation and/or other materials provided with the distribution.
1505641e30Sjmcneill *
1605641e30Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1705641e30Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1805641e30Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1905641e30Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2005641e30Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2105641e30Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2205641e30Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2305641e30Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2405641e30Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2505641e30Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2605641e30Sjmcneill * SUCH DAMAGE.
2705641e30Sjmcneill */
2805641e30Sjmcneill
2905641e30Sjmcneill /*
3005641e30Sjmcneill * Allwinner thermal sensor controller
3105641e30Sjmcneill */
3205641e30Sjmcneill
3305641e30Sjmcneill #include <sys/cdefs.h>
34*dad5cb8dSjmcneill __KERNEL_RCSID(0, "$NetBSD: sunxi_thermal.c,v 1.16 2023/05/02 23:08:58 jmcneill Exp $");
3505641e30Sjmcneill
3605641e30Sjmcneill #include <sys/param.h>
3705641e30Sjmcneill #include <sys/systm.h>
3805641e30Sjmcneill #include <sys/device.h>
3905641e30Sjmcneill #include <sys/kernel.h>
4005641e30Sjmcneill #include <sys/reboot.h>
4105641e30Sjmcneill
4205641e30Sjmcneill #include <dev/sysmon/sysmonvar.h>
4305641e30Sjmcneill #include <dev/sysmon/sysmon_taskq.h>
4405641e30Sjmcneill
4505641e30Sjmcneill #include <dev/fdt/fdtvar.h>
4605641e30Sjmcneill
4705641e30Sjmcneill #include <arm/sunxi/sunxi_sid.h>
4805641e30Sjmcneill
4905641e30Sjmcneill #define THS_CTRL0 0x00
5005641e30Sjmcneill #define THS_CTRL1 0x04
5105641e30Sjmcneill #define ADC_CALI_EN (1 << 17)
5205641e30Sjmcneill #define THS_CTRL2 0x40
5305641e30Sjmcneill #define SENSOR_ACQ1_SHIFT 16
5405641e30Sjmcneill #define SENSOR2_EN (1 << 2)
5505641e30Sjmcneill #define SENSOR1_EN (1 << 1)
5605641e30Sjmcneill #define SENSOR0_EN (1 << 0)
5705641e30Sjmcneill #define THS_INTC 0x44
5805641e30Sjmcneill #define THS_INTS 0x48
5905641e30Sjmcneill #define THS2_DATA_IRQ_STS (1 << 10)
6005641e30Sjmcneill #define THS1_DATA_IRQ_STS (1 << 9)
6105641e30Sjmcneill #define THS0_DATA_IRQ_STS (1 << 8)
6205641e30Sjmcneill #define SHUT_INT2_STS (1 << 6)
6305641e30Sjmcneill #define SHUT_INT1_STS (1 << 5)
6405641e30Sjmcneill #define SHUT_INT0_STS (1 << 4)
6505641e30Sjmcneill #define ALARM_INT2_STS (1 << 2)
6605641e30Sjmcneill #define ALARM_INT1_STS (1 << 1)
6705641e30Sjmcneill #define ALARM_INT0_STS (1 << 0)
6805641e30Sjmcneill #define THS_ALARM0_CTRL 0x50
6905641e30Sjmcneill #define ALARM_T_HOT_MASK 0xfff
7005641e30Sjmcneill #define ALARM_T_HOT_SHIFT 16
7105641e30Sjmcneill #define ALARM_T_HYST_MASK 0xfff
7205641e30Sjmcneill #define ALARM_T_HYST_SHIFT 0
7305641e30Sjmcneill #define THS_SHUTDOWN0_CTRL 0x60
7405641e30Sjmcneill #define SHUT_T_HOT_MASK 0xfff
7505641e30Sjmcneill #define SHUT_T_HOT_SHIFT 16
7605641e30Sjmcneill #define THS_FILTER 0x70
7705641e30Sjmcneill #define THS_CALIB0 0x74
7805641e30Sjmcneill #define THS_CALIB1 0x78
7905641e30Sjmcneill #define THS_DATA0 0x80
8005641e30Sjmcneill #define THS_DATA1 0x84
8105641e30Sjmcneill #define THS_DATA2 0x88
8205641e30Sjmcneill #define DATA_MASK 0xfff
8305641e30Sjmcneill
8405641e30Sjmcneill #define A83T_ADC_ACQUIRE_TIME 0x17
8505641e30Sjmcneill #define A83T_FILTER 0x4
8605641e30Sjmcneill #define A83T_INTC 0x1000
8705641e30Sjmcneill #define A83T_TEMP_BASE 2719000
8805641e30Sjmcneill #define A83T_TEMP_MUL 1000
8905641e30Sjmcneill #define A83T_TEMP_DIV 14186
9005641e30Sjmcneill #define A83T_CLK_RATE 24000000
9105641e30Sjmcneill
9205641e30Sjmcneill #define A64_ADC_ACQUIRE_TIME 0x190
9305641e30Sjmcneill #define A64_FILTER 0x6
9405641e30Sjmcneill #define A64_INTC 0x18000
9505641e30Sjmcneill #define A64_TEMP_BASE 2170000
9605641e30Sjmcneill #define A64_TEMP_MUL 1000
9705641e30Sjmcneill #define A64_TEMP_DIV 8560
9805641e30Sjmcneill #define A64_CLK_RATE 4000000
9905641e30Sjmcneill
10005641e30Sjmcneill #define H3_ADC_ACQUIRE_TIME 0x3f
10105641e30Sjmcneill #define H3_FILTER 0x6
10205641e30Sjmcneill #define H3_INTC 0x191000
10305641e30Sjmcneill #define H3_TEMP_BASE 217
10405641e30Sjmcneill #define H3_TEMP_MUL 1000
10505641e30Sjmcneill #define H3_TEMP_DIV 8253
10605641e30Sjmcneill #define H3_TEMP_MINUS 1794000
10705641e30Sjmcneill #define H3_CLK_RATE 4000000
10805641e30Sjmcneill #define H3_INIT_ALARM 90 /* degC */
10905641e30Sjmcneill #define H3_INIT_SHUT 105 /* degC */
11005641e30Sjmcneill
111fdc625b4Sjmcneill #define H5_ADC_ACQUIRE_TIME 0x1df
112fdc625b4Sjmcneill #define H5_FILTER 0x6
113fdc625b4Sjmcneill #define H5_INTC 0x3a070
114fdc625b4Sjmcneill #define H5_TEMP_DIV 20
115fdc625b4Sjmcneill #define H5_TEMP_BASE_L 233832448
116fdc625b4Sjmcneill #define H5_TEMP_MUL_L 124885
117fdc625b4Sjmcneill #define H5_TEMP_BASE_H_0 271581184
118fdc625b4Sjmcneill #define H5_TEMP_MUL_H_0 152253
119fdc625b4Sjmcneill #define H5_TEMP_BASE_H_1 289406976
120fdc625b4Sjmcneill #define H5_TEMP_MUL_H_1 166723
121fdc625b4Sjmcneill #define H5_CLK_RATE 24000000
122fdc625b4Sjmcneill #define H5_INIT_ALARM 105 /* degC */
123fdc625b4Sjmcneill #define H5_INIT_SHUT 120 /* degC */
124fdc625b4Sjmcneill
12505641e30Sjmcneill #define TEMP_C_TO_K 273150000
12605641e30Sjmcneill #define SENSOR_ENABLE_ALL (SENSOR0_EN|SENSOR1_EN|SENSOR2_EN)
12705641e30Sjmcneill #define SHUT_INT_ALL (SHUT_INT0_STS|SHUT_INT1_STS|SHUT_INT2_STS)
12805641e30Sjmcneill #define ALARM_INT_ALL (ALARM_INT0_STS)
12905641e30Sjmcneill
13005641e30Sjmcneill #define MAX_SENSORS 3
13105641e30Sjmcneill
13205641e30Sjmcneill #if notyet
13305641e30Sjmcneill #define THROTTLE_ENABLE_DEFAULT 1
13405641e30Sjmcneill
13505641e30Sjmcneill /* Enable thermal throttling */
13605641e30Sjmcneill static int sunxi_thermal_throttle_enable = THROTTLE_ENABLE_DEFAULT;
13705641e30Sjmcneill #endif
13805641e30Sjmcneill
13905641e30Sjmcneill struct sunxi_thermal_sensor {
14005641e30Sjmcneill const char *name;
14105641e30Sjmcneill const char *desc;
14205641e30Sjmcneill int init_alarm;
14305641e30Sjmcneill int init_shut;
14405641e30Sjmcneill };
14505641e30Sjmcneill
14605641e30Sjmcneill struct sunxi_thermal_config {
14705641e30Sjmcneill struct sunxi_thermal_sensor sensors[MAX_SENSORS];
14805641e30Sjmcneill int nsensors;
14905641e30Sjmcneill uint64_t clk_rate;
15005641e30Sjmcneill uint32_t adc_acquire_time;
15105641e30Sjmcneill int adc_cali_en;
15205641e30Sjmcneill uint32_t filter;
15305641e30Sjmcneill uint32_t intc;
154fdc625b4Sjmcneill int (*to_temp)(u_int, uint32_t);
155fdc625b4Sjmcneill uint32_t (*to_reg)(u_int, int);
15605641e30Sjmcneill int calib0, calib1;
15705641e30Sjmcneill uint32_t calib0_mask, calib1_mask;
15805641e30Sjmcneill };
15905641e30Sjmcneill
16005641e30Sjmcneill static int
a83t_to_temp(u_int sensor,uint32_t val)161fdc625b4Sjmcneill a83t_to_temp(u_int sensor, uint32_t val)
16205641e30Sjmcneill {
16305641e30Sjmcneill return ((A83T_TEMP_BASE - (val * A83T_TEMP_MUL)) / A83T_TEMP_DIV);
16405641e30Sjmcneill }
16505641e30Sjmcneill
16605641e30Sjmcneill static const struct sunxi_thermal_config a83t_config = {
16705641e30Sjmcneill .nsensors = 3,
16805641e30Sjmcneill .sensors = {
16905641e30Sjmcneill [0] = {
17005641e30Sjmcneill .name = "cluster0",
17105641e30Sjmcneill .desc = "CPU cluster 0 temperature",
17205641e30Sjmcneill },
17305641e30Sjmcneill [1] = {
17405641e30Sjmcneill .name = "cluster1",
17505641e30Sjmcneill .desc = "CPU cluster 1 temperature",
17605641e30Sjmcneill },
17705641e30Sjmcneill [2] = {
17805641e30Sjmcneill .name = "gpu",
17905641e30Sjmcneill .desc = "GPU temperature",
18005641e30Sjmcneill },
18105641e30Sjmcneill },
18205641e30Sjmcneill .clk_rate = A83T_CLK_RATE,
18305641e30Sjmcneill .adc_acquire_time = A83T_ADC_ACQUIRE_TIME,
18405641e30Sjmcneill .adc_cali_en = 1,
18505641e30Sjmcneill .filter = A83T_FILTER,
18605641e30Sjmcneill .intc = A83T_INTC,
18705641e30Sjmcneill .to_temp = a83t_to_temp,
18805641e30Sjmcneill .calib0_mask = 0xffffffff,
18905641e30Sjmcneill .calib1_mask = 0xffffffff,
19005641e30Sjmcneill };
19105641e30Sjmcneill
19205641e30Sjmcneill static int
a64_to_temp(u_int sensor,uint32_t val)193fdc625b4Sjmcneill a64_to_temp(u_int sensor, uint32_t val)
19405641e30Sjmcneill {
19505641e30Sjmcneill return ((A64_TEMP_BASE - (val * A64_TEMP_MUL)) / A64_TEMP_DIV);
19605641e30Sjmcneill }
19705641e30Sjmcneill
19805641e30Sjmcneill static const struct sunxi_thermal_config a64_config = {
19905641e30Sjmcneill .nsensors = 3,
20005641e30Sjmcneill .sensors = {
20105641e30Sjmcneill [0] = {
20205641e30Sjmcneill .name = "cpu",
20305641e30Sjmcneill .desc = "CPU temperature",
20405641e30Sjmcneill },
20505641e30Sjmcneill [1] = {
20605641e30Sjmcneill .name = "gpu1",
20705641e30Sjmcneill .desc = "GPU temperature 1",
20805641e30Sjmcneill },
20905641e30Sjmcneill [2] = {
21005641e30Sjmcneill .name = "gpu2",
21105641e30Sjmcneill .desc = "GPU temperature 2",
21205641e30Sjmcneill },
21305641e30Sjmcneill },
21405641e30Sjmcneill .clk_rate = A64_CLK_RATE,
21505641e30Sjmcneill .adc_acquire_time = A64_ADC_ACQUIRE_TIME,
21605641e30Sjmcneill .filter = A64_FILTER,
21705641e30Sjmcneill .intc = A64_INTC,
21805641e30Sjmcneill .to_temp = a64_to_temp,
21905641e30Sjmcneill };
22005641e30Sjmcneill
22105641e30Sjmcneill static int
h3_to_temp(u_int sensor,uint32_t val)222fdc625b4Sjmcneill h3_to_temp(u_int sensor, uint32_t val)
22305641e30Sjmcneill {
22405641e30Sjmcneill return (H3_TEMP_BASE - ((val * H3_TEMP_MUL) / H3_TEMP_DIV));
22505641e30Sjmcneill }
22605641e30Sjmcneill
22705641e30Sjmcneill static uint32_t
h3_to_reg(u_int sensor,int val)228fdc625b4Sjmcneill h3_to_reg(u_int sensor, int val)
22905641e30Sjmcneill {
23005641e30Sjmcneill return ((H3_TEMP_MINUS - (val * H3_TEMP_DIV)) / H3_TEMP_MUL);
23105641e30Sjmcneill }
23205641e30Sjmcneill
23305641e30Sjmcneill static const struct sunxi_thermal_config h3_config = {
23405641e30Sjmcneill .nsensors = 1,
23505641e30Sjmcneill .sensors = {
23605641e30Sjmcneill [0] = {
23705641e30Sjmcneill .name = "cpu",
23805641e30Sjmcneill .desc = "CPU temperature",
23905641e30Sjmcneill .init_alarm = H3_INIT_ALARM,
24005641e30Sjmcneill .init_shut = H3_INIT_SHUT,
24105641e30Sjmcneill },
24205641e30Sjmcneill },
24305641e30Sjmcneill .clk_rate = H3_CLK_RATE,
24405641e30Sjmcneill .adc_acquire_time = H3_ADC_ACQUIRE_TIME,
24505641e30Sjmcneill .filter = H3_FILTER,
24605641e30Sjmcneill .intc = H3_INTC,
24705641e30Sjmcneill .to_temp = h3_to_temp,
24805641e30Sjmcneill .to_reg = h3_to_reg,
24905641e30Sjmcneill .calib0_mask = 0xfff,
25005641e30Sjmcneill };
25105641e30Sjmcneill
252fdc625b4Sjmcneill static int
h5_to_temp(u_int sensor,uint32_t val)253fdc625b4Sjmcneill h5_to_temp(u_int sensor, uint32_t val)
254fdc625b4Sjmcneill {
255fdc625b4Sjmcneill int base, mul;
256fdc625b4Sjmcneill
257fdc625b4Sjmcneill if (val >= 0x500) {
258fdc625b4Sjmcneill base = H5_TEMP_BASE_L;
259fdc625b4Sjmcneill mul = H5_TEMP_MUL_L;
260fdc625b4Sjmcneill } else {
261fdc625b4Sjmcneill base = sensor == 0 ? H5_TEMP_BASE_H_0 : H5_TEMP_BASE_H_1;
26283927cccSbsiegert mul = sensor == 0 ? H5_TEMP_MUL_H_0 : H5_TEMP_MUL_H_1;
263fdc625b4Sjmcneill }
264fdc625b4Sjmcneill
265fdc625b4Sjmcneill return (base - val * mul) >> H5_TEMP_DIV;
266fdc625b4Sjmcneill }
267fdc625b4Sjmcneill
268fdc625b4Sjmcneill static uint32_t
h5_to_reg(u_int sensor,int val)269fdc625b4Sjmcneill h5_to_reg(u_int sensor, int val)
270fdc625b4Sjmcneill {
271fdc625b4Sjmcneill int base, mul;
272fdc625b4Sjmcneill
273fdc625b4Sjmcneill if (val <= 70) {
274fdc625b4Sjmcneill base = H5_TEMP_BASE_L;
275fdc625b4Sjmcneill mul = H5_TEMP_MUL_L;
276fdc625b4Sjmcneill } else {
277fdc625b4Sjmcneill base = sensor == 0 ? H5_TEMP_BASE_H_0 : H5_TEMP_BASE_H_1;
27883927cccSbsiegert mul = sensor == 0 ? H5_TEMP_MUL_H_0 : H5_TEMP_MUL_H_1;
279fdc625b4Sjmcneill }
280fdc625b4Sjmcneill
281fdc625b4Sjmcneill return (base - (val << H5_TEMP_DIV)) / mul;
282fdc625b4Sjmcneill }
283fdc625b4Sjmcneill
284fdc625b4Sjmcneill static const struct sunxi_thermal_config h5_config = {
285fdc625b4Sjmcneill .nsensors = 2,
286fdc625b4Sjmcneill .sensors = {
287fdc625b4Sjmcneill [0] = {
288fdc625b4Sjmcneill .name = "cpu",
289fdc625b4Sjmcneill .desc = "CPU temperature",
290fdc625b4Sjmcneill .init_alarm = H5_INIT_ALARM,
291fdc625b4Sjmcneill .init_shut = H5_INIT_SHUT,
292fdc625b4Sjmcneill },
293fdc625b4Sjmcneill [1] = {
294fdc625b4Sjmcneill .name = "gpu",
295fdc625b4Sjmcneill .desc = "GPU temperature",
296fdc625b4Sjmcneill .init_alarm = H5_INIT_ALARM,
297fdc625b4Sjmcneill .init_shut = H5_INIT_SHUT,
298fdc625b4Sjmcneill },
299fdc625b4Sjmcneill },
300fdc625b4Sjmcneill .clk_rate = H5_CLK_RATE,
301fdc625b4Sjmcneill .adc_acquire_time = H5_ADC_ACQUIRE_TIME,
302fdc625b4Sjmcneill .filter = H5_FILTER,
303fdc625b4Sjmcneill .intc = H5_INTC,
304fdc625b4Sjmcneill .to_temp = h5_to_temp,
305fdc625b4Sjmcneill .to_reg = h5_to_reg,
306fdc625b4Sjmcneill };
307fdc625b4Sjmcneill
308646c0f59Sthorpej static struct device_compatible_entry compat_data[] = {
309c34066c2Sjmcneill { .compat = "allwinner,sun8i-a83t-ths", .data = &a83t_config },
310c34066c2Sjmcneill { .compat = "allwinner,sun8i-h3-ths", .data = &h3_config },
311c34066c2Sjmcneill { .compat = "allwinner,sun50i-a64-ths", .data = &a64_config },
312c34066c2Sjmcneill { .compat = "allwinner,sun50i-h5-ths", .data = &h5_config },
313c34066c2Sjmcneill
314c34066c2Sjmcneill /*
315c34066c2Sjmcneill * DTCOMPAT: Old compat strings. Do not add to this list.
316c34066c2Sjmcneill */
317646c0f59Sthorpej { .compat = "allwinner,sun8i-a83t-ts", .data = &a83t_config },
318646c0f59Sthorpej { .compat = "allwinner,sun8i-h3-ts", .data = &h3_config },
319646c0f59Sthorpej { .compat = "allwinner,sun50i-a64-ts", .data = &a64_config },
320646c0f59Sthorpej { .compat = "allwinner,sun50i-h5-ts", .data = &h5_config },
321ec189949Sthorpej DEVICE_COMPAT_EOL
32205641e30Sjmcneill };
32305641e30Sjmcneill
32405641e30Sjmcneill struct sunxi_thermal_softc {
32505641e30Sjmcneill device_t dev;
32605641e30Sjmcneill int phandle;
32705641e30Sjmcneill bus_space_tag_t bst;
32805641e30Sjmcneill bus_space_handle_t bsh;
329646c0f59Sthorpej const struct sunxi_thermal_config *conf;
33005641e30Sjmcneill
33105641e30Sjmcneill kmutex_t lock;
33205641e30Sjmcneill callout_t tick;
33305641e30Sjmcneill
33405641e30Sjmcneill struct sysmon_envsys *sme;
33505641e30Sjmcneill envsys_data_t data[MAX_SENSORS];
33605641e30Sjmcneill };
33705641e30Sjmcneill
33805641e30Sjmcneill #define RD4(sc, reg) \
33905641e30Sjmcneill bus_space_read_4((sc)->bst, (sc)->bsh, (reg))
34005641e30Sjmcneill #define WR4(sc, reg, val) \
34105641e30Sjmcneill bus_space_write_4((sc)->bst, (sc)->bsh, (reg), (val))
34205641e30Sjmcneill
34305641e30Sjmcneill static int
sunxi_thermal_init(struct sunxi_thermal_softc * sc)34405641e30Sjmcneill sunxi_thermal_init(struct sunxi_thermal_softc *sc)
34505641e30Sjmcneill {
34605641e30Sjmcneill uint32_t calib[2];
34705641e30Sjmcneill int error;
34805641e30Sjmcneill
34905641e30Sjmcneill if (sc->conf->calib0_mask != 0 || sc->conf->calib1_mask != 0) {
35005641e30Sjmcneill /* Read calibration settings from SRAM */
35105641e30Sjmcneill error = sunxi_sid_read_tscalib(calib);
35205641e30Sjmcneill if (error != 0)
35305641e30Sjmcneill return error;
35405641e30Sjmcneill
35505641e30Sjmcneill calib[0] &= sc->conf->calib0_mask;
35605641e30Sjmcneill calib[1] &= sc->conf->calib1_mask;
35705641e30Sjmcneill
35805641e30Sjmcneill /* Write calibration settings to thermal controller */
35905641e30Sjmcneill if (calib[0] != 0)
36005641e30Sjmcneill WR4(sc, THS_CALIB0, calib[0]);
36105641e30Sjmcneill if (calib[1] != 0)
36205641e30Sjmcneill WR4(sc, THS_CALIB1, calib[1]);
36305641e30Sjmcneill }
36405641e30Sjmcneill
36505641e30Sjmcneill /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */
36605641e30Sjmcneill WR4(sc, THS_CTRL1, ADC_CALI_EN);
36705641e30Sjmcneill WR4(sc, THS_CTRL0, sc->conf->adc_acquire_time);
36805641e30Sjmcneill WR4(sc, THS_CTRL2, sc->conf->adc_acquire_time << SENSOR_ACQ1_SHIFT);
36905641e30Sjmcneill
37005641e30Sjmcneill /* Enable average filter */
37105641e30Sjmcneill WR4(sc, THS_FILTER, sc->conf->filter);
37205641e30Sjmcneill
37305641e30Sjmcneill /* Enable interrupts */
37405641e30Sjmcneill WR4(sc, THS_INTS, RD4(sc, THS_INTS));
37505641e30Sjmcneill WR4(sc, THS_INTC, sc->conf->intc | SHUT_INT_ALL | ALARM_INT_ALL);
37605641e30Sjmcneill
37705641e30Sjmcneill /* Enable sensors */
37805641e30Sjmcneill WR4(sc, THS_CTRL2, RD4(sc, THS_CTRL2) | SENSOR_ENABLE_ALL);
37905641e30Sjmcneill
38005641e30Sjmcneill return 0;
38105641e30Sjmcneill }
38205641e30Sjmcneill
38305641e30Sjmcneill static int
sunxi_thermal_gettemp(struct sunxi_thermal_softc * sc,int sensor)38405641e30Sjmcneill sunxi_thermal_gettemp(struct sunxi_thermal_softc *sc, int sensor)
38505641e30Sjmcneill {
38605641e30Sjmcneill uint32_t val;
38705641e30Sjmcneill
38805641e30Sjmcneill val = RD4(sc, THS_DATA0 + (sensor * 4));
38905641e30Sjmcneill
390fdc625b4Sjmcneill return sc->conf->to_temp(sensor, val);
39105641e30Sjmcneill }
39205641e30Sjmcneill
39305641e30Sjmcneill static int
sunxi_thermal_getshut(struct sunxi_thermal_softc * sc,int sensor)39405641e30Sjmcneill sunxi_thermal_getshut(struct sunxi_thermal_softc *sc, int sensor)
39505641e30Sjmcneill {
39605641e30Sjmcneill uint32_t val;
39705641e30Sjmcneill
39805641e30Sjmcneill val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4));
39905641e30Sjmcneill val = (val >> SHUT_T_HOT_SHIFT) & SHUT_T_HOT_MASK;
40005641e30Sjmcneill
401fdc625b4Sjmcneill return sc->conf->to_temp(sensor, val);
40205641e30Sjmcneill }
40305641e30Sjmcneill
40405641e30Sjmcneill static void
sunxi_thermal_setshut(struct sunxi_thermal_softc * sc,int sensor,int temp)40505641e30Sjmcneill sunxi_thermal_setshut(struct sunxi_thermal_softc *sc, int sensor, int temp)
40605641e30Sjmcneill {
40705641e30Sjmcneill uint32_t val;
40805641e30Sjmcneill
40905641e30Sjmcneill val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4));
41005641e30Sjmcneill val &= ~(SHUT_T_HOT_MASK << SHUT_T_HOT_SHIFT);
411fdc625b4Sjmcneill val |= (sc->conf->to_reg(sensor, temp) << SHUT_T_HOT_SHIFT);
41205641e30Sjmcneill WR4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4), val);
41305641e30Sjmcneill }
41405641e30Sjmcneill
41505641e30Sjmcneill static int
sunxi_thermal_gethyst(struct sunxi_thermal_softc * sc,int sensor)41605641e30Sjmcneill sunxi_thermal_gethyst(struct sunxi_thermal_softc *sc, int sensor)
41705641e30Sjmcneill {
41805641e30Sjmcneill uint32_t val;
41905641e30Sjmcneill
42005641e30Sjmcneill val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4));
42105641e30Sjmcneill val = (val >> ALARM_T_HYST_SHIFT) & ALARM_T_HYST_MASK;
42205641e30Sjmcneill
423fdc625b4Sjmcneill return sc->conf->to_temp(sensor, val);
42405641e30Sjmcneill }
42505641e30Sjmcneill
42605641e30Sjmcneill static int
sunxi_thermal_getalarm(struct sunxi_thermal_softc * sc,int sensor)42705641e30Sjmcneill sunxi_thermal_getalarm(struct sunxi_thermal_softc *sc, int sensor)
42805641e30Sjmcneill {
42905641e30Sjmcneill uint32_t val;
43005641e30Sjmcneill
43105641e30Sjmcneill val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4));
43205641e30Sjmcneill val = (val >> ALARM_T_HOT_SHIFT) & ALARM_T_HOT_MASK;
43305641e30Sjmcneill
434fdc625b4Sjmcneill return sc->conf->to_temp(sensor, val);
43505641e30Sjmcneill }
43605641e30Sjmcneill
43705641e30Sjmcneill static void
sunxi_thermal_setalarm(struct sunxi_thermal_softc * sc,int sensor,int temp)43805641e30Sjmcneill sunxi_thermal_setalarm(struct sunxi_thermal_softc *sc, int sensor, int temp)
43905641e30Sjmcneill {
44005641e30Sjmcneill uint32_t val;
44105641e30Sjmcneill
44205641e30Sjmcneill val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4));
44305641e30Sjmcneill val &= ~(ALARM_T_HOT_MASK << ALARM_T_HOT_SHIFT);
444fdc625b4Sjmcneill val |= (sc->conf->to_reg(sensor, temp) << ALARM_T_HOT_SHIFT);
44505641e30Sjmcneill WR4(sc, THS_ALARM0_CTRL + (sensor * 4), val);
44605641e30Sjmcneill }
44705641e30Sjmcneill
44805641e30Sjmcneill static void
sunxi_thermal_task_shut(void * arg)44905641e30Sjmcneill sunxi_thermal_task_shut(void *arg)
45005641e30Sjmcneill {
45105641e30Sjmcneill struct sunxi_thermal_softc * const sc = arg;
45205641e30Sjmcneill
45305641e30Sjmcneill device_printf(sc->dev,
45405641e30Sjmcneill "WARNING - current temperature exceeds safe limits\n");
45505641e30Sjmcneill
456599c2405Sthorpej kern_reboot(RB_POWERDOWN, NULL);
45705641e30Sjmcneill }
45805641e30Sjmcneill
45905641e30Sjmcneill static void
sunxi_thermal_task_alarm(void * arg)46005641e30Sjmcneill sunxi_thermal_task_alarm(void *arg)
46105641e30Sjmcneill {
46205641e30Sjmcneill struct sunxi_thermal_softc * const sc = arg;
46305641e30Sjmcneill
46405641e30Sjmcneill const int alarm_val = sunxi_thermal_getalarm(sc, 0);
46505641e30Sjmcneill const int temp_val = sunxi_thermal_gettemp(sc, 0);
46605641e30Sjmcneill
46705641e30Sjmcneill if (temp_val < alarm_val)
46805641e30Sjmcneill pmf_event_inject(NULL, PMFE_THROTTLE_DISABLE);
46905641e30Sjmcneill else
47005641e30Sjmcneill callout_schedule(&sc->tick, hz);
47105641e30Sjmcneill }
47205641e30Sjmcneill
47305641e30Sjmcneill static void
sunxi_thermal_tick(void * arg)47405641e30Sjmcneill sunxi_thermal_tick(void *arg)
47505641e30Sjmcneill {
47605641e30Sjmcneill struct sunxi_thermal_softc * const sc = arg;
47705641e30Sjmcneill
47805641e30Sjmcneill sysmon_task_queue_sched(0, sunxi_thermal_task_alarm, sc);
47905641e30Sjmcneill }
48005641e30Sjmcneill
48105641e30Sjmcneill static int
sunxi_thermal_intr(void * arg)48205641e30Sjmcneill sunxi_thermal_intr(void *arg)
48305641e30Sjmcneill {
48405641e30Sjmcneill struct sunxi_thermal_softc * const sc = arg;
48505641e30Sjmcneill uint32_t ints;
48605641e30Sjmcneill
48705641e30Sjmcneill mutex_enter(&sc->lock);
48805641e30Sjmcneill
48905641e30Sjmcneill ints = RD4(sc, THS_INTS);
49005641e30Sjmcneill WR4(sc, THS_INTS, ints);
49105641e30Sjmcneill
49205641e30Sjmcneill if ((ints & SHUT_INT_ALL) != 0)
49305641e30Sjmcneill sysmon_task_queue_sched(0, sunxi_thermal_task_shut, sc);
49405641e30Sjmcneill
49505641e30Sjmcneill if ((ints & ALARM_INT_ALL) != 0) {
49605641e30Sjmcneill pmf_event_inject(NULL, PMFE_THROTTLE_ENABLE);
49705641e30Sjmcneill sysmon_task_queue_sched(0, sunxi_thermal_task_alarm, sc);
49805641e30Sjmcneill }
49905641e30Sjmcneill
50005641e30Sjmcneill mutex_exit(&sc->lock);
50105641e30Sjmcneill
50205641e30Sjmcneill return 1;
50305641e30Sjmcneill }
50405641e30Sjmcneill
50505641e30Sjmcneill static void
sunxi_thermal_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)50605641e30Sjmcneill sunxi_thermal_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
50705641e30Sjmcneill {
50805641e30Sjmcneill struct sunxi_thermal_softc * const sc = sme->sme_cookie;
50905641e30Sjmcneill
51005641e30Sjmcneill const int64_t temp = sunxi_thermal_gettemp(sc, edata->private);
51105641e30Sjmcneill
51205641e30Sjmcneill edata->value_cur = temp * 1000000 + TEMP_C_TO_K;
51305641e30Sjmcneill edata->state = ENVSYS_SVALID;
51405641e30Sjmcneill }
51505641e30Sjmcneill
51605641e30Sjmcneill static int
sunxi_thermal_init_clocks(struct sunxi_thermal_softc * sc)51705641e30Sjmcneill sunxi_thermal_init_clocks(struct sunxi_thermal_softc *sc)
51805641e30Sjmcneill {
51905641e30Sjmcneill struct fdtbus_reset *rst;
52005641e30Sjmcneill struct clk *clk;
52105641e30Sjmcneill int error;
52205641e30Sjmcneill
523*dad5cb8dSjmcneill clk = fdtbus_clock_get(sc->phandle, "bus");
524*dad5cb8dSjmcneill if (clk == NULL) {
525*dad5cb8dSjmcneill /* DTCOMPAT */
52605641e30Sjmcneill clk = fdtbus_clock_get(sc->phandle, "ahb");
527*dad5cb8dSjmcneill }
528f41bc86cSjmcneill if (clk) {
52905641e30Sjmcneill error = clk_enable(clk);
53005641e30Sjmcneill if (error != 0)
53105641e30Sjmcneill return error;
532f41bc86cSjmcneill }
53305641e30Sjmcneill
534*dad5cb8dSjmcneill clk = fdtbus_clock_get(sc->phandle, "mod");
535*dad5cb8dSjmcneill if (clk == NULL) {
536*dad5cb8dSjmcneill /* DTCOMPAT */
53705641e30Sjmcneill clk = fdtbus_clock_get(sc->phandle, "ths");
538*dad5cb8dSjmcneill }
539f41bc86cSjmcneill if (clk) {
54005641e30Sjmcneill error = clk_set_rate(clk, sc->conf->clk_rate);
54105641e30Sjmcneill if (error != 0)
54205641e30Sjmcneill return error;
54305641e30Sjmcneill error = clk_enable(clk);
54405641e30Sjmcneill if (error != 0)
54505641e30Sjmcneill return error;
546f41bc86cSjmcneill }
54705641e30Sjmcneill
54805641e30Sjmcneill rst = fdtbus_reset_get_index(sc->phandle, 0);
549f41bc86cSjmcneill if (rst) {
55005641e30Sjmcneill error = fdtbus_reset_deassert(rst);
55105641e30Sjmcneill if (error != 0)
55205641e30Sjmcneill return error;
553f41bc86cSjmcneill }
55405641e30Sjmcneill
55505641e30Sjmcneill return 0;
55605641e30Sjmcneill }
55705641e30Sjmcneill
55805641e30Sjmcneill static int
sunxi_thermal_match(device_t parent,cfdata_t cf,void * aux)55905641e30Sjmcneill sunxi_thermal_match(device_t parent, cfdata_t cf, void *aux)
56005641e30Sjmcneill {
56105641e30Sjmcneill struct fdt_attach_args * const faa = aux;
56205641e30Sjmcneill
5636e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
56405641e30Sjmcneill }
56505641e30Sjmcneill
56605641e30Sjmcneill static void
sunxi_thermal_attach(device_t parent,device_t self,void * aux)56705641e30Sjmcneill sunxi_thermal_attach(device_t parent, device_t self, void *aux)
56805641e30Sjmcneill {
56905641e30Sjmcneill struct sunxi_thermal_softc * const sc = device_private(self);
57005641e30Sjmcneill struct fdt_attach_args * const faa = aux;
57105641e30Sjmcneill const int phandle = faa->faa_phandle;
57205641e30Sjmcneill char intrstr[128];
57305641e30Sjmcneill bus_addr_t addr;
57405641e30Sjmcneill bus_size_t size;
57505641e30Sjmcneill void *ih;
57605641e30Sjmcneill int i;
57705641e30Sjmcneill
57805641e30Sjmcneill if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
57905641e30Sjmcneill aprint_error(": couldn't get registers\n");
58005641e30Sjmcneill return;
58105641e30Sjmcneill }
58205641e30Sjmcneill
58305641e30Sjmcneill sc->dev = self;
58405641e30Sjmcneill sc->phandle = phandle;
58505641e30Sjmcneill sc->bst = faa->faa_bst;
5866e54367aSthorpej sc->conf = of_compatible_lookup(phandle, compat_data)->data;
58705641e30Sjmcneill if (bus_space_map(sc->bst, addr, size, 0, &sc->bsh) != 0) {
58805641e30Sjmcneill aprint_error(": couldn't map registers\n");
58905641e30Sjmcneill return;
59005641e30Sjmcneill }
59105641e30Sjmcneill mutex_init(&sc->lock, MUTEX_DEFAULT, IPL_VM);
59205641e30Sjmcneill callout_init(&sc->tick, CALLOUT_MPSAFE);
59305641e30Sjmcneill callout_setfunc(&sc->tick, sunxi_thermal_tick, sc);
59405641e30Sjmcneill
59505641e30Sjmcneill if (sunxi_thermal_init_clocks(sc) != 0) {
59605641e30Sjmcneill aprint_error(": couldn't enable clocks\n");
59705641e30Sjmcneill return;
59805641e30Sjmcneill }
59905641e30Sjmcneill
60005641e30Sjmcneill if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
60105641e30Sjmcneill aprint_error(": couldn't decode interrupt\n");
60205641e30Sjmcneill return;
60305641e30Sjmcneill }
60405641e30Sjmcneill
60505641e30Sjmcneill aprint_naive("\n");
60605641e30Sjmcneill aprint_normal(": Thermal sensor controller\n");
60705641e30Sjmcneill
608076a1169Sjmcneill ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, FDT_INTR_MPSAFE,
609076a1169Sjmcneill sunxi_thermal_intr, sc, device_xname(self));
61005641e30Sjmcneill if (ih == NULL) {
61105641e30Sjmcneill aprint_error_dev(self, "couldn't establish interrupt on %s\n",
61205641e30Sjmcneill intrstr);
61305641e30Sjmcneill return;
61405641e30Sjmcneill }
61505641e30Sjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr);
61605641e30Sjmcneill
61705641e30Sjmcneill for (i = 0; i < sc->conf->nsensors; i++) {
61805641e30Sjmcneill if (sc->conf->sensors[i].init_alarm > 0)
61905641e30Sjmcneill sunxi_thermal_setalarm(sc, i,
62005641e30Sjmcneill sc->conf->sensors[i].init_alarm);
62105641e30Sjmcneill if (sc->conf->sensors[i].init_shut > 0)
62205641e30Sjmcneill sunxi_thermal_setshut(sc, i,
62305641e30Sjmcneill sc->conf->sensors[i].init_shut);
62405641e30Sjmcneill }
62505641e30Sjmcneill
62605641e30Sjmcneill if (sunxi_thermal_init(sc) != 0) {
62705641e30Sjmcneill aprint_error_dev(self, "failed to initialize sensors\n");
62805641e30Sjmcneill return;
62905641e30Sjmcneill }
63005641e30Sjmcneill
63105641e30Sjmcneill sc->sme = sysmon_envsys_create();
63205641e30Sjmcneill sc->sme->sme_name = device_xname(self);
63305641e30Sjmcneill sc->sme->sme_cookie = sc;
63405641e30Sjmcneill sc->sme->sme_refresh = sunxi_thermal_refresh;
63505641e30Sjmcneill for (i = 0; i < sc->conf->nsensors; i++) {
63605641e30Sjmcneill sc->data[i].private = i;
63705641e30Sjmcneill sc->data[i].units = ENVSYS_STEMP;
63805641e30Sjmcneill sc->data[i].state = ENVSYS_SINVALID;
639388e0209Sjmcneill strlcpy(sc->data[i].desc, sc->conf->sensors[i].desc,
640388e0209Sjmcneill sizeof(sc->data[i].desc));
64105641e30Sjmcneill sysmon_envsys_sensor_attach(sc->sme, &sc->data[i]);
64205641e30Sjmcneill }
64305641e30Sjmcneill sysmon_envsys_register(sc->sme);
64405641e30Sjmcneill
64505641e30Sjmcneill for (i = 0; i < sc->conf->nsensors; i++) {
64605641e30Sjmcneill device_printf(self,
64705641e30Sjmcneill "%s: alarm %dC hyst %dC shut %dC\n",
64805641e30Sjmcneill sc->conf->sensors[i].name,
64905641e30Sjmcneill sunxi_thermal_getalarm(sc, i),
65005641e30Sjmcneill sunxi_thermal_gethyst(sc, i),
65105641e30Sjmcneill sunxi_thermal_getshut(sc, i));
65205641e30Sjmcneill }
65305641e30Sjmcneill }
65405641e30Sjmcneill
65505641e30Sjmcneill CFATTACH_DECL_NEW(sunxi_thermal, sizeof(struct sunxi_thermal_softc),
65605641e30Sjmcneill sunxi_thermal_match, sunxi_thermal_attach, NULL, NULL);
657