diff --git a/src/board/system76/common/dgpu.c b/src/board/system76/common/dgpu.c index 5d7ec7a..5e64bfe 100644 --- a/src/board/system76/common/dgpu.c +++ b/src/board/system76/common/dgpu.c @@ -1,59 +1,14 @@ // SPDX-License-Identifier: GPL-3.0-only #include -#include #include -#include #include #include #include #include -#include - -// Fan speed is the lowest requested over HEATUP seconds -#ifndef BOARD_DGPU_HEATUP -#define BOARD_DGPU_HEATUP 4 -#endif - -static uint8_t FAN_HEATUP[BOARD_DGPU_HEATUP] = { 0 }; - -// Fan speed is the highest HEATUP speed over COOLDOWN seconds -#ifndef BOARD_DGPU_COOLDOWN -#define BOARD_DGPU_COOLDOWN 10 -#endif - -static uint8_t FAN_COOLDOWN[BOARD_DGPU_COOLDOWN] = { 0 }; int16_t dgpu_temp = 0; -#define DGPU_TEMP(X) ((int16_t)(X)) - -#define FAN_POINT(T, D) \ - { .temp = DGPU_TEMP(T), .duty = PWM_DUTY(D) } - -// Fan curve with temperature in degrees C, duty cycle in percent -static struct FanPoint __code FAN_POINTS[] = { -#ifdef BOARD_DGPU_FAN_POINTS - BOARD_DGPU_FAN_POINTS -#else - FAN_POINT(70, 40), - FAN_POINT(75, 50), - FAN_POINT(80, 60), - FAN_POINT(85, 65), - FAN_POINT(90, 65) -#endif -}; - -static struct Fan __code FAN = { - .points = FAN_POINTS, - .points_size = ARRAY_SIZE(FAN_POINTS), - .heatup = FAN_HEATUP, - .heatup_size = ARRAY_SIZE(FAN_HEATUP), - .cooldown = FAN_COOLDOWN, - .cooldown_size = ARRAY_SIZE(FAN_COOLDOWN), - .interpolate = SMOOTH_FANS != 0, -}; - void dgpu_init(void) { // Set up for i2c usage i2c_reset(&I2C_DGPU, true); @@ -77,28 +32,12 @@ bool dgpu_get_temp(int16_t *const data) { return true; } -uint8_t dgpu_get_fan_duty(void) { - uint8_t duty; +void dgpu_read_temp(void) { if (power_state == POWER_STATE_S0) { if (dgpu_get_temp(&dgpu_temp)) { - duty = fan_duty(&FAN, dgpu_temp); - } else { - duty = PWM_DUTY(50); + return; } - } else { - dgpu_temp = 0; - duty = PWM_DUTY(0); } - if (peci_on && fan_max) { - // Override duty if fans are manually set to maximum - duty = PWM_DUTY(100); - } else { - // Apply heatup and cooldown filters to duty - duty = fan_heatup(&FAN, duty); - duty = fan_cooldown(&FAN, duty); - } - - TRACE("DGPU temp=%d\n", dgpu_temp); - return duty; + dgpu_temp = 0; } diff --git a/src/board/system76/common/fan.c b/src/board/system76/common/fan.c index 1c17535..dfabde8 100644 --- a/src/board/system76/common/fan.c +++ b/src/board/system76/common/fan.c @@ -1,9 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only #include +#include +#include +#include #include +#include #include +bool fan_max = false; + +static uint8_t last_duty_dgpu = 0; +static uint8_t last_duty_peci = 0; + +#define FAN_POINT(T, D) { .temp = (int16_t)(T), .duty = PWM_DUTY(D) } + #if SMOOTH_FANS != 0 #define MAX_JUMP_UP ((MAX_FAN_SPEED - MIN_FAN_SPEED) / (uint8_t)SMOOTH_FANS_UP) #define MAX_JUMP_DOWN ((MAX_FAN_SPEED - MIN_FAN_SPEED) / (uint8_t)SMOOTH_FANS_DOWN) @@ -14,9 +25,83 @@ #define MIN_SPEED_TO_SMOOTH PWM_DUTY(SMOOTH_FANS_MIN) -bool fan_max = false; -uint8_t last_duty_dgpu = 0; -uint8_t last_duty_peci = 0; +// Fan speed is the lowest requested over HEATUP seconds +#ifndef BOARD_HEATUP +#define BOARD_HEATUP 4 +#endif + +static uint8_t FAN1_HEATUP[BOARD_HEATUP] = { 0 }; + +// Fan speed is the highest HEATUP speed over COOLDOWN seconds +#ifndef BOARD_COOLDOWN +#define BOARD_COOLDOWN 10 +#endif + +static uint8_t FAN1_COOLDOWN[BOARD_COOLDOWN] = { 0 }; + +// Fan curve with temperature in degrees C, duty cycle in percent +static struct FanPoint __code FAN1_POINTS[] = { +#ifdef BOARD_FAN_POINTS + BOARD_FAN_POINTS +#else + FAN_POINT(70, 40), + FAN_POINT(75, 50), + FAN_POINT(80, 60), + FAN_POINT(85, 65), + FAN_POINT(90, 65) +#endif +}; + +static struct Fan __code FAN1 = { + .points = FAN1_POINTS, + .points_size = ARRAY_SIZE(FAN1_POINTS), + .heatup = FAN1_HEATUP, + .heatup_size = ARRAY_SIZE(FAN1_HEATUP), + .cooldown = FAN1_COOLDOWN, + .cooldown_size = ARRAY_SIZE(FAN1_COOLDOWN), + .interpolate = SMOOTH_FANS != 0, +}; + +#if CONFIG_HAVE_DGPU + +// Fan speed is the lowest requested over HEATUP seconds +#ifndef BOARD_DGPU_HEATUP +#define BOARD_DGPU_HEATUP 4 +#endif + +static uint8_t FAN2_HEATUP[BOARD_DGPU_HEATUP] = { 0 }; + +// Fan speed is the highest HEATUP speed over COOLDOWN seconds +#ifndef BOARD_DGPU_COOLDOWN +#define BOARD_DGPU_COOLDOWN 10 +#endif + +static uint8_t FAN2_COOLDOWN[BOARD_DGPU_COOLDOWN] = { 0 }; + +// Fan curve with temperature in degrees C, duty cycle in percent +static struct FanPoint __code FAN2_POINTS[] = { +#ifdef BOARD_DGPU_FAN_POINTS + BOARD_DGPU_FAN_POINTS +#else + FAN_POINT(70, 40), + FAN_POINT(75, 50), + FAN_POINT(80, 60), + FAN_POINT(85, 65), + FAN_POINT(90, 65) +#endif +}; + +static struct Fan __code FAN2 = { + .points = FAN2_POINTS, + .points_size = ARRAY_SIZE(FAN2_POINTS), + .heatup = FAN2_HEATUP, + .heatup_size = ARRAY_SIZE(FAN2_HEATUP), + .cooldown = FAN2_COOLDOWN, + .cooldown_size = ARRAY_SIZE(FAN2_COOLDOWN), + .interpolate = SMOOTH_FANS != 0, +}; + +#endif // CONFIG_HAVE_DGPU void fan_reset(void) { // Do not manually set fans to maximum speed @@ -25,7 +110,7 @@ void fan_reset(void) { // Get duty cycle based on temperature, adapted from // https://github.com/pop-os/system76-power/blob/master/src/fan.rs -uint8_t fan_duty(const struct Fan *const fan, int16_t temp) __reentrant { +static uint8_t fan_duty(const struct Fan *const fan, int16_t temp) __reentrant { for (uint8_t i = 0; i < fan->points_size; i++) { const struct FanPoint *cur = &fan->points[i]; @@ -60,66 +145,7 @@ uint8_t fan_duty(const struct Fan *const fan, int16_t temp) __reentrant { return MAX_FAN_SPEED; } -void fan_duty_set(uint8_t peci_fan_duty, uint8_t dgpu_fan_duty) __reentrant { -#if SYNC_FANS != 0 - peci_fan_duty = peci_fan_duty > dgpu_fan_duty ? peci_fan_duty : dgpu_fan_duty; - dgpu_fan_duty = peci_fan_duty > dgpu_fan_duty ? peci_fan_duty : dgpu_fan_duty; -#endif - - // set PECI fan duty - if (peci_fan_duty != DCR2) { - TRACE("PECI fan_duty_raw=%d\n", peci_fan_duty); - last_duty_peci = peci_fan_duty = fan_smooth(last_duty_peci, peci_fan_duty); - DCR2 = fan_max ? MAX_FAN_SPEED : peci_fan_duty; -#if HAVE_CPU_FAN2 - // FIXME: Handle better - DCR3 = fan_max ? MAX_FAN_SPEED : peci_fan_duty; -#endif - TRACE("PECI fan_duty_smoothed=%d\n", peci_fan_duty); - } - - // set dGPU fan duty - if (dgpu_fan_duty != DCR4) { - TRACE("DGPU fan_duty_raw=%d\n", dgpu_fan_duty); - last_duty_dgpu = dgpu_fan_duty = fan_smooth(last_duty_dgpu, dgpu_fan_duty); - DCR4 = fan_max ? MAX_FAN_SPEED : dgpu_fan_duty; - TRACE("DGPU fan_duty_smoothed=%d\n", dgpu_fan_duty); - } -} - -uint8_t fan_heatup(const struct Fan *const fan, uint8_t duty) __reentrant { - uint8_t lowest = duty; - - uint8_t i; - for (i = 0; (i + 1) < fan->heatup_size; i++) { - uint8_t value = fan->heatup[i + 1]; - if (value < lowest) { - lowest = value; - } - fan->heatup[i] = value; - } - fan->heatup[i] = duty; - - return lowest; -} - -uint8_t fan_cooldown(const struct Fan *const fan, uint8_t duty) __reentrant { - uint8_t highest = duty; - - uint8_t i; - for (i = 0; (i + 1) < fan->cooldown_size; i++) { - uint8_t value = fan->cooldown[i + 1]; - if (value > highest) { - highest = value; - } - fan->cooldown[i] = value; - } - fan->cooldown[i] = duty; - - return highest; -} - -uint8_t fan_smooth(uint8_t last_duty, uint8_t duty) __reentrant { +static uint8_t fan_smooth(uint8_t last_duty, uint8_t duty) __reentrant { uint8_t next_duty = duty; // ramping down @@ -150,3 +176,110 @@ uint8_t fan_smooth(uint8_t last_duty, uint8_t duty) __reentrant { return next_duty; } + +static uint8_t fan_heatup(const struct Fan *const fan, uint8_t duty) __reentrant { + uint8_t lowest = duty; + + uint8_t i; + for (i = 0; (i + 1) < fan->heatup_size; i++) { + uint8_t value = fan->heatup[i + 1]; + if (value < lowest) { + lowest = value; + } + fan->heatup[i] = value; + } + fan->heatup[i] = duty; + + return lowest; +} + +static uint8_t fan_cooldown(const struct Fan *const fan, uint8_t duty) __reentrant { + uint8_t highest = duty; + + uint8_t i; + for (i = 0; (i + 1) < fan->cooldown_size; i++) { + uint8_t value = fan->cooldown[i + 1]; + if (value > highest) { + highest = value; + } + fan->cooldown[i] = value; + } + fan->cooldown[i] = duty; + + return highest; +} + +static uint8_t peci_get_fan_duty(void) { + uint8_t duty; + + if (power_state == POWER_STATE_S0) { + duty = fan_duty(&FAN1, peci_temp); + if (fan_max) { + duty = PWM_DUTY(100); + } else { + duty = fan_heatup(&FAN1, duty); + duty = fan_cooldown(&FAN1, duty); + } + } else { + duty = PWM_DUTY(0); + } + + return duty; +} + +#if CONFIG_HAVE_DGPU +static uint8_t dgpu_get_fan_duty(void) { + uint8_t duty; + + if (power_state == POWER_STATE_S0) { + duty = fan_duty(&FAN2, dgpu_temp); + if (fan_max) { + duty = PWM_DUTY(100); + } else { + duty = fan_heatup(&FAN2, duty); + duty = fan_cooldown(&FAN2, duty); + } + } else { + duty = PWM_DUTY(0); + } + + return duty; +} +#else +static uint8_t dgpu_get_fan_duty(void) { + return PWM_DUTY(0); +} +#endif // CONFIG_HAVE_DGPU + +static void fan_duty_set(uint8_t peci_fan_duty, uint8_t dgpu_fan_duty) __reentrant { +#if SYNC_FANS != 0 + peci_fan_duty = peci_fan_duty > dgpu_fan_duty ? peci_fan_duty : dgpu_fan_duty; + dgpu_fan_duty = peci_fan_duty > dgpu_fan_duty ? peci_fan_duty : dgpu_fan_duty; +#endif + + // set PECI fan duty + if (peci_fan_duty != DCR2) { + TRACE("PECI fan_duty_raw=%d\n", peci_fan_duty); + last_duty_peci = peci_fan_duty = fan_smooth(last_duty_peci, peci_fan_duty); + DCR2 = fan_max ? MAX_FAN_SPEED : peci_fan_duty; +#if HAVE_CPU_FAN2 + // FIXME: Handle better + DCR3 = fan_max ? MAX_FAN_SPEED : peci_fan_duty; +#endif + TRACE("PECI fan_duty_smoothed=%d\n", peci_fan_duty); + } + + // set dGPU fan duty + if (dgpu_fan_duty != DCR4) { + TRACE("DGPU fan_duty_raw=%d\n", dgpu_fan_duty); + last_duty_dgpu = dgpu_fan_duty = fan_smooth(last_duty_dgpu, dgpu_fan_duty); + DCR4 = fan_max ? MAX_FAN_SPEED : dgpu_fan_duty; + TRACE("DGPU fan_duty_smoothed=%d\n", dgpu_fan_duty); + } +} + +void fan_update_duty(void) { + uint8_t fan1_duty = peci_get_fan_duty(); + uint8_t fan2_duty = dgpu_get_fan_duty(); + fan_duty_set(fan1_duty, fan2_duty); +} diff --git a/src/board/system76/common/include/board/dgpu.h b/src/board/system76/common/include/board/dgpu.h index 2129583..cffa2fb 100644 --- a/src/board/system76/common/include/board/dgpu.h +++ b/src/board/system76/common/include/board/dgpu.h @@ -12,7 +12,7 @@ extern int16_t dgpu_temp; void dgpu_init(void); bool dgpu_get_temp(int16_t *const data); -uint8_t dgpu_get_fan_duty(void); +void dgpu_read_temp(void); #else @@ -23,9 +23,7 @@ static inline bool dgpu_get_temp(int16_t *const data) { return true; } -static inline uint8_t dgpu_get_fan_duty(void) { - return 0; -} +static inline void dgpu_read_temp(void) {} #endif // CONFIG_HAVE_DGPU diff --git a/src/board/system76/common/include/board/fan.h b/src/board/system76/common/include/board/fan.h index c30efe5..184ab21 100644 --- a/src/board/system76/common/include/board/fan.h +++ b/src/board/system76/common/include/board/fan.h @@ -49,11 +49,6 @@ struct Fan { extern bool fan_max; void fan_reset(void); - -uint8_t fan_duty(const struct Fan *const fan, int16_t temp) __reentrant; -void fan_duty_set(uint8_t peci_fan_duty, uint8_t dgpu_fan_duty) __reentrant; -uint8_t fan_heatup(const struct Fan *const fan, uint8_t duty) __reentrant; -uint8_t fan_cooldown(const struct Fan *const fan, uint8_t duty) __reentrant; -uint8_t fan_smooth(uint8_t last_duty, uint8_t duty) __reentrant; +void fan_update_duty(void); #endif // _BOARD_FAN_H diff --git a/src/board/system76/common/include/board/peci.h b/src/board/system76/common/include/board/peci.h index e5b5cbf..e913872 100644 --- a/src/board/system76/common/include/board/peci.h +++ b/src/board/system76/common/include/board/peci.h @@ -13,6 +13,6 @@ extern int16_t peci_temp; void peci_init(void); bool peci_available(void); int16_t peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data); -uint8_t peci_get_fan_duty(void); +void peci_read_temp(void); #endif // _BOARD_PECI_H diff --git a/src/board/system76/common/main.c b/src/board/system76/common/main.c index b8f5f10..073be24 100644 --- a/src/board/system76/common/main.c +++ b/src/board/system76/common/main.c @@ -133,8 +133,12 @@ void main(void) { if ((time - last_time_fan) >= fan_interval) { last_time_fan = time; + // Read thermal data + peci_read_temp(); + dgpu_read_temp(); + // Update fan speeds - fan_duty_set(peci_get_fan_duty(), dgpu_get_fan_duty()); + fan_update_duty(); // NOTE: These values are reported to ACPI. Update them at the // same interval as the fan duties. diff --git a/src/board/system76/common/peci.c b/src/board/system76/common/peci.c index f22e045..8102703 100644 --- a/src/board/system76/common/peci.c +++ b/src/board/system76/common/peci.c @@ -1,5 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only +// PECI information can be found here: +// https://www.intel.com/content/dam/www/public/us/en/documents/design-guides/core-i7-lga-2011-guide.pdf + #include #include #include @@ -11,56 +14,15 @@ #include #include -// Fan speed is the lowest requested over HEATUP seconds -#ifndef BOARD_HEATUP -#define BOARD_HEATUP 4 -#endif - -static uint8_t FAN_HEATUP[BOARD_HEATUP] = { 0 }; - -// Fan speed is the highest HEATUP speed over COOLDOWN seconds -#ifndef BOARD_COOLDOWN -#define BOARD_COOLDOWN 10 -#endif - -static uint8_t FAN_COOLDOWN[BOARD_COOLDOWN] = { 0 }; - bool peci_on = false; int16_t peci_temp = 0; -#define PECI_TEMP(X) ((int16_t)(X)) - // Tjunction = 100C for i7-8565U (and probably the same for all WHL-U) -#define T_JUNCTION PECI_TEMP(100) +#define T_JUNCTION ((int16_t)100) // Maximum OOB channel response time in ms #define PECI_ESPI_TIMEOUT 10 -#define FAN_POINT(T, D) { .temp = PECI_TEMP(T), .duty = PWM_DUTY(D) } - -// Fan curve with temperature in degrees C, duty cycle in percent -static struct FanPoint __code FAN_POINTS[] = { -#ifdef BOARD_FAN_POINTS - BOARD_FAN_POINTS -#else - FAN_POINT(70, 40), - FAN_POINT(75, 50), - FAN_POINT(80, 60), - FAN_POINT(85, 65), - FAN_POINT(90, 65) -#endif -}; - -static struct Fan __code FAN = { - .points = FAN_POINTS, - .points_size = ARRAY_SIZE(FAN_POINTS), - .heatup = FAN_HEATUP, - .heatup_size = ARRAY_SIZE(FAN_HEATUP), - .cooldown = FAN_COOLDOWN, - .cooldown_size = ARRAY_SIZE(FAN_COOLDOWN), - .interpolate = SMOOTH_FANS != 0, -}; - // Returns true if peci is available bool peci_available(void) { // Ensure power state is up to date @@ -419,36 +381,13 @@ int16_t peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data) { #endif // CONFIG_PECI_OVER_ESPI -// PECI information can be found here: -// https://www.intel.com/content/dam/www/public/us/en/documents/design-guides/core-i7-lga-2011-guide.pdf -uint8_t peci_get_fan_duty(void) { - uint8_t duty; - +void peci_read_temp(void) { peci_on = peci_available(); if (peci_on) { if (peci_get_temp(&peci_temp)) { - // Use result if finished successfully - duty = fan_duty(&FAN, peci_temp); - } else { - // Default to 50% if there is an error - peci_temp = 0; - duty = PWM_DUTY(50); + return; } - } else { - // Turn fan off if not in S0 state - peci_temp = 0; - duty = PWM_DUTY(0); } - if (peci_on && fan_max) { - // Override duty if fans are manually set to maximum - duty = PWM_DUTY(100); - } else { - // Apply heatup and cooldown filters to duty - duty = fan_heatup(&FAN, duty); - duty = fan_cooldown(&FAN, duty); - } - - TRACE("PECI temp=%d\n", peci_temp); - return duty; + peci_temp = 0; } diff --git a/src/board/system76/common/pwm.c b/src/board/system76/common/pwm.c index 330cad9..2c49660 100644 --- a/src/board/system76/common/pwm.c +++ b/src/board/system76/common/pwm.c @@ -35,11 +35,14 @@ void pwm_init(void) { // Set cycle time to 255 + 1 CTR0 = 255; - // Turn off CPU fan (temperature control in peci_get_fan_duty) + // Turn off fans DCR2 = 0; #if HAVE_CPU_FAN2 DCR3 = 0; #endif +#if CONFIG_HAVE_DGPU + DCR4 = 0; +#endif #if CONFIG_EC_ITE_IT5570E || CONFIG_EC_ITE_IT5571E // Reload counters when they reach 0 instead of immediately