diff --git a/src/board/system76/common/dgpu.c b/src/board/system76/common/dgpu.c index 9412e93..2ae1f55 100644 --- a/src/board/system76/common/dgpu.c +++ b/src/board/system76/common/dgpu.c @@ -13,24 +13,20 @@ #include // Fan speed is the lowest requested over HEATUP seconds -#ifdef BOARD_DGPU_HEATUP - #define HEATUP BOARD_DGPU_HEATUP -#else - #define HEATUP 10 +#ifndef BOARD_DGPU_HEATUP + #define BOARD_DGPU_HEATUP 10 #endif +static uint8_t FAN_HEATUP[BOARD_DGPU_HEATUP] = { 0 }; + // Fan speed is the highest HEATUP speed over COOLDOWN seconds -#ifdef BOARD_DGPU_COOLDOWN - #define COOLDOWN BOARD_DGPU_COOLDOWN -#else - #define COOLDOWN 10 +#ifndef BOARD_DGPU_COOLDOWN + #define BOARD_DGPU_COOLDOWN 10 #endif -// Interpolate duty cycle -#define INTERPOLATE 0 +static uint8_t FAN_COOLDOWN[BOARD_DGPU_COOLDOWN] = { 0 }; int16_t dgpu_temp = 0; -uint8_t dgpu_duty = 0; #define DGPU_TEMP(X) ((int16_t)(X)) @@ -49,76 +45,15 @@ static struct FanPoint __code FAN_POINTS[] = { #endif }; -// Get duty cycle based on temperature, adapted from -// https://github.com/pop-os/system76-power/blob/master/src/fan.rs -static uint8_t fan_duty(int16_t temp) { - for (int i = 0; i < ARRAY_SIZE(FAN_POINTS); i++) { - const struct FanPoint * cur = &FAN_POINTS[i]; - - // If exactly the current temp, return the current duty - if (temp == cur->temp) { - return cur->duty; - } else if (temp < cur->temp) { - // If lower than first temp, return 0% - if (i == 0) { - return PWM_DUTY(0); - } else { - const struct FanPoint * prev = &FAN_POINTS[i - 1]; - -#if INTERPOLATE - // If in between current temp and previous temp, interpolate - if (temp > prev->temp) { - int16_t dtemp = (cur->temp - prev->temp); - int16_t dduty = ((int16_t)cur->duty) - ((int16_t)prev->duty); - return (uint8_t)( - ((int16_t)prev->duty) + - ((temp - prev->temp) * dduty) / dtemp - ); - } -#else // INTERPOLATE - return prev->duty; -#endif // INTERPOLATE - } - } - } - - // If no point is found, return 100% - return PWM_DUTY(100); -} - -static uint8_t fan_heatup(uint8_t duty) { - static uint8_t history[HEATUP] = { 0 }; - uint8_t lowest = duty; - - int i; - for (i = 0; (i + 1) < ARRAY_SIZE(history); i++) { - uint8_t value = history[i + 1]; - if (value < lowest) { - lowest = value; - } - history[i] = value; - } - history[i] = duty; - - return lowest; -} - -static uint8_t fan_cooldown(uint8_t duty) { - static uint8_t history[COOLDOWN] = { 0 }; - uint8_t highest = duty; - - int i; - for (i = 0; (i + 1) < ARRAY_SIZE(history); i++) { - uint8_t value = history[i + 1]; - if (value > highest) { - highest = value; - } - history[i] = value; - } - history[i] = duty; - - return highest; -} +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 = false, +}; void dgpu_init(void) { // Set up for i2c usage @@ -126,34 +61,38 @@ void dgpu_init(void) { } void dgpu_event(void) { + uint8_t duty; if (power_state == POWER_STATE_S0 && gpio_get(&DGPU_PWR_EN) && !gpio_get(&GC6_FB_EN)) { // Use I2CS if in S0 state int8_t rlts; int res = i2c_get(&I2C_DGPU, 0x4F, 0x00, &rlts, 1); if (res == 1) { dgpu_temp = (int16_t)rlts; - dgpu_duty = fan_duty(dgpu_temp); + duty = fan_duty(&FAN, dgpu_temp); } else { DEBUG("DGPU temp error: %d\n", res); // Default to 50% if there is an error dgpu_temp = 0; - dgpu_duty = PWM_DUTY(50); + duty = PWM_DUTY(50); } } else { // Turn fan off if not in S0 state or GPU power not on dgpu_temp = 0; - dgpu_duty = PWM_DUTY(0); + duty = PWM_DUTY(0); } - uint8_t heatup_duty = fan_heatup(dgpu_duty); - uint8_t cooldown_duty = fan_cooldown(heatup_duty); if (fan_max) { // Override duty if fans are manually set to maximum - cooldown_duty = 0xFF; + duty = PWM_DUTY(100); + } else { + // Apply heatup and cooldown filters to duty + duty = fan_heatup(&FAN, duty); + duty = fan_cooldown(&FAN, duty); } - if (cooldown_duty != DCR4) { - DCR4 = cooldown_duty; - DEBUG("DGPU temp=%d = %d\n", dgpu_temp, cooldown_duty); + + if (duty != DCR4) { + DCR4 = duty; + DEBUG("DGPU temp=%d = %d\n", dgpu_temp, duty); } } diff --git a/src/board/system76/common/fan.c b/src/board/system76/common/fan.c index 7018662..8de859b 100644 --- a/src/board/system76/common/fan.c +++ b/src/board/system76/common/fan.c @@ -8,3 +8,72 @@ void fan_reset(void) { // Do not manually set fans to maximum speed fan_max = false; } + +// 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 * fan, int16_t temp) __reentrant { + for (int i = 0; i < fan->points_size; i++) { + const struct FanPoint * cur = &fan->points[i]; + + // If exactly the current temp, return the current duty + if (temp == cur->temp) { + return cur->duty; + } else if (temp < cur->temp) { + // If lower than first temp, return 0% + if (i == 0) { + return PWM_DUTY(0); + } else { + const struct FanPoint * prev = &fan->points[i - 1]; + + if (fan->interpolate) { + // If in between current temp and previous temp, interpolate + if (temp > prev->temp) { + int16_t dtemp = (cur->temp - prev->temp); + int16_t dduty = ((int16_t)cur->duty) - ((int16_t)prev->duty); + return (uint8_t)( + ((int16_t)prev->duty) + + ((temp - prev->temp) * dduty) / dtemp + ); + } + } else { + return prev->duty; + } + } + } + } + + // If no point is found, return 100% + return PWM_DUTY(100); +} + +uint8_t fan_heatup(const struct Fan * fan, uint8_t duty) __reentrant { + uint8_t lowest = duty; + + int 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 * fan, uint8_t duty) __reentrant { + uint8_t highest = duty; + + int 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; +} diff --git a/src/board/system76/common/include/board/dgpu.h b/src/board/system76/common/include/board/dgpu.h index b68fa31..d206388 100644 --- a/src/board/system76/common/include/board/dgpu.h +++ b/src/board/system76/common/include/board/dgpu.h @@ -11,7 +11,6 @@ #if HAVE_DGPU extern int16_t dgpu_temp; - extern uint8_t dgpu_duty; #endif // HAVE_DGPU void dgpu_init(void); diff --git a/src/board/system76/common/include/board/fan.h b/src/board/system76/common/include/board/fan.h index c37d1c9..9db05c8 100644 --- a/src/board/system76/common/include/board/fan.h +++ b/src/board/system76/common/include/board/fan.h @@ -6,15 +6,29 @@ #include #include +#define PWM_DUTY(X) ((uint8_t)(((((uint16_t)(X)) * 255) + 99) / 100)) + struct FanPoint { int16_t temp; uint8_t duty; }; -#define PWM_DUTY(X) ((uint8_t)(((((uint16_t)(X)) * 255) + 99) / 100)) +struct Fan { + const struct FanPoint * points; + uint8_t points_size; + uint8_t * heatup; + uint8_t heatup_size; + uint8_t * cooldown; + uint8_t cooldown_size; + bool interpolate; +}; extern bool fan_max; void fan_reset(void); +uint8_t fan_duty(const struct Fan * fan, int16_t temp) __reentrant; +uint8_t fan_heatup(const struct Fan * fan, uint8_t duty) __reentrant; +uint8_t fan_cooldown(const struct Fan * fan, uint8_t duty) __reentrant; + #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 745ef69..c968245 100644 --- a/src/board/system76/common/include/board/peci.h +++ b/src/board/system76/common/include/board/peci.h @@ -5,11 +5,7 @@ #include -extern int16_t peci_offset; extern int16_t peci_temp; -extern uint8_t peci_duty; -extern uint8_t peci_tcontrol; -extern uint8_t peci_tjmax; void peci_init(void); int peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data); diff --git a/src/board/system76/common/peci.c b/src/board/system76/common/peci.c index 9f41268..010403f 100644 --- a/src/board/system76/common/peci.c +++ b/src/board/system76/common/peci.c @@ -9,28 +9,23 @@ #include // Fan speed is the lowest requested over HEATUP seconds -#ifdef BOARD_HEATUP - #define HEATUP BOARD_HEATUP -#else - #define HEATUP 10 +#ifndef BOARD_HEATUP + #define BOARD_HEATUP 10 #endif +static uint8_t FAN_HEATUP[BOARD_HEATUP] = { 0 }; + // Fan speed is the highest HEATUP speed over COOLDOWN seconds -#ifdef BOARD_COOLDOWN - #define COOLDOWN BOARD_COOLDOWN -#else - #define COOLDOWN 10 +#ifndef BOARD_COOLDOWN + #define BOARD_COOLDOWN 10 #endif -// Interpolate duty cycle -#define INTERPOLATE 0 +static uint8_t FAN_COOLDOWN[BOARD_COOLDOWN] = { 0 }; // Tjunction = 100C for i7-8565U (and probably the same for all WHL-U) #define T_JUNCTION 100 -int16_t peci_offset = 0; int16_t peci_temp = 0; -uint8_t peci_duty = 0; #define PECI_TEMP(X) (((int16_t)(X)) << 6) @@ -49,76 +44,15 @@ static struct FanPoint __code FAN_POINTS[] = { #endif }; -// Get duty cycle based on temperature, adapted from -// https://github.com/pop-os/system76-power/blob/master/src/fan.rs -static uint8_t fan_duty(int16_t temp) { - for (int i = 0; i < ARRAY_SIZE(FAN_POINTS); i++) { - const struct FanPoint * cur = &FAN_POINTS[i]; - - // If exactly the current temp, return the current duty - if (temp == cur->temp) { - return cur->duty; - } else if (temp < cur->temp) { - // If lower than first temp, return 0% - if (i == 0) { - return PWM_DUTY(0); - } else { - const struct FanPoint * prev = &FAN_POINTS[i - 1]; - -#if INTERPOLATE - // If in between current temp and previous temp, interpolate - if (temp > prev->temp) { - int16_t dtemp = (cur->temp - prev->temp); - int16_t dduty = ((int16_t)cur->duty) - ((int16_t)prev->duty); - return (uint8_t)( - ((int16_t)prev->duty) + - ((temp - prev->temp) * dduty) / dtemp - ); - } -#else // INTERPOLATE - return prev->duty; -#endif // INTERPOLATE - } - } - } - - // If no point is found, return 100% - return PWM_DUTY(100); -} - -static uint8_t fan_heatup(uint8_t duty) { - static uint8_t history[HEATUP] = { 0 }; - uint8_t lowest = duty; - - int i; - for (i = 0; (i + 1) < ARRAY_SIZE(history); i++) { - uint8_t value = history[i + 1]; - if (value < lowest) { - lowest = value; - } - history[i] = value; - } - history[i] = duty; - - return lowest; -} - -static uint8_t fan_cooldown(uint8_t duty) { - static uint8_t history[COOLDOWN] = { 0 }; - uint8_t highest = duty; - - int i; - for (i = 0; (i + 1) < ARRAY_SIZE(history); i++) { - uint8_t value = history[i + 1]; - if (value > highest) { - highest = value; - } - history[i] = value; - } - history[i] = duty; - - return highest; -} +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 = false, +}; void peci_init(void) { // Allow PECI pin to be used @@ -183,6 +117,7 @@ int peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data) { // 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 void peci_event(void) { + uint8_t duty; if (power_state == POWER_STATE_S0) { // Use PECI if in S0 state @@ -211,31 +146,32 @@ void peci_event(void) { // Use result if finished successfully uint8_t low = HORDDR; uint8_t high = HORDDR; - peci_offset = ((int16_t)high << 8) | (int16_t)low; + uint16_t peci_offset = ((int16_t)high << 8) | (int16_t)low; peci_temp = PECI_TEMP(T_JUNCTION) + peci_offset; - peci_duty = fan_duty(peci_temp); + duty = fan_duty(&FAN, peci_temp); } else { // Default to 50% if there is an error - peci_offset = 0; peci_temp = 0; - peci_duty = PWM_DUTY(50); + duty = PWM_DUTY(50); } } else { // Turn fan off if not in S0 state - peci_offset = 0; peci_temp = 0; - peci_duty = PWM_DUTY(0); + duty = PWM_DUTY(0); } - uint8_t heatup_duty = fan_heatup(peci_duty); - uint8_t cooldown_duty = fan_cooldown(heatup_duty); if (fan_max) { // Override duty if fans are manually set to maximum - cooldown_duty = 0xFF; + duty = PWM_DUTY(100); + } else { + // Apply heatup and cooldown filters to duty + duty = fan_heatup(&FAN, duty); + duty = fan_cooldown(&FAN, duty); } - if (cooldown_duty != DCR2) { - DCR2 = cooldown_duty; - DEBUG("PECI offset=%d, temp=%d = %d\n", peci_offset, peci_temp, cooldown_duty); + + if (duty != DCR2) { + DCR2 = duty; + DEBUG("PECI temp=%d = %d\n", peci_temp, duty); } }