diff options
author | Scott Worley <scott.worley@microchip.corp-partner.google.com> | 2017-12-21 14:42:22 -0500 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-01-02 15:48:21 -0800 |
commit | a92647b9ce517fc28f924c2abc60dec3d1bdc98e (patch) | |
tree | c6c399323b6906d2508c3b3d873170f9b402e82d /chip/mchp | |
parent | f147f61f38a27e57308033f81fedfd8834b58a62 (diff) | |
download | chrome-ec-a92647b9ce517fc28f924c2abc60dec3d1bdc98e.tar.gz |
ec_chip_mchp: Add LPC and eSPI files.
Add Microchip MEC17xx family LPC and eSPI
support files. Fix fault when host sends
old style command packet.
BRANCH=none
BUG=
TEST=Review only.
Change-Id: I35c33be407e093b19c456b5a78c2c4e19a267b95
Signed-off-by: Scott Worley <scott.worley@microchip.corp-partner.google.com>
Reviewed-on: https://chromium-review.googlesource.com/840647
Commit-Ready: Randall Spangler <rspangler@chromium.org>
Tested-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Diffstat (limited to 'chip/mchp')
-rw-r--r-- | chip/mchp/espi.c | 1619 | ||||
-rw-r--r-- | chip/mchp/lpc.c | 886 | ||||
-rw-r--r-- | chip/mchp/lpc_chip.h | 39 |
3 files changed, 2544 insertions, 0 deletions
diff --git a/chip/mchp/espi.c b/chip/mchp/espi.c new file mode 100644 index 0000000000..30050e235e --- /dev/null +++ b/chip/mchp/espi.c @@ -0,0 +1,1619 @@ +/* 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 */ + MCHP_ESPI_IO_BAR_CTL_MASK(MCHP_ESPI_IO_BAR_ID_ACPI_EC0) = 0x04; + MCHP_ESPI_IO_BAR(MCHP_ESPI_IO_BAR_ID_ACPI_EC0) = + (0x62ul << 16) + 0x01ul; + MCHP_INT_ENABLE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_IBF_GIRQ_BIT(0); + /* Clear STATUS_PROCESSING bit in case it was set during sysjump */ + MCHP_ACPI_EC_STATUS(0) &= ~EC_LPC_STATUS_PROCESSING; + task_enable_irq(MCHP_IRQ_ACPIEC0_IBF); + + /* Set up ACPI1 for 0x200-0x203, 0x204-0x207 */ + MCHP_ESPI_IO_BAR_CTL_MASK(MCHP_ESPI_IO_BAR_ID_ACPI_EC1) = 0x07; + MCHP_ESPI_IO_BAR(MCHP_ESPI_IO_BAR_ID_ACPI_EC1) = + (0x200ul << 16) + 0x01ul; + MCHP_INT_ENABLE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_IBF_GIRQ_BIT(1); + MCHP_ACPI_EC_STATUS(1) &= ~EC_LPC_STATUS_PROCESSING; + task_enable_irq(MCHP_IRQ_ACPIEC1_IBF); + + /* Set up 8042 interface at 0x60/0x64 */ + MCHP_ESPI_IO_BAR_CTL_MASK(MCHP_ESPI_IO_BAR_ID_8042) = 0x04; + MCHP_ESPI_IO_BAR(MCHP_ESPI_IO_BAR_ID_8042) = + (0x64ul << 16) + 0x01ul; + + /* Set up indication of Auxiliary sts */ + MCHP_8042_KB_CTRL |= 1 << 7; + + MCHP_8042_ACT |= 1; + MCHP_INT_ENABLE(MCHP_8042_GIRQ) = MCHP_8042_OBE_GIRQ_BIT + + MCHP_8042_IBF_GIRQ_BIT; + task_enable_irq(MCHP_IRQ_8042EM_IBF); + task_enable_irq(MCHP_IRQ_8042EM_OBE); + +#ifndef CONFIG_KEYBOARD_IRQ_GPIO + /* Set up SERIRQ for keyboard */ + MCHP_8042_KB_CTRL |= (1 << 5); + MCHP_ESPI_IO_SERIRQ_REG(MCHP_ESPI_8042_SIRQ0) = 1; +#endif + + /* Set up EMI module for memory mapped region, + * IO range 0x800-0x80f + */ + MCHP_ESPI_IO_BAR_CTL_MASK(MCHP_ESPI_IO_BAR_ID_EMI0) = 0x0F; + MCHP_ESPI_IO_BAR(MCHP_ESPI_IO_BAR_ID_EMI0) = + (0x800ul << 16) + 0x01ul; + MCHP_INT_ENABLE(MCHP_EMI_GIRQ) = MCHP_EMI_GIRQ_BIT(0); + task_enable_irq(MCHP_IRQ_EMI0); + + /* + * Access data RAM + * MCHP EMI Base address register = physical address in + * SRAM of buffer. + * EMI hardware adds 16-bit offset Host programs into + * EC_Address_LSB/MSB registers. + */ + MCHP_EMI_MBA0(0) = lpc_mem_mapped_addr(); + + /* + * Limit EMI read / write range. First 256 bytes are RW for host + * commands. Second 256 bytes are RO for mem-mapped data. + */ + MCHP_EMI_MRL0(0) = 0x200; + MCHP_EMI_MWL0(0) = 0x100; + + /* Setup Port80 Debug Hardware for I/O 80h */ + MCHP_P80_CFG(0) = MCHP_P80_FLUSH_FIFO_WO + + MCHP_P80_RESET_TIMESTAMP_WO; + + /* IO 0x80 only, mask = 0 */ + MCHP_ESPI_IO_BAR_CTL_MASK(MCHP_ESPI_IO_BAR_P80_0) = 0x00; + MCHP_ESPI_IO_BAR(MCHP_ESPI_IO_BAR_P80_0) = + (0x80ul << 16) + 0x01ul; + + MCHP_P80_CFG(0) = MCHP_P80_FIFO_THRHOLD_1 + + MCHP_P80_TIMEBASE_1500KHZ + + MCHP_P80_TIMER_ENABLE; + + + MCHP_P80_ACTIVATE(0) = 1; + + MCHP_INT_SOURCE(15) = (1ul << 22); + MCHP_INT_ENABLE(15) = (1ul << 22); + + task_enable_irq(MCHP_IRQ_PORT80DBG0); + + 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_BASE)); + 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) +{ + uint8_t tidx, 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_BASE), 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; + uint8_t tidx, 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; + uint8_t tidx, 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_BASE), 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) +{ + uint8_t tidx, 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_BASE)); +#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) +{ + uint8_t tidx, 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_BASE)); +#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_ESPI_EC_CHAN_BITMAP + MCHP_ESPI_IO_CAP0 = CONFIG_ESPI_EC_CHAN_BITMAP; +#endif + +#ifdef CONFIG_ESPI_EC_MAX_FREQ + MCHP_ESPI_IO_CAP1 &= ~(MCHP_ESPI_CAP1_MAX_FREQ_MASK); +#if CONFIG_ESPI_EC_MAX_FREQ == 25 + MCHP_ESPI_IO_CAP1 |= MCHP_ESPI_CAP1_MAX_FREQ_25M; +#elif CONFIG_ESPI_EC_MAX_FREQ == 33 + MCHP_ESPI_IO_CAP1 |= MCHP_ESPI_CAP1_MAX_FREQ_33M; +#elif CONFIG_ESPI_EC_MAX_FREQ == 50 + MCHP_ESPI_IO_CAP1 |= MCHP_ESPI_CAP1_MAX_FREQ_50M; +#elif CONFIG_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_ESPI_EC_MODE + MCHP_ESPI_IO_CAP1 &= ~(MCHP_ESPI_CAP1_IO_MASK); + MCHP_ESPI_IO_CAP1 |= ((CONFIG_ESPI_EC_MODE) + << MCHP_ESPI_CAP1_IO_BITPOS); +#endif + +#ifdef CONFIG_ESPI_PLTRST_IS_VWIRE + 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 diff --git a/chip/mchp/lpc.c b/chip/mchp/lpc.c new file mode 100644 index 0000000000..37c2adc6ac --- /dev/null +++ b/chip/mchp/lpc.c @@ -0,0 +1,886 @@ +/* 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. + */ + +/* LPC module for MCHP MEC family */ + +#include "common.h" +#include "acpi.h" +#include "console.h" +#include "gpio.h" +#include "hooks.h" +#include "host_command.h" +#include "keyboard_protocol.h" +#include "lpc.h" +#include "lpc_chip.h" +#include "espi.h" +#include "port80.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "timer.h" +#include "util.h" +#include "chipset.h" +#include "tfdp_chip.h" + +/* Console output macros */ + +#define CPUTS(outstr) cputs(CC_LPC, outstr) +#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args) + +static uint8_t +mem_mapped[0x200] __attribute__((section(".bss.big_align"))); + +static struct host_packet lpc_packet; +static struct host_cmd_handler_args host_cmd_args; +static uint8_t host_cmd_flags; /* Flags from host command */ + +static uint8_t params_copy[EC_LPC_HOST_PACKET_SIZE] __aligned(4); +static int init_done; + +static struct ec_lpc_host_args * const lpc_host_args = + (struct ec_lpc_host_args *)mem_mapped; + +#ifdef CONFIG_BOARD_ID_CMD_ACPI_EC1 +static uint8_t custom_acpi_cmd; +static uint8_t custom_acpi_ec2os_cnt; +static uint8_t custom_apci_ec2os[4]; +#endif + + +static void keyboard_irq_assert(void) +{ +#ifdef CONFIG_KEYBOARD_IRQ_GPIO + /* + * Enforce signal-high for long enough for the signal to be + * pulled high by the external pullup resistor. This ensures the + * host will see the following falling edge, regardless of the + * line state before this function call. + */ + gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 1); + udelay(4); + /* Generate a falling edge */ + gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 0); + udelay(4); + + /* Set signal high, now that we've generated the edge */ + gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 1); +#else + /* + * SERIRQ is automatically sent by KBC + */ +#endif +} + +/** + * Generate SMI pulse to the host chipset via GPIO. + * + * If the x86 is in S0, SMI# is sampled at 33MHz, so minimum pulse length + * is 60ns. If the x86 is in S3, SMI# is sampled at 32.768KHz, so we need + * pulse length >61us. Both are short enough and events are infrequent, + * so just delay for 65us. + */ +static void lpc_generate_smi(void) +{ + /* CPRINTS("LPC Pulse SMI"); */ + trace0(0, LPC, 0, "LPC Pulse SMI"); +#ifdef CONFIG_ESPI + /* eSPI: pulse SMI# Virtual Wire low */ + espi_vw_pulse_wire(VW_SMI_L, 0); +#else + gpio_set_level(GPIO_PCH_SMI_L, 0); + udelay(65); + gpio_set_level(GPIO_PCH_SMI_L, 1); +#endif +} + +static void lpc_generate_sci(void) +{ + /* CPRINTS("LPC Pulse SCI"); */ + trace0(0, LPC, 0, "LPC Pulse SCI"); +#ifdef CONFIG_SCI_GPIO + gpio_set_level(CONFIG_SCI_GPIO, 0); + udelay(65); + gpio_set_level(CONFIG_SCI_GPIO, 1); +#else +#ifdef CONFIG_ESPI + espi_vw_pulse_wire(VW_SCI_L, 0); +#else + MCHP_ACPI_PM_STS |= 1; + udelay(65); + MCHP_ACPI_PM_STS &= ~1; +#endif +#endif +} + +/** + * Update the level-sensitive wake signal to the AP. + * + * @param wake_events Currently asserted wake events + */ +static void lpc_update_wake(host_event_t wake_events) +{ + /* + * Mask off power button event, since the AP gets that through a + * separate dedicated GPIO. + */ + wake_events &= ~EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON); + +#ifdef CONFIG_ESPI + espi_vw_set_wire(VW_WAKE_L, !wake_events); +#else + /* Signal is asserted low when wake events is non-zero */ + gpio_set_level(GPIO_PCH_WAKE_L, !wake_events); +#endif +} + +static uint8_t *lpc_get_hostcmd_data_range(void) +{ + return mem_mapped; +} + + +/** + * Update the host event status. + * + * Sends a pulse if masked event status becomes non-zero: + * - SMI pulse via PCH_SMI_L GPIO + * - SCI pulse via PCH_SCI_L GPIO + */ +void lpc_update_host_event_status(void) +{ + int need_sci = 0; + int need_smi = 0; + + CPRINTS("LPC update_host_event_status"); + trace0(0, LPC, 0, "LPC update_host_event_status"); + + if (!init_done) + return; + + /* Disable LPC interrupt while updating status register */ + task_disable_irq(MCHP_IRQ_ACPIEC0_IBF); + + if (lpc_get_host_events_by_type(LPC_HOST_EVENT_SMI)) { + /* Only generate SMI for first event */ + if (!(MCHP_ACPI_EC_STATUS(0) & EC_LPC_STATUS_SMI_PENDING)) + need_smi = 1; + MCHP_ACPI_EC_STATUS(0) |= EC_LPC_STATUS_SMI_PENDING; + } else { + MCHP_ACPI_EC_STATUS(0) &= ~EC_LPC_STATUS_SMI_PENDING; + } + + if (lpc_get_host_events_by_type(LPC_HOST_EVENT_SCI)) { + /* Generate SCI for every event */ + need_sci = 1; + MCHP_ACPI_EC_STATUS(0) |= EC_LPC_STATUS_SCI_PENDING; + } else { + MCHP_ACPI_EC_STATUS(0) &= ~EC_LPC_STATUS_SCI_PENDING; + } + + /* Copy host events to mapped memory */ + *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = + lpc_get_host_events(); + + task_enable_irq(MCHP_IRQ_ACPIEC0_IBF); + + /* Process the wake events. */ + lpc_update_wake(lpc_get_host_events_by_type(LPC_HOST_EVENT_WAKE)); + + /* Send pulse on SMI signal if needed */ + if (need_smi) + lpc_generate_smi(); + + /* ACPI 5.0-12.6.1: Generate SCI for SCI_EVT=1. */ + if (need_sci) + lpc_generate_sci(); +} + +static void lpc_send_response(struct host_cmd_handler_args *args) +{ + uint8_t *out; + int size = args->response_size; + int csum; + int i; + + /* Ignore in-progress on LPC since interface is synchronous anyway */ + if (args->result == EC_RES_IN_PROGRESS) + return; + + /* Handle negative size */ + if (size < 0) { + args->result = EC_RES_INVALID_RESPONSE; + size = 0; + } + + /* New-style response */ + lpc_host_args->flags = + (host_cmd_flags & ~EC_HOST_ARGS_FLAG_FROM_HOST) | + EC_HOST_ARGS_FLAG_TO_HOST; + + lpc_host_args->data_size = size; + + csum = args->command + lpc_host_args->flags + + lpc_host_args->command_version + + lpc_host_args->data_size; + + for (i = 0, out = (uint8_t *)args->response; i < size; i++, out++) + csum += *out; + + lpc_host_args->checksum = (uint8_t)csum; + + /* Fail if response doesn't fit in the param buffer */ + if (size > EC_PROTO2_MAX_PARAM_SIZE) + args->result = EC_RES_INVALID_RESPONSE; + + /* Write result to the data byte. */ + MCHP_ACPI_EC_EC2OS(1, 0) = args->result; + + /* + * Clear processing flag in hardware and + * sticky status in interrupt aggregator. + */ + MCHP_ACPI_EC_STATUS(1) &= ~EC_LPC_STATUS_PROCESSING; + MCHP_INT_SOURCE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_IBF_GIRQ_BIT(1); + +} + +static void lpc_send_response_packet(struct host_packet *pkt) +{ + int i; + uint8_t *p8; + + /* Ignore in-progress on LPC since interface is synchronous anyway */ + if (pkt->driver_result == EC_RES_IN_PROGRESS) { + /* CPRINTS("LPC EC_RES_IN_PROGRESS"); */ + return; + } + + /* CPRINTS("LPC Set EC2OS(1,0)=0x%02x",pkt->driver_result); */ + trace1(0, LPC, 0, "LPC Set EC2OS(1,0)=0x%02x", pkt->driver_result); + + p8 = (uint8_t *)pkt->response; + if (p8 != NULL) { + for (i = 0; i < pkt->response_size; i++) + trace2(0, LPC, 0, "response[%d] = 0x%02x", i, *p8++); + } + + /* Write result to the data byte. */ + MCHP_ACPI_EC_EC2OS(1, 0) = pkt->driver_result; + + /* Clear the busy bit, so the host knows the EC is done. */ + MCHP_ACPI_EC_STATUS(1) &= ~EC_LPC_STATUS_PROCESSING; + MCHP_INT_SOURCE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_IBF_GIRQ_BIT(1); +} + +uint8_t *lpc_get_memmap_range(void) +{ + return mem_mapped + 0x100; +} + +uint32_t lpc_mem_mapped_addr(void) +{ + return (uint32_t)mem_mapped; +} + +void lpc_mem_mapped_init(void) +{ + /* We support LPC args and version 3 protocol */ + *(lpc_get_memmap_range() + EC_MEMMAP_HOST_CMD_FLAGS) = + EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED | + EC_HOST_CMD_FLAG_VERSION_3; +} + + +/* + * Most registers in LPC module are reset when the host is off. We need to + * set up LPC again when the host is starting up. + * MCHP does not appear to connect LRESET# to an interrupt! + * MCHP LRESET# can be one of two pins + * GPIO_0052 Func 2 + * GPIO_0064 Func 1 + * Use GPIO interrupt to detect LRESET# changes. + * Use GPIO_0064 for LRESET#. Must update board/board_name/gpio.inc + * + * For eSPI PLATFORM_RESET# virtual wire is used as LRESET# + * + */ +#ifndef CONFIG_ESPI +static void setup_lpc(void) +{ + gpio_config_module(MODULE_LPC, 1); + + /* + * MCHP LRESET# interrupt is GPIO interrupt + * and configured by GPIO table in board level gpio.inc + * Refer to lpcrst_interrupt() in this file. + */ + + /* Set up ACPI0 for 0x62/0x66 */ + MCHP_LPC_ACPI_EC0_BAR = 0x00628304; + + /* Clear STATUS_PROCESSING bit in case it was set during sysjump */ + MCHP_ACPI_EC_STATUS(0) &= ~EC_LPC_STATUS_PROCESSING; + MCHP_INT_ENABLE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_IBF_GIRQ_BIT(0); + task_enable_irq(MCHP_IRQ_ACPIEC0_IBF); + + /* Set up ACPI1 for 0x200/0x204 */ + MCHP_LPC_ACPI_EC1_BAR = 0x02008407; + + MCHP_ACPI_EC_STATUS(1) &= ~EC_LPC_STATUS_PROCESSING; + MCHP_INT_ENABLE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_IBF_GIRQ_BIT(1); + task_enable_irq(MCHP_IRQ_ACPIEC1_IBF); + + /* Set up 8042 interface at 0x60/0x64 */ + MCHP_LPC_8042_BAR = 0x00608104; + + /* Set up indication of Auxiliary sts */ + MCHP_8042_KB_CTRL |= 1 << 7; + + MCHP_8042_ACT |= 1; + + MCHP_INT_ENABLE(MCHP_8042_GIRQ) = MCHP_8042_OBE_GIRQ_BIT + + MCHP_8042_IBF_GIRQ_BIT; + + task_enable_irq(MCHP_IRQ_8042EM_IBF); + task_enable_irq(MCHP_IRQ_8042EM_OBE); + +#ifndef CONFIG_KEYBOARD_IRQ_GPIO + /* Set up SERIRQ for keyboard */ + MCHP_8042_KB_CTRL |= (1 << 5); + MCHP_LPC_SIRQ(1) = 0x01; +#endif + + /* Set up EMI module for memory mapped region, base address 0x800 */ + MCHP_LPC_EMI0_BAR = 0x0800800f; + + MCHP_INT_ENABLE(MCHP_EMI_GIRQ) = MCHP_EMI_GIRQ_BIT(0); + task_enable_irq(MCHP_IRQ_EMI0); + + /* + * Access data RAM + * MCHP EMI Base address register = physical address of buffer + * in SRAM. EMI hardware adds 16-bit offset Host programs into + * EC_Address_LSB/MSB registers. + */ + MCHP_EMI_MBA0(0) = (uint32_t)mem_mapped; + + /* + * Limit EMI read / write range. First 256 bytes are RW for host + * commands. Second 256 bytes are RO for mem-mapped data. + */ + MCHP_EMI_MRL0(0) = 0x200; + MCHP_EMI_MWL0(0) = 0x100; + + /* Setup Port80 Debug Hardware ports. + * First instance for I/O 80h only. + * Set FIFO interrupt threshold to maximum of 14 bytes. + */ + MCHP_P80_CFG(0) = MCHP_P80_FLUSH_FIFO_WO + + MCHP_P80_RESET_TIMESTAMP_WO; + + MCHP_LPC_P80DBG0_BAR = (0x80ul << 16) + 0x01ul; + + MCHP_P80_CFG(0) = MCHP_P80_FIFO_THRHOLD_14 + + MCHP_P80_TIMEBASE_1500KHZ + + MCHP_P80_TIMER_ENABLE; + + task_enable_irq(MCHP_IRQ_PORT80DBG0); + + lpc_mem_mapped_init(); + + /* Sufficiently initialized */ + init_done = 1; + + /* Update host events now that we can copy them to memmap */ + lpc_update_host_event_status(); +} +DECLARE_HOOK(HOOK_CHIPSET_STARTUP, setup_lpc, HOOK_PRIO_FIRST); +#endif + +static void lpc_init(void) +{ + CPRINTS("LPC HOOK_INIT"); + trace0(0, HOOK, 0, "HOOK_INIT - lpc_init"); + + /* Initialize host args and memory map to all zero */ + memset(lpc_host_args, 0, sizeof(*lpc_host_args)); + memset(lpc_get_memmap_range(), 0, EC_MEMMAP_SIZE); + + /* + * Clear PCR sleep enables for peripherals we are using for + * both LPC and eSPI. + * Global Config, ACPI EC0/1, 8042 Keyboard controller, + * Port80 Capture0, and EMI. + * NOTE: EMI doesn't have a sleep enable. + */ + MCHP_PCR_SLP_DIS_DEV_MASK(2, MCHP_PCR_SLP_EN2_GCFG + + MCHP_PCR_SLP_EN2_ACPI_EC0 + MCHP_PCR_SLP_EN2_ACPI_EC0 + + MCHP_PCR_SLP_EN2_MIF8042); + + MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_P80CAP0); + +#ifdef CONFIG_ESPI + + espi_init(); + +#else + /* Clear PCR LPC sleep enable */ + MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_LPC); + + /* + * b[8]=1(LRESET# is platform reset), b[0]=0 VCC_PWRGD is + * asserted when LRESET# is 1(inactive) + */ + MCHP_PCR_PWR_RST_CTL = 0x100ul; + + /* Activate LPC interface */ + MCHP_LPC_ACT |= 1; + + /* + * Ring Oscillator not permitted to shut down + * until LPC activate bit is cleared + */ + MCHP_LPC_EC_CLK_CTRL |= 3; + + setup_lpc(); +#endif +} +/* + * Set priority to higher than default; this way LPC memory mapped data + * is ready before other inits try to initialize their memmap data. + */ +DECLARE_HOOK(HOOK_INIT, lpc_init, HOOK_PRIO_INIT_LPC); + +#ifdef CONFIG_CHIPSET_RESET_HOOK +static void lpc_chipset_reset(void) +{ + hook_notify(HOOK_CHIPSET_RESET); +} +DECLARE_DEFERRED(lpc_chipset_reset); +#endif + + +#ifdef CONFIG_ESPI +/* + * Called from power/skylake.c chipset_reset() + * For LPC it doesn't call here, instead it pulses RCIN# low for 10 us + */ +void lpc_host_reset(void) +{ + /* Host Reset Control will assert KBRST# (LPC) or RCIN# VW (eSPI) */ +#ifdef CONFIG_ESPI_VW_SIGNALS + espi_vw_pulse_wire(VW_RCIN_L, 0); +#else + gpio_set_level(GPIO_PCH_RCIN_L, 0); + udelay(10); + gpio_set_level(GPIO_PCH_RCIN_L, 1); +#endif +} +#endif /* #ifdef CONFIG_ESPI */ + +void lpc_set_init_done(int val) +{ + init_done = val; +} + +/* + * MCHP MCHP family allows selecting one of two GPIO pins alternate + * functions as LRESET#. + * LRESET# can be monitored as bit[1](read-only) of the LPC Bus Monitor + * register. + * bit[1]==0 -> LRESET# is high + * bit[1]==1 -> LRESET# is low (active) + * LRESET# active causes the EC to activate internal signal RESET_HOST. + * MCHP_PCR_PWR_RST_STS bit[3](read-only) = RESET_HOST_STATUS = + * 0 = Reset active + * 1 = Reset not active + * MCHP is different than MEC1322 in that LRESET# is not connected + * to a separate interrupt source. + * If using LPC the board design must select on of the two GPIO pins + * dedicated for LRESET# and this pin must be configured in the + * board level gpio.inc + */ +void lpcrst_interrupt(enum gpio_signal signal) +{ +#ifndef CONFIG_ESPI + /* Initialize LPC module when LRESET# is deasserted */ + if (!lpc_get_pltrst_asserted()) { + setup_lpc(); + } else { + /* Store port 80 reset event */ + port_80_write(PORT_80_EVENT_RESET); + +#ifdef CONFIG_CHIPSET_RESET_HOOK + /* Notify HOOK_CHIPSET_RESET */ + hook_call_deferred(&lpc_chipset_reset_data, MSEC); +#endif + } + + CPRINTS("LPC RESET# %sasserted", + lpc_get_pltrst_asserted() ? "" : "de"); +#endif +} + + +void emi0_interrupt(void) +{ + uint8_t h2e; + + h2e = MCHP_EMI_H2E_MBX(0); + CPRINTS("LPC Host 0x%02x -> EMI0 H2E(0)", h2e); + trace1(0, LPC, 0, "EMI0 H2E = 0x%02x", h2e); + port_80_write(h2e); +} +DECLARE_IRQ(MCHP_IRQ_EMI0, emi0_interrupt, 1); + +/* + * ISR empties BIOS Debug 0 FIFO and + * writes data to circular buffer. How can we be + * sure this routine can read the last Port 80h byte? + */ +int port_80_read(void) +{ + int data; + + data = PORT_80_IGNORE; + if (MCHP_P80_STS(0) & MCHP_P80_STS_NOT_EMPTY) + data = MCHP_P80_CAP(0) & 0xFF; + + return data; +} + +#ifdef CONFIG_BOARD_ID_CMD_ACPI_EC1 +/* + * Handle custom ACPI EC0 commands. + * Some chipset's CoreBoot will send read board ID command expecting + * a two byte response. + */ +static int acpi_ec0_custom(int is_cmd, uint8_t value, uint8_t *resultptr) +{ + int rval; + + rval = 0; + custom_acpi_ec2os_cnt = 0; + *resultptr = 0x00; + + if (is_cmd && (value == 0x0d)) { + MCHP_INT_SOURCE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_OBE_GIRQ_BIT(0); + /* Write two bytes sequence 0xC2, 0x04 to Host */ + if (MCHP_ACPI_EC_BYTE_CTL(0) & 0x01) { + trace0(0, LPC, 0, + "AEC0 ISR: Cmd 0x0d 4-byte mode. Result=0x04c2"); + /* Host enabled 4-byte mode */ + MCHP_ACPI_EC_EC2OS(0, 0) = 0x02; /* was 0xc2 */ + MCHP_ACPI_EC_EC2OS(0, 1) = 0x04; + MCHP_ACPI_EC_EC2OS(0, 2) = 0x00; + MCHP_ACPI_EC_EC2OS(0, 3) = 0x00; /* OBF is set */ + } else { + trace0(0, LPC, 0, + "AEC0 ISR: Cmd 0x0d 1-byte mode. Result=0xc2,0x04"); + /* single byte mode */ + *resultptr = 0x02; /* was 0xc2 */ + custom_acpi_ec2os_cnt = 1; + custom_apci_ec2os[0] = 0x04; + MCHP_ACPI_EC_EC2OS(0, 0) = 0x02; /* was 0xc2 */ + MCHP_INT_ENABLE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_OBE_GIRQ_BIT(0); + task_enable_irq(MCHP_IRQ_ACPIEC0_OBE); + } + custom_acpi_cmd = 0; + rval = 1; + } + + return rval; +} +#endif + +void acpi_0_interrupt(void) +{ + uint8_t value, result, is_cmd; + + is_cmd = MCHP_ACPI_EC_STATUS(0); + + /* Set the bust bi */ + MCHP_ACPI_EC_STATUS(0) |= EC_LPC_STATUS_PROCESSING; + + result = MCHP_ACPI_EC_BYTE_CTL(0); + + /* Read command/data; this clears the FRMH bit. */ + value = MCHP_ACPI_EC_OS2EC(0, 0); + + /* CPRINTS("ACPI EC0 ISR sts=0x%02x OS2EC=0x%02x ",is_cmd,value); */ + trace3(0, LPC, 0, + "AEC0 ISR: sts=0x%02x O2SEC=0x%02x byte_ctrl=0x%02x", + is_cmd, value, result); + + is_cmd &= EC_LPC_STATUS_LAST_CMD; + + /* Handle whatever this was. */ + result = 0; + if (acpi_ap_to_ec(is_cmd, value, &result)) + MCHP_ACPI_EC_EC2OS(0, 0) = result; +#ifdef CONFIG_BOARD_ID_CMD_ACPI_EC1 + else + acpi_ec0_custom(is_cmd, value, &result); +#endif + /* Clear the busy bit */ + MCHP_ACPI_EC_STATUS(0) &= ~EC_LPC_STATUS_PROCESSING; + + /* Clear R/W1C status bit in Aggregator */ + MCHP_INT_SOURCE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_IBF_GIRQ_BIT(0); + + /* + * ACPI 5.0-12.6.1: Generate SCI for Input Buffer Empty / Output Buffer + * Full condition on the kernel channel. + */ + lpc_generate_sci(); +} +DECLARE_IRQ(MCHP_IRQ_ACPIEC0_IBF, acpi_0_interrupt, 1); + +#ifdef CONFIG_BOARD_ID_CMD_ACPI_EC1 +/* + * ACPI EC0 output buffer empty ISR. + * Used to handle custom ACPI EC0 command requiring + * two byte response. + */ +void acpi_0_obe_isr(void) +{ + uint8_t sts, data; + + MCHP_INT_SOURCE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_OBE_GIRQ_BIT(0); + + sts = MCHP_ACPI_EC_STATUS(0); + data = MCHP_ACPI_EC_BYTE_CTL(0); + trace3(0, LPC, 0, + "AEC0 OBE ISR: sts=0x%02x bytectrl=0x%02x ec2os_cnt=0x%02x", + sts, data, custom_acpi_ec2os_cnt); + + data = sts; + if (custom_acpi_ec2os_cnt) { + custom_acpi_ec2os_cnt--; + data = custom_apci_ec2os[custom_acpi_ec2os_cnt]; + trace1(0, LPC, 0, + "AEC0 OBE ISR: write EC2OS(0,0)=0x%02x", data); + MCHP_ACPI_EC_EC2OS(0, 0) = data; + } + + if (custom_acpi_ec2os_cnt == 0) { /* was last byte? */ + MCHP_INT_DISABLE(MCHP_ACPI_EC_GIRQ) = + MCHP_ACPI_EC_OBE_GIRQ_BIT(0); + } + + lpc_generate_sci(); +} +DECLARE_IRQ(MCHP_IRQ_ACPIEC0_OBE, acpi_0_obe_isr, 1); +#endif + +void acpi_1_interrupt(void) +{ + const struct ec_host_request *r; + + uint8_t st = MCHP_ACPI_EC_STATUS(1); + + CPRINTS("ACPI EC1 ISR: sts=0x%02x", st); + trace1(0, LPC, 0, "ACPI EC1 ISR: sts=0x%02x", st); + + if (!(st & EC_LPC_STATUS_FROM_HOST) || + !(st & EC_LPC_STATUS_LAST_CMD)) + return; + + /* Set the busy bit */ + MCHP_ACPI_EC_STATUS(1) |= EC_LPC_STATUS_PROCESSING; + + /* + * Read the command byte. This clears the FRMH bit in + * the status byte. + */ + host_cmd_args.command = MCHP_ACPI_EC_OS2EC(1, 0); + CPRINTS("ACPI EC1 ISR: OS2EC0=0x%02x", host_cmd_args.command); + trace1(0, LPC, 0, + "ACPI EC1 ISR OS2EC[0]=0x%02x", host_cmd_args.command); + + host_cmd_args.result = EC_RES_SUCCESS; + host_cmd_args.send_response = lpc_send_response; + host_cmd_flags = lpc_host_args->flags; + + /* We only support new style command (v3) now */ + if (host_cmd_args.command == EC_COMMAND_PROTOCOL_3) { + lpc_packet.send_response = lpc_send_response_packet; + + lpc_packet.request = (const void *)lpc_get_hostcmd_data_range(); + lpc_packet.request_temp = params_copy; + lpc_packet.request_max = sizeof(params_copy); + /* Don't know the request size so pass in the entire buffer */ + lpc_packet.request_size = EC_LPC_HOST_PACKET_SIZE; + + lpc_packet.response = (void *)lpc_get_hostcmd_data_range(); + lpc_packet.response_max = EC_LPC_HOST_PACKET_SIZE; + lpc_packet.response_size = 0; + + lpc_packet.driver_result = EC_RES_SUCCESS; + + r = lpc_packet.request; + CPRINTS("ACPI EC1 Packet Command=0x%04x", r->command); + trace1(0, LPC, 0, + "ACPI EC1 ISR: Packet Cmd = 0x%04x", r->command); + + host_packet_receive(&lpc_packet); + + } else { + CPRINTS("ACPI EC1 ISR: Invalid protocol"); + trace0(0, LPC, 0, "ACPI EC1 ISR: Invalid protocol"); + /* Old style command unsupported */ + host_cmd_args.result = EC_RES_INVALID_COMMAND; + + /* Hand off to host command handler */ + host_command_received(&host_cmd_args); + } + + CPRINTS("ACPI EC1 ISR: Exit"); + trace0(0, LPC, 0, "ACPI EC1 ISR: Exit"); +} +DECLARE_IRQ(MCHP_IRQ_ACPIEC1_IBF, acpi_1_interrupt, 1); + +#ifdef HAS_TASK_KEYPROTO +/* + * Reading data out of input buffer clears read-only status + * in 8042EM. Next, we must clear aggregator status. + */ +void kb_ibf_interrupt(void) +{ + if (lpc_keyboard_input_pending()) + keyboard_host_write(MCHP_8042_H2E, + MCHP_8042_STS & (1 << 3)); + + MCHP_INT_SOURCE(MCHP_8042_GIRQ) = MCHP_8042_IBF_GIRQ_BIT; + task_wake(TASK_ID_KEYPROTO); +} +DECLARE_IRQ(MCHP_IRQ_8042EM_IBF, kb_ibf_interrupt, 1); + +/* + * Interrupt generated when Host reads data byte from 8042EM output buffer. + * The 8042EM STATUS.OBF bit will clear when the Host reads the data and + * assert its OBE signal to interrupt aggregator. + * Clear aggregator 8042EM OBE R/WC status bit before invoking task. + */ +void kb_obe_interrupt(void) +{ + MCHP_INT_SOURCE(MCHP_8042_GIRQ) = MCHP_8042_OBE_GIRQ_BIT; + task_wake(TASK_ID_KEYPROTO); +} +DECLARE_IRQ(MCHP_IRQ_8042EM_OBE, kb_obe_interrupt, 1); +#endif + +/* + * Bit 0 of 8042EM STATUS register is OBF meaning EC has written data + * to EC2HOST data register. OBF is cleared when the host reads the data. + */ +int lpc_keyboard_has_char(void) +{ + return (MCHP_8042_STS & (1 << 0)) ? 1 : 0; +} + +int lpc_keyboard_input_pending(void) +{ + return (MCHP_8042_STS & (1 << 1)) ? 1 : 0; +} + +/* + * called from common/keyboard_8042.c + */ +void lpc_keyboard_put_char(uint8_t chr, int send_irq) +{ + MCHP_8042_E2H = chr; + if (send_irq) + keyboard_irq_assert(); +} + +/* + * Read 8042 register and write to read-only register + * insuring compiler does not optimize out the read. + */ +void lpc_keyboard_clear_buffer(void) +{ + MCHP_PCR_CHIP_OSC_ID = MCHP_8042_OBF_CLR; +} + +void lpc_keyboard_resume_irq(void) +{ + if (lpc_keyboard_has_char()) + keyboard_irq_assert(); +} + +void lpc_set_acpi_status_mask(uint8_t mask) +{ + MCHP_ACPI_EC_STATUS(0) |= mask; +} + +void lpc_clear_acpi_status_mask(uint8_t mask) +{ + MCHP_ACPI_EC_STATUS(0) &= ~mask; +} + +int lpc_get_pltrst_asserted(void) +{ +#ifdef CONFIG_ESPI + /* + * eSPI PLTRST# a VWire or side-band signal + * Controlled by CONFIG_ESPI_PLTRST_IS_VWIRE + */ + return espi_vw_get_wire(VW_PLTRST_L); +#else + /* returns 1 if LRESET# pin is asserted(low) else 0 */ + return (MCHP_LPC_BUS_MONITOR & (1<<1)) ? 1 : 0; +#endif +} + +/* Enable LPC ACPI-EC0 interrupts */ +void lpc_enable_acpi_interrupts(void) +{ + task_enable_irq(MCHP_IRQ_ACPIEC0_IBF); +} + +/* Disable LPC ACPI-EC0 interrupts */ +void lpc_disable_acpi_interrupts(void) +{ + task_disable_irq(MCHP_IRQ_ACPIEC0_IBF); +} + +/* On boards without a host, this command is used to set up LPC */ +static int lpc_command_init(int argc, char **argv) +{ + lpc_init(); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(lpcinit, lpc_command_init, NULL, NULL); + +/* Get protocol information */ +static int lpc_get_protocol_info(struct host_cmd_handler_args *args) +{ + struct ec_response_get_protocol_info *r = args->response; + + CPRINTS("MEC1701 Handler EC_CMD_GET_PROTOCOL_INFO"); + trace0(0, LPC, 0, "Handler EC_CMD_GET_PROTOCOL_INFO"); + + memset(r, 0, sizeof(*r)); + r->protocol_versions = (1 << 3); + r->max_request_packet_size = EC_LPC_HOST_PACKET_SIZE; + r->max_response_packet_size = EC_LPC_HOST_PACKET_SIZE; + r->flags = 0; + + args->response_size = sizeof(*r); + + return EC_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, + lpc_get_protocol_info, + EC_VER_MASK(0)); diff --git a/chip/mchp/lpc_chip.h b/chip/mchp/lpc_chip.h new file mode 100644 index 0000000000..1961919931 --- /dev/null +++ b/chip/mchp/lpc_chip.h @@ -0,0 +1,39 @@ +/* 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. + */ + +/* Microchip MEC1701 specific module for Chrome EC */ + +#ifndef __CROS_EC_LPC_CHIP_H +#define __CROS_EC_LPC_CHIP_H + +#ifdef CONFIG_ESPI + +#include "espi.h" + +/* eSPI Initialization functions */ +void espi_init(void); + +/* eSPI ESPI_RESET# interrupt handler */ +void espi_reset_handler(void); + +/* + * + */ +int espi_vw_pulse_wire(enum espi_vw_signal signal, int pulse_level); + +void lpc_update_host_event_status(void); + +#endif + +/* LPC LRESET interrupt handler */ +void lpcrst_interrupt(enum gpio_signal signal); + +void lpc_set_init_done(int val); + +uint32_t lpc_mem_mapped_addr(void); + +void lpc_mem_mapped_init(void); + +#endif /* __CROS_EC_LPC_CHIP_H */ |