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

Randy Dunlap <[email protected]> Tue, 4 Aug 2026 09:39:10 -0700
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]>

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 <[email protected]>
> ---
>  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.

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


-- 
~Randy