| /* 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 "common.h" |
| #include "acpi.h" |
| #include "console.h" |
| #include "gpio.h" |
| #include "hooks.h" |
| #include "host_command.h" |
| #include "keyboard_protocol.h" |
| #include "port80.h" |
| #include "util.h" |
| #include "chipset.h" |
| |
| #include "registers.h" |
| #include "espi.h" |
| #include "lpc.h" |
| #include "lpc_chip.h" |
| #include "system.h" |
| #include "task.h" |
| #include "console.h" |
| #include "uart.h" |
| #include "util.h" |
| #include "power.h" |
| #include "timer.h" |
| #include "tfdp_chip.h" |
| |
| /* Console output macros */ |
| #ifdef CONFIG_MCHP_ESPI_DEBUG |
| #ifdef CONFIG_MCHP_TFDP |
| #define CPUTS(...) |
| #define CPRINTS(...) |
| #else |
| #define CPUTS(outstr) cputs(CC_LPC, outstr) |
| #define CPRINTS(format, args...) cprints(CC_LPC, format, ## args) |
| #endif |
| #else |
| #define CPUTS(...) |
| #define CPRINTS(...) |
| #endif |
| |
| /* |
| * eSPI slave to master virtual wire pulse timeout. |
| */ |
| #define ESPI_S2M_VW_PULSE_LOOP_CNT 50 |
| #define ESPI_S2M_VW_PULSE_LOOP_DLY_US 10 |
| |
| /* |
| * eSPI master enable virtual wire channel timeout. |
| */ |
| #define ESPI_CHAN_READY_TIMEOUT_US (100 * MSEC) |
| #define ESPI_CHAN_READY_POLL_INTERVAL_US 100 |
| |
| static uint32_t espi_channels_ready; |
| |
| /* |
| * eSPI Virtual Wire reset values |
| * VWire name used by chip independent code. |
| * Host eSPI Master VWire index containing signal |
| * Reset value of VWire. Note, each Host VWire index may |
| * have a different reset source: |
| * EC Power-on/chip reset |
| * ESPI_RESET# assertion by Host eSPI master |
| * eSPI Platform Reset assertion by Host eSPI master |
| * MEC1701H allows eSPI Platform reset to |
| * be a VWire or side band signal. |
| * |
| * NOTE MEC1701H Boot-ROM will restore VWires ... from |
| * VBAT power register MCHP_VBAT_VWIRE_BACKUP. |
| * bits[3:0] = Master-to-Slave Index 02h SRC3:SRC0 values |
| * MSVW00 register |
| * SRC0 = SLP_S3# |
| * SRC1 = SLP_S4# |
| * SRC2 = SLP_S5# |
| * SRC3 = reserved |
| * bits[7:4] = Master-to-Slave Index 42h SRC3:SRC0 values |
| * MSVW04 register |
| * SRC0 = SLP_LAN# |
| * SRC1 = SLP_WLAN# |
| * SRC2 = reserved |
| * SRC3 = reserved |
| * |
| */ |
| struct vw_info_t { |
| uint16_t name; /* signal name */ |
| uint8_t host_idx; /* Host VWire index of signal */ |
| uint8_t reset_val; /* reset value of VWire */ |
| uint8_t flags; /* b[0]=0(MSVW), =1(SMVW) */ |
| uint8_t reg_idx; /* MSVW or SMVW index */ |
| uint8_t src_num; /* SRC number */ |
| uint8_t rsvd; |
| }; |
| |
| |
| /* VW signals used in eSPI */ |
| /* |
| * MEC1701H VWire mapping based on eSPI Spec 1.0, |
| * eSPI Compatibility spec 0.96, |
| * MCHP HW defaults and ec/include/espi.h |
| * |
| * MSVW00 index=02h PORValue=00000000_04040404_00000102 reset=RESET_SYS |
| * SRC0 = VW_SLP_S3_L, IntrDis |
| * SRC1 = VW_SLP_S4_L, IntrDis |
| * SRC2 = VW_SLP_S5_L, IntrDis |
| * SRC3 = reserved, IntrDis |
| * MSVW01 index=03h PORValue=00000000_04040404_00000003 reset=RESET_ESPI |
| * SRC0 = VW_SUS_STAT_L, IntrDis |
| * SRC1 = VW_PLTRST_L, IntrDis |
| * SRC2 = VW_OOB_RST_WARN, IntrDis |
| * SRC3 = reserved, IntrDis |
| * MSVW02 index=07h PORValue=00000000_04040404_00000307 reset=PLTRST |
| * SRC0 = VW_HOST_RST_WARN |
| * SRC1 = 0 reserved |
| * SRC2 = 0 reserved |
| * SRC3 = 0 reserved |
| * MSVW03 index=41h PORValue=00000000_04040404_00000041 reset=RESET_ESPI |
| * SRC0 = VW_SUS_WARN_L, IntrDis |
| * SRC1 = VW_SUS_PWRDN_ACK_L, IntrDis |
| * SRC2 = 0 reserved, IntrDis |
| * SRC3 = VW_SLP_A_L, IntrDis |
| * MSVW04 index=42h PORValue=00000000_04040404_00000141 reset=RESET_SYS |
| * SRC0 = VW_SLP_LAN, IntrDis |
| * SRC1 = VW_SLP_WLAN, IntrDis |
| * SRC2 = reserved, IntrDis |
| * SRC3 = reserved, IntrDis |
| * |
| * SMVW00 index=04h PORValue=01010000_0000C004 STOM=1100 reset=RESET_ESPI |
| * SRC0 = VW_OOB_RST_ACK |
| * SRC1 = 0 reserved |
| * SRC2 = VW_WAKE_L |
| * SRC3 = VW_PME_L |
| * SMVW01 index=05h PORValue=00000000_00000005 STOM=0000 reset=RESET_ESPI |
| * SRC0 = SLAVE_BOOT_LOAD_DONE !!! NOTE: Google combines SRC0 & SRC3 |
| * SRC1 = VW_ERROR_FATAL |
| * SRC2 = VW_ERROR_NON_FATAL |
| * SRC3 = SLAVE_BOOT_LOAD_STATUS !!! into VW_SLAVE_BTLD_STATUS_DONE |
| * SMVW02 index=06h PORValue=00010101_00007306 STOM=0111 reset=PLTRST |
| * SRC0 = VW_SCI_L |
| * SRC1 = VW_SMI_L |
| * SRC2 = VW_RCIN_L |
| * SRC3 = VW_HOST_RST_ACK |
| * SMVW03 index=40h PORValue=00000000_00000040 STOM=0000 reset=RESET_ESPI |
| * SRC0 = assign VW_SUS_ACK |
| * SRC1 = 0 |
| * SRC2 = 0 |
| * SRC3 = 0 |
| * |
| * table of vwire structures |
| * MSVW00 at 0x400F9C00 offset = 0x000 |
| * MSVW01 at 0x400F9C0C offset = 0x00C |
| * |
| * SMVW00 at 0x400F9E00 offset = 0x200 |
| * SMVW01 at 0x400F9E08 offset = 0x208 |
| * |
| */ |
| |
| /* |
| * Virtual Wire table |
| * Each entry contains: |
| * Signal name from include/espi.h |
| * Host chipset VWire index number |
| * Reset value of VWire |
| * flags where bit[0]==0 Wire is Master-to-Slave or 1 Slave-to-Master |
| * MEC1701 register index into MSVW or SMVW register banks |
| * MEC1701 source number in MSVW or SMVW bank |
| * Reserved |
| * Pointer to name string for debug |
| */ |
| static const struct vw_info_t vw_info_tbl[] = { |
| /* name host reset reg SRC |
| * index value flags index num rsvd |
| */ |
| /* MSVW00 Host index 02h (In) */ |
| {VW_SLP_S3_L, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}, |
| {VW_SLP_S4_L, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00}, |
| {VW_SLP_S5_L, 0x02, 0x00, 0x10, 0x00, 0x02, 0x00}, |
| /* MSVW01 Host index 03h (In) */ |
| {VW_SUS_STAT_L, 0x03, 0x00, 0x10, 0x01, 0x00, 0x00}, |
| {VW_PLTRST_L, 0x03, 0x00, 0x10, 0x01, 0x01, 0x00}, |
| {VW_OOB_RST_WARN, 0x03, 0x00, 0x10, 0x01, 0x02, 0x00}, |
| /* SMVW00 Host Index 04h (Out) */ |
| {VW_OOB_RST_ACK, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00}, |
| {VW_WAKE_L, 0x04, 0x01, 0x01, 0x00, 0x02, 0x00}, |
| {VW_PME_L, 0x04, 0x01, 0x01, 0x00, 0x03, 0x00}, |
| /* SMVW01 Host index 05h (Out) */ |
| {VW_ERROR_FATAL, 0x05, 0x00, 0x01, 0x01, 0x01, 0x00}, |
| {VW_ERROR_NON_FATAL, 0x05, 0x00, 0x01, 0x01, 0x02, 0x00}, |
| {VW_SLAVE_BTLD_STATUS_DONE, 0x05, 0x00, 0x01, 0x01, 0x30, 0x00}, |
| /* SMVW02 Host index 06h (Out) */ |
| {VW_SCI_L, 0x06, 0x01, 0x01, 0x02, 0x00, 0x00}, |
| {VW_SMI_L, 0x06, 0x01, 0x01, 0x02, 0x01, 0x00}, |
| {VW_RCIN_L, 0x06, 0x01, 0x01, 0x02, 0x02, 0x00}, |
| {VW_HOST_RST_ACK, 0x06, 0x00, 0x01, 0x02, 0x03, 0x00}, |
| /* MSVW02 Host index 07h (In) */ |
| {VW_HOST_RST_WARN, 0x07, 0x00, 0x10, 0x02, 0x00, 0x00}, |
| /* SMVW03 Host Index 40h (Out) */ |
| {VW_SUS_ACK, 0x40, 0x00, 0x01, 0x03, 0x00, 0x00}, |
| /* MSVW03 Host Index 41h (In) */ |
| {VW_SUS_WARN_L, 0x41, 0x00, 0x10, 0x03, 0x00, 0x00}, |
| {VW_SUS_PWRDN_ACK_L, 0x41, 0x00, 0x10, 0x03, 0x01, 0x00}, |
| {VW_SLP_A_L, 0x41, 0x00, 0x10, 0x03, 0x03, 0x00}, |
| /* MSVW04 Host index 42h (In) */ |
| {VW_SLP_LAN, 0x42, 0x00, 0x10, 0x04, 0x00, 0x00}, |
| {VW_SLP_WLAN, 0x42, 0x00, 0x10, 0x04, 0x01, 0x00} |
| }; |
| BUILD_ASSERT(ARRAY_SIZE(vw_info_tbl) == VW_SIGNAL_COUNT); |
| |
| |
| /************************************************************************/ |
| /* eSPI internal utilities */ |
| |
| static int espi_vw_get_signal_index(enum espi_vw_signal event) |
| { |
| int i; |
| |
| /* Search table by signal name */ |
| for (i = 0; i < ARRAY_SIZE(vw_info_tbl); i++) { |
| if (vw_info_tbl[i].name == event) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| |
| /* |
| * Initialize eSPI hardware upon ESPI_RESET# de-assertion |
| */ |
| #ifdef CONFIG_MCHP_ESPI_RESET_DEASSERT_INIT |
| static void espi_reset_deassert_init(void) |
| { |
| |
| } |
| #endif |
| |
| /* Call this on entry to deepest sleep state with EC turned off. |
| * May not be required in future host eSPI chipsets. |
| * |
| * Save Master-to-Slave VWire Index 02h & 42h before |
| * entering a deep sleep state where EC power is shut off. |
| * PCH requires we restore these VWires on wake. |
| * SLP_S3#, SLP_S4#, SLP_S5# in index 02h |
| * SLP_LAN#, SLP_WLAN# in index 42h |
| * Current VWire states are saved to a battery backed 8-bit |
| * register in MEC1701H. |
| * If a VBAT POR occurs the value of this register = 0 which |
| * is the default state of the above VWires on a hardware |
| * POR. |
| * VBAT byte bit definitions |
| * Host Index 02h -> MSVW00 |
| * Host Index 42h -> MSVW04 |
| * 0 Host Index 02h SRC0 |
| * 1 Host Index 02h SRC1 |
| * 2 Host Index 02h SRC2 |
| * 3 Host Index 02h SRC3 |
| * 4 Host Index 42h SRC0 |
| * 5 Host Index 42h SRC1 |
| * 6 Host Index 42h SRC2 |
| * 7 Host Index 42h SRC3 |
| */ |
| #ifdef CONFIG_MCHP_ESPI_VW_SAVE_ON_SLEEP |
| static void espi_vw_save(void) |
| { |
| uint32_t i, r; |
| uint8_t vb; |
| |
| vb = 0; |
| r = MCHP_ESPI_VW_M2S_SRC_ALL(MSVW_H42); |
| for (i = 0; i < 4; i++) { |
| if (r & (1ul << (i << 3))) |
| vb |= (1u << i); |
| } |
| |
| vb <<= 4; |
| r = MCHP_ESPI_VW_M2S_SRC_ALL(MSVW_H02); |
| for (i = 0; i < 4; i++) { |
| if (r & (1ul << (i << 3))) |
| vb |= (1u << i); |
| } |
| |
| r = MCHP_VBAT_RAM(MCHP_VBAT_VWIRE_BACKUP); |
| r = (r & 0xFFFFFF00) | vb; |
| MCHP_VBAT_RAM(MCHP_VBAT_VWIRE_BACKUP) = r; |
| } |
| |
| /* |
| * Update MEC1701H VBAT powered VWire backup values restored on |
| * MCHP chip reset. MCHP Boot-ROM loads these values into |
| * MSVW00 SRC[0:3](Index 02h) and MSVW04 SRC[0:3](Index 42h) |
| * on chip reset(POR, WDT reset, chip reset, wake from EC off). |
| * Always clear backup value after restore. |
| */ |
| static void espi_vw_restore(void) |
| { |
| uint32_t i, r; |
| uint8_t vb; |
| |
| #ifdef EVB_NO_ESPI_TEST_MODE |
| vb = 0xff; /* force SLP_Sx# signals to 1 */ |
| #else |
| vb = MCHP_VBAT_RAM(MCHP_VBAT_VWIRE_BACKUP) & 0xff; |
| #endif |
| r = 0; |
| for (i = 0; i < 4; i++) { |
| if (vb & (1u << i)) |
| r |= (1ul << (i << 3)); |
| } |
| MCHP_ESPI_VW_M2S_SRC_ALL(MSVW_H02) = r; |
| CPRINTS("eSPI restore MSVW00(Index 02h) = 0x%08x", r); |
| trace11(0, ESPI, 0, "eSPI restore MSVW00(Index 02h) = 0x%08x", r); |
| |
| vb >>= 4; |
| r = 0; |
| for (i = 0; i < 4; i++) { |
| if (vb & (1u << i)) |
| r |= (1ul << (i << 3)); |
| } |
| MCHP_ESPI_VW_M2S_SRC_ALL(MSVW_H42) = r; |
| CPRINTS("eSPI restore MSVW00(Index 42h) = 0x%08x", r); |
| trace11(0, ESPI, 0, "eSPI restore MSVW04(Index 42h) = 0x%08x", r); |
| |
| r = MCHP_VBAT_RAM(MCHP_VBAT_VWIRE_BACKUP); |
| MCHP_VBAT_RAM(MCHP_VBAT_VWIRE_BACKUP) = r & 0xFFFFFF00; |
| |
| } |
| #endif |
| |
| static uint8_t __attribute__((unused)) espi_msvw_srcs_get(uint8_t msvw_id) |
| { |
| uint8_t msvw; |
| |
| msvw = 0; |
| if (msvw_id < MSVW_MAX) { |
| uint32_t r = MCHP_ESPI_VW_M2S_SRC_ALL(msvw_id); |
| |
| msvw = (r & 0x01); |
| msvw |= ((r >> 7) & 0x02); |
| msvw |= ((r >> 14) & 0x04); |
| msvw |= ((r >> 21) & 0x08); |
| } |
| |
| return msvw; |
| } |
| |
| static void __attribute__((unused)) espi_msvw_srcs_set(uint8_t msvw_id, |
| uint8_t src_bitmap) |
| { |
| if (msvw_id < MSVW_MAX) { |
| uint32_t r = (src_bitmap & 0x08) << 21; |
| |
| r |= (src_bitmap & 0x04) << 14; |
| r |= (src_bitmap & 0x02) << 7; |
| r |= (src_bitmap & 0x01); |
| MCHP_ESPI_VW_M2S_SRC_ALL(msvw_id) = r; |
| } |
| } |
| |
| static uint8_t __attribute__((unused)) espi_smvw_srcs_get(uint8_t smvw_id) |
| { |
| uint8_t smvw; |
| |
| smvw = 0; |
| if (smvw_id < SMVW_MAX) { |
| uint32_t r = MCHP_ESPI_VW_S2M_SRC_ALL(smvw_id); |
| |
| smvw = (r & 0x01); |
| smvw |= ((r >> 7) & 0x02); |
| smvw |= ((r >> 14) & 0x04); |
| smvw |= ((r >> 21) & 0x08); |
| } |
| |
| return smvw; |
| } |
| |
| static void __attribute__((unused)) espi_smvw_srcs_set(uint8_t smvw_id, |
| uint8_t src_bitmap) |
| { |
| if (smvw_id < SMVW_MAX) { |
| uint32_t r = (src_bitmap & 0x08) << 21; |
| |
| r |= (src_bitmap & 0x04) << 14; |
| r |= (src_bitmap & 0x02) << 7; |
| r |= (src_bitmap & 0x01); |
| MCHP_ESPI_VW_S2M_SRC_ALL(smvw_id) = r; |
| } |
| } |
| |
| |
| /* |
| * Called before releasing RSMRST# |
| * ESPI_RESET# is asserted |
| * PLATFORM_RESET# is asserted |
| */ |
| static void espi_bar_pre_init(void) |
| { |
| /* Configuration IO BAR set to 0x2E/0x2F */ |
| MCHP_ESPI_IO_BAR_ADDR_LSB(MCHP_ESPI_IO_BAR_ID_CFG_PORT) = 0x2E; |
| MCHP_ESPI_IO_BAR_ADDR_MSB(MCHP_ESPI_IO_BAR_ID_CFG_PORT) = 0x00; |
| MCHP_ESPI_IO_BAR_VALID(MCHP_ESPI_IO_BAR_ID_CFG_PORT) = 1; |
| } |
| |
| /* |
| * Called before releasing RSMRST# |
| * ESPI_RESET# is asserted |
| * PLATFORM_RESET# is asserted |
| * Set all MSVW to either edge interrupt |
| * IRQ_SELECT fields are reset on RESET_SYS not ESPI_RESET or PLTRST |
| * |
| */ |
| static void espi_vw_pre_init(void) |
| { |
| uint32_t i; |
| |
| CPRINTS("eSPI VW Pre-Init"); |
| trace0(0, ESPI, 0, "eSPI VW Pre-Init"); |
| |
| #ifdef CONFIG_MCHP_ESPI_VW_SAVE_ON_SLEEP |
| espi_vw_restore(); |
| #endif |
| |
| /* disable all */ |
| for (i = 0; i < MSVW_MAX; i++) |
| MCHP_ESPI_VW_M2S_IRQSEL_ALL(i) = 0x0f0f0f0ful; |
| |
| /* clear spurious status */ |
| MCHP_INT_SOURCE(24) = 0xfffffffful; |
| MCHP_INT_SOURCE(25) = 0xfffffffful; |
| |
| MCHP_ESPI_VW_M2S_IRQSEL_ALL(MSVW_H02) = 0x040f0f0ful; |
| MCHP_ESPI_VW_M2S_IRQSEL_ALL(MSVW_H03) = 0x040f0f0ful; |
| MCHP_ESPI_VW_M2S_IRQSEL_ALL(MSVW_H07) = 0x0404040ful; |
| MCHP_ESPI_VW_M2S_IRQSEL_ALL(MSVW_H41) = 0x0f040f0ful; |
| MCHP_ESPI_VW_M2S_IRQSEL_ALL(MSVW_H42) = 0x04040f0ful; |
| MCHP_ESPI_VW_M2S_IRQSEL_ALL(MSVW_H47) = 0x0404040ful; |
| |
| MCHP_INT_ENABLE(24) = 0xfff3b177ul; |
| MCHP_INT_ENABLE(25) = 0x01ul; |
| |
| MCHP_INT_SOURCE(24) = 0xfffffffful; |
| MCHP_INT_SOURCE(25) = 0xfffffffful; |
| |
| MCHP_INT_BLK_EN = (1ul << 24) + (1ul << 25); |
| |
| task_enable_irq(MCHP_IRQ_GIRQ24); |
| task_enable_irq(MCHP_IRQ_GIRQ25); |
| |
| CPRINTS("eSPI VW Pre-Init Done"); |
| trace0(0, ESPI, 0, "eSPI VW Pre-Init Done"); |
| } |
| |
| |
| /* |
| * If VWire, Flash, and OOB channels have been enabled |
| * then set VWires SLAVE_BOOT_LOAD_STATUS = SLAVE_BOOT_LOAD_DONE = 1 |
| * SLAVE_BOOT_LOAD_STATUS = SRC3 of Slave-to-Master Index 05h |
| * SLAVE_BOOT_LOAD_DONE = SRC0 of Slave-to-Master Index 05h |
| * Note, if set individually then set status first then done. |
| * We set both simultaneously. ESPI_ALERT# will assert only if one |
| * or both bits change. |
| * SRC0 is bit[32] of SMVW01 |
| * SRC3 is bit[56] of SMVW01 |
| */ |
| static void espi_send_boot_load_done(void) |
| { |
| /* First set SLAVE_BOOT_LOAD_STATUS = 1 */ |
| MCHP_ESPI_VW_S2M_SRC3(SMVW_H05) = 1; |
| /* Next set SLAVE_BOOT_LOAD_DONE = 1 */ |
| MCHP_ESPI_VW_S2M_SRC0(SMVW_H05) = 1; |
| |
| CPRINTS("eSPI Send SLAVE_BOOT_LOAD_STATUS/DONE = 1"); |
| trace0(0, ESPI, 0, "VW SLAVE_BOOT_LOAD_STATUS/DONE = 1"); |
| } |
| |
| |
| /* |
| * Called when eSPI PLTRST# VWire de-asserts |
| * Re-initialize any hardware that was reset while PLTRST# was |
| * asserted. |
| * Logical Device BAR's, etc. |
| * Each BAR requires address, mask, and valid bit |
| * mask = bit map of address[7:0] to mask out |
| * 0 = no masking, match exact address |
| * 0x01 = mask bit[0], match two consecutive addresses |
| * 0xff = mask bits[7:0], match 256 consecutive bytes |
| * eSPI has two registers for each BAR |
| * Host visible register |
| * base address in bits[31:16] |
| * valid = bit[0] |
| * EC only register |
| * mask = bits[7:0] |
| * Logical device number = bits[13:8] |
| * Virtualized = bit[16] Not Implemented |
| */ |
| static void espi_host_init(void) |
| { |
| CPRINTS("eSPI - espi_host_init"); |
| trace0(0, ESPI, 0, "eSPI Host Init"); |
| |
| /* BAR's */ |
| |
| /* Configuration IO BAR set to 0x2E/0x2F */ |
| MCHP_ESPI_IO_BAR_CTL_MASK(MCHP_ESPI_IO_BAR_ID_CFG_PORT) = 0x01; |
| MCHP_ESPI_IO_BAR_ADDR_LSB(MCHP_ESPI_IO_BAR_ID_CFG_PORT) = 0x2E; |
| MCHP_ESPI_IO_BAR_ADDR_MSB(MCHP_ESPI_IO_BAR_ID_CFG_PORT) = 0x00; |
| MCHP_ESPI_IO_BAR_VALID(MCHP_ESPI_IO_BAR_ID_CFG_PORT) = 1; |
| |
| /* Set up ACPI0 for 0x62/0x66 */ |
| chip_acpi_ec_config(0, 0x62, 0x04); |
| |
| /* Set up ACPI1 for 0x200-0x203, 0x204-0x207 */ |
| chip_acpi_ec_config(1, 0x200, 0x07); |
| |
| /* Set up 8042 interface at 0x60/0x64 */ |
| chip_8042_config(0x60); |
| |
| /* EMI at 0x800 for accessing shared memory */ |
| chip_emi0_config(0x800); |
| |
| /* Setup Port80 Debug Hardware for I/O 80h */ |
| chip_port80_config(0x80); |
| |
| lpc_mem_mapped_init(); |
| |
| MCHP_ESPI_PC_STATUS = 0xfffffffful; |
| /* PC enable & Mastering enable changes */ |
| MCHP_ESPI_PC_IEN = (1ul << 25) + (1ul << 28); |
| |
| |
| /* Sufficiently initialized */ |
| lpc_set_init_done(1); |
| |
| /* last set eSPI Peripheral Channel Ready = 1 */ |
| /* Done in ISR for PC Channel */ |
| MCHP_ESPI_IO_PC_READY = 1; |
| |
| /* Update host events now that we can copy them to memmap */ |
| /* NOTE: This routine may pulse SCI# and/or SMI# |
| * For eSPI these are virtual wires. VWire channel should be |
| * enabled before PLTRST# is de-asserted so its safe BUT has |
| * PC Channel(I/O) Enable occurred? |
| */ |
| lpc_update_host_event_status(); |
| |
| CPRINTS("eSPI - espi_host_init Done"); |
| trace0(0, ESPI, 0, "eSPI Host Init Done"); |
| } |
| DECLARE_HOOK(HOOK_CHIPSET_STARTUP, espi_host_init, HOOK_PRIO_FIRST); |
| |
| |
| /* |
| * Called in response to VWire OOB_RST_WARN==1 from |
| * espi_vw_evt_oob_rst_warn. |
| * Host chipset eSPI documentation states eSPI slave should |
| * if necessary flush any OOB upstream (OOB TX) data before the slave |
| * sends OOB_RST_ACK=1 to the Host. |
| */ |
| static void espi_oob_flush(void) |
| { |
| } |
| |
| |
| /* |
| * Called in response to VWire HOST_RST_WARN==1 from |
| * espi_vw_evt_host_rst_warn. |
| * Host chipset eSPI documentation states assertion of HOST_RST_WARN |
| * can be used if necessary to flush any Peripheral Channel data |
| * before slave sends HOST_RST_ACK to Host. |
| */ |
| static void espi_pc_flush(void) |
| { |
| } |
| |
| /* The ISRs of VW signals which used for power sequences */ |
| void espi_vw_power_signal_interrupt(enum espi_vw_signal signal) |
| { |
| CPRINTS("eSPI power signal interrupt for VW %d", signal); |
| trace1(0, ESPI, 0, "eSPI pwr intr VW %d", (signal - VW_SIGNAL_START)); |
| power_signal_interrupt((enum gpio_signal) signal); |
| } |
| |
| /************************************************************************/ |
| /* IC specific low-level driver */ |
| |
| |
| /** |
| * 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) |
| { |
| int tidx; |
| uint8_t ridx, src_num; |
| |
| tidx = espi_vw_get_signal_index(signal); |
| |
| if (tidx < 0) |
| return EC_ERROR_PARAM1; |
| |
| if (0 == (vw_info_tbl[tidx].flags & (1u << 0))) |
| return EC_ERROR_PARAM1; /* signal is Master-to-Slave */ |
| |
| ridx = vw_info_tbl[tidx].reg_idx; |
| src_num = vw_info_tbl[tidx].src_num; |
| |
| if (level) |
| level = 1; |
| |
| if (signal == VW_SLAVE_BTLD_STATUS_DONE) { |
| /* SLAVE_BOOT_LOAD_STATUS */ |
| MCHP_ESPI_VW_S2M_SRC3(ridx) = level; |
| /* SLAVE_BOOT_LOAD_DONE after status */ |
| MCHP_ESPI_VW_S2M_SRC0(ridx) = level; |
| } else { |
| MCHP_ESPI_VW_S2M_SRC(ridx, src_num) = level; |
| } |
| |
| #ifdef CONFIG_MCHP_ESPI_DEBUG |
| CPRINTS("eSPI VW Set Wire %s = %d", |
| espi_vw_get_wire_name(signal), level); |
| trace2(0, ESPI, 0, "VW SetWire[%d] = %d", |
| ((uint32_t)signal - VW_SIGNAL_START), level); |
| #endif |
| |
| return EC_SUCCESS; |
| } |
| |
| /* |
| * Set Slave to Master virtual wire to level and wait for hardware |
| * to process virtual wire. |
| * If virtual wire written to same value then hardware change bit |
| * is 0 and routine returns success. |
| * If virtual wire written to different value then hardware change bit |
| * goes to 1 until bit is transmitted upstream to the master. This may |
| * happen quickly is bus is idle. Poll for hardware clearing change bit |
| * until timeout. |
| */ |
| static int espi_vw_s2m_set_w4m(uint32_t ridx, uint32_t src_num, |
| uint8_t level) |
| { |
| uint32_t i; |
| |
| MCHP_ESPI_VW_S2M_SRC(ridx, src_num) = level & 0x01; |
| |
| for (i = 0; i < ESPI_S2M_VW_PULSE_LOOP_CNT; i++) { |
| if ((MCHP_ESPI_VW_S2M_CHANGE(ridx) & |
| (1u << src_num)) == 0) |
| return EC_SUCCESS; |
| udelay(ESPI_S2M_VW_PULSE_LOOP_DLY_US); |
| } |
| |
| return EC_ERROR_TIMEOUT; |
| } |
| |
| /* |
| * Create a pulse on a Slave-to-Master VWire |
| * Use case is generate low pulse on SCI# virtual wire. |
| * Should a timeout mechanism be added because we are |
| * waiting on Host eSPI Master to respond to eSPI Alert and |
| * then read the VWires. If the eSPI Master is OK the maximum |
| * time will still be variable depending upon link frequency and |
| * other activity on the link. Other activity is currently bounded by |
| * Host chipset eSPI maximum payload length of 64 bytes + packet overhead. |
| * Lowest eSPI transfer rate is 1x at 20 MHz, assume 30% packet overhead. |
| * (64 * 1.3) * 8 = 666 bits is roughly 34 us. Pad to 100 us. |
| */ |
| int espi_vw_pulse_wire(enum espi_vw_signal signal, int pulse_level) |
| { |
| int rc, tidx; |
| uint8_t ridx, src_num, level; |
| |
| tidx = espi_vw_get_signal_index(signal); |
| |
| if (tidx < 0) |
| return EC_ERROR_PARAM1; |
| |
| if (0 == (vw_info_tbl[tidx].flags & (1u << 0))) |
| return EC_ERROR_PARAM1; /* signal is Master-to-Slave */ |
| |
| ridx = vw_info_tbl[tidx].reg_idx; |
| src_num = vw_info_tbl[tidx].src_num; |
| |
| level = 0; |
| if (pulse_level) |
| level = 1; |
| |
| #ifdef CONFIG_MCHP_ESPI_DEBUG |
| CPRINTS("eSPI VW Pulse Wire %s to %d", |
| espi_vw_get_wire_name(signal), level); |
| trace2(0, ESPI, 0, "eSPI pulse VW[%d] = %d", signal, level); |
| trace2(0, ESPI, 0, " S2M index=%d src=%d", ridx, src_num); |
| #endif |
| |
| /* set requested inactive state */ |
| rc = espi_vw_s2m_set_w4m(ridx, src_num, ~level); |
| if (rc != EC_SUCCESS) |
| return rc; |
| |
| /* drive to requested active state */ |
| rc = espi_vw_s2m_set_w4m(ridx, src_num, level); |
| if (rc != EC_SUCCESS) |
| return rc; |
| |
| /* set to requested inactive state */ |
| rc = espi_vw_s2m_set_w4m(ridx, src_num, ~level); |
| |
| return rc; |
| } |
| |
| /** |
| * 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) |
| { |
| int vw, tidx; |
| uint8_t ridx, src_num; |
| |
| vw = 0; |
| tidx = espi_vw_get_signal_index(signal); |
| |
| if (tidx >= 0 && (0 == (vw_info_tbl[tidx].flags & (1u << 0)))) { |
| ridx = vw_info_tbl[tidx].reg_idx; |
| src_num = vw_info_tbl[tidx].src_num; |
| vw = MCHP_ESPI_VW_M2S_SRC(ridx, src_num) & 0x01; |
| #ifdef CONFIG_MCHP_ESPI_DEBUG |
| CPRINTS("VW GetWire %s = %d", |
| espi_vw_get_wire_name(signal), vw); |
| trace2(0, ESPI, 0, "VW GetWire[%d] = %d", |
| ((uint32_t)signal - VW_SIGNAL_START), vw); |
| #endif |
| } |
| |
| return vw; |
| } |
| |
| /** |
| * 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) |
| { |
| int tidx; |
| uint8_t ridx, src_num, girq_num, bpos; |
| |
| tidx = espi_vw_get_signal_index(signal); |
| |
| if (tidx < 0) |
| return EC_ERROR_PARAM1; |
| |
| if (0 != (vw_info_tbl[tidx].flags & (1u << 0))) |
| return EC_ERROR_PARAM1; /* signal is Slave-to-Master */ |
| |
| #ifdef CONFIG_MCHP_ESPI_DEBUG |
| CPRINTS("VW IntrEn for VW[%s]", |
| espi_vw_get_wire_name(signal)); |
| trace1(0, ESPI, 0, "VW IntrEn for VW[%d]", |
| ((uint32_t)signal - VW_SIGNAL_START)); |
| #endif |
| |
| ridx = vw_info_tbl[tidx].reg_idx; |
| src_num = vw_info_tbl[tidx].src_num; |
| |
| /* |
| * Set SRCn_IRQ_SELECT field for VWire to either edge |
| * Write enable set bit in GIRQ24 or GIRQ25 |
| * GIRQ24 MSVW00[0:3] through MSVW06[0:3] (bits[0:27]) |
| * GIRQ25 MSVW07[0:3] through MSVW10[0:3] (bits[0:25]) |
| */ |
| MCHP_ESPI_VW_M2S_IRQSEL(ridx, src_num) = |
| MCHP_ESPI_MSVW_IRQSEL_BOTH_EDGES; |
| |
| girq_num = 24; |
| if (ridx > 6) { |
| girq_num++; |
| ridx -= 7; |
| } |
| bpos = (ridx << 2) + src_num; |
| |
| MCHP_INT_SOURCE(girq_num) = (1ul << bpos); |
| MCHP_INT_ENABLE(girq_num) = (1ul << bpos); |
| |
| 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) |
| { |
| int tidx; |
| uint8_t ridx, src_num, bpos; |
| |
| tidx = espi_vw_get_signal_index(signal); |
| |
| if (tidx < 0) |
| return EC_ERROR_PARAM1; |
| |
| if (0 != (vw_info_tbl[tidx].flags & (1u << 0))) |
| return EC_ERROR_PARAM1; /* signal is Slave-to-Master */ |
| |
| #ifdef CONFIG_MCHP_ESPI_DEBUG |
| CPRINTS("VW IntrDis for VW[%s]", |
| espi_vw_get_wire_name(signal)); |
| trace1(0, ESPI, 0, "VW IntrDis for VW[%d]", |
| (signal - VW_SIGNAL_START)); |
| #endif |
| |
| ridx = vw_info_tbl[tidx].reg_idx; |
| src_num = vw_info_tbl[tidx].src_num; |
| |
| /* |
| * Set SRCn_IRQ_SELECT field for VWire to disabled |
| * Write enable set bit in GIRQ24 or GIRQ25 |
| * GIRQ24 MSVW00[0:3] through MSVW06[0:3] (bits[0:27]) |
| * GIRQ25 MSVW07[0:3] through MSVW10[0:3] (bits[0:25]) |
| */ |
| MCHP_ESPI_VW_M2S_IRQSEL(ridx, src_num) = |
| MCHP_ESPI_MSVW_IRQSEL_DISABLED; |
| |
| if (ridx < 7) { |
| bpos = (ridx << 2) + src_num; |
| MCHP_INT_DISABLE(24) = (1ul << bpos); |
| |
| } else { |
| bpos = ((ridx - 7) << 2) + src_num; |
| MCHP_INT_DISABLE(25) = (1ul << bpos); |
| } |
| |
| return EC_SUCCESS; |
| } |
| |
| /************************************************************************/ |
| /* VW event handlers */ |
| |
| #ifdef CONFIG_CHIPSET_RESET_HOOK |
| static void espi_chipset_reset(void) |
| { |
| hook_notify(HOOK_CHIPSET_RESET); |
| } |
| DECLARE_DEFERRED(espi_chipset_reset); |
| #endif |
| |
| |
| /* SLP_Sx event handler */ |
| void espi_vw_evt_slp_s3_n(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW SLP_S3: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_SLP_S3_L change to %d", wire_state); |
| espi_vw_power_signal_interrupt(VW_SLP_S3_L); |
| } |
| |
| void espi_vw_evt_slp_s4_n(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW SLP_S4: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_SLP_S4_L change to %d", wire_state); |
| espi_vw_power_signal_interrupt(VW_SLP_S4_L); |
| } |
| |
| void espi_vw_evt_slp_s5_n(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW SLP_S5: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_SLP_S5_L change to %d", wire_state); |
| espi_vw_power_signal_interrupt(VW_SLP_S5_L); |
| } |
| |
| void espi_vw_evt_sus_stat_n(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW SUS_STAT: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_SUS_STAT change to %d", wire_state); |
| espi_vw_power_signal_interrupt(VW_SUS_STAT_L); |
| } |
| |
| /* PLTRST# event handler */ |
| void espi_vw_evt_pltrst_n(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW PLTRST#: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_PLTRST# change to %d", wire_state); |
| |
| if (wire_state) /* Platform Reset de-assertion */ |
| espi_host_init(); |
| else /* assertion */ |
| #ifdef CONFIG_CHIPSET_RESET_HOOK |
| hook_call_deferred(&espi_chipset_reset_data, MSEC); |
| #endif |
| |
| } |
| |
| /* OOB Reset Warn event handler */ |
| void espi_vw_evt_oob_rst_warn(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW OOB_RST_WARN: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_OOB_RST_WARN change to %d", wire_state); |
| |
| espi_oob_flush(); |
| |
| espi_vw_set_wire(VW_OOB_RST_ACK, wire_state); |
| } |
| |
| /* SUS_WARN# event handler */ |
| void espi_vw_evt_sus_warn_n(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW SUS_WARN#: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_SUS_WARN# change to %d", wire_state); |
| |
| udelay(100); |
| |
| /* |
| * Add any Deep Sx prep here |
| * NOTE: we could schedule a deferred function and have |
| * it send ACK to host after preparing for Deep Sx |
| */ |
| #ifdef CONFIG_MCHP_ESPI_VW_SAVE_ON_SLEEP |
| espi_vw_save(); |
| #endif |
| /* Send ACK to host by WARN#'s wire */ |
| espi_vw_set_wire(VW_SUS_ACK, wire_state); |
| } |
| |
| /* |
| * SUS_PWRDN_ACK |
| * PCH is informing us it does not need suspend power well. |
| * if SUS_PWRDN_ACK == 1 we can turn off suspend power well assuming |
| * hardware design allow. |
| */ |
| void espi_vw_evt_sus_pwrdn_ack(uint32_t wire_state, uint32_t bpos) |
| { |
| trace1(0, ESPI, 0, "VW_SUS_PWRDN_ACK change to %d", wire_state); |
| CPRINTS("VW SUS_PWRDN_ACK: %d", wire_state); |
| } |
| |
| /* SLP_A#(SLP_M#) */ |
| void espi_vw_evt_slp_a_n(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW SLP_A: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_SLP_A# change to %d", wire_state); |
| |
| /* Put handling of ASW well devices here, if any */ |
| } |
| |
| /* HOST_RST WARN event handler */ |
| void espi_vw_evt_host_rst_warn(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW HOST_RST_WARN: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_HOST_RST_WARN change to %d", wire_state); |
| |
| espi_pc_flush(); |
| |
| /* Send HOST_RST_ACK to host */ |
| espi_vw_set_wire(VW_HOST_RST_ACK, wire_state); |
| } |
| |
| /* SLP_LAN# */ |
| void espi_vw_evt_slp_lan_n(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW SLP_LAN: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_SLP_LAN# change to %d", wire_state); |
| } |
| |
| /* SLP_WLAN# */ |
| void espi_vw_evt_slp_wlan_n(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW SLP_WLAN: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_SLP_WLAN# change to %d", wire_state); |
| } |
| |
| void espi_vw_evt_host_c10(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("VW HOST_C10: %d", wire_state); |
| trace1(0, ESPI, 0, "VW_HOST_C10 change to %d", wire_state); |
| } |
| |
| void espi_vw_evt1_dflt(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("Unknown M2S VW: state=%d GIRQ24 bitpos=%d", wire_state, bpos); |
| MCHP_INT_DISABLE(24) = (1ul << bpos); |
| } |
| |
| void espi_vw_evt2_dflt(uint32_t wire_state, uint32_t bpos) |
| { |
| CPRINTS("Unknown M2S VW: state=%d GIRQ25 bitpos=%d", wire_state, bpos); |
| MCHP_INT_DISABLE(25) = (1ul << bpos); |
| } |
| |
| /************************************************************************/ |
| /* Interrupt handlers */ |
| |
| /* MEC1701H |
| * GIRQ19 all direct connect capable, none wake capable |
| * b[0] = Peripheral Channel (PC) |
| * b[1] = Bus Master 1 (BM1) |
| * b[2] = Bus Master 2 (BM2) |
| * b[3] = LTR |
| * b[4] = OOB_UP |
| * b[5] = OOB_DN |
| * b[6] = Flash Channel (FC) |
| * b[7] = ESPI_RESET# change |
| * b[8] = VWire Channel (VW) enable assertion |
| * b[9:31] = 0 reserved |
| * |
| * GIRQ22 b[9]=ESPI interface wake peripheral logic only, not EC. |
| * Not direct connect capable |
| * |
| * GIRQ24 |
| * b[0:3] = MSVW00_SRC[0:3] |
| * b[4:7] = MSVW01_SRC[0:3] |
| * b[8:11] = MSVW02_SRC[0:3] |
| * b[12:15] = MSVW03_SRC[0:3] |
| * b[16:19] = MSVW04_SRC[0:3] |
| * b[20:23] = MSVW05_SRC[0:3] |
| * b[24:27] = MSVW06_SRC[0:3] |
| * b[28:31] = 0 reserved |
| * |
| * GIRQ25 |
| * b[0:3] = MSVW07_SRC[0:3] |
| * b[4:7] = MSVW08_SRC[0:3] |
| * b[8:11] = MSVW09_SRC[0:3] |
| * b[12:15] = MSVW10_SRC[0:3] |
| * b[16:31] = 0 reserved |
| * |
| */ |
| |
| typedef void (*FPVW)(uint32_t, uint32_t); |
| |
| #define MCHP_GIRQ24_NUM_M2S (7 * 4) |
| const FPVW girq24_vw_handlers[MCHP_GIRQ24_NUM_M2S] = { |
| espi_vw_evt_slp_s3_n, /* MSVW00, Host M2S 02h */ |
| espi_vw_evt_slp_s4_n, |
| espi_vw_evt_slp_s5_n, |
| espi_vw_evt1_dflt, |
| espi_vw_evt_sus_stat_n, /* MSVW01, Host M2S 03h */ |
| espi_vw_evt_pltrst_n, |
| espi_vw_evt_oob_rst_warn, |
| espi_vw_evt1_dflt, |
| espi_vw_evt_host_rst_warn, /* MSVW02, Host M2S 07h */ |
| espi_vw_evt1_dflt, |
| espi_vw_evt1_dflt, |
| espi_vw_evt1_dflt, |
| espi_vw_evt_sus_warn_n, /* MSVW03, Host M2S 41h */ |
| espi_vw_evt_sus_pwrdn_ack, |
| espi_vw_evt1_dflt, |
| espi_vw_evt_slp_a_n, |
| espi_vw_evt_slp_lan_n, /* MSVW04, Host M2S 42h */ |
| espi_vw_evt_slp_wlan_n, |
| espi_vw_evt1_dflt, |
| espi_vw_evt1_dflt, |
| espi_vw_evt1_dflt, /* MSVW05, Host M2S 43h */ |
| espi_vw_evt1_dflt, |
| espi_vw_evt1_dflt, |
| espi_vw_evt1_dflt, |
| espi_vw_evt1_dflt, /* MSVW06, Host M2S 44h */ |
| espi_vw_evt1_dflt, |
| espi_vw_evt1_dflt, |
| espi_vw_evt1_dflt |
| }; |
| |
| #define MCHP_GIRQ25_NUM_M2S (4 * 4) |
| const FPVW girq25_vw_handlers[MCHP_GIRQ25_NUM_M2S] = { |
| espi_vw_evt_host_c10, /* MSVW07, Host M2S 47h */ |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, /* MSVW08 unassigned */ |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, /* MSVW09 unassigned */ |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, /* MSVW10 unassigned */ |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, |
| espi_vw_evt2_dflt, |
| }; |
| |
| /* Interrupt handler for eSPI virtual wires in MSVW00 - MSVW01 */ |
| void espi_mswv1_interrupt(void) |
| { |
| uint32_t d, girq24_result, bpos; |
| |
| d = MCHP_INT_ENABLE(24); |
| girq24_result = MCHP_INT_RESULT(24); |
| MCHP_INT_SOURCE(24) = girq24_result; |
| |
| bpos = __builtin_ctz(girq24_result); /* rbit, clz sequence */ |
| while (bpos != 32) { |
| d = *(uint8_t *)(MCHP_ESPI_MSVW_BASE + 8 + |
| (12 * (bpos >> 2)) + (bpos & 0x03)) & 0x01; |
| (girq24_vw_handlers[bpos])(d, bpos); |
| girq24_result &= ~(1ul << bpos); |
| bpos = __builtin_ctz(girq24_result); |
| } |
| } |
| DECLARE_IRQ(MCHP_IRQ_GIRQ24, espi_mswv1_interrupt, 2); |
| |
| |
| /* Interrupt handler for eSPI virtual wires in MSVW07 - MSVW10 */ |
| void espi_msvw2_interrupt(void) |
| { |
| uint32_t d, girq25_result, bpos; |
| |
| d = MCHP_INT_ENABLE(25); |
| girq25_result = MCHP_INT_RESULT(25); |
| MCHP_INT_SOURCE(25) = girq25_result; |
| |
| bpos = __builtin_ctz(girq25_result); /* rbit, clz sequence */ |
| while (bpos != 32) { |
| d = *(uint8_t *)(MCHP_ESPI_MSVW_BASE + (12 * 7) + 8 + |
| (12 * (bpos >> 2)) + (bpos & 0x03)) & 0x01; |
| (girq25_vw_handlers[bpos])(d, bpos); |
| girq25_result &= ~(1ul << bpos); |
| bpos = __builtin_ctz(girq25_result); |
| } |
| } |
| DECLARE_IRQ(MCHP_IRQ_GIRQ25, espi_msvw2_interrupt, 2); |
| |
| |
| |
| /* |
| * NOTES: |
| * While ESPI_RESET# is asserted, all eSPI blocks are held in reset and |
| * their registers can't be programmed. All channel Enable and Ready bits |
| * are cleared. The only operational logic is the ESPI_RESET# change |
| * detection logic. |
| * Once ESPI_RESET# de-asserts, firmware can enable interrupts on all |
| * other eSPI channels/components. |
| * Implications are: |
| * ESPI_RESET# assertion - |
| * All channel ready bits are cleared stopping all outstanding |
| * transactions and clearing registers and internal FIFO's. |
| * ESPI_RESET# de-assertion - |
| * All channels/components can now be programmed and can detect |
| * reception of channel enable messages from the eSPI Master. |
| */ |
| |
| /* |
| * eSPI Reset change handler |
| * Multiple scenarios must be handled. |
| * eSPI Link initialization from de-assertion of RSMRST# |
| * Upon RSMRST# de-assertion, the PCH may drive ESPI_RESET# low |
| * and then back high. If the platform has a pull-down on ESPI_RESET# |
| * then we will not see both edges. We must handle the scenario where |
| * ESPI_RESET# has only a rising edge or is pulsed low once RSMRST# |
| * has been released. |
| * eSPI Link is operational and PCH asserts ESPI_RESET# due to |
| * global reset event or some other system problem. |
| * eSPI link is operational and the system generates a global reset |
| * event to the PCH. EC is unaware of global reset and sees PCH |
| * activate ESPI_RESET#. |
| * |
| * ESPI_RESET# assertion will disable all MCHP eSPI channel ready |
| * bits and place all channels is reset state. Any hardware affected by |
| * ESPI_RESET# must be re-initialized after ESPI_RESET# de-asserts. |
| * |
| * Note ESPI_RESET# is not equivalent to LPC LRESET#. LRESET# is |
| * equivalent to eSPI Platform Reset. |
| * |
| */ |
| void espi_reset_isr(void) |
| { |
| uint8_t erst; |
| |
| erst = MCHP_ESPI_IO_RESET_STATUS; |
| MCHP_ESPI_IO_RESET_STATUS = erst; |
| MCHP_INT_SOURCE(MCHP_ESPI_GIRQ) = MCHP_ESPI_RESET_GIRQ_BIT; |
| if (erst & (1ul << 1)) { /* rising edge - reset de-asserted */ |
| MCHP_INT_ENABLE(MCHP_ESPI_GIRQ) = ( |
| MCHP_ESPI_PC_GIRQ_BIT + |
| MCHP_ESPI_OOB_TX_GIRQ_BIT + |
| MCHP_ESPI_FC_GIRQ_BIT + |
| MCHP_ESPI_VW_EN_GIRQ_BIT); |
| MCHP_ESPI_OOB_TX_IEN = (1ul << 1); |
| MCHP_ESPI_FC_IEN = (1ul << 1); |
| MCHP_ESPI_PC_IEN = (1ul << 25); |
| CPRINTS("eSPI Reset de-assert"); |
| trace0(0, ESPI, 0, "eSPI Reset de-assert"); |
| |
| } else { /* falling edge - reset asserted */ |
| MCHP_INT_SOURCE(MCHP_ESPI_GIRQ) = ( |
| MCHP_ESPI_PC_GIRQ_BIT + |
| MCHP_ESPI_OOB_TX_GIRQ_BIT + |
| MCHP_ESPI_FC_GIRQ_BIT + |
| MCHP_ESPI_VW_EN_GIRQ_BIT); |
| MCHP_INT_DISABLE(MCHP_ESPI_GIRQ) = ( |
| MCHP_ESPI_PC_GIRQ_BIT + |
| MCHP_ESPI_OOB_TX_GIRQ_BIT + |
| MCHP_ESPI_FC_GIRQ_BIT + |
| MCHP_ESPI_VW_EN_GIRQ_BIT); |
| espi_channels_ready = 0; |
| |
| chipset_handle_espi_reset_assert(); |
| |
| CPRINTS("eSPI Reset assert"); |
| trace0(0, ESPI, 0, "eSPI Reset assert"); |
| } |
| } |
| DECLARE_IRQ(MCHP_IRQ_ESPI_RESET, espi_reset_isr, 3); |
| |
| /* |
| * eSPI Virtual Wire channel enable handler |
| * Must disable once VW Enable is set by eSPI Master |
| */ |
| void espi_vw_en_isr(void) |
| { |
| MCHP_INT_DISABLE(MCHP_ESPI_GIRQ) = MCHP_ESPI_VW_EN_GIRQ_BIT; |
| MCHP_INT_SOURCE(MCHP_ESPI_GIRQ) = MCHP_ESPI_VW_EN_GIRQ_BIT; |
| |
| MCHP_ESPI_IO_VW_READY = 1; |
| |
| espi_channels_ready |= (1ul << 0); |
| |
| CPRINTS("eSPI VW Enable received, set VW Ready"); |
| trace0(0, ESPI, 0, "VW Enable. Set VW Ready"); |
| |
| if (0x03 == (espi_channels_ready & 0x03)) |
| espi_send_boot_load_done(); |
| } |
| DECLARE_IRQ(MCHP_IRQ_ESPI_VW_EN, espi_vw_en_isr, 2); |
| |
| |
| /* |
| * eSPI OOB TX and OOB channel enable change interrupt handler |
| */ |
| void espi_oob_tx_isr(void) |
| { |
| uint32_t sts; |
| |
| sts = MCHP_ESPI_OOB_TX_STATUS; |
| MCHP_ESPI_OOB_TX_STATUS = sts; |
| MCHP_INT_SOURCE(MCHP_ESPI_GIRQ) = MCHP_ESPI_OOB_TX_GIRQ_BIT; |
| if (sts & (1ul << 1)) { |
| /* Channel Enable change */ |
| if (sts & (1ul << 9)) { /* enable? */ |
| MCHP_ESPI_OOB_RX_LEN = 73; |
| MCHP_ESPI_IO_OOB_READY = 1; |
| espi_channels_ready |= (1ul << 2); |
| CPRINTS("eSPI OOB_UP ISR: OOB Channel Enable"); |
| trace0(0, ESPI, 0, "OOB_TX OOB Enable"); |
| } else { /* no, disabled by Master */ |
| espi_channels_ready &= ~(1ul << 2); |
| CPRINTS("eSPI OOB_UP ISR: OOB Channel Disable"); |
| trace0(0, ESPI, 0, "eSPI OOB_TX OOB Disable"); |
| } |
| } else { |
| /* Handle OOB Up transmit status: done and/or errors, here */ |
| CPRINTS("eSPI OOB_UP status = 0x%x", sts); |
| trace11(0, ESPI, 0, "eSPI OOB_TX Status = 0x%08x", sts); |
| } |
| } |
| DECLARE_IRQ(MCHP_IRQ_ESPI_OOB_UP, espi_oob_tx_isr, 2); |
| |
| |
| /* eSPI OOB RX interrupt handler */ |
| void espi_oob_rx_isr(void) |
| { |
| uint32_t sts; |
| |
| sts = MCHP_ESPI_OOB_RX_STATUS; |
| MCHP_ESPI_OOB_RX_STATUS = sts; |
| MCHP_INT_SOURCE(MCHP_ESPI_GIRQ) = MCHP_ESPI_OOB_RX_GIRQ_BIT; |
| /* Handle OOB Up transmit status: done and/or errors, if any */ |
| CPRINTS("eSPI OOB_DN status = 0x%x", sts); |
| trace11(0, ESPI, 0, "eSPI OOB_RX Status = 0x%08x", sts); |
| } |
| DECLARE_IRQ(MCHP_IRQ_ESPI_OOB_DN, espi_oob_rx_isr, 2); |
| |
| |
| /* |
| * eSPI Flash Channel enable change and data transfer |
| * interrupt handler |
| */ |
| void espi_fc_isr(void) |
| { |
| uint32_t sts; |
| |
| sts = MCHP_ESPI_FC_STATUS; |
| MCHP_ESPI_FC_STATUS = sts; |
| MCHP_INT_SOURCE(MCHP_ESPI_GIRQ) = MCHP_ESPI_FC_GIRQ_BIT; |
| if (sts & (1ul << 1)) { |
| /* Channel Enable change */ |
| if (sts & (1ul << 0)) { /* enable? */ |
| MCHP_ESPI_IO_FC_READY = 1; |
| espi_channels_ready |= (1ul << 1); |
| CPRINTS("eSPI FC ISR: Enable"); |
| trace0(0, ESPI, 0, "eSPI FC Enable"); |
| if (0x03 == (espi_channels_ready & 0x03)) |
| espi_send_boot_load_done(); |
| } else { /* no, disabled by Master */ |
| espi_channels_ready &= ~(1ul << 1); |
| CPRINTS("eSPI FC ISR: Disable"); |
| trace0(0, ESPI, 0, "eSPI FC Disable"); |
| } |
| } else { |
| /* Handle FC command status: done and/or errors */ |
| CPRINTS("eSPI FC status = 0x%x", sts); |
| trace11(0, ESPI, 0, "eSPI FC Status = 0x%08x", sts); |
| } |
| } |
| DECLARE_IRQ(MCHP_IRQ_ESPI_FC, espi_fc_isr, 2); |
| |
| |
| /* eSPI Peripheral Channel interrupt handler */ |
| void espi_pc_isr(void) |
| { |
| uint32_t sts; |
| |
| sts = MCHP_ESPI_PC_STATUS; |
| MCHP_ESPI_PC_STATUS = sts; |
| MCHP_INT_SOURCE(MCHP_ESPI_GIRQ) = MCHP_ESPI_PC_GIRQ_BIT; |
| if (sts & (1ul << 25)) { |
| if (sts & (1ul << 24)) { |
| MCHP_ESPI_IO_PC_READY = 1; |
| espi_channels_ready |= (1ul << 3); |
| CPRINTS("eSPI PC Channel Enable"); |
| trace0(0, ESPI, 0, "eSPI PC Enable"); |
| } else { |
| espi_channels_ready &= ~(1ul << 3); |
| CPRINTS("eSPI PC Channel Disable"); |
| trace0(0, ESPI, 0, "eSPI PC Disable"); |
| } |
| |
| } else { |
| /* Handler PC channel errors here */ |
| CPRINTS("eSPI PC status = 0x%x", sts); |
| trace11(0, ESPI, 0, "eSPI PC Status = 0x%08x", sts); |
| } |
| } |
| DECLARE_IRQ(MCHP_IRQ_ESPI_PC, espi_pc_isr, 2); |
| |
| |
| /************************************************************************/ |
| |
| /* |
| * Enable/disable direct mode interrupt for ESPI_RESET# change. |
| * Optionally clear status before enable or after disable. |
| */ |
| static void espi_reset_ictrl(int enable, int clr_status) |
| { |
| if (enable) { |
| if (clr_status) { |
| MCHP_ESPI_IO_RESET_STATUS = |
| MCHP_ESPI_RST_CHG_STS; |
| MCHP_INT_SOURCE(MCHP_ESPI_GIRQ) = |
| MCHP_ESPI_RESET_GIRQ_BIT; |
| } |
| MCHP_ESPI_IO_RESET_IEN |= MCHP_ESPI_RST_IEN; |
| MCHP_INT_ENABLE(MCHP_ESPI_GIRQ) = |
| MCHP_ESPI_RESET_GIRQ_BIT; |
| task_enable_irq(MCHP_IRQ_ESPI_RESET); |
| } else { |
| task_disable_irq(MCHP_IRQ_ESPI_RESET); |
| MCHP_INT_DISABLE(MCHP_ESPI_GIRQ) = |
| MCHP_ESPI_RESET_GIRQ_BIT; |
| MCHP_ESPI_IO_RESET_IEN &= ~(MCHP_ESPI_RST_IEN); |
| if (clr_status) { |
| MCHP_ESPI_IO_RESET_STATUS = |
| MCHP_ESPI_RST_CHG_STS; |
| MCHP_INT_SOURCE(MCHP_ESPI_GIRQ) = |
| MCHP_ESPI_RESET_GIRQ_BIT; |
| } |
| } |
| } |
| |
| /* eSPI Initialization functions */ |
| |
| /* MEC1701H */ |
| void espi_init(void) |
| { |
| espi_channels_ready = 0; |
| |
| CPRINTS("eSPI - espi_init"); |
| trace0(0, ESPI, 0, "eSPI Init"); |
| |
| /* Clear PCR eSPI sleep enable */ |
| MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_ESPI); |
| |
| /* |
| * b[8]=0(eSPI PLTRST# VWire is platform reset), b[0]=0 |
| * VCC_PWRGD is asserted when PLTRST# VWire is 1(inactive) |
| */ |
| MCHP_PCR_PWR_RST_CTL = 0; |
| |
| /* |
| * There is no MODULE_ESPI in include/module_id.h |
| * eSPI pins marked as MODULE_LPC in board/myboard/board.h |
| * eSPI pins are on VTR3. |
| * Make sure VTR3 chip knows VTR3 is 1.8V |
| * This is done in system_pre_init() |
| */ |
| gpio_config_module(MODULE_LPC, 1); |
| |
| /* Override Boot-ROM configuration */ |
| #ifdef CONFIG_HOSTCMD_ESPI_EC_CHAN_BITMAP |
| MCHP_ESPI_IO_CAP0 = CONFIG_HOSTCMD_ESPI_EC_CHAN_BITMAP; |
| #endif |
| |
| #ifdef CONFIG_HOSTCMD_ESPI_EC_MAX_FREQ |
| MCHP_ESPI_IO_CAP1 &= ~(MCHP_ESPI_CAP1_MAX_FREQ_MASK); |
| #if CONFIG_HOSTCMD_ESPI_EC_MAX_FREQ == 25 |
| MCHP_ESPI_IO_CAP1 |= MCHP_ESPI_CAP1_MAX_FREQ_25M; |
| #elif CONFIG_HOSTCMD_ESPI_EC_MAX_FREQ == 33 |
| MCHP_ESPI_IO_CAP1 |= MCHP_ESPI_CAP1_MAX_FREQ_33M; |
| #elif CONFIG_HOSTCMD_ESPI_EC_MAX_FREQ == 50 |
| MCHP_ESPI_IO_CAP1 |= MCHP_ESPI_CAP1_MAX_FREQ_50M; |
| #elif CONFIG_HOSTCMD_ESPI_EC_MAX_FREQ == 66 |
| MCHP_ESPI_IO_CAP1 |= MCHP_ESPI_CAP1_MAX_FREQ_66M; |
| #else |
| MCHP_ESPI_IO_CAP1 |= MCHP_ESPI_CAP1_MAX_FREQ_20M; |
| #endif |
| #endif |
| |
| #ifdef CONFIG_HOSTCMD_ESPI_EC_MODE |
| MCHP_ESPI_IO_CAP1 &= ~(MCHP_ESPI_CAP1_IO_MASK); |
| MCHP_ESPI_IO_CAP1 |= ((CONFIG_HOSTCMD_ESPI_EC_MODE) |
| << MCHP_ESPI_CAP1_IO_BITPOS); |
| #endif |
| |
| #ifdef CONFIG_HOSTCMD_ESPI |
| MCHP_ESPI_IO_PLTRST_SRC = MCHP_ESPI_PLTRST_SRC_VW; |
| #else |
| MCHP_ESPI_IO_PLTRST_SRC = MCHP_ESPI_PLTRST_SRC_PIN; |
| #endif |
| |
| MCHP_PCR_PWR_RST_CTL &= |
| ~(1ul << MCHP_PCR_PWR_HOST_RST_SEL_BITPOS); |
| |
| MCHP_ESPI_ACTIVATE = 1; |
| |
| espi_bar_pre_init(); |
| |
| /* |
| * VWires are configured to be reset by different events. |
| * Default configuration has: |
| * RESET_SYS (chip reset) MSVW00, MSVW04 |
| * RESET_ESPI MSVW01, MSVW03, SMVW00, SMVW01 |
| * PLTRST MSVW02, SMVW02 |
| */ |
| espi_vw_pre_init(); |
| |
| /* |
| * Configure MSVW00 & MSVW04 |
| * Any change to default values (SRCn bits) |
| * Any change to interrupt enable, SRCn_IRQ_SELECT bit fields |
| * Should interrupt bits in MSVWyx and GIRQ24/25 be touched |
| * before ESPI_RESET# de-asserts? |
| */ |
| |
| MCHP_ESPI_PC_STATUS = 0xfffffffful; |
| MCHP_ESPI_OOB_RX_STATUS = 0xfffffffful; |
| MCHP_ESPI_FC_STATUS = 0xfffffffful; |
| MCHP_INT_DISABLE(MCHP_ESPI_GIRQ) = 0x1FFul; |
| MCHP_INT_SOURCE(MCHP_ESPI_GIRQ) = 0x1FFul; |
| |
| task_enable_irq(MCHP_IRQ_ESPI_PC); |
| task_enable_irq(MCHP_IRQ_ESPI_OOB_UP); |
| task_enable_irq(MCHP_IRQ_ESPI_OOB_DN); |
| task_enable_irq(MCHP_IRQ_ESPI_FC); |
| task_enable_irq(MCHP_IRQ_ESPI_VW_EN); |
| |
| /* Enable eSPI Master-to-Slave Virtual wire NVIC inputs |
| * VWire block interrupts are all disabled by default |
| * and will be controlled by espi_vw_enable/disable_wire_in |
| */ |
| CPRINTS("eSPI - enable ESPI_RESET# interrupt"); |
| trace0(0, ESPI, 0, "Enable ESPI_RESET# interrupt"); |
| |
| /* Enable ESPI_RESET# interrupt and clear status */ |
| espi_reset_ictrl(1, 1); |
| |
| CPRINTS("eSPI - espi_init - done"); |
| trace0(0, ESPI, 0, "eSPI Init Done"); |
| |
| } |
| |
| |
| #ifdef CONFIG_MCHP_ESPI_EC_CMD |
| /* TODO */ |
| static int command_espi(int argc, char **argv) |
| { |
| uint32_t chan, w0, w1, w2; |
| char *e; |
| |
| if (argc == 1) { |
| return EC_ERROR_INVAL; |
| /* Get value of eSPI registers */ |
| } else if (argc == 2) { |
| int i; |
| |
| if (strcasecmp(argv[1], "cfg") == 0) { |
| ccprintf("eSPI Reg32A [0x%08x]\n", |
| MCHP_ESPI_IO_REG32_A); |
| ccprintf("eSPI Reg32B [0x%08x]\n", |
| MCHP_ESPI_IO_REG32_B); |
| ccprintf("eSPI Reg32C [0x%08x]\n", |
| MCHP_ESPI_IO_REG32_C); |
| ccprintf("eSPI Reg32D [0x%08x]\n", |
| MCHP_ESPI_IO_REG32_D); |
| } else if (strcasecmp(argv[1], "vsm") == 0) { |
| for (i = 0; i < MSVW_MAX; i++) { |
| w0 = MSVW(i, 0); |
| w1 = MSVW(i, 1); |
| w2 = MSVW(i, 2); |
| ccprintf("MSVW%d: 0x%08x:%08x:%08x\n", i, |
| w2, w1, w0); |
| } |
| } else if (strcasecmp(argv[1], "vms") == 0) { |
| for (i = 0; i < SMVW_MAX; i++) { |
| w0 = SMVW(i, 0); |
| w1 = SMVW(i, 1); |
| ccprintf("SMVW%d: 0x%08x:%08x\n", i, w1, w0); |
| } |
| } |
| /* Enable/Disable the channels of eSPI */ |
| } else if (argc == 3) { |
| uint32_t m = (uint32_t) strtoi(argv[2], &e, 0); |
| |
| if (*e) |
| return EC_ERROR_PARAM2; |
| if (m < 0 || m > 4) |
| return EC_ERROR_PARAM2; |
| else if (m == 4) |
| chan = 0x0F; |
| else |
| chan = 0x01 << m; |
| if (strcasecmp(argv[1], "en") == 0) |
| MCHP_ESPI_IO_CAP0 |= chan; |
| else if (strcasecmp(argv[1], "dis") == 0) |
| MCHP_ESPI_IO_CAP0 &= ~chan; |
| else |
| return EC_ERROR_PARAM1; |
| ccprintf("eSPI IO Cap0 [0x%02x]\n", MCHP_ESPI_IO_CAP0); |
| } |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(espi, command_espi, |
| "cfg/vms/vsm/en/dis [channel]", |
| "eSPI configurations"); |
| #endif |