summaryrefslogtreecommitdiff
path: root/chip/mchp
diff options
context:
space:
mode:
authorScott Worley <scott.worley@microchip.corp-partner.google.com>2017-12-21 14:42:22 -0500
committerchrome-bot <chrome-bot@chromium.org>2018-01-02 15:48:21 -0800
commita92647b9ce517fc28f924c2abc60dec3d1bdc98e (patch)
treec6c399323b6906d2508c3b3d873170f9b402e82d /chip/mchp
parentf147f61f38a27e57308033f81fedfd8834b58a62 (diff)
downloadchrome-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.c1619
-rw-r--r--chip/mchp/lpc.c886
-rw-r--r--chip/mchp/lpc_chip.h39
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 */