diff options
Diffstat (limited to 'chip/it83xx/espi.c')
-rw-r--r-- | chip/it83xx/espi.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/chip/it83xx/espi.c b/chip/it83xx/espi.c new file mode 100644 index 0000000000..9d105d4af8 --- /dev/null +++ b/chip/it83xx/espi.c @@ -0,0 +1,387 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * 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 "hooks.h" +#include "port80.h" +#include "power.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "uart.h" +#include "util.h" + +#define CHIP_ESPI_VW_INTERRUPT_NUM 8 + +/* 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 at initialization */ +static const struct vw_channel_t vw_init_setting[] = { + {ESPI_SYSTEM_EVENT_VW_IDX_4, + VW_LEVEL_FIELD(0), + VW_VALID_FIELD(VW_IDX_4_OOB_RST_ACK)}, + {ESPI_SYSTEM_EVENT_VW_IDX_5, + VW_LEVEL_FIELD(VW_IDX_5_BTLD_STATUS_DONE), + VW_VALID_FIELD(VW_IDX_5_BTLD_STATUS_DONE)}, + {ESPI_SYSTEM_EVENT_VW_IDX_40, + VW_LEVEL_FIELD(0), + VW_VALID_FIELD(VW_IDX_40_SUS_ACK)}, +}; + +/* 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_BASE)] = {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: master to slave. */ + 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: master to slave. */ + 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: slave to master. */ + 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: slave to master. */ + 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_SLAVE_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: slave to master. */ + 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: master to slave. */ + 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: slave to master. */ + 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: master to slave. */ + 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: master to slave. */ + 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_BASE_END - VW_SIGNAL_BASE)); + +/* 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_BASE; + + 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; +} + +static void espi_vw_host_startup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vw_host_startup_setting); i++) + IT83XX_ESPI_VWIDX(vw_host_startup_setting[i].index) = + (vw_host_startup_setting[i].level_mask | + vw_host_startup_setting[i].valid_mask); +} + +static void espi_vw_no_isr(uint8_t flag_changed) +{ +} + +static void espi_vw_idx41_isr(uint8_t flag_changed) +{ + 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)); +} + +static void espi_vw_idx7_isr(uint8_t flag_changed) +{ + 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)); +} + +static void espi_vw_idx3_isr(uint8_t flag_changed) +{ + 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(); + else + /* Store port 80 reset event */ + port_80_write(PORT_80_EVENT_RESET); + + CPRINTS("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) +{ + 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_index; +}; + +static const struct vw_interrupt_t vw_isr_list[CHIP_ESPI_VW_INTERRUPT_NUM] = { + {espi_vw_idx2_isr, ESPI_SYSTEM_EVENT_VW_IDX_2}, + {espi_vw_idx3_isr, ESPI_SYSTEM_EVENT_VW_IDX_3}, + {espi_vw_idx7_isr, ESPI_SYSTEM_EVENT_VW_IDX_7}, + {espi_vw_idx41_isr, ESPI_SYSTEM_EVENT_VW_IDX_41}, + {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_42}, + {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_43}, + {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_44}, + {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_47}, +}; + +/* + * 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[CHIP_ESPI_VW_INTERRUPT_NUM]; + +void espi_vw_interrupt(void) +{ + int i; + uint8_t vwidx_updated = IT83XX_ESPI_VWCTRL1; + + /* + * TODO(b:68918637): write-1 clear bug. + * for now, we have to write 0xff to clear pending bit. + */ +#if 0 + IT83XX_ESPI_VWCTRL1 = vwidx_updated; +#else + IT83XX_ESPI_VWCTRL1 = 0xff; +#endif + task_clear_pending_irq(IT83XX_IRQ_ESPI_VW); + + for (i = 0; i < CHIP_ESPI_VW_INTERRUPT_NUM; i++) { + if (vwidx_updated & (1 << 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); + vw_index_flag[i] = idx_flag; + } + } +} + +void espi_interrupt(void) +{ +} + +void espi_init(void) +{ + int i; + + /* TODO: PLL change won't success if eSPI chip select is low. */ +#if (PLL_CLOCK != 48000000) +#error "Not support PLL change if eSPI module is enabled. " +#endif + + for (i = 0; i < ARRAY_SIZE(vw_init_setting); i++) + IT83XX_ESPI_VWIDX(vw_init_setting[i].index) = + (vw_init_setting[i].level_mask | + vw_init_setting[i].valid_mask); + + for (i = 0; i < CHIP_ESPI_VW_INTERRUPT_NUM; i++) + vw_index_flag[i] = IT83XX_ESPI_VWIDX(vw_isr_list[i].vw_index); + + task_clear_pending_irq(IT83XX_IRQ_ESPI_VW); + /* bit7: VW interrupt enable */ + IT83XX_ESPI_VWCTRL0 |= (1 << 7); + task_enable_irq(IT83XX_IRQ_ESPI_VW); +} |