| /* Copyright 2017 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* ESPI module for Chrome EC */ |
| |
| #include "console.h" |
| #include "espi.h" |
| #include "gpio.h" |
| #include "hooks.h" |
| #include "port80.h" |
| #include "power.h" |
| #include "registers.h" |
| #include "system.h" |
| #include "system_boot_time.h" |
| #include "task.h" |
| #include "uart.h" |
| #include "util.h" |
| |
| /* Console output macros */ |
| #define CPRINTS(format, args...) cprints(CC_LPC, format, ##args) |
| |
| struct vw_channel_t { |
| uint8_t index; /* VW index of signal */ |
| uint8_t level_mask; /* level bit of signal */ |
| uint8_t valid_mask; /* valid bit of signal */ |
| }; |
| |
| /* VW settings after the controller enables the VW channel. */ |
| static const struct vw_channel_t en_vw_setting[] = { |
| /* EC sends SUS_ACK# = 1 VW to PCH. That does not apply to GLK SoC. */ |
| #ifndef CONFIG_CHIPSET_GEMINILAKE |
| { ESPI_SYSTEM_EVENT_VW_IDX_40, VW_LEVEL_FIELD(0), |
| VW_VALID_FIELD(VW_IDX_40_SUS_ACK) }, |
| #endif |
| }; |
| |
| /* VW settings after the controller enables the OOB channel. */ |
| static const struct vw_channel_t en_oob_setting[] = { |
| { ESPI_SYSTEM_EVENT_VW_IDX_4, VW_LEVEL_FIELD(0), |
| VW_VALID_FIELD(VW_IDX_4_OOB_RST_ACK) }, |
| }; |
| |
| /* VW settings after the controller enables the flash channel. */ |
| static const struct vw_channel_t en_flash_setting[] = { |
| { ESPI_SYSTEM_EVENT_VW_IDX_5, VW_LEVEL_FIELD(VW_IDX_5_BTLD_STATUS_DONE), |
| VW_VALID_FIELD(VW_IDX_5_BTLD_STATUS_DONE) }, |
| }; |
| |
| /* VW settings at host startup */ |
| static const struct vw_channel_t vw_host_startup_setting[] = { |
| { ESPI_SYSTEM_EVENT_VW_IDX_6, |
| VW_LEVEL_FIELD(VW_IDX_6_SCI | VW_IDX_6_SMI | VW_IDX_6_RCIN | |
| VW_IDX_6_HOST_RST_ACK), |
| VW_VALID_FIELD(VW_IDX_6_SCI | VW_IDX_6_SMI | VW_IDX_6_RCIN | |
| VW_IDX_6_HOST_RST_ACK) }, |
| }; |
| |
| #define VW_CHAN(name, idx, level, valid) \ |
| [(name - VW_SIGNAL_START)] = { idx, level, valid } |
| |
| /* VW signals used in eSPI (NOTE: must match order of enum espi_vw_signal). */ |
| static const struct vw_channel_t vw_channel_list[] = { |
| /* index 02h: controller to peripheral. */ |
| VW_CHAN(VW_SLP_S3_L, ESPI_SYSTEM_EVENT_VW_IDX_2, |
| VW_LEVEL_FIELD(VW_IDX_2_SLP_S3), |
| VW_VALID_FIELD(VW_IDX_2_SLP_S3)), |
| VW_CHAN(VW_SLP_S4_L, ESPI_SYSTEM_EVENT_VW_IDX_2, |
| VW_LEVEL_FIELD(VW_IDX_2_SLP_S4), |
| VW_VALID_FIELD(VW_IDX_2_SLP_S4)), |
| VW_CHAN(VW_SLP_S5_L, ESPI_SYSTEM_EVENT_VW_IDX_2, |
| VW_LEVEL_FIELD(VW_IDX_2_SLP_S5), |
| VW_VALID_FIELD(VW_IDX_2_SLP_S5)), |
| /* index 03h: controller to peripheral. */ |
| VW_CHAN(VW_SUS_STAT_L, ESPI_SYSTEM_EVENT_VW_IDX_3, |
| VW_LEVEL_FIELD(VW_IDX_3_SUS_STAT), |
| VW_VALID_FIELD(VW_IDX_3_SUS_STAT)), |
| VW_CHAN(VW_PLTRST_L, ESPI_SYSTEM_EVENT_VW_IDX_3, |
| VW_LEVEL_FIELD(VW_IDX_3_PLTRST), |
| VW_VALID_FIELD(VW_IDX_3_PLTRST)), |
| VW_CHAN(VW_OOB_RST_WARN, ESPI_SYSTEM_EVENT_VW_IDX_3, |
| VW_LEVEL_FIELD(VW_IDX_3_OOB_RST_WARN), |
| VW_VALID_FIELD(VW_IDX_3_OOB_RST_WARN)), |
| /* index 04h: peripheral to controller. */ |
| VW_CHAN(VW_OOB_RST_ACK, ESPI_SYSTEM_EVENT_VW_IDX_4, |
| VW_LEVEL_FIELD(VW_IDX_4_OOB_RST_ACK), |
| VW_VALID_FIELD(VW_IDX_4_OOB_RST_ACK)), |
| VW_CHAN(VW_WAKE_L, ESPI_SYSTEM_EVENT_VW_IDX_4, |
| VW_LEVEL_FIELD(VW_IDX_4_WAKE), VW_VALID_FIELD(VW_IDX_4_WAKE)), |
| VW_CHAN(VW_PME_L, ESPI_SYSTEM_EVENT_VW_IDX_4, |
| VW_LEVEL_FIELD(VW_IDX_4_PME), VW_VALID_FIELD(VW_IDX_4_PME)), |
| /* index 05h: peripheral to controller. */ |
| VW_CHAN(VW_ERROR_FATAL, ESPI_SYSTEM_EVENT_VW_IDX_5, |
| VW_LEVEL_FIELD(VW_IDX_5_FATAL), VW_VALID_FIELD(VW_IDX_5_FATAL)), |
| VW_CHAN(VW_ERROR_NON_FATAL, ESPI_SYSTEM_EVENT_VW_IDX_5, |
| VW_LEVEL_FIELD(VW_IDX_5_NON_FATAL), |
| VW_VALID_FIELD(VW_IDX_5_NON_FATAL)), |
| VW_CHAN(VW_PERIPHERAL_BTLD_STATUS_DONE, ESPI_SYSTEM_EVENT_VW_IDX_5, |
| VW_LEVEL_FIELD(VW_IDX_5_BTLD_STATUS_DONE), |
| VW_VALID_FIELD(VW_IDX_5_BTLD_STATUS_DONE)), |
| /* index 06h: peripheral to controller. */ |
| VW_CHAN(VW_SCI_L, ESPI_SYSTEM_EVENT_VW_IDX_6, |
| VW_LEVEL_FIELD(VW_IDX_6_SCI), VW_VALID_FIELD(VW_IDX_6_SCI)), |
| VW_CHAN(VW_SMI_L, ESPI_SYSTEM_EVENT_VW_IDX_6, |
| VW_LEVEL_FIELD(VW_IDX_6_SMI), VW_VALID_FIELD(VW_IDX_6_SMI)), |
| VW_CHAN(VW_RCIN_L, ESPI_SYSTEM_EVENT_VW_IDX_6, |
| VW_LEVEL_FIELD(VW_IDX_6_RCIN), VW_VALID_FIELD(VW_IDX_6_RCIN)), |
| VW_CHAN(VW_HOST_RST_ACK, ESPI_SYSTEM_EVENT_VW_IDX_6, |
| VW_LEVEL_FIELD(VW_IDX_6_HOST_RST_ACK), |
| VW_VALID_FIELD(VW_IDX_6_HOST_RST_ACK)), |
| /* index 07h: controller to peripheral. */ |
| VW_CHAN(VW_HOST_RST_WARN, ESPI_SYSTEM_EVENT_VW_IDX_7, |
| VW_LEVEL_FIELD(VW_IDX_7_HOST_RST_WARN), |
| VW_VALID_FIELD(VW_IDX_7_HOST_RST_WARN)), |
| /* index 40h: peripheral to controller. */ |
| VW_CHAN(VW_SUS_ACK, ESPI_SYSTEM_EVENT_VW_IDX_40, |
| VW_LEVEL_FIELD(VW_IDX_40_SUS_ACK), |
| VW_VALID_FIELD(VW_IDX_40_SUS_ACK)), |
| /* index 41h: controller to peripheral. */ |
| VW_CHAN(VW_SUS_WARN_L, ESPI_SYSTEM_EVENT_VW_IDX_41, |
| VW_LEVEL_FIELD(VW_IDX_41_SUS_WARN), |
| VW_VALID_FIELD(VW_IDX_41_SUS_WARN)), |
| VW_CHAN(VW_SUS_PWRDN_ACK_L, ESPI_SYSTEM_EVENT_VW_IDX_41, |
| VW_LEVEL_FIELD(VW_IDX_41_SUS_PWRDN_ACK), |
| VW_VALID_FIELD(VW_IDX_41_SUS_PWRDN_ACK)), |
| VW_CHAN(VW_SLP_A_L, ESPI_SYSTEM_EVENT_VW_IDX_41, |
| VW_LEVEL_FIELD(VW_IDX_41_SLP_A), |
| VW_VALID_FIELD(VW_IDX_41_SLP_A)), |
| /* index 42h: controller to peripheral. */ |
| VW_CHAN(VW_SLP_LAN, ESPI_SYSTEM_EVENT_VW_IDX_42, |
| VW_LEVEL_FIELD(VW_IDX_42_SLP_LAN), |
| VW_VALID_FIELD(VW_IDX_42_SLP_LAN)), |
| VW_CHAN(VW_SLP_WLAN, ESPI_SYSTEM_EVENT_VW_IDX_42, |
| VW_LEVEL_FIELD(VW_IDX_42_SLP_WLAN), |
| VW_VALID_FIELD(VW_IDX_42_SLP_WLAN)), |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(vw_channel_list) == VW_SIGNAL_COUNT); |
| |
| /* Get vw index & value information by signal */ |
| static int espi_vw_get_signal_index(enum espi_vw_signal event) |
| { |
| uint32_t i = event - VW_SIGNAL_START; |
| |
| return (i < ARRAY_SIZE(vw_channel_list)) ? i : -1; |
| } |
| |
| /** |
| * Set eSPI Virtual-Wire signal to Host |
| * |
| * @param signal vw signal needs to set |
| * @param level level of vw signal |
| * @return EC_SUCCESS, or non-zero if error. |
| */ |
| int espi_vw_set_wire(enum espi_vw_signal signal, uint8_t level) |
| { |
| /* Get index of vw signal list by signale name */ |
| int i = espi_vw_get_signal_index(signal); |
| |
| if (i < 0) |
| return EC_ERROR_PARAM1; |
| |
| /* critical section with interrupts off */ |
| interrupt_disable(); |
| if (level) |
| IT83XX_ESPI_VWIDX(vw_channel_list[i].index) |= |
| vw_channel_list[i].level_mask; |
| else |
| IT83XX_ESPI_VWIDX(vw_channel_list[i].index) &= |
| ~vw_channel_list[i].level_mask; |
| /* restore interrupts */ |
| interrupt_enable(); |
| |
| return EC_SUCCESS; |
| } |
| |
| /** |
| * Get eSPI Virtual-Wire signal from host |
| * |
| * @param signal vw signal needs to get |
| * @return 1: set by host, otherwise: no signal |
| */ |
| int espi_vw_get_wire(enum espi_vw_signal signal) |
| { |
| /* Get index of vw signal list by signale name */ |
| int i = espi_vw_get_signal_index(signal); |
| |
| if (i < 0) |
| return 0; |
| |
| /* Not valid */ |
| if (!(IT83XX_ESPI_VWIDX(vw_channel_list[i].index) & |
| vw_channel_list[i].valid_mask)) |
| return 0; |
| |
| return !!(IT83XX_ESPI_VWIDX(vw_channel_list[i].index) & |
| vw_channel_list[i].level_mask); |
| } |
| |
| /** |
| * Enable VW interrupt of power sequence signal |
| * |
| * @param signal vw signal needs to enable interrupt |
| * @return EC_SUCCESS, or non-zero if error. |
| */ |
| int espi_vw_enable_wire_int(enum espi_vw_signal signal) |
| { |
| /* |
| * Common code calls this function to enable VW interrupt of power |
| * sequence signal. |
| * IT83xx only use a bit (bit7@IT83XX_ESPI_VWCTRL0) to enable VW |
| * interrupt. |
| * VW interrupt will be triggerd with any updated VW index flag |
| * if this control bit is set. |
| * So we will always return success here. |
| */ |
| return EC_SUCCESS; |
| } |
| |
| /** |
| * Disable VW interrupt of power sequence signal |
| * |
| * @param signal vw signal needs to disable interrupt |
| * @return EC_SUCCESS, or non-zero if error. |
| */ |
| int espi_vw_disable_wire_int(enum espi_vw_signal signal) |
| { |
| /* |
| * We can't disable VW interrupt of power sequence signal |
| * individually. |
| */ |
| return EC_ERROR_UNIMPLEMENTED; |
| } |
| |
| /* Configure virtual wire outputs */ |
| static void espi_configure_vw(const struct vw_channel_t *settings, |
| size_t entries) |
| { |
| size_t i; |
| |
| for (i = 0; i < entries; i++) |
| IT83XX_ESPI_VWIDX(settings[i].index) |= |
| (settings[i].level_mask | settings[i].valid_mask); |
| } |
| |
| static void espi_vw_host_startup(void) |
| { |
| espi_configure_vw(vw_host_startup_setting, |
| ARRAY_SIZE(vw_host_startup_setting)); |
| } |
| |
| static void espi_vw_no_isr(uint8_t flag_changed, uint8_t vw_evt) |
| { |
| CPRINTS("espi VW interrupt event is ignored! (bit%d at VWCTRL1)", |
| vw_evt); |
| } |
| |
| #ifndef CONFIG_CHIPSET_GEMINILAKE |
| static void espi_vw_idx41_isr(uint8_t flag_changed, uint8_t vw_evt) |
| { |
| if (flag_changed & VW_LEVEL_FIELD(VW_IDX_41_SUS_WARN)) |
| espi_vw_set_wire(VW_SUS_ACK, espi_vw_get_wire(VW_SUS_WARN_L)); |
| } |
| #endif |
| |
| static void espi_vw_idx7_isr(uint8_t flag_changed, uint8_t vw_evt) |
| { |
| if (flag_changed & VW_LEVEL_FIELD(VW_IDX_7_HOST_RST_WARN)) |
| espi_vw_set_wire(VW_HOST_RST_ACK, |
| espi_vw_get_wire(VW_HOST_RST_WARN)); |
| } |
| |
| #ifdef CONFIG_CHIPSET_RESET_HOOK |
| static void espi_chipset_reset(void) |
| { |
| hook_notify(HOOK_CHIPSET_RESET); |
| update_ap_boot_time(ESPIRST); |
| } |
| DECLARE_DEFERRED(espi_chipset_reset); |
| #endif |
| |
| static void espi_vw_idx3_isr(uint8_t flag_changed, uint8_t vw_evt) |
| { |
| if (flag_changed & VW_LEVEL_FIELD(VW_IDX_3_PLTRST)) { |
| int pltrst = espi_vw_get_wire(VW_PLTRST_L); |
| |
| if (pltrst) { |
| espi_vw_host_startup(); |
| update_ap_boot_time(PLTRST_HIGH); |
| } else { |
| #ifdef CONFIG_CHIPSET_RESET_HOOK |
| hook_call_deferred(&espi_chipset_reset_data, MSEC); |
| #endif |
| /* Store port 80 reset event */ |
| port_80_write(PORT_80_EVENT_RESET); |
| update_ap_boot_time(PLTRST_LOW); |
| } |
| |
| CPRINTS("VW PLTRST_L %sasserted", pltrst ? "de" : ""); |
| } |
| |
| if (flag_changed & VW_LEVEL_FIELD(VW_IDX_3_OOB_RST_WARN)) |
| espi_vw_set_wire(VW_OOB_RST_ACK, |
| espi_vw_get_wire(VW_OOB_RST_WARN)); |
| } |
| |
| static void espi_vw_idx2_isr(uint8_t flag_changed, uint8_t vw_evt) |
| { |
| if (flag_changed & VW_LEVEL_FIELD(VW_IDX_2_SLP_S3)) |
| power_signal_interrupt(VW_SLP_S3_L); |
| if (flag_changed & VW_LEVEL_FIELD(VW_IDX_2_SLP_S4)) |
| power_signal_interrupt(VW_SLP_S4_L); |
| if (flag_changed & VW_LEVEL_FIELD(VW_IDX_2_SLP_S5)) |
| power_signal_interrupt(VW_SLP_S5_L); |
| } |
| |
| struct vw_interrupt_t { |
| void (*vw_isr)(uint8_t flag_changed, uint8_t vw_evt); |
| uint8_t vw_index; |
| }; |
| |
| /* |
| * The ISR of espi VW interrupt in array needs to match bit order in |
| * IT83XX_ESPI_VWCTRL1 register. |
| */ |
| #ifdef CONFIG_CHIPSET_GEMINILAKE |
| static const struct vw_interrupt_t vw_isr_list[] = { |
| [0] = { espi_vw_idx2_isr, ESPI_SYSTEM_EVENT_VW_IDX_2 }, |
| [1] = { espi_vw_idx3_isr, ESPI_SYSTEM_EVENT_VW_IDX_3 }, |
| [2] = { espi_vw_idx7_isr, ESPI_SYSTEM_EVENT_VW_IDX_7 }, |
| [3] = { espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_41 }, |
| [4] = { espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_42 }, |
| [5] = { espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_43 }, |
| [6] = { espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_44 }, |
| [7] = { espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_47 }, |
| }; |
| #else |
| static const struct vw_interrupt_t vw_isr_list[] = { |
| [0] = { espi_vw_idx2_isr, ESPI_SYSTEM_EVENT_VW_IDX_2 }, |
| [1] = { espi_vw_idx3_isr, ESPI_SYSTEM_EVENT_VW_IDX_3 }, |
| [2] = { espi_vw_idx7_isr, ESPI_SYSTEM_EVENT_VW_IDX_7 }, |
| [3] = { espi_vw_idx41_isr, ESPI_SYSTEM_EVENT_VW_IDX_41 }, |
| [4] = { espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_42 }, |
| [5] = { espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_43 }, |
| [6] = { espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_44 }, |
| [7] = { espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_47 }, |
| }; |
| #endif |
| |
| /* |
| * This is used to record the previous VW valid / level field state to discover |
| * changes. Then do following sequence only when state is changed. |
| */ |
| static uint8_t vw_index_flag[ARRAY_SIZE(vw_isr_list)]; |
| |
| void espi_vw_interrupt(void) |
| { |
| int i; |
| uint8_t vwidx_updated = IT83XX_ESPI_VWCTRL1; |
| |
| #ifdef IT83XX_ESPI_VWCTRL1_WRITE_FF_CLEAR |
| /* For IT8320BX, we have to write 0xff to clear pending bit.*/ |
| IT83XX_ESPI_VWCTRL1 = 0xff; |
| #else |
| /* write-1 to clear */ |
| IT83XX_ESPI_VWCTRL1 = vwidx_updated; |
| #endif |
| task_clear_pending_irq(IT83XX_IRQ_ESPI_VW); |
| |
| for (i = 0; i < ARRAY_SIZE(vw_isr_list); i++) { |
| if (vwidx_updated & BIT(i)) { |
| uint8_t idx_flag; |
| |
| idx_flag = IT83XX_ESPI_VWIDX(vw_isr_list[i].vw_index); |
| vw_isr_list[i].vw_isr(vw_index_flag[i] ^ idx_flag, i); |
| vw_index_flag[i] = idx_flag; |
| } |
| } |
| } |
| |
| static void espi_reset_vw_index_flags(void) |
| { |
| int i; |
| |
| /* reset vw_index_flag */ |
| for (i = 0; i < ARRAY_SIZE(vw_isr_list); i++) |
| vw_index_flag[i] = IT83XX_ESPI_VWIDX(vw_isr_list[i].vw_index); |
| } |
| |
| #ifdef IT83XX_ESPI_RESET_MODULE_BY_FW |
| void __ram_code espi_fw_reset_module(void) |
| { |
| /* |
| * (b/111480168): Force a reset of logic VCC domain in EC. This will |
| * reset both LPC and eSPI blocks. The IT8320DX spec describes the |
| * purpose of these bits as deciding whether VCC power status is used as |
| * an internal "power good" signal. However, toggling this field while |
| * VCC is applied results in resettig VCC domain logic in EC. This code |
| * must reside in SRAM to prevent DMA address corruption. |
| * |
| * bit[7-6]: |
| * 00b: The VCC power status is treated as power-off. |
| * 01b: The VCC power status is treated as power-on. |
| */ |
| IT83XX_GCTRL_RSTS = (IT83XX_GCTRL_RSTS & ~0xc0); |
| IT83XX_GCTRL_RSTS = (IT83XX_GCTRL_RSTS & ~0xc0) | BIT(6); |
| } |
| #endif |
| |
| void espi_reset_pin_asserted_interrupt(enum gpio_signal signal) |
| { |
| #ifdef IT83XX_ESPI_RESET_MODULE_BY_FW |
| espi_fw_reset_module(); |
| /* |
| * bit[7], enable P80L function. |
| * bit[6], accept port 80h cycle. |
| * bit[1-0], 10b: I2EC is read-only. |
| */ |
| IT83XX_GCTRL_SPCTRL1 |= 0xC2; |
| #endif |
| /* reset vw_index_flag when espi_reset# asserted. */ |
| espi_reset_vw_index_flags(); |
| } |
| |
| static int espi_get_reset_enable_config(void) |
| { |
| uint8_t config; |
| const struct gpio_info *espi_rst = gpio_list + GPIO_ESPI_RESET_L; |
| |
| /* |
| * Determine if eSPI HW reset is connected to eiter B7 or D2. |
| * bit[2-1]: |
| * 00b: reserved. |
| * 01b: espi_reset# is enabled on GPB7. |
| * 10b: espi_reset# is enabled on GPD2. |
| * 11b: reset is disabled. |
| */ |
| if (espi_rst->port == GPIO_D && espi_rst->mask == BIT(2)) { |
| config = IT83XX_GPIO_GCR_LPC_RST_D2; |
| } else if (espi_rst->port == GPIO_B && espi_rst->mask == BIT(7)) { |
| config = IT83XX_GPIO_GCR_LPC_RST_B7; |
| } else { |
| config = IT83XX_GPIO_GCR_LPC_RST_DISABLE; |
| CPRINTS("EC's espi_reset pin is not enabled correctly"); |
| } |
| |
| return config; |
| } |
| |
| static void espi_enable_reset(void) |
| { |
| int config = espi_get_reset_enable_config(); |
| |
| #ifdef IT83XX_ESPI_RESET_MODULE_BY_FW |
| /* |
| * Need to overwrite the config to ensure that eSPI HW reset is |
| * disabled. The reset function is instead handled by FW in the |
| * interrupt handler. |
| */ |
| config = IT83XX_GPIO_GCR_LPC_RST_DISABLE; |
| CPRINTS("EC's espi_reset pin hw auto reset is disabled"); |
| |
| #endif |
| IT83XX_GPIO_GCR = (IT83XX_GPIO_GCR & ~0x6) | |
| (config << IT83XX_GPIO_GCR_LPC_RST_POS); |
| |
| /* enable interrupt of EC's espi_reset pin */ |
| gpio_clear_pending_interrupt(GPIO_ESPI_RESET_L); |
| gpio_enable_interrupt(GPIO_ESPI_RESET_L); |
| } |
| |
| /* Interrupt event of controller enables the VW channel. */ |
| static void espi_vw_en_asserted(uint8_t evt) |
| { |
| /* |
| * Configure peripheral to controller virtual wire outputs after |
| * receiving the event of controller enables the VW channel. |
| */ |
| espi_configure_vw(en_vw_setting, ARRAY_SIZE(en_vw_setting)); |
| } |
| |
| /* Interrupt event of controller enables the OOB channel. */ |
| static void espi_oob_en_asserted(uint8_t evt) |
| { |
| /* |
| * Configure peripheral to controller virtual wire outputs after |
| * receiving the event of controller enables the OOB channel. |
| */ |
| espi_configure_vw(en_oob_setting, ARRAY_SIZE(en_oob_setting)); |
| } |
| |
| /* Interrupt event of controller enables the flash channel. */ |
| static void espi_flash_en_asserted(uint8_t evt) |
| { |
| /* |
| * Configure peripheral to controller virtual wire outputs after |
| * receiving the event of controller enables the flash channel. |
| */ |
| espi_configure_vw(en_flash_setting, ARRAY_SIZE(en_flash_setting)); |
| } |
| |
| static void espi_no_isr(uint8_t evt) |
| { |
| CPRINTS("espi interrupt event is ignored! (bit%d at ESGCTRL0)", evt); |
| } |
| |
| /* |
| * The ISR of espi interrupt event in array need to be matched bit order in |
| * IT83XX_ESPI_ESGCTRL0 register. |
| */ |
| static void (*espi_isr[])(uint8_t evt) = { |
| [0] = espi_no_isr, [1] = espi_vw_en_asserted, |
| [2] = espi_oob_en_asserted, [3] = espi_flash_en_asserted, |
| [4] = espi_no_isr, [5] = espi_no_isr, |
| [6] = espi_no_isr, [7] = espi_no_isr, |
| }; |
| |
| void espi_interrupt(void) |
| { |
| int i; |
| /* get espi interrupt events */ |
| uint8_t espi_event = IT83XX_ESPI_ESGCTRL0; |
| |
| /* write-1 to clear */ |
| IT83XX_ESPI_ESGCTRL0 = espi_event; |
| /* process espi interrupt events */ |
| for (i = 0; i < ARRAY_SIZE(espi_isr); i++) { |
| if (espi_event & BIT(i)) |
| espi_isr[i](i); |
| } |
| /* |
| * bit7: the peripheral has received a peripheral posted/completion. |
| * This bit indicates the peripheral has received a packet from eSPI |
| * peripheral channel. We can check cycle type (bit[3-0] at ESPCTRL0) |
| * and make corresponding modification if needed. |
| */ |
| if (IT83XX_ESPI_ESPCTRL0 & ESPI_INTERRUPT_EVENT_PUT_PC) { |
| /* write-1-clear to release PC_FREE */ |
| IT83XX_ESPI_ESPCTRL0 = ESPI_INTERRUPT_EVENT_PUT_PC; |
| CPRINTS("A packet from peripheral channel is ignored!"); |
| } |
| |
| task_clear_pending_irq(IT83XX_IRQ_ESPI); |
| } |
| |
| #ifdef IT83XX_ESPI_INHIBIT_CS_BY_PAD_DISABLED |
| /* Enable/Disable eSPI pad */ |
| void espi_enable_pad(int enable) |
| { |
| if (enable) |
| /* Enable eSPI pad. */ |
| IT83XX_ESPI_ESGCTRL2 &= ~BIT(6); |
| else |
| /* Disable eSPI pad. */ |
| IT83XX_ESPI_ESGCTRL2 |= BIT(6); |
| } |
| #endif |
| |
| void espi_init(void) |
| { |
| /* |
| * bit[2-0], the maximum frequency of operation supported by peripheral: |
| * 000b: 20MHz |
| * 001b: 25MHz |
| * 010b: 33MHz |
| * 011b: 50MHz |
| * 100b: 66MHz |
| */ |
| #ifdef IT83XX_ESPI_PERIPHERAL_MAX_FREQ_CONFIGURABLE |
| IT83XX_ESPI_GCAC1 = (IT83XX_ESPI_GCAC1 & ~0x7) | BIT(2); |
| #endif |
| /* reset vw_index_flag at initialization */ |
| espi_reset_vw_index_flags(); |
| |
| /* |
| * bit[3]: The reset source of PNPCFG is RSTPNP bit in RSTCH |
| * register and WRST#. |
| */ |
| IT83XX_GCTRL_RSTS &= ~BIT(3); |
| task_clear_pending_irq(IT83XX_IRQ_ESPI_VW); |
| /* bit7: VW interrupt enable */ |
| IT83XX_ESPI_VWCTRL0 |= BIT(7); |
| task_enable_irq(IT83XX_IRQ_ESPI_VW); |
| |
| /* bit7: eSPI interrupt enable */ |
| IT83XX_ESPI_ESGCTRL1 |= BIT(7); |
| /* bit4: eSPI to WUC enable */ |
| IT83XX_ESPI_ESGCTRL2 |= BIT(4); |
| task_enable_irq(IT83XX_IRQ_ESPI); |
| |
| /* enable interrupt and reset from eSPI_reset# */ |
| espi_enable_reset(); |
| } |