[RFC PATCH 1/4] espi: add core bus framework

Krishnamoorthi M <[email protected]> Tue, 4 Aug 2026 17:22:56 +0530
Newsgroups org.ozlabs.lists.linux-aspeed,dev.linux.lists.chrome-platform,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-spi,org.ozlabs.lists.openbmc
Message-ID <[email protected]>
Add the core of a new Enhanced Serial Peripheral Interface (eSPI) bus
subsystem under drivers/espi/.

eSPI is a capability-negotiated, message-oriented link: the controller
and slave negotiate I/O mode, clock and channel support at runtime and
exchange packetised transactions over four logically independent channels
over a shared physical link (peripheral, virtual wire, out-of-band and
flash). This does not fit the synchronous, transfer-oriented SPI model,
so eSPI is modelled as its own bus type rather than an extension of SPI.

The core provides:
  - espi_bus_type, binding devices to drivers by ID table or ACPI match
    and emitting a MODALIAS uevent;
  - controller lifecycle helpers, with each controller registered as a
    device and assigned a bus number from an allocating XArray;
  - read-only sysfs attributes for the controller capabilities;
  - a channel API that serialises access with ctrl->lock, dispatches to
    the controller ops, and returns -EOPNOTSUPP for unimplemented ops.

Co-developed-by: Akshata MukundShetty <[email protected]>
Signed-off-by: Akshata MukundShetty <[email protected]>
Signed-off-by: Krishnamoorthi M <[email protected]>
---
 drivers/Kconfig           |   2 +
 drivers/Makefile          |   1 +
 drivers/espi/Kconfig      |  19 ++
 drivers/espi/Makefile     |   2 +
 drivers/espi/espi-core.c  | 493 ++++++++++++++++++++++++++++++++++++++
 include/linux/espi/espi.h | 345 ++++++++++++++++++++++++++
 6 files changed, 862 insertions(+)
 create mode 100644 drivers/espi/Kconfig
 create mode 100644 drivers/espi/Makefile
 create mode 100644 drivers/espi/espi-core.c
 create mode 100644 include/linux/espi/espi.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index f2bed2ddeb66..d7661432ce7c 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -73,6 +73,8 @@ source "drivers/i3c/Kconfig"
 
 source "drivers/spi/Kconfig"
 
+source "drivers/espi/Kconfig"
+
 source "drivers/spmi/Kconfig"
 
 source "drivers/hsi/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 0841ea851847..ff3d42dd14ba 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_ATA)		+= ata/
 obj-$(CONFIG_TARGET_CORE)	+= target/
 obj-$(CONFIG_MTD)		+= mtd/
 obj-$(CONFIG_SPI)		+= spi/
+obj-$(CONFIG_ESPI)		+= espi/
 obj-$(CONFIG_SPMI)		+= spmi/
 obj-$(CONFIG_HSI)		+= hsi/
 obj-$(CONFIG_SLIMBUS)		+= slimbus/
diff --git a/drivers/espi/Kconfig b/drivers/espi/Kconfig
new file mode 100644
index 000000000000..4411d8336e82
--- /dev/null
+++ b/drivers/espi/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# eSPI (Enhanced Serial Peripheral Interface) bus configuration
+#
+
+menuconfig ESPI
+	bool "eSPI (Enhanced Serial Peripheral Interface) bus support"
+	help
+	  Enhanced Serial Peripheral Interface (eSPI) bus framework.
+
+	  eSPI is a serial bus specification by Intel that replaces the
+	  legacy Low Pin Count (LPC) bus. It connects a host controller to
+	  one or more slave devices (for example an EC or BMC) over a
+	  low-pin-count link clocked up to 66 MHz with single, dual or quad
+	  I/O, and carries traffic on four logically independent channels
+	  over a shared physical link: Peripheral (I/O and memory), Virtual
+	  Wire, OOB (Out-of-Band) messaging, and Flash Access.
+
+	  If unsure, say N.
diff --git a/drivers/espi/Makefile b/drivers/espi/Makefile
new file mode 100644
index 000000000000..72712fcb0ada
--- /dev/null
+++ b/drivers/espi/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+obj-$(CONFIG_ESPI)		+= espi-core.o
diff --git a/drivers/espi/espi-core.c b/drivers/espi/espi-core.c
new file mode 100644
index 000000000000..35481f1c1858
--- /dev/null
+++ b/drivers/espi/espi-core.c
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * eSPI (Enhanced Serial Peripheral Interface) core framework
+ *
+ * Copyright (c) 2026, Advanced Micro Devices, Inc.
+ */
+
+#define pr_fmt(fmt)	"espi: " fmt
+
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/xarray.h>
+#include <linux/espi/espi.h>
+
+static DEFINE_XARRAY_ALLOC(espi_controllers);
+
+static int espi_bus_match(struct device *dev, const struct device_driver *drv)
+{
+	const struct espi_device *edev = to_espi_device(dev);
+	const struct espi_driver *edrv = to_espi_driver(drv);
+
+	if (edrv->id_table) {
+		const struct espi_device_id *id = edrv->id_table;
+
+		while (id->name[0]) {
+			if (!strcmp(edev->modalias, id->name))
+				return 1;
+			id++;
+		}
+	}
+	if (acpi_driver_match_device(dev, drv))
+		return 1;
+
+	return 0;
+}
+
+static int espi_bus_uevent(const struct device *dev, struct kobj_uevent_env *env)
+{
+	const struct espi_device *edev = to_espi_device(dev);
+
+	return add_uevent_var(env, "MODALIAS=espi:%s", edev->modalias);
+}
+
+static int espi_bus_probe(struct device *dev)
+{
+	const struct espi_driver *edrv = to_espi_driver(dev->driver);
+	struct espi_device *edev = to_espi_device(dev);
+
+	if (edrv->probe)
+		return edrv->probe(edev);
+	return 0;
+}
+
+static void espi_bus_remove(struct device *dev)
+{
+	const struct espi_driver *edrv = to_espi_driver(dev->driver);
+	struct espi_device *edev = to_espi_device(dev);
+
+	if (edrv->remove)
+		edrv->remove(edev);
+}
+
+const struct bus_type espi_bus_type = {
+	.name	= "espi",
+	.match	= espi_bus_match,
+	.uevent	= espi_bus_uevent,
+	.probe	= espi_bus_probe,
+	.remove	= espi_bus_remove,
+};
+EXPORT_SYMBOL_GPL(espi_bus_type);
+
+static ssize_t supported_channels_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	struct espi_controller *ctrl = to_espi_controller(dev);
+
+	return sysfs_emit(buf, "0x%02x\n", ctrl->caps.supported_channels);
+}
+static DEVICE_ATTR_RO(supported_channels);
+
+static ssize_t max_freq_mhz_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct espi_controller *ctrl = to_espi_controller(dev);
+
+	return sysfs_emit(buf, "%u\n", ctrl->caps.max_freq_mhz);
+}
+static DEVICE_ATTR_RO(max_freq_mhz);
+
+static ssize_t io_mode_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct espi_controller *ctrl = to_espi_controller(dev);
+	static const char * const modes[] = { "single", "dual", "quad" };
+	u8 m = ctrl->caps.io_mode;
+
+	if (WARN_ON_ONCE(m > ESPI_IO_MODE_QUAD))
+		return sysfs_emit(buf, "unknown\n");
+	return sysfs_emit(buf, "%s\n", modes[m]);
+}
+static DEVICE_ATTR_RO(io_mode);
+
+static ssize_t channel_enabled_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct espi_controller *ctrl = to_espi_controller(dev);
+
+	return sysfs_emit(buf, "0x%02x\n", READ_ONCE(ctrl->channel_enabled));
+}
+static DEVICE_ATTR_RO(channel_enabled);
+
+static struct attribute *espi_controller_attrs[] = {
+	&dev_attr_supported_channels.attr,
+	&dev_attr_max_freq_mhz.attr,
+	&dev_attr_io_mode.attr,
+	&dev_attr_channel_enabled.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(espi_controller);
+
+static void espi_controller_release(struct device *dev)
+{
+	struct espi_controller *ctrl = to_espi_controller(dev);
+
+	mutex_destroy(&ctrl->lock);
+	mutex_destroy(&ctrl->device_list_lock);
+	kfree(ctrl);
+}
+
+static const struct device_type espi_controller_type = {
+	.groups	 = espi_controller_groups,
+	.release = espi_controller_release,
+};
+
+struct espi_controller *espi_controller_alloc(struct device *parent,
+					      unsigned int size)
+{
+	struct espi_controller *ctrl;
+
+	if (!parent)
+		return ERR_PTR(-EINVAL);
+
+	ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL);
+	if (!ctrl)
+		return ERR_PTR(-ENOMEM);
+
+	device_initialize(&ctrl->dev);
+	ctrl->dev.parent = parent;
+	ctrl->dev.type = &espi_controller_type;
+
+	mutex_init(&ctrl->lock);
+	INIT_LIST_HEAD(&ctrl->device_list);
+	mutex_init(&ctrl->device_list_lock);
+	BLOCKING_INIT_NOTIFIER_HEAD(&ctrl->notifier_list);
+
+	if (size)
+		espi_controller_set_devdata(ctrl, (void *)ctrl + sizeof(*ctrl));
+
+	return ctrl;
+}
+EXPORT_SYMBOL_GPL(espi_controller_alloc);
+
+int espi_controller_register(struct espi_controller *ctrl)
+{
+	int ret;
+	u32 id;
+
+	if (!ctrl || !ctrl->ops)
+		return -EINVAL;
+
+	ret = xa_alloc(&espi_controllers, &id, ctrl, xa_limit_31b,
+		       GFP_KERNEL);
+	if (ret)
+		return ret;
+
+	ctrl->bus_num = id;
+	ret = dev_set_name(&ctrl->dev, "espi%d", ctrl->bus_num);
+	if (ret)
+		goto err_erase;
+
+	if (ctrl->ops->setup) {
+		ret = ctrl->ops->setup(ctrl);
+		if (ret) {
+			dev_err(&ctrl->dev, "controller setup failed: %d\n", ret);
+			goto err_erase;
+		}
+	}
+
+	ret = device_add(&ctrl->dev);
+	if (ret) {
+		dev_err(&ctrl->dev, "device_add failed: %d\n", ret);
+		if (ctrl->ops->cleanup)
+			ctrl->ops->cleanup(ctrl);
+		goto err_erase;
+	}
+
+	dev_info(&ctrl->dev, "registered: channels=0x%02x freq=%uMHz\n",
+		 ctrl->caps.supported_channels, ctrl->caps.max_freq_mhz);
+	return 0;
+
+err_erase:
+	xa_erase(&espi_controllers, ctrl->bus_num);
+	ctrl->bus_num = -1;
+	return ret;
+}
+EXPORT_SYMBOL_GPL(espi_controller_register);
+
+void espi_controller_unregister(struct espi_controller *ctrl)
+{
+	if (!ctrl)
+		return;
+	/*
+	 * Remove from the lookup table before dropping the device reference,
+	 * so a concurrent espi_controller_get_by_bus_num() can never take a
+	 * reference on a controller that is going away.
+	 */
+	xa_erase(&espi_controllers, ctrl->bus_num);
+	if (ctrl->ops && ctrl->ops->cleanup)
+		ctrl->ops->cleanup(ctrl);
+	device_unregister(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(espi_controller_unregister);
+
+void espi_controller_put(struct espi_controller *ctrl)
+{
+	if (ctrl)
+		put_device(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(espi_controller_put);
+
+struct espi_controller *espi_controller_get_by_bus_num(int bus_num)
+{
+	struct espi_controller *ctrl;
+
+	guard(spinlock)(&espi_controllers.xa_lock);
+	ctrl = xa_load(&espi_controllers, bus_num);
+	if (ctrl)
+		get_device(&ctrl->dev);
+	return ctrl;
+}
+EXPORT_SYMBOL_GPL(espi_controller_get_by_bus_num);
+
+int espi_get_capabilities(struct espi_controller *ctrl,
+			  struct espi_capabilities *caps)
+{
+	if (!ctrl || !caps)
+		return -EINVAL;
+	guard(mutex)(&ctrl->lock);
+	*caps = ctrl->caps;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(espi_get_capabilities);
+
+bool espi_channel_is_enabled(struct espi_controller *ctrl, u8 channel)
+{
+	if (!ctrl || channel >= ESPI_CHANNEL_COUNT)
+		return false;
+	guard(mutex)(&ctrl->lock);
+	return !!(ctrl->channel_enabled & BIT(channel));
+}
+EXPORT_SYMBOL_GPL(espi_channel_is_enabled);
+
+int espi_get_configuration(struct espi_controller *ctrl,
+			   u32 slave_reg_addr, u32 *config)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->get_configuration)
+		return -EOPNOTSUPP;
+	if (!config)
+		return -EINVAL;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->get_configuration(ctrl, slave_reg_addr, config);
+}
+EXPORT_SYMBOL_GPL(espi_get_configuration);
+
+int espi_set_configuration(struct espi_controller *ctrl,
+			   u32 slave_reg_addr, u32 config)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->set_configuration)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->set_configuration(ctrl, slave_reg_addr, config);
+}
+EXPORT_SYMBOL_GPL(espi_set_configuration);
+
+int espi_inband_reset(struct espi_controller *ctrl)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->inband_reset)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->inband_reset(ctrl);
+}
+EXPORT_SYMBOL_GPL(espi_inband_reset);
+
+int espi_get_status(struct espi_controller *ctrl,
+		    struct espi_slave_status *status)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->get_status)
+		return -EOPNOTSUPP;
+	if (!status)
+		return -EINVAL;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->get_status(ctrl, status);
+}
+EXPORT_SYMBOL_GPL(espi_get_status);
+
+int espi_enable_channel(struct espi_controller *ctrl, u8 channel)
+{
+	int ret;
+
+	if (!ctrl || !ctrl->ops || !ctrl->ops->enable_channel)
+		return -EOPNOTSUPP;
+	if (channel >= ESPI_CHANNEL_COUNT)
+		return -EINVAL;
+	guard(mutex)(&ctrl->lock);
+	ret = ctrl->ops->enable_channel(ctrl, channel);
+	if (!ret)
+		ctrl->channel_enabled |= BIT(channel);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(espi_enable_channel);
+
+int espi_disable_channel(struct espi_controller *ctrl, u8 channel)
+{
+	int ret;
+
+	if (!ctrl || !ctrl->ops || !ctrl->ops->disable_channel)
+		return -EOPNOTSUPP;
+	if (channel >= ESPI_CHANNEL_COUNT)
+		return -EINVAL;
+	guard(mutex)(&ctrl->lock);
+	ret = ctrl->ops->disable_channel(ctrl, channel);
+	if (!ret)
+		ctrl->channel_enabled &= ~BIT(channel);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(espi_disable_channel);
+
+int espi_periph_io_read(struct espi_controller *ctrl,
+			u16 port, u8 width, u32 *value)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->periph_io_read)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->periph_io_read(ctrl, port, width, value);
+}
+EXPORT_SYMBOL_GPL(espi_periph_io_read);
+
+int espi_periph_io_write(struct espi_controller *ctrl,
+			 u16 port, u8 width, u32 value)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->periph_io_write)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->periph_io_write(ctrl, port, width, value);
+}
+EXPORT_SYMBOL_GPL(espi_periph_io_write);
+
+int espi_periph_mem_read(struct espi_controller *ctrl,
+			 u32 addr, void *buf, size_t len)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->periph_mem_read)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->periph_mem_read(ctrl, addr, buf, len);
+}
+EXPORT_SYMBOL_GPL(espi_periph_mem_read);
+
+int espi_periph_mem_write(struct espi_controller *ctrl,
+			  u32 addr, const void *buf, size_t len)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->periph_mem_write)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->periph_mem_write(ctrl, addr, buf, len);
+}
+EXPORT_SYMBOL_GPL(espi_periph_mem_write);
+
+int espi_vwire_get(struct espi_controller *ctrl, u8 index, u8 *value, u8 *valid)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->vwire_get)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->vwire_get(ctrl, index, value, valid);
+}
+EXPORT_SYMBOL_GPL(espi_vwire_get);
+
+int espi_vwire_put(struct espi_controller *ctrl, u8 index, u8 value, u8 valid)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->vwire_put)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->vwire_put(ctrl, index, value, valid);
+}
+EXPORT_SYMBOL_GPL(espi_vwire_put);
+
+int espi_oob_send(struct espi_controller *ctrl, const void *buf, size_t len, u8 tag)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->oob_send)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->oob_send(ctrl, buf, len, tag);
+}
+EXPORT_SYMBOL_GPL(espi_oob_send);
+
+int espi_oob_recv(struct espi_controller *ctrl, void *buf, size_t *len, u8 *tag)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->oob_recv)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->oob_recv(ctrl, buf, len, tag);
+}
+EXPORT_SYMBOL_GPL(espi_oob_recv);
+
+int espi_flash_read(struct espi_controller *ctrl, u32 offset, void *buf, size_t len)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->flash_read)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->flash_read(ctrl, offset, buf, len);
+}
+EXPORT_SYMBOL_GPL(espi_flash_read);
+
+int espi_flash_write(struct espi_controller *ctrl, u32 offset, const void *buf, size_t len)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->flash_write)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->flash_write(ctrl, offset, buf, len);
+}
+EXPORT_SYMBOL_GPL(espi_flash_write);
+
+int espi_flash_erase(struct espi_controller *ctrl, u32 offset, size_t len)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->flash_erase)
+		return -EOPNOTSUPP;
+	guard(mutex)(&ctrl->lock);
+	return ctrl->ops->flash_erase(ctrl, offset, len);
+}
+EXPORT_SYMBOL_GPL(espi_flash_erase);
+
+/*
+ * espi_handle_alert - dispatch a hardware alert to the controller
+ *
+ * Must be called from process context (threaded IRQ or workqueue).
+ *
+ * ctrl->lock is NOT held across ops->handle_alert so that the driver
+ * callback can call espi_notify_event() without deadlocking: notifier
+ * callbacks may in turn call channel APIs that also acquire ctrl->lock.
+ * The driver is responsible for taking ctrl->lock around any register
+ * accesses that need serialisation with the channel API.
+ */
+int espi_handle_alert(struct espi_controller *ctrl)
+{
+	if (!ctrl || !ctrl->ops || !ctrl->ops->handle_alert)
+		return -EOPNOTSUPP;
+	return ctrl->ops->handle_alert(ctrl);
+}
+EXPORT_SYMBOL_GPL(espi_handle_alert);
+
+int __espi_register_driver(struct module *owner, struct espi_driver *drv)
+{
+	drv->driver.owner = owner;
+	drv->driver.bus = &espi_bus_type;
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(__espi_register_driver);
+
+void espi_unregister_driver(struct espi_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(espi_unregister_driver);
+
+static int __init espi_init(void)
+{
+	int ret = bus_register(&espi_bus_type);
+
+	if (ret)
+		pr_err("failed to register eSPI bus: %d\n", ret);
+	return ret;
+}
+postcore_initcall(espi_init);
+
+MODULE_AUTHOR("Krishnamoorthi M <[email protected]>");
+MODULE_DESCRIPTION("eSPI core framework");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/espi/espi.h b/include/linux/espi/espi.h
new file mode 100644
index 000000000000..a191ddc10cdd
--- /dev/null
+++ b/include/linux/espi/espi.h
@@ -0,0 +1,345 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * eSPI (Enhanced Serial Peripheral Interface) framework
+ *
+ * Copyright (c) 2026, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ */
+#ifndef _LINUX_ESPI_ESPI_H
+#define _LINUX_ESPI_ESPI_H
+
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/mod_devicetable.h>
+#include <linux/notifier.h>
+
+struct espi_controller;
+struct espi_device;
+struct espi_driver;
+struct espi_board_info;
+
+#define ESPI_NAME_SIZE			32
+
+#define ESPI_CHANNEL_PERIPH		0
+#define ESPI_CHANNEL_VWIRE		1
+#define ESPI_CHANNEL_OOB		2
+#define ESPI_CHANNEL_FLASH		3
+#define ESPI_CHANNEL_COUNT		4
+
+#define ESPI_CHANNEL_PERIPH_SUPP	BIT(ESPI_CHANNEL_PERIPH)
+#define ESPI_CHANNEL_VWIRE_SUPP		BIT(ESPI_CHANNEL_VWIRE)
+#define ESPI_CHANNEL_OOB_SUPP		BIT(ESPI_CHANNEL_OOB)
+#define ESPI_CHANNEL_FLASH_SUPP		BIT(ESPI_CHANNEL_FLASH)
+#define ESPI_CHANNEL_ALL		(ESPI_CHANNEL_PERIPH_SUPP | \
+					 ESPI_CHANNEL_VWIRE_SUPP | \
+					 ESPI_CHANNEL_OOB_SUPP | \
+					 ESPI_CHANNEL_FLASH_SUPP)
+
+#define ESPI_IO_MODE_SINGLE		0
+#define ESPI_IO_MODE_DUAL		1
+#define ESPI_IO_MODE_QUAD		2
+
+#define ESPI_FREQ_16MHZ			16
+#define ESPI_FREQ_33MHZ			33
+#define ESPI_FREQ_66MHZ			66
+
+enum espi_cmd_type {
+	ESPI_CMD_SET_CONFIGURATION	= 0,
+	ESPI_CMD_GET_CONFIGURATION	= 1,
+	ESPI_CMD_IN_BAND_RESET		= 2,
+	ESPI_CMD_GET_STATUS		= 3,
+	ESPI_CMD_PERIPHERAL		= 4,
+	ESPI_CMD_VWIRE			= 5,
+	ESPI_CMD_OOB			= 6,
+	ESPI_CMD_FLASH			= 7,
+};
+
+#define ESPI_SLAVE_REG_DEVICE_ID	0x0004
+#define ESPI_SLAVE_REG_GENERAL_CFG	0x0008
+
+/* General Configuration register (0x08) field definitions. */
+#define ESPI_GENCFG_OP_FREQ		GENMASK(22, 20)
+#define ESPI_GENCFG_IO_MODE		GENMASK(27, 26)
+#define ESPI_GENCFG_ALERT_MODE		BIT(28)
+#define ESPI_GENCFG_CRC_EN		BIT(31)
+/* ESPI_GENCFG_OP_FREQ values */
+#define ESPI_GENCFG_FREQ_20MHZ		0x0
+#define ESPI_GENCFG_FREQ_33MHZ		0x2
+#define ESPI_GENCFG_FREQ_66MHZ		0x4
+
+/* Channel-specific capability/configuration register addresses */
+#define ESPI_SLAVE_REG_PERIPH_CFG	0x0010
+#define ESPI_SLAVE_REG_VWIRE_CFG	0x0020
+#define ESPI_SLAVE_REG_OOB_CFG		0x0030
+#define ESPI_SLAVE_REG_FLASH_CFG	0x0040
+
+struct espi_slave_status {
+	bool pc_free;
+	bool np_free;
+	bool vwire_free;
+	bool oob_free;
+	bool flash_free;
+	u16 raw;
+};
+
+struct espi_device_id {
+	char name[ESPI_NAME_SIZE];
+	kernel_ulong_t driver_data;
+};
+
+enum espi_event_type {
+	ESPI_EVENT_CHANNEL_READY	= 0,
+	ESPI_EVENT_CHANNEL_RESET	= 1,
+	ESPI_EVENT_VWIRE_CHANGED	= 2,
+	ESPI_EVENT_OOB_RECEIVED		= 3,
+	ESPI_EVENT_PERIPH_POSTED	= 4,
+	ESPI_EVENT_ALERT		= 5,
+	ESPI_EVENT_RESET		= 6,
+};
+
+struct espi_event {
+	enum espi_event_type type;
+	int channel;
+	u32 details;
+	struct espi_controller *ctrl;
+};
+
+struct espi_board_info {
+	char type[ESPI_NAME_SIZE];
+	u8 cs;
+	void *platform_data;
+	struct fwnode_handle *fwnode;
+};
+
+struct espi_capabilities {
+	u32 supported_channels;
+	u32 max_freq_mhz;
+	u8 io_mode;
+	bool alert_mode;
+	bool crc_supported;
+	u16 periph_max_payload;
+	u8 vwire_max_count;
+	u16 oob_max_payload;
+	u16 flash_max_payload;
+};
+
+/**
+ * struct espi_controller_ops - hardware operation callbacks
+ *
+ * All callbacks are optional and return 0 or a negative errno. The core
+ * returns -EOPNOTSUPP for operations a controller does not provide.
+ */
+struct espi_controller_ops {
+	int (*setup)(struct espi_controller *ctrl);
+	void (*cleanup)(struct espi_controller *ctrl);
+
+	int (*get_configuration)(struct espi_controller *ctrl,
+				 u32 slave_reg_addr, u32 *config);
+	int (*set_configuration)(struct espi_controller *ctrl,
+				 u32 slave_reg_addr, u32 config);
+	int (*inband_reset)(struct espi_controller *ctrl);
+	int (*get_status)(struct espi_controller *ctrl,
+			  struct espi_slave_status *status);
+
+	int (*enable_channel)(struct espi_controller *ctrl, u8 channel);
+	int (*disable_channel)(struct espi_controller *ctrl, u8 channel);
+
+	int (*periph_io_read)(struct espi_controller *ctrl,
+			      u16 port, u8 width, u32 *value);
+	int (*periph_io_write)(struct espi_controller *ctrl,
+			       u16 port, u8 width, u32 value);
+	int (*periph_mem_read)(struct espi_controller *ctrl,
+			       u32 addr, void *buf, size_t len);
+	int (*periph_mem_write)(struct espi_controller *ctrl,
+				u32 addr, const void *buf, size_t len);
+
+	int (*vwire_get)(struct espi_controller *ctrl,
+			 u8 index, u8 *value, u8 *valid);
+	int (*vwire_put)(struct espi_controller *ctrl,
+			 u8 index, u8 value, u8 valid);
+
+	int (*oob_send)(struct espi_controller *ctrl,
+			const void *buf, size_t len, u8 tag);
+	int (*oob_recv)(struct espi_controller *ctrl,
+			void *buf, size_t *len, u8 *tag);
+
+	int (*flash_read)(struct espi_controller *ctrl,
+			  u32 offset, void *buf, size_t len);
+	int (*flash_write)(struct espi_controller *ctrl,
+			   u32 offset, const void *buf, size_t len);
+	int (*flash_erase)(struct espi_controller *ctrl,
+			   u32 offset, size_t len);
+
+	int (*handle_alert)(struct espi_controller *ctrl);
+};
+
+struct espi_controller {
+	struct device dev;
+	int bus_num;
+	u8 max_targets;		/* number of Chip Select# pins supported */
+
+	const struct espi_controller_ops *ops;
+	struct espi_capabilities caps;
+
+	struct mutex lock;	/* serialises controller ops and @channel_enabled */
+	u32 channel_enabled;
+
+	struct list_head device_list;
+	struct mutex device_list_lock;	/* protects @device_list */
+
+	struct blocking_notifier_head notifier_list;
+};
+
+#define to_espi_controller(d)	container_of(d, struct espi_controller, dev)
+
+struct espi_device {
+	struct device dev;
+	struct espi_controller *ctrl;
+	u8 cs;
+	char modalias[ESPI_NAME_SIZE];
+	void *platform_data;
+	struct list_head list;
+};
+
+#define to_espi_device(d)	container_of(d, struct espi_device, dev)
+
+struct espi_driver {
+	struct device_driver driver;
+	int (*probe)(struct espi_device *edev);
+	void (*remove)(struct espi_device *edev);
+	const struct espi_device_id *id_table;
+};
+
+#define to_espi_driver(d)	container_of_const(d, struct espi_driver, driver)
+
+extern const struct bus_type espi_bus_type;
+
+struct espi_controller *espi_controller_alloc(struct device *parent,
+					      unsigned int size);
+int espi_controller_register(struct espi_controller *ctrl);
+void espi_controller_unregister(struct espi_controller *ctrl);
+void espi_controller_put(struct espi_controller *ctrl);
+struct espi_controller *espi_controller_get_by_bus_num(int bus_num);
+
+static inline void *espi_controller_get_devdata(struct espi_controller *ctrl)
+{
+	return dev_get_drvdata(&ctrl->dev);
+}
+
+static inline void espi_controller_set_devdata(struct espi_controller *ctrl,
+					       void *data)
+{
+	dev_set_drvdata(&ctrl->dev, data);
+}
+
+int espi_get_capabilities(struct espi_controller *ctrl,
+			  struct espi_capabilities *caps);
+bool espi_channel_is_enabled(struct espi_controller *ctrl, u8 channel);
+
+int espi_get_configuration(struct espi_controller *ctrl,
+			   u32 slave_reg_addr, u32 *config);
+int espi_set_configuration(struct espi_controller *ctrl,
+			   u32 slave_reg_addr, u32 config);
+int espi_inband_reset(struct espi_controller *ctrl);
+int espi_get_status(struct espi_controller *ctrl,
+		    struct espi_slave_status *status);
+
+int espi_enable_channel(struct espi_controller *ctrl, u8 channel);
+int espi_disable_channel(struct espi_controller *ctrl, u8 channel);
+
+/**
+ * espi_periph_io_read - issue a Peripheral Channel I/O read cycle
+ * @ctrl:  controller
+ * @port:  16-bit I/O port address
+ * @width: access width in bytes (1, 2, or 4)
+ * @value: receives the read value
+ */
+int espi_periph_io_read(struct espi_controller *ctrl,
+			u16 port, u8 width, u32 *value);
+/**
+ * espi_periph_io_write - issue a Peripheral Channel I/O write cycle
+ * @ctrl:  controller
+ * @port:  16-bit I/O port address
+ * @width: access width in bytes (1, 2, or 4)
+ * @value: value to write
+ */
+int espi_periph_io_write(struct espi_controller *ctrl,
+			 u16 port, u8 width, u32 value);
+int espi_periph_mem_read(struct espi_controller *ctrl,
+			 u32 addr, void *buf, size_t len);
+int espi_periph_mem_write(struct espi_controller *ctrl,
+			  u32 addr, const void *buf, size_t len);
+
+int espi_vwire_get(struct espi_controller *ctrl,
+		   u8 index, u8 *value, u8 *valid);
+/**
+ * espi_vwire_put - send a PUT_VIRTUAL_WIRE command
+ * @ctrl:  controller
+ * @index: VWire group index
+ * @value: wire value byte
+ * @valid: valid mask byte
+ */
+int espi_vwire_put(struct espi_controller *ctrl,
+		   u8 index, u8 value, u8 valid);
+
+int espi_oob_send(struct espi_controller *ctrl,
+		  const void *buf, size_t len, u8 tag);
+int espi_oob_recv(struct espi_controller *ctrl,
+		  void *buf, size_t *len, u8 *tag);
+
+int espi_flash_read(struct espi_controller *ctrl,
+		    u32 offset, void *buf, size_t len);
+int espi_flash_write(struct espi_controller *ctrl,
+		     u32 offset, const void *buf, size_t len);
+int espi_flash_erase(struct espi_controller *ctrl,
+		     u32 offset, size_t len);
+
+int espi_handle_alert(struct espi_controller *ctrl);
+
+struct espi_device *espi_new_device(struct espi_controller *ctrl,
+				    const struct espi_board_info *info);
+void espi_remove_device(struct espi_device *edev);
+
+int espi_register_notifier(struct espi_controller *ctrl,
+			   struct notifier_block *nb);
+int espi_unregister_notifier(struct espi_controller *ctrl,
+			     struct notifier_block *nb);
+int espi_notify_event(struct espi_controller *ctrl,
+		      struct espi_event *event);
+
+static inline int espi_dev_get_configuration(struct espi_device *edev,
+					     u32 addr, u32 *config)
+{
+	return espi_get_configuration(edev->ctrl, addr, config);
+}
+
+static inline int espi_dev_set_configuration(struct espi_device *edev,
+					     u32 addr, u32 config)
+{
+	return espi_set_configuration(edev->ctrl, addr, config);
+}
+
+static inline int espi_dev_inband_reset(struct espi_device *edev)
+{
+	return espi_inband_reset(edev->ctrl);
+}
+
+static inline int espi_dev_get_status(struct espi_device *edev,
+				      struct espi_slave_status *status)
+{
+	return espi_get_status(edev->ctrl, status);
+}
+
+int __espi_register_driver(struct module *owner, struct espi_driver *drv);
+void espi_unregister_driver(struct espi_driver *drv);
+
+#define espi_register_driver(drv) \
+	__espi_register_driver(THIS_MODULE, drv)
+
+#define module_espi_driver(__espi_driver) \
+	module_driver(__espi_driver, espi_register_driver, espi_unregister_driver)
+
+#endif /* _LINUX_ESPI_ESPI_H */
-- 
2.34.1