diff --git a/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906.dts b/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906.dts index fb7c7231a30..c81159ad450 100644 --- a/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906.dts +++ b/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906.dts @@ -54,6 +54,15 @@ status = "okay"; }; +/* Enable aggregated GIRQ24 and GIRQ25 for eSPI virtual wires interrupts */ +&girq24 { + status = "okay"; +}; + +&girq25 { + status = "okay"; +}; + &rtimer { status = "okay"; }; @@ -71,6 +80,27 @@ status = "okay"; }; +&espi0 { + status = "okay"; +}; + +/* enable various eSPI child devices (host facing) */ +&kbc0 { + status = "okay"; +}; + +&acpi_ec0 { + status = "okay"; +}; + +&acpi_ec1 { + status = "okay"; +}; + +&p80bd0 { + status = "okay"; +}; + &i2c_smb_0 { status = "okay"; label = "I2C0"; diff --git a/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906_defconfig b/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906_defconfig index 8c03840f5d9..80eb72b962f 100644 --- a/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906_defconfig +++ b/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906_defconfig @@ -17,3 +17,4 @@ CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y CONFIG_ADC=y CONFIG_I2C=y +CONFIG_ESPI=y diff --git a/boards/arm/mec172xevb_assy6906/pinmux.c b/boards/arm/mec172xevb_assy6906/pinmux.c index 49aa3e37420..871fff36497 100644 --- a/boards/arm/mec172xevb_assy6906/pinmux.c +++ b/boards/arm/mec172xevb_assy6906/pinmux.c @@ -7,10 +7,27 @@ #include #include #include +#include #include #include +enum gpio_ports { + port_000_036 = 0, + port_040_076, + port_100_136, + port_140_176, + port_200_236, + port_240_276, + port_max, +}; + +struct pin_info { + enum gpio_ports port_num; + uint8_t pin; + uint32_t flags; +}; + struct pinmux_ports_t { #if DT_NODE_HAS_STATUS(DT_NODELABEL(pinmux_000_036), okay) const struct device *porta; @@ -32,6 +49,31 @@ struct pinmux_ports_t { #endif }; +const struct pin_info uart_pin_table[] = { +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart0), okay) + { port_100_136, MCHP_GPIO_104, MCHP_GPIO_CTRL_MUX_F1 }, + { port_100_136, MCHP_GPIO_105, MCHP_GPIO_CTRL_MUX_F1 }, +#endif +#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) + { port_140_176, MCHP_GPIO_170, MCHP_GPIO_CTRL_MUX_F1 }, + { port_140_176, MCHP_GPIO_171, MCHP_GPIO_CTRL_MUX_F1 }, +#endif +}; + +/* eSPI: Reset#, Alert#, CS#, CLK, IO0 - IO4 */ +const struct pin_info espi_pin_table[] = { +#if defined(CONFIG_ESPI_XEC_V2) && DT_NODE_HAS_STATUS(DT_NODELABEL(espi0), okay) + { port_040_076, MCHP_GPIO_061, MCHP_GPIO_CTRL_MUX_F1 }, + { port_040_076, MCHP_GPIO_063, MCHP_GPIO_CTRL_MUX_F1 }, + { port_040_076, MCHP_GPIO_066, MCHP_GPIO_CTRL_MUX_F1 }, + { port_040_076, MCHP_GPIO_065, MCHP_GPIO_CTRL_MUX_F1 }, + { port_040_076, MCHP_GPIO_070, MCHP_GPIO_CTRL_MUX_F1 }, + { port_040_076, MCHP_GPIO_071, MCHP_GPIO_CTRL_MUX_F1 }, + { port_040_076, MCHP_GPIO_072, MCHP_GPIO_CTRL_MUX_F1 }, + { port_040_076, MCHP_GPIO_073, MCHP_GPIO_CTRL_MUX_F1 }, +#endif +}; + static void brd_init_pinmux_ports(struct pinmux_ports_t *pp) { ARG_UNUSED(pp); @@ -68,16 +110,52 @@ static void brd_init_pinmux_ports(struct pinmux_ports_t *pp) #endif } -static void brd_cfg_uart(struct pinmux_ports_t *pp) +const struct device *get_port_device(struct pinmux_ports_t *pp, + uint8_t port_num) { -#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart0), okay) - pinmux_pin_set(pp->portc, MCHP_GPIO_104, MCHP_GPIO_CTRL_MUX_F1); - pinmux_pin_set(pp->portc, MCHP_GPIO_105, MCHP_GPIO_CTRL_MUX_F1); + switch (port_num) { +#if DT_NODE_HAS_STATUS(DT_NODELABEL(pinmux_000_036), okay) + case port_000_036: + return pp->porta; #endif -#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) - pinmux_pin_set(pp->portd, MCHP_GPIO_170, MCHP_GPIO_CTRL_MUX_F1); - pinmux_pin_set(pp->portd, MCHP_GPIO_171, MCHP_GPIO_CTRL_MUX_F1); +#if DT_NODE_HAS_STATUS(DT_NODELABEL(pinmux_040_076), okay) + case port_040_076: + return pp->portb; #endif +#if DT_NODE_HAS_STATUS(DT_NODELABEL(pinmux_100_136), okay) + case port_100_136: + return pp->portc; +#endif +#if DT_NODE_HAS_STATUS(DT_NODELABEL(pinmux_140_176), okay) + case port_140_176: + return pp->portd; +#endif +#if DT_NODE_HAS_STATUS(DT_NODELABEL(pinmux_200_236), okay) + case port_200_236: + return pp->porte; +#endif +#if DT_NODE_HAS_STATUS(DT_NODELABEL(pinmux_240_276), okay) + case port_240_276: + return pp->portf; +#endif + default: + return NULL; + } +} + +static void brd_pin_table_init(struct pinmux_ports_t *pp, + const struct pin_info *table, size_t nentries) +{ + for (size_t n = 0; n < nentries; n++) { + const struct device *dev = + get_port_device(pp, table[n].port_num); + + if (!dev) { + continue; + } + + pinmux_pin_set(dev, table[n].pin, table[n].flags); + } } /* caller passes dev = NULL */ @@ -87,7 +165,8 @@ static int board_pinmux_init(const struct device *dev) struct pinmux_ports_t pp; brd_init_pinmux_ports(&pp); - brd_cfg_uart(&pp); + brd_pin_table_init(&pp, uart_pin_table, ARRAY_SIZE(uart_pin_table)); + brd_pin_table_init(&pp, espi_pin_table, ARRAY_SIZE(espi_pin_table)); return 0; } diff --git a/drivers/espi/CMakeLists.txt b/drivers/espi/CMakeLists.txt index d26e50cbd13..5ba46d2615f 100644 --- a/drivers/espi/CMakeLists.txt +++ b/drivers/espi/CMakeLists.txt @@ -8,3 +8,5 @@ zephyr_library_sources_ifdef(CONFIG_ESPI_NPCX host_subs_npcx.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE espi_handlers.c) zephyr_library_sources_ifdef(CONFIG_ESPI_EMUL espi_emul.c) zephyr_library_sources_ifdef(CONFIG_ESPI_SAF espi_saf_mchp_xec.c) +zephyr_library_sources_ifdef(CONFIG_ESPI_XEC_V2 espi_mchp_xec_v2.c) +zephyr_library_sources_ifdef(CONFIG_ESPI_XEC_V2 espi_mchp_xec_host_v2.c) diff --git a/drivers/espi/Kconfig b/drivers/espi/Kconfig index e74dc39e277..6a8c2ecac9f 100644 --- a/drivers/espi/Kconfig +++ b/drivers/espi/Kconfig @@ -12,6 +12,8 @@ if ESPI source "drivers/espi/Kconfig.xec" +source "drivers/espi/Kconfig.xec_v2" + source "drivers/espi/Kconfig.npcx" source "drivers/espi/Kconfig.espi_emul" diff --git a/drivers/espi/Kconfig.xec b/drivers/espi/Kconfig.xec index 6614a54a82d..6ff528068bf 100644 --- a/drivers/espi/Kconfig.xec +++ b/drivers/espi/Kconfig.xec @@ -5,9 +5,9 @@ config ESPI_XEC bool "XEC Microchip ESPI driver" - depends on SOC_FAMILY_MEC + depends on SOC_SERIES_MEC1501X help - Enable the Microchip XEC ESPI driver. + Enable the Microchip XEC ESPI driver for MEC15xx family. if ESPI_XEC diff --git a/drivers/espi/Kconfig.xec_v2 b/drivers/espi/Kconfig.xec_v2 new file mode 100644 index 00000000000..25a3c88a2c3 --- /dev/null +++ b/drivers/espi/Kconfig.xec_v2 @@ -0,0 +1,132 @@ +# Microchip XEC ESPI configuration options + +# Copyright (c) 2019 Intel Corporation +# Copyright (c) 2021 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +config ESPI_XEC_V2 + bool "XEC Microchip ESPI V2 driver" + depends on SOC_SERIES_MEC172X + help + Enable the Microchip XEC ESPI driver for MEC172x series. + +if ESPI_XEC_V2 + +config ESPI_OOB_CHANNEL + default y + +config ESPI_FLASH_CHANNEL + default y + +config ESPI_PERIPHERAL_HOST_IO + default y + +config ESPI_PERIPHERAL_HOST_IO_PVT + default y + +config ESPI_PERIPHERAL_DEBUG_PORT_80 + default y + +config ESPI_PERIPHERAL_8042_KBC + default y + +config ESPI_PERIPHERAL_UART + default y + +config ESPI_PERIPHERAL_UART_SOC_MAPPING + int "SoC port exposed as logical eSPI UART" + default 1 + depends on ESPI_PERIPHERAL_UART + help + This tells the driver to which SoC UART to direct the UART traffic + send over eSPI from host. MEC172x implements two UARTs. + +config ESPI_PERIPHERAL_XEC_MAILBOX + bool "SoC Mailbox over eSPI" + depends on ESPI_PERIPHERAL_CHANNEL + help + Enable a 32 byte mailbox interface accessible via Host I/O over the + ESPI Peripheral Channel. + +config ESPI_PERIPHERAL_XEC_ACPI_EC2 + bool "SoC ACPI EC 2 over eSPI" + depends on ESPI_PERIPHERAL_CHANNEL + help + Enable ACPI EC2 interface accessible via Host I/O over the + ESPI Peripheral Channel. + +config ESPI_PERIPHERAL_XEC_ACPI_EC3 + bool "SoC ACPI EC 3 over eSPI" + depends on ESPI_PERIPHERAL_CHANNEL + help + Enable ACPI EC3 interface accessible via Host I/O over the + ESPI Peripheral Channel. + +config ESPI_PERIPHERAL_XEC_ACPI_EC4 + bool "SoC ACPI EC 4 over eSPI" + depends on ESPI_PERIPHERAL_CHANNEL + help + Enable ACPI EC4 interface accessible via Host I/O over the + ESPI Peripheral Channel. + +config ESPI_PERIPHERAL_XEC_ACPI_PM1 + bool "SoC ACPI PM1 over eSPI" + depends on ESPI_PERIPHERAL_CHANNEL + help + Enable ACPI PM1 interface accessible via Host I/O over the + ESPI Peripheral Channel. + +config ESPI_PERIPHERAL_XEC_EMI0 + bool "SoC EMI 0 over eSPI" + depends on ESPI_PERIPHERAL_CHANNEL + help + Enable EMI 0 interface accessible via Host I/O over the + ESPI Peripheral Channel. + +config ESPI_PERIPHERAL_XEC_EMI1 + bool "SoC EMI 1 over eSPI" + depends on ESPI_PERIPHERAL_CHANNEL + help + Enable EMI 1 interface accessible via Host I/O over the + ESPI Peripheral Channel. + +config ESPI_PERIPHERAL_XEC_EMI2 + bool "SoC EMI 2 over eSPI" + depends on ESPI_PERIPHERAL_CHANNEL + help + Enable EMI 2 interface accessible via Host I/O over the + ESPI Peripheral Channel. + +config ESPI_OOB_BUFFER_SIZE + int "eSPI OOB channel buffer size in bytes" + default 128 + depends on ESPI_OOB_CHANNEL + help + Use minimum RAM buffer size by default but allow applications to + override the value. + Maximum OOB payload is 73 bytes. + +config ESPI_FLASH_BUFFER_SIZE + int "eSPI Flash channel buffer size in bytes" + default 256 + depends on ESPI_FLASH_CHANNEL + help + Use maximum RAM buffer size defined by spec but allow applications + to override if eSPI host doesn't support it. + +config ESPI_SAF + bool "XEC Microchip ESPI SAF driver" + depends on ESPI_FLASH_CHANNEL + default n + help + Enable Slave Attached Flash eSPI driver. SAF depends upon ESPI XEC driver + and flash channel. + +config ESPI_SAF_INIT_PRIORITY + int "ESPI SAF driver initialization priority" + depends on ESPI_SAF + default 4 + help + Driver initialization priority for eSPI SAF driver. + +endif #ESPI_XEC_V2 diff --git a/drivers/espi/espi_mchp_xec_host_v2.c b/drivers/espi/espi_mchp_xec_host_v2.c new file mode 100644 index 00000000000..a00e7f86d33 --- /dev/null +++ b/drivers/espi/espi_mchp_xec_host_v2.c @@ -0,0 +1,819 @@ +/* + * Copyright (c) 2019 Intel Corporation + * Copyright (c) 2021 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT microchip_xec_espi_host_dev + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "espi_utils.h" +#include "espi_mchp_xec_v2.h" + +#define CONNECT_IRQ_MBOX0 NULL +#define CONNECT_IRQ_KBC0 NULL +#define CONNECT_IRQ_ACPI_EC0 NULL +#define CONNECT_IRQ_ACPI_EC1 NULL +#define CONNECT_IRQ_ACPI_EC2 NULL +#define CONNECT_IRQ_ACPI_EC3 NULL +#define CONNECT_IRQ_ACPI_EC4 NULL +#define CONNECT_IRQ_ACPI_PM1 NULL +#define CONNECT_IRQ_EMI0 NULL +#define CONNECT_IRQ_EMI1 NULL +#define CONNECT_IRQ_EMI2 NULL +#define CONNECT_IRQ_RTC0 NULL +#define CONNECT_IRQ_P80BD0 NULL + +#define INIT_MBOX0 NULL +#define INIT_KBC0 NULL +#define INIT_ACPI_EC0 NULL +#define INIT_ACPI_EC1 NULL +#define INIT_ACPI_EC2 NULL +#define INIT_ACPI_EC3 NULL +#define INIT_ACPI_EC4 NULL +#define INIT_ACPI_PM1 NULL +#define INIT_EMI0 NULL +#define INIT_EMI1 NULL +#define INIT_EMI2 NULL +#define INIT_RTC0 NULL +#define INIT_P80BD0 NULL +#define INIT_UART0 NULL +#define INIT_UART1 NULL + +/* BARs as defined in LPC spec chapter 11 */ +#define ESPI_XEC_KBC_BAR_ADDRESS 0x00600000 +#define ESPI_XEC_UART0_BAR_ADDRESS 0x03F80000 +#define ESPI_XEC_MBOX_BAR_ADDRESS 0x03600000 +#define ESPI_XEC_PORT80_BAR_ADDRESS 0x00800000 +#define ESPI_XEC_PORT81_BAR_ADDRESS 0x00810000 +#define ESPI_XEC_ACPI_EC0_BAR_ADDRESS 0x00620000 + +/* Espi peripheral has 3 uart ports */ +#define ESPI_PERIPHERAL_UART_PORT0 0 +#define ESPI_PERIPHERAL_UART_PORT1 1 + +#define UART_DEFAULT_IRQ_POS 2u +#define UART_DEFAULT_IRQ BIT(UART_DEFAULT_IRQ_POS) + +/* PCR */ +#define XEC_PCR_REG_BASE \ + ((struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr)))) + +struct xec_espi_host_sram_config { + uint32_t host_sram1_base; + uint32_t host_sram2_base; + uint16_t ec_sram1_ofs; + uint16_t ec_sram2_ofs; + uint8_t sram1_acc_size; + uint8_t sram2_acc_size; +}; + +struct xec_espi_host_dev_config { + const struct device *parent; + uint32_t reg_base; /* logical device registers */ + uint32_t host_mem_base; /* 32-bit host memory address */ + uint16_t host_io_base; /* 16-bit host I/O address */ + uint8_t ldn; /* Logical device number */ + uint8_t num_ecia; + uint32_t *girqs; +}; + +struct xec_acpi_ec_config { + uintptr_t regbase; + uint32_t ibf_ecia_info; + uint32_t obe_ecia_info; +}; + + +#ifdef CONFIG_ESPI_PERIPHERAL_XEC_MAILBOX + +BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(mbox0), okay), + "XEC mbox0 DT node is disabled!"); + +static struct xec_mbox_config { + uintptr_t regbase; + uint32_t ecia_info; +}; + +static const struct xec_mbox0_config xec_mbox0_cfg = { + .regbase = DT_REG_ADDR(DT_NODELABEL(mbox0)), + .ecia_info = DT_PROP_BY_IDX(DT_NODELABEL(mbox0), girqs, 0), +}; + +/* dev is a pointer to espi0 (parent) device */ +static void mbox0_isr(const struct device *dev) +{ + uint8_t girq = MCHP_XEC_ECIA_GIRQ(xec_mbox0_cfg.ecia_info); + uint8_t bitpos = MCHP_XEC_ECIA_GIRQ_POS(xec_mbox0_cfg.ecia_info); + + /* clear GIRQ source, inline version */ + mchp_soc_ecia_girq_src_clr(girq, bitpos); +} + +static int connect_irq_mbox0(const struct device *dev) +{ + /* clear GIRQ source */ + mchp_xec_ecia_info_girq_src_clr(xec_mbox0_cfg.ecia_info); + + IRQ_CONNECT(DT_IRQN(DT_NODELABLE(mbox0)), + DT_IRQ(DT_NODELABLE(mbox0), priority), + acpi_ec0_isr, + DEVICE_DT_GET(DT_NODELABEL(espi0)), + 0); + irq_enable(DT_IRQN(DT_NODELABLE(mbox0))); + + /* enable GIRQ source */ + mchp_xec_ecia_info_girq_src_en(xec_mbox0_cfg.ecia_info); + + return 0; +} + +/* Called by eSPI Bus init, eSPI reset de-assertion, and eSPI Platform Reset + * de-assertion. + */ +static int init_mbox0(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + struct espi_iom_regs *regs = (struct espi_iom_regs *)cfg->base_addr; + + regs->IOHBAR[IOB_MBOX] = ESPI_XEC_MBOX_BAR_ADDRESS | + MCHP_ESPI_IO_BAR_HOST_VALID; + return 0; +} + +#undef CONNECT_IRQ_MBOX0 +#define CONNECT_IRQ_MBOX0 connect_irq_mbox0 +#undef INIT_MBOX0 +#define INIT_MBOX0 init_mbox0 + +#endif /* CONFIG_ESPI_PERIPHERAL_XEC_MAILBOX */ + +#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC + +BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_NODELABEL(kbc0), okay), + "XEC kbc0 DT node is disabled!"); + +struct xec_kbc0_config { + uintptr_t regbase; + uint32_t ibf_ecia_info; + uint32_t obe_ecia_info; +}; + +static const struct xec_kbc0_config xec_kbc0_cfg = { + .regbase = DT_REG_ADDR(DT_NODELABEL(kbc0)), + .ibf_ecia_info = DT_PROP_BY_IDX(DT_NODELABEL(kbc0), girqs, 1), + .obe_ecia_info = DT_PROP_BY_IDX(DT_NODELABEL(kbc0), girqs, 0), +}; + +static void kbc0_ibf_isr(const struct device *dev) +{ + struct kbc_regs *kbc_hw = (struct kbc_regs *)xec_kbc0_cfg.regbase; + struct espi_xec_data *const data = + (struct espi_xec_data *const)dev->data; + + + /* The high byte contains information from the host, + * and the lower byte speficies if the host sent + * a command or data. 1 = Command. + */ + uint32_t isr_data = ((kbc_hw->EC_DATA & 0xFF) << E8042_ISR_DATA_POS) | + ((kbc_hw->EC_KBC_STS & MCHP_KBC_STS_CD) << + E8042_ISR_CMD_DATA_POS); + + struct espi_event evt = { + .evt_type = ESPI_BUS_PERIPHERAL_NOTIFICATION, + .evt_details = ESPI_PERIPHERAL_8042_KBC, + .evt_data = isr_data + }; + + espi_send_callbacks(&data->callbacks, dev, evt); + + mchp_xec_ecia_info_girq_src_clr(xec_kbc0_cfg.ibf_ecia_info); +} + +static void kbc0_obe_isr(const struct device *dev) +{ + /* disable and clear GIRQ interrupt and status */ + mchp_xec_ecia_info_girq_src_dis(xec_kbc0_cfg.obe_ecia_info); + mchp_xec_ecia_info_girq_src_clr(xec_kbc0_cfg.obe_ecia_info); +} + +/* dev is a pointer to espi0 device */ +static int kbc0_rd_req(const struct device *dev, enum lpc_peripheral_opcode op, + uint32_t *data) +{ + struct kbc_regs *kbc_hw = (struct kbc_regs *)xec_kbc0_cfg.regbase; + + ARG_UNUSED(dev); + + if (op >= E8042_START_OPCODE && op <= E8042_MAX_OPCODE) { + /* Make sure kbc 8042 is on */ + if (!(kbc_hw->KBC_CTRL & MCHP_KBC_CTRL_OBFEN)) { + return -ENOTSUP; + } + + switch (op) { + case E8042_OBF_HAS_CHAR: + /* EC has written data back to host. OBF is + * automatically cleared after host reads + * the data + */ + *data = kbc_hw->EC_KBC_STS & MCHP_KBC_STS_OBF ? 1 : 0; + break; + case E8042_IBF_HAS_CHAR: + *data = kbc_hw->EC_KBC_STS & MCHP_KBC_STS_IBF ? 1 : 0; + break; + case E8042_READ_KB_STS: + *data = kbc_hw->EC_KBC_STS; + break; + default: + return -EINVAL; + } + } else { + return -ENOTSUP; + } + + return 0; +} + +/* dev is a pointer to espi0 device */ +static int kbc0_wr_req(const struct device *dev, enum lpc_peripheral_opcode op, + uint32_t *data) +{ + struct kbc_regs *kbc_hw = (struct kbc_regs *)xec_kbc0_cfg.regbase; + + volatile uint32_t __attribute__((unused)) dummy; + + ARG_UNUSED(dev); + + if (op >= E8042_START_OPCODE && op <= E8042_MAX_OPCODE) { + /* Make sure kbc 8042 is on */ + if (!(kbc_hw->KBC_CTRL & MCHP_KBC_CTRL_OBFEN)) { + return -ENOTSUP; + } + + switch (op) { + case E8042_WRITE_KB_CHAR: + kbc_hw->EC_DATA = *data & 0xff; + break; + case E8042_WRITE_MB_CHAR: + kbc_hw->EC_AUX_DATA = *data & 0xff; + break; + case E8042_RESUME_IRQ: + mchp_xec_ecia_info_girq_src_clr( + xec_kbc0_cfg.ibf_ecia_info); + mchp_xec_ecia_info_girq_src_en( + xec_kbc0_cfg.ibf_ecia_info); + break; + case E8042_PAUSE_IRQ: + mchp_xec_ecia_info_girq_src_dis( + xec_kbc0_cfg.ibf_ecia_info); + break; + case E8042_CLEAR_OBF: + dummy = kbc_hw->HOST_AUX_DATA; + break; + case E8042_SET_FLAG: + /* FW shouldn't modify these flags directly */ + *data &= ~(MCHP_KBC_STS_OBF | MCHP_KBC_STS_IBF | + MCHP_KBC_STS_AUXOBF); + kbc_hw->EC_KBC_STS |= *data; + break; + case E8042_CLEAR_FLAG: + /* FW shouldn't modify these flags directly */ + *data |= (MCHP_KBC_STS_OBF | MCHP_KBC_STS_IBF | + MCHP_KBC_STS_AUXOBF); + kbc_hw->EC_KBC_STS &= ~(*data); + break; + default: + return -EINVAL; + } + } else { + return -ENOTSUP; + } + + return 0; +} + +static int connect_irq_kbc0(const struct device *dev) +{ + /* clear GIRQ source */ + mchp_xec_ecia_info_girq_src_clr(xec_kbc0_cfg.ibf_ecia_info); + mchp_xec_ecia_info_girq_src_clr(xec_kbc0_cfg.obe_ecia_info); + + IRQ_CONNECT(DT_IRQ_BY_NAME(DT_NODELABEL(kbc0), kbc_ibf, irq), + DT_IRQ_BY_NAME(DT_NODELABEL(kbc0), kbc_ibf, priority), + kbc0_ibf_isr, + DEVICE_DT_GET(DT_NODELABEL(espi0)), + 0); + irq_enable(DT_IRQ_BY_NAME(DT_NODELABEL(kbc0), kbc_ibf, irq)); + + IRQ_CONNECT(DT_IRQ_BY_NAME(DT_NODELABEL(kbc0), kbc_obe, irq), + DT_IRQ_BY_NAME(DT_NODELABEL(kbc0), kbc_obe, priority), + kbc0_obe_isr, + DEVICE_DT_GET(DT_NODELABEL(espi0)), + 0); + irq_enable(DT_IRQ_BY_NAME(DT_NODELABEL(kbc0), kbc_obe, irq)); + + /* enable GIRQ sources */ + mchp_xec_ecia_info_girq_src_en(xec_kbc0_cfg.ibf_ecia_info); + mchp_xec_ecia_info_girq_src_en(xec_kbc0_cfg.obe_ecia_info); + + return 0; +} + +static int init_kbc0(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + struct espi_iom_regs *regs = (struct espi_iom_regs *)cfg->base_addr; + struct kbc_regs *kbc_hw = (struct kbc_regs *)xec_kbc0_cfg.regbase; + + kbc_hw->KBC_CTRL |= MCHP_KBC_CTRL_AUXH; + kbc_hw->KBC_CTRL |= MCHP_KBC_CTRL_OBFEN; + /* This is the activate register, but the HAL has a funny name */ + kbc_hw->KBC_PORT92_EN = MCHP_KBC_PORT92_EN; + regs->IOHBAR[IOB_KBC] = ESPI_XEC_KBC_BAR_ADDRESS | + MCHP_ESPI_IO_BAR_HOST_VALID; + + return 0; +} + +#undef CONNECT_IRQ_KBC0 +#define CONNECT_IRQ_KBC0 connect_irq_kbc0 +#undef INIT_KBC0 +#define INIT_KBC0 init_kbc0 + +#endif /* CONFIG_ESPI_PERIPHERAL_8042_KBC */ + +#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO + +static const struct xec_acpi_ec_config xec_acpi_ec0_cfg = { + .regbase = DT_REG_ADDR(DT_NODELABEL(acpi_ec0)), + .ibf_ecia_info = DT_PROP_BY_IDX(DT_NODELABEL(acpi_ec0), girqs, 0), + .obe_ecia_info = DT_PROP_BY_IDX(DT_NODELABEL(acpi_ec0), girqs, 1), +}; + +static void acpi_ec0_ibf_isr(const struct device *dev) +{ + struct espi_xec_data *const data = + (struct espi_xec_data *const)dev->data; + struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION, + ESPI_PERIPHERAL_HOST_IO, ESPI_PERIPHERAL_NODATA + }; + + espi_send_callbacks(&data->callbacks, dev, evt); + + /* clear GIRQ status */ + mchp_xec_ecia_info_girq_src_clr(xec_acpi_ec0_cfg.ibf_ecia_info); +} + +static void acpi_ec0_obe_isr(const struct device *dev) +{ + /* disable and clear GIRQ status */ + mchp_xec_ecia_info_girq_src_dis(xec_acpi_ec0_cfg.obe_ecia_info); + mchp_xec_ecia_info_girq_src_clr(xec_acpi_ec0_cfg.obe_ecia_info); +} + +static int eacpi_rd_req(const struct device *dev, + enum lpc_peripheral_opcode op, + uint32_t *data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(op); + ARG_UNUSED(data); + + return -EINVAL; +} + +static int eacpi_wr_req(const struct device *dev, + enum lpc_peripheral_opcode op, + uint32_t *data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(op); + ARG_UNUSED(data); + + return -EINVAL; +} + +static int connect_irq_acpi_ec0(const struct device *dev) +{ + mchp_xec_ecia_info_girq_src_clr(xec_acpi_ec0_cfg.ibf_ecia_info); + mchp_xec_ecia_info_girq_src_clr(xec_acpi_ec0_cfg.obe_ecia_info); + + IRQ_CONNECT(DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec0), acpi_ibf, irq), + DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec0), acpi_ibf, priority), + acpi_ec0_ibf_isr, + DEVICE_DT_GET(DT_NODELABEL(espi0)), + 0); + irq_enable(DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec0), acpi_ibf, irq)); + + IRQ_CONNECT(DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec0), acpi_obe, irq), + DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec0), acpi_obe, priority), + acpi_ec0_obe_isr, + DEVICE_DT_GET(DT_NODELABEL(espi0)), + 0); + irq_enable(DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec0), acpi_obe, irq)); + + mchp_xec_ecia_info_girq_src_en(xec_acpi_ec0_cfg.ibf_ecia_info); + mchp_xec_ecia_info_girq_src_en(xec_acpi_ec0_cfg.obe_ecia_info); + + return 0; +} + +static int init_acpi_ec0(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + struct espi_iom_regs *regs = (struct espi_iom_regs *)cfg->base_addr; + + regs->IOHBAR[IOB_ACPI_EC0] = ESPI_XEC_ACPI_EC0_BAR_ADDRESS | + MCHP_ESPI_IO_BAR_HOST_VALID; + + return 0; +} + +#undef CONNECT_IRQ_ACPI_EC0 +#define CONNECT_IRQ_ACPI_EC0 connect_irq_acpi_ec0 +#undef INIT_ACPI_EC0 +#define INIT_ACPI_EC0 init_acpi_ec0 + +#endif /* CONFIG_ESPI_PERIPHERAL_HOST_IO */ + +#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO_PVT + +static const struct xec_acpi_ec_config xec_acpi_ec1_cfg = { + .regbase = DT_REG_ADDR(DT_NODELABEL(acpi_ec1)), + .ibf_ecia_info = DT_PROP_BY_IDX(DT_NODELABEL(acpi_ec1), girqs, 0), + .obe_ecia_info = DT_PROP_BY_IDX(DT_NODELABEL(acpi_ec1), girqs, 1), +}; + +static void acpi_ec1_ibf_isr(const struct device *dev) +{ + struct espi_xec_data *const data = + (struct espi_xec_data *const)dev->data; + struct espi_event evt = { + .evt_type = ESPI_BUS_PERIPHERAL_NOTIFICATION, + .evt_details = ESPI_PERIPHERAL_HOST_IO_PVT, + .evt_data = ESPI_PERIPHERAL_NODATA + }; + + espi_send_callbacks(&data->callbacks, dev, evt); + + /* clear GIRQ status */ + mchp_xec_ecia_info_girq_src_clr(xec_acpi_ec1_cfg.ibf_ecia_info); +} + +static void acpi_ec1_obe_isr(const struct device *dev) +{ + /* disable and clear GIRQ status */ + mchp_xec_ecia_info_girq_src_dis(xec_acpi_ec1_cfg.obe_ecia_info); + mchp_xec_ecia_info_girq_src_clr(xec_acpi_ec1_cfg.obe_ecia_info); +} + +static int connect_irq_acpi_ec1(const struct device *dev) +{ + mchp_xec_ecia_info_girq_src_clr(xec_acpi_ec1_cfg.ibf_ecia_info); + mchp_xec_ecia_info_girq_src_clr(xec_acpi_ec1_cfg.obe_ecia_info); + + IRQ_CONNECT(DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec1), acpi_ibf, irq), + DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec1), acpi_ibf, priority), + acpi_ec1_ibf_isr, + DEVICE_DT_GET(DT_NODELABEL(espi0)), + 0); + irq_enable(DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec1), acpi_ibf, irq)); + + IRQ_CONNECT(DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec1), acpi_obe, irq), + DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec1), acpi_obe, priority), + acpi_ec1_obe_isr, + DEVICE_DT_GET(DT_NODELABEL(espi0)), + 0); + irq_enable(DT_IRQ_BY_NAME(DT_NODELABEL(acpi_ec1), acpi_obe, irq)); + + mchp_xec_ecia_info_girq_src_en(xec_acpi_ec1_cfg.ibf_ecia_info); + mchp_xec_ecia_info_girq_src_en(xec_acpi_ec1_cfg.obe_ecia_info); + + return 0; +} + +static int init_acpi_ec1(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + struct espi_iom_regs *regs = (struct espi_iom_regs *)cfg->base_addr; + + regs->IOHBAR[IOB_ACPI_EC1] = + CONFIG_ESPI_PERIPHERAL_HOST_IO_PVT_PORT_NUM | + MCHP_ESPI_IO_BAR_HOST_VALID; + regs->IOHBAR[IOB_MBOX] = ESPI_XEC_MBOX_BAR_ADDRESS | + MCHP_ESPI_IO_BAR_HOST_VALID; + + return 0; +} + +#undef CONNECT_IRQ_ACPI_EC1 +#define CONNECT_IRQ_ACPI_EC1 connect_irq_acpi_ec1 +#undef INIT_ACPI_EC1 +#define INIT_ACPI_EC1 init_acpi_ec1 + +#endif /* CONFIG_ESPI_PERIPHERAL_HOST_IO_PVT */ + +#ifdef CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80 + +struct xec_p80bd_config { + uintptr_t regbase; + uint32_t ecia_info; +}; + +static const struct xec_p80bd_config xec_p80bd0_cfg = { + .regbase = DT_REG_ADDR(DT_NODELABEL(p80bd0)), + .ecia_info = DT_PROP_BY_IDX(DT_NODELABEL(p80bd0), girqs, 0), +}; + +/* + * MEC172x P80 BIOS Debug Port hardware captures writes to its 4-byte I/O range + * Hardware provides status indicating byte lane(s) of each write. + * We must decode the byte lane information and produce one or more + * notification packets. + */ +static void p80bd0_isr(const struct device *dev) +{ + struct espi_xec_data *const data = + (struct espi_xec_data *const)dev->data; + struct p80bd_regs *p80regs = + (struct p80bd_regs *)xec_p80bd0_cfg.regbase; + struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION, 0, + ESPI_PERIPHERAL_NODATA }; + int count = 8; /* limit ISR to 8 bytes */ + uint32_t dattr = p80regs->EC_DA; + + /* b[7:0]=8-bit value written, b[15:8]=attributes */ + while ((dattr & MCHP_P80BD_ECDA_NE) && (count--)) { /* Not empty? */ + /* espi_event protocol No Data value is 0 so pick a bit and + * set it. This depends on the application. + */ + evt.evt_data = (dattr & 0xffu) | BIT(16); + switch (dattr & MCHP_P80BD_ECDA_LANE_MSK) { + case MCHP_P80BD_ECDA_LANE_0: + evt.evt_details |= (ESPI_PERIPHERAL_INDEX_0 << 16) | + ESPI_PERIPHERAL_DEBUG_PORT80; + break; + case MCHP_P80BD_ECDA_LANE_1: + evt.evt_details |= (ESPI_PERIPHERAL_INDEX_1 << 16) | + ESPI_PERIPHERAL_DEBUG_PORT80; + break; + case MCHP_P80BD_ECDA_LANE_2: + break; + case MCHP_P80BD_ECDA_LANE_3: + break; + default: + break; + } + + if (evt.evt_details) { + espi_send_callbacks(&data->callbacks, dev, evt); + evt.evt_details = 0; + } + } + + /* clear GIRQ status */ + mchp_xec_ecia_info_girq_src_clr(xec_p80bd0_cfg.ecia_info); +} + +static int connect_irq_p80bd0(const struct device *dev) +{ + mchp_xec_ecia_info_girq_src_clr(xec_p80bd0_cfg.ecia_info); + + IRQ_CONNECT(DT_IRQN(DT_NODELABEL(p80bd0)), + DT_IRQ(DT_NODELABEL(acpi_ec1), priority), + p80bd0_isr, + DEVICE_DT_GET(DT_NODELABEL(espi0)), + 0); + irq_enable(DT_IRQN(DT_NODELABEL(p80bd0))); + + mchp_xec_ecia_info_girq_src_en(xec_p80bd0_cfg.ecia_info); + + return 0; +} + +static int init_p80bd0(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + struct espi_iom_regs *regs = (struct espi_iom_regs *)cfg->base_addr; + struct p80bd_regs *p80bd_hw = + (struct p80bd_regs *)xec_p80bd0_cfg.regbase; + + regs->IOHBAR[IOB_P80BD] = ESPI_XEC_PORT80_BAR_ADDRESS | + MCHP_ESPI_IO_BAR_HOST_VALID; + + p80bd_hw->ACTV = 1; + p80bd_hw->STS_IEN = MCHP_P80BD_SI_THR_IEN; + + return 0; +} + +#undef CONNECT_IRQ_P80BD0 +#define CONNECT_IRQ_P80BD0 connect_irq_p80bd0 +#undef INIT_P80BD0 +#define INIT_P80BD0 init_p80bd0 + +#endif /* CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80 */ + +#ifdef CONFIG_ESPI_PERIPHERAL_UART + +#if CONFIG_ESPI_PERIPHERAL_UART_SOC_MAPPING == 0 +int init_uart0(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + struct espi_iom_regs *regs = (struct espi_iom_regs *)cfg->base_addr; + + regs->IOHBAR[IOB_UART0] = ESPI_XEC_UART0_BAR_ADDRESS | + MCHP_ESPI_IO_BAR_HOST_VALID; + + return 0; +} + +#undef INIT_UART0 +#define INIT_UART0 init_uart0 + +#elif CONFIG_ESPI_PERIPHERAL_UART_SOC_MAPPING == 1 +int init_uart1(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + struct espi_iom_regs *regs = (struct espi_iom_regs *)cfg->base_addr; + + regs->IOHBAR[IOB_UART1] = ESPI_XEC_UART0_BAR_ADDRESS | + MCHP_ESPI_IO_BAR_HOST_VALID; + + return 0; +} + +#undef INIT_UART1 +#define INIT_UART1 init_uart1 +#endif /* CONFIG_ESPI_PERIPHERAL_UART_SOC_MAPPING */ +#endif /* CONFIG_ESPI_PERIPHERAL_UART */ + +typedef int (*host_dev_irq_connect)(const struct device *dev); + +static const host_dev_irq_connect hdic_tbl[] = { + CONNECT_IRQ_MBOX0, + CONNECT_IRQ_KBC0, + CONNECT_IRQ_ACPI_EC0, + CONNECT_IRQ_ACPI_EC1, + CONNECT_IRQ_ACPI_EC2, + CONNECT_IRQ_ACPI_EC3, + CONNECT_IRQ_ACPI_EC4, + CONNECT_IRQ_ACPI_PM1, + CONNECT_IRQ_EMI0, + CONNECT_IRQ_EMI1, + CONNECT_IRQ_EMI2, + CONNECT_IRQ_RTC0, + CONNECT_IRQ_P80BD0, +}; + +typedef int (*host_dev_init)(const struct device *dev); + +static const host_dev_init hd_init_tbl[] = { + INIT_MBOX0, + INIT_KBC0, + INIT_ACPI_EC0, + INIT_ACPI_EC1, + INIT_ACPI_EC2, + INIT_ACPI_EC3, + INIT_ACPI_EC4, + INIT_ACPI_PM1, + INIT_EMI0, + INIT_EMI1, + INIT_EMI2, + INIT_RTC0, + INIT_P80BD0, + INIT_UART0, + INIT_UART1, +}; + +int xec_host_dev_connect_irqs(const struct device *dev) +{ + int ret = 0; + + for (int i = 0; i < ARRAY_SIZE(hdic_tbl); i++) { + if (hdic_tbl[i] == NULL) { + continue; + } + + ret = hdic_tbl[i](dev); + if (ret < 0) { + break; + } + } + + return ret; +} + +int xec_host_dev_init(const struct device *dev) +{ + int ret = 0; + + for (int i = 0; i < ARRAY_SIZE(hd_init_tbl); i++) { + if (hd_init_tbl[i] == NULL) { + continue; + } + + ret = hd_init_tbl[i](dev); + if (ret < 0) { + break; + } + } + + return ret; +} + +#ifdef CONFIG_ESPI_PERIPHERAL_CHANNEL + +typedef int (*xec_lpc_req)(const struct device *, + enum lpc_peripheral_opcode, + uint32_t *); + +struct espi_lpc_req { + uint16_t opcode_start; + uint16_t opcode_max; + xec_lpc_req rd_req; + xec_lpc_req wr_req; +}; + +static const struct espi_lpc_req espi_lpc_req_tbl[] = { +#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC + { E8042_START_OPCODE, E8042_MAX_OPCODE, kbc0_rd_req, kbc0_wr_req }, +#endif +#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO + { EACPI_START_OPCODE, EACPI_MAX_OPCODE, eacpi_rd_req, eacpi_wr_req }, +#endif +#ifdef CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE + { ECUSTOM_START_OPCODE, ECUSTOM_MAX_OPCODE, ecust_rd_req, ecust_wr_req}, +#endif +}; + +static int espi_xec_lpc_req(const struct device *dev, + enum lpc_peripheral_opcode op, + uint32_t *data, uint8_t write) +{ + ARG_UNUSED(dev); + + for (int i = 0; i < ARRAY_SIZE(espi_lpc_req_tbl); i++) { + const struct espi_lpc_req *req = &espi_lpc_req_tbl[i]; + + if ((op >= req->opcode_start) && (op <= req->opcode_max)) { + if (write) { + return req->wr_req(dev, op, data); + } else { + return req->rd_req(dev, op, data); + } + } + } + + return -ENOTSUP; +} + +/* dev = pointer to espi0 device */ +int espi_xec_read_lpc_request(const struct device *dev, + enum lpc_peripheral_opcode op, + uint32_t *data) +{ + return espi_xec_lpc_req(dev, op, data, 0); +} + +int espi_xec_write_lpc_request(const struct device *dev, + enum lpc_peripheral_opcode op, + uint32_t *data) +{ + return espi_xec_lpc_req(dev, op, data, 1); +} +#else +int espi_xec_write_lpc_request(const struct device *dev, + enum lpc_peripheral_opcode op, + uint32_t *data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(op); + ARG_UNUSED(data); + + return -ENOTSUP; +} + +int espi_xec_read_lpc_request(const struct device *dev, + enum lpc_peripheral_opcode op, + uint32_t *data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(op); + ARG_UNUSED(data); + + return -ENOTSUP; +} +#endif /* CONFIG_ESPI_PERIPHERAL_CHANNEL */ diff --git a/drivers/espi/espi_mchp_xec_v2.c b/drivers/espi/espi_mchp_xec_v2.c new file mode 100644 index 00000000000..016c39ed1a6 --- /dev/null +++ b/drivers/espi/espi_mchp_xec_v2.c @@ -0,0 +1,1387 @@ +/* + * Copyright (c) 2019 Intel Corporation + * Copyright (c) 2021 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT microchip_xec_espi_v2 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "espi_utils.h" +#include "espi_mchp_xec_v2.h" + +/* Minimum delay before acknowledging a virtual wire */ +#define ESPI_XEC_VWIRE_ACK_DELAY 10ul + +/* Maximum timeout to transmit a virtual wire packet. + * 10 ms expresed in multiples of 100us + */ +#define ESPI_XEC_VWIRE_SEND_TIMEOUT 100ul + +#define VW_MAX_GIRQS 2ul + +/* 200ms */ +#define MAX_OOB_TIMEOUT 200ul +/* 1s */ +#define MAX_FLASH_TIMEOUT 1000ul + +/* While issuing flash erase command, it should be ensured that the transfer + * length specified is non-zero. + */ +#define ESPI_FLASH_ERASE_DUMMY 0x01ul + +/* OOB maximum address configuration */ +#define ESPI_XEC_OOB_ADDR_MSW 0x1ffful +#define ESPI_XEC_OOB_ADDR_LSW 0xfffful + +/* OOB Rx length */ +#define ESPI_XEC_OOB_RX_LEN 0x7f00ul + +/* Espi peripheral has 3 uart ports */ +#define ESPI_PERIPHERAL_UART_PORT0 0 +#define ESPI_PERIPHERAL_UART_PORT1 1 + +#define UART_DEFAULT_IRQ_POS 2u +#define UART_DEFAULT_IRQ BIT(UART_DEFAULT_IRQ_POS) + +LOG_MODULE_REGISTER(espi, CONFIG_ESPI_LOG_LEVEL); + +#define ESPI_XEC_REG_BASE(dev) \ + ((struct espi_iom_regs *)ESPI_XEC_CONFIG(dev)->base_addr) + +#define ESPI_XEC_MSVW_REG_BASE(dev) \ + ((struct espi_msvw_ar_regs *)(ESPI_XEC_CONFIG(dev)->vw_base_addr)) + +#define ESPI_XEC_SMVW_REG_OFS 0x200 + +#define ESPI_XEC_SMVW_REG_BASE(dev) \ + ((struct espi_smvw_ar_regs *) \ + (ESPI_XEC_CONFIG(dev)->vw_base_addr + ESPI_XEC_SMVW_REG_OFS)) + +/* PCR */ +#define XEC_PCR_REG_BASE \ + ((struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr)))) + +/* Microchip cannonical virtual wire mapping + * ------------------------------------------------------------------------| + * VW Idx | VW reg | SRC_ID3 | SRC_ID2 | SRC_ID1 | SRC_ID0 | + * ------------------------------------------------------------------------| + * System Event Virtual Wires + * ------------------------------------------------------------------------| + * 2h | MSVW00 | res | SLP_S5# | SLP_S4# | SLP_S3# | + * 3h | MSVW01 | res | OOB_RST_WARN | PLTRST# | SUS_STAT# | + * 4h | SMVW00 | PME# | WAKE# | res | OOB_RST_ACK | + * 5h | SMVW01 | SLV_BOOT_STS | ERR_NONFATAL | ERR_FATAL | SLV_BT_DONE | + * 6h | SMVW02 | HOST_RST_ACK | RCIN# | SMI# | SCI# | + * 7h | MSVW02 | res | res | res | HOS_RST_WARN| + * ------------------------------------------------------------------------| + * Platform specific virtual wires + * ------------------------------------------------------------------------| + * 40h | SMVW03 | res | res | DNX_ACK | SUS_ACK# | + * 41h | MSVW03 | SLP_A# | res | SUS_PDNACK| SUS_WARN# | + * 42h | MSVW04 | res | res | SLP_WLAN# | SLP_LAN# | + * 43h | MSVW05 | generic | generic | generic | generic | + * 44h | MSVW06 | generic | generic | generic | generic | + * 45h | SMVW04 | generic | generic | generic | generic | + * 46h | SMVW05 | generic | generic | generic | generic | + * 47h | MSVW07 | res | res | res | HOST_C10 | + * 4Ah | MSVW08 | res | res | DNX_WARN | res | + */ + +static const struct xec_signal vw_tbl[] = { + /* MSVW00 */ + [ESPI_VWIRE_SIGNAL_SLP_S3] = {MCHP_MSVW00, ESPI_VWIRE_SRC_ID0, + ESPI_MASTER_TO_SLAVE}, + [ESPI_VWIRE_SIGNAL_SLP_S4] = {MCHP_MSVW00, ESPI_VWIRE_SRC_ID1, + ESPI_MASTER_TO_SLAVE}, + [ESPI_VWIRE_SIGNAL_SLP_S5] = {MCHP_MSVW00, ESPI_VWIRE_SRC_ID2, + ESPI_MASTER_TO_SLAVE}, + /* MSVW01 */ + [ESPI_VWIRE_SIGNAL_SUS_STAT] = {MCHP_MSVW01, ESPI_VWIRE_SRC_ID0, + ESPI_MASTER_TO_SLAVE}, + [ESPI_VWIRE_SIGNAL_PLTRST] = {MCHP_MSVW01, ESPI_VWIRE_SRC_ID1, + ESPI_MASTER_TO_SLAVE}, + [ESPI_VWIRE_SIGNAL_OOB_RST_WARN] = {MCHP_MSVW01, ESPI_VWIRE_SRC_ID2, + ESPI_MASTER_TO_SLAVE}, + /* SMVW00 */ + [ESPI_VWIRE_SIGNAL_OOB_RST_ACK] = {MCHP_SMVW00, ESPI_VWIRE_SRC_ID0, + ESPI_SLAVE_TO_MASTER}, + [ESPI_VWIRE_SIGNAL_WAKE] = {MCHP_SMVW00, ESPI_VWIRE_SRC_ID2, + ESPI_SLAVE_TO_MASTER}, + [ESPI_VWIRE_SIGNAL_PME] = {MCHP_SMVW00, ESPI_VWIRE_SRC_ID3, + ESPI_SLAVE_TO_MASTER}, + /* SMVW01 */ + [ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID0, + ESPI_SLAVE_TO_MASTER}, + [ESPI_VWIRE_SIGNAL_ERR_FATAL] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID1, + ESPI_SLAVE_TO_MASTER}, + [ESPI_VWIRE_SIGNAL_ERR_NON_FATAL] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID2, + ESPI_SLAVE_TO_MASTER}, + [ESPI_VWIRE_SIGNAL_SLV_BOOT_STS] = {MCHP_SMVW01, ESPI_VWIRE_SRC_ID3, + ESPI_SLAVE_TO_MASTER}, + /* SMVW02 */ + [ESPI_VWIRE_SIGNAL_SCI] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID0, + ESPI_SLAVE_TO_MASTER}, + [ESPI_VWIRE_SIGNAL_SMI] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID1, + ESPI_SLAVE_TO_MASTER}, + [ESPI_VWIRE_SIGNAL_RST_CPU_INIT] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID2, + ESPI_SLAVE_TO_MASTER}, + [ESPI_VWIRE_SIGNAL_HOST_RST_ACK] = {MCHP_SMVW02, ESPI_VWIRE_SRC_ID3, + ESPI_SLAVE_TO_MASTER}, + /* MSVW02 */ + [ESPI_VWIRE_SIGNAL_HOST_RST_WARN] = {MCHP_MSVW02, ESPI_VWIRE_SRC_ID0, + ESPI_MASTER_TO_SLAVE}, + /* SMVW03 */ + [ESPI_VWIRE_SIGNAL_SUS_ACK] = {MCHP_SMVW03, ESPI_VWIRE_SRC_ID0, + ESPI_SLAVE_TO_MASTER}, + [ESPI_VWIRE_SIGNAL_DNX_ACK] = {MCHP_SMVW03, ESPI_VWIRE_SRC_ID1, + ESPI_SLAVE_TO_MASTER}, + /* MSVW03 */ + [ESPI_VWIRE_SIGNAL_SUS_WARN] = {MCHP_MSVW03, ESPI_VWIRE_SRC_ID0, + ESPI_MASTER_TO_SLAVE}, + [ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK] = {MCHP_MSVW03, ESPI_VWIRE_SRC_ID1, + ESPI_MASTER_TO_SLAVE}, + [ESPI_VWIRE_SIGNAL_SLP_A] = {MCHP_MSVW03, ESPI_VWIRE_SRC_ID3, + ESPI_MASTER_TO_SLAVE}, + /* MSVW04 */ + [ESPI_VWIRE_SIGNAL_SLP_LAN] = {MCHP_MSVW04, ESPI_VWIRE_SRC_ID0, + ESPI_MASTER_TO_SLAVE}, + [ESPI_VWIRE_SIGNAL_SLP_WLAN] = {MCHP_MSVW04, ESPI_VWIRE_SRC_ID1, + ESPI_MASTER_TO_SLAVE}, + /* MSVW07 */ + [ESPI_VWIRE_SIGNAL_HOST_C10] = {MCHP_MSVW07, ESPI_VWIRE_SRC_ID0, + ESPI_MASTER_TO_SLAVE}, + /* MSVW08 */ + [ESPI_VWIRE_SIGNAL_DNX_WARN] = {MCHP_MSVW08, ESPI_VWIRE_SRC_ID1, + ESPI_MASTER_TO_SLAVE}, +}; + +/* Buffer size are expressed in bytes */ +#ifdef CONFIG_ESPI_OOB_CHANNEL +static uint32_t target_rx_mem[CONFIG_ESPI_OOB_BUFFER_SIZE >> 2]; +static uint32_t target_tx_mem[CONFIG_ESPI_OOB_BUFFER_SIZE >> 2]; +#endif +#ifdef CONFIG_ESPI_FLASH_CHANNEL +static uint32_t target_mem[CONFIG_ESPI_FLASH_BUFFER_SIZE >> 2]; +#endif + +static inline uintptr_t xec_msvw_addr(const struct device *dev, + uint8_t vw_index) +{ + uintptr_t vwbase = ESPI_XEC_CONFIG(dev)->vw_base_addr; + + return vwbase + vw_index * sizeof(struct espi_msvw_reg); +} + +static inline uintptr_t xec_smvw_addr(const struct device *dev, + uint8_t vw_index) +{ + uintptr_t vwbase = ESPI_XEC_CONFIG(dev)->vw_base_addr; + + vwbase += ESPI_XEC_SMVW_REG_OFS; + return vwbase + vw_index * sizeof(struct espi_smvw_reg); +} + +static int espi_xec_configure(const struct device *dev, struct espi_cfg *cfg) +{ + struct espi_iom_regs *iom_regs = ESPI_XEC_REG_BASE(dev); + uint8_t iomode = 0; + uint8_t cap0 = iom_regs->CAP0; + uint8_t cap1 = iom_regs->CAP1; + uint8_t cur_iomode = (cap1 & MCHP_ESPI_GBL_CAP1_IO_MODE_MASK) >> + MCHP_ESPI_GBL_CAP1_IO_MODE_POS; + + /* Set frequency */ + cap1 &= ~MCHP_ESPI_GBL_CAP1_MAX_FREQ_MASK; + + switch (cfg->max_freq) { + case 20: + cap1 |= MCHP_ESPI_GBL_CAP1_MAX_FREQ_20M; + break; + case 25: + cap1 |= MCHP_ESPI_GBL_CAP1_MAX_FREQ_25M; + break; + case 33: + cap1 |= MCHP_ESPI_GBL_CAP1_MAX_FREQ_33M; + break; + case 50: + cap1 |= MCHP_ESPI_GBL_CAP1_MAX_FREQ_50M; + break; + case 66: + cap1 |= MCHP_ESPI_GBL_CAP1_MAX_FREQ_66M; + break; + default: + return -EINVAL; + } + + /* Set IO mode */ + iomode = (cfg->io_caps >> 1); + if (iomode > 3) { + return -EINVAL; + } + + if (iomode != cur_iomode) { + cap1 &= ~(MCHP_ESPI_GBL_CAP1_IO_MODE_MASK0 << + MCHP_ESPI_GBL_CAP1_IO_MODE_POS); + cap1 |= (iomode << MCHP_ESPI_GBL_CAP1_IO_MODE_POS); + } + + /* Validdate and translate eSPI API channels to MEC capabilities */ + cap0 &= ~MCHP_ESPI_GBL_CAP0_MASK; + if (cfg->channel_caps & ESPI_CHANNEL_PERIPHERAL) { + if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_CHANNEL)) { + cap0 |= MCHP_ESPI_GBL_CAP0_PC_SUPP; + } else { + return -EINVAL; + } + } + + if (cfg->channel_caps & ESPI_CHANNEL_VWIRE) { + if (IS_ENABLED(CONFIG_ESPI_VWIRE_CHANNEL)) { + cap0 |= MCHP_ESPI_GBL_CAP0_VW_SUPP; + } else { + return -EINVAL; + } + } + + if (cfg->channel_caps & ESPI_CHANNEL_OOB) { + if (IS_ENABLED(CONFIG_ESPI_OOB_CHANNEL)) { + cap0 |= MCHP_ESPI_GBL_CAP0_OOB_SUPP; + } else { + return -EINVAL; + } + } + + if (cfg->channel_caps & ESPI_CHANNEL_FLASH) { + if (IS_ENABLED(CONFIG_ESPI_FLASH_CHANNEL)) { + cap0 |= MCHP_ESPI_GBL_CAP0_FC_SUPP; + } else { + LOG_ERR("Flash channel not supported"); + return -EINVAL; + } + } + + iom_regs->CAP0 = cap0; + iom_regs->CAP1 = cap1; + + /* Activate the eSPI block *. + * Need to guarantee that this register is configured before RSMRST# + * de-assertion and after pinmux + */ + iom_regs->ACTV = 1; + LOG_DBG("eSPI block activated successfully"); + + return 0; +} + +static bool espi_xec_channel_ready(const struct device *dev, + enum espi_channel ch) +{ + struct espi_iom_regs *iom_regs = ESPI_XEC_REG_BASE(dev); + bool sts; + + switch (ch) { + case ESPI_CHANNEL_PERIPHERAL: + sts = iom_regs->PCRDY & MCHP_ESPI_PC_READY; + break; + case ESPI_CHANNEL_VWIRE: + sts = iom_regs->VWRDY & MCHP_ESPI_VW_READY; + break; + case ESPI_CHANNEL_OOB: + sts = iom_regs->OOBRDY & MCHP_ESPI_OOB_READY; + break; + case ESPI_CHANNEL_FLASH: + sts = iom_regs->FCRDY & MCHP_ESPI_FC_READY; + break; + default: + sts = false; + break; + } + + return sts; +} + +static int espi_xec_send_vwire(const struct device *dev, + enum espi_vwire_signal signal, uint8_t level) +{ + struct xec_signal signal_info = vw_tbl[signal]; + uint8_t xec_id = signal_info.xec_reg_idx; + uint8_t src_id = signal_info.bit; + uintptr_t regaddr; + + if ((src_id >= ESPI_VWIRE_SRC_ID_MAX) || + (xec_id >= ESPI_MSVW_IDX_MAX)) { + return -EINVAL; + } + + if (signal_info.dir == ESPI_MASTER_TO_SLAVE) { + regaddr = xec_msvw_addr(dev, xec_id); + + sys_write8(level, regaddr + MSVW_BI_SRC0 + src_id); + } + + if (signal_info.dir == ESPI_SLAVE_TO_MASTER) { + regaddr = xec_smvw_addr(dev, xec_id); + + sys_write8(level, regaddr + SMVW_BI_SRC0 + src_id); + + /* Ensure eSPI virtual wire packet is transmitted + * There is no interrupt, so need to poll register + */ + uint8_t rd_cnt = ESPI_XEC_VWIRE_SEND_TIMEOUT; + + while (sys_read8(regaddr + SMVW_BI_SRC_CHG) && rd_cnt--) { + k_busy_wait(100); + } + } + + return 0; +} + +static int espi_xec_receive_vwire(const struct device *dev, + enum espi_vwire_signal signal, + uint8_t *level) +{ + struct xec_signal signal_info = vw_tbl[signal]; + uint8_t xec_id = signal_info.xec_reg_idx; + uint8_t src_id = signal_info.bit; + uintptr_t regaddr; + + if ((src_id >= ESPI_VWIRE_SRC_ID_MAX) || + (xec_id >= ESPI_SMVW_IDX_MAX) || (level == NULL)) { + return -EINVAL; + } + + if (signal_info.dir == ESPI_MASTER_TO_SLAVE) { + regaddr = xec_msvw_addr(dev, xec_id); + *level = sys_read8(regaddr + MSVW_BI_SRC0 + src_id) & BIT(0); + } + + if (signal_info.dir == ESPI_SLAVE_TO_MASTER) { + regaddr = xec_smvw_addr(dev, xec_id); + *level = sys_read8(regaddr + SMVW_BI_SRC0 + src_id) & BIT(0); + } + + return 0; +} + +#ifdef CONFIG_ESPI_OOB_CHANNEL +static int espi_xec_send_oob(const struct device *dev, + struct espi_oob_packet *pckt) +{ + int ret; + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); + uint8_t err_mask = MCHP_ESPI_OOB_TX_STS_IBERR | + MCHP_ESPI_OOB_TX_STS_OVRUN | + MCHP_ESPI_OOB_TX_STS_BADREQ; + + LOG_DBG("%s", __func__); + + if (!(regs->OOBTXSTS & MCHP_ESPI_OOB_TX_STS_CHEN)) { + LOG_ERR("OOB channel is disabled"); + return -EIO; + } + + if (regs->OOBTXSTS & MCHP_ESPI_OOB_TX_STS_BUSY) { + LOG_ERR("OOB channel is busy"); + return -EBUSY; + } + + if (pckt->len > CONFIG_ESPI_OOB_BUFFER_SIZE) { + LOG_ERR("insufficient space"); + return -EINVAL; + } + + memcpy(target_tx_mem, pckt->buf, pckt->len); + + regs->OOBTXL = pckt->len; + regs->OOBTXC = MCHP_ESPI_OOB_TX_CTRL_START; + LOG_DBG("%s %d", __func__, regs->OOBTXL); + + /* Wait until ISR or timeout */ + ret = k_sem_take(&data->tx_lock, K_MSEC(MAX_OOB_TIMEOUT)); + if (ret == -EAGAIN) { + return -ETIMEDOUT; + } + + if (regs->OOBTXSTS & err_mask) { + LOG_ERR("Tx failed %x", regs->OOBTXSTS); + regs->OOBTXSTS = err_mask; + return -EIO; + } + + return 0; +} + +static int espi_xec_receive_oob(const struct device *dev, + struct espi_oob_packet *pckt) +{ + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + uint8_t err_mask = MCHP_ESPI_OOB_RX_STS_IBERR | + MCHP_ESPI_OOB_RX_STS_OVRUN; + + if (regs->OOBRXSTS & err_mask) { + return -EIO; + } + +#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC + int ret; + struct espi_xec_data *data = (struct espi_xec_data *)(dev->data); + + /* Wait until ISR or timeout */ + ret = k_sem_take(&data->rx_lock, K_MSEC(MAX_OOB_TIMEOUT)); + if (ret == -EAGAIN) { + return -ETIMEDOUT; + } +#endif + /* Check if buffer passed to driver can fit the received buffer */ + uint32_t rcvd_len = regs->OOBRXL & MCHP_ESPI_OOB_RX_LEN_MASK; + + if (rcvd_len > pckt->len) { + LOG_ERR("space rcvd %d vs %d", rcvd_len, pckt->len); + return -EIO; + } + + pckt->len = rcvd_len; + memcpy(pckt->buf, target_rx_mem, pckt->len); + memset(target_rx_mem, 0, pckt->len); + + /* Only after data has been copied from SRAM, indicate channel + * is available for next packet + */ + regs->OOBRXC |= MCHP_ESPI_OOB_RX_CTRL_AVAIL; + + return 0; +} +#endif /* CONFIG_ESPI_OOB_CHANNEL */ + +#ifdef CONFIG_ESPI_FLASH_CHANNEL +static int espi_xec_flash_read(const struct device *dev, + struct espi_flash_packet *pckt) +{ + int ret; + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + struct espi_xec_data *data = (struct espi_xec_data *)(dev->data); + uint32_t err_mask = MCHP_ESPI_FC_STS_IBERR | + MCHP_ESPI_FC_STS_FAIL | + MCHP_ESPI_FC_STS_OVFL | + MCHP_ESPI_FC_STS_BADREQ; + + LOG_DBG("%s", __func__); + + if (!(regs->FCSTS & MCHP_ESPI_FC_STS_CHAN_EN)) { + LOG_ERR("Flash channel is disabled"); + return -EIO; + } + + if (pckt->len > CONFIG_ESPI_FLASH_BUFFER_SIZE) { + LOG_ERR("Invalid size request"); + return -EINVAL; + } + + regs->FCFA[1] = 0; + regs->FCFA[0] = pckt->flash_addr; + regs->FCBA[1] = 0; + regs->FCBA[0] = (uint32_t)&target_mem[0]; + regs->FCLEN = pckt->len; + regs->FCCTL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_RD0); + regs->FCCTL |= MCHP_ESPI_FC_CTRL_START; + + /* Wait until ISR or timeout */ + ret = k_sem_take(&data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT)); + if (ret == -EAGAIN) { + LOG_ERR("%s timeout", __func__); + return -ETIMEDOUT; + } + + if (regs->FCSTS & err_mask) { + LOG_ERR("%s error %x", __func__, err_mask); + regs->FCSTS = err_mask; + return -EIO; + } + + memcpy(pckt->buf, target_mem, pckt->len); + + return 0; +} + +static int espi_xec_flash_write(const struct device *dev, + struct espi_flash_packet *pckt) +{ + int ret; + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + uint32_t err_mask = MCHP_ESPI_FC_STS_IBERR | + MCHP_ESPI_FC_STS_OVRUN | + MCHP_ESPI_FC_STS_FAIL | + MCHP_ESPI_FC_STS_BADREQ; + + struct espi_xec_data *data = (struct espi_xec_data *)(dev->data); + + LOG_DBG("%s", __func__); + + if (!(regs->FCSTS & MCHP_ESPI_FC_STS_CHAN_EN)) { + LOG_ERR("Flash channel is disabled"); + return -EIO; + } + + if ((regs->FCCFG & MCHP_ESPI_FC_CFG_BUSY)) { + LOG_ERR("Flash channel is busy"); + return -EBUSY; + } + + memcpy(target_mem, pckt->buf, pckt->len); + + regs->FCFA[1] = 0; + regs->FCFA[0] = pckt->flash_addr; + regs->FCBA[1] = 0; + regs->FCBA[0] = (uint32_t)&target_mem[0]; + regs->FCLEN = pckt->len; + regs->FCCTL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_WR0); + regs->FCCTL |= MCHP_ESPI_FC_CTRL_START; + + /* Wait until ISR or timeout */ + ret = k_sem_take(&data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT)); + if (ret == -EAGAIN) { + LOG_ERR("%s timeout", __func__); + return -ETIMEDOUT; + } + + if (regs->FCSTS & err_mask) { + LOG_ERR("%s err: %x", __func__, err_mask); + regs->FCSTS = err_mask; + return -EIO; + } + + return 0; +} + +static int espi_xec_flash_erase(const struct device *dev, + struct espi_flash_packet *pckt) +{ + int ret; + uint32_t status; + uint32_t err_mask = MCHP_ESPI_FC_STS_IBERR | + MCHP_ESPI_FC_STS_OVRUN | + MCHP_ESPI_FC_STS_FAIL | + MCHP_ESPI_FC_STS_BADREQ; + + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + struct espi_xec_data *data = (struct espi_xec_data *)(dev->data); + + LOG_DBG("%s", __func__); + + if (!(regs->FCSTS & MCHP_ESPI_FC_STS_CHAN_EN)) { + LOG_ERR("Flash channel is disabled"); + return -EIO; + } + + if ((regs->FCCFG & MCHP_ESPI_FC_CFG_BUSY)) { + LOG_ERR("Flash channel is busy"); + return -EBUSY; + } + + /* Clear status register */ + status = regs->FCSTS; + regs->FCSTS = status; + + regs->FCFA[1] = 0; + regs->FCFA[0] = pckt->flash_addr; + regs->FCLEN = ESPI_FLASH_ERASE_DUMMY; + regs->FCCTL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_ERS0); + regs->FCCTL |= MCHP_ESPI_FC_CTRL_START; + + /* Wait until ISR or timeout */ + ret = k_sem_take(&data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT)); + if (ret == -EAGAIN) { + LOG_ERR("%s timeout", __func__); + return -ETIMEDOUT; + } + + if (regs->FCSTS & err_mask) { + LOG_ERR("%s err: %x", __func__, err_mask); + regs->FCSTS = err_mask; + return -EIO; + } + + return 0; +} +#endif /* CONFIG_ESPI_FLASH_CHANNEL */ + +static int espi_xec_manage_callback(const struct device *dev, + struct espi_callback *callback, bool set) +{ + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); + + return espi_manage_callback(&data->callbacks, callback, set); +} + +#ifdef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE +static void send_slave_bootdone(const struct device *dev) +{ + int ret; + uint8_t boot_done; + + ret = espi_xec_receive_vwire(dev, ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE, + &boot_done); + if (!ret && !boot_done) { + /* SLAVE_BOOT_DONE & SLAVE_LOAD_STS have to be sent together */ + espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_SLV_BOOT_STS, 1); + espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE, 1); + } +} +#endif + +#ifdef CONFIG_ESPI_OOB_CHANNEL +static void espi_init_oob(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + + /* Enable OOB Tx/Rx interrupts */ + mchp_xec_ecia_girq_src_en(cfg->irq_info_list[oob_up_girq_idx].gid, + cfg->irq_info_list[oob_up_girq_idx].gpos); + mchp_xec_ecia_girq_src_en(cfg->irq_info_list[oob_dn_girq_idx].gid, + cfg->irq_info_list[oob_dn_girq_idx].gpos); + + regs->OOBTXA[1] = 0; + regs->OOBRXA[1] = 0; + regs->OOBTXA[0] = (uint32_t)&target_tx_mem[0]; + regs->OOBRXA[0] = (uint32_t)&target_rx_mem[0]; + regs->OOBRXL = 0x00FF0000; + + /* Enable OOB Tx channel enable change status interrupt */ + regs->OOBTXIEN |= MCHP_ESPI_OOB_TX_IEN_CHG_EN | + MCHP_ESPI_OOB_TX_IEN_DONE; + + /* Enable Rx channel to receive data any time + * there are case where OOB is not initiated by a previous OOB Tx + */ + regs->OOBRXIEN |= MCHP_ESPI_OOB_RX_IEN; + regs->OOBRXC |= MCHP_ESPI_OOB_RX_CTRL_AVAIL; +} +#endif + +#ifdef CONFIG_ESPI_FLASH_CHANNEL +static void espi_init_flash(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + + LOG_DBG("%s", __func__); + + /* Need to clear status done when ROM boots in MAF */ + LOG_DBG("%s ESPI_FC_REGS->CFG %X", __func__, regs->FCCFG); + regs->FCSTS = MCHP_ESPI_FC_STS_DONE; + + /* Enable interrupts */ + mchp_xec_ecia_girq_src_en(cfg->irq_info_list[fc_girq_idx].gid, + cfg->irq_info_list[fc_girq_idx].gpos); + regs->FCIEN |= MCHP_ESPI_FC_IEN_CHG_EN; + regs->FCIEN |= MCHP_ESPI_FC_IEN_DONE; +} +#endif + +static void espi_bus_init(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + + /* Enable bus interrupts */ + mchp_xec_ecia_girq_src_en(cfg->irq_info_list[pc_girq_idx].gid, + cfg->irq_info_list[pc_girq_idx].gpos); + mchp_xec_ecia_girq_src_en(cfg->irq_info_list[rst_girq_idx].gid, + cfg->irq_info_list[rst_girq_idx].gpos); + mchp_xec_ecia_girq_src_en(cfg->irq_info_list[vw_ch_en_girq_idx].gid, + cfg->irq_info_list[vw_ch_en_girq_idx].gpos); +} + +/* Clear specified eSPI bus GIRQ status */ +static int xec_espi_bus_intr_clr(const struct device *dev, + enum xec_espi_girq_idx idx) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + + if (idx >= max_girq_idx) { + return -EINVAL; + } + + mchp_xec_ecia_girq_src_clr(cfg->irq_info_list[idx].gid, + cfg->irq_info_list[idx].gpos); + + return 0; +} + +/* Enable/disable specified eSPI bus GIRQ */ +static int xec_espi_bus_intr_ctl(const struct device *dev, + enum xec_espi_girq_idx idx, + uint8_t enable) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + + if (idx >= max_girq_idx) { + return -EINVAL; + } + + if (enable) { + mchp_xec_ecia_girq_src_en(cfg->irq_info_list[idx].gid, + cfg->irq_info_list[idx].gpos); + } else { + mchp_xec_ecia_girq_src_dis(cfg->irq_info_list[idx].gid, + cfg->irq_info_list[idx].gpos); + } + + return 0; +} + +static void espi_rst_isr(const struct device *dev) +{ + uint8_t rst_sts; + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); + struct espi_event evt = { ESPI_BUS_RESET, 0, 0 }; + +#ifdef ESPI_XEC_V2_DEBUG + data->espi_rst_count++; +#endif + + rst_sts = regs->ERIS; + + /* eSPI reset status register is clear on write register */ + regs->ERIS = MCHP_ESPI_RST_ISTS; + /* clear GIRQ latched status */ + xec_espi_bus_intr_clr(dev, rst_girq_idx); + + if (rst_sts & MCHP_ESPI_RST_ISTS) { + if (rst_sts & MCHP_ESPI_RST_ISTS_PIN_RO_HI) { + data->espi_rst_asserted = 1; + } else { + data->espi_rst_asserted = 0; + } + + evt.evt_data = data->espi_rst_asserted; + espi_send_callbacks(&data->callbacks, dev, evt); +#ifdef CONFIG_ESPI_OOB_CHANNEL + espi_init_oob(dev); +#endif +#ifdef CONFIG_ESPI_FLASH_CHANNEL + espi_init_flash(dev); +#endif + espi_bus_init(dev); + } +} + +/* Configure sub devices BAR address if not using default I/O based address + * then make its BAR valid. + * Refer to microchip eSPI I/O base addresses for default values + */ +static void config_sub_devices(const struct device *dev) +{ + xec_host_dev_init(dev); +} + +static void configure_sirq(const struct device *dev) +{ + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + +#ifdef CONFIG_ESPI_PERIPHERAL_UART + switch (CONFIG_ESPI_PERIPHERAL_UART_SOC_MAPPING) { + case ESPI_PERIPHERAL_UART_PORT0: + regs->SIRQ[SIRQ_UART0] = UART_DEFAULT_IRQ; + break; + case ESPI_PERIPHERAL_UART_PORT1: + regs->SIRQ[SIRQ_UART1] = UART_DEFAULT_IRQ; + break; + } +#endif +#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC + regs->SIRQ[SIRQ_KBC_KIRQ] = 1; + regs->SIRQ[SIRQ_KBC_MIRQ] = 12; +#endif +} + +static void setup_espi_io_config(const struct device *dev, + uint16_t host_address) +{ + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + + regs->IOHBAR[IOB_IOC] = (host_address << 16) | + MCHP_ESPI_IO_BAR_HOST_VALID; + + config_sub_devices(dev); + configure_sirq(dev); + + regs->PCSTS = MCHP_ESPI_PC_STS_EN_CHG | + MCHP_ESPI_PC_STS_BM_EN_CHG_POS; + regs->PCIEN |= MCHP_ESPI_PC_IEN_EN_CHG; + regs->PCRDY = 1; +} + +/* + * Write the interrupt select field of the specified MSVW source. + * Each MSVW controls 4 virtual wires. + */ +static int xec_espi_vw_intr_ctrl(const struct device *dev, uint8_t msvw_idx, + uint8_t src_id, uint8_t intr_mode) +{ + struct espi_msvw_ar_regs *regs = ESPI_XEC_MSVW_REG_BASE(dev); + + + if ((msvw_idx >= ESPI_NUM_MSVW) || (src_id > 3)) { + return -EINVAL; + } + + uintptr_t msvw_addr = (uintptr_t)®s->MSVW[msvw_idx]; + + sys_write8(intr_mode, msvw_addr + MSVW_BI_IRQ_SEL0 + src_id); + + return 0; +} + +static void espi_pc_isr(const struct device *dev) +{ + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + uint32_t status = regs->PCSTS; + + if (status & MCHP_ESPI_PC_STS_EN_CHG) { + if (status & MCHP_ESPI_PC_STS_EN) { + setup_espi_io_config(dev, MCHP_ESPI_IOBAR_INIT_DFLT); + } + + regs->PCSTS = MCHP_ESPI_PC_STS_EN_CHG; + } + + xec_espi_bus_intr_clr(dev, pc_girq_idx); +} + +static void espi_vw_chan_en_isr(const struct device *dev) +{ + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); + struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY, + .evt_details = ESPI_CHANNEL_VWIRE, + .evt_data = 0 }; + uint32_t status = regs->VWSTS; + + if (status & MCHP_ESPI_VW_EN_STS_RO) { + regs->VWRDY = 1; + evt.evt_data = 1; + /* VW channel interrupt can disabled at this point */ + xec_espi_bus_intr_ctl(dev, vw_ch_en_girq_idx, 0); + +#ifdef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE + send_slave_bootdone(dev); +#endif + } + + espi_send_callbacks(&data->callbacks, dev, evt); + + xec_espi_bus_intr_clr(dev, vw_ch_en_girq_idx); +} + +#ifdef CONFIG_ESPI_OOB_CHANNEL +static void espi_oob_down_isr(const struct device *dev) +{ + uint32_t status; + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); +#ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC + struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_OOB_RECEIVED, + .evt_details = 0, + .evt_data = 0 }; +#endif + + status = regs->OOBRXSTS; + + LOG_DBG("%s %x", __func__, status); + if (status & MCHP_ESPI_OOB_RX_STS_DONE) { + /* Register is write-on-clear, ensure only 1 bit is affected */ + regs->OOBRXSTS = MCHP_ESPI_OOB_RX_STS_DONE; + +#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC + k_sem_give(&data->rx_lock); +#else + evt.evt_details = ESPI_OOB_REGS->RX_LEN & + MCHP_ESPI_OOB_RX_LEN_MASK; + espi_send_callbacks(&data->callbacks, dev, evt); +#endif + } + + xec_espi_bus_intr_clr(dev, oob_dn_girq_idx); +} + +static void espi_oob_up_isr(const struct device *dev) +{ + uint32_t status; + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); + struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY, + .evt_details = ESPI_CHANNEL_OOB, + .evt_data = 0 + }; + + status = regs->OOBTXSTS; + LOG_DBG("%s sts:%x", __func__, status); + + if (status & MCHP_ESPI_OOB_TX_STS_DONE) { + /* Register is write-on-clear, ensure only 1 bit is affected */ + status = regs->OOBTXSTS = MCHP_ESPI_OOB_TX_STS_DONE; + k_sem_give(&data->tx_lock); + } + + if (status & MCHP_ESPI_OOB_TX_STS_CHG_EN) { + if (status & MCHP_ESPI_OOB_TX_STS_CHEN) { + espi_init_oob(dev); + /* Indicate OOB channel is ready to eSPI host */ + regs->OOBRDY = 1; + evt.evt_data = 1; + } + + status = regs->OOBTXSTS = MCHP_ESPI_OOB_TX_STS_CHG_EN; + espi_send_callbacks(&data->callbacks, dev, evt); + } + + xec_espi_bus_intr_clr(dev, oob_up_girq_idx); +} +#endif + +#ifdef CONFIG_ESPI_FLASH_CHANNEL +static void espi_flash_isr(const struct device *dev) +{ + uint32_t status; + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); + struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY, + .evt_details = ESPI_CHANNEL_FLASH, + .evt_data = 0, + }; + + status = regs->FCSTS; + LOG_DBG("%s %x", __func__, status); + + if (status & MCHP_ESPI_FC_STS_DONE) { + /* Ensure to clear only relevant bit */ + regs->FCSTS = MCHP_ESPI_FC_STS_DONE; + + k_sem_give(&data->flash_lock); + } + + if (status & MCHP_ESPI_FC_STS_CHAN_EN_CHG) { + /* Ensure to clear only relevant bit */ + regs->FCSTS = MCHP_ESPI_FC_STS_CHAN_EN_CHG; + + if (status & MCHP_ESPI_FC_STS_CHAN_EN) { + espi_init_flash(dev); + /* Indicate flash channel is ready to eSPI master */ + regs->FCRDY = MCHP_ESPI_FC_READY; + evt.evt_data = 1; + } + + espi_send_callbacks(&data->callbacks, dev, evt); + } + + xec_espi_bus_intr_clr(dev, fc_girq_idx); +} +#endif + +/* Send callbacks if enabled and track eSPI host system state */ +static void notify_system_state(const struct device *dev, + enum espi_vwire_signal signal) +{ + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); + struct espi_event evt = { ESPI_BUS_EVENT_VWIRE_RECEIVED, 0, 0 }; + uint8_t status = 0; + + espi_xec_receive_vwire(dev, signal, &status); + if (!status) { + data->sx_state = signal; + } + + evt.evt_details = signal; + evt.evt_data = status; + espi_send_callbacks(&data->callbacks, dev, evt); +} + +static void notify_host_warning(const struct device *dev, + enum espi_vwire_signal signal) +{ + uint8_t status; + + espi_xec_receive_vwire(dev, signal, &status); + + if (!IS_ENABLED(CONFIG_ESPI_AUTOMATIC_WARNING_ACKNOWLEDGE)) { + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); + struct espi_event evt = {ESPI_BUS_EVENT_VWIRE_RECEIVED, 0, 0 }; + + evt.evt_details = signal; + evt.evt_data = status; + espi_send_callbacks(&data->callbacks, dev, evt); + } else { + k_busy_wait(ESPI_XEC_VWIRE_ACK_DELAY); + /* Some flows are dependent on awareness of client's driver + * about these warnings in such cases these automatic response + * should not be enabled. + */ + switch (signal) { + case ESPI_VWIRE_SIGNAL_HOST_RST_WARN: + espi_xec_send_vwire(dev, + ESPI_VWIRE_SIGNAL_HOST_RST_ACK, + status); + break; + case ESPI_VWIRE_SIGNAL_SUS_WARN: + espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_SUS_ACK, + status); + break; + case ESPI_VWIRE_SIGNAL_OOB_RST_WARN: + espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_OOB_RST_ACK, + status); + break; + case ESPI_VWIRE_SIGNAL_DNX_WARN: + espi_xec_send_vwire(dev, ESPI_VWIRE_SIGNAL_DNX_ACK, + status); + break; + default: + break; + } + } +} + +/* + * VW handlers must have signature + * typedef void (*mchp_xec_ecia_callback_t) (int girq_id, int src, void *user) + * where parameter user is a pointer to const struct device + * These handlers are registered to their respective GIRQ child device of the + * ECIA driver. + */ + +static void vw_slp3_handler(int girq_id, int src, void *user) +{ + const struct device *dev = (const struct device *)user; + + notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S3); +} + +static void vw_slp4_handler(int girq_id, int src, void *user) +{ + const struct device *dev = (const struct device *)user; + + notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S4); +} + +static void vw_slp5_handler(int girq_id, int src, void *user) +{ + const struct device *dev = (const struct device *)user; + + notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S5); +} + +static void vw_host_rst_warn_handler(int girq_id, int src, void *user) +{ + const struct device *dev = (const struct device *)user; + + notify_host_warning(dev, ESPI_VWIRE_SIGNAL_HOST_RST_WARN); +} + +static void vw_sus_warn_handler(int girq_id, int src, void *user) +{ + const struct device *dev = (const struct device *)user; + + notify_host_warning(dev, ESPI_VWIRE_SIGNAL_SUS_WARN); +} + +static void vw_oob_rst_handler(int girq_id, int src, void *user) +{ + const struct device *dev = (const struct device *)user; + + notify_host_warning(dev, ESPI_VWIRE_SIGNAL_OOB_RST_WARN); +} + +static void vw_sus_pwrdn_ack_handler(int girq_id, int src, void *user) +{ + const struct device *dev = (const struct device *)user; + + notify_system_state(dev, ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK); +} + +static void vw_sus_slp_a_handler(int girq_id, int src, void *user) +{ + const struct device *dev = (const struct device *)user; + + notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_A); +} + +static void vw_sus_dnx_warn_handler(int girq_id, int src, void *user) +{ + const struct device *dev = (const struct device *)user; + + notify_host_warning(dev, ESPI_VWIRE_SIGNAL_DNX_WARN); +} + +static void vw_pltrst_handler(int girq_id, int src, void *user) +{ + const struct device *dev = (const struct device *)user; + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); + struct espi_event evt = { ESPI_BUS_EVENT_VWIRE_RECEIVED, + ESPI_VWIRE_SIGNAL_PLTRST, 0 + }; + uint8_t status = 0; + + espi_xec_receive_vwire(dev, ESPI_VWIRE_SIGNAL_PLTRST, &status); + if (status) { + setup_espi_io_config(dev, MCHP_ESPI_IOBAR_INIT_DFLT); + } + + /* PLT_RST will be received several times */ + if (status != data->plt_rst_asserted) { + data->plt_rst_asserted = status; + evt.evt_data = status; + espi_send_callbacks(&data->callbacks, dev, evt); + } +} + +const struct espi_vw_isr m2s_vwires_isr[] = { + {ESPI_VWIRE_SIGNAL_SLP_S3, MCHP_MSVW00_GIRQ, MCHP_MSVW00_SRC0_GIRQ_POS, + vw_slp3_handler}, + {ESPI_VWIRE_SIGNAL_SLP_S4, MCHP_MSVW00_GIRQ, MCHP_MSVW00_SRC1_GIRQ_POS, + vw_slp4_handler}, + {ESPI_VWIRE_SIGNAL_SLP_S5, MCHP_MSVW00_GIRQ, MCHP_MSVW00_SRC2_GIRQ_POS, + vw_slp5_handler}, + {ESPI_VWIRE_SIGNAL_PLTRST, MCHP_MSVW01_GIRQ, MCHP_MSVW01_SRC1_GIRQ_POS, + vw_pltrst_handler}, + {ESPI_VWIRE_SIGNAL_OOB_RST_WARN, MCHP_MSVW01_GIRQ, + MCHP_MSVW01_SRC2_GIRQ_POS, vw_oob_rst_handler}, + {ESPI_VWIRE_SIGNAL_HOST_RST_WARN, MCHP_MSVW02_GIRQ, + MCHP_MSVW02_SRC0_GIRQ_POS, vw_host_rst_warn_handler}, + {ESPI_VWIRE_SIGNAL_SUS_WARN, MCHP_MSVW03_GIRQ, + MCHP_MSVW03_SRC0_GIRQ_POS, vw_sus_warn_handler}, + {ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK, MCHP_MSVW03_GIRQ, + MCHP_MSVW03_SRC1_GIRQ_POS, vw_sus_pwrdn_ack_handler}, + {ESPI_VWIRE_SIGNAL_SLP_A, MCHP_MSVW03_GIRQ, + MCHP_MSVW03_SRC3_GIRQ_POS, vw_sus_slp_a_handler}, + {ESPI_VWIRE_SIGNAL_DNX_WARN, MCHP_MSVW08_GIRQ, + MCHP_MSVW08_SRC1_GIRQ_POS, vw_sus_dnx_warn_handler} +}; + +static int espi_xec_init(const struct device *dev); + +static const struct espi_driver_api espi_xec_driver_api = { + .config = espi_xec_configure, + .get_channel_status = espi_xec_channel_ready, + .send_vwire = espi_xec_send_vwire, + .receive_vwire = espi_xec_receive_vwire, +#ifdef CONFIG_ESPI_OOB_CHANNEL + .send_oob = espi_xec_send_oob, + .receive_oob = espi_xec_receive_oob, +#endif +#ifdef CONFIG_ESPI_FLASH_CHANNEL + .flash_read = espi_xec_flash_read, + .flash_write = espi_xec_flash_write, + .flash_erase = espi_xec_flash_erase, +#endif + .manage_callback = espi_xec_manage_callback, + .read_lpc_request = espi_xec_read_lpc_request, + .write_lpc_request = espi_xec_write_lpc_request, +}; + +static struct espi_xec_data espi_xec_data_var; + +/* n = node-id, p = property, i = index */ +#define XEC_IRQ_INFO(n, p, i) \ + { \ + .gid = MCHP_XEC_ECIA_GIRQ(DT_PROP_BY_IDX(n, p, i)), \ + .gpos = MCHP_XEC_ECIA_GIRQ_POS(DT_PROP_BY_IDX(n, p, i)), \ + .anid = MCHP_XEC_ECIA_NVIC_AGGR(DT_PROP_BY_IDX(n, p, i)), \ + .dnid = MCHP_XEC_ECIA_NVIC_DIRECT(DT_PROP_BY_IDX(n, p, i)), \ + }, + +static const struct espi_xec_irq_info espi_xec_irq_info_0[] = { + DT_FOREACH_PROP_ELEM(DT_NODELABEL(espi0), girqs, XEC_IRQ_INFO) +}; + +static const struct espi_xec_config espi_xec_config = { + .base_addr = DT_INST_REG_ADDR(0), + .vw_base_addr = DT_INST_REG_ADDR_BY_NAME(0, vw), + .pcr_idx = DT_INST_PROP_BY_IDX(0, pcrs, 0), + .pcr_bitpos = DT_INST_PROP_BY_IDX(0, pcrs, 1), + .irq_info_list = espi_xec_irq_info_0, + .irq_info_size = ARRAY_SIZE(espi_xec_irq_info_0), +}; + +DEVICE_DT_INST_DEFINE(0, &espi_xec_init, NULL, + &espi_xec_data_var, &espi_xec_config, + PRE_KERNEL_2, CONFIG_ESPI_INIT_PRIORITY, + &espi_xec_driver_api); + +/* + * Connect ESPI bus interrupt handlers: ESPI_RESET and channels. + * MEC172x hardware fixed SAF interrupt routing bug. SAF driver + * will connect its direct mode interrupt handler(s) on this GIRQ. + */ +static void espi_xec_connect_irqs(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* eSPI Reset */ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 7, irq), + DT_INST_IRQ_BY_IDX(0, 7, priority), + espi_rst_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQ_BY_IDX(0, 7, irq)); + + /* eSPI Virtual wire channel enable change ISR */ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 8, irq), + DT_INST_IRQ_BY_IDX(0, 8, priority), + espi_vw_chan_en_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQ_BY_IDX(0, 8, irq)); + + /* eSPI Peripheral Channel */ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 0, irq), + DT_INST_IRQ_BY_IDX(0, 0, priority), + espi_pc_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQ_BY_IDX(0, 0, irq)); + +#ifdef CONFIG_ESPI_OOB_CHANNEL + /* eSPI OOB Upstream direction */ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 4, irq), + DT_INST_IRQ_BY_IDX(0, 4, priority), + espi_oob_up_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQ_BY_IDX(0, 4, irq)); + + /* eSPI OOB Channel Downstream direction */ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 5, irq), + DT_INST_IRQ_BY_IDX(0, 5, priority), + espi_oob_down_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQ_BY_IDX(0, 5, irq)); +#endif + +#ifdef CONFIG_ESPI_FLASH_CHANNEL + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 6, irq), + DT_INST_IRQ_BY_IDX(0, 6, priority), + espi_flash_isr, + DEVICE_DT_INST_GET(0), 0); + irq_enable(DT_INST_IRQ_BY_IDX(0, 6, irq)); +#endif +} + +/* + * Initialize eSPI hardware and associated peripherals blocks using eSPI + * as their host interface. + * We change VW capabilities reported to match the number of VWires the + * driver is supporting. + * A VW packet on the bus contains VW count followed by the VW groups. + * The VW count is a zero based 6-bit value: (0 - 63) specifying the number of + * groups in the packet. + * A VW group consists of two bytes: VW host index and VW data. Each group + * contains the state of 4 virtual wires. + * The total supported virtual wires is 64 * 4 = 256. + * MEC172x supports 11 MSVW groups and 11 SMVW groups. + */ +static int espi_xec_init(const struct device *dev) +{ + struct espi_xec_config *const cfg = ESPI_XEC_CONFIG(dev); + struct espi_iom_regs *regs = ESPI_XEC_REG_BASE(dev); + struct espi_xec_data *const data = ESPI_XEC_DATA(dev); + struct pcr_regs *pcr = XEC_PCR_REG_BASE; + int ret; + + data->plt_rst_asserted = 0; +#ifdef ESPI_XEC_V2_DEBUG + data->espi_rst_count = 0; +#endif + /* clear eSPI PCR sleep enable */ + z_mchp_xec_pcr_periph_sleep(cfg->pcr_idx, cfg->pcr_bitpos, 0); + + /* Configure eSPI_PLTRST# to cause nSIO_RESET reset + * NOTE: this is also clearing bit 0(PWR_INV) causing the internal + * RESET_VCC to de-assert. Host facing peripherals will no longer + * be held in reset. + */ + pcr->PWR_RST_CTRL = MCHP_PCR_PR_CTRL_USE_ESPI_PLTRST; + regs->PLTSRC = MCHP_ESPI_PLTRST_SRC_IS_VW; + + /* Configure the channels and its capabilities based on build config */ + regs->CAP0 |= MCHP_ESPI_GBL_CAP0_VW_SUPP | MCHP_ESPI_GBL_CAP0_PC_SUPP; + + regs->CAPVW = MAX(ESPI_NUM_MSVW, ESPI_NUM_SMVW); + regs->CAPPC |= MCHP_ESPI_PC_CAP_MAX_PLD_SZ_64; + +#ifdef CONFIG_ESPI_OOB_CHANNEL + regs->CAP0 |= MCHP_ESPI_GBL_CAP0_OOB_SUPP; + regs->CAPOOB |= MCHP_ESPI_OOB_CAP_MAX_PLD_SZ_73; + + k_sem_init(&data->tx_lock, 0, 1); +#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC + k_sem_init(&data->rx_lock, 0, 1); +#endif /* CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC */ +#else + regs->CAP0 &= ~MCHP_ESPI_GBL_CAP0_OOB_SUPP; +#endif + +#ifdef CONFIG_ESPI_FLASH_CHANNEL + regs->CAP0 |= MCHP_ESPI_GBL_CAP0_FC_SUPP | + MCHP_ESPI_FC_CAP_MAX_PLD_SZ_64; + regs->CAPFC |= MCHP_ESPI_FC_CAP_SHARE_MAF_SAF | + MCHP_ESPI_FC_CAP_MAX_RD_SZ_64; + + k_sem_init(&data->flash_lock, 0, 1); +#else + regs->CAP0 &= ~MCHP_ESPI_GBL_CAP0_FC_SUPP; +#endif + + /* Clear reset interrupt status and enable interrupts */ + regs->ERIS = MCHP_ESPI_RST_ISTS; + regs->ERIE |= MCHP_ESPI_RST_IEN; + regs->PCSTS = MCHP_ESPI_PC_STS_EN_CHG; + regs->PCIEN |= MCHP_ESPI_PC_IEN_EN_CHG; + + /* register VWire handlers with their aggregated GIRQs + * in the ECIA driver + */ + for (int i = 0; i < ARRAY_SIZE(m2s_vwires_isr); i++) { + const struct espi_vw_isr *vwi = &m2s_vwires_isr[i]; + struct xec_signal signal_info = vw_tbl[vwi->signal]; + uint8_t xec_id = signal_info.xec_reg_idx; + + /* enables interrupt in eSPI MSVWn register */ + xec_espi_vw_intr_ctrl(dev, xec_id, signal_info.bit, + MSVW_IRQ_SEL_EDGE_BOTH); + + /* register handler */ + ret = mchp_xec_ecia_set_callback(vwi->girq_id, vwi->girq_pos, + vwi->the_isr, (void *)dev); + __ASSERT_NO_MSG(ret == 0); + + mchp_xec_ecia_girq_src_en(vwi->girq_id, vwi->girq_pos); + } + + /* Enable interrupts for each logical channel enable assertion */ + xec_espi_bus_intr_ctl(dev, pc_girq_idx, 1); + xec_espi_bus_intr_ctl(dev, vw_ch_en_girq_idx, 1); + xec_espi_bus_intr_ctl(dev, rst_girq_idx, 1); + +#ifdef CONFIG_ESPI_OOB_CHANNEL + espi_init_oob(dev); +#endif +#ifdef CONFIG_ESPI_FLASH_CHANNEL + espi_init_flash(dev); +#endif + + espi_xec_connect_irqs(dev); + + ret = xec_host_dev_connect_irqs(dev); + + return ret; +} diff --git a/drivers/espi/espi_mchp_xec_v2.h b/drivers/espi/espi_mchp_xec_v2.h new file mode 100644 index 00000000000..d984c39a50e --- /dev/null +++ b/drivers/espi/espi_mchp_xec_v2.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_ESPI_MCHP_XEC_ESPI_V2_H_ +#define ZEPHYR_DRIVERS_ESPI_MCHP_XEC_ESPI_V2_H_ + +#include +#include +#include + +#define ESPI_XEC_V2_DEBUG 1 + +struct espi_isr { + uint8_t girq_id; + uint8_t girq_pos; + void (*the_isr)(const struct device *dev); +}; + +struct espi_vw_isr { + uint8_t signal; + uint8_t girq_id; + uint8_t girq_pos; + void (*the_isr)(int girq, int bpos, void *dev); +}; + +struct espi_xec_irq_info { + uint8_t gid; /* GIRQ id [8, 26] */ + uint8_t gpos; /* bit position in GIRQ [0, 31] */ + uint8_t anid; /* Aggregated GIRQ NVIC number */ + uint8_t dnid; /* Direct GIRQ NVIC number */ +}; + +struct espi_xec_config { + uint32_t base_addr; + uint32_t vw_base_addr; + uint8_t pcr_idx; + uint8_t pcr_bitpos; + const struct espi_xec_irq_info *irq_info_list; + uint8_t irq_info_size; +}; + +#define ESPI_XEC_CONFIG(dev) \ + ((struct espi_xec_config * const)(dev)->config) + +struct espi_xec_data { + sys_slist_t callbacks; + struct k_sem tx_lock; + struct k_sem rx_lock; + struct k_sem flash_lock; + uint8_t plt_rst_asserted; + uint8_t espi_rst_asserted; + uint8_t sx_state; +#ifdef ESPI_XEC_V2_DEBUG + uint32_t espi_rst_count; +#endif +}; + +#define ESPI_XEC_DATA(dev) \ + ((struct espi_xec_data * const)(dev)->data) + +struct xec_signal { + uint8_t xec_reg_idx; + uint8_t bit; + uint8_t dir; +}; + +enum mchp_msvw_regs { + MCHP_MSVW00, + MCHP_MSVW01, + MCHP_MSVW02, + MCHP_MSVW03, + MCHP_MSVW04, + MCHP_MSVW05, + MCHP_MSVW06, + MCHP_MSVW07, + MCHP_MSVW08, +}; + +enum mchp_smvw_regs { + MCHP_SMVW00, + MCHP_SMVW01, + MCHP_SMVW02, + MCHP_SMVW03, + MCHP_SMVW04, + MCHP_SMVW05, + MCHP_SMVW06, + MCHP_SMVW07, + MCHP_SMVW08, +}; + +enum xec_espi_girq_idx { + pc_girq_idx = 0, + bm1_girq_idx, + bm2_girq_idx, + ltr_girq_idx, + oob_up_girq_idx, + oob_dn_girq_idx, + fc_girq_idx, + rst_girq_idx, + vw_ch_en_girq_idx, + max_girq_idx, +}; + + +int xec_host_dev_init(const struct device *dev); +int xec_host_dev_connect_irqs(const struct device *dev); + +int espi_xec_read_lpc_request(const struct device *dev, + enum lpc_peripheral_opcode op, + uint32_t *data); + +int espi_xec_write_lpc_request(const struct device *dev, + enum lpc_peripheral_opcode op, + uint32_t *data); + +#endif /* ZEPHYR_DRIVERS_ESPI_MCHP_XEC_ESPI_V2_H_ */ diff --git a/drivers/interrupt_controller/intc_mchp_ecia_xec.c b/drivers/interrupt_controller/intc_mchp_ecia_xec.c index b364945a0e7..9f96f06d2db 100644 --- a/drivers/interrupt_controller/intc_mchp_ecia_xec.c +++ b/drivers/interrupt_controller/intc_mchp_ecia_xec.c @@ -19,6 +19,7 @@ #include #include #include +#include /* defined at the SoC layer */ #define MCHP_FIRST_GIRQ MCHP_FIRST_GIRQ_NOS @@ -186,6 +187,58 @@ void mchp_xec_ecia_nvic_clr_pend(uint32_t nvic_num) NVIC_ClearPendingIRQ(nvic_num); } +/* API taking input encoded with MCHP_XEC_ECIA(g, gb, na, nd) macro */ + +void mchp_xec_ecia_info_girq_aggr_en(int ecia_info, uint8_t enable) +{ + uint8_t girq_num = MCHP_XEC_ECIA_GIRQ(ecia_info); + + mchp_xec_ecia_girq_aggr_en(girq_num, enable); +} + +void mchp_xec_ecia_info_girq_src_clr(int ecia_info) +{ + uint8_t girq_num = MCHP_XEC_ECIA_GIRQ(ecia_info); + uint8_t bitpos = MCHP_XEC_ECIA_GIRQ_POS(ecia_info); + + mchp_xec_ecia_girq_src_clr(girq_num, bitpos); +} + +void mchp_xec_ecia_info_girq_src_en(int ecia_info) +{ + uint8_t girq_num = MCHP_XEC_ECIA_GIRQ(ecia_info); + uint8_t bitpos = MCHP_XEC_ECIA_GIRQ_POS(ecia_info); + + mchp_xec_ecia_girq_src_en(girq_num, bitpos); +} + +void mchp_xec_ecia_info_girq_src_dis(int ecia_info) +{ + uint8_t girq_num = MCHP_XEC_ECIA_GIRQ(ecia_info); + uint8_t bitpos = MCHP_XEC_ECIA_GIRQ_POS(ecia_info); + + mchp_xec_ecia_girq_src_dis(girq_num, bitpos); +} + +uint32_t mchp_xec_ecia_info_girq_result(int ecia_info) +{ + uint8_t girq_num = MCHP_XEC_ECIA_GIRQ(ecia_info); + + return mchp_xec_ecia_girq_result(girq_num); +} + +/* + * Clear NVIC pending status given GIRQ source information encoded by macro + * MCHP_XEC_ECIA. For aggregated only sources the ecoding sets direct NVIC + * number equal to aggregated NVIC number. + */ +void mchp_xec_ecia_info_nvic_clr_pend(int ecia_info) +{ + uint8_t nvic_num = MCHP_XEC_ECIA_NVIC_DIRECT(ecia_info); + + mchp_xec_ecia_nvic_clr_pend(nvic_num); +} + /** * @brief enable GIRQn interrupt for specific source * @@ -207,6 +260,20 @@ int mchp_xec_ecia_enable(int girq, int src) return 0; } +/** + * @brief enable EXTI interrupt for specific line specified by parameter + * encoded with MCHP_XEC_ECIA macro. + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + */ +int mchp_xec_ecia_info_enable(int ecia_info) +{ + uint8_t girq = (uint8_t)MCHP_XEC_ECIA_GIRQ(ecia_info); + uint8_t src = (uint8_t)MCHP_XEC_ECIA_GIRQ_POS(ecia_info); + + return mchp_xec_ecia_enable(girq, src); +} + /** * @brief disable EXTI interrupt for specific line * @@ -228,6 +295,20 @@ int mchp_xec_ecia_disable(int girq, int src) return 0; } +/** + * @brief disable EXTI interrupt for specific line specified by parameter + * encoded with MCHP_XEC_ECIA macro. + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + */ +int mchp_xec_ecia_info_disable(int ecia_info) +{ + uint8_t girq = (uint8_t)MCHP_XEC_ECIA_GIRQ(ecia_info); + uint8_t src = (uint8_t)MCHP_XEC_ECIA_GIRQ_POS(ecia_info); + + return mchp_xec_ecia_disable(girq, src); +} + /* forward reference */ static const struct device *get_girq_dev(int girq_num); @@ -279,6 +360,22 @@ int mchp_xec_ecia_set_callback(int girq_num, int src, return mchp_xec_ecia_set_callback_by_dev(dev, src, cb, data); } +/** + * @brief set GIRQn interrupt source callback + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + * @param cb user callback + * @param data user data + */ +int mchp_xec_ecia_info_set_callback(int ecia_info, mchp_xec_ecia_callback_t cb, + void *data) +{ + const struct device *dev = get_girq_dev(MCHP_XEC_ECIA_GIRQ(ecia_info)); + uint8_t src = MCHP_XEC_ECIA_GIRQ_POS(ecia_info); + + return mchp_xec_ecia_set_callback_by_dev(dev, src, cb, data); +} + /** * @brief unset GIRQn interrupt source callback by device handle * @@ -321,6 +418,20 @@ int mchp_ecia_unset_callback(int girq_num, int src) return mchp_ecia_unset_callback_by_dev(dev, src); } +/** + * @brief unset GIRQn interrupt source callback + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + */ +int mchp_ecia_info_unset_callback(int ecia_info) +{ + const struct device *dev = get_girq_dev(MCHP_XEC_ECIA_GIRQ(ecia_info)); + uint8_t src = MCHP_XEC_ECIA_GIRQ_POS(ecia_info); + + return mchp_ecia_unset_callback_by_dev(dev, src); +} + + /* * Create a build time flag to know if any aggregated GIRQ has been enabled. * We make use of DT FOREACH macro to check GIRQ node status. diff --git a/dts/arm/microchip/mec172x/mec172x-vw-routing.dtsi b/dts/arm/microchip/mec172x/mec172x-vw-routing.dtsi new file mode 100644 index 00000000000..8d3439abecd --- /dev/null +++ b/dts/arm/microchip/mec172x/mec172x-vw-routing.dtsi @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2021 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define MSVW 0 +#define SMVW 1 + +/ { + mchp-xec-espi-vw-routing { + compatible = "microchip,xec-espi-vw-routing"; + + /* eSPI Virtual Vire (VW) routing */ + vw-slp-s3-n { + /* Host-index MSVW/SMVW MSVW/SMVW-index source */ + vw-reg = <0x02 MSVW 0 0>; + vw-girq = <24 0>; + }; + vw-slp-s4-n { + vw-reg = <0x02 MSVW 0 1>; + vw-girq = <24 1>; + }; + vw-slp-s5-n { + vw-reg = <0x02 MSVW 0 2>; + vw-girq = <24 2>; + }; + vw-sus-stat-n { + vw-reg = <0x03 MSVW 1 0>; + vw-girq = <24 4>; + }; + vw-pltrst-n { + vw-reg = <0x03 MSVW 1 1>; + vw-girq = <24 5>; + }; + vw-oob-rst-warn { + vw-reg = <0x03 MSVW 1 2>; + vw-girq = <24 6>; + }; + vw-host-rst-warn { + vw-reg = <0x07 MSVW 2 0>; + vw-girq = <24 8>; + }; + vw-sus-warn-n { + vw-reg = <0x41 MSVW 3 0>; + vw-girq = <24 12>; + }; + vw-sus-pwrdn-ack { + vw-reg = <0x41 MSVW 3 1>; + vw-girq = <24 13>; + }; + vw-slp-a-n { + vw-reg = <0x41 MSVW 3 3>; + vw-girq = <24 15>; + }; + vw-slp-lan-n { + vw-reg = <0x42 MSVW 4 0>; + vw-girq = <24 16>; + }; + vw-slp-wlen-n { + vw-reg = <0x42 MSVW 4 1>; + vw-girq = <24 17>; + }; + vw-host-c10 { + vw-reg = <0x47 MSVW 7 0>; + vw-girq = <25 0>; + }; + vw-dnx-warn { + vw-reg = <0x4a MSVW 8 1>; + vw-girq = <25 5>; + }; + + /* Device to Host (SMVW) do not have SoC interrupts */ + vw-oob-rst-ack { + vw-reg = <0x04 SMVW 0 0>; + }; + vw-wake-n { + vw-reg = <0x04 SMVW 0 2>; + }; + vw-pme-n { + vw-reg = <0x04 SMVW 0 3>; + }; + vw-slave-boot-load-done { + vw-reg = <0x05 SMVW 1 0>; + }; + vw-error-fatal { + vw-reg = <0x05 SMVW 1 1>; + }; + vw-error-non-fatal { + vw-reg = <0x05 SMVW 1 2>; + }; + vw-slave-boot-load-status { + vw-reg = <0x05 SMVW 1 3>; + }; + vw-sci-n { + vw-reg = <0x06 SMVW 2 0>; + }; + vw-smi-n { + vw-reg = <0x06 SMVW 2 1>; + }; + vw-rcin-n { + vw-reg = <0x06 SMVW 2 2>; + }; + vw-host-rst-ack { + vw-reg = <0x06 SMVW 2 3>; + }; + vw-sus-ack-n { + vw-reg = <0x40 SMVW 3 0>; + }; + vw-dnx-ack { + vw-reg = <0x40 SMVW 3 1>; + }; + }; +}; diff --git a/dts/arm/microchip/mec172xnsz.dtsi b/dts/arm/microchip/mec172xnsz.dtsi index f19e95cc4a6..7a96a43dff8 100644 --- a/dts/arm/microchip/mec172xnsz.dtsi +++ b/dts/arm/microchip/mec172xnsz.dtsi @@ -11,6 +11,8 @@ #include #include +#include "mec172x/mec172x-vw-routing.dtsi" + / { cpus { #address-cells = <1>; @@ -592,28 +594,7 @@ #size-cells = <0>; status = "disabled"; }; - uart0: uart@400f2400 { - compatible = "microchip,xec-uart"; - reg = <0x400f2400 0x400>; - interrupts = <40 0>; - clock-frequency = <1843200>; - current-speed = <38400>; - label = "UART_0"; - girqs = <15 0>; - pcrs = <2 1>; - status = "disabled"; - }; - uart1: uart@400f2800 { - compatible = "microchip,xec-uart"; - reg = <0x400f2800 0x400>; - interrupts = <41 0>; - clock-frequency = <1843200>; - current-speed = <38400>; - label = "UART_1"; - girqs = <15 1>; - pcrs = <2 2>; - status = "disabled"; - }; +/* uarts were here */ ps2_0: ps2@40009000 { reg = <0x40009000 0x40>; interrupts = <100 1>; @@ -888,51 +869,6 @@ label = "BCLINK_0"; status = "disabled"; }; - mbox0: mbox@400f0000 { - reg = <0x400f0000 0x200>; - interrupts = <60 0>; - girqs = <15 20>; - pcrs = <2 17>; - label = "MBOX_0"; - status = "disabled"; - }; - emi0: emi@400f4000 { - reg = <0x400f4000 0x400>; - interrupts = <42 0>; - girqs = <15 2>; - label = "EMI_0"; - status = "disabled"; - }; - emi1: emi@400f4400 { - reg = <0x400f4400 0x400>; - interrupts = <43 0>; - girqs = <15 3>; - label = "EMI_1"; - status = "disabled"; - }; - emi2: emi@400f4800 { - reg = <0x400f4800 0x400>; - interrupts = <44 0>; - girqs = <15 4>; - label = "EMI_2"; - status = "disabled"; - }; - rtc0: rtc@400f5000 { - reg = <0x400f5000 0x100>; - interrupts = <119 0>, <120 0>; - girqs = <21 8>, <21 9>; - pcrs = <2 18>; - label = "RTC_0"; - status = "disabled"; - }; - p80bd0: p80bd@400f8000 { - reg = <0x400f8000 0x800>; - interrupts = <62 0>; - girqs = <15 22>; - pcrs = <2 25>; - label = "P80BD_0"; - status = "disabled"; - }; tfdp0: tfdp@40008c00 { reg = <0x40008c00 0x10>; pcrs = <1 7>; @@ -945,6 +881,212 @@ label = "GLBLCFG_0"; status = "disabled"; }; + espi0: espi@400f3400 { + compatible = "microchip,xec-espi-v2"; + reg = < 0x400f3400 0x400 + 0x400f3800 0x400 + 0x400f9c00 0x400>; + reg-names = "io", "mem", "vw"; + interrupts = <103 3>, <104 3>, <105 3>, <106 3>, + <107 3>, <108 3>, <109 3>, <110 3>, + <156 3>; + interrupt-names = "pc", "bm1", "bm2", "ltr", "oob_up", + "oob_dn", "fc", "rst", "vw_chan_en"; + girqs = < MCHP_XEC_ECIA(19, 0, 11, 103) + MCHP_XEC_ECIA(19, 1, 11, 104) + MCHP_XEC_ECIA(19, 2, 11, 105) + MCHP_XEC_ECIA(19, 3, 11, 106) + MCHP_XEC_ECIA(19, 4, 11, 107) + MCHP_XEC_ECIA(19, 5, 11, 108) + MCHP_XEC_ECIA(19, 6, 11, 109) + MCHP_XEC_ECIA(19, 7, 11, 110) + MCHP_XEC_ECIA(19, 8, 11, 156) >; + pcrs = <2 19>; + label = "ESPI_0"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + mbox0: mbox@400f0000 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f0000 0x200>; + interrupts = <60 0>; + girqs = < MCHP_XEC_ECIA(15, 20, 7, 60) >; + pcrs = <2 17>; + ldn = <0>; + label = "MBOX_0"; + status = "disabled"; + }; + kbc0: kbc@400f0400 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f0400 0x400>; + interrupts = <58 3>, <59 3>; + interrupt-names = "kbc_obe", "kbc_ibf"; + girqs = < MCHP_XEC_ECIA(15, 18, 7, 58) + MCHP_XEC_ECIA(15, 19, 7, 58) >; + ldn = <1>; + label = "KBC_0"; + status = "disabled"; + }; + acpi_ec0: acpi_ec@400f0800 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f0800 0x400>; + interrupts = <45 3>, <46 3>; + interrupt-names = "acpi_ibf", "acpi_obe"; + girqs = < MCHP_XEC_ECIA(15, 5, 7, 45) + MCHP_XEC_ECIA(15, 6, 7, 46) >; + ldn = <2>; + label = "ACPI_EC_0"; + status = "disabled"; + }; + acpi_ec1: acpi_ec@400f0c00 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f0c00 0x400>; + interrupts = <47 3>, <48 3>; + interrupt-names = "acpi_ibf", "acpi_obe"; + girqs = < MCHP_XEC_ECIA(15, 7, 7, 58) + MCHP_XEC_ECIA(15, 8, 7, 58) >; + ldn = <3>; + label = "ACPI_EC_1"; + status = "disabled"; + }; + acpi_ec2: acpi_ec@400f1000 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f1000 0x400>; + interrupts = <49 3>, <50 3>; + interrupt-names = "acpi_ibf", "acpi_obe"; + girqs = < MCHP_XEC_ECIA(15, 9, 7, 58) + MCHP_XEC_ECIA(15, 10, 7, 58) >; + ldn = <4>; + label = "ACPI_EC_2"; + status = "disabled"; + }; + acpi_ec3: acpi_ec@400f1400 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f1400 0x400>; + interrupts = <51 3>, <52 3>; + interrupt-names = "acpi_ibf", "acpi_obe"; + girqs = < MCHP_XEC_ECIA(15, 11, 7, 58) + MCHP_XEC_ECIA(15, 12, 7, 58) >; + ldn = <5>; + label = "ACPI_EC_3"; + status = "disabled"; + }; + acpi_ec4: acpi_ec@400f1800 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f1800 0x400>; + interrupts = <53 3>, <54 3>; + interrupt-names = "acpi_ibf", "acpi_obe"; + girqs = < MCHP_XEC_ECIA(15, 13, 7, 58) + MCHP_XEC_ECIA(15, 14, 7, 58) >; + ldn = <6>; + label = "ACPI_EC_4"; + status = "disabled"; + }; + acpi_pm1: acpi_pm1@400f1c00 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f1c00 0x400>; + interrupts = <55 3>, <56 3>, <57 3>; + interrupt-names = "pm1_ctl", "pm1_en", "pm1_sts"; + girqs = < MCHP_XEC_ECIA(15, 15, 7, 58) + MCHP_XEC_ECIA(15, 16, 7, 58) + MCHP_XEC_ECIA(15, 17, 7, 58) >; + ldn = <7>; + label = "ACPI_PM1"; + status = "disabled"; + }; + port92: port92@400f2000 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f2000 0x400>; + label = "PORT92"; + ldn = <8>; + status = "disabled"; + }; + uart0: uart@400f2400 { + compatible = "microchip,xec-uart"; + reg = <0x400f2400 0x400>; + interrupts = <40 0>; + clock-frequency = <1843200>; + current-speed = <38400>; + label = "UART_0"; + girqs = <15 0>; + pcrs = <2 1>; + ldn = <9>; + status = "disabled"; + }; + uart1: uart@400f2800 { + compatible = "microchip,xec-uart"; + reg = <0x400f2800 0x400>; + interrupts = <41 0>; + clock-frequency = <1843200>; + current-speed = <38400>; + label = "UART_1"; + girqs = <15 1>; + pcrs = <2 2>; + ldn = <10>; + status = "disabled"; + }; + emi0: emi@400f4000 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f4000 0x400>; + interrupts = <42 0>; + girqs = < MCHP_XEC_ECIA(15, 2, 7, 42) >; + label = "EMI_0"; + ldn = <16>; + status = "disabled"; + }; + emi1: emi@400f4400 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f4400 0x400>; + interrupts = <43 0>; + girqs = < MCHP_XEC_ECIA(15, 3, 7, 43) >; + label = "EMI_1"; + ldn = <17>; + status = "disabled"; + }; + emi2: emi@400f4800 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f4800 0x400>; + interrupts = <44 0>; + girqs = < MCHP_XEC_ECIA(15, 4, 7, 44) >; + label = "EMI_2"; + ldn = <18>; + status = "disabled"; + }; + rtc0: rtc@400f5000 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f5000 0x100>; + interrupts = <119 0>, <120 0>; + girqs = < MCHP_XEC_ECIA(21, 8, 13, 119) + MCHP_XEC_ECIA(21, 9, 13, 120) >; + pcrs = <2 18>; + label = "RTC_0"; + ldn = <20>; + status = "disabled"; + }; + /* Capture writes to host I/O 0x80 - 0x83 */ + p80bd0: p80bd@400f8000 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f8000 0x400>; + interrupts = <62 0>; + girqs = < MCHP_XEC_ECIA(15, 22, 7, 58) >; + pcrs = <2 25>; + ldn = <32>; + label = "P80BD_0"; + status = "disabled"; + }; + /* Capture writes to an 8-bit I/O and map to one of 0x80 to 0x83 */ + p80bd0_alias: p80bd@400f8400 { + compatible = "microchip,xec-espi-host-dev"; + reg = <0x400f8400 0x400>; + label = "P80BD_0_ALIAS"; + ldn = <33>; + host-io = <0x90>; + /* map 0x90 to 0x80 */ + host-io-addr-mask = <0x01>; + status = "disabled"; + }; + }; }; }; diff --git a/dts/bindings/espi/microchip,xec-espi-host-dev.yaml b/dts/bindings/espi/microchip,xec-espi-host-dev.yaml new file mode 100644 index 00000000000..db2c1385f12 --- /dev/null +++ b/dts/bindings/espi/microchip,xec-espi-host-dev.yaml @@ -0,0 +1,95 @@ +# Copyright (c) 2021, Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +description: Microchip XEC eSPI Host devices + +compatible: "microchip,xec-espi-host-dev" + +include: [base.yaml] + +on-bus: espi + +properties: + reg: + required: true + + interrupts: + required: false + + interrupt-names: + required: false + + ldn: + type: int + required: true + description: logical device number + + girqs: + type: array + required: false + description: array of GIRQ and bit positions + + pcrs: + type: array + required: false + description: PCR sleep register index and bit position + +# optional properties application to different host facing devices + host-io: + type: int + required: false + description: | + Logical device Host I/O (x86) base. Refer to SoC documentation for the + number of I/O decoders implemented by a device (1 or 2) and the fixed + I/O masks. + + host-io-addr-mask: + type: int + required: false + description: | + Host I/O address mask. This value is fixed for all HW and is only + used by Port80 BIOS debug alias device to specify the byte lane the + alias address is mapped to in the 80h to 83h I/O range. + + host-mem: + type: int + required: false + description: | + Logical device Host memory (x86) base address. Refer to SoC + documentation for which logical devices implement a memory decoder + and the fixed memory address masking. + + emi-mems: + type: array + required: false + description: | + Each EMI host device supports Host access to two SoC data memory + regions. Each region requires three configuration parameters: + Base address in the SoC data memory, read limit, and write limit. + If bits[14:2] of the address written by the Host to the EC address + register is less than the limit value the access is allowed. Bit[15] + of the EC address selects which of the two memory regions is accessed. + + "emi-mem-cells": + type: int + const: 3 + + "#girq-cells": + type: int + const: 1 + + "#pcr-cells": + type: int + const: 2 + +emi-mem-cells: + - base + - rdlimit + - wrlimit + +girq-cells: + - girqinfo + +pcr-cells: + - reg_idx + - bitpos diff --git a/dts/bindings/espi/microchip,xec-espi-v2.yaml b/dts/bindings/espi/microchip,xec-espi-v2.yaml new file mode 100644 index 00000000000..563dc93a52f --- /dev/null +++ b/dts/bindings/espi/microchip,xec-espi-v2.yaml @@ -0,0 +1,44 @@ +# Copyright (c) 2019 Intel Corporation +# Copyright (c) 2021 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +description: Microchip ESPI V2 controller + +compatible: "microchip,xec-espi-v2" + +include: espi-controller.yaml + +properties: + reg: + description: mmio register space + required: true + + reg-names: + required: true + description: Name of each register space + + girqs: + type: array + required: true + description: | + Array of encoded interrupt information + + pcrs: + type: array + required: true + description: eSPI PCR register index and bit position + + "#girq-cells": + type: int + const: 1 + + "#pcr-cells": + type: int + const: 2 + +girq-cells: + - girqinfo + +pcr-cells: + - regidx + - bitpos diff --git a/dts/bindings/espi/microchip,xec-espi-vw-routing.yaml b/dts/bindings/espi/microchip,xec-espi-vw-routing.yaml new file mode 100644 index 00000000000..e743994d8dd --- /dev/null +++ b/dts/bindings/espi/microchip,xec-espi-vw-routing.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2021 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +description: Microchip XEC eSPI Virtual Wire routing + +compatible: "microchip,xec-espi-vw-routing" + +child-binding: + description: | + Child node containing the routing of an eSPI virtual wire to the SoC + VW registers and ECIA GIRQ registers. + properties: + vw-reg: + type: array + required: true + description: vw signal's register index and vw bitmask. + + vw-girq: + type: array + required: false + description: | + Routing of MSVW source to aggregated GIRQs + + Example: OOB_RST_WARN is source 2 of MSVW01 routed to GIRQ24 b[5] + vw-girq = <24 5>; diff --git a/dts/bindings/rtc/microchip,xec-timer.yaml b/dts/bindings/rtc/microchip,xec-timer.yaml index 0457d5a6e5c..d391635b39d 100644 --- a/dts/bindings/rtc/microchip,xec-timer.yaml +++ b/dts/bindings/rtc/microchip,xec-timer.yaml @@ -37,7 +37,7 @@ properties: required: true description: PCR sleep enable register index and bit position. - "girq-cells": + "#girq-cells": type: int const: 2 diff --git a/dts/bindings/serial/microchip,xec-uart.yaml b/dts/bindings/serial/microchip,xec-uart.yaml index 932ba026c38..fd05b7735e9 100644 --- a/dts/bindings/serial/microchip,xec-uart.yaml +++ b/dts/bindings/serial/microchip,xec-uart.yaml @@ -11,6 +11,11 @@ properties: interrupts: required: true + ldn: + type: int + required: true + description: logical device number + girqs: type: array required: true diff --git a/dts/bindings/timer/microchip,xec-rtos-timer.yaml b/dts/bindings/timer/microchip,xec-rtos-timer.yaml index 1175cf9b7f5..30234e82491 100644 --- a/dts/bindings/timer/microchip,xec-rtos-timer.yaml +++ b/dts/bindings/timer/microchip,xec-rtos-timer.yaml @@ -22,7 +22,7 @@ properties: required: true description: Array of GIRQ numbers [8:26] and bit positions [0:31]. - "girq-cells": + "#girq-cells": type: int const: 2 diff --git a/include/drivers/interrupt_controller/intc_mchp_xec_ecia.h b/include/drivers/interrupt_controller/intc_mchp_xec_ecia.h index e5093004a59..3214a6f97f6 100644 --- a/include/drivers/interrupt_controller/intc_mchp_xec_ecia.h +++ b/include/drivers/interrupt_controller/intc_mchp_xec_ecia.h @@ -28,6 +28,14 @@ */ int mchp_xec_ecia_enable(int girq_id, int src); +/** + * @brief enable EXTI interrupt for specific line specified by parameter + * encoded with MCHP_XEC_ECIA macro. + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + */ +int mchp_xec_ecia_info_enable(int ecia_info); + /** * @brief disable EXTI interrupt for specific line * @@ -36,6 +44,14 @@ int mchp_xec_ecia_enable(int girq_id, int src); */ int mchp_xec_ecia_disable(int girq_id, int src); +/** + * @brief disable EXTI interrupt for specific line specified by parameter + * encoded with MCHP_XEC_ECIA macro. + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + */ +int mchp_xec_ecia_info_disable(int ecia_info); + /* callback for ECIA GIRQ interrupt source */ typedef void (*mchp_xec_ecia_callback_t) (int girq_id, int src, void *user); @@ -51,6 +67,16 @@ typedef void (*mchp_xec_ecia_callback_t) (int girq_id, int src, void *user); int mchp_xec_ecia_set_callback(int girq_id, int src, mchp_xec_ecia_callback_t cb, void *data); +/** + * @brief set GIRQn interrupt source callback + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + * @param cb user callback + * @param data user data + */ +int mchp_xec_ecia_info_set_callback(int ecia_info, mchp_xec_ecia_callback_t cb, + void *data); + /** * @brief set GIRQn interrupt source callback * @@ -141,4 +167,44 @@ uint32_t mchp_xec_ecia_girq_result(uint8_t girq_id); */ void mchp_xec_ecia_nvic_clr_pend(uint32_t nvic_num); +/* API using GIRQ parameters encoded with MCHP_XEC_ECIA */ + +/** @brief enable or disable aggregated GIRQ output + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + * @param enable is flag to indicate enable(1) or disable(0) + */ +void mchp_xec_ecia_info_girq_aggr_en(int ecia_info, uint8_t enable); + +/** @brief clear GIRQ latched source status bit + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + */ +void mchp_xec_ecia_info_girq_src_clr(int ecia_info); + +/** @brief enable a source in a GIRQ + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + */ +void mchp_xec_ecia_info_girq_src_en(int ecia_info); + +/** @brief disable a source in a GIRQ + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + */ +void mchp_xec_ecia_info_girq_src_dis(int ecia_info); + +/** @brief Read GIRQ result register (bit-wise OR of enable and source) + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + * @return 32-bit unsigned result register value + */ +uint32_t mchp_xec_ecia_info_girq_result(int ecia_info); + +/** @brief Clear external NVIC input pending status given encoded ECIA info. + * + * @param ecia_info is GIRQ connection encoded with MCHP_XEC_ECIA + */ +void mchp_xec_ecia_info_nvic_clr_pend(int ecia_info); + #endif /* ZEPHYR_DRIVERS_INTERRUPT_CONTROLLER_MCHP_XEC_ECIA_H_ */ diff --git a/soc/arm/microchip_mec/common/soc_espi_v2.h b/soc/arm/microchip_mec/common/soc_espi_v2.h new file mode 100644 index 00000000000..64486b78caf --- /dev/null +++ b/soc/arm/microchip_mec/common/soc_espi_v2.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2019 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _SOC_ESPI_V2_H_ +#define _SOC_ESPI_V2_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +int mchp_xec_espi_host_dev_init(const struct device *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* _SOC_ESPI_V2_H_ */ diff --git a/soc/arm/microchip_mec/mec172x/Kconfig.defconfig.mec172xnsz b/soc/arm/microchip_mec/mec172x/Kconfig.defconfig.mec172xnsz index 29b21eff03c..90883022061 100644 --- a/soc/arm/microchip_mec/mec172x/Kconfig.defconfig.mec172xnsz +++ b/soc/arm/microchip_mec/mec172x/Kconfig.defconfig.mec172xnsz @@ -31,4 +31,8 @@ config I2C_XEC_V2 default y depends on I2C +config ESPI_XEC_V2 + default y + depends on ESPI + endif # SOC_MEC172X_NSZ diff --git a/soc/arm/microchip_mec/mec172x/reg/mec172x_ecia.h b/soc/arm/microchip_mec/mec172x/reg/mec172x_ecia.h index 00614c40806..495e425b9e4 100644 --- a/soc/arm/microchip_mec/mec172x/reg/mec172x_ecia.h +++ b/soc/arm/microchip_mec/mec172x/reg/mec172x_ecia.h @@ -1078,6 +1078,18 @@ enum MCHP_GIRQ_INDEX { #define MCHP_GIRQ25_NVIC_AGGR 16u +#define MCHP_MSVW00_GIRQ 24 +#define MCHP_MSVW01_GIRQ 24 +#define MCHP_MSVW02_GIRQ 24 +#define MCHP_MSVW03_GIRQ 24 +#define MCHP_MSVW04_GIRQ 24 +#define MCHP_MSVW05_GIRQ 24 +#define MCHP_MSVW06_GIRQ 24 +#define MCHP_MSVW07_GIRQ 25 +#define MCHP_MSVW08_GIRQ 25 +#define MCHP_MSVW09_GIRQ 25 +#define MCHP_MSVW10_GIRQ 25 + /* GIRQ26 Source, Enable_Set/Clr, Result registers bit positions */ #define MCHP_GPIO_0240_GIRQ_POS 0 #define MCHP_GPIO_0241_GIRQ_POS 1 diff --git a/soc/arm/microchip_mec/mec172x/soc.h b/soc/arm/microchip_mec/mec172x/soc.h index 0523753c59f..a9bbbee1a3b 100644 --- a/soc/arm/microchip_mec/mec172x/soc.h +++ b/soc/arm/microchip_mec/mec172x/soc.h @@ -62,6 +62,7 @@ #include "../common/soc_pins.h" #include "../common/soc_espi_channels.h" #include "../common/soc_espi_saf.h" +#include "../common/soc_espi_v2.h" #endif