From owner-svn-src-all@freebsd.org Sun Oct 23 17:48:35 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 82CE3C1E065; Sun, 23 Oct 2016 17:48:35 +0000 (UTC) (envelope-from jmcneill@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 52386BCC; Sun, 23 Oct 2016 17:48:35 +0000 (UTC) (envelope-from jmcneill@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u9NHmYba089216; Sun, 23 Oct 2016 17:48:34 GMT (envelope-from jmcneill@FreeBSD.org) Received: (from jmcneill@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u9NHmYMZ089215; Sun, 23 Oct 2016 17:48:34 GMT (envelope-from jmcneill@FreeBSD.org) Message-Id: <201610231748.u9NHmYMZ089215@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: jmcneill set sender to jmcneill@FreeBSD.org using -f From: Jared McNeill Date: Sun, 23 Oct 2016 17:48:34 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r307824 - head/sys/arm/allwinner X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 23 Oct 2016 17:48:35 -0000 Author: jmcneill Date: Sun Oct 23 17:48:34 2016 New Revision: 307824 URL: https://svnweb.freebsd.org/changeset/base/307824 Log: Throttle CPU frequency when hot temperature threshold has been reached to prevent overheating. When sensor 0's alarm interrupt is fired, set a throttle flag. Further requests to set CPU frequency will be rejected until sensor 0's temperature returns to a level below the hot temperature threshold. Relnotes: yes Modified: head/sys/arm/allwinner/aw_thermal.c Modified: head/sys/arm/allwinner/aw_thermal.c ============================================================================== --- head/sys/arm/allwinner/aw_thermal.c Sun Oct 23 14:28:29 2016 (r307823) +++ head/sys/arm/allwinner/aw_thermal.c Sun Oct 23 17:48:34 2016 (r307824) @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -51,6 +52,8 @@ __FBSDID("$FreeBSD$"); #include +#include "cpufreq_if.h" + #define THS_CTRL0 0x00 #define THS_CTRL1 0x04 #define ADC_CALI_EN (1 << 17) @@ -70,6 +73,14 @@ __FBSDID("$FreeBSD$"); #define ALARM_INT2_STS (1 << 2) #define ALARM_INT1_STS (1 << 1) #define ALARM_INT0_STS (1 << 0) +#define THS_ALARM0_CTRL 0x50 +#define ALARM_T_HOT_MASK 0xfff +#define ALARM_T_HOT_SHIFT 16 +#define ALARM_T_HYST_MASK 0xfff +#define ALARM_T_HYST_SHIFT 0 +#define THS_SHUTDOWN0_CTRL 0x60 +#define SHUT_T_HOT_MASK 0xfff +#define SHUT_T_HOT_SHIFT 16 #define THS_FILTER 0x70 #define THS_CALIB0 0x74 #define THS_CALIB1 0x78 @@ -97,16 +108,24 @@ __FBSDID("$FreeBSD$"); #define H3_ADC_ACQUIRE_TIME 0x3f #define H3_FILTER 0x6 #define H3_INTC 0x191000 -#define H3_TEMP_BASE 217000000 -#define H3_TEMP_MUL 121168 -#define H3_TEMP_DIV 1000000 +#define H3_TEMP_BASE 2794000 +#define H3_TEMP_MUL 1000 +#define H3_TEMP_DIV -14882 #define H3_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) +#define ALARM_INT_ALL (ALARM_INT0_STS) #define MAX_SENSORS 3 +#define MAX_CF_LEVELS 64 + +#define THROTTLE_ENABLE_DEFAULT 1 + +/* Enable thermal throttling */ +static int aw_thermal_throttle_enable = THROTTLE_ENABLE_DEFAULT; +TUNABLE_INT("hw.aw_thermal.throttle_enable", &aw_thermal_throttle_enable); struct aw_thermal_sensor { const char *name; @@ -118,14 +137,23 @@ struct aw_thermal_config { int nsensors; uint64_t clk_rate; uint32_t adc_acquire_time; + int adc_cali_en; uint32_t filter; uint32_t intc; + int (*to_temp)(uint32_t); int temp_base; int temp_mul; int temp_div; - int calib; + int calib0, calib1; + uint32_t calib0_mask, calib1_mask; }; +static int +a83t_to_temp(uint32_t val) +{ + return ((A83T_TEMP_BASE - (val * A83T_TEMP_MUL)) / A83T_TEMP_DIV); +} + static const struct aw_thermal_config a83t_config = { .nsensors = 3, .sensors = { @@ -144,14 +172,22 @@ static const struct aw_thermal_config a8 }, .clk_rate = A83T_CLK_RATE, .adc_acquire_time = A83T_ADC_ACQUIRE_TIME, + .adc_cali_en = 1, .filter = A83T_FILTER, .intc = A83T_INTC, - .temp_base = A83T_TEMP_BASE, - .temp_mul = A83T_TEMP_MUL, - .temp_div = A83T_TEMP_DIV, - .calib = 1, + .to_temp = a83t_to_temp, + .calib0 = 1, + .calib0_mask = 0xffffffff, + .calib1 = 1, + .calib1_mask = 0xffffffff, }; +static int +a64_to_temp(uint32_t val) +{ + return ((A64_TEMP_BASE - (val * A64_TEMP_MUL)) / A64_TEMP_DIV); +} + static const struct aw_thermal_config a64_config = { .nsensors = 3, .sensors = { @@ -172,11 +208,15 @@ static const struct aw_thermal_config a6 .adc_acquire_time = A64_ADC_ACQUIRE_TIME, .filter = A64_FILTER, .intc = A64_INTC, - .temp_base = A64_TEMP_BASE, - .temp_mul = A64_TEMP_MUL, - .temp_div = A64_TEMP_DIV, + .to_temp = a64_to_temp, }; +static int +h3_to_temp(uint32_t val) +{ + return (((int)(val * H3_TEMP_MUL) - H3_TEMP_BASE) / H3_TEMP_DIV); +} + static const struct aw_thermal_config h3_config = { .nsensors = 1, .sensors = { @@ -189,9 +229,9 @@ static const struct aw_thermal_config h3 .adc_acquire_time = H3_ADC_ACQUIRE_TIME, .filter = H3_FILTER, .intc = H3_INTC, - .temp_base = H3_TEMP_BASE, - .temp_mul = H3_TEMP_MUL, - .temp_div = H3_TEMP_DIV, + .to_temp = h3_to_temp, + .calib0 = 1, + .calib0_mask = 0xfff, }; static struct ofw_compat_data compat_data[] = { @@ -205,8 +245,14 @@ static struct ofw_compat_data compat_dat (void *)ofw_bus_search_compatible((d), compat_data)->ocd_data struct aw_thermal_softc { + device_t dev; struct resource *res[2]; struct aw_thermal_config *conf; + + int throttle; + int min_freq; + struct cf_level levels[MAX_CF_LEVELS]; + eventhandler_tag cf_pre_tag; }; static struct resource_spec aw_thermal_spec[] = { @@ -224,15 +270,20 @@ aw_thermal_init(struct aw_thermal_softc uint32_t calib0, calib1; int error; - if (sc->conf->calib) { + if (sc->conf->calib0 != 0 || sc->conf->calib1 != 0) { /* Read calibration settings from SRAM */ error = aw_sid_read_tscalib(&calib0, &calib1); if (error != 0) return (error); + calib0 &= sc->conf->calib0_mask; + calib1 &= sc->conf->calib1_mask; + /* Write calibration settings to thermal controller */ - WR4(sc, THS_CALIB0, calib0); - WR4(sc, THS_CALIB1, calib1); + if (sc->conf->calib0 != 0 && calib0 != 0) + WR4(sc, THS_CALIB0, calib0); + if (sc->conf->calib1 != 0 && calib1 != 0) + WR4(sc, THS_CALIB1, calib1); } /* Configure ADC acquire time (CLK_IN/(N+1)) and enable sensors */ @@ -245,7 +296,7 @@ aw_thermal_init(struct aw_thermal_softc /* Enable interrupts */ WR4(sc, THS_INTS, RD4(sc, THS_INTS)); - WR4(sc, THS_INTC, sc->conf->intc | SHUT_INT_ALL); + WR4(sc, THS_INTC, sc->conf->intc | SHUT_INT_ALL | ALARM_INT_ALL); /* Enable sensors */ WR4(sc, THS_CTRL2, RD4(sc, THS_CTRL2) | SENSOR_ENABLE_ALL); @@ -254,20 +305,46 @@ aw_thermal_init(struct aw_thermal_softc } static int -aw_thermal_reg_to_temp(struct aw_thermal_softc *sc, uint32_t val) +aw_thermal_gettemp(struct aw_thermal_softc *sc, int sensor) { - return ((sc->conf->temp_base - (val * sc->conf->temp_mul)) / - sc->conf->temp_div); + uint32_t val; + + val = RD4(sc, THS_DATA0 + (sensor * 4)); + + return (sc->conf->to_temp(val) + TEMP_C_TO_K); } static int -aw_thermal_gettemp(struct aw_thermal_softc *sc, int sensor) +aw_thermal_getshut(struct aw_thermal_softc *sc, int sensor) { uint32_t val; - val = RD4(sc, THS_DATA0 + (sensor * 4)); + val = RD4(sc, THS_SHUTDOWN0_CTRL + (sensor * 4)); + val = (val >> SHUT_T_HOT_SHIFT) & SHUT_T_HOT_MASK; + + return (sc->conf->to_temp(val) + TEMP_C_TO_K); +} + +static int +aw_thermal_gethyst(struct aw_thermal_softc *sc, int sensor) +{ + uint32_t val; - return (aw_thermal_reg_to_temp(sc, val) + TEMP_C_TO_K); + val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4)); + val = (val >> ALARM_T_HYST_SHIFT) & ALARM_T_HYST_MASK; + + return (sc->conf->to_temp(val) + TEMP_C_TO_K); +} + +static int +aw_thermal_getalarm(struct aw_thermal_softc *sc, int sensor) +{ + uint32_t val; + + val = RD4(sc, THS_ALARM0_CTRL + (sensor * 4)); + val = (val >> ALARM_T_HOT_SHIFT) & ALARM_T_HOT_MASK; + + return (sc->conf->to_temp(val) + TEMP_C_TO_K); } static int @@ -285,6 +362,55 @@ aw_thermal_sysctl(SYSCTL_HANDLER_ARGS) } static void +aw_thermal_throttle(struct aw_thermal_softc *sc, int enable) +{ + device_t cf_dev; + int count, error; + + if (enable == sc->throttle) + return; + + if (enable != 0) { + /* Set the lowest available frequency */ + cf_dev = devclass_get_device(devclass_find("cpufreq"), 0); + if (cf_dev == NULL) + return; + count = MAX_CF_LEVELS; + error = CPUFREQ_LEVELS(cf_dev, sc->levels, &count); + if (error != 0 || count == 0) + return; + sc->min_freq = sc->levels[count - 1].total_set.freq; + error = CPUFREQ_SET(cf_dev, &sc->levels[count - 1], + CPUFREQ_PRIO_USER); + if (error != 0) + return; + } + + sc->throttle = enable; +} + +static void +aw_thermal_cf_pre_change(void *arg, const struct cf_level *level, int *status) +{ + struct aw_thermal_softc *sc; + int temp_cur, temp_alarm; + + sc = arg; + + if (aw_thermal_throttle_enable == 0 || sc->throttle == 0 || + level->total_set.freq == sc->min_freq) + return; + + temp_cur = aw_thermal_gettemp(sc, 0); + temp_alarm = aw_thermal_getalarm(sc, 0); + + if (temp_cur < temp_alarm) + aw_thermal_throttle(sc, 0); + else + *status = ENXIO; +} + +static void aw_thermal_intr(void *arg) { struct aw_thermal_softc *sc; @@ -299,9 +425,12 @@ aw_thermal_intr(void *arg) if ((ints & SHUT_INT_ALL) != 0) { device_printf(dev, - "WARNING - current temperature exceeds safe limits\n"); + "WARNING - current temperature exceeds safe limits\n"); shutdown_nice(RB_POWEROFF); } + + if ((ints & ALARM_INT_ALL) != 0) + aw_thermal_throttle(sc, 1); } static int @@ -383,6 +512,18 @@ aw_thermal_attach(device_t dev) sc, i, aw_thermal_sysctl, "IK0", sc->conf->sensors[i].desc); + if (bootverbose) + for (i = 0; i < sc->conf->nsensors; i++) { + device_printf(dev, + "#%d: alarm %dC hyst %dC shut %dC\n", i, + aw_thermal_getalarm(sc, i) - TEMP_C_TO_K, + aw_thermal_gethyst(sc, i) - TEMP_C_TO_K, + aw_thermal_getshut(sc, i) - TEMP_C_TO_K); + } + + sc->cf_pre_tag = EVENTHANDLER_REGISTER(cpufreq_pre_change, + aw_thermal_cf_pre_change, sc, EVENTHANDLER_PRI_FIRST); + return (0); fail: