@@ -1059,6 +1059,12 @@ S: Supported
F: drivers/gpu/drm/amd/display/dc/dml/
F: drivers/gpu/drm/amd/display/dc/dml2/
+AMD ESPI DRIVER
+M: Raju Rangoju<raju.rangoju@amd.com>
+L: linux-spi@vger.kernel.org
+S: Supported
+F: drivers/spi/espi-amd*
+
AMD FAM15H PROCESSOR POWER MONITORING DRIVER
M: Huang Rui <ray.huang@amd.com>
L: linux-hwmon@vger.kernel.org
@@ -1238,6 +1238,16 @@ config SPI_AMD
help
Enables SPI controller driver for AMD SoC.
+config SPI_AMD_ESPI
+ tristate "AMD eSPI controller"
+ depends on X86_64 || SPI_MASTER || COMPILE_TEST
+ help
+ AMD SOCs contains multiple SPI controllers, including a dedicated
+ eSPI controller. This config enables eSPI controller driver for
+ AMD SoC. AMD eSPI driver aims to comprehensive support for the
+ eSPI protocol, including initialization of eSPI controller
+ and slave devices and eSPI channel specific commands.
+
#
# Add new SPI master controllers in alphabetical order above this line
#
@@ -159,6 +159,8 @@ obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
obj-$(CONFIG_SPI_ZYNQ_QSPI) += spi-zynq-qspi.o
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
obj-$(CONFIG_SPI_AMD) += spi-amd.o
+obj-$(CONFIG_SPI_AMD_ESPI) += espi-amd.o
+espi-amd-objs := espi-amd-core.o espi-amd-dev.o
# SPI slave protocol handlers
obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
new file mode 100644
@@ -0,0 +1,883 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD eSPI controller driver
+ *
+ * Copyright (c) 2025, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Krishnamoorthi M <krishnamoorthi.m@amd.com>
+ * Akshata MukundShetty<akshata.mukundshetty@amd.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+
+#include "espi-amd.h"
+#include "espi-amd-slave.h"
+#include "espi-amd-err.h"
+
+#define ESPI_CH_READY_TIMEOUT_US 200
+
+/*
+ * In case of get configuration command, hdata0 contains bits[15:8] of the slave register address
+ * and hdata1 contains bits[7:0] of the slave register address.
+ */
+#define ESPI_CONFIGURATION_HDATA0(a) (((a) >> 8) & GENMASK(7, 0))
+#define ESPI_CONFIGURATION_HDATA1(a) ((a) & GENMASK(7, 0))
+#define ESPI_INTR_MASK GENMASK(31, 0)
+
+static int amd_espi_set_initial_config(struct amd_espi *amd_espi);
+
+static void amd_espi_clear_status(struct amd_espi *amd_espi)
+{
+ u32 status = readl(ESPI_BASE + AMD_ESPI_SLAVE0_INT_STS_REG);
+
+ if (status)
+ writel(status, (ESPI_BASE + AMD_ESPI_SLAVE0_INT_STS_REG));
+}
+
+void amd_espi_clr_all_intr(struct amd_espi *amd_espi)
+{
+ /* Set all bits to clear all the interrupt */
+ writel(ESPI_INTR_MASK, (ESPI_BASE + AMD_ESPI_SLAVE0_INT_STS_REG));
+}
+
+static int amd_espi_check_error_status(struct amd_espi *amd_espi, u32 status)
+{
+ int ret = CB_SUCCESS;
+
+ if (!(status & ESPI_DNCMD_INT)) {
+ ret = ESPI_DNCMD_INT;
+ dev_err(amd_espi->dev, "eSPI downstream command completion failure\n");
+ } else if (status & ESPI_BUS_ERR_INT) {
+ ret = ESPI_BUS_ERR_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_BUS_TIMING]);
+ } else if (status & ESPI_WAIT_TIMEOUT_INT) {
+ ret = ESPI_WAIT_TIMEOUT_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_BUS_WAIT_STATE]);
+ } else if (status & ESPI_CRC_ERR_INT) {
+ ret = ESPI_CRC_ERR_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_CRC]);
+ } else if (status & ESPI_NO_RESP_INT) {
+ ret = ESPI_NO_RESP_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_NO_RESP]);
+ } else if (status & ESPI_FATAL_ERR_INT) {
+ ret = ESPI_FATAL_ERR_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_FATAL_ERR]);
+ } else if (status & ESPI_NON_FATAL_ERR_INT) {
+ ret = ESPI_NON_FATAL_ERR_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_NON_FATAL_ERR]);
+ } else if (status & ESPI_INVALID_RESP_CODE_INT) {
+ ret = ESPI_INVALID_RESP_CODE_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_INVALID_RESP_CODE]);
+ } else if (status & ESPI_INVALID_CYCLE_TYPE_INT) {
+ ret = ESPI_INVALID_CYCLE_TYPE_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_INVALID_CYCLE_TYPE]);
+ } else if (status & ESPI_UNSUCCESS_CPL_RECV_INT) {
+ ret = ESPI_UNSUCCESS_CPL_RECV_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_UNSUCCESS_CPL_RECV]);
+ } else if (status & ESPI_ILLEGAL_RESP_TAG_INT) {
+ ret = ESPI_ILLEGAL_RESP_TAG_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_ILLEGAL_RESP_TAG]);
+ } else if (status & ESPI_ILLEGAL_RESP_LEN_INT) {
+ ret = ESPI_ILLEGAL_RESP_LEN_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_ILLEGAL_RESP_LEN]);
+ } else if (status & ESPI_RX_OOB_OVERFLOW_INT) {
+ ret = ESPI_RX_OOB_OVERFLOW_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_OOB_DATA_LEN]);
+ } else if (status & ESPI_RX_PC_MSG_OVERFLOW_INT) {
+ ret = ESPI_RX_PC_MSG_OVERFLOW_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_PC_MSG_DATA]);
+ } else if (status & ESPI_RX_FLASH_MSG_OVERFLOW_INT) {
+ ret = ESPI_RX_FLASH_MSG_OVERFLOW_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_FLASH_DATA_LEN]);
+ } else if (status & ESPI_PROTOCOL_ERR_INT) {
+ ret = ESPI_PROTOCOL_ERR_INT;
+ dev_err(amd_espi->dev, "%s\n", espi_error_codes[POS_PROTOCOL_ERR]);
+ }
+
+ return ret;
+}
+
+static int amd_espi_alloc_cmd_data(struct espi_txcmd *cmd)
+{
+ u32 size = 0;
+
+ switch (cmd->hdr0.cmd_type) {
+ case SET_CONFIGURATION:
+ case GET_CONFIGURATION:
+ case IN_BAND_RESET:
+ size = 1;
+ default:
+ break;
+ }
+
+ if (!size)
+ return -EOPNOTSUPP;
+
+ size = DATA_SIZE_ROUNDOFF_4(size);
+ cmd->data = kzalloc(size, GFP_KERNEL);
+
+ if (!cmd->data)
+ return -ENOMEM;
+
+ return CB_SUCCESS;
+}
+
+static void amd_espi_send_downstream_data(struct amd_espi *amd_espi, struct espi_txcmd *cmd)
+{
+ union espi_txdata *data = cmd->data;
+
+ /* Write first 4 bytes of data - common for all eSPI commands */
+ writel(data->val, (ESPI_BASE + AMD_ESPI_DS_DATA_REG0));
+}
+
+static void amd_espi_cmd_reg_dump(struct device *dev, struct espi_txcmd *cmd)
+{
+ dev_err(dev, "eSPI command packet:\n"
+ "Header-0: %08x\nHeader-1: %08x\n"
+ "Header-2: %08x\nData: %08x\n",
+ cmd->hdr0.val, cmd->hdr1.val, cmd->hdr2.val, cmd->data->val);
+}
+
+static int amd_espi_send_cmd(struct amd_espi *amd_espi, struct espi_txcmd *cmd)
+{
+ u32 status;
+ int ret;
+
+ /* Wait until HW is ready to send the command */
+ ret = readx_poll_timeout(ioread32, ESPI_BASE + AMD_ESPI_DS_HEADER_REG0,
+ status, (status & BIT(3)) == 0, ESPI_MSG_DELAY_MIN_US,
+ ESPI_RESP_MAX_TIMEOUT_US);
+ if (ret) {
+ dev_err(amd_espi->dev, "eSPI controller is not ready to accept commands\n");
+ amd_espi_cmd_reg_dump(amd_espi->dev, cmd);
+ return ESPI_DNCMD_INT;
+ }
+ amd_espi_clear_status(amd_espi);
+
+ writel(cmd->hdr1.val, (ESPI_BASE + AMD_ESPI_DS_HEADER_REG1));
+ writel(cmd->hdr2.val, (ESPI_BASE + AMD_ESPI_DS_HEADER_REG2));
+
+ amd_espi_send_downstream_data(amd_espi, cmd);
+
+ writel(cmd->hdr0.val, (ESPI_BASE + AMD_ESPI_DS_HEADER_REG0));
+
+ /* Wait until HW successfully sent the packet */
+ ret = readx_poll_timeout(ioread32, ESPI_BASE + AMD_ESPI_DS_HEADER_REG0,
+ status, (status & BIT(3)) == 0, ESPI_MSG_DELAY_MIN_US,
+ ESPI_RESP_MAX_TIMEOUT_US);
+ if (ret) {
+ dev_err(amd_espi->dev, "eSPI timed out waiting for command to complete\n");
+ amd_espi_cmd_reg_dump(amd_espi->dev, cmd);
+ return ESPI_DNCMD_INT;
+ }
+
+ /* Wait for DS command completion interrupt */
+ ret = readx_poll_timeout(ioread32, ESPI_BASE + AMD_ESPI_SLAVE0_INT_STS_REG,
+ status, status != 0, ESPI_MSG_DELAY_MIN_US,
+ ESPI_RESP_MAX_TIMEOUT_US);
+ if (ret) {
+ dev_err(amd_espi->dev, "eSPI command completion interrupt error (status = 0x%x)\n",
+ status);
+ amd_espi_cmd_reg_dump(amd_espi->dev, cmd);
+ return -ETIMEDOUT;
+ }
+
+ ret = amd_espi_check_error_status(amd_espi, status);
+ if (ret != CB_SUCCESS) {
+ amd_espi_cmd_reg_dump(amd_espi->dev, cmd);
+ dev_err(amd_espi->dev, "eSPI status register: 0x%x\n", status);
+ }
+
+ if (ret == ESPI_NO_RESP_INT || ret == ESPI_CRC_ERR_INT) {
+ dev_info(amd_espi->dev, "Triggering Inband-reset after CRC/NO_RESP Error\n");
+ if (amd_espi_inband_reset(amd_espi) != CB_SUCCESS)
+ dev_err(amd_espi->dev, "In-band reset failed!\n");
+ }
+
+ /* Clear downstream command completion interrupt after command completion */
+ writel(ESPI_DNCMD_INT, ESPI_BASE + AMD_ESPI_SLAVE0_INT_STS_REG);
+
+ return ret;
+}
+
+int amd_espi_inband_reset(struct amd_espi *amd_espi)
+{
+ int ret;
+ struct espi_txcmd cmd = {
+ .hdr0 = {
+ .cmd_type = IN_BAND_RESET,
+ .cmd_status = 1,
+ },
+ };
+
+ ret = amd_espi_alloc_cmd_data(&cmd);
+ if (ret)
+ return ret;
+
+ ret = amd_espi_send_cmd(amd_espi, &cmd);
+ if (ret != CB_SUCCESS) {
+ kfree(cmd.data);
+ return ret;
+ }
+
+ ret = amd_espi_set_initial_config(amd_espi);
+
+ kfree(cmd.data);
+ return ret;
+}
+
+int amd_espi_set_iomode(struct amd_espi *amd_espi, u32 *slave_config, u32 *ctrlr_config,
+ u8 io_mode)
+{
+ struct espi_master *master = amd_espi->master;
+
+ switch (io_mode) {
+ case IO_MODE_QUAD:
+ if (master->caps.io_mode_quad && espi_slave_supports_quad_io(*slave_config)) {
+ *ctrlr_config = ESPI_CNTRL_SET_IO_MODE(*ctrlr_config, IO_MODE_QUAD);
+ *slave_config = ESPI_SLAVE_SET_IO_MODE(*slave_config,
+ ESPI_SLAVE_IO_MODE_SEL_QUAD);
+ break;
+ }
+ dev_info(amd_espi->dev, "eSPI Quad IO not supported. Dropping to Dual mode.\n");
+ fallthrough;
+ case IO_MODE_DUAL:
+ if (master->caps.io_mode_dual && espi_slave_supports_dual_io(*slave_config)) {
+ *ctrlr_config = ESPI_CNTRL_SET_IO_MODE(*ctrlr_config, IO_MODE_DUAL);
+ *slave_config = ESPI_SLAVE_SET_IO_MODE(*slave_config,
+ ESPI_SLAVE_IO_MODE_SEL_DUAL);
+ break;
+ }
+ dev_info(amd_espi->dev, "eSPI Dual IO not supported. Dropping to Single mode.\n");
+ fallthrough;
+ case IO_MODE_SINGLE:
+ default:
+ if (master->caps.io_mode_single && espi_slave_supports_single_io(*slave_config)) {
+ *ctrlr_config = ESPI_CNTRL_SET_IO_MODE(*ctrlr_config, IO_MODE_SINGLE);
+ *slave_config = ESPI_SLAVE_SET_IO_MODE(*slave_config,
+ ESPI_SLAVE_IO_MODE_SEL_SINGLE);
+ } else {
+ dev_err(amd_espi->dev, "%s, eSPI IO mode not supported\n", __func__);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return CB_SUCCESS;
+}
+
+int amd_espi_set_freqmode(struct amd_espi *amd_espi, u32 *slave_config, u32 *ctrlr_config,
+ u8 op_freq)
+{
+ struct espi_master *master = amd_espi->master;
+
+ switch (op_freq) {
+ case SLAVE_OP_FREQ_66:
+ if (master->caps.op_freq_66 && espi_slave_supports_66_mhz(*slave_config)) {
+ *ctrlr_config = ESPI_CNTRL_SET_OP_FREQ(*ctrlr_config,
+ CNTRL_SLAVE0_OP_FREQ_66);
+ *slave_config = ESPI_SLAVE_SET_OP_FREQ(*slave_config,
+ ESPI_SLAVE_OP_FREQ_SEL_66_MHZ);
+ break;
+ }
+ dev_info(amd_espi->dev, "eSPI frequency 66 MHz not supported. Dropping to 33MHz.\n");
+ fallthrough;
+ case SLAVE_OP_FREQ_33:
+ if (master->caps.op_freq_33 && ((espi_slave_supports_66_mhz(*slave_config)) ||
+ (espi_slave_supports_33_mhz(*slave_config)))) {
+ *ctrlr_config = ESPI_CNTRL_SET_OP_FREQ(*ctrlr_config,
+ CNTRL_SLAVE0_OP_FREQ_33);
+ *slave_config = ESPI_SLAVE_SET_OP_FREQ(*slave_config,
+ ESPI_SLAVE_OP_FREQ_SEL_33_MHZ);
+ break;
+ }
+ dev_info(amd_espi->dev, "eSPI frequency 33 MHz not supported. Dropping to 16MHz.\n");
+ fallthrough;
+ case SLAVE_OP_FREQ_16:
+ default:
+ if (master->caps.op_freq_16 && ((espi_slave_supports_66_mhz(*slave_config)) ||
+ (espi_slave_supports_33_mhz(*slave_config)) ||
+ (espi_slave_supports_16_mhz(*slave_config)))) {
+ *ctrlr_config = ESPI_CNTRL_SET_OP_FREQ(*ctrlr_config,
+ CNTRL_SLAVE0_OP_FREQ_16);
+ *slave_config = ESPI_SLAVE_SET_OP_FREQ(*slave_config,
+ ESPI_SLAVE_OP_FREQ_SEL_16_MHZ);
+ } else {
+ dev_err(amd_espi->dev, "%s, eSPI frequency mode not supported\n", __func__);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return CB_SUCCESS;
+}
+
+static int amd_espi_get_config(struct amd_espi *amd_espi, u16 slave_reg_address, u32 *config)
+{
+ int ret;
+ struct espi_txcmd cmd = {
+ .hdr0 = {
+ .cmd_type = GET_CONFIGURATION,
+ .cmd_status = 1,
+ .hdata0 = ESPI_CONFIGURATION_HDATA0(slave_reg_address),
+ .hdata1 = ESPI_CONFIGURATION_HDATA1(slave_reg_address),
+ },
+ };
+
+ ret = amd_espi_alloc_cmd_data(&cmd);
+ if (ret)
+ return ret;
+
+ ret = amd_espi_send_cmd(amd_espi, &cmd);
+ if (ret != CB_SUCCESS) {
+ kfree(cmd.data);
+ return ret;
+ }
+
+ *config = readl(ESPI_BASE + AMD_ESPI_DS_HEADER_REG1);
+
+ kfree(cmd.data);
+ return CB_SUCCESS;
+}
+
+int amd_espi_get_channel_config(struct amd_espi *amd_espi)
+{
+ u32 chnl_config;
+ u32 ret = 0;
+
+ if (amd_espi_get_config(amd_espi, ESPI_SLAVE_PERIPH_CFG, &chnl_config) == CB_SUCCESS) {
+ if (chnl_config & ESPI_SLAVE_CHANNEL_ENABLE)
+ ret |= CHANNEL_MODE_PC;
+ }
+
+ if (amd_espi_get_config(amd_espi, ESPI_SLAVE_VW_CFG, &chnl_config) == CB_SUCCESS) {
+ if (chnl_config & ESPI_SLAVE_CHANNEL_ENABLE)
+ ret |= CHANNEL_MODE_VW;
+ }
+
+ return (ret == 0) ? -EOPNOTSUPP : ret;
+}
+
+int amd_espi_get_general_config(struct amd_espi *amd_espi, u32 *config)
+{
+ return amd_espi_get_config(amd_espi, ESPI_SLAVE_GENERAL_CAPS_CFG, config);
+}
+
+int amd_espi_set_config(struct amd_espi *amd_espi, u32 config, u16 slave_reg_address)
+{
+ int ret;
+ struct espi_txcmd cmd = {
+ .hdr0 = {
+ .cmd_type = SET_CONFIGURATION,
+ .cmd_status = 1,
+ .hdata0 = ESPI_CONFIGURATION_HDATA0(slave_reg_address),
+ .hdata1 = ESPI_CONFIGURATION_HDATA1(slave_reg_address),
+ },
+ .hdr1 = {
+ .val = config,
+ },
+ };
+
+ ret = amd_espi_alloc_cmd_data(&cmd);
+ if (ret)
+ return ret;
+
+ ret = amd_espi_send_cmd(amd_espi, &cmd);
+ kfree(cmd.data);
+
+ return ret;
+}
+
+int amd_espi_set_general_conf(struct amd_espi *amd_espi, struct config *dev)
+{
+ u32 ctrlr_config = readl(ESPI_BASE + AMD_ESPI_SLAVE0_CONFIG_REG);
+ u32 slave_config;
+ int status;
+
+ status = amd_espi_get_general_config(amd_espi, &slave_config);
+ if (status != CB_SUCCESS)
+ return status;
+
+ if (amd_espi->master->caps.alert_mode == 1) {
+ slave_config |= ESPI_SLAVE_ALERT_MODE_PIN;
+ ctrlr_config |= ESPI_CNTRL_SLAVE0_ALERT_MODE;
+ }
+
+ if (amd_espi->master->caps.crc_check_support == 1) {
+ slave_config |= ESPI_SLAVE_CRC_ENABLE;
+ ctrlr_config |= ESPI_CNTRL_SLAVE0_CRC_CHECK_EN;
+ }
+
+ status = amd_espi_set_iomode(amd_espi, &slave_config, &ctrlr_config, dev->io_mode);
+ if (status != CB_SUCCESS) {
+ dev_err(amd_espi->dev, "%s, IO mode not supported\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ status = amd_espi_set_freqmode(amd_espi, &slave_config, &ctrlr_config, dev->op_freq);
+ if (status != CB_SUCCESS) {
+ dev_err(amd_espi->dev, "%s, Operating frequency not supported\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ status = amd_espi_set_config(amd_espi, slave_config, ESPI_SLAVE_GENERAL_CAPS_CFG);
+ if (status != CB_SUCCESS)
+ return status;
+
+ writel(ctrlr_config, (ESPI_BASE + AMD_ESPI_SLAVE0_CONFIG_REG));
+
+ return CB_SUCCESS;
+}
+
+static int amd_espi_wait_channel_ready(struct amd_espi *amd_espi, u32 slave_reg_addr)
+{
+ u32 config;
+ int ret;
+
+ udelay(ESPI_CH_READY_TIMEOUT_US);
+ ret = amd_espi_get_config(amd_espi, slave_reg_addr, &config);
+ if (ret != CB_SUCCESS)
+ return ret;
+
+ if (!!(config & ESPI_SLAVE_CHANNEL_READY)) {
+ ret = CB_SUCCESS;
+ } else {
+ ret = -ETIMEDOUT;
+ dev_err(amd_espi->dev, "Channel is not ready after %d usec (slave addr: 0x%x)\n",
+ ESPI_CH_READY_TIMEOUT_US, slave_reg_addr);
+ }
+
+ return ret;
+}
+
+static void amd_espi_enable_ctrlr_channel(struct amd_espi *amd_espi, u32 channel_en)
+{
+ u32 reg = readl(ESPI_BASE + AMD_ESPI_SLAVE0_CONFIG_REG);
+
+ writel(reg | channel_en, (ESPI_BASE + AMD_ESPI_SLAVE0_CONFIG_REG));
+}
+
+static int amd_espi_set_channel_config(struct amd_espi *amd_espi, u32 slave_config,
+ u32 slave_reg_addr, u32 ctrlr_enable)
+{
+ int ret = amd_espi_set_config(amd_espi, slave_config, slave_reg_addr);
+
+ if (ret != CB_SUCCESS) {
+ dev_err(amd_espi->dev, "Channel configuration failed\n");
+ return ret;
+ }
+
+ /* Channel already enabled */
+ if (!(slave_config & ESPI_SLAVE_CHANNEL_ENABLE))
+ return CB_SUCCESS;
+
+ ret = amd_espi_wait_channel_ready(amd_espi, slave_reg_addr);
+ if (ret != CB_SUCCESS) {
+ dev_err(amd_espi->dev, "Channel is not ready\n");
+ return ret;
+ }
+
+ amd_espi_enable_ctrlr_channel(amd_espi, ctrlr_enable);
+
+ return CB_SUCCESS;
+}
+
+int amd_espi_setup_periph_channel(struct amd_espi *amd_espi, u32 slave_caps)
+{
+ /* Peripheral channel requires BME bit to be set when enabling */
+ const u32 slave_en_mask = ESPI_SLAVE_CHANNEL_ENABLE | ESPI_SLAVE_PERIPH_BUS_MASTER_ENABLE;
+ struct espi_master *master = amd_espi->master;
+ u32 slave_config;
+ int ret;
+
+ ret = amd_espi_get_config(amd_espi, ESPI_SLAVE_PERIPH_CFG, &slave_config);
+ if (ret != CB_SUCCESS)
+ return ret;
+
+ /* Check if PC is already enabled */
+ if (slave_config & ESPI_SLAVE_CHANNEL_ENABLE)
+ return CB_SUCCESS;
+
+ /*
+ * Peripheral channel is the only one which is enabled on reset. So, if the mainboard
+ * wants to disable it, set configuration to disable peripheral channel. It also
+ * requires that BME bit be cleared.
+ */
+ if (master->caps.periph_ch_en) {
+ if (!(slave_caps & ESPI_SLAVE_PERIPH_CH_SUPP)) {
+ dev_err(amd_espi->dev, "eSPI slave doesn't support periph channel!\n");
+ return -EOPNOTSUPP;
+ }
+ slave_config |= slave_en_mask;
+ } else {
+ slave_config &= ~slave_en_mask; /* If master does not support make it to 0 */
+ }
+
+ return amd_espi_set_channel_config(amd_espi, slave_config, ESPI_SLAVE_PERIPH_CFG,
+ CHANNEL_MODE_PC);
+}
+
+int amd_espi_setup_vw_channel(struct amd_espi *amd_espi, u32 slave_caps)
+{
+ struct espi_master *master = amd_espi->master;
+ u32 slave_vw_caps, slave_config;
+ int ret;
+
+ if (!master->caps.vw_ch_en) {
+ dev_err(amd_espi->dev, "Master doesn't support VW channel\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!(slave_caps & ESPI_SLAVE_VW_CH_SUPP)) {
+ dev_err(amd_espi->dev, "eSPI slave doesn't support VW channel\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = amd_espi_get_config(amd_espi, ESPI_SLAVE_VW_CFG, &slave_vw_caps);
+ if (ret != CB_SUCCESS)
+ return ret;
+
+ slave_config = slave_vw_caps | ESPI_SLAVE_CHANNEL_ENABLE;
+
+ /* Check if VW channel is already enabled */
+ if (slave_config & ESPI_SLAVE_CHANNEL_ENABLE)
+ return CB_SUCCESS;
+
+ return amd_espi_set_channel_config(amd_espi, slave_config, ESPI_SLAVE_VW_CFG,
+ CHANNEL_MODE_VW);
+}
+
+static int amd_espi_get_master_cap(struct amd_espi *amd_espi, struct espi_master *master)
+{
+ u32 master_cap_reg, info;
+
+ master_cap_reg = readl(ESPI_BASE + AMD_ESPI_MASTER_CAP_REG);
+
+ /* Supported channels by master */
+ if (master_cap_reg & BIT(0))
+ master->caps.flash_ch_en = 1;
+
+ if (master_cap_reg & BIT(1))
+ master->caps.oob_ch_en = 1;
+
+ if (master_cap_reg & BIT(2))
+ master->caps.vw_ch_en = 1;
+
+ if (master_cap_reg & BIT(3))
+ master->caps.periph_ch_en = 1;
+
+ /* eSPI version */
+ master->caps.espi_version = ESPI_GET_ESPI_VERSION(master_cap_reg);
+
+ /* Operating frequency supported by master */
+ info = ESPI_GET_FREQ(master_cap_reg);
+ switch (info) {
+ case CNTRL_OP_FREQ_66:
+ master->caps.op_freq_66 = 1;
+ fallthrough;
+ case CNTRL_OP_FREQ_33:
+ master->caps.op_freq_33 = 1;
+ fallthrough;
+ case CNTRL_OP_FREQ_16:
+ master->caps.op_freq_16 = 1;
+ break;
+ default:
+ dev_err(amd_espi->dev, "%s, operating frequency Error\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ /* IO mode supported by master */
+ info = ESPI_GET_IO_MODE(master_cap_reg);
+ switch (info) {
+ case IO_MODE_QUAD:
+ master->caps.io_mode_quad = 1;
+ fallthrough;
+ case IO_MODE_DUAL:
+ master->caps.io_mode_dual = 1;
+ fallthrough;
+ case IO_MODE_SINGLE:
+ master->caps.io_mode_single = 1;
+ break;
+ default:
+ dev_err(amd_espi->dev, "%s, IO Mode Error\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ /* Number of slaves */
+ master->caps.no_of_slaves = ESPI_GET_NO_OF_SLAVES(master_cap_reg);
+
+ /* PC channel max payload size */
+ master->caps.pc_ch_max_payload_size = ESPI_GET_MAX_PAYLOAD_PC(master_cap_reg);
+
+ /* Flash access channel max payload size */
+ master->caps.flash_ch_max_payload = ESPI_GET_MAX_PAYLOAD_FLASH(master_cap_reg);
+
+ /* OOB message channel maximum payload size */
+ master->caps.oob_ch_max_payload = ESPI_GET_MAX_PAYLOAD_OOB(master_cap_reg);
+
+ /* Maximum virtual wire count */
+ master->caps.vw_ch_max_count = ESPI_GET_MAX_PAYLOAD_VW(master_cap_reg);
+
+ /* Alert mode support by master */
+ master->caps.alert_mode = ESPI_GET_ALERT_MODE(master_cap_reg);
+
+ /* CRC support by master */
+ master->caps.crc_check_support = ESPI_GET_CRC_SUPPORT(master_cap_reg);
+
+ return CB_SUCCESS;
+}
+
+static void amd_espi_set_def_initial_config(struct espi_master *master, struct config *dev_conf)
+{
+ dev_conf->channel_mode = CHANNEL_MODE_PC;
+ dev_conf->io_mode = IO_MODE_SINGLE;
+ dev_conf->op_freq = SLAVE_OP_FREQ_16;
+}
+
+static void amd_espi_control_reg_init(struct amd_espi *amd_espi)
+{
+ u32 espi_version, reg_val;
+
+ /* Clear any existing active bits */
+ reg_val = readl(ESPI_BASE + AMD_ESPI_SLAVE0_RX_VW_REG);
+ writel((reg_val | ESPI_RXVW_CLR_MASK), (ESPI_BASE + AMD_ESPI_SLAVE0_RX_VW_REG));
+
+ /* Check eSPI version */
+ espi_version = readl(ESPI_BASE + AMD_ESPI_MASTER_CAP_REG);
+ espi_version = ESPI_GET_ESPI_VERSION(espi_version);
+
+ /*
+ * Watchdog enable and wait state control enable.
+ * Set wait state counter to 0x3F.
+ */
+ reg_val = readl(ESPI_BASE + AMD_ESPI_GLOBAL_CNTRL_REG0);
+ reg_val = reg_val | BIT(0) | BIT(1) |
+ (ESPI_WAIT_STATE_COUNTER << ESPI_WAIT_STATE_COUNTER_SHIFT);
+ writel(reg_val, (ESPI_BASE + AMD_ESPI_GLOBAL_CNTRL_REG0));
+
+ /*
+ * Set slave0 error interrupt enable[19:0]
+ * Register command interrupt enable[31:24]
+ */
+ reg_val = readl(ESPI_BASE + AMD_ESPI_SLAVE0_INT_EN_REG);
+ writel(reg_val | ESPI_ALL_ERR_INT | ESPI_ALL_REG_CMD_INT,
+ (ESPI_BASE + AMD_ESPI_SLAVE0_INT_EN_REG));
+
+ /* Set eSPI controller error interrupt mapping, default is SMI (1Fh) */
+ reg_val = readl(ESPI_BASE + AMD_ESPI_GLOBAL_CNTRL_REG1);
+ reg_val = ESPI_RGCMD_INT(amd_espi->irq, reg_val) |
+ ESPI_ERR_INT(ESPI_INT_SMI, reg_val);
+ writel(reg_val, (ESPI_BASE + AMD_ESPI_GLOBAL_CNTRL_REG1));
+
+ /*
+ * Set the IRQ mask bit and polarity. Unmask IRQ 0~23.
+ * Enable to configure the VW index/data register
+ */
+ reg_val = readl(ESPI_BASE + AMD_ESPI_SLAVE0_VW_MISC_CNTRL_REG);
+ reg_val &= ~(GENMASK(31, 8));
+ writel((reg_val | GENMASK(3, 0)), (ESPI_BASE + AMD_ESPI_SLAVE0_VW_MISC_CNTRL_REG));
+
+ /* eSPI bus master enable */
+ reg_val = readl(ESPI_BASE + AMD_ESPI_GLOBAL_CNTRL_REG1);
+ if (!(reg_val & ESPI_BUS_MASTER_EN)) {
+ reg_val |= ESPI_BUS_MASTER_EN | BIT(21);
+ writel(reg_val, (ESPI_BASE + AMD_ESPI_GLOBAL_CNTRL_REG1));
+ }
+}
+
+static int amd_espi_set_initial_config(struct amd_espi *amd_espi)
+{
+ struct espi_master *master = amd_espi->master;
+ u32 espi_initial_mode, reg_val;
+ struct config espi_dev_conf;
+ int ret;
+
+ /* Clearing all the interrupts */
+ amd_espi_clr_all_intr(amd_espi);
+
+ espi_initial_mode = readl(ESPI_BASE + AMD_ESPI_SLAVE0_CONFIG_REG);
+ espi_initial_mode |= ((CNTRL_SLAVE0_OP_FREQ_16 << ESPI_CNTRL_SLAVE0_FREQ_SHIFT) |
+ (IO_MODE_SINGLE << ESPI_CNTRL_SLAVE0_IO_SHIFT));
+
+ if (amd_espi->master->caps.alert_mode == 1)
+ espi_initial_mode |= ESPI_CNTRL_SLAVE0_ALERT_MODE;
+
+ if (amd_espi->master->caps.crc_check_support == 1)
+ espi_initial_mode |= ESPI_CNTRL_SLAVE0_CRC_CHECK_EN;
+
+ writel(espi_initial_mode, (ESPI_BASE + AMD_ESPI_SLAVE0_CONFIG_REG));
+
+ /* Unmask IRQ 0~23 */
+ reg_val = readl(ESPI_BASE + AMD_ESPI_SLAVE0_VW_MISC_CNTRL_REG);
+ reg_val &= ~(GENMASK(31, 8));
+ writel((reg_val | GENMASK(3, 0)), (ESPI_BASE + AMD_ESPI_SLAVE0_VW_MISC_CNTRL_REG));
+
+ amd_espi_set_def_initial_config(master, &espi_dev_conf);
+
+ ret = amd_espi_set_general_conf(amd_espi, &espi_dev_conf);
+ if (ret != CB_SUCCESS)
+ return ret;
+
+ amd_espi_clr_all_intr(amd_espi);
+
+ return CB_SUCCESS;
+}
+
+static int amd_espi_init_slave(struct amd_espi *amd_espi)
+{
+ struct espi_master *master = amd_espi->master;
+ u32 slave_caps, reg_val;
+ int ret;
+
+ ret = amd_espi_get_master_cap(amd_espi, master);
+ if (ret != CB_SUCCESS) {
+ dev_err(amd_espi->dev, "Get master capability failed\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = amd_espi_set_initial_config(amd_espi);
+ if (ret != CB_SUCCESS) {
+ dev_err(amd_espi->dev, "eSPI set initial config failed\n");
+ return ret;
+ }
+
+ ret = amd_espi_inband_reset(amd_espi);
+ if (ret != CB_SUCCESS) {
+ dev_err(amd_espi->dev, "In-band reset failed!\n");
+ return ret;
+ }
+
+ ret = amd_espi_get_general_config(amd_espi, &slave_caps);
+ if (ret != CB_SUCCESS)
+ return ret;
+
+ reg_val = readl(ESPI_BASE + AMD_ESPI_GLOBAL_CNTRL_REG1);
+ writel(reg_val | BIT(20), (ESPI_BASE + AMD_ESPI_GLOBAL_CNTRL_REG1));
+
+ if (amd_espi_setup_periph_channel(amd_espi, slave_caps) != CB_SUCCESS) {
+ dev_err(amd_espi->dev, "Peripheral channel setup failed\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (amd_espi_setup_vw_channel(amd_espi, slave_caps) != CB_SUCCESS) {
+ dev_err(amd_espi->dev, "Virtual wire channel setup failed\n");
+ return -EOPNOTSUPP;
+ }
+
+ return CB_SUCCESS;
+}
+
+static int amd_espi_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int amd_espi_resume(struct device *dev)
+{
+ struct amd_espi *amd_espi = dev_get_drvdata(dev);
+ int ret;
+
+ ret = amd_espi_set_initial_config(amd_espi);
+ if (ret != CB_SUCCESS)
+ dev_err(amd_espi->dev, "espi_set_initial_config in %s failed!\n", __func__);
+
+ return ret;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(amd_espi_pm_ops, amd_espi_suspend, amd_espi_resume);
+
+static int amd_espi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct config *espi_dev_conf;
+ struct amd_espi *amd_espi;
+ struct resource *res;
+ int ret;
+
+ amd_espi = devm_kzalloc(dev, sizeof(struct amd_espi), GFP_KERNEL);
+ if (!amd_espi)
+ return -ENOMEM;
+
+ amd_espi->master = devm_kzalloc(dev, sizeof(struct espi_master), GFP_KERNEL);
+ if (!amd_espi->master)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EOPNOTSUPP;
+
+ amd_espi->io_remap_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(amd_espi->io_remap_addr)) {
+ ret = PTR_ERR(amd_espi->io_remap_addr);
+ return dev_err_probe(dev, ret, "ioremap of eSPI registers failed\n");
+ }
+
+ ret = amd_espi_device_create(amd_espi, dev);
+ if (ret != 0)
+ return dev_err_probe(amd_espi->dev, ret, "device creation failed\n");
+
+ platform_set_drvdata(pdev, amd_espi);
+
+ /* Allocate mem for espi_device */
+ espi_dev_conf = devm_kzalloc(dev, sizeof(struct config), GFP_KERNEL);
+ if (!espi_dev_conf) {
+ ret = -ENOMEM;
+ goto espidev_free;
+ }
+
+ amd_espi->irq = platform_get_irq(pdev, 0);
+ if (amd_espi->irq < 0) {
+ ret = amd_espi->irq;
+ goto espidev_free;
+ }
+
+ amd_espi_control_reg_init(amd_espi);
+ ret = amd_espi_init_slave(amd_espi);
+ if (ret != CB_SUCCESS)
+ goto espidev_free;
+
+ dev_info(dev, "AMD ESPI device initialization completed\n");
+
+ return 0;
+
+espidev_free:
+ amd_espi_device_remove(amd_espi);
+ return ret;
+}
+
+static void amd_espi_remove(struct platform_device *pdev)
+{
+ struct amd_espi *amd_espi = platform_get_drvdata(pdev);
+
+ amd_espi_device_remove(amd_espi);
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id espi_acpi_match[] = {
+ { "AMDI0070", AMD_ESPI_V1 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, espi_acpi_match);
+#endif
+
+static struct platform_driver amd_espi_driver = {
+ .driver = {
+ .name = "amd_espi",
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(espi_acpi_match),
+ .pm = pm_sleep_ptr(&amd_espi_pm_ops),
+ },
+ .probe = amd_espi_probe,
+ .remove = amd_espi_remove,
+};
+
+module_platform_driver(amd_espi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD eSPI Controller Driver");
new file mode 100644
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD eSPI controller driver - device file operations
+ *
+ * Copyright (c) 2025, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Krishnamoorthi M <krishnamoorthi.m@amd.com>
+ * Akshata MukundShetty<akshata.mukundshetty@amd.com>
+ */
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "espi-amd.h"
+#include "espi-amd-slave.h"
+
+#define ESPI_DEV_MINORS 0
+#define N_ESPI_MINORS 1
+
+static LIST_HEAD(device_list);
+static DEFINE_MUTEX(device_list_lock);
+
+static struct class *amd_espi_dev_class;
+static struct cdev cdev;
+
+static int amd_espi_open(struct inode *inode, struct file *filp)
+{
+ struct amd_espi *espi;
+ int status = -ENXIO;
+
+ guard(mutex)(&device_list_lock);
+
+ list_for_each_entry(espi, &device_list, device_entry) {
+ if (espi->dev_minor == inode->i_rdev) {
+ status = 0;
+ break;
+ }
+ }
+ if (status)
+ goto err_dev_find;
+
+ espi->users++;
+ filp->private_data = espi;
+ nonseekable_open(inode, filp);
+
+err_dev_find:
+ return status;
+}
+
+static int amd_espi_release(struct inode *inode, struct file *filp)
+{
+ struct amd_espi *espi;
+
+ guard(mutex)(&device_list_lock);
+ espi = filp->private_data;
+ filp->private_data = NULL;
+ espi->users--;
+ if (!espi->users && !espi->dev)
+ kfree(espi);
+
+ return 0;
+}
+
+static const struct file_operations amd_espi_fops = {
+ .owner = THIS_MODULE,
+ .open = amd_espi_open,
+ .release = amd_espi_release,
+};
+
+int amd_espi_device_create(struct amd_espi *amd_espi, struct device *dev)
+{
+ int ret;
+
+ INIT_LIST_HEAD(&amd_espi->device_entry);
+ ret = alloc_chrdev_region(&amd_espi->dev_minor, 0, ESPI_DEV_MINORS, "amd_espi");
+ if (ret < 0) {
+ dev_err(amd_espi->dev, "Device numbers allocation failed: %d\n", ret);
+ return ret;
+ }
+
+ amd_espi_dev_class = class_create("amd_espi");
+ if (IS_ERR(amd_espi_dev_class)) {
+ dev_err(amd_espi->dev, "Class creation failed\n");
+ ret = PTR_ERR(amd_espi_dev_class);
+ goto espi_unregister_chrdev;
+ }
+
+ cdev_init(&cdev, &amd_espi_fops);
+ ret = cdev_add(&cdev, amd_espi->dev_minor, N_ESPI_MINORS);
+ if (ret)
+ goto espi_class_cleanup;
+
+ amd_espi->dev = dev;
+ dev = device_create(amd_espi_dev_class, NULL, amd_espi->dev_minor, &amd_espi, "amd_espi");
+ if (IS_ERR(dev)) {
+ dev_err(amd_espi->dev, "Device creation failed\n");
+ ret = PTR_ERR(dev);
+ goto espi_del_dev;
+ }
+
+ list_add(&amd_espi->device_entry, &device_list);
+
+ return 0;
+
+espi_del_dev:
+ cdev_del(&cdev);
+
+espi_class_cleanup:
+ class_destroy(amd_espi_dev_class);
+
+espi_unregister_chrdev:
+ unregister_chrdev_region(amd_espi->dev_minor, ESPI_DEV_MINORS);
+ return ret;
+}
+
+void amd_espi_device_remove(struct amd_espi *amd_espi)
+{
+ list_del(&amd_espi->device_entry);
+ device_destroy(amd_espi_dev_class, amd_espi->dev_minor);
+ cdev_del(&cdev);
+ class_destroy(amd_espi_dev_class);
+ unregister_chrdev_region(amd_espi->dev_minor, ESPI_DEV_MINORS);
+}
new file mode 100644
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * AMD eSPI controller driver error codes
+ *
+ * Copyright (c) 2025, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Krishnamoorthi M <krishnamoorthi.m@amd.com>
+ * Akshata MukundShetty<akshata.mukundshetty@amd.com>
+ */
+#ifndef AMD_ESPI_ERROR_H
+#define AMD_ESPI_ERROR_H
+
+/* Error code bit positions */
+#define POS_BUS_TIMING 0
+#define POS_BUS_WAIT_STATE 1
+#define POS_CRC 2
+#define POS_NO_RESP 4
+#define POS_FATAL_ERR 5
+#define POS_NON_FATAL_ERR 6
+#define POS_INVALID_RESP_CODE 7
+#define POS_INVALID_CYCLE_TYPE 8
+#define POS_UNSUCCESS_CPL_RECV 9
+#define POS_ILLEGAL_RESP_TAG 10
+#define POS_ILLEGAL_RESP_LEN 11
+#define POS_OOB_DATA_LEN 12
+#define POS_PC_MSG_DATA 13
+#define POS_FLASH_DATA_LEN 14
+#define POS_PROTOCOL_ERR 15
+
+/* Human-readable error strings */
+static char *espi_error_codes[] = {
+ "ERR 00: eSPI BUS TIMING ERROR",
+ "ERR 01: eSPI WAIT STATE TIMER TIMEOUT",
+ "ERR 02: eSPI CRC ERROR",
+ "",
+ "ERR 04: NO RESPONSE FROM SLAVE",
+ "ERR 05: FATAL_ERROR RESPONSE FROM SLAVE",
+ "ERR 06: NON_FATAL_ERROR RESPONSE FROM SLAVE",
+ "ERR 07: INVALID RESPONSE CODE RECEIVED",
+ "ERR 08: INVALID CYCLE TYPE RECEIVED",
+ "ERR 09: UNSUCCESSFUL COMPLETION PACKET",
+ "ERR 10: ILLEGAL RESPONSE TAG",
+ "ERR 11: ILLEGAL RESPONSE LENGTH",
+ "ERR 12: OOB PACKET DATA LENGTH ERROR",
+ "ERR 13: PC MESSAGE DATA LENGTH ERROR",
+ "ERR 14: FLASH PACKET DATA LENGTH ERROR",
+ "ERR 15: PROTOCOL ERROR",
+};
+#endif
new file mode 100644
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * AMD eSPI slave device specific macros
+ *
+ * Copyright (c) 2025, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Krishnamoorthi M <krishnamoorthi.m@amd.com>
+ * Akshata MukundShetty<akshata.mukundshetty@amd.com>
+ */
+#ifndef AMD_ESPI_SLAVE_H
+#define AMD_ESPI_SLAVE_H
+
+#define ESPI_SLAVE_GENERAL_CAPS_CFG 0x08
+
+/* eSPI slave channel support */
+#define ESPI_SLAVE_PERIPH_CH_SUPP BIT(0)
+#define ESPI_SLAVE_VW_CH_SUPP BIT(1)
+#define ESPI_SLAVE_OOB_CH_SUPP BIT(2)
+#define ESPI_SLAVE_FLASH_CH_SUPP BIT(3)
+
+/* eSPI slave frequency support */
+#define ESPI_SLAVE_OP_FREQ_SUPP_SHIFT 16
+#define ESPI_SLAVE_OP_FREQ_SUPP_MASK GENMASK(18, 16)
+#define ESPI_SLAVE_OP_FREQ_SUPP_VAL(x) ((x) << ESPI_SLAVE_OP_FREQ_SUPP_SHIFT)
+#define ESPI_SLAVE_OP_FREQ_SUPP_16_MHZ ESPI_SLAVE_OP_FREQ_SUPP_VAL(0)
+#define ESPI_SLAVE_OP_FREQ_SUPP_33_MHZ ESPI_SLAVE_OP_FREQ_SUPP_VAL(2)
+#define ESPI_SLAVE_OP_FREQ_SUPP_66_MHZ ESPI_SLAVE_OP_FREQ_SUPP_VAL(4)
+
+/* eSPI slave frequency selection */
+#define ESPI_SLAVE_OP_FREQ_SEL_SHIFT 20
+#define ESPI_SLAVE_OP_FREQ_SEL_MASK ~GENMASK(22, 20)
+#define ESPI_SLAVE_OP_FREQ_SEL_VAL(x) ((x) << ESPI_SLAVE_OP_FREQ_SEL_SHIFT)
+#define ESPI_SLAVE_OP_FREQ_SEL_16_MHZ ESPI_SLAVE_OP_FREQ_SEL_VAL(0)
+#define ESPI_SLAVE_OP_FREQ_SEL_33_MHZ ESPI_SLAVE_OP_FREQ_SEL_VAL(2)
+#define ESPI_SLAVE_OP_FREQ_SEL_66_MHZ ESPI_SLAVE_OP_FREQ_SEL_VAL(4)
+#define ESPI_SLAVE_SET_OP_FREQ(conf, freq) (((conf) & ESPI_SLAVE_OP_FREQ_SEL_MASK) | (freq))
+
+/* eSPI slave IO mode support */
+#define ESPI_SLAVE_IO_MODE_SUPP_SHIFT 24
+#define ESPI_SLAVE_IO_MODE_SUPP_MASK GENMASK(25, 24)
+#define ESPI_SLAVE_IO_MODE_SUPP_VAL(x) ((x) << ESPI_SLAVE_IO_MODE_SUPP_SHIFT)
+#define ESPI_SLAVE_IO_MODE_SUPP_SINGLE_ONLY ESPI_SLAVE_IO_MODE_SUPP_VAL(0)
+#define ESPI_SLAVE_IO_MODE_SUPP_SINGLE_DUAL ESPI_SLAVE_IO_MODE_SUPP_VAL(1)
+#define ESPI_SLAVE_IO_MODE_SUPP_SINGLE_QUAD ESPI_SLAVE_IO_MODE_SUPP_VAL(2)
+#define ESPI_SLAVE_IO_MODE_SUPP_SINGLE_DUAL_QUAD ESPI_SLAVE_IO_MODE_SUPP_VAL(3)
+
+/* eSPI slave IO mode selection */
+#define ESPI_SLAVE_IO_MODE_SEL_SHIFT 26
+#define ESPI_SLAVE_IO_MODE_SEL_MASK ~GENMASK(27, 26)
+#define ESPI_SLAVE_IO_MODE_SEL_VAL(x) ((x) << ESPI_SLAVE_IO_MODE_SEL_SHIFT)
+#define ESPI_SLAVE_IO_MODE_SEL_SINGLE ESPI_SLAVE_IO_MODE_SEL_VAL(0)
+#define ESPI_SLAVE_IO_MODE_SEL_DUAL ESPI_SLAVE_IO_MODE_SEL_VAL(1)
+#define ESPI_SLAVE_IO_MODE_SEL_QUAD ESPI_SLAVE_IO_MODE_SEL_VAL(2)
+#define ESPI_SLAVE_SET_IO_MODE(conf, mode) (((conf) & ESPI_SLAVE_IO_MODE_SEL_MASK) | (mode))
+
+#define ESPI_SLAVE_ALERT_MODE_PIN BIT(28)
+#define ESPI_SLAVE_CRC_ENABLE BIT(31)
+
+/* eSPI slave channel capabilities and configuration registers */
+#define ESPI_SLAVE_PERIPH_CFG 0x10
+#define ESPI_SLAVE_VW_CFG 0x20
+#define ESPI_SLAVE_OOB_CFG 0x30
+#define ESPI_SLAVE_FLASH_CFG 0x40
+
+#define ESPI_SLAVE_CHANNEL_ENABLE BIT(0)
+#define ESPI_SLAVE_CHANNEL_READY BIT(1)
+#define ESPI_SLAVE_PERIPH_BUS_MASTER_ENABLE BIT(2)
+
+static inline bool espi_slave_supports_quad_io(u32 caps)
+{
+ u32 mode = caps & ESPI_SLAVE_IO_MODE_SUPP_MASK;
+
+ return (mode == ESPI_SLAVE_IO_MODE_SUPP_SINGLE_QUAD) ||
+ (mode == ESPI_SLAVE_IO_MODE_SUPP_SINGLE_DUAL_QUAD);
+}
+
+static inline bool espi_slave_supports_dual_io(u32 caps)
+{
+ u32 mode = caps & ESPI_SLAVE_IO_MODE_SUPP_MASK;
+
+ return (mode == ESPI_SLAVE_IO_MODE_SUPP_SINGLE_DUAL) ||
+ (mode == ESPI_SLAVE_IO_MODE_SUPP_SINGLE_DUAL_QUAD);
+}
+
+static inline bool espi_slave_supports_single_io(u32 caps)
+{
+ /* Single IO mode is supported by default */
+ return true;
+}
+
+static inline bool espi_slave_supports_66_mhz(u32 caps)
+{
+ u32 freq = caps & ESPI_SLAVE_OP_FREQ_SUPP_MASK;
+ return freq == ESPI_SLAVE_OP_FREQ_SUPP_66_MHZ;
+}
+
+static inline bool espi_slave_supports_33_mhz(u32 caps)
+{
+ u32 freq = caps & ESPI_SLAVE_OP_FREQ_SUPP_MASK;
+ return freq == ESPI_SLAVE_OP_FREQ_SUPP_33_MHZ;
+}
+
+static inline bool espi_slave_supports_16_mhz(u32 caps)
+{
+ u32 freq = caps & ESPI_SLAVE_OP_FREQ_SUPP_MASK;
+ return freq == ESPI_SLAVE_OP_FREQ_SUPP_16_MHZ;
+}
+#endif
new file mode 100644
@@ -0,0 +1,291 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * AMD eSPI general macros and structures
+ *
+ * Copyright (c) 2025, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Krishnamoorthi M <krishnamoorthi.m@amd.com>
+ * Akshata MukundShetty<akshata.mukundshetty@amd.com>
+ */
+#ifndef AMD_ESPI_H
+#define AMD_ESPI_H
+
+#include <linux/ioctl.h>
+
+#define CB_SUCCESS 0
+#define DATA_SIZE_ROUNDOFF_4(NUM) (((NUM) + 3) & (~0x03u))
+
+/* Timeouts and delay */
+#define ESPI_MSG_DELAY_MIN_US 50
+#define ESPI_RESP_MAX_TIMEOUT_US 200000 /* 200 ms */
+
+/* Downstream transmit registers */
+#define AMD_ESPI_DS_HEADER_REG0 0x00
+#define AMD_ESPI_DS_HEADER_REG1 0x04
+#define AMD_ESPI_DS_HEADER_REG2 0x08
+#define AMD_ESPI_DS_DATA_REG0 0x0C
+
+/* Master capability and control registers */
+#define AMD_ESPI_MASTER_CAP_REG 0x2C
+#define AMD_ESPI_GLOBAL_CNTRL_REG0 0x30
+#define AMD_ESPI_GLOBAL_CNTRL_REG1 0x34
+#define AMD_ESPI_MISC_CNTRL_REG0 0x38
+#define AMD_ESPI_MISC_CNTRL_REG1 0x3c
+
+/* Slave-0 configuration and interrupt registers */
+#define AMD_ESPI_SLAVE0_CONFIG_REG 0x68
+#define AMD_ESPI_SLAVE0_INT_EN_REG 0x6C
+#define AMD_ESPI_SLAVE0_INT_STS_REG 0x70
+#define ESPI_CNTRL_SLAVE0_ALERT_MODE BIT(30)
+#define ESPI_CNTRL_SLAVE0_CRC_CHECK_EN BIT(31)
+
+/* Slave-0 Virtual wire registers */
+#define AMD_ESPI_SLAVE0_RX_VW_REG 0x9C
+#define AMD_ESPI_SLAVE0_VW_MISC_CNTRL_REG 0xA8
+
+/* Slave-0 interrupt enable and status bits */
+#define ESPI_BUS_ERR_INT BIT(0)
+#define ESPI_WAIT_TIMEOUT_INT BIT(1)
+#define ESPI_CRC_ERR_INT BIT(2)
+#define ESPI_NO_RESP_INT BIT(4)
+#define ESPI_FATAL_ERR_INT BIT(5)
+#define ESPI_NON_FATAL_ERR_INT BIT(6)
+#define ESPI_INVALID_RESP_CODE_INT BIT(7)
+#define ESPI_INVALID_CYCLE_TYPE_INT BIT(8)
+#define ESPI_UNSUCCESS_CPL_RECV_INT BIT(9)
+#define ESPI_ILLEGAL_RESP_TAG_INT BIT(10)
+#define ESPI_ILLEGAL_RESP_LEN_INT BIT(11)
+#define ESPI_RX_OOB_OVERFLOW_INT BIT(12)
+#define ESPI_RX_PC_MSG_OVERFLOW_INT BIT(13)
+#define ESPI_RX_FLASH_MSG_OVERFLOW_INT BIT(14)
+#define ESPI_PROTOCOL_ERR_INT BIT(15)
+#define ESPI_DNCMD_INT BIT(28)
+#define ESPI_RXMSG_INT BIT(29)
+#define ESPI_ALL_ERR_INT GENMASK(19, 0)
+#define ESPI_ALL_REG_CMD_INT GENMASK(31, 24)
+
+/* eSPI global control register configurations */
+#define ESPI_BUS_MASTER_EN BIT(1)
+#define ESPI_WAIT_STATE_COUNTER 0x3F
+#define ESPI_WAIT_STATE_COUNTER_SHIFT 23
+#define ESPI_ERR_INT_MAP_SHIFT 8
+#define ESPI_ERR_INT(irq, val) ((u32)(((val) & ~(GENMASK(12, 8))) | \
+ ((irq) << ESPI_ERR_INT_MAP_SHIFT)))
+#define ESPI_INT_SMI 0x1f
+#define ESPI_RGCMD_INT_MAP_SHIFT 13
+#define ESPI_RGCMD_INT(irq, val) ((u32)(((val) & ~(GENMASK(17, 13))) | \
+ ((irq) << ESPI_RGCMD_INT_MAP_SHIFT)))
+#define ESPI_RXVW_CLR_MASK 0xFFFF6F00
+
+/* eSPI master capability register values */
+#define ESPI_GET_ESPI_VERSION(val) (((val) & GENMASK(6, 4)) >> 4)
+#define ESPI_GET_MAX_PAYLOAD_FLASH(val) (((val) & GENMASK(9, 7)) >> 7)
+#define ESPI_GET_MAX_PAYLOAD_OOB(val) (((val) & GENMASK(12, 10)) >> 10)
+#define ESPI_GET_MAX_PAYLOAD_VW(val) (((val) & GENMASK(18, 13)) >> 13)
+#define ESPI_GET_MAX_PAYLOAD_PC(val) (((val) & GENMASK(21, 19)) >> 19)
+#define ESPI_GET_NO_OF_SLAVES(val) (((val) & GENMASK(24, 22)) >> 22)
+#define ESPI_GET_FREQ(val) (((val) & GENMASK(27, 25)) >> 25)
+#define ESPI_GET_IO_MODE(val) (((val) & GENMASK(29, 28)) >> 28)
+#define ESPI_GET_ALERT_MODE(val) (((val) & BIT(30)) >> 30)
+#define ESPI_GET_CRC_SUPPORT(val) (((val) & BIT(31)) >> 31)
+
+/* Channel modes */
+#define CHANNEL_MODE_FLASH BIT(0)
+#define CHANNEL_MODE_OOB BIT(1)
+#define CHANNEL_MODE_VW BIT(2)
+#define CHANNEL_MODE_PC BIT(3)
+#define CHAN_NOT_ENABLED BIT(4)
+
+/*
+ * Controller IO MODE encoding values
+ * 00 - Single I/O
+ * 01 - Dual I/O, Single I/O
+ * 10 - Quad I/O, Dual I/O, Single I/O
+ */
+#define IO_MODE_SINGLE 0x0
+#define IO_MODE_DUAL 0x1
+#define IO_MODE_QUAD 0x2
+#define ESPI_CNTRL_SLAVE0_IO_SHIFT 28
+#define ESPI_CNTRL_IO_MODE_MASK ~GENMASK(29, 28)
+#define ESPI_CNTRL_SET_IO_MODE(conf, mode) (((conf) & ESPI_CNTRL_IO_MODE_MASK) |\
+ ((mode) << ESPI_CNTRL_SLAVE0_IO_SHIFT))
+
+/*
+ * Controller operating support frequency values
+ * 000 - 16.7 MHz
+ * 001 - 16.7 MHz, 33 MHz
+ * 011 - 16.7 MHz, 33 MHz, 66 MHz
+ */
+#define CNTRL_OP_FREQ_16 0x0
+#define CNTRL_OP_FREQ_33 0x1
+#define CNTRL_OP_FREQ_66 0x3
+
+/* Slave0 operating frequency values */
+#define SLAVE_OP_FREQ_16 0x0
+#define SLAVE_OP_FREQ_33 0x2
+#define SLAVE_OP_FREQ_66 0x4
+
+/* Slave0 operating frequency values in controller side */
+#define CNTRL_SLAVE0_OP_FREQ_16 0x0
+#define CNTRL_SLAVE0_OP_FREQ_33 0x1
+#define CNTRL_SLAVE0_OP_FREQ_66 0x2
+
+#define ESPI_CNTRL_SLAVE0_FREQ_SHIFT 25
+#define ESPI_CNTRL_OP_MODE_MASK ~GENMASK(27, 25)
+#define ESPI_CNTRL_SET_OP_FREQ(conf, freq) (((conf) & ESPI_CNTRL_OP_MODE_MASK) |\
+ ((freq) << ESPI_CNTRL_SLAVE0_FREQ_SHIFT))
+
+#define ESPI_BASE ((u8 __iomem *)amd_espi->io_remap_addr)
+
+/*
+ * enum amd_espi_versions - eSPI controller versions
+ * @AMD_ESPI_V1: AMDI0070 hardware version
+ */
+enum amd_espi_versions {
+ AMD_ESPI_V1 = 1,
+};
+
+/* eSPI commands */
+enum espi_cmd_type {
+ SET_CONFIGURATION,
+ GET_CONFIGURATION,
+ IN_BAND_RESET,
+ PERIPHERAL_CHNL = 4,
+ VW_CHNL,
+ OOB_CHNL,
+ FLASH_CHNL,
+};
+
+struct amd_espi {
+ void __iomem *io_remap_addr;
+ struct device *dev;
+ struct espi_master *master;
+ struct list_head device_entry;
+ dev_t dev_minor;
+ enum amd_espi_versions version;
+ unsigned int users;
+ int irq;
+ void (*suspend)(struct amd_espi *amd_espi);
+ void (*resume)(struct amd_espi *amd_espi);
+};
+
+struct master_caps {
+ /* Channel support by master, bits [3:0] */
+ u32 periph_ch_en:1;
+ u32 vw_ch_en:1;
+ u32 oob_ch_en:1;
+ u32 flash_ch_en:1;
+
+ /* eSPI version, bits [6:4] */
+ u32 espi_version:3;
+
+ /* Flash access channel max supported payload, bits[9:7] */
+ u32 flash_ch_max_payload:3;
+
+ /* OOB message channel max supported payload, bits[10:12] */
+ u32 oob_ch_max_payload:3;
+
+ /* Operating maximum virtual wire count, bits[18:13] */
+ u32 vw_ch_max_count:6;
+
+ /* Peripheral channel max supported payload, bits[21:19] */
+ u32 pc_ch_max_payload_size:3;
+
+ /* Number of slaves supported by master bits, [24:22] */
+ u32 no_of_slaves:3;
+
+ /* Operating frequencies, bits [27:25] */
+ u32 op_freq_66:1;
+ u32 op_freq_33:1;
+ u32 op_freq_16:1;
+
+ /* IO modes bits [29:28] */
+ u32 io_mode_single:1;
+ u32 io_mode_dual:1;
+ u32 io_mode_quad:1;
+
+ /* Alert mode support by master, bit[30] */
+ u32 alert_mode:1;
+
+ /* CRC checking support by master, bit[31] */
+ u32 crc_check_support:1;
+} __packed;
+
+struct espi_master {
+ struct device *dev;
+ struct master_caps caps;
+};
+
+struct config {
+ u8 io_mode;
+ u8 channel_mode;
+ u8 op_freq;
+};
+
+/* TX Header and data packet */
+union espi_txhdr0 {
+ u32 val;
+ struct {
+ u32 cmd_type:3;
+ u32 cmd_status:1;
+ u32 slave_sel:2;
+ u32 rsvd:2;
+ u32 hdata0:8;
+ u32 hdata1:8;
+ u32 hdata2:8;
+ };
+};
+
+union espi_txhdr1 {
+ u32 val;
+ struct {
+ u32 hdata3:8;
+ u32 hdata4:8;
+ u32 hdata5:8;
+ u32 hdata6:8;
+ };
+};
+
+union espi_txhdr2 {
+ u32 val;
+ struct {
+ u32 hdata7:8;
+ u32 rsvd:24;
+ };
+};
+
+union espi_txdata {
+ u32 val;
+ struct {
+ u32 dbyte0:8;
+ u32 dbyte1:8;
+ u32 dbyte2:8;
+ u32 dbyte3:8;
+ };
+};
+
+struct espi_txcmd {
+ union espi_txhdr0 hdr0;
+ union espi_txhdr1 hdr1;
+ union espi_txhdr2 hdr2;
+ union espi_txdata *data;
+ u32 expected_status_codes;
+};
+
+/* Function prototypes */
+int amd_espi_device_create(struct amd_espi *amd_espi, struct device *dev);
+void amd_espi_device_remove(struct amd_espi *amd_espi);
+int amd_espi_inband_reset(struct amd_espi *amd_espi);
+int amd_espi_get_general_config(struct amd_espi *amd_espi, u32 *config);
+int amd_espi_set_general_conf(struct amd_espi *amd_espi, struct config *dev);
+int amd_espi_set_config(struct amd_espi *amd_espi, u32 config, u16 slave_reg_address);
+int amd_espi_set_iomode(struct amd_espi *amd_espi, u32 *slave_config, u32 *ctrlr_config,
+ u8 io_mode);
+int amd_espi_set_freqmode(struct amd_espi *amd_espi, u32 *slave_config, u32 *ctrlr_config,
+ u8 op_freq);
+int amd_espi_get_channel_config(struct amd_espi *amd_espi);
+int amd_espi_setup_periph_channel(struct amd_espi *amd_espi, u32 slave_caps);
+int amd_espi_setup_vw_channel(struct amd_espi *amd_espi, u32 slave_caps);
+void amd_espi_clr_all_intr(struct amd_espi *amd_espi);
+#endif