diff mbox series

[01/10] spi: espi_amd: Add AMD eSPI controller driver support

Message ID 20250313183440.261872-2-Raju.Rangoju@amd.com
State New
Headers show
Series [01/10] spi: espi_amd: Add AMD eSPI controller driver support | expand

Commit Message

Raju Rangoju March 13, 2025, 6:34 p.m. UTC
This patch enables core eSPI controller initialization in AMD embedded
platforms, supporting communication with eSPI slave devices through the
device file entry. Currently, the eSPI controller in AMD embedded platforms
has only one chip select line enabled, which is connected to the Embedded
Controller (EC).

Co-developed-by: Krishnamoorthi M <krishnamoorthi.m@amd.com>
Signed-off-by: Krishnamoorthi M <krishnamoorthi.m@amd.com>
Co-developed-by: Akshata MukundShetty <akshata.mukundshetty@amd.com>
Signed-off-by: Akshata MukundShetty <akshata.mukundshetty@amd.com>
Signed-off-by: Raju Rangoju <Raju.Rangoju@amd.com>
---
 MAINTAINERS                  |   6 +
 drivers/spi/Kconfig          |  10 +
 drivers/spi/Makefile         |   2 +
 drivers/spi/espi-amd-core.c  | 883 +++++++++++++++++++++++++++++++++++
 drivers/spi/espi-amd-dev.c   | 128 +++++
 drivers/spi/espi-amd-err.h   |  50 ++
 drivers/spi/espi-amd-slave.h | 109 +++++
 drivers/spi/espi-amd.h       | 291 ++++++++++++
 8 files changed, 1479 insertions(+)
 create mode 100644 drivers/spi/espi-amd-core.c
 create mode 100644 drivers/spi/espi-amd-dev.c
 create mode 100644 drivers/spi/espi-amd-err.h
 create mode 100644 drivers/spi/espi-amd-slave.h
 create mode 100644 drivers/spi/espi-amd.h
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 9f052994b7d4..c102dcce5892 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index ea8a31032927..72b950f3eccb 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -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
 #
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9db7554c1864..0dec8ad30967 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -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
diff --git a/drivers/spi/espi-amd-core.c b/drivers/spi/espi-amd-core.c
new file mode 100644
index 000000000000..72a625b8b16d
--- /dev/null
+++ b/drivers/spi/espi-amd-core.c
@@ -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");
diff --git a/drivers/spi/espi-amd-dev.c b/drivers/spi/espi-amd-dev.c
new file mode 100644
index 000000000000..4e46c30d3405
--- /dev/null
+++ b/drivers/spi/espi-amd-dev.c
@@ -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);
+}
diff --git a/drivers/spi/espi-amd-err.h b/drivers/spi/espi-amd-err.h
new file mode 100644
index 000000000000..d2d3967a3c6d
--- /dev/null
+++ b/drivers/spi/espi-amd-err.h
@@ -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
diff --git a/drivers/spi/espi-amd-slave.h b/drivers/spi/espi-amd-slave.h
new file mode 100644
index 000000000000..07aaad4f28aa
--- /dev/null
+++ b/drivers/spi/espi-amd-slave.h
@@ -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
diff --git a/drivers/spi/espi-amd.h b/drivers/spi/espi-amd.h
new file mode 100644
index 000000000000..57b156fb0a05
--- /dev/null
+++ b/drivers/spi/espi-amd.h
@@ -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