Re: [RFC PATCH 3/4] Documentation: espi: add subsystem overview and MAINTAINERS entry

From: M, Krishnamoorthi

Date: Tue Aug 04 2026 - 15:15:19 EST




On 8/4/2026 10:09 PM, Randy Dunlap wrote:


On 8/4/26 4:52 AM, Krishnamoorthi M wrote:
Add a driver-api overview of the eSPI subsystem and a MAINTAINERS entry
covering the subsystem files.

The document describes the architecture, how to write a controller driver
and a slave driver, the per-channel APIs (Peripheral, Virtual Wire, OOB,
Flash), the alert mechanism flow, and the event notification model. An
API Reference section renders kernel-doc from the exported symbols.

Signed-off-by: Krishnamoorthi M <krishnamoorthi.m@xxxxxxx>
---
Documentation/driver-api/espi.rst | 213 +++++++++++++++++++++++++++++
Documentation/driver-api/index.rst | 1 +
MAINTAINERS | 8 ++
3 files changed, 222 insertions(+)
create mode 100644 Documentation/driver-api/espi.rst

diff --git a/Documentation/driver-api/espi.rst b/Documentation/driver-api/espi.rst
new file mode 100644
index 000000000000..60a3187edb05
--- /dev/null
+++ b/Documentation/driver-api/espi.rst
@@ -0,0 +1,213 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+===========================================
+eSPI (Enhanced Serial Peripheral Interface)
+===========================================
+
+Introduction
+============
+
+eSPI is a bus defined by Intel that replaces the legacy LPC bus. Unlike
+SPI it is a structured, capability-negotiated, message-oriented protocol
+with four logically independent channels (Peripheral, Virtual Wire, OOB,
+Flash) over a shared physical link, and asynchronous target-to-controller
+events, so it is modelled as its own bus type rather than an extension of
+the SPI subsystem.
+
+Architecture
+============
+
+* ``struct espi_controller`` - host controller, created with
+ espi_controller_alloc() and registered with espi_controller_register().
+ It is not itself a device on espi_bus_type.
+* ``struct espi_device`` - a target on the bus, matched to a
+ ``struct espi_driver`` via its modalias.
+* ``struct espi_controller_ops`` - the optional hardware-op table; the
+ channel API returns -EOPNOTSUPP for ops a controller does not provide.
+
+Writing a controller driver
+===========================
+
+A controller driver allocates and registers a controller from its
+``probe()`` function::
+
+ ctrl = espi_controller_alloc(&pdev->dev, sizeof(*priv));
+ if (IS_ERR(ctrl))
+ return PTR_ERR(ctrl);
+
+ priv = espi_controller_get_devdata(ctrl);
+ ctrl->ops = &my_espi_ops;
+ ctrl->max_targets = 1;
+
+ /* populate ctrl->caps from hardware capability registers */
+ ctrl->caps.supported_channels = ESPI_CHANNEL_ALL;
+ ctrl->caps.max_freq_mhz = 33;
+ ctrl->caps.io_mode = ESPI_IO_MODE_SINGLE;
+
+ ret = espi_controller_register(ctrl);
+ if (ret)
+ goto err_put;
+
+After registration the controller calls espi_new_device() for each
+target enumerated from firmware (ACPI or device tree)::
+
+ struct espi_board_info info = {
+ .type = "my-ec",
+ .cs = 0,
+ };
+ edev = espi_new_device(ctrl, &info);
+
+On removal::
+
+ espi_remove_device(edev);
+ espi_controller_unregister(ctrl);
+ espi_controller_put(ctrl);
+
+Writing a slave driver
+======================
+
+A slave driver declares a device ID table and a ``struct espi_driver``::
+
+ static const struct espi_device_id my_ec_ids[] = {
+ { "my-ec", 0 },
+ { }
+ };
+ MODULE_DEVICE_TABLE(espi, my_ec_ids);
+
+ static int my_ec_probe(struct espi_device *edev)
+ {
+ /* register for hardware events */
+ nb->notifier_call = my_ec_event;
+ espi_register_notifier(edev->ctrl, nb);
+ return 0;
+ }
+
+ static void my_ec_remove(struct espi_device *edev)
+ {
+ espi_unregister_notifier(edev->ctrl, nb);
+ }
+
+ static struct espi_driver my_ec_driver = {
+ .driver = { .name = "my-ec" },
+ .id_table = my_ec_ids,
+ .probe = my_ec_probe,
+ .remove = my_ec_remove,
+ };
+ module_espi_driver(my_ec_driver);
+
+Channel-independent commands
+============================
+
+espi_get_configuration(), espi_set_configuration(), espi_inband_reset()
+and espi_get_status(). GET_STATUS is optional: controllers whose hardware
+does not implement the wire command leave .get_status unset.
+
+Capability negotiation and channel management
+=============================================
+
+At boot the controller driver reads the target's capability registers via
+espi_get_configuration(), negotiates link parameters (I/O mode, clock
+frequency, CRC) via espi_set_configuration(), then enables each channel
+with espi_enable_channel(). espi_channel_is_enabled() may be called at
+any time to query the current state. Channels may be disabled individually
+with espi_disable_channel(), for example before an in-band reset.
+
+Channel APIs
+============
+
+Peripheral channel
+------------------
+
+Carries I/O and memory cycles between the host and target endpoints.
+
+* espi_periph_io_read() / espi_periph_io_write() — 16-bit I/O port
+ access; ``width`` is the access size in bytes (1, 2, or 4).
+* espi_periph_mem_read() / espi_periph_mem_write() — 32-bit memory
+ mapped access.
+
+Virtual Wire channel
+--------------------
+
+Carries logical signal state (power sequencing, SMI#, SCI#, IRQs) as
+indexed wire groups. Each group carries up to four wire values with
+individual valid bits.
+
+* espi_vwire_get() — read a wire group from the target.
+* espi_vwire_put() — send a PUT_VIRTUAL_WIRE command to the target.
+ Named after the eSPI PUT_VW wire command, not a reference-count
+ release.
+
+Wire changes from the target generate an ``ESPI_EVENT_VWIRE_CHANGED``
+event delivered through the notifier chain.
+
+OOB channel
+-----------
+
+Tunnels SMBus/I2C messages between the host and target out-of-band
+processor (BMC, EC). Messages are exchanged as opaque byte buffers with
+a tag field for matching requests to responses.
+
+* espi_oob_send() / espi_oob_recv()
+
+Incoming OOB messages generate an ``ESPI_EVENT_OOB_RECEIVED`` event.
+
+Flash Access channel
+--------------------
+
+Provides access to a SPI flash device attached to the target. The target
+acts as a proxy for flash read, write, and erase operations.
+
+* espi_flash_read() / espi_flash_write() / espi_flash_erase()
+
+Alert mechanism
+===============
+
+When the target has upstream data pending it asserts ``ALERT#``. The
+controller's hard-IRQ handler acknowledges the interrupt and defers
+processing to a threaded IRQ or workqueue. From that process context the
+controller driver calls espi_handle_alert(), which acquires the
+controller lock and dispatches to ``ops->handle_alert``. The hardware
+callback reads the target's status register (GET_STATUS), identifies the
+pending channel, and calls espi_notify_event() to deliver the appropriate
+``ESPI_EVENT_*`` to all registered slave driver notifiers::
+
+ ALERT# asserted by target
+ |
+ v
+ hard-IRQ handler (controller driver)
+ |
+ v
+ threaded IRQ / workqueue
+ |
+ v
+ espi_handle_alert(ctrl) [espi-core.c]
+ |
+ v
+ ops->handle_alert(ctrl) [controller driver]
+ | reads GET_STATUS, decodes channel
+ v
+ espi_notify_event(ctrl, &event) [espi-slave.c]
+ |
+ v
+ slave driver notifier callback
+
+espi_handle_alert() must always be called from process context; it must
+never be called from a hard-IRQ handler.
+
+Events and concurrency
+======================
+
+Hardware events (Virtual Wire changes, OOB messages, Peripheral channel
+completions, channel state changes) are delivered through a per-controller
+blocking notifier chain (espi_register_notifier()/espi_notify_event()).
+Callbacks run in process context; controllers deliver events from a
+threaded IRQ or workqueue, never from hardirq and never while holding the
+controller lock.
+
+API Reference
+=============
+
+.. kernel-doc:: include/linux/espi/espi.h
+

Describe each of these:

WARNING: ../include/linux/espi/espi.h:176 struct member 'setup' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'cleanup' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'get_configuration' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'set_configuration' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'inband_reset' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'get_status' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'enable_channel' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'disable_channel' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'periph_io_read' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'periph_io_write' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'periph_mem_read' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'periph_mem_write' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'vwire_get' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'vwire_put' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'oob_send' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'oob_recv' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'flash_read' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'flash_write' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'flash_erase' not described in 'espi_controller_ops'
WARNING: ../include/linux/espi/espi.h:176 struct member 'handle_alert' not described in 'espi_controller_ops'


or don't use "/**" for that struct's comment header.

Noted. Will add descriptions for all struct espi_controller_ops members in v2.

Thanks,
Krishna

+.. kernel-doc:: drivers/espi/espi-slave.c
+ :export: