diff --git a/src/arch/8051/include/arch/time.h b/src/arch/8051/include/arch/time.h index 9fb414cbf..9007cd56e 100644 --- a/src/arch/8051/include/arch/time.h +++ b/src/arch/8051/include/arch/time.h @@ -3,9 +3,13 @@ #ifndef _ARCH_TIME_H #define _ARCH_TIME_H +#include #include void time_init(void); uint32_t time_get(void); +// Set every 50 ms by timer_0; cleared by the main-loop periodic handler +extern volatile bool timer_50ms_pending; + #endif // _ARCH_TIME_H diff --git a/src/arch/8051/time.c b/src/arch/8051/time.c index 3b4192711..89322dd32 100644 --- a/src/arch/8051/time.c +++ b/src/arch/8051/time.c @@ -8,12 +8,25 @@ static volatile uint32_t time_overflows = 0; +// Set every 50 ms by timer_0; cleared by main loop periodic handler. +// 50 ms is the base for all periodic intervals: +// 2 ticks = 100 ms (power/usbpd poll) +// 5 ticks = 250 ms (smooth fan) +// 20 ticks = 1000 ms (fan normal + battery) +volatile bool timer_50ms_pending = false; + void timer_0(void) __interrupt(1) { // Stop timer TR0 = 0; time_overflows++; + static uint8_t ticks = 0; + if (++ticks >= 50) { + ticks = 0; + timer_50ms_pending = true; + } + // Start timer TH0 = 0xFD; TL0 = 0x01; diff --git a/src/board/novacustom/v540tnx/include/board/gpio.h b/src/board/novacustom/v540tnx/include/board/gpio.h index b6a936feb..98b8e4581 100644 --- a/src/board/novacustom/v540tnx/include/board/gpio.h +++ b/src/board/novacustom/v540tnx/include/board/gpio.h @@ -22,6 +22,7 @@ extern struct Gpio __code EC_EN; extern struct Gpio __code EC_RSMRST_N; extern struct Gpio __code GC6_FB_EN; extern struct Gpio __code H_PROCHOT_EC; +#define HAVE_JACK_IN_N 1 extern struct Gpio __code JACK_IN_N; extern struct Gpio __code LAN_WAKEUP_N; extern struct Gpio __code LED_ACIN; @@ -40,6 +41,9 @@ extern struct Gpio __code PD_IRQ; extern struct Gpio __code PWR_BTN_N; extern struct Gpio __code PWR_SW_N; extern struct Gpio __code RGBKB_DET_N; +// SINK_CTRL is on GPH7 (same as v560tnx): WU127, Group 13 bit 7, INT148 +#define HAVE_SINK_CTRL 1 +#define SINK_CTRL_IRQ _GPIO_WUC_IRQ_H7 // 148 (IER18[4]) extern struct Gpio __code SINK_CTRL; #define HAVE_SLP_SUS_N 0 #define HAVE_SUS_PWR_ACK 0 diff --git a/src/board/novacustom/v540tu/include/board/gpio.h b/src/board/novacustom/v540tu/include/board/gpio.h index 9845834af..753bed2de 100644 --- a/src/board/novacustom/v540tu/include/board/gpio.h +++ b/src/board/novacustom/v540tu/include/board/gpio.h @@ -18,6 +18,7 @@ extern struct Gpio __code DD_ON; extern struct Gpio __code EC_EN; extern struct Gpio __code EC_RSMRST_N; extern struct Gpio __code H_PROCHOT_EC; +#define HAVE_JACK_IN_N 1 extern struct Gpio __code JACK_IN_N; extern struct Gpio __code LAN_WAKEUP_N; extern struct Gpio __code LED_ACIN; diff --git a/src/board/novacustom/v560tnx/include/board/gpio.h b/src/board/novacustom/v560tnx/include/board/gpio.h index b6a936feb..7a06d916b 100644 --- a/src/board/novacustom/v560tnx/include/board/gpio.h +++ b/src/board/novacustom/v560tnx/include/board/gpio.h @@ -22,6 +22,7 @@ extern struct Gpio __code EC_EN; extern struct Gpio __code EC_RSMRST_N; extern struct Gpio __code GC6_FB_EN; extern struct Gpio __code H_PROCHOT_EC; +#define HAVE_JACK_IN_N 1 extern struct Gpio __code JACK_IN_N; extern struct Gpio __code LAN_WAKEUP_N; extern struct Gpio __code LED_ACIN; @@ -40,6 +41,9 @@ extern struct Gpio __code PD_IRQ; extern struct Gpio __code PWR_BTN_N; extern struct Gpio __code PWR_SW_N; extern struct Gpio __code RGBKB_DET_N; +// SINK_CTRL is on GPH7 (not GPC3 like nv40mz): WU127, Group 13 bit 7, INT148 +#define HAVE_SINK_CTRL 1 +#define SINK_CTRL_IRQ _GPIO_WUC_IRQ_H7 // 148 (IER18[4]) extern struct Gpio __code SINK_CTRL; #define HAVE_SLP_SUS_N 0 #define HAVE_SUS_PWR_ACK 0 diff --git a/src/board/novacustom/v560tu/include/board/gpio.h b/src/board/novacustom/v560tu/include/board/gpio.h index 9845834af..753bed2de 100644 --- a/src/board/novacustom/v560tu/include/board/gpio.h +++ b/src/board/novacustom/v560tu/include/board/gpio.h @@ -18,6 +18,7 @@ extern struct Gpio __code DD_ON; extern struct Gpio __code EC_EN; extern struct Gpio __code EC_RSMRST_N; extern struct Gpio __code H_PROCHOT_EC; +#define HAVE_JACK_IN_N 1 extern struct Gpio __code JACK_IN_N; extern struct Gpio __code LAN_WAKEUP_N; extern struct Gpio __code LED_ACIN; diff --git a/src/board/system76/common/battery.c b/src/board/system76/common/battery.c index 6fb081dd3..2f5495cab 100644 --- a/src/board/system76/common/battery.c +++ b/src/board/system76/common/battery.c @@ -87,5 +87,6 @@ void battery_event(void) { TRACE("BAT %d mV %d mA\n", battery_info.voltage, battery_info.current); + battery_charger_configure(); battery_charger_event(); } diff --git a/src/board/system76/common/common.mk b/src/board/system76/common/common.mk index dcf9e36b6..f98b3ba0a 100644 --- a/src/board/system76/common/common.mk +++ b/src/board/system76/common/common.mk @@ -2,6 +2,7 @@ board-common-y += acpi.c board-common-y += battery.c +board-common-y += debug_mailbox.c board-common-y += config.c board-common-y += dgpu.c board-common-y += ecpm.c @@ -51,6 +52,7 @@ endif # Set external programmer PROGRAMMER=$(wildcard /dev/ttyACM* /dev/ttyUSB*) + ifeq ($(CONFIG_BUS_ESPI),y) CFLAGS += -DCONFIG_BUS_ESPI=1 diff --git a/src/board/system76/common/debug_mailbox.c b/src/board/system76/common/debug_mailbox.c new file mode 100644 index 000000000..5374478e7 --- /dev/null +++ b/src/board/system76/common/debug_mailbox.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-3.0-only + +// Debug command mailbox at fixed XDATA address 0x0DF0. +// +// The DBGR/SMB hardware slave allows an external host to write to any EC +// XDATA address via the battery SMBus pins. Write a non-zero command byte +// to trigger execution; firmware clears it on completion. +// +// Host protocol: +// 1. Write command byte to 0x0DF0 via DBGR/SMB memory write +// 2. Poll 0x0DF0 until it reads 0x00 (command complete) +// 3. Read status from 0x0DF1: 0=idle, 1=busy, 2=done + +#include + +#include +#include +#include + +#define DBG_CMD_POWER_OFF 0x01 +#define DBG_CMD_POWER_ON 0x02 + +#define MAILBOX_STATUS_IDLE 0x00 +#define MAILBOX_STATUS_BUSY 0x01 +#define MAILBOX_STATUS_DONE 0x02 + +static volatile uint8_t __xdata __at(0x0DF0) mailbox_cmd; +static volatile uint8_t __xdata __at(0x0DF1) mailbox_status; + +void mailbox_event(void) { + uint8_t cmd = mailbox_cmd; + if (!cmd) + return; + + DEBUG("mailbox: cmd=0x%02X\n", cmd); + mailbox_status = MAILBOX_STATUS_BUSY; + + switch (cmd) { + case DBG_CMD_POWER_OFF: + power_off(); + break; + case DBG_CMD_POWER_ON: + power_on(); + break; + default: + DEBUG("mailbox: unknown cmd 0x%02X\n", cmd); + break; + } + + mailbox_status = MAILBOX_STATUS_DONE; + mailbox_cmd = 0; +} diff --git a/src/board/system76/common/dgpu.c b/src/board/system76/common/dgpu.c index 658ea7a7f..7e4696878 100644 --- a/src/board/system76/common/dgpu.c +++ b/src/board/system76/common/dgpu.c @@ -2,6 +2,7 @@ #include #include +#include #if HAVE_DGPU @@ -94,13 +95,14 @@ uint8_t dgpu_get_d_notify_level(bool ac) { return 0; } -int16_t dgpu_set_fan_curve(uint8_t count, struct FanPoint *points) { +int16_t dgpu_set_fan_curve(uint8_t count, struct FanPoint *points) __reentrant { + int i; if (count != FAN.points_size) { TRACE("DGPU: Incorrect number of fan points: %d, expected %d\n", count, FAN.points_size); return -1; } - for (int i = 0; i < count; ++i) { + for (i = 0; i < count; ++i) { TRACE("DGPU: fan curve t%d: %d, d%d: %d\n", i, points[i].temp, i, points[i].duty); FAN.points[i].temp = points[i].temp; FAN.points[i].duty = points[i].duty; @@ -164,3 +166,7 @@ uint8_t dgpu_get_fan_duty(void) { } #endif // HAVE_DGPU + +// Defined outside #if HAVE_DGPU so irq.h consumers can always reference it. +// When HAVE_DGPU=0, main.c's #if HAVE_DGPU guard prevents the ISR case from firing. +volatile bool dgpu_irq_pending = false; diff --git a/src/board/system76/common/fan.c b/src/board/system76/common/fan.c index d0e1199e2..eac18687d 100644 --- a/src/board/system76/common/fan.c +++ b/src/board/system76/common/fan.c @@ -159,12 +159,13 @@ uint8_t fan_smooth(uint8_t last_duty, uint8_t duty) __reentrant { return next_duty; } -uint8_t fan_points_are_valid(uint8_t count, struct FanPoint *points) { +uint8_t fan_points_are_valid(uint8_t count, struct FanPoint *points) __reentrant { + int i; /* * Fan curve speeds have to be non-decreasing. * Fan curve temperatures have to be increasing. */ - for (int i = 1; i < count; i++) { + for (i = 1; i < count; i++) { if (points[i].temp <= points[i - 1].temp || points[i].duty < points[i - 1].duty) return false; } diff --git a/src/board/system76/common/flash/wrapper.c b/src/board/system76/common/flash/wrapper.c index a68aff6ba..4cb409679 100644 --- a/src/board/system76/common/flash/wrapper.c +++ b/src/board/system76/common/flash/wrapper.c @@ -15,7 +15,7 @@ uint8_t __code __at(FLASH_OFFSET) flash_rom[] = { #include }; -static void flash_api(uint32_t addr, uint8_t *data, uint32_t length, uint8_t command) { +static void flash_api(uint32_t addr, uint8_t *data, uint32_t length, uint8_t command) __reentrant { // Use DMA mapping to copy flash ROM to scratch ROM SCARH = 0x80; SCARL = (uint8_t)(FLASH_OFFSET); @@ -29,11 +29,11 @@ static void flash_api(uint32_t addr, uint8_t *data, uint32_t length, uint8_t com SCARH = 0x07; } -void flash_read(uint32_t addr, __xdata uint8_t *data, uint32_t length) { +void flash_read(uint32_t addr, __xdata uint8_t *data, uint32_t length) __reentrant { flash_api(addr, data, length, FLASH_COMMAND_READ); } -uint32_t flash_read_u32(uint32_t addr) { +uint32_t flash_read_u32(uint32_t addr) __reentrant { uint32_t data; flash_api(addr, (uint8_t *)&data, sizeof(data), FLASH_COMMAND_READ); @@ -41,7 +41,7 @@ uint32_t flash_read_u32(uint32_t addr) { return data; } -uint16_t flash_read_u16(uint32_t addr) { +uint16_t flash_read_u16(uint32_t addr) __reentrant { uint16_t data; flash_api(addr, (uint8_t *)&data, sizeof(data), FLASH_COMMAND_READ); @@ -49,7 +49,7 @@ uint16_t flash_read_u16(uint32_t addr) { return data; } -uint8_t flash_read_u8(uint32_t addr) { +uint8_t flash_read_u8(uint32_t addr) __reentrant { uint8_t data; flash_api(addr, &data, sizeof(data), FLASH_COMMAND_READ); @@ -57,22 +57,22 @@ uint8_t flash_read_u8(uint32_t addr) { return data; } -void flash_write(uint32_t addr, __xdata uint8_t *data, uint32_t length) { +void flash_write(uint32_t addr, __xdata uint8_t *data, uint32_t length) __reentrant { flash_api(addr, data, length, FLASH_COMMAND_WRITE); } -void flash_write_u32(uint32_t addr, uint32_t data) { +void flash_write_u32(uint32_t addr, uint32_t data) __reentrant { flash_api(addr, (uint8_t *)&data, sizeof(data), FLASH_COMMAND_WRITE); } -void flash_write_u16(uint32_t addr, uint16_t data) { +void flash_write_u16(uint32_t addr, uint16_t data) __reentrant { flash_api(addr, (uint8_t *)&data, sizeof(data), FLASH_COMMAND_WRITE); } -void flash_write_u8(uint32_t addr, uint8_t data) { +void flash_write_u8(uint32_t addr, uint8_t data) __reentrant { flash_api(addr, &data, sizeof(data), FLASH_COMMAND_WRITE); } -void flash_erase(uint32_t addr) { +void flash_erase(uint32_t addr) __reentrant { flash_api(addr, NULL, 0, FLASH_COMMAND_ERASE_1K); } diff --git a/src/board/system76/common/include/board/debug_mailbox.h b/src/board/system76/common/include/board/debug_mailbox.h new file mode 100644 index 000000000..bbb7718ff --- /dev/null +++ b/src/board/system76/common/include/board/debug_mailbox.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0-only + +#ifndef _BOARD_DEBUG_MAILBOX_H +#define _BOARD_DEBUG_MAILBOX_H + +// Poll the debug command mailbox at 0x0DF0 and execute any pending command. +// Called from the main loop each iteration. +void mailbox_event(void); + +#endif // _BOARD_DEBUG_MAILBOX_H diff --git a/src/board/system76/common/include/board/dgpu.h b/src/board/system76/common/include/board/dgpu.h index f0b5a4df8..05e38cfda 100644 --- a/src/board/system76/common/include/board/dgpu.h +++ b/src/board/system76/common/include/board/dgpu.h @@ -19,7 +19,7 @@ extern int16_t dgpu_temp; #endif // HAVE_DGPU void dgpu_init(void); -int16_t dgpu_set_fan_curve(uint8_t count, struct FanPoint *points); +int16_t dgpu_set_fan_curve(uint8_t count, struct FanPoint *points) __reentrant; uint8_t dgpu_get_fan_duty(void); uint8_t dgpu_get_d_notify_level(bool ac); void set_mux_ctrl(void); diff --git a/src/board/system76/common/include/board/fan.h b/src/board/system76/common/include/board/fan.h index 140623884..253beb82a 100644 --- a/src/board/system76/common/include/board/fan.h +++ b/src/board/system76/common/include/board/fan.h @@ -64,6 +64,6 @@ void fan_duty_set(uint8_t peci_fan_duty, uint8_t dgpu_fan_duty) __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; uint8_t fan_smooth(uint8_t last_duty, uint8_t duty) __reentrant; -uint8_t fan_points_are_valid(uint8_t count, struct FanPoint *points); +uint8_t fan_points_are_valid(uint8_t count, struct FanPoint *points) __reentrant; #endif // _BOARD_FAN_H diff --git a/src/board/system76/common/include/board/flash.h b/src/board/system76/common/include/board/flash.h index f775bc495..463a3a2e1 100644 --- a/src/board/system76/common/include/board/flash.h +++ b/src/board/system76/common/include/board/flash.h @@ -26,7 +26,7 @@ * \param[out] data The memory area to copy to. * \param[in] length The number of bytes to copy. */ -void flash_read(uint32_t addr, __xdata uint8_t *data, uint32_t length); +void flash_read(uint32_t addr, __xdata uint8_t *data, uint32_t length) __reentrant; /** * Read a single byte from flash. @@ -35,7 +35,7 @@ void flash_read(uint32_t addr, __xdata uint8_t *data, uint32_t length); * * \return The value read from flash. */ -uint8_t flash_read_u8(uint32_t addr); +uint8_t flash_read_u8(uint32_t addr) __reentrant; /** * Read two bytes from flash. @@ -44,7 +44,7 @@ uint8_t flash_read_u8(uint32_t addr); * * \return The value read from flash. */ -uint16_t flash_read_u16(uint32_t addr); +uint16_t flash_read_u16(uint32_t addr) __reentrant; /** * Read four bytes from flash. @@ -53,7 +53,7 @@ uint16_t flash_read_u16(uint32_t addr); * * \return The value read from flash. */ -uint32_t flash_read_u32(uint32_t addr); +uint32_t flash_read_u32(uint32_t addr) __reentrant; /** * Write data to flash from the specified buffer. @@ -62,7 +62,7 @@ uint32_t flash_read_u32(uint32_t addr); * \param[in] data The memory area to copy from. * \param[in] length The number of bytes to copy. */ -void flash_write(uint32_t addr, __xdata uint8_t *data, uint32_t length); +void flash_write(uint32_t addr, __xdata uint8_t *data, uint32_t length) __reentrant; /** * Write a single byte to flash. @@ -70,7 +70,7 @@ void flash_write(uint32_t addr, __xdata uint8_t *data, uint32_t length); * \param[in] addr The flash address to read. * \param[in] data The value to write to flash. */ -void flash_write_u8(uint32_t addr, uint8_t data); +void flash_write_u8(uint32_t addr, uint8_t data) __reentrant; /** * Write two bytes to flash. @@ -78,7 +78,7 @@ void flash_write_u8(uint32_t addr, uint8_t data); * \param[in] addr The flash address to read. * \param[in] data The value to write to flash. */ -void flash_write_u16(uint32_t addr, uint16_t data); +void flash_write_u16(uint32_t addr, uint16_t data) __reentrant; /** * Write two bytes to flash. @@ -86,13 +86,13 @@ void flash_write_u16(uint32_t addr, uint16_t data); * \param[in] addr The flash address to read. * \param[in] data The value to write to flash. */ -void flash_write_u32(uint32_t addr, uint32_t data); +void flash_write_u32(uint32_t addr, uint32_t data) __reentrant; /** * Erase a 1K block of flash. * * \param[in] addr The flash address contained in the 1K block. */ -void flash_erase(uint32_t addr); +void flash_erase(uint32_t addr) __reentrant; #endif // _BOARD_FLASH_H diff --git a/src/board/system76/common/include/board/irq.h b/src/board/system76/common/include/board/irq.h new file mode 100644 index 000000000..83f7df02d --- /dev/null +++ b/src/board/system76/common/include/board/irq.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-only + +#ifndef _BOARD_IRQ_H +#define _BOARD_IRQ_H + +#include + +// Pending flags set by external_1 ISR, cleared by main loop handlers. +extern volatile bool acin_irq_pending; // ACIN_N (AC adapter) +extern volatile bool pwr_sw_irq_pending; // PWR_SW_N (power button) +extern volatile bool sys_pwrgd_irq_pending; // ALL_SYS_PWRGD +extern volatile bool plt_rst_irq_pending; // BUF_PLT_RST_N +extern volatile bool slp_sus_irq_pending; // SLP_SUS_N +extern volatile bool lan_wakeup_irq_pending; // LAN_WAKEUP_N +extern volatile bool usbpd_irq_pending; // JACK_IN_N / SINK_CTRL / PD_IRQ +extern volatile bool lid_irq_pending; +extern volatile bool smfi_irq_pending; +extern volatile bool kbc_irq_pending; +extern volatile bool pmc_irq_pending; +extern volatile bool dgpu_irq_pending; +extern volatile bool espi_irq_pending; + +#endif // _BOARD_IRQ_H diff --git a/src/board/system76/common/include/board/kbc.h b/src/board/system76/common/include/board/kbc.h index 0b2352cde..859f617ee 100644 --- a/src/board/system76/common/include/board/kbc.h +++ b/src/board/system76/common/include/board/kbc.h @@ -12,6 +12,7 @@ extern uint8_t kbc_leds; void kbc_init(void); bool kbc_scancode(uint16_t key, bool pressed); void kbc_event(struct Kbc *kbc); +bool kbc_output_pending(void); void kbc_clear_lock(void); #endif // _BOARD_KBC_H diff --git a/src/board/system76/common/include/board/kbscan.h b/src/board/system76/common/include/board/kbscan.h index 9147b4735..a1572106f 100644 --- a/src/board/system76/common/include/board/kbscan.h +++ b/src/board/system76/common/include/board/kbscan.h @@ -21,8 +21,14 @@ extern uint16_t kbscan_repeat_period; // ms between pressing key and repeating extern uint16_t kbscan_repeat_delay; -// Debounced kbscan matrix -extern uint8_t kbscan_matrix[KM_OUT]; +// Raw scan matrix filled by INT84 ISR from hardware KSM result registers +extern volatile uint8_t kbscan_matrix[KM_OUT]; + +// Set by ISR when a new scan result is available +extern volatile bool kbscan_irq_pending; + +// Set by kbscan_event when a key is held with repeat pending +extern volatile bool kbscan_repeat_active; void kbscan_init(void); void kbscan_event(void); diff --git a/src/board/system76/common/include/board/peci.h b/src/board/system76/common/include/board/peci.h index 1b2ab0c6e..029671af4 100644 --- a/src/board/system76/common/include/board/peci.h +++ b/src/board/system76/common/include/board/peci.h @@ -29,9 +29,9 @@ extern uint8_t t_junction; void peci_init(void); bool peci_available(void); -int16_t peci_set_fan_curve(uint8_t count, struct FanPoint *points); -int16_t peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data); -int16_t peci_rd_pkg_config(uint8_t index, uint16_t param, uint32_t *value); +int16_t peci_set_fan_curve(uint8_t count, struct FanPoint *points) __reentrant; +int16_t peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data) __reentrant; +int16_t peci_rd_pkg_config(uint8_t index, uint16_t param, uint32_t *value) __reentrant; uint8_t peci_get_fan_duty(void); #endif // _BOARD_PECI_H diff --git a/src/board/system76/common/include/board/power.h b/src/board/system76/common/include/board/power.h index b3d7dd845..e3c0036ab 100644 --- a/src/board/system76/common/include/board/power.h +++ b/src/board/system76/common/include/board/power.h @@ -29,12 +29,23 @@ void update_power_state(void); extern uint8_t pep_hook; #endif +bool is_standby_power_needed(void); + void power_init(void); void power_on(void); void power_off(void); void power_cpu_reset(void); void power_apply_limit(bool ac); -void power_event(void); +// Per-signal event handlers — called from main loop when IRQ flag is set. +void acin_event(void); +void pwr_sw_event(void); +void sys_pwrgd_event(void); +void plt_rst_event(void); +void slp_sus_event(void); +void sus_pwrdn_event(void); +void lan_wakeup_event(void); +void power_led_event(void); +void prochot_event(void); #endif // _BOARD_POWER_H diff --git a/src/board/system76/common/kbc.c b/src/board/system76/common/kbc.c index aa9942b60..553bb1ec8 100644 --- a/src/board/system76/common/kbc.c +++ b/src/board/system76/common/kbc.c @@ -10,8 +10,8 @@ #include void kbc_init(void) { - // Disable interrupts - *(KBC.control) = 0; + // Enable interrupts + *(KBC.control) = BIT(4) | BIT(3); #if CONFIG_BUS_ESPI // Set IRQ mode to edge-triggered, 1-cycle pulse width *(KBC.irq) = BIT(3); @@ -173,14 +173,14 @@ static void kbc_clear_output(struct Kbc *kbc) { } static void kbc_on_input_command(struct Kbc *kbc, uint8_t data) { - TRACE("kbc cmd: %02X\n", data); + DEBUG("kbc cmd: %02X\n", data); // Controller commands always reset the state state = KBC_STATE_NORMAL; // Controller commands clear the output buffer kbc_clear_output(kbc); switch (data) { case 0x20: - TRACE(" read configuration byte\n"); + DEBUG(" read configuration byte\n"); state = KBC_STATE_KEYBOARD; // Interrupt enable flags state_data = *kbc->control & 0x03; @@ -199,57 +199,57 @@ static void kbc_on_input_command(struct Kbc *kbc, uint8_t data) { } break; case 0x60: - TRACE(" write configuration byte\n"); + DEBUG(" write configuration byte\n"); state = KBC_STATE_WRITE_CONFIG; break; case 0xA7: - TRACE(" disable second port\n"); + DEBUG(" disable second port\n"); kbc_second = false; break; case 0xA8: - TRACE(" enable second port\n"); + DEBUG(" enable second port\n"); kbc_second = true; break; case 0xA9: - TRACE(" test second port\n"); + DEBUG(" test second port\n"); // TODO: communicate with touchpad? state = KBC_STATE_KEYBOARD; state_data = 0x00; break; case 0xAA: - TRACE(" test controller\n"); + DEBUG(" test controller\n"); // Why not pass the test? state = KBC_STATE_KEYBOARD; state_data = 0x55; break; case 0xAB: - TRACE(" test first port\n"); + DEBUG(" test first port\n"); // We _ARE_ the keyboard, so everything is good. state = KBC_STATE_KEYBOARD; state_data = 0x00; break; case 0xAD: - TRACE(" disable first port\n"); + DEBUG(" disable first port\n"); kbc_first = false; break; case 0xAE: - TRACE(" enable first port\n"); + DEBUG(" enable first port\n"); kbc_first = true; break; case 0xD1: - TRACE(" write port byte\n"); + DEBUG(" write port byte\n"); state = KBC_STATE_WRITE_PORT; break; case 0xD2: - TRACE(" write first port output\n"); + DEBUG(" write first port output\n"); state = KBC_STATE_FIRST_PORT_OUTPUT; break; case 0xD3: - TRACE(" write second port output\n"); + DEBUG(" write second port output\n"); state = KBC_STATE_SECOND_PORT_OUTPUT; break; case 0xD4: - TRACE(" write second port input\n"); + DEBUG(" write second port input\n"); state = KBC_STATE_SECOND_PORT_INPUT; break; } @@ -516,8 +516,28 @@ void kbc_event(struct Kbc *kbc) { kbc_on_input_data(kbc, data); } } - // Write data if possible + // Write data if possible. + // Use else-if so IBF processing and OBF write never happen in the same call: + // kbc_clear_output() needs a loop iteration to settle before OBF reads as 0. + // kbc_output_pending() in the main loop triggers the follow-up call. else if (!(sts & KBC_STS_OBF)) { kbc_on_output_empty(kbc); } } + +bool kbc_output_pending(void) { + // If OBF is already set, host hasn't read yet — don't call kbc_event() + // (avoids busy-waiting in kbc_keyboard() on every main loop iteration) + if (KBHISR & KBC_STS_OBF) + return false; + if (kbc_buffer_head != kbc_buffer_tail) + return true; + switch (state) { + case KBC_STATE_KEYBOARD: + case KBC_STATE_MOUSE: + case KBC_STATE_TOUCHPAD: + return true; + default: + return false; + } +} diff --git a/src/board/system76/common/kbscan.c b/src/board/system76/common/kbscan.c index b4bd4d379..0911ac7b2 100644 --- a/src/board/system76/common/kbscan.c +++ b/src/board/system76/common/kbscan.c @@ -27,7 +27,17 @@ bool kbscan_enabled = false; uint16_t kbscan_repeat_period = 91; uint16_t kbscan_repeat_delay = 500; -uint8_t kbscan_matrix[KM_OUT] = { 0 }; +// Raw scan matrix filled by INT84 ISR from hardware KSM result registers. +// 1 = key pressed (active high, ISR inverts the active-low hardware data). +volatile uint8_t kbscan_matrix[KM_OUT] = { 0 }; + +// Set by ISR when a new scan result is available +volatile bool kbscan_irq_pending = false; + +// Set by kbscan_event when a key is held and repeat is pending. +// Allows main loop to keep calling kbscan_event() via timer_0 wakeup +// even when INT84 doesn't fire (hardware KSM only fires on scan change). +volatile bool kbscan_repeat_active = false; uint8_t sci_extra = 0; @@ -42,84 +52,47 @@ static inline bool matrix_position_is_fn(uint8_t row, uint8_t col) { } void kbscan_init(void) { - KSOCTRL = 0x05; - KSICTRLR = 0x04; - - // Set all outputs to GPIO mode, low, and inputs - KSOL = 0; - KSOLGCTRL = 0xFF; - KSOLGOEN = 0; - KSOH1 = 0; - KSOHGCTRL = 0xFF; - KSOHGOEN = 0; - KSOH2 = 0; - - // Set all inputs to KBS mode, low, and inputs + // Configure KSO: pull-up enabled (bit2), open-drain (bit0) + KSOCTRL = BIT(2) | BIT(0); + // Configure KSI: pull-up enabled (bit2) + KSICTRLR = BIT(2); + + // Put KSO lines in KBM (hardware scan) mode, not GPIO mode + KSOLGCTRL = 0; // KSO0-7: hardware scan controller + KSOHGCTRL = 0; // KSO8-15: hardware scan controller + // KSO16/17 (KSOH2) have no GCTRL; driven by hardware when SDEN=1 + + // Set all KSI lines to KBS mode (inputs driven by keyboard) KSIGCTRL = 0; KSIGOEN = 0; KSIGDAT = 0; -} -// Debounce time in milliseconds -#define DEBOUNCE_DELAY 15 - -static uint8_t kbscan_get_row(uint8_t i) { - // Report all keys as released when lid is closed - if (!lid_state) { - return 0; - } - - // Set current line as output - if (i < 8) { - KSOLGOEN = BIT(i); - KSOHGOEN = 0; -#if KM_OUT >= 17 - GPCRC3 = GPIO_IN; -#endif -#if KM_OUT >= 18 - GPCRC5 = GPIO_IN; -#endif - } else if (i < 16) { - KSOLGOEN = 0; - KSOHGOEN = BIT((i - 8)); -#if KM_OUT >= 17 - GPCRC3 = GPIO_IN; -#endif -#if KM_OUT >= 18 - GPCRC5 = GPIO_IN; -#endif - } else if (i == 16) { - KSOLGOEN = 0; - KSOHGOEN = 0; -#if KM_OUT >= 17 - GPCRC3 = GPIO_OUT; -#endif -#if KM_OUT >= 18 - GPCRC5 = GPIO_IN; -#endif - } else if (i == 17) { - KSOLGOEN = 0; - KSOHGOEN = 0; -#if KM_OUT >= 17 - GPCRC3 = GPIO_IN; -#endif -#if KM_OUT >= 18 - GPCRC5 = GPIO_OUT; -#endif - } -#if KM_OUT >= 17 - GPDRC &= ~BIT(3); -#endif -#if KM_OUT >= 18 - GPDRC &= ~BIT(5); + // Scan Data Control 2: + // WKSOHDLY=0 (23us KSO high delay) + // KSOPCS: number of KSO columns +#if KM_OUT <= 16 + SDC2R = 0x00; // KSOPCS=00: 16 KSO columns +#elif KM_OUT == 17 + SDC2R = 0x40; // KSOPCS=01: 17 KSO columns +#else + SDC2R = 0x80; // KSOPCS=10: 18 KSO columns #endif - // TODO: figure out optimal delay - delay_ticks(20); + // Scan Data Control 3: + // WKSOLDLY=0 (11us KSO low delay), SDLYBR=1 (1ms between scan rounds) + SDC3R = 0x01; + + // Scan Data Control 1: + // SDEN=1 (enable hardware scan), INTSDVEN=1 (INT84 on scan valid), SLS=001 (2 rounds) + SDC1R = BIT(7) | BIT(5) | 0x01; - return ~KSI; + // Enable INT84 (scan data valid interrupt) in intc_init() + // (called after kbscan_init from init(), intc_init runs last) } +// Debounce time in milliseconds +#define DEBOUNCE_DELAY 15 + #if KM_NKEY static bool kbscan_has_ghost_in_row(uint8_t row, uint8_t rowdata) { // Use arguments @@ -149,14 +122,15 @@ static uint8_t kbscan_get_real_keys(uint8_t row, uint8_t rowdata) { static bool kbscan_has_ghost_in_row(uint8_t row, uint8_t rowdata) { rowdata = kbscan_get_real_keys(row, rowdata); - // No ghosts exist when less than 2 keys in the row are active. + // No ghosts exist when less than 2 keys in the row are active. if (!popcount_more_than_one(rowdata)) { return false; } // Check against other rows to see if more than one column matches. for (uint8_t i = 0; i < KM_OUT; i++) { - uint8_t otherrow = kbscan_get_real_keys(i, kbscan_get_row(i)); + // Read raw scan data for row i from hardware KSM result + uint8_t otherrow = kbscan_get_real_keys(i, kbscan_matrix[i]); if (i != row && popcount_more_than_one(otherrow & rowdata)) { return true; } @@ -336,25 +310,33 @@ void kbscan_event(void) { static uint8_t kbscan_last_layer[KM_OUT][KM_IN] = { { 0 } }; static bool kbscan_ghost[KM_OUT] = { false }; - static bool debounce = false; - static uint32_t debounce_time = 0; + // Debounced state: updated here, compared against kbscan_matrix (raw) + static uint8_t kbscan_debounced[KM_OUT] = { 0 }; + + // Per-row debounce: rows are independent so fast multi-key typing is not blocked + static bool debounce[KM_OUT] = { false }; + static uint32_t debounce_time[KM_OUT] = { 0 }; static bool repeat = false; static uint16_t repeat_key = 0; static uint32_t repeat_key_time = 0; + static uint32_t repeat_start = 0; - // If debounce complete - if (debounce) { - uint32_t time = time_get(); - if ((time - debounce_time) >= DEBOUNCE_DELAY) { - // Finish debounce - debounce = false; + // Report all keys as released when lid is closed + if (!lid_state) { + for (uint8_t i = 0; i < KM_OUT; i++) { + kbscan_debounced[i] = 0; } + return; } for (uint8_t i = 0; i < KM_OUT; i++) { - uint8_t new = kbscan_get_row(i); - uint8_t last = kbscan_matrix[i]; + // Expire per-row debounce (inline time_get to keep DSEG usage minimal) + if (debounce[i] && (time_get() - debounce_time[i]) >= DEBOUNCE_DELAY) + debounce[i] = false; + + uint8_t new = kbscan_matrix[i]; // raw scan from ISR (1=pressed) + uint8_t last = kbscan_debounced[i]; if (new != last) { if (kbscan_has_ghost_in_row(i, new)) { kbscan_ghost[i] = true; @@ -362,8 +344,8 @@ void kbscan_event(void) { } else if (kbscan_ghost[i]) { kbscan_ghost[i] = false; // Debounce to allow remaining ghosts to settle. - debounce = true; - debounce_time = time_get(); + debounce[i] = true; + debounce_time[i] = time_get(); } // A key was pressed or released @@ -375,14 +357,14 @@ void kbscan_event(void) { if (new_b != last_b) { bool reset = false; - // If debouncing - if (debounce) { + // If debouncing this row + if (debounce[i]) { // Debounce presses and releases reset = true; } else { - // Begin debounce - debounce = true; - debounce_time = time_get(); + // Begin debounce for this row + debounce[i] = true; + debounce_time[i] = time_get(); // Check keys used for config reset if (matrix_position_is_esc(i, j)) @@ -431,18 +413,17 @@ void kbscan_event(void) { } } - kbscan_matrix[i] = new; + kbscan_debounced[i] = new; } else if (new && repeat_key != 0 && key_should_repeat(repeat_key)) { - // A key is being pressed + // A key is being held — check typematic repeat uint32_t time = time_get(); - static uint32_t repeat_start = 0; if (!repeat) { if (time < repeat_key_time) { // Overflow, reset repeat_key_time repeat_key_time = time; } else if ((time - repeat_key_time) >= kbscan_repeat_delay) { - // Typematic repeat + // Typematic repeat delay elapsed repeat = true; repeat_start = time; } @@ -459,16 +440,6 @@ void kbscan_event(void) { kbscan_layer = layer; - // Reset all lines to inputs - KSOLGOEN = 0; - KSOHGOEN = 0; -#if KM_OUT >= 17 - GPCRC3 = GPIO_IN; -#endif -#if KM_OUT >= 18 - GPCRC5 = GPIO_IN; -#endif - - // TODO: figure out optimal delay - delay_ticks(10); + // Drive repeat from main loop when INT84 doesn't fire (key stable/held) + kbscan_repeat_active = (repeat_key != 0); } diff --git a/src/board/system76/common/main.c b/src/board/system76/common/main.c index 4d6650bb8..47412d1ff 100644 --- a/src/board/system76/common/main.c +++ b/src/board/system76/common/main.c @@ -8,11 +8,14 @@ #include #include #include +#include #include #include +#include #include #include #include +#include #include #include #include @@ -31,24 +34,237 @@ #include #include #include +#include +#include #ifdef PARALLEL_DEBUG #include #endif // PARALLEL_DEBUG + +// -------------------------------------------------------------------------- +// SINK_CTRL interrupt routing. Boards override SINK_CTRL_IRQ in gpio.h +// if SINK_CTRL is not on GPC3 (e.g. GPH7 on v560tnx/v540tnx → IRQ 148). +// WUC/IER/ISR registers are derived automatically from the GPIO struct. +// -------------------------------------------------------------------------- +#ifndef HAVE_SINK_CTRL +#define HAVE_SINK_CTRL 0 +#endif + +// Default IRQ for GPC3 (nv40mz, galp5, galp6): WU108, Group 11 bit 4, INT113 +#ifndef SINK_CTRL_IRQ +#define SINK_CTRL_IRQ _GPIO_WUC_IRQ_C3 // 113 +#endif + +// -------------------------------------------------------------------------- +// Default interrupt routing for JACK_IN_N (boards can override in gpio.h) +// GPC6 = WUEMR2[3] -> INT6 = IER0[6] +// -------------------------------------------------------------------------- +#ifndef HAVE_JACK_IN_N +#define HAVE_JACK_IN_N 0 +#endif + +// -------------------------------------------------------------------------- +// Pending flags — set by external_1 ISR, cleared by main loop handlers. +// kbscan_irq_pending and kbscan_matrix[] are defined in kbscan.c. +// dgpu_irq_pending is defined in dgpu.c. +// -------------------------------------------------------------------------- +volatile bool acin_irq_pending = false; +volatile bool pwr_sw_irq_pending = false; +volatile bool sys_pwrgd_irq_pending = false; +volatile bool plt_rst_irq_pending = false; +volatile bool slp_sus_irq_pending = false; +volatile bool lan_wakeup_irq_pending = false; +volatile bool usbpd_irq_pending = false; +volatile bool lid_irq_pending = false; +volatile bool smfi_irq_pending = false; +volatile bool kbc_irq_pending = false; +volatile bool pmc_irq_pending = false; +volatile bool espi_irq_pending = false; + void external_0(void) __interrupt(0) {} // timer_0 is in time.c void timer_0(void) __interrupt(1); -void external_1(void) __interrupt(2) {} void timer_1(void) __interrupt(3) {} void serial(void) __interrupt(4) {} void timer_2(void) __interrupt(5) {} +static volatile uint8_t last_irq = 0; + +// -------------------------------------------------------------------------- +// external_1 ISR: dispatches INTC interrupts to pending flags +// -------------------------------------------------------------------------- +void external_1(void) __interrupt(2) { + uint8_t irq = intc_get_irq(); + last_irq = irq; + switch (irq) { +#if HAVE_JACK_IN_N + case _GPIO_WUC_IRQ_C6: // JACK_IN_N (GPC6 → INT6) + gpio_irq_ack(&JACK_IN_N); + usbpd_irq_pending = true; + break; +#endif + case _GPIO_WUC_IRQ_B3: // PWR_SW_N (GPB3 → INT14) + gpio_irq_ack(&PWR_SW_N); + pwr_sw_irq_pending = true; + break; + case _GPIO_WUC_IRQ_D2: // BUF_PLT_RST_N (GPD2 → INT17) + gpio_irq_ack(&BUF_PLT_RST_N); + plt_rst_irq_pending = true; + break; + case 22: // SMFI semaphore (INT22 = IER2[6]) + ISR2 = BIT(6); // write-1-to-clear: unblock IVCT for other pending IRQs + smfi_irq_pending = true; + break; + case 24: // KBC IBF (INT24 = IER3[0]) + ISR3 = BIT(0); // clear edge-triggered ISR latch so INT24 doesn't lock out lower IRQs + kbc_irq_pending = true; + break; + case 25: // PMC1 IBF (INT25 = IER3[1]) + ISR3 = BIT(1); + pmc_irq_pending = true; + break; +#if HAVE_PD_IRQ + case _GPIO_WUC_IRQ_E2: // PD_IRQ (GPE2 → INT74) + gpio_irq_ack(&PD_IRQ); + usbpd_irq_pending = true; + break; +#endif + case 84: // KSM scan data valid (INT84 = IER10[4]) + { + uint8_t c; + for (c = 0; c < KM_OUT; c++) + kbscan_matrix[c] = ~KSO_LSDR[c]; // invert: 1=pressed + } + SDSR = BIT(0); // clear SDV (write-1-to-clear) + ISR10 = BIT(4); // clear ISR latch + kbscan_irq_pending = true; + break; +#if HAVE_DGPU + case _GPIO_WUC_IRQ_H4: // DGPU_PWR_EN (GPH4 → INT85, WU88 Group 9 bit 0) + gpio_irq_ack(&DGPU_PWR_EN); + dgpu_irq_pending = true; + break; +#endif +#if HAVE_LAN_WAKEUP_N + case _GPIO_WUC_IRQ_B2: // LAN_WAKEUP_N (GPB2 → INT92) + gpio_irq_ack(&LAN_WAKEUP_N); + lan_wakeup_irq_pending = true; + break; +#endif + case _GPIO_WUC_IRQ_C0: // ALL_SYS_PWRGD (GPC0 → INT93) + gpio_irq_ack(&ALL_SYS_PWRGD); + sys_pwrgd_irq_pending = true; + break; + case _GPIO_WUC_IRQ_B0: // ACIN_N (GPB0 → INT106, WU101 Group 10 bit 5) + gpio_irq_ack(&ACIN_N); + acin_irq_pending = true; + break; + case _GPIO_WUC_IRQ_B1: // LID_SW_N (GPB1 → INT107, WU102 Group 10 bit 6) + gpio_irq_ack(&LID_SW_N); + lid_irq_pending = true; + break; +#if HAVE_SINK_CTRL + case SINK_CTRL_IRQ: // SINK_CTRL (board-specific pin → IRQ from gpio_wuc.h) + gpio_irq_ack(&SINK_CTRL); + usbpd_irq_pending = true; + break; +#endif +#if HAVE_DGPU + case _GPIO_WUC_IRQ_J3: // GC6_FB_EN (GPJ3 → INT131) + gpio_irq_ack(&GC6_FB_EN); + dgpu_irq_pending = true; + break; +#endif +#if HAVE_SLP_SUS_N + case _GPIO_WUC_IRQ_J7: // SLP_SUS_N (GPJ7 → INT135) + gpio_irq_ack(&SLP_SUS_N); + slp_sus_irq_pending = true; + break; +#endif +#if CONFIG_BUS_ESPI + case 154: // eSPI VW (INT154 = IER19[2]) + ISR19 = BIT(2); // clear ISR latch + espi_irq_pending = true; + break; +#endif + case 159: // PLL Frequency Change Event (edge-triggered, always-enabled) + ISR19 = BIT(7); // write-1-to-clear: unblock IVCT for other pending IRQs + break; + } +} + uint8_t main_cycle = 0; -const uint16_t battery_interval = 1000; -// update fan speed more frequently for smoother fans -// NOTE: event loop is longer than 100ms and maybe even longer than 250 -const uint16_t fan_interval = SMOOTH_FANS != 0 ? 250 : 1000; + +// -------------------------------------------------------------------------- +// intc_init: configure WUC edge detection and enable IER bits +// -------------------------------------------------------------------------- +static void intc_init(void) { + // Clear all IER registers for a clean slate (prevents stale bits from + // power-up or previous firmware, including IER19[7] = IRQ159 if maskable) + IER0 = 0; IER1 = 0; IER2 = 0; IER3 = 0; + IER4 = 0; IER5 = 0; IER6 = 0; IER7 = 0; + IER8 = 0; IER9 = 0; IER10 = 0; IER11 = 0; + IER12 = 0; IER13 = 0; IER14 = 0; IER15 = 0; + IER16 = 0; IER17 = 0; IER18 = 0; +#if CONFIG_EC_ITE_IT5570E + IER19 = 0; IER20 = 0; IER21 = 0; +#endif + + // GPIO edge-detect interrupts: WUC/IER registers derived from struct Gpio fields. + // gpio_irq_enable() sets rising edge initially, clears WUESR, and enables IER bit. + +#if HAVE_JACK_IN_N + gpio_irq_enable(&JACK_IN_N); +#endif + + gpio_irq_enable(&PWR_SW_N); + gpio_irq_enable(&BUF_PLT_RST_N); + +#if HAVE_PD_IRQ + gpio_irq_enable(&PD_IRQ); +#endif + + // INT84: KSM scan data valid. Clear any stale SDV before enabling IER so + // the INTC sees a clean rising edge when the first scan completes. + IER10 |= BIT(4); + SDSR = BIT(0); + +#if HAVE_DGPU + gpio_irq_enable(&DGPU_PWR_EN); + gpio_irq_enable(&GC6_FB_EN); +#endif + +#if HAVE_LAN_WAKEUP_N + gpio_irq_enable(&LAN_WAKEUP_N); +#endif + + gpio_irq_enable(&ALL_SYS_PWRGD); + gpio_irq_enable(&ACIN_N); + + // LID_SW_N: gpio_irq_enable arms for the correct edge based on current state. + gpio_irq_enable(&LID_SW_N); + +#if HAVE_SINK_CTRL + gpio_irq_enable(&SINK_CTRL); +#endif + +#if HAVE_SLP_SUS_N + gpio_irq_enable(&SLP_SUS_N); +#endif + + // Non-GPIO interrupts (no WUC): enable IER bits directly. + IER2 |= BIT(6); // SMFI semaphore (INT22) + IER3 |= BIT(0) | BIT(1); // KBC IBF (INT24), PMC1 IBF (INT25) + + // eSPI VW (INT154 = IER19[2]) +#if CONFIG_BUS_ESPI + IER19 |= BIT(2); +#endif + + // Enable 8051 external interrupt 1 (EX1 bit in IE register) + IE |= 0x04; +} void init(void) { // Must happen first @@ -83,7 +299,7 @@ void init(void) { usbpd_init(); ps2_init(); - //TODO: INTC + intc_init(); // Must happen last power_init(); @@ -102,62 +318,163 @@ void main(void) { INFO("System76 EC board '%s', version '%s'\n", board(), version()); ec_print_reset_reason(); - uint32_t last_time_battery = 0; - uint32_t last_time_fan = 0; + // Set the initial lid and AC states + lid_event(); + acin_event(); + + // Sequence the board to the initial state + usbpd_event(); + // If we were woken by power button, power on. + if (!gpio_get(&PWR_SW_N) && gpio_get(&LID_SW_N)) + power_on(); + else + power_off(); + update_power_state(); for (main_cycle = 0;; main_cycle++) { - switch (main_cycle % 3U) { - case 0: - // Handle USB-C events immediately before power states + // Idle until next interrupt (~1ms timer_0 or INTC wakeup) + PCON |= 1; + if (last_irq) { + DEBUG("IRQ %u\n", last_irq); + last_irq = 0; + } + + // USB-PD signals: JACK_IN_N, SINK_CTRL, PD_IRQ + if (usbpd_irq_pending) { + usbpd_irq_pending = false; usbpd_event(); + } - // Handle power states - power_event(); - break; - case 1: -#if PARALLEL_DEBUG - if (!parallel_debug) -#endif // PARALLEL_DEBUG - { - // Scans keyboard and sends keyboard packets - kbscan_event(); - } - break; - case 2: - // Handle lid close/open + // AC adapter plug/unplug: also refreshes USB-PD current limits + if (acin_irq_pending) { + acin_irq_pending = false; + usbpd_event(); + acin_event(); + } + + // Power button press/release + if (pwr_sw_irq_pending) { + pwr_sw_irq_pending = false; + pwr_sw_event(); + } + + // Platform reset de-assertion (BUF_PLT_RST_N) + if (plt_rst_irq_pending) { + plt_rst_irq_pending = false; + plt_rst_event(); + } + + // System power good (ALL_SYS_PWRGD) + if (sys_pwrgd_irq_pending) { + sys_pwrgd_irq_pending = false; + update_power_state(); + sys_pwrgd_event(); + } + + // Suspend (SLP_SUS_N) — debug logging only +#if HAVE_SLP_SUS_N + if (slp_sus_irq_pending) { + slp_sus_irq_pending = false; + slp_sus_event(); + } +#endif + + // LAN remote wakeup +#if HAVE_LAN_WAKEUP_N + if (lan_wakeup_irq_pending) { + lan_wakeup_irq_pending = false; + lan_wakeup_event(); + } +#endif + + if (lid_irq_pending) { + lid_irq_pending = false; lid_event(); - break; } - if (main_cycle == 0) { - uint32_t time = time_get(); - // Only run the following once per interval - if ((time - last_time_fan) >= fan_interval) { - last_time_fan = time; + // No interrupt possible until SMFI Semaphore register is used + smfi_event(); - // Update fan speeds - fan_duty_set(peci_get_fan_duty(), dgpu_get_fan_duty()); - } + // INT24 (IBF set): host wrote a command/data byte — process it. + if (kbc_irq_pending) { + kbc_irq_pending = false; + kbc_event(&KBC); + } + // kbc_event() never processes IBF and writes OBF in the same call + // (kbc_clear_output() needs a loop iteration to settle). Drive the + // OBF side by polling kbc_output_pending() — no interrupt available. + if (kbc_output_pending()) { + kbc_event(&KBC); + } - // Only run the following once per interval - if ((time - last_time_battery) >= battery_interval) { - last_time_battery = time; + if (pmc_irq_pending) { + pmc_irq_pending = false; + pmc_event(&PMC_1); + } - // Updates battery status - battery_event(); - } +#if CONFIG_BUS_ESPI + if (espi_irq_pending) { + espi_irq_pending = false; + espi_event(); + } +#endif + + // Keyboard: new scan data (INT84) or key held with repeat pending. + // INT84 fires only on scan change, so kbscan_repeat_active keeps + // kbscan_event() running via timer_0 (~1ms) while a key is held. +#ifdef PARALLEL_DEBUG + if (!parallel_debug) +#endif // PARALLEL_DEBUG + if (kbscan_irq_pending || kbscan_repeat_active) { + kbscan_irq_pending = false; + kbscan_event(); + } + +#if HAVE_DGPU + if (dgpu_irq_pending) { + dgpu_irq_pending = false; + // Re-evaluate fan curves when dGPU state changes + fan_duty_set(peci_get_fan_duty(), dgpu_get_fan_duty()); } +#endif // Board-specific events board_event(); - // Checks for keyboard/mouse packets from host - kbc_event(&KBC); - // Handles ACPI communication - pmc_event(&PMC_1); - // AP/EC communication over SMFI - smfi_event(); - // Idle until next timer interrupt - //Disabled until interrupts used: PCON |= 1; + // DBGR/SMB debug command mailbox (fixed address 0x0DF0) + mailbox_event(); + + + // Periodic tasks driven by 50 ms timer_0 flag. + // Counters tick at 50 ms each: power=2 (100ms), fan=5 or 20 (250ms or 1000ms), + // battery=20 (1000ms). No time_get() needed. + if (timer_50ms_pending) { + static uint8_t power_ticks = 0; + static uint8_t fan_ticks = 0; + static uint8_t batt_ticks = 0; + timer_50ms_pending = false; + + prochot_event(); + + // Power/usbpd: debounce timeouts, usbpd_check_mode — every 100 ms + if (++power_ticks >= 2) { + power_ticks = 0; + usbpd_event(); + sus_pwrdn_event(); + power_led_event(); + } + + // Fan: smooth=250 ms (5 ticks), normal=1000 ms (20 ticks) + if (++fan_ticks >= (SMOOTH_FANS != 0 ? 5 : 20)) { + fan_ticks = 0; + fan_duty_set(peci_get_fan_duty(), dgpu_get_fan_duty()); + } + + // Battery: every 1000 ms (20 ticks) + if (++batt_ticks >= 20) { + batt_ticks = 0; + battery_event(); + } + } } } diff --git a/src/board/system76/common/peci.c b/src/board/system76/common/peci.c index 0e8c6fa3b..797732a95 100644 --- a/src/board/system76/common/peci.c +++ b/src/board/system76/common/peci.c @@ -61,13 +61,14 @@ static struct Fan FAN = { .interpolate = SMOOTH_FANS != 0, }; -int16_t peci_set_fan_curve(uint8_t count, struct FanPoint *points) { +int16_t peci_set_fan_curve(uint8_t count, struct FanPoint *points) __reentrant { + int i; if (count != FAN.points_size) { TRACE("PECI: Incorrect number of fan points: %d, expected %d\n", count, FAN.points_size); return -1; } - for (int i = 0; i < count; ++i) { + for (i = 0; i < count; ++i) { TRACE("PECI: fan curve t%d: %d, d%d: %d\n", i, points[i].temp, i, points[i].duty); FAN.points[i].temp = points[i].temp; FAN.points[i].duty = points[i].duty; @@ -347,9 +348,10 @@ bool peci_get_temp(int16_t *data) { // Returns positive completion code on success, negative completion code or // negative (0x1000 | status register) on PECI hardware error -int16_t peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data) { +int16_t peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data) __reentrant { int retry = 50; // TODO how many retries are appropriate? uint8_t cc = HORDDR; + uint8_t status; // Wait for any in-progress transaction to complete while (HOSTAR & BIT(0)) {} @@ -387,7 +389,7 @@ int16_t peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data) { // Wait for command completion while (!(HOSTAR & BIT(1))) {} - uint8_t status = HOSTAR; + status = HOSTAR; if (status & 0xEC) { ERROR("peci_wr_pkg_config: hardware error: 0x%02X\n", status); // Clear status @@ -411,9 +413,11 @@ int16_t peci_wr_pkg_config(uint8_t index, uint16_t param, uint32_t data) { return -((int16_t)cc); } -int16_t peci_rd_pkg_config(uint8_t index, uint16_t param, uint32_t *value) { +int16_t peci_rd_pkg_config(uint8_t index, uint16_t param, uint32_t *value) __reentrant { int retry = 50; // TODO how many retries are appropriate? uint8_t cc = HORDDR; + uint8_t status; + int i; *value = 0; // Wait for any in-progress transaction to complete @@ -447,7 +451,7 @@ int16_t peci_rd_pkg_config(uint8_t index, uint16_t param, uint32_t *value) { // Wait for command completion while (!(HOSTAR & BIT(1))) {} - uint8_t status = HOSTAR; + status = HOSTAR; if (status & 0xEC) { ERROR("peci_rd_pkg_config: hardware error: 0x%02X\n", status); // Clear status @@ -466,7 +470,7 @@ int16_t peci_rd_pkg_config(uint8_t index, uint16_t param, uint32_t *value) { return -((int16_t)cc); } else { // Read data if finished successfully - for (int i = 0; i < 4; ++i) { + for (i = 0; i < 4; ++i) { *value |= (((uint32_t)HORDDR) << (8 * i)); } diff --git a/src/board/system76/common/pmc.c b/src/board/system76/common/pmc.c index 361db784f..5bdb9959d 100644 --- a/src/board/system76/common/pmc.c +++ b/src/board/system76/common/pmc.c @@ -16,8 +16,8 @@ bool pmc_s0_hack = false; void pmc_init(void) { - *(PMC_1.control) = 0x41; - *(PMC_2.control) = 0x41; + *(PMC_1.control) = 0xc1; + *(PMC_2.control) = 0xc1; } enum PmcState { diff --git a/src/board/system76/common/power.c b/src/board/system76/common/power.c index 0dfd904b2..e5b95494d 100644 --- a/src/board/system76/common/power.c +++ b/src/board/system76/common/power.c @@ -98,6 +98,10 @@ #define HAVE_XLP_OUT 1 #endif +#ifndef HAVE_H_PROCHOT_EC +#define HAVE_H_PROCHOT_EC 0 +#endif + extern uint8_t main_cycle; // VccRTC stable (55%) to RTCRST# high @@ -218,7 +222,7 @@ void update_power_state(void) { } } -static bool is_standby_power_needed(void) { +bool is_standby_power_needed(void) { if (options_get(OPT_ALWAYS_ON_USB)) return true; @@ -500,81 +504,80 @@ static bool power_button_disabled(void) { return !gpio_get(&LID_SW_N) && gpio_get(&ACIN_N); } -void power_event(void) { - // Check if the adapter line goes low - static bool ac_send_sci = true; - static bool ac_last = true; - static uint32_t ac_unplug_time = 0; - bool ac_new = gpio_get(&ACIN_N); - - if (power_state == POWER_STATE_G3 && is_standby_power_needed()) - power_sequence(POWER_STATE_G3_AOU); - - if (ac_new != ac_last) { - // Configure smart charger - DEBUG("Power adapter "); - if (ac_new) { - DEBUG("unplugged\n"); - GPIO_SET_DEBUG(H_PROCHOT_EC, false); - ac_unplug_time = time_get(); - battery_charger_disable(); - // USB power may have been kept to prevent PDC glitch - if (power_state == POWER_STATE_G3_AOU) - power_off(); - } else { - DEBUG("plugged in\n"); - battery_charger_configure(); - if (options_get(OPT_POWER_ON_AC) == 1) - power_on(); - } - power_apply_limit(!ac_new); - battery_debug(); - - // Reset main loop cycle to force reading PECI and battery - main_cycle = 0; +// -------------------------------------------------------------------------- +// acin_event: handle AC adapter plug/unplug (ACIN_N, active-low) +// -------------------------------------------------------------------------- +#if HAVE_H_PROCHOT_EC +static uint8_t prochot_ticks; +#endif - // Send SCI to update AC and battery information - ac_send_sci = true; - } - if (ac_send_sci) { - // Send SCI 0x16 for AC detect event if ACPI OS is loaded - if (acpi_ecos != EC_OS_NONE) { - if (pmc_sci(&PMC_1, 0x16)) { - ac_send_sci = false; - } - } +void acin_event(void) { + bool ac = gpio_get(&ACIN_N); + + // Configure smart charger + DEBUG("Power adapter "); + if (ac) { + DEBUG("unplugged\n"); +#if HAVE_H_PROCHOT_EC + // Throttle CPU immediately; deassert after 1 second (20 × 50ms ticks) + gpio_set(&H_PROCHOT_EC, false); + prochot_ticks = 20; +#endif + battery_charger_disable(); + // USB power may have been kept to prevent PDC glitch + if (power_state == POWER_STATE_G3_AOU) + power_off(); + } else { + DEBUG("plugged in\n"); + battery_charger_configure(); + if (options_get(OPT_POWER_ON_AC) == 1) + power_on(); } - ac_last = ac_new; - // Unthrottle on AC, or after 3 seconds on DC - gpio_set(&H_PROCHOT_EC, !ac_new | (ac_unplug_time < (time_get() - 3000))); + power_apply_limit(!ac); + battery_debug(); - gpio_set(&AC_PRESENT, !ac_new); + // Send SCI 0x16 for AC detect event if ACPI OS is loaded + if (acpi_ecos != EC_OS_NONE) + pmc_sci(&PMC_1, 0x16); +} - // Configure charger based on charging thresholds when plugged in - if (!ac_new) { - battery_charger_configure(); +#if HAVE_H_PROCHOT_EC +// Called every 50ms. Deasserts H_PROCHOT_EC# after 1 second (20 ticks). +void prochot_event(void) { + if (prochot_ticks == 0) + return; + if (--prochot_ticks == 0) { + DEBUG("PROCHOT deasserted\n"); + gpio_set(&H_PROCHOT_EC, true); } +} +#else +void prochot_event(void) {} +#endif - // Read power switch state - static bool ps_last = true; - bool ps_new = gpio_get(&PWR_SW_N); - if (!ps_new && ps_last) { +// -------------------------------------------------------------------------- +// pwr_sw_event: handle power button press/release (PWR_SW_N, active-low) +// -------------------------------------------------------------------------- +void pwr_sw_event(void) { + bool ps = gpio_get(&PWR_SW_N); // false = pressed (active-low) + + if (!ps) { // Ensure press is not spurious for (uint8_t i = 100; i != 0; i--) { delay_ms(1); - if (gpio_get(&PWR_SW_N) != ps_new) { + if (gpio_get(&PWR_SW_N)) { DEBUG("%02X: Spurious press\n", main_cycle); - ps_new = ps_last; + ps = true; break; } else if (power_button_disabled()) { // Ignore press when power button disabled - ps_new = ps_last; + ps = true; break; } } - if (ps_new != ps_last) { + if (!ps) { DEBUG("%02X: Power switch press\n", main_cycle); // Enable S5 power if necessary, before sending PWR_BTN @@ -584,27 +587,22 @@ void power_event(void) { power_on(); // After power on ensure there is no secondary press sent to PCH - ps_new = ps_last; + ps = true; } } - } -#if LEVEL >= LEVEL_DEBUG - else if (ps_new && !ps_last) { + } else { DEBUG("%02X: Power switch release\n", main_cycle); } -#endif - ps_last = ps_new; // Send power signal to PCH - gpio_set(&PWR_BTN_N, ps_new); - - // Update power state before determining actions - update_power_state(); + gpio_set(&PWR_BTN_N, ps); +} - // If system power is good - static bool pg_last = false; - bool pg_new = gpio_get(&ALL_SYS_PWRGD); - if (pg_new && !pg_last) { +// -------------------------------------------------------------------------- +// sys_pwrgd_event: handle ALL_SYS_PWRGD (system power good) +// -------------------------------------------------------------------------- +void sys_pwrgd_event(void) { + if (gpio_get(&ALL_SYS_PWRGD)) { DEBUG("%02X: ALL_SYS_PWRGD asserted\n", main_cycle); //TODO: tPLT04; @@ -621,7 +619,7 @@ void power_event(void) { // Assert SYS_PWROK, system can finally perform PLT_RST# and boot GPIO_SET_DEBUG(PCH_PWROK_EC, true); #endif // HAVE_PCH_PWROK_EC - } else if (!pg_new && pg_last) { + } else { DEBUG("%02X: ALL_SYS_PWRGD de-asserted\n", main_cycle); #if HAVE_PCH_PWROK_EC @@ -634,58 +632,49 @@ void power_event(void) { GPIO_SET_DEBUG(PM_PWROK, false); #endif // HAVE_PM_PWROK } - pg_last = pg_new; +} - // clang-format off - static bool rst_last = false; - bool rst_new = gpio_get(&BUF_PLT_RST_N); -#if LEVEL >= LEVEL_DEBUG - if (!rst_new && rst_last) { - DEBUG("%02X: PLT_RST# asserted\n", main_cycle); - } else -#endif - if (rst_new && !rst_last) { +// -------------------------------------------------------------------------- +// plt_rst_event: handle BUF_PLT_RST_N (platform reset, active-low) +// -------------------------------------------------------------------------- +void plt_rst_event(void) { + if (gpio_get(&BUF_PLT_RST_N)) { DEBUG("%02X: PLT_RST# de-asserted\n", main_cycle); #if CONFIG_BUS_ESPI espi_reset(); #else // CONFIG_BUS_ESPI power_cpu_reset(); #endif // CONFIG_BUS_ESPI + } else { + DEBUG("%02X: PLT_RST# asserted\n", main_cycle); } - rst_last = rst_new; - // clang-format on +} +// -------------------------------------------------------------------------- +// slp_sus_event: handle SLP_SUS_N (suspend, active-low) — debug logging only +// -------------------------------------------------------------------------- #if HAVE_SLP_SUS_N +void slp_sus_event(void) { #if LEVEL >= LEVEL_DEBUG - static bool sus_last = true; - bool sus_new = gpio_get(&SLP_SUS_N); - if (!sus_new && sus_last) { + if (!gpio_get(&SLP_SUS_N)) DEBUG("%02X: SLP_SUS# asserted\n", main_cycle); - } else if (sus_new && !sus_last) { + else DEBUG("%02X: SLP_SUS# de-asserted\n", main_cycle); - } - sus_last = sus_new; #endif +} #endif // HAVE_SLP_SUS_N +// -------------------------------------------------------------------------- +// sus_pwrdn_event: handle SUSWARN_N / VW_SUS_PWRDN_ACK (S5 power-down ack) +// -------------------------------------------------------------------------- +void sus_pwrdn_event(void) { #if CONFIG_BUS_ESPI // ESPI systems must keep S5 planes powered unless VW_SUS_PWRDN_ACK is high if (vw_get(&VW_SUS_PWRDN_ACK) == VWS_HIGH) #elif HAVE_SUSWARN_N // EC must keep VccPRIM powered if SUSPWRDNACK is de-asserted low or system // state is S3 - static bool ack_last = false; - bool ack_new = gpio_get(&SUSWARN_N); -#if LEVEL >= LEVEL_DEBUG - if (ack_new && !ack_last) { - DEBUG("%02X: SUSPWRDNACK asserted\n", main_cycle); - } else if (!ack_new && ack_last) { - DEBUG("%02X: SUSPWRDNACK de-asserted\n", main_cycle); - } -#endif - ack_last = ack_new; - - if (ack_new) + if (gpio_get(&SUSWARN_N)) #endif // HAVE_SUSWARN_N { // Handle powering off when power not needed @@ -700,27 +689,32 @@ void power_event(void) { #endif // CONFIG_SECURITY } } +} +// -------------------------------------------------------------------------- +// lan_wakeup_event: handle LAN_WAKEUP_N (remote wake from G3, active-low) +// -------------------------------------------------------------------------- #if HAVE_LAN_WAKEUP_N - static bool wake_last = true; - bool wake_new = gpio_get(&LAN_WAKEUP_N); - if (!wake_new && wake_last) { +void lan_wakeup_event(void) { + if (!gpio_get(&LAN_WAKEUP_N)) { update_power_state(); DEBUG("%02X: LAN_WAKEUP# asserted\n", main_cycle); - if (power_state == POWER_STATE_G3) { + if (power_state == POWER_STATE_G3) power_on(); - } - } -#if LEVEL >= LEVEL_DEBUG - else if (wake_new && !wake_last) { + } else { DEBUG("%02X: LAN_WAKEUP# de-asserted\n", main_cycle); } -#endif - wake_last = wake_new; +} #endif // HAVE_LAN_WAKEUP_N +// -------------------------------------------------------------------------- +// power_led_event: update power and battery LEDs based on current state +// -------------------------------------------------------------------------- +void power_led_event(void) { static uint32_t last_time = 0; uint32_t time = time_get(); + bool ac_new = gpio_get(&ACIN_N); + if (power_state == POWER_STATE_S0) { #if USE_S0IX if (pep_hook & PEP_S0IX_FLAG) { diff --git a/src/board/system76/common/smfi.c b/src/board/system76/common/smfi.c index 72e8812d1..987e9c498 100644 --- a/src/board/system76/common/smfi.c +++ b/src/board/system76/common/smfi.c @@ -182,7 +182,7 @@ static enum Result cmd_keymap_get(void) { } } -static enum Result cmd_keymap_set(void) { +static enum Result cmd_keymap_set(void) __reentrant { int16_t layer = smfi_cmd[SMFI_CMD_DATA]; int16_t output = smfi_cmd[SMFI_CMD_DATA + 1]; int16_t input = smfi_cmd[SMFI_CMD_DATA + 2]; @@ -230,10 +230,11 @@ static enum Result cmd_security_set(void) { #endif // CONFIG_SECURITY // Command structure: [fan] [temp0] [duty0] ... [temp3] [duty3] -static enum Result cmd_fan_curve_set(void) { +static enum Result cmd_fan_curve_set(void) __reentrant { + int i; struct FanPoint points[4]; - for (int i = 0; i < 4; ++i) { + for (i = 0; i < 4; ++i) { points[i].temp = smfi_cmd[2 * i + SMFI_CMD_DATA + 1]; points[i].duty = smfi_cmd[2 * i + SMFI_CMD_DATA + 2] * 255 / 100; } @@ -265,7 +266,6 @@ static enum Result cmd_camera_enablement_set(void) { static enum Result cmd_wifi_bt_enablement_set(void) { wireless_power(smfi_cmd[SMFI_CMD_DATA]); - TRACE("WIRELESS %sABLED\n", smfi_cmd[SMFI_CMD_DATA] ? "EN" : "DIS"); return RES_OK; } diff --git a/src/board/system76/common/usbpd/tps65987.c b/src/board/system76/common/usbpd/tps65987.c index ca867a0ca..e74a2ace0 100644 --- a/src/board/system76/common/usbpd/tps65987.c +++ b/src/board/system76/common/usbpd/tps65987.c @@ -65,7 +65,7 @@ enum { #define PDO_CURRENT_MA(pdo) (((pdo) & 0x3FF) * 10) -static int16_t usbpd_current_limit(uint8_t address) { +static int16_t usbpd_current_limit(uint8_t address) __reentrant { uint8_t value[7] = { 0 }; int16_t res = i2c_get(&I2C_USBPD, address, REG_ACTIVE_CONTRACT_PDO, value, sizeof(value)); if (res == 7) { diff --git a/src/board/system76/darp8/include/board/gpio.h b/src/board/system76/darp8/include/board/gpio.h index 58cf82f8e..98aa20b7d 100644 --- a/src/board/system76/darp8/include/board/gpio.h +++ b/src/board/system76/darp8/include/board/gpio.h @@ -20,6 +20,7 @@ extern struct Gpio __code DD_ON; extern struct Gpio __code EC_EN; extern struct Gpio __code EC_RSMRST_N; extern struct Gpio __code H_PROCHOT_EC; +#define HAVE_JACK_IN_N 1 extern struct Gpio __code JACK_IN_N; extern struct Gpio __code LAN_WAKEUP_N; extern struct Gpio __code LED_ACIN; diff --git a/src/board/system76/galp5/include/board/gpio.h b/src/board/system76/galp5/include/board/gpio.h index 052b77db4..3279532af 100644 --- a/src/board/system76/galp5/include/board/gpio.h +++ b/src/board/system76/galp5/include/board/gpio.h @@ -21,6 +21,7 @@ extern struct Gpio __code DGPU_PWR_EN; extern struct Gpio __code EC_EN; extern struct Gpio __code EC_RSMRST_N; extern struct Gpio __code GC6_FB_EN; +#define HAVE_H_PROCHOT_EC 1 extern struct Gpio __code H_PROCHOT_EC; #define JACK_IN_N SINK_CTRL // XXX no way to tell if both adapters are in at once extern struct Gpio __code LAN_WAKEUP_N; @@ -39,6 +40,7 @@ extern struct Gpio __code PWR_BTN_N; extern struct Gpio __code PWR_SW_N; extern struct Gpio __code SB_KBCRST_N; #define HAVE_SCI_N 0 +#define HAVE_SINK_CTRL 1 extern struct Gpio __code SINK_CTRL; extern struct Gpio __code SLP_S0_N; extern struct Gpio __code SLP_SUS_N; diff --git a/src/board/system76/galp6/include/board/gpio.h b/src/board/system76/galp6/include/board/gpio.h index 3dd8d55d4..d2f0c7343 100644 --- a/src/board/system76/galp6/include/board/gpio.h +++ b/src/board/system76/galp6/include/board/gpio.h @@ -22,6 +22,7 @@ extern struct Gpio __code EC_EN; extern struct Gpio __code EC_RSMRST_N; extern struct Gpio __code GC6_FB_EN; extern struct Gpio __code H_PROCHOT_EC; +#define HAVE_JACK_IN_N 1 extern struct Gpio __code JACK_IN_N; extern struct Gpio __code LAN_WAKEUP_N; extern struct Gpio __code LED_ACIN; @@ -41,6 +42,8 @@ extern struct Gpio __code PWR_SW_N; #define HAVE_SCI_N 0 extern struct Gpio __code SLP_S0_N; extern struct Gpio __code SLP_SUS_N; +// SINK_CTRL is on GPC3 (same as nv40mz default mapping INT113) +#define HAVE_SINK_CTRL 1 extern struct Gpio __code SINK_CTRL; extern struct Gpio __code SMI_N; extern struct Gpio __code SUSB_N_PCH; diff --git a/src/ec/ite/ec.mk b/src/ec/ite/ec.mk index 70e6a855d..c476b52e7 100644 --- a/src/ec/ite/ec.mk +++ b/src/ec/ite/ec.mk @@ -17,9 +17,10 @@ SRAM_SIZE=2048 else ifeq ($(CONFIG_EC_ITE_IT5570E), y) CFLAGS+=-DCONFIG_EC_ITE_IT5570E=1 # SRAM is 6144 bytes, only 4096 bytes are mapped at address 0. Region at -# 0x0E00-0x1000 is used for AP communication. So this is brought down to 2048, -# which matches IT8587E limits -SRAM_SIZE=2048 +# 0x0E00-0x0FFF is used for AP communication, so the safe XSEG limit is +# 0x0E00 = 3584 bytes (was 2048 to match IT8587E, but IT5570E-only builds +# can use the full safe range). +SRAM_SIZE=3584 else $(error Unsupported EC) endif diff --git a/src/ec/ite/gpio.c b/src/ec/ite/gpio.c index 5e2c15df7..da4a32d57 100644 --- a/src/ec/ite/gpio.c +++ b/src/ec/ite/gpio.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only #include +#include +#include #include bool gpio_get(struct Gpio *gpio) { @@ -19,6 +21,150 @@ void gpio_set(struct Gpio *gpio, bool value) { } } +// Chrome EC-style: return WUEMR register pointer for a WUC group number. +volatile uint8_t __xdata *gpio_wuemr(uint8_t group) { + switch (group) { + case 1: return &WUEMR1; + case 2: return &WUEMR2; + case 3: return &WUEMR3; + case 4: return &WUEMR4; + case 6: return &WUEMR6; + case 7: return &WUEMR7; + case 8: return &WUEMR8; + case 9: return &WUEMR9; + case 10: return &WUEMR10; + case 11: return &WUEMR11; + case 12: return &WUEMR12; + case 13: return &WUEMR13; + case 14: return &WUEMR14; + default: return 0; + } +} + +// Chrome EC-style: return WUESR register pointer for a WUC group number. +volatile uint8_t __xdata *gpio_wuesr(uint8_t group) { + switch (group) { + case 1: return &WUESR1; + case 2: return &WUESR2; + case 3: return &WUESR3; + case 4: return &WUESR4; + case 6: return &WUESR6; + case 7: return &WUESR7; + case 8: return &WUESR8; + case 9: return &WUESR9; + case 10: return &WUESR10; + case 11: return &WUESR11; + case 12: return &WUESR12; + case 13: return &WUESR13; + case 14: return &WUESR14; + default: return 0; + } +} + +// Return IER register pointer for an IRQ number (IER_reg = irq / 8). +volatile uint8_t __xdata *gpio_ier(uint8_t irq) { + switch (irq >> 3) { + case 0: return &IER0; + case 1: return &IER1; + case 2: return &IER2; + case 3: return &IER3; + case 4: return &IER4; + case 5: return &IER5; + case 6: return &IER6; + case 7: return &IER7; + case 8: return &IER8; + case 9: return &IER9; + case 10: return &IER10; + case 11: return &IER11; + case 12: return &IER12; + case 13: return &IER13; + case 14: return &IER14; + case 15: return &IER15; + case 16: return &IER16; + case 17: return &IER17; + case 18: return &IER18; +#if CONFIG_EC_ITE_IT5570E + case 19: return &IER19; +#endif + default: return 0; + } +} + +// Return ISR register pointer for an IRQ number (ISR_reg = irq / 8). +volatile uint8_t __xdata *gpio_isr(uint8_t irq) { + switch (irq >> 3) { + case 0: return &ISR0; + case 1: return &ISR1; + case 2: return &ISR2; + case 3: return &ISR3; + case 4: return &ISR4; + case 5: return &ISR5; + case 6: return &ISR6; + case 7: return &ISR7; + case 8: return &ISR8; + case 9: return &ISR9; + case 10: return &ISR10; + case 11: return &ISR11; + case 12: return &ISR12; + case 13: return &ISR13; + case 14: return &ISR14; + case 15: return &ISR15; + case 16: return &ISR16; + case 17: return &ISR17; + case 18: return &ISR18; +#if CONFIG_EC_ITE_IT5570E + case 19: return &ISR19; +#endif + default: return 0; + } +} + +void gpio_irq_enable(const struct Gpio *gpio) { + volatile uint8_t __xdata *wuemr; + volatile uint8_t __xdata *wuesr; + volatile uint8_t __xdata *ier; + uint8_t mask; + + if (!gpio->wuc_group) return; + + wuemr = gpio_wuemr(gpio->wuc_group); + wuesr = gpio_wuesr(gpio->wuc_group); + ier = gpio_ier(gpio->irq); + if (!wuemr || !wuesr || !ier) return; + + mask = BIT(gpio->wuc_bit); + // Arm for the NEXT transition from the current pin state for any-edge detection. + // WUEMR bit=0 = falling edge, bit=1 = rising edge. + if (*(gpio->data) & gpio->value) + *wuemr |= mask; // pin HIGH: arm for falling edge (bit=1=falling, next transition) + else + *wuemr &= ~mask; // pin LOW: arm for rising edge (bit=0=rising, next transition) + *wuesr = mask; // clear sense register + *ier |= BIT(gpio->irq & 7U); +} + +void gpio_irq_ack(const struct Gpio *gpio) { + volatile uint8_t __xdata *wuemr; + volatile uint8_t __xdata *wuesr; + volatile uint8_t __xdata *isr_r; + uint8_t wuc_mask; + + if (!gpio->wuc_group) return; + + wuemr = gpio_wuemr(gpio->wuc_group); + wuesr = gpio_wuesr(gpio->wuc_group); + isr_r = gpio_isr(gpio->irq); + if (!wuemr || !wuesr || !isr_r) return; + + wuc_mask = BIT(gpio->wuc_bit); + // Double-clear prevents spurious re-trigger when toggling edge direction + // while the GPIO is already at the new edge's starting level (WUC_ACK). + *wuesr = wuc_mask; + *wuemr ^= wuc_mask; // toggle edge direction for any-edge detection + *wuesr = wuc_mask; + *isr_r = BIT(gpio->irq & 7U); // clear INTC ISR latch +} + #ifdef GPIO_DEBUG static void gpio_debug_bank( char *bank, diff --git a/src/ec/ite/i2c.c b/src/ec/ite/i2c.c index c1ef73dba..69d5fa8b6 100644 --- a/src/ec/ite/i2c.c +++ b/src/ec/ite/i2c.c @@ -47,8 +47,13 @@ void i2c_reset(struct I2C *i2c, bool kill) { // Set kill bit if (kill) *(i2c->hoctl) |= BIT(1); - // Wait for host to finish - while (*(i2c->hosta) & HOSTA_BUSY) {} + // Wait for host to finish, with timeout to avoid hanging if the + // bus is held by external hardware (e.g. DBGR/SMB debugger). + uint32_t timeout; + for (timeout = I2C_TIMEOUT; timeout > 0; timeout--) { + if (!(*(i2c->hosta) & HOSTA_BUSY)) + break; + } } // Clear status register *(i2c->hosta) = *(i2c->hosta); @@ -95,7 +100,7 @@ void i2c_stop(struct I2C *i2c) { i2c_reset(i2c, false); } -static int16_t i2c_transaction(struct I2C *i2c, uint8_t *data, uint16_t length, bool read) { +static int16_t i2c_transaction(struct I2C *i2c, uint8_t *data, uint16_t length, bool read) __reentrant { uint16_t i; for (i = 0; i < length; i++) { if (read) { diff --git a/src/ec/ite/include/ec/gpio.h b/src/ec/ite/include/ec/gpio.h index e821b63f7..231c40eab 100644 --- a/src/ec/ite/include/ec/gpio.h +++ b/src/ec/ite/include/ec/gpio.h @@ -4,6 +4,7 @@ #define _EC_GPIO_H #include +#include #include #include @@ -21,20 +22,45 @@ struct Gpio { volatile uint8_t __xdata *mirror; volatile uint8_t __xdata *control; uint8_t value; + // WUC (Wake-Up Control) and INTC fields — populated from gpio_wuc.h table. + // wuc_group = 0 means this pin has no WUC/INTC capability. + uint8_t wuc_group; // WUC group number (1-14; 0 = none) + uint8_t wuc_bit; // bit within WUEMR/WUESR (0-7) + uint8_t irq; // INTC IRQ number (IER_reg * 8 + IER_bit) }; // clang-format off #define GPIO(BLOCK, NUMBER) { \ - .data = &GPDR ## BLOCK, \ - .mirror = &GPDMR ## BLOCK, \ - .control = &GPCR ## BLOCK ## NUMBER, \ - .value = BIT(NUMBER), \ + .data = &GPDR ## BLOCK, \ + .mirror = &GPDMR ## BLOCK, \ + .control = &GPCR ## BLOCK ## NUMBER, \ + .value = BIT(NUMBER), \ + .wuc_group = _GPIO_WUC_GROUP_ ## BLOCK ## NUMBER, \ + .wuc_bit = _GPIO_WUC_BIT_ ## BLOCK ## NUMBER, \ + .irq = _GPIO_WUC_IRQ_ ## BLOCK ## NUMBER, \ } // clang-format on bool gpio_get(struct Gpio *gpio); void gpio_set(struct Gpio *gpio, bool value); +// Chrome EC-style register accessors: compute WUEMR/WUESR from group number, +// IER/ISR from IRQ number. Return NULL for invalid/unsupported inputs. +volatile uint8_t __xdata *gpio_wuemr(uint8_t group); +volatile uint8_t __xdata *gpio_wuesr(uint8_t group); +volatile uint8_t __xdata *gpio_ier(uint8_t irq); +volatile uint8_t __xdata *gpio_isr(uint8_t irq); + +// Enable edge-detect interrupt for a GPIO. +// Sets rising edge initially, clears WUESR, and enables the IER bit. +// gpio->wuc_group must be non-zero; no-op otherwise. +void gpio_irq_enable(const struct Gpio *gpio); + +// Acknowledge a WUC-sourced interrupt from within the ISR. +// Toggles WUEMR edge direction (any-edge detection), double-clears WUESR +// to prevent spurious re-trigger, and clears the INTC ISR latch. +void gpio_irq_ack(const struct Gpio *gpio); + #ifdef GPIO_DEBUG void gpio_debug(void); #endif diff --git a/src/ec/ite/include/ec/gpio_wuc.h b/src/ec/ite/include/ec/gpio_wuc.h new file mode 100644 index 000000000..607c31196 --- /dev/null +++ b/src/ec/ite/include/ec/gpio_wuc.h @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: GPL-3.0-only + +// IT5570E GPIO → WUC → INTC mapping table +// +// For each GPIO pin, three macros define the interrupt routing: +// _GPIO_WUC_GROUP_ WUC group number (0 = no WUC / no INTC routing) +// _GPIO_WUC_BIT_ Bit position within WUEMR/WUESR (0-7) +// _GPIO_WUC_IRQ_ INTC IRQ number (= IER_reg * 8 + IER_bit) +// +// Source: IT5570E datasheet Table 7-6 "WUC Input Assignments" +// WUC bit formula: bit = (WUxx - group_start) where group_start = (group * 10) for +// groups 1-8, or (group * 8 + offset) for groups 9-14 (WU88-WU135). +// IRQ formula: IRQ = IER_reg * 8 + IER_bit +// +// These macros are used by the GPIO() initializer macro in gpio.h to populate +// the wuc_group, wuc_bit, and irq fields of struct Gpio. + +#ifndef _EC_GPIO_WUC_H +#define _EC_GPIO_WUC_H + +// clang-format off + +// ------------------------------------------------------------------------- +// Port A +// ------------------------------------------------------------------------- +// GPA0: WU91 (Group 9, bit 3), WKO[91] → INT96 → IER12[0] +#define _GPIO_WUC_GROUP_A0 9 +#define _GPIO_WUC_BIT_A0 3 +#define _GPIO_WUC_IRQ_A0 96 + +// GPA1: WU92 (Group 9, bit 4), WKO[92] → INT97 → IER12[1] +#define _GPIO_WUC_GROUP_A1 9 +#define _GPIO_WUC_BIT_A1 4 +#define _GPIO_WUC_IRQ_A1 97 + +// GPA2: WU93 (Group 9, bit 5), WKO[93] → INT98 → IER12[2] +#define _GPIO_WUC_GROUP_A2 9 +#define _GPIO_WUC_BIT_A2 5 +#define _GPIO_WUC_IRQ_A2 98 + +// GPA3: WU80 (Group 8, bit 0), WKO[80] → INT88 → IER11[0] +#define _GPIO_WUC_GROUP_A3 8 +#define _GPIO_WUC_BIT_A3 0 +#define _GPIO_WUC_IRQ_A3 88 + +// GPA4: WU81 (Group 8, bit 1), WKO[81] → INT89 → IER11[1] +#define _GPIO_WUC_GROUP_A4 8 +#define _GPIO_WUC_BIT_A4 1 +#define _GPIO_WUC_IRQ_A4 89 + +// GPA5: WU82 (Group 8, bit 2), WKO[82] → INT90 → IER11[2] +#define _GPIO_WUC_GROUP_A5 8 +#define _GPIO_WUC_BIT_A5 2 +#define _GPIO_WUC_IRQ_A5 90 + +// GPA6: WU83 (Group 8, bit 3), WKO[83] → INT91 → IER11[3] +#define _GPIO_WUC_GROUP_A6 8 +#define _GPIO_WUC_BIT_A6 3 +#define _GPIO_WUC_IRQ_A6 91 + +// GPA7: WU100 (Group 10, bit 4), WKO[100] → INT105 → IER13[1] +#define _GPIO_WUC_GROUP_A7 10 +#define _GPIO_WUC_BIT_A7 4 +#define _GPIO_WUC_IRQ_A7 105 + +// ------------------------------------------------------------------------- +// Port B +// ------------------------------------------------------------------------- +// GPB0: WU101 (Group 10, bit 5), WKO[101] → INT106 → IER13[2] +#define _GPIO_WUC_GROUP_B0 10 +#define _GPIO_WUC_BIT_B0 5 +#define _GPIO_WUC_IRQ_B0 106 + +// GPB1: WU102 (Group 10, bit 6), WKO[102] → INT107 → IER13[3] +#define _GPIO_WUC_GROUP_B1 10 +#define _GPIO_WUC_BIT_B1 6 +#define _GPIO_WUC_IRQ_B1 107 + +// GPB2: WU84 (Group 8, bit 4), WKO[84] → INT92 → IER11[4] +#define _GPIO_WUC_GROUP_B2 8 +#define _GPIO_WUC_BIT_B2 4 +#define _GPIO_WUC_IRQ_B2 92 + +// GPB3: WU25/PWRSW (Group 2, bit 5), WKO[25] → INT14 → IER1[6] +// Note: GPB3 is also WU103 (Group 10, bit 7, INT108) but WU25 is the primary entry. +#define _GPIO_WUC_GROUP_B3 2 +#define _GPIO_WUC_BIT_B3 5 +#define _GPIO_WUC_IRQ_B3 14 + +// GPB4: WU94 (Group 9, bit 6), WKO[94] → INT99 → IER12[3] +#define _GPIO_WUC_GROUP_B4 9 +#define _GPIO_WUC_BIT_B4 6 +#define _GPIO_WUC_IRQ_B4 99 + +// GPB5: WU104 (Group 11, bit 0), WKO[104] → INT109 → IER13[5] +#define _GPIO_WUC_GROUP_B5 11 +#define _GPIO_WUC_BIT_B5 0 +#define _GPIO_WUC_IRQ_B5 109 + +// GPB6: WU105 (Group 11, bit 1), WKO[105] → INT110 → IER13[6] +#define _GPIO_WUC_GROUP_B6 11 +#define _GPIO_WUC_BIT_B6 1 +#define _GPIO_WUC_IRQ_B6 110 + +// GPB7: no WUC capability +#define _GPIO_WUC_GROUP_B7 0 +#define _GPIO_WUC_BIT_B7 0 +#define _GPIO_WUC_IRQ_B7 0 + +// ------------------------------------------------------------------------- +// Port C +// ------------------------------------------------------------------------- +// GPC0: WU85 (Group 8, bit 5), WKO[85] → INT93 → IER11[5] +#define _GPIO_WUC_GROUP_C0 8 +#define _GPIO_WUC_BIT_C0 5 +#define _GPIO_WUC_IRQ_C0 93 + +// GPC1: WU107 (Group 11, bit 3), WKO[107] → INT112 → IER14[0] +#define _GPIO_WUC_GROUP_C1 11 +#define _GPIO_WUC_BIT_C1 3 +#define _GPIO_WUC_IRQ_C1 112 + +// GPC2: WU95 (Group 9, bit 7), WKO[95] → INT100 → IER12[4] +#define _GPIO_WUC_GROUP_C2 9 +#define _GPIO_WUC_BIT_C2 7 +#define _GPIO_WUC_IRQ_C2 100 + +// GPC3: WU108 (Group 11, bit 4), WKO[108] → INT113 → IER14[1] +#define _GPIO_WUC_GROUP_C3 11 +#define _GPIO_WUC_BIT_C3 4 +#define _GPIO_WUC_IRQ_C3 113 + +// GPC4: WU22 (Group 2, bit 2), WKO[22] → INT21 → IER2[5] +#define _GPIO_WUC_GROUP_C4 2 +#define _GPIO_WUC_BIT_C4 2 +#define _GPIO_WUC_IRQ_C4 21 + +// GPC5: WU109 (Group 11, bit 5), WKO[109] → INT114 → IER14[2] +#define _GPIO_WUC_GROUP_C5 11 +#define _GPIO_WUC_BIT_C5 5 +#define _GPIO_WUC_IRQ_C5 114 + +// GPC6: WU23 (Group 2, bit 3), WKO[23] → INT6 → IER0[6] +#define _GPIO_WUC_GROUP_C6 2 +#define _GPIO_WUC_BIT_C6 3 +#define _GPIO_WUC_IRQ_C6 6 + +// GPC7: WU86 (Group 8, bit 6), WKO[86] → INT94 → IER11[6] +#define _GPIO_WUC_GROUP_C7 8 +#define _GPIO_WUC_BIT_C7 6 +#define _GPIO_WUC_IRQ_C7 94 + +// ------------------------------------------------------------------------- +// Port D +// ------------------------------------------------------------------------- +// GPD0: WU20 (Group 2, bit 0), WKO[20] → INT1 → IER0[1] +#define _GPIO_WUC_GROUP_D0 2 +#define _GPIO_WUC_BIT_D0 0 +#define _GPIO_WUC_IRQ_D0 1 + +// GPD1: WU21 (Group 2, bit 1), WKO[21] → INT31 → IER3[7] +#define _GPIO_WUC_GROUP_D1 2 +#define _GPIO_WUC_BIT_D1 1 +#define _GPIO_WUC_IRQ_D1 31 + +// GPD2: WU24 (Group 2, bit 4), WKO[24] → INT17 → IER2[1] +#define _GPIO_WUC_GROUP_D2 2 +#define _GPIO_WUC_BIT_D2 4 +#define _GPIO_WUC_IRQ_D2 17 + +// GPD3: WU110 (Group 11, bit 6), WKO[110] → INT115 → IER14[3] +#define _GPIO_WUC_GROUP_D3 11 +#define _GPIO_WUC_BIT_D3 6 +#define _GPIO_WUC_IRQ_D3 115 + +// GPD4: WU111 (Group 11, bit 7), WKO[111] → INT116 → IER14[4] +#define _GPIO_WUC_GROUP_D4 11 +#define _GPIO_WUC_BIT_D4 7 +#define _GPIO_WUC_IRQ_D4 116 + +// GPD5: WU112 (Group 12, bit 0), WKO[112] → INT117 → IER14[5] +#define _GPIO_WUC_GROUP_D5 12 +#define _GPIO_WUC_BIT_D5 0 +#define _GPIO_WUC_IRQ_D5 117 + +// GPD6: WU113 (Group 12, bit 1), WKO[113] → INT118 → IER14[6] +#define _GPIO_WUC_GROUP_D6 12 +#define _GPIO_WUC_BIT_D6 1 +#define _GPIO_WUC_IRQ_D6 118 + +// GPD7: WU87 (Group 8, bit 7), WKO[87] → INT95 → IER11[7] +#define _GPIO_WUC_GROUP_D7 8 +#define _GPIO_WUC_BIT_D7 7 +#define _GPIO_WUC_IRQ_D7 95 + +// ------------------------------------------------------------------------- +// Port E +// ------------------------------------------------------------------------- +// GPE0: WU70 (Group 7, bit 0), WKO[70] → INT72 → IER9[0] +#define _GPIO_WUC_GROUP_E0 7 +#define _GPIO_WUC_BIT_E0 0 +#define _GPIO_WUC_IRQ_E0 72 + +// GPE1: WU71 (Group 7, bit 1), WKO[71] → INT73 → IER9[1] +#define _GPIO_WUC_GROUP_E1 7 +#define _GPIO_WUC_BIT_E1 1 +#define _GPIO_WUC_IRQ_E1 73 + +// GPE2: WU72 (Group 7, bit 2), WKO[72] → INT74 → IER9[2] +#define _GPIO_WUC_GROUP_E2 7 +#define _GPIO_WUC_BIT_E2 2 +#define _GPIO_WUC_IRQ_E2 74 + +// GPE3: WU73 (Group 7, bit 3), WKO[73] → INT75 → IER9[3] +#define _GPIO_WUC_GROUP_E3 7 +#define _GPIO_WUC_BIT_E3 3 +#define _GPIO_WUC_IRQ_E3 75 + +// GPE4: WU114 (Group 12, bit 2), WKO[114] → INT119 → IER14[7] +#define _GPIO_WUC_GROUP_E4 12 +#define _GPIO_WUC_BIT_E4 2 +#define _GPIO_WUC_IRQ_E4 119 + +// GPE5: WU40 (Group 4, bit 0) — WUC input but no INTC routing +#define _GPIO_WUC_GROUP_E5 0 +#define _GPIO_WUC_BIT_E5 0 +#define _GPIO_WUC_IRQ_E5 0 + +// GPE6: WU45 (Group 4, bit 5) — WUC input but no INTC routing +#define _GPIO_WUC_GROUP_E6 0 +#define _GPIO_WUC_BIT_E6 0 +#define _GPIO_WUC_IRQ_E6 0 + +// GPE7: WU46 (Group 4, bit 6) — WUC input but no INTC routing +#define _GPIO_WUC_GROUP_E7 0 +#define _GPIO_WUC_BIT_E7 0 +#define _GPIO_WUC_IRQ_E7 0 + +// ------------------------------------------------------------------------- +// Port F +// ------------------------------------------------------------------------- +// GPF0: WU96 (Group 10, bit 0), WKO[96] → INT101 → IER12[5] +#define _GPIO_WUC_GROUP_F0 10 +#define _GPIO_WUC_BIT_F0 0 +#define _GPIO_WUC_IRQ_F0 101 + +// GPF1: WU97 (Group 10, bit 1), WKO[97] → INT102 → IER12[6] +#define _GPIO_WUC_GROUP_F1 10 +#define _GPIO_WUC_BIT_F1 1 +#define _GPIO_WUC_IRQ_F1 102 + +// GPF2: WU98 (Group 10, bit 2), WKO[98] → INT103 → IER12[7] +#define _GPIO_WUC_GROUP_F2 10 +#define _GPIO_WUC_BIT_F2 2 +#define _GPIO_WUC_IRQ_F2 103 + +// GPF3: WU99 (Group 10, bit 3), WKO[99] → INT104 → IER13[0] +#define _GPIO_WUC_GROUP_F3 10 +#define _GPIO_WUC_BIT_F3 3 +#define _GPIO_WUC_IRQ_F3 104 + +// GPF4: WU64 (Group 6, bit 4), WKO[64] → INT52 → IER6[4] +#define _GPIO_WUC_GROUP_F4 6 +#define _GPIO_WUC_BIT_F4 4 +#define _GPIO_WUC_IRQ_F4 52 + +// GPF5: WU65 (Group 6, bit 5), WKO[65] → INT53 → IER6[5] +#define _GPIO_WUC_GROUP_F5 6 +#define _GPIO_WUC_BIT_F5 5 +#define _GPIO_WUC_IRQ_F5 53 + +// GPF6: WU66 (Group 6, bit 6), WKO[66] → INT54 → IER6[6] +#define _GPIO_WUC_GROUP_F6 6 +#define _GPIO_WUC_BIT_F6 6 +#define _GPIO_WUC_IRQ_F6 54 + +// GPF7: WU67 (Group 6, bit 7), WKO[67] → INT55 → IER6[7] +#define _GPIO_WUC_GROUP_F7 6 +#define _GPIO_WUC_BIT_F7 7 +#define _GPIO_WUC_IRQ_F7 55 + +// ------------------------------------------------------------------------- +// Port G +// ------------------------------------------------------------------------- +// GPG0: WU115 (Group 12, bit 3), WKO[115] → INT120 → IER15[0] +#define _GPIO_WUC_GROUP_G0 12 +#define _GPIO_WUC_BIT_G0 3 +#define _GPIO_WUC_IRQ_G0 120 + +// GPG1: WU116 (Group 12, bit 4), WKO[116] → INT121 → IER15[1] +#define _GPIO_WUC_GROUP_G1 12 +#define _GPIO_WUC_BIT_G1 4 +#define _GPIO_WUC_IRQ_G1 121 + +// GPG2: WU117 (Group 12, bit 5), WKO[117] → INT122 → IER15[2] +#define _GPIO_WUC_GROUP_G2 12 +#define _GPIO_WUC_BIT_G2 5 +#define _GPIO_WUC_IRQ_G2 122 + +// GPG3: no WUC capability +#define _GPIO_WUC_GROUP_G3 0 +#define _GPIO_WUC_BIT_G3 0 +#define _GPIO_WUC_IRQ_G3 0 + +// GPG4: no WUC capability +#define _GPIO_WUC_GROUP_G4 0 +#define _GPIO_WUC_BIT_G4 0 +#define _GPIO_WUC_IRQ_G4 0 + +// GPG5: no WUC capability +#define _GPIO_WUC_GROUP_G5 0 +#define _GPIO_WUC_BIT_G5 0 +#define _GPIO_WUC_IRQ_G5 0 + +// GPG6: WU118 (Group 12, bit 6), WKO[118] → INT123 → IER15[3] +#define _GPIO_WUC_GROUP_G6 12 +#define _GPIO_WUC_BIT_G6 6 +#define _GPIO_WUC_IRQ_G6 123 + +// GPG7: no WUC capability +#define _GPIO_WUC_GROUP_G7 0 +#define _GPIO_WUC_BIT_G7 0 +#define _GPIO_WUC_IRQ_G7 0 + +// ------------------------------------------------------------------------- +// Port H +// ------------------------------------------------------------------------- +// GPH0: WU60 (Group 6, bit 0), WKO[60] → INT48 → IER6[0] +#define _GPIO_WUC_GROUP_H0 6 +#define _GPIO_WUC_BIT_H0 0 +#define _GPIO_WUC_IRQ_H0 48 + +// GPH1: WU61 (Group 6, bit 1), WKO[61] → INT49 → IER6[1] +#define _GPIO_WUC_GROUP_H1 6 +#define _GPIO_WUC_BIT_H1 1 +#define _GPIO_WUC_IRQ_H1 49 + +// GPH2: WU62 (Group 6, bit 2), WKO[62] → INT50 → IER6[2] +#define _GPIO_WUC_GROUP_H2 6 +#define _GPIO_WUC_BIT_H2 2 +#define _GPIO_WUC_IRQ_H2 50 + +// GPH3: WU63 (Group 6, bit 3), WKO[63] → INT51 → IER6[3] +#define _GPIO_WUC_GROUP_H3 6 +#define _GPIO_WUC_BIT_H3 3 +#define _GPIO_WUC_IRQ_H3 51 + +// GPH4: WU88 (Group 9, bit 0), WKO[88] → INT85 → IER10[5] +#define _GPIO_WUC_GROUP_H4 9 +#define _GPIO_WUC_BIT_H4 0 +#define _GPIO_WUC_IRQ_H4 85 + +// GPH5: WU89 (Group 9, bit 1), WKO[89] → INT86 → IER10[6] +#define _GPIO_WUC_GROUP_H5 9 +#define _GPIO_WUC_BIT_H5 1 +#define _GPIO_WUC_IRQ_H5 86 + +// GPH6: WU90 (Group 9, bit 2), WKO[90] → INT87 → IER10[7] +#define _GPIO_WUC_GROUP_H6 9 +#define _GPIO_WUC_BIT_H6 2 +#define _GPIO_WUC_IRQ_H6 87 + +// GPH7: WU127 (Group 13, bit 7), WKO[127] → INT148 → IER18[4] +#define _GPIO_WUC_GROUP_H7 13 +#define _GPIO_WUC_BIT_H7 7 +#define _GPIO_WUC_IRQ_H7 148 + +// ------------------------------------------------------------------------- +// Port I +// ------------------------------------------------------------------------- +// GPI0: WU119 (Group 12, bit 7), WKO[119] → INT124 → IER15[4] +#define _GPIO_WUC_GROUP_I0 12 +#define _GPIO_WUC_BIT_I0 7 +#define _GPIO_WUC_IRQ_I0 124 + +// GPI1: WU120 (Group 13, bit 0), WKO[120] → INT125 → IER15[5] +#define _GPIO_WUC_GROUP_I1 13 +#define _GPIO_WUC_BIT_I1 0 +#define _GPIO_WUC_IRQ_I1 125 + +// GPI2: WU121 (Group 13, bit 1), WKO[121] → INT126 → IER15[6] +#define _GPIO_WUC_GROUP_I2 13 +#define _GPIO_WUC_BIT_I2 1 +#define _GPIO_WUC_IRQ_I2 126 + +// GPI3: WU122 (Group 13, bit 2), WKO[122] → INT127 → IER15[7] +#define _GPIO_WUC_GROUP_I3 13 +#define _GPIO_WUC_BIT_I3 2 +#define _GPIO_WUC_IRQ_I3 127 + +// GPI4: WU74 (Group 7, bit 4), WKO[74] → INT76 → IER9[4] +#define _GPIO_WUC_GROUP_I4 7 +#define _GPIO_WUC_BIT_I4 4 +#define _GPIO_WUC_IRQ_I4 76 + +// GPI5: WU75 (Group 7, bit 5), WKO[75] → INT77 → IER9[5] +#define _GPIO_WUC_GROUP_I5 7 +#define _GPIO_WUC_BIT_I5 5 +#define _GPIO_WUC_IRQ_I5 77 + +// GPI6: WU76 (Group 7, bit 6), WKO[76] → INT78 → IER9[6] +#define _GPIO_WUC_GROUP_I6 7 +#define _GPIO_WUC_BIT_I6 6 +#define _GPIO_WUC_IRQ_I6 78 + +// GPI7: WU77 (Group 7, bit 7), WKO[77] → INT79 → IER9[7] +#define _GPIO_WUC_GROUP_I7 7 +#define _GPIO_WUC_BIT_I7 7 +#define _GPIO_WUC_IRQ_I7 79 + +// ------------------------------------------------------------------------- +// Port J +// ------------------------------------------------------------------------- +// GPJ0: WU128 (Group 14, bit 0), WKO[128] → INT128 → IER16[0] +#define _GPIO_WUC_GROUP_J0 14 +#define _GPIO_WUC_BIT_J0 0 +#define _GPIO_WUC_IRQ_J0 128 + +// GPJ1: WU129 (Group 14, bit 1), WKO[129] → INT129 → IER16[1] +#define _GPIO_WUC_GROUP_J1 14 +#define _GPIO_WUC_BIT_J1 1 +#define _GPIO_WUC_IRQ_J1 129 + +// GPJ2: WU130 (Group 14, bit 2), WKO[130] → INT130 → IER16[2] +#define _GPIO_WUC_GROUP_J2 14 +#define _GPIO_WUC_BIT_J2 2 +#define _GPIO_WUC_IRQ_J2 130 + +// GPJ3: WU131 (Group 14, bit 3), WKO[131] → INT131 → IER16[3] +#define _GPIO_WUC_GROUP_J3 14 +#define _GPIO_WUC_BIT_J3 3 +#define _GPIO_WUC_IRQ_J3 131 + +// GPJ4: WU132 (Group 14, bit 4), WKO[132] → INT132 → IER16[4] +#define _GPIO_WUC_GROUP_J4 14 +#define _GPIO_WUC_BIT_J4 4 +#define _GPIO_WUC_IRQ_J4 132 + +// GPJ5: WU133 (Group 14, bit 5), WKO[133] → INT133 → IER16[5] +#define _GPIO_WUC_GROUP_J5 14 +#define _GPIO_WUC_BIT_J5 5 +#define _GPIO_WUC_IRQ_J5 133 + +// GPJ6: WU134 (Group 14, bit 6), WKO[134] → INT134 → IER16[6] +#define _GPIO_WUC_GROUP_J6 14 +#define _GPIO_WUC_BIT_J6 6 +#define _GPIO_WUC_IRQ_J6 134 + +// GPJ7: WU135 (Group 14, bit 7), WKO[135] → INT135 → IER16[7] +#define _GPIO_WUC_GROUP_J7 14 +#define _GPIO_WUC_BIT_J7 7 +#define _GPIO_WUC_IRQ_J7 135 + +// ------------------------------------------------------------------------- +// Port M — no WUC capability on any pin +// ------------------------------------------------------------------------- +#define _GPIO_WUC_GROUP_M0 0 +#define _GPIO_WUC_BIT_M0 0 +#define _GPIO_WUC_IRQ_M0 0 + +#define _GPIO_WUC_GROUP_M1 0 +#define _GPIO_WUC_BIT_M1 0 +#define _GPIO_WUC_IRQ_M1 0 + +#define _GPIO_WUC_GROUP_M2 0 +#define _GPIO_WUC_BIT_M2 0 +#define _GPIO_WUC_IRQ_M2 0 + +#define _GPIO_WUC_GROUP_M3 0 +#define _GPIO_WUC_BIT_M3 0 +#define _GPIO_WUC_IRQ_M3 0 + +#define _GPIO_WUC_GROUP_M4 0 +#define _GPIO_WUC_BIT_M4 0 +#define _GPIO_WUC_IRQ_M4 0 + +#define _GPIO_WUC_GROUP_M5 0 +#define _GPIO_WUC_BIT_M5 0 +#define _GPIO_WUC_IRQ_M5 0 + +#define _GPIO_WUC_GROUP_M6 0 +#define _GPIO_WUC_BIT_M6 0 +#define _GPIO_WUC_IRQ_M6 0 + +// clang-format on + +#endif // _EC_GPIO_WUC_H diff --git a/src/ec/ite/include/ec/kbscan.h b/src/ec/ite/include/ec/kbscan.h index 180dc1354..553b838f5 100644 --- a/src/ec/ite/include/ec/kbscan.h +++ b/src/ec/ite/include/ec/kbscan.h @@ -41,6 +41,9 @@ volatile uint8_t __xdata __at(0x1D1F) KSO15LSDR; volatile uint8_t __xdata __at(0x1D20) KSO16LSDR; volatile uint8_t __xdata __at(0x1D21) KSO17LSDR; +// Array overlay for indexed access in ISR (KSO0LSDR..KSO17LSDR) +volatile uint8_t __xdata __at(0x1D10) KSO_LSDR[18]; + volatile uint8_t __xdata __at(0x1D22) SDC1R; volatile uint8_t __xdata __at(0x1D23) SDC2R; volatile uint8_t __xdata __at(0x1D24) SDC3R; diff --git a/src/ec/ite/include/ec/wuc.h b/src/ec/ite/include/ec/wuc.h index 800fdba7b..dc43545bf 100644 --- a/src/ec/ite/include/ec/wuc.h +++ b/src/ec/ite/include/ec/wuc.h @@ -11,6 +11,23 @@ #include +#ifndef BIT +#define BIT(n) (1U << (n)) +#endif + +// Enable WUC edge detection on a pin. WUEMR bit=1 = falling edge, bit=0 = rising edge. +// WUC_ENABLE sets bit=1 (falling edge); use WUEMR &= ~BIT(n) for rising edge initially. +#define WUC_ENABLE(wuemr, bit) ((wuemr) |= BIT(bit)) +// ISR ack for any-edge detection: clear WUESR, toggle edge direction, clear WUESR again. +// The double-clear prevents spurious re-trigger when toggling WUEMR while the GPIO is +// already at the new edge's starting level. +#define WUC_ACK(wuemr, wuesr, bit) \ + do { \ + (wuesr) = BIT(bit); \ + (wuemr) ^= BIT(bit); \ + (wuesr) = BIT(bit); \ + } while (0) + volatile uint8_t __xdata __at(0x1B00) WUEMR1; volatile uint8_t __xdata __at(0x1B04) WUESR1; volatile uint8_t __xdata __at(0x1B08) WUENR1;