Re: [RFC PATCH 3/4] Documentation: espi: add subsystem overview and MAINTAINERS entry
"M, Krishnamoorthi" <[email protected]> Wed, 5 Aug 2026 00:44:39 +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]> |
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 <[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. Noted. Will add descriptions for all struct espi_controller_ops members in v2. Thanks, Krishna > >> +.. kernel-doc:: drivers/espi/espi-slave.c >> + :export: > >