[RFC PATCH 0/4] espi: introduce eSPI bus framework

Krishnamoorthi M <[email protected]> Tue, 4 Aug 2026 17:22:55 +0530
Newsgroups org.ozlabs.lists.openbmc,dev.linux.lists.chrome-platform,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-spi,org.ozlabs.lists.linux-aspeed
Message-ID <[email protected]>
This RFC proposes a new eSPI (Enhanced Serial Peripheral Interface) bus
subsystem for Linux.

Background
==========

I previously posted to the list asking whether extending the existing SPI
subsystem or introducing a new bus type was the preferred direction for eSPI
support [1]. This series represents the new-bus-type approach, implemented
and validated on AMD hardware.

eSPI is an Intel-defined protocol replacing the legacy LPC bus. Unlike SPI,
eSPI is capability-negotiated: the controller and target exchange capability
registers at link bring-up to agree on I/O mode, clock frequency and CRC.
Traffic is carried over four logically independent channels on a single
shared physical link, and the target signals upstream data availability
asynchronously via an ALERT# pin rather than chip-select assertion.
These characteristics do not fit the synchronous, single-channel, transfer-
oriented SPI model, so a dedicated bus type is proposed.

Channel Model
=============

The four eSPI channels each serve a distinct purpose:

  - Peripheral channel: carries host I/O and memory cycles to/from the
    target, replacing the LPC I/O and memory cycles used by devices such
    as EC and BMC firmware.

  - Virtual Wire channel: transfers logical signal state (power sequencing
    signals, SMI#, SCI#, IRQs) as indexed wire groups, replacing the
    physical LPC sideband signals.

  - OOB channel: tunnels SMBus/I2C messages between the host and an
    out-of-band processor on the target, enabling management traffic
    independent of the host OS.

  - Flash Access channel: provides access to a SPI flash device attached
    to the target, allowing the host to share a single flash with the
    target firmware.

Design Overview
===============

The framework follows the established Linux bus/device/driver model:

  - struct espi_controller: the host controller, registered with
    espi_controller_register(). Capabilities are negotiated at runtime
    and stored in struct espi_capabilities. Each controller is assigned
    a bus number from an XArray allocator. Read-only sysfs attributes
    (supported_channels, channel_enabled, io_mode, max_freq_mhz) expose
    the negotiated link state to userspace.

  - struct espi_device: a target on the bus, identified by its Chip
    Select# index (cs field). The eSPI spec allows one controller to
    drive multiple targets via separate CS# pins; ctrl->max_targets
    advertises the hardware limit and cs is range-checked at device
    creation. Devices are matched to drivers by modalias.

  - struct espi_controller_ops: an all-optional hardware callback table.
    The core returns -EOPNOTSUPP for unimplemented ops, allowing
    incremental controller driver development across patch series.

  - A per-controller blocking notifier chain delivers hardware events
    (Virtual Wire changes, OOB messages, channel state transitions,
    In-Band Reset) to slave drivers from process context. A blocking
    notifier is used rather than a raw notifier because slave driver
    callbacks may sleep, for example to issue follow-up configuration
    commands over the bus.

  - A single per-controller mutex serialises all channel operations.
    This is intentional: eSPI has one shared physical link and only one
    downstream transaction can be in flight at a time regardless of the
    logical channel, consistent with how struct spi_controller is modelled.
    Because the mutex is a sleeping lock, all ops must be called from
    process context; the alert handler is therefore registered with
    IRQF_ONESHOT and dispatches from a threaded IRQ.

Alert Mechanism
===============

When the target has upstream data pending it asserts ALERT# (dedicated
pin or in-band on I/O[1]). The controller's hard-IRQ handler acknowledges
the interrupt and defers processing to a threaded IRQ, which calls
espi_handle_alert(). This dispatches to ops->handle_alert WITHOUT holding
ctrl->lock, so that the driver callback can call espi_notify_event() to
deliver the appropriate ESPI_EVENT_* to registered slave driver notifiers
without deadlocking: notifier callbacks may in turn call channel APIs
that also acquire ctrl->lock. The driver is responsible for acquiring
ctrl->lock around any register accesses that require serialisation with
the channel API. The alert path implementation is deferred to follow-on
patches; the hook points are in place in this series.

Scope of this RFC
=================

This series covers the framework foundation and the AMD FCH controller
driver (ACPI HID: AMDI0070). It intentionally limits scope to the
channel-independent layer: capability discovery, GET/SET_CONFIGURATION
and In-Band Reset. Channel-specific operations (Peripheral I/O and
memory, Virtual Wire, OOB, Flash Access) and the alert/interrupt path
are declared in the API but their implementations are deferred to
follow-on patches, to be posted once the framework design is reviewed.

Testing
=======

The series has been validated on AMD hardware (AMD FCH, AMDI0070) using
an internal test slave driver that binds as an eSPI slave device and
exposes a sysfs command interface. The following scenarios were
exercised:

  - GET_CONFIGURATION on the General Capabilities register (0x08):
    verified correct decoding of I/O mode, operating frequency, CRC,
    Alert mode, Max WAIT STATE, and supported channels.

  - SET_CONFIGURATION on the General Capabilities register: verified
    successful negotiation of I/O mode (single/dual/quad) and operating
    frequency (16/33/66 MHz), and confirmed the host-side register is
    updated to match the negotiated parameters.

  - In-Band Reset: verified the reset completes successfully and the
    host controller registers are restored to match the target's
    post-reset state (16 MHz / single I/O).

Known Limitations / Future Work
================================

  - No Device Tree bindings in this series. The AMD controller uses
    ACPI enumeration. DT support will follow.

  - Slave device enumeration is manual (espi_new_device). ACPI/DT-based
    enumeration will be added in a subsequent patch.

  - Channel ops (Peripheral, VWire, OOB, Flash) and alert/interrupt
    handling are deferred to follow-on patches.

Feedback Requested
==================

  1. We chose a dedicated bus_type for the reasons described above
     (capability negotiation, four independent channels, asynchronous
     ALERT#). Does the community agree this is the right direction, or
     is there a strong preference to extend the SPI subsystem instead?
  2. Is the blocking notifier chain the right mechanism for event
     delivery to slave drivers?
  3. Any concerns with the ops table design or the -EOPNOTSUPP fallback?
  4. Naming and structure of the public API in include/linux/espi/espi.h.

References
==========

[1] https://lore.kernel.org/lkml/[email protected]/T/#u
[2] Intel Enhanced Serial Peripheral Interface (eSPI) Interface Base
    Specification

Krishnamoorthi M (4):
  espi: add core bus framework
  espi: add slave device model and event notification
  Documentation: espi: add subsystem overview and MAINTAINERS entry
  espi: amd: add AMD eSPI controller driver

 Documentation/driver-api/espi.rst  | 213 +++++++++++++
 Documentation/driver-api/index.rst |   1 +
 MAINTAINERS                        |   8 +
 drivers/Kconfig                    |   2 +
 drivers/Makefile                   |   1 +
 drivers/espi/Kconfig               |  41 +++
 drivers/espi/Makefile              |   3 +
 drivers/espi/espi-amd.c            | 453 ++++++++++++++++++++++++++
 drivers/espi/espi-amd.h            | 126 ++++++++
 drivers/espi/espi-core.c           | 493 +++++++++++++++++++++++++++++
 drivers/espi/espi-slave.c          | 177 +++++++++++
 include/linux/espi/espi.h          | 345 ++++++++++++++++++++
 12 files changed, 1863 insertions(+)
 create mode 100644 Documentation/driver-api/espi.rst
 create mode 100644 drivers/espi/Kconfig
 create mode 100644 drivers/espi/Makefile
 create mode 100644 drivers/espi/espi-amd.c
 create mode 100644 drivers/espi/espi-amd.h
 create mode 100644 drivers/espi/espi-core.c
 create mode 100644 drivers/espi/espi-slave.c
 create mode 100644 include/linux/espi/espi.h

-- 
2.34.1