diff --git a/power.sh b/power.sh index f5e7418..3029913 100755 --- a/power.sh +++ b/power.sh @@ -2,13 +2,10 @@ set -e -./ectool.sh info -sudo modprobe msr - -header=1 -if [ -e power.csv ] +has_ec=0 +if ./ectool.sh info 2> /dev/null then - header=0 + has_ec=1 fi has_dgpu=0 @@ -17,6 +14,14 @@ then has_dgpu=1 fi +sudo modprobe msr + +header=1 +if [ -e power.csv ] +then + header=0 +fi + while true do if [ "${header}" == "1" ] @@ -29,12 +34,18 @@ do F="${F}\tCPU C" F="${F}\tCPU TCC" F="${F}\tCPU TJM" - F="${F}\tCPU FAN" + if [ "${has_ec}" == "1" ] + then + F="${F}\tCPU FAN" + fi if [ "${has_dgpu}" == "1" ] then F="${F}\tGPU W" F="${F}\tGPU C" - F="${F}\tGPU FAN" + if [ "${has_ec}" == "1" ] + then + F="${F}\tGPU FAN" + fi fi else F="$(date "+%T")" @@ -71,9 +82,12 @@ do F="${F}\t$(printf "%d" "${TCC}")" F="${F}\t$(printf "%d" "${TJMAX}")" - D="$(sudo tool/target/release/system76_ectool fan 0)" - P="$(echo "(${D} * 100)/255" | bc -lq)" - F="${F}\t$(printf "%.0f" "${P}")" + if [ "${has_ec}" == "1" ] + then + D="$(sudo tool/target/release/system76_ectool fan 0)" + P="$(echo "(${D} * 100)/255" | bc -lq)" + F="${F}\t$(printf "%.0f" "${P}")" + fi if [ "${has_dgpu}" == "1" ] then @@ -83,9 +97,12 @@ do DGPU_T="$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader)" F="${F}\t${DGPU_T}" - D="$(sudo tool/target/release/system76_ectool fan 1)" - P="$(echo "(${D} * 100)/255" | bc -lq)" - F="${F}\t$(printf "%.0f" "${P}")" + if [ "${has_ec}" == "1" ] + then + D="$(sudo tool/target/release/system76_ectool fan 1)" + P="$(echo "(${D} * 100)/255" | bc -lq)" + F="${F}\t$(printf "%.0f" "${P}")" + fi fi fi diff --git a/src/board/system76/addw2/board.c b/src/board/system76/addw2/board.c index 5c3375d..a99e60a 100644 --- a/src/board/system76/addw2/board.c +++ b/src/board/system76/addw2/board.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -25,10 +26,36 @@ void board_init(void) { dgpu_init(); } +// Set PL4 using PECI +static int set_power_limit(uint8_t watts) { + return peci_wr_pkg_config( + 60, // index + 0, // param + ((uint32_t)watts) * 8 + ); +} + void board_event(void) { + bool acin = !gpio_get(&ACIN_N); + gpio_set(&AC_PRESENT, acin); + + static uint8_t last_power_limit = 0; + if (power_state == POWER_STATE_S0) { + uint8_t power_limit = acin ? POWER_LIMIT_AC : POWER_LIMIT_DC; + if (power_limit != last_power_limit) { + int res = set_power_limit(power_limit); + DEBUG("set_power_limit %d = %d\n", power_limit, res); + if (res >= 0) { + last_power_limit = power_limit; + } else { + ERROR("set_power_limit failed: %X\n", -res); + } + } + } else { + last_power_limit = 0; + } + if (main_cycle == 0) { - bool acin = !gpio_get(&ACIN_N); - gpio_set(&AC_PRESENT, acin); if (acin) { // Discharging (no AC adapter) gpio_set(&LED_BAT_CHG, false); diff --git a/src/board/system76/addw2/board.mk b/src/board/system76/addw2/board.mk index 96372b4..fe9e61f 100644 --- a/src/board/system76/addw2/board.mk +++ b/src/board/system76/addw2/board.mk @@ -31,6 +31,11 @@ CFLAGS+=\ -DCHARGER_CHARGE_VOLTAGE=12600 \ -DCHARGER_INPUT_CURRENT=11800 +# Set CPU power limits in watts +CFLAGS+=\ + -DPOWER_LIMIT_AC=180 \ + -DPOWER_LIMIT_DC=28 + # Enable debug logging over keyboard parallel port #CFLAGS+=-DPARPORT_DEBUG diff --git a/src/board/system76/addw2/dgpu.c b/src/board/system76/addw2/dgpu.c index bc033a7..916c748 100644 --- a/src/board/system76/addw2/dgpu.c +++ b/src/board/system76/addw2/dgpu.c @@ -116,7 +116,7 @@ void dgpu_init(void) { } void dgpu_event(void) { - if (power_state == POWER_STATE_S0 && gpio_get(&DGPU_PWR_EN)) { + 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); diff --git a/src/board/system76/addw2/gpio.c b/src/board/system76/addw2/gpio.c index a76073a..eac9c04 100644 --- a/src/board/system76/addw2/gpio.c +++ b/src/board/system76/addw2/gpio.c @@ -11,6 +11,7 @@ struct Gpio __code DD_ON = GPIO(E, 4); struct Gpio __code DGPU_PWR_EN = GPIO(H, 4); struct Gpio __code EC_EN = GPIO(B, 6); // renamed to SUSBC_EN struct Gpio __code EC_RSMRST_N = GPIO(E, 5); +struct Gpio __code GC6_FB_EN = GPIO(H, 3); struct Gpio __code LED_ACIN = GPIO(C, 7); struct Gpio __code LED_AIRPLANE_N = GPIO(H, 7); struct Gpio __code LED_CAP_N = GPIO(J, 2); diff --git a/src/board/system76/addw2/include/board/gpio.h b/src/board/system76/addw2/include/board/gpio.h index 5c84811..4fb84b1 100644 --- a/src/board/system76/addw2/include/board/gpio.h +++ b/src/board/system76/addw2/include/board/gpio.h @@ -22,6 +22,7 @@ extern struct Gpio __code DD_ON; extern struct Gpio __code DGPU_PWR_EN; extern struct Gpio __code EC_EN; // renamed to SUSBC_EN extern struct Gpio __code EC_RSMRST_N; +extern struct Gpio __code GC6_FB_EN; extern struct Gpio __code LED_ACIN; extern struct Gpio __code LED_AIRPLANE_N; extern struct Gpio __code LED_CAP_N; diff --git a/src/board/system76/common/include/board/peci.h b/src/board/system76/common/include/board/peci.h index c447300..9e7015c 100644 --- a/src/board/system76/common/include/board/peci.h +++ b/src/board/system76/common/include/board/peci.h @@ -10,6 +10,7 @@ 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); void peci_event(void); #endif // _BOARD_PECI_H diff --git a/src/board/system76/common/peci.c b/src/board/system76/common/peci.c index 04d9a66..8211713 100644 --- a/src/board/system76/common/peci.c +++ b/src/board/system76/common/peci.c @@ -123,6 +123,57 @@ void peci_init(void) { PADCTLR = 0x02; } +// Returns positive completion code on success, negative completion code or +// negative (0x1000 | status register) on PECI hardware error +int peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data) { + // Wait for completion + while (HOSTAR & 1) {} + // Clear status + HOSTAR = HOSTAR; + + // Enable PECI, clearing data fifo's, enable AW_FCS + HOCTLR = (1 << 5) | (1 << 3) | (1 << 1); + // Set address to default + HOTRADDR = 0x30; + // Set write length + HOWRLR = 10; + // Set read length + HORDLR = 1; + // Set command + HOCMDR = 0xA5; + + // Write host ID + HOWRDR = 0; + // Write index + HOWRDR = index; + // Write param + HOWRDR = (uint8_t)param; + HOWRDR = (uint8_t)(param >> 8); + // Write data + HOWRDR = (uint8_t)data; + HOWRDR = (uint8_t)(data >> 8); + HOWRDR = (uint8_t)(data >> 16); + HOWRDR = (uint8_t)(data >> 24); + + // Start transaction + HOCTLR |= 1; + + // Wait for completion + while (HOSTAR & 1) {} + + int status = (int)HOSTAR; + if (status & (1 << 1)) { + int cc = (int)HORDDR; + if (cc & 0x80) { + return -cc; + } else { + return cc; + } + } else { + return -(0x1000 | status); + } +} + // 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) { if (power_state == POWER_STATE_S0) { diff --git a/src/board/system76/gaze15/board.c b/src/board/system76/gaze15/board.c index 35c3e08..d0b4df5 100644 --- a/src/board/system76/gaze15/board.c +++ b/src/board/system76/gaze15/board.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -26,10 +27,36 @@ void board_init(void) { dgpu_init(); } +// Set PL4 using PECI +static int set_power_limit(uint8_t watts) { + return peci_wr_pkg_config( + 60, // index + 0, // param + ((uint32_t)watts) * 8 + ); +} + void board_event(void) { + bool acin = !gpio_get(&ACIN_N); + gpio_set(&AC_PRESENT, acin); + + static uint8_t last_power_limit = 0; + if (power_state == POWER_STATE_S0) { + uint8_t power_limit = acin ? POWER_LIMIT_AC : POWER_LIMIT_DC; + if (power_limit != last_power_limit) { + int res = set_power_limit(power_limit); + DEBUG("set_power_limit %d = %d\n", power_limit, res); + if (res >= 0) { + last_power_limit = power_limit; + } else { + ERROR("set_power_limit failed: %X\n", -res); + } + } + } else { + last_power_limit = 0; + } + if (main_cycle == 0) { - bool acin = !gpio_get(&ACIN_N); - gpio_set(&AC_PRESENT, acin); if (acin) { // Discharging (no AC adapter) gpio_set(&LED_BAT_CHG, false); diff --git a/src/board/system76/gaze15/board.mk b/src/board/system76/gaze15/board.mk index d92fc57..b275aa2 100644 --- a/src/board/system76/gaze15/board.mk +++ b/src/board/system76/gaze15/board.mk @@ -31,6 +31,11 @@ CFLAGS+=\ -DCHARGER_CHARGE_VOLTAGE=16800 \ -DCHARGER_INPUT_CURRENT=9230 +# Set CPU power limits in watts +CFLAGS+=\ + -DPOWER_LIMIT_AC=180 \ + -DPOWER_LIMIT_DC=28 + # Enable debug logging over keyboard parallel port #CFLAGS+=-DPARPORT_DEBUG diff --git a/src/board/system76/gaze15/dgpu.c b/src/board/system76/gaze15/dgpu.c index bc033a7..916c748 100644 --- a/src/board/system76/gaze15/dgpu.c +++ b/src/board/system76/gaze15/dgpu.c @@ -116,7 +116,7 @@ void dgpu_init(void) { } void dgpu_event(void) { - if (power_state == POWER_STATE_S0 && gpio_get(&DGPU_PWR_EN)) { + 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); diff --git a/src/board/system76/gaze15/gpio.c b/src/board/system76/gaze15/gpio.c index 2fcb96c..ccefebd 100644 --- a/src/board/system76/gaze15/gpio.c +++ b/src/board/system76/gaze15/gpio.c @@ -11,6 +11,7 @@ struct Gpio __code CCD_EN = GPIO(D, 1); struct Gpio __code DD_ON = GPIO(E, 4); struct Gpio __code DGPU_PWR_EN = GPIO(J, 2); struct Gpio __code EC_RSMRST_N = GPIO(E, 5); +struct Gpio __code GC6_FB_EN = GPIO(J, 3); struct Gpio __code LED_ACIN = GPIO(C, 7); struct Gpio __code LED_AIRPLANE_N = GPIO(H, 7); struct Gpio __code LED_BAT_CHG = GPIO(H, 5); diff --git a/src/board/system76/gaze15/include/board/gpio.h b/src/board/system76/gaze15/include/board/gpio.h index cf568aa..784f910 100644 --- a/src/board/system76/gaze15/include/board/gpio.h +++ b/src/board/system76/gaze15/include/board/gpio.h @@ -23,6 +23,7 @@ extern struct Gpio __code DD_ON; extern struct Gpio __code DGPU_PWR_EN; #define HAVE_EC_EN 0 extern struct Gpio __code EC_RSMRST_N; +extern struct Gpio __code GC6_FB_EN; extern struct Gpio __code LED_ACIN; extern struct Gpio __code LED_AIRPLANE_N; extern struct Gpio __code LED_BAT_CHG; diff --git a/src/board/system76/oryp6/board.c b/src/board/system76/oryp6/board.c index 8da9045..e4182d2 100644 --- a/src/board/system76/oryp6/board.c +++ b/src/board/system76/oryp6/board.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -28,10 +29,36 @@ void board_init(void) { dgpu_init(); } +// Set PL4 using PECI +static int set_power_limit(uint8_t watts) { + return peci_wr_pkg_config( + 60, // index + 0, // param + ((uint32_t)watts) * 8 + ); +} + void board_event(void) { + bool acin = !gpio_get(&ACIN_N); + gpio_set(&AC_PRESENT, acin); + + static uint8_t last_power_limit = 0; + if (power_state == POWER_STATE_S0) { + uint8_t power_limit = acin ? POWER_LIMIT_AC : POWER_LIMIT_DC; + if (power_limit != last_power_limit) { + int res = set_power_limit(power_limit); + DEBUG("set_power_limit %d = %d\n", power_limit, res); + if (res >= 0) { + last_power_limit = power_limit; + } else { + ERROR("set_power_limit failed: %X\n", -res); + } + } + } else { + last_power_limit = 0; + } + if (main_cycle == 0) { - bool acin = !gpio_get(&ACIN_N); - gpio_set(&AC_PRESENT, acin); if (acin) { // Discharging (no AC adapter) gpio_set(&LED_BAT_CHG, false); diff --git a/src/board/system76/oryp6/board.mk b/src/board/system76/oryp6/board.mk index 0fa8921..970d02e 100644 --- a/src/board/system76/oryp6/board.mk +++ b/src/board/system76/oryp6/board.mk @@ -31,6 +31,11 @@ CFLAGS+=\ -DCHARGER_CHARGE_VOLTAGE=16800 \ -DCHARGER_INPUT_CURRENT=13050 +# Set CPU power limits in watts +CFLAGS+=\ + -DPOWER_LIMIT_AC=180 \ + -DPOWER_LIMIT_DC=28 + # Enable debug logging over keyboard parallel port #CFLAGS+=-DPARPORT_DEBUG