public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/4] PCI: Add DOE support for endpoint
@ 2026-02-13 12:35 Aksh Garg
  2026-02-13 12:36 ` [RFC PATCH 1/4] PCI: Add documentation for DOE endpoint support Aksh Garg
                   ` (5 more replies)
  0 siblings, 6 replies; 19+ messages in thread
From: Aksh Garg @ 2026-02-13 12:35 UTC (permalink / raw)
  To: linux-pci, linux-doc, bhelgaas, corbet, cassel
  Cc: linux-kernel, linux-arm-kernel, s-vadapalli, danishanwar, srk,
	a-garg7

This patch series introduces the framework for supporting the Data
Object Exchange (DOE) feature for PCIe endpoint devices. Please refer
to the documentation added in patch 1 for details on the feature and
implementation architecture.

The implementation provides a common framework for all PCIe endpoint
controllers, not specific to any particular SoC vendor.

Posting this as an RFC patch series to get feedback on the design and
implementation.

Aksh Garg (4):
  PCI: Add documentation for DOE endpoint support
  PCI/DOE: Move common definitions to the header file
  PCI/DOE: Add DOE mailbox support for endpoint functions
  PCI: Document APIs for endpoint DOE implementation

 Documentation/PCI/index.rst      |   1 +
 Documentation/PCI/pci-doe-ep.rst | 297 ++++++++++++++
 drivers/pci/Kconfig              |  14 +
 drivers/pci/Makefile             |   1 +
 drivers/pci/doe-ep.c             | 671 +++++++++++++++++++++++++++++++
 drivers/pci/doe.c                |  11 -
 include/linux/pci-doe.h          | 107 ++++-
 include/linux/pci-epc.h          |   4 +
 8 files changed, 1090 insertions(+), 16 deletions(-)
 create mode 100644 Documentation/PCI/pci-doe-ep.rst
 create mode 100644 drivers/pci/doe-ep.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [RFC PATCH 1/4] PCI: Add documentation for DOE endpoint support
  2026-02-13 12:35 [RFC PATCH 0/4] PCI: Add DOE support for endpoint Aksh Garg
@ 2026-02-13 12:36 ` Aksh Garg
  2026-02-13 20:33   ` Niklas Cassel
  2026-02-13 12:36 ` [RFC PATCH 2/4] PCI/DOE: Move common definitions to the header file Aksh Garg
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: Aksh Garg @ 2026-02-13 12:36 UTC (permalink / raw)
  To: linux-pci, linux-doc, bhelgaas, corbet, cassel
  Cc: linux-kernel, linux-arm-kernel, s-vadapalli, danishanwar, srk,
	a-garg7

From: Aksh Garg <a-garg7@ti.com>

Document the architecture and implementation details for the Data Object
Exchange (DOE) framework for PCIe Endpoint devices.

Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
 Documentation/PCI/index.rst      |   1 +
 Documentation/PCI/pci-doe-ep.rst | 291 +++++++++++++++++++++++++++++++
 2 files changed, 292 insertions(+)
 create mode 100644 Documentation/PCI/pci-doe-ep.rst

diff --git a/Documentation/PCI/index.rst b/Documentation/PCI/index.rst
index 5d720d2a415e..4c9a4e749442 100644
--- a/Documentation/PCI/index.rst
+++ b/Documentation/PCI/index.rst
@@ -20,3 +20,4 @@ PCI Bus Subsystem
    controller/index
    boot-interrupts
    tph
+   pci-doe-ep
diff --git a/Documentation/PCI/pci-doe-ep.rst b/Documentation/PCI/pci-doe-ep.rst
new file mode 100644
index 000000000000..d6715944c117
--- /dev/null
+++ b/Documentation/PCI/pci-doe-ep.rst
@@ -0,0 +1,291 @@
+.. SPDX-License-Identifier: GPL-2.0-only or MIT
+
+.. include:: <isonum.txt>
+
+=============================================
+Data Object Exchange (DOE) for PCIe Endpoint
+=============================================
+
+:Copyright: |copy| 2026 Texas Instruments Incorporated
+:Author: Aksh Garg <a-garg7@ti.com>
+:Co-Author: Siddharth Vadapalli <s-vadapalli@ti.com>
+
+Overview
+========
+
+DOE (Data Object Exchange) is a standard PCIe extended capability feature as
+introduced in the Data Object Exchange (DOE) ECN for PCIe r5.0. It is an optional
+mechanism for system firmware/software running on root complex (host) to perform
+:ref:`data object <data-object-term>` exchanges with an endpoint function. Each
+data object is uniquely identified by the Vendor ID of the vendor publishing the
+data object definition and a Data Object Type value assigned by that vendor.
+
+Think of DOE as a sophisticated mailbox system built into PCIe. The root complex
+can send structured requests to the endpoint device through DOE mailboxes, and
+the endpoint device responds with appropriate data. DOE mailboxes are implemented
+as PCIe Extended Capabilities in endpoint devices, allowing multiple mailboxes
+per function, each potentially supporting different data object protocols.
+
+The DOE support for root complex devices has already been implemented in
+``drivers/pci/doe.c``.
+
+How DOE Works
+=============
+
+The DOE mailbox operates through a simple request-response model:
+
+1. **Host sends request**: The root complex writes a data object (vendor ID, type,
+   and payload) to the DOE write mailbox register (one DWORD at a time) of the
+   endpoint function's config space and sets the GO bit in the DOE Status register
+   to indicate that a request is ready for processing.
+2. **Endpoint processes**: The endpoint function reads the request from DOE write
+   mailbox register, sets the BUSY bit in the DOE Status register, identifies the
+   protocol of the data object, and executes the appropriate handler.
+3. **Endpoint responds**: The endpoint function writes the response data object to the
+   DOE read mailbox register (one DWORD at a time), and sets the READY bit in the DOE
+   Status register to indicate that the response is ready. If an error occurs during
+   request processing (such as unsupported protocol or handler failure), the endpoint
+   sets the ERROR bit in the DOE Status register instead of the READY bit.
+4. **Host reads response**: The root complex retrieves the response data from the DOE read
+   mailbox register once the READY bit is set in the DOE Status register, and then writes
+   any value to this register to indicate a successful read. If the ERROR bit was set,
+   the root complex discards the response and performs error handling as needed.
+
+Each mailbox operates independently and can handle one transaction at a time. The
+DOE specification supports data objects of size up to 256KB (2\ :sup:`18` dwords).
+
+For complete DOE capability details, refer to `PCI Express Base Specification Revision 7.0,
+Section 6.30 - Data Object Exchange (DOE)`.
+
+Key Terminologies
+=================
+
+.. _data-object-term:
+
+**Data Object**
+  A structured, vendor-defined, or standard-defined message exchanged between
+  root complex and endpoint function via DOE capability registers in configuration
+  space of the function.
+
+**Mailbox**
+  A DOE capability on the endpoint device, where each physical function can have
+  multiple mailboxes.
+
+**Protocol**
+  A specific type of DOE communication data object identified by a Vendor ID and Type.
+
+**Handler**
+  A function that processes DOE requests of a specific protocol and generates responses.
+
+Architecture of DOE Implementation for Endpoint
+===============================================
+
+.. code-block:: text
+
+       +------------------+
+       |                  |
+       |   Root Complex   |
+       |                  |
+       +--------^---------+
+                |
+                | Config space access
+                |   over PCIe link
+                |
+     +----------v-----------+
+     |                      |
+     |    PCIe Controller   |
+     |      as Endpoint     |
+     |                      |
+     |  +-----------------+ |
+     |  |   DOE Mailbox   | |
+     |  +-------^---------+ |
+     +----------|-----------+
+    +-----------|---------------------------------------------------------------+
+    |           |                                       +--------------------+  |
+    | +---------v--------+           Allocate           |  +--------------+  |  |
+    | |                  |-------------------------------->|   Request    |  |  |
+    | |   EP Controller  |-------------------------------->|    Buffer    |  |  |
+    | |      Driver      |             Free             |  +--------------+  |  |
+    | |                  |---+                          |                    |  |
+    | +--------^---------+   |         Free             |                    |  |
+    |          |             +-----------------------+  |                    |  |
+    |          |                                     |  |                    |  |
+    |          | pci_ep_doe_process_request()        |  |                    |  |
+    |          |                                     |  |                    |  |
+    | +--------v---------+                           |  |                    |  |
+    | |                  |<----+                     |  |         DDR        |  |
+    | |    DOE EP Core   |     |  Discovery          |  |                    |  |
+    | |    (doe-ep.c)    |     |  Protocol           |  |                    |  |
+    | |                  |-----+  Handler            |  |                    |  |
+    | +--------^---------+                           |  |                    |  |
+    |          |                                     |  |                    |  |
+    |          | protocol_handler()                  |  |                    |  |
+    |          |                                     |  |                    |  |
+    | +--------v---------+                           |  |                    |  |
+    | |                  |                           |  |  +--------------+  |  |
+    | | Protocol Handler |                           +---->|   Response   |  |  |
+    | |      Module      |-------------------------------->|    Buffer    |  |  |
+    | | (CMA/SPDM/Other) |           Allocate           |  +--------------+  |  |
+    | |                  |                              |                    |  |
+    | +------------------+                              |                    |  |
+    |                                                   +--------------------+  |
+    +---------------------------------------------------------------------------+
+
+Initialization and Cleanup
+--------------------------
+
+**Framework Initialization**
+
+The controller driver calls ``pci_ep_doe_init(epc)`` during its probe sequence.
+This initializes the xarray data structure (a resizable array data structure
+defined in linux) named ``doe_mbs`` that stores metadata of DOE mailboxes for
+the controller in ``struct pci_epc``.
+
+**Mailbox Registration**
+
+For each DOE capability found in the endpoint function's configuration space,
+the controller driver calls ``pci_ep_doe_add_mailbox(epc, func_no, cap_offset)``.
+This creates a mailbox structure and allocates an ordered workqueue for processing
+DOE requests sequentially for that mailbox, enabling concurrent request handling
+across different mailboxes. Each mailbox is uniquely identified by the combination
+of physical function number and capability offset for that controller.
+
+**Cleanup**
+
+During driver removal or controller shutdown, the controller driver calls
+``pci_ep_doe_destroy(epc)`` to clean up all DOE resources. This function
+destroys all registered mailboxes, cancels any pending tasks, flushes and
+destroys the workqueues, and frees all memory allocated to the mailboxes.
+
+Register and Unregister Protocol Handler
+----------------------------------------
+
+Protocol implementations (such as CMA, SPDM, or vendor-specific protocols)
+register their handlers with the DOE EP core during module initialization.
+
+**Registration**
+
+Protocol modules call ``pci_ep_doe_register_protocol(vendor_id, type, handler)``
+to register their handler function. The handler is stored in a global xarray
+and will be invoked when DOE requests matching the vendor ID and type are received.
+The discovery protocol (VID = 0x0001 (PCI-SIG vendor ID), Type = 0x00 (discovery
+protocol)) is handled internally by the DOE EP core and cannot be registered by
+external modules.
+
+**Unregistration**
+
+During module cleanup, protocol modules call
+``pci_ep_doe_unregister_protocol(vendor_id, type)`` to remove their handler
+from the registry.
+
+Request Handling
+----------------
+
+The complete flow of a DOE request from the root complex to the response:
+
+**Step 1: Root Complex → EP Controller Driver**
+
+The root complex writes a DOE request (Vendor ID, Type, and Payload) to the
+DOE write mailbox register in the endpoint function's configuration space and sets
+the GO bit in the DOE Control register, indicating that the request is ready for
+processing.
+
+**Step 2: EP Controller Driver → DOE EP Core**
+
+The controller driver reads the request header to determine the data object length.
+Based on this length field, it allocates a request buffer in memory (DDR) of the
+appropriate size. The driver then reads the complete request payload from the DOE
+write mailbox register and converts the data from little-endian format (the format
+followed in the PCIe transactions over the link) to CPU-native format using
+``le32_to_cpu()``. The driver creates pointers for the response buffer and response
+size, which will be populated by the protocol handler. Finally, the driver calls
+``pci_ep_doe_process_request(epc, func_no, cap_offset, vendor, type, request,
+request_sz, &response, &response_sz)`` to hand off the request to the DOE EP core,
+and sets the BUSY bit in the DOE Status register.
+
+**Step 3: DOE EP Core Processing**
+
+The DOE EP core looks up the protocol handler based on the Vendor ID and Type
+from the request header. It creates a task structure and submits it to the
+mailbox's ordered workqueue. This ensures that requests for each mailbox are
+processed sequentially, one at a time, as required by the DOE specification.
+
+**Step 4: Protocol Handler Execution**
+
+The workqueue executes the task by calling the registered protocol handler:
+``handler(request, request_sz, &response, &response_sz)``. The handler processes
+the request, allocates a response buffer in memory (DDR), builds the response
+data, and returns the response pointer and size. For the discovery protocol,
+the DOE EP core handles this directly without invoking an external handler.
+
+**Step 5: DOE EP Core → EP Controller Driver**
+
+The DOE EP core waits for the handler to complete by the work queue, and returns
+the response pointer and size to the controller driver.
+
+**Step 6: EP Controller Driver → Root Complex**
+
+The controller driver converts the response from CPU-native format to
+little-endian format using ``cpu_to_le32()``, writes the response to DOE read
+mailbox register, and sets the READY bit in the DOE Status register. The root
+complex then reads the response from the read mailbox register. Finally,
+the controller driver frees both the request buffer (which it allocated) and the
+response buffer (which the handler allocated).
+
+Abort Handling
+--------------
+
+The DOE specification allows the root complex to abort ongoing DOE operations
+by setting the ABORT bit in the DOE Control register.
+
+**Trigger**
+
+When the root complex sets the ABORT bit, the EP controller driver detects this
+condition (typically in an interrupt handler or register polling routine). The
+action taken depends on the timing of the abort:
+
+- **ABORT during request transfer**: If the ABORT bit is set while the root complex
+  is still transferring the request to the mailbox registers, the controller driver
+  discards the request and no call to ``pci_ep_doe_abort()`` is needed.
+
+- **ABORT after request submission**: If the ABORT bit is set after the request
+  has been fully received and submitted to the DOE EP core via
+  ``pci_ep_doe_process_request()``, the controller driver must call
+  ``pci_ep_doe_abort(epc, func_no, cap_offset)`` for the affected mailbox to
+  perform abort sequence in the DOE EP core.
+
+**Abort Sequence**
+
+The abort function performs the following actions:
+
+1. Sets the CANCEL flag on the mailbox to prevent queued requests from starting
+2. Flushes the workqueue to wait for any currently executing handler to complete
+   (handlers cannot be interrupted mid-execution)
+3. Clears the CANCEL flag to allow the mailbox to accept new requests
+
+Queued requests that have not started execution will be aborted with an error
+status. The currently executing request will complete normally, and the controller
+will reject the response if it arrives after the abort sequence has been triggered.
+
+.. note::
+   Independent of when the ABORT bit is triggered, the controller driver must
+   clear the ERROR, BUSY, and READY bits in the DOE Status register after
+   completing the abort operation to reset the mailbox to an idle state.
+
+Error Handling
+--------------
+
+Errors can occur during DOE request processing for various reasons, such as
+unsupported protocols, handler failures, or memory allocation failures.
+
+**Error Detection**
+
+When an error occurs during DOE request processing, the DOE EP core propagates this error
+back to the controller driver through the ``pci_ep_doe_process_request()`` return value.
+
+**Error Response**
+
+When the controller driver receives an error code from
+``pci_ep_doe_process_request()``, it sets the ERROR bit in the DOE Status
+register instead of writing a response to the read mailbox register,
+and frees the buffers.
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [RFC PATCH 2/4] PCI/DOE: Move common definitions to the header file
  2026-02-13 12:35 [RFC PATCH 0/4] PCI: Add DOE support for endpoint Aksh Garg
  2026-02-13 12:36 ` [RFC PATCH 1/4] PCI: Add documentation for DOE endpoint support Aksh Garg
@ 2026-02-13 12:36 ` Aksh Garg
  2026-02-22 13:01   ` Lukas Wunner
  2026-02-13 12:36 ` [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions Aksh Garg
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: Aksh Garg @ 2026-02-13 12:36 UTC (permalink / raw)
  To: linux-pci, linux-doc, bhelgaas, corbet, cassel
  Cc: linux-kernel, linux-arm-kernel, s-vadapalli, danishanwar, srk,
	a-garg7

From: Aksh Garg <a-garg7@ti.com>

Move common macros and structures from drivers/pci/doe.c to
include/linux/pci-doe.h to allow reuse across root complex and
endpoint DOE implementations.

Also add CONFIG_PCI_DOE guards around the root complex DOE APIs to
maintain proper conditional compilation.

These changes prepare the groundwork for the DOE endpoint implementation
that will reuse these common definitions.

Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
 drivers/pci/doe.c       | 11 -----------
 include/linux/pci-doe.h | 38 +++++++++++++++++++++++++++++++++-----
 2 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c
index 62be9c8dbc52..0171ef3215a7 100644
--- a/drivers/pci/doe.c
+++ b/drivers/pci/doe.c
@@ -28,12 +28,6 @@
 #define PCI_DOE_TIMEOUT HZ
 #define PCI_DOE_POLL_INTERVAL	(PCI_DOE_TIMEOUT / 128)
 
-#define PCI_DOE_FLAG_CANCEL	0
-#define PCI_DOE_FLAG_DEAD	1
-
-/* Max data object length is 2^18 dwords */
-#define PCI_DOE_MAX_LENGTH	(1 << 18)
-
 /**
  * struct pci_doe_mb - State for a single DOE mailbox
  *
@@ -63,11 +57,6 @@ struct pci_doe_mb {
 #endif
 };
 
-struct pci_doe_feature {
-	u16 vid;
-	u8 type;
-};
-
 /**
  * struct pci_doe_task - represents a single query/response
  *
diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
index bd4346a7c4e7..a966626c47f9 100644
--- a/include/linux/pci-doe.h
+++ b/include/linux/pci-doe.h
@@ -13,12 +13,25 @@
 #ifndef LINUX_PCI_DOE_H
 #define LINUX_PCI_DOE_H
 
-struct pci_doe_mb;
+/* Mailbox state flags */
+#define PCI_DOE_FLAG_CANCEL		0
+#define PCI_DOE_FLAG_DEAD		1
+
+/* Max data object length is 2^18 dwords */
+#define PCI_DOE_MAX_LENGTH		(1 << 18)
 
-#define PCI_DOE_FEATURE_DISCOVERY 0
-#define PCI_DOE_FEATURE_CMA 1
-#define PCI_DOE_FEATURE_SSESSION 2
+#define PCI_DOE_FEATURE_DISCOVERY	0
+#define PCI_DOE_FEATURE_CMA		1
+#define PCI_DOE_FEATURE_SSESSION	2
 
+struct pci_doe_feature {
+	u16 vid;
+	u8 type;
+};
+
+struct pci_doe_mb;
+
+#ifdef CONFIG_PCI_DOE
 struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
 					u8 type);
 
@@ -26,4 +39,19 @@ int pci_doe(struct pci_doe_mb *doe_mb, u16 vendor, u8 type,
 	    const void *request, size_t request_sz,
 	    void *response, size_t response_sz);
 
-#endif
+#else
+static inline struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev,
+						      u16 vendor, u8 type)
+{
+	return NULL;
+}
+
+static inline int pci_doe(struct pci_doe_mb *doe_mb, u16 vendor, u8 type,
+			  const void *request, size_t request_sz,
+			  void *response, size_t response_sz)
+{
+	return -EOPNOTSUPP;
+}
+#endif /* CONFIG_PCI_DOE */
+
+#endif /* LINUX_PCI_DOE_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions
  2026-02-13 12:35 [RFC PATCH 0/4] PCI: Add DOE support for endpoint Aksh Garg
  2026-02-13 12:36 ` [RFC PATCH 1/4] PCI: Add documentation for DOE endpoint support Aksh Garg
  2026-02-13 12:36 ` [RFC PATCH 2/4] PCI/DOE: Move common definitions to the header file Aksh Garg
@ 2026-02-13 12:36 ` Aksh Garg
  2026-02-13 13:21   ` Niklas Cassel
                     ` (3 more replies)
  2026-02-13 12:36 ` [RFC PATCH 4/4] PCI: Document APIs for endpoint DOE implementation Aksh Garg
                   ` (2 subsequent siblings)
  5 siblings, 4 replies; 19+ messages in thread
From: Aksh Garg @ 2026-02-13 12:36 UTC (permalink / raw)
  To: linux-pci, linux-doc, bhelgaas, corbet, cassel
  Cc: linux-kernel, linux-arm-kernel, s-vadapalli, danishanwar, srk,
	a-garg7

From: Aksh Garg <a-garg7@ti.com>

DOE (Data Object Exchange) is a standard PCIe extended capability
feature introduced in the Data Object Exchange (DOE) ECN for
PCIe r5.0. It provides a communication mechanism primarily used for
implementing PCIe security features such as device authentication, and
secure link establishment. Think of DOE as a sophisticated mailbox
system built into PCIe. The root complex can send structured requests
to the endpoint device through DOE mailboxes, and the endpoint device
responds with appropriate data.

Add the DOE support for PCIe endpoint devices, enabling endpoint
functions to process the DOE requests from the host. The implementation
provides framework APIs for controller drivers to register mailboxes,
protocol handler registration for different DOE data object types, and
request processing with workqueues ensuring sequential handling per
mailbox. The Discovery protocol is handled internally by the DOE core.

This implementation complements the existing DOE implementation for
root complex in drivers/pci/doe.c.

Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
 drivers/pci/Kconfig     |  14 +
 drivers/pci/Makefile    |   1 +
 drivers/pci/doe-ep.c    | 671 ++++++++++++++++++++++++++++++++++++++++
 include/linux/pci-doe.h |  69 +++++
 include/linux/pci-epc.h |   4 +
 5 files changed, 759 insertions(+)
 create mode 100644 drivers/pci/doe-ep.c

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index e3f848ffb52a..2a57a314054c 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -146,6 +146,20 @@ config PCI_DOE
 	  Say Y here if you want be able to communicate with PCIe DOE
 	  mailboxes.
 
+config PCI_DOE_EP
+	bool "PCI Endpoint Data Object Exchange (DOE) support"
+	depends on PCI_ENDPOINT
+	help
+	  This enables support for Data Object Exchange (DOE) protocol
+	  on PCI Endpoint controllers. It provides a communication
+	  mechanism through mailboxes, primarily used for PCIe security
+	  features.
+
+	  Say Y here if you want be able to communicate using PCIe DOE
+	  mailboxes.
+
+	  If unsure, say N.
+
 config PCI_ECAM
 	bool
 
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index e10cfe5a280b..66084e2ef9b2 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_PCI_P2PDMA)	+= p2pdma.o
 obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
 obj-$(CONFIG_VGA_ARB)		+= vgaarb.o
 obj-$(CONFIG_PCI_DOE)		+= doe.o
+obj-$(CONFIG_PCI_DOE_EP)	+= doe-ep.o
 obj-$(CONFIG_PCI_IDE)		+= ide.o
 obj-$(CONFIG_PCI_TSM)		+= tsm.o
 obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
diff --git a/drivers/pci/doe-ep.c b/drivers/pci/doe-ep.c
new file mode 100644
index 000000000000..ff0551644f54
--- /dev/null
+++ b/drivers/pci/doe-ep.c
@@ -0,0 +1,671 @@
+// SPDX-License-Identifier: GPL-2.0-only or MIT
+/*
+ * Data Object Exchange for PCIe Endpoint
+ *	PCIe r7.0, sec 6.30 DOE
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com
+ *	Aksh Garg <a-garg7@ti.com>
+ *	Siddharth Vadapalli <s-vadapalli@ti.com>
+ */
+
+#define dev_fmt(fmt) "DOE EP: " fmt
+
+#include <linux/bitfield.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/pci-epc.h>
+#include <linux/pci-doe.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/xarray.h>
+
+/*
+ * Combines function number and capability offset into a unique lookup key
+ * for storing/retrieving DOE mailboxes in an xarray.
+ */
+#define PCI_DOE_MB_KEY(func, offset) \
+	(((unsigned long)(func) << 16) | (offset))
+
+/**
+ * struct pci_doe_protocol - DOE protocol handler entry
+ * @vid: Vendor ID
+ * @type: Protocol type
+ * @handler: Handler function pointer
+ */
+struct pci_doe_protocol {
+	u16 vid;
+	u8 type;
+	pci_doe_protocol_handler_t handler;
+};
+
+/*
+ * Global registry of protocol handlers,
+ * holding struct pci_doe_protocol
+ */
+static DEFINE_XARRAY_ALLOC(pci_doe_protocols);
+
+/* Serializes concurrent accesses to pci_doe_protocols xarray */
+static DEFINE_MUTEX(protocols_mutex);
+
+/**
+ * struct pci_ep_doe_mb - State for a single DOE mailbox on EP
+ *
+ * This state is used to manage a single DOE mailbox capability on the
+ * endpoint side.
+ *
+ * @epc: PCI endpoint controller this mailbox belongs to
+ * @func_no: Physical function number of the function this mailbox belongs to
+ * @cap_offset: Capability offset
+ * @work_queue: Queue of work items
+ * @flags: Bit array of PCI_DOE_FLAG_* flags
+ */
+struct pci_ep_doe_mb {
+	struct pci_epc *epc;
+	u8 func_no;
+	u16 cap_offset;
+	struct workqueue_struct *work_queue;
+	unsigned long flags;
+};
+
+/**
+ * struct pci_ep_doe_task - Represents a single DOE request/response task
+ *
+ * @feat: DOE feature (vendor ID and type)
+ * @request_pl: Request payload
+ * @request_pl_sz: Size of request payload in bytes
+ * @response_pl: Output pointer for response buffer
+ * @response_pl_sz: Output pointer for response size
+ * @task_status: Task completion status (0 on success, -errno on failure)
+ * @complete: Completion callback
+ * @private: Private data for completion
+ * @work: Work structure for workqueue
+ * @doe_mb: DOE mailbox handling this task
+ */
+struct pci_ep_doe_task {
+	struct pci_doe_feature feat;
+	const void *request_pl;
+	size_t request_pl_sz;
+	void **response_pl;
+	size_t *response_pl_sz;
+	int task_status;
+	void (*complete)(struct pci_ep_doe_task *task);
+	void *private;
+
+	/* Initialized by pci_ep_doe_submit_task() */
+	struct work_struct work;
+	struct pci_ep_doe_mb *doe_mb;
+};
+
+/**
+ * pci_ep_doe_init() - Initialize the DOE framework for a controller in EP mode
+ * @epc: PCI endpoint controller
+ *
+ * Initialize the DOE framework data structures. This only initializes
+ * the xarray that will hold the mailboxes.
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+int pci_ep_doe_init(struct pci_epc *epc)
+{
+	if (!epc)
+		return -EINVAL;
+
+	xa_init(&epc->doe_mbs);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_init);
+
+/**
+ * pci_ep_doe_add_mailbox() - Add a DOE mailbox for a physical function
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: Offset of the DOE capability
+ *
+ * Create and register a DOE mailbox for the specified physical function
+ * and capability offset. The controller driver should call this for each
+ * DOE capability it finds in its config space.
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset)
+{
+	struct pci_ep_doe_mb *doe_mb;
+	unsigned long key;
+	int ret;
+
+	if (!epc)
+		return -EINVAL;
+
+	doe_mb = kzalloc(sizeof(*doe_mb), GFP_KERNEL);
+	if (!doe_mb)
+		return -ENOMEM;
+
+	doe_mb->epc = epc;
+	doe_mb->func_no = func_no;
+	doe_mb->cap_offset = cap_offset;
+
+	doe_mb->work_queue = alloc_ordered_workqueue("pci_ep_doe[%s:pf%d:offset%x]", 0,
+						     dev_name(&epc->dev),
+						     func_no, cap_offset);
+	if (!doe_mb->work_queue) {
+		dev_err(epc->dev.parent,
+			"[pf%d:offset%x] failed to allocate work queue\n",
+			func_no, cap_offset);
+		ret = -ENOMEM;
+		goto err_free;
+	}
+
+	/* Add to xarray with composite key */
+	key = PCI_DOE_MB_KEY(func_no, cap_offset);
+	ret = xa_insert(&epc->doe_mbs, key, doe_mb, GFP_KERNEL);
+	if (ret) {
+		dev_err(epc->dev.parent,
+			"[pf%d:offset%x] failed to insert mailbox: %d\n",
+			func_no, cap_offset, ret);
+		goto err_destroy;
+	}
+
+	dev_dbg(epc->dev.parent,
+		"DOE mailbox added: pf%d offset 0x%x\n",
+		func_no, cap_offset);
+
+	return 0;
+
+err_destroy:
+	destroy_workqueue(doe_mb->work_queue);
+err_free:
+	kfree(doe_mb);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_add_mailbox);
+
+/**
+ * pci_ep_doe_register_protocol() - Register a protocol handler
+ * @vendor: Vendor ID
+ * @type: Protocol type
+ * @handler: Handler function pointer
+ *
+ * Register a protocol handler that will be called when DOE requests
+ * with the specified vendor ID and type are received. Protocol libraries
+ * should call this during module initialization.
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+int pci_ep_doe_register_protocol(u16 vendor, u8 type,
+				 pci_doe_protocol_handler_t handler)
+{
+	struct pci_doe_protocol *protocol;
+	unsigned long index;
+	int ret;
+	u32 id;
+
+	if (!handler)
+		return -EINVAL;
+
+	/* Don't allow registering discovery protocol */
+	if (vendor == PCI_VENDOR_ID_PCI_SIG && type == PCI_DOE_FEATURE_DISCOVERY)
+		return -EINVAL;
+
+	mutex_lock(&protocols_mutex);
+
+	/* Check if already registered */
+	xa_for_each(&pci_doe_protocols, index, protocol) {
+		if (protocol->vid == vendor && protocol->type == type) {
+			ret = -EEXIST;
+			goto err;
+		}
+	}
+
+	protocol = kzalloc(sizeof(*protocol), GFP_KERNEL);
+	if (!protocol) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	protocol->vid = vendor;
+	protocol->type = type;
+	protocol->handler = handler;
+
+	ret = xa_alloc(&pci_doe_protocols, &id, protocol,
+		       xa_limit_32b, GFP_KERNEL);
+	if (ret) {
+		kfree(protocol);
+		goto err;
+	}
+
+	mutex_unlock(&protocols_mutex);
+
+	pr_debug("DOE EP: Registered protocol %04x:%02x at index %u\n",
+		 vendor, type, id);
+
+	return 0;
+
+err:
+	mutex_unlock(&protocols_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_register_protocol);
+
+/**
+ * pci_ep_doe_unregister_protocol() - Unregister a protocol handler
+ * @vendor: Vendor ID
+ * @type: Protocol type
+ *
+ * Unregister a previously registered protocol handler. Protocol libraries
+ * should call this during module cleanup.
+ */
+void pci_ep_doe_unregister_protocol(u16 vendor, u8 type)
+{
+	struct pci_doe_protocol *protocol;
+	unsigned long index;
+
+	mutex_lock(&protocols_mutex);
+
+	/* Find and remove the protocol entry */
+	xa_for_each(&pci_doe_protocols, index, protocol) {
+		if (protocol->vid == vendor && protocol->type == type) {
+			xa_erase(&pci_doe_protocols, index);
+			kfree(protocol);
+			mutex_unlock(&protocols_mutex);
+			pr_debug("DOE EP: Unregistered protocol %04x:%02x\n",
+				 vendor, type);
+			return;
+		}
+	}
+
+	mutex_unlock(&protocols_mutex);
+	pr_warn("DOE EP: Attempted to unregister non-existent protocol %04x:%02x\n",
+		vendor, type);
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_unregister_protocol);
+
+/**
+ * pci_ep_doe_cancel_tasks() - Cancel all pending tasks
+ * @doe_mb: DOE mailbox
+ *
+ * Cancel all pending tasks in the mailbox. Mark the mailbox as dead
+ * so no new tasks can be submitted.
+ */
+static void pci_ep_doe_cancel_tasks(struct pci_ep_doe_mb *doe_mb)
+{
+	if (!doe_mb)
+		return;
+
+	/* Mark the mailbox as dead */
+	set_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags);
+
+	/* Stop all pending work items from starting */
+	set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
+}
+
+/**
+ * pci_ep_doe_get_mailbox() - Get DOE mailbox by function and offset
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: Offset of the DOE capability
+ *
+ * Internal helper to look up a DOE mailbox by its function number and
+ * capability offset.
+ *
+ * RETURNS: Pointer to the mailbox or NULL if not found
+ */
+static struct pci_ep_doe_mb *pci_ep_doe_get_mailbox(struct pci_epc *epc,
+						    u8 func_no, u16 cap_offset)
+{
+	unsigned long key;
+
+	if (!epc)
+		return NULL;
+
+	key = PCI_DOE_MB_KEY(func_no, cap_offset);
+	return xa_load(&epc->doe_mbs, key);
+}
+
+/**
+ * pci_ep_doe_find_protocol() - Find protocol handler
+ * @vendor: Vendor ID
+ * @type: Protocol type
+ *
+ * Look up a protocol handler in the registered protocols.
+ *
+ * RETURNS: Handler function pointer or NULL if not found
+ */
+static pci_doe_protocol_handler_t pci_ep_doe_find_protocol(u16 vendor, u8 type)
+{
+	pci_doe_protocol_handler_t handler = NULL;
+	struct pci_doe_protocol *protocol;
+	unsigned long index;
+
+	mutex_lock(&protocols_mutex);
+
+	xa_for_each(&pci_doe_protocols, index, protocol) {
+		if (protocol->vid == vendor && protocol->type == type) {
+			handler = protocol->handler;
+			break;
+		}
+	}
+
+	mutex_unlock(&protocols_mutex);
+	return handler;
+}
+
+/**
+ * pci_ep_doe_handle_discovery() - Handle Discovery protocol request
+ * @request: Request payload
+ * @request_sz: Request size
+ * @response: Output pointer for response buffer
+ * @response_sz: Output pointer for response size
+ *
+ * Handle the DOE Discovery protocol. The request contains an index specifying
+ * which protocol to query. This function creates a response containing the
+ * vendor ID and protocol type for the requested index, along with the next
+ * index value for further discovery:
+ *
+ * - next_index = 0: Signals this is the last protocol supported
+ * - next_index = n (non-zero): Signals more protocols available,
+ *   query index n next
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+static int pci_ep_doe_handle_discovery(const void *request, size_t request_sz,
+				       void **response, size_t *response_sz)
+{
+	struct pci_doe_protocol *protocol;
+	u8 requested_index, next_index;
+	unsigned long index;
+	u8 xarray_count = 0;
+	u32 *response_pl;
+	u32 request_pl;
+	u16 vendor;
+	u8 type;
+
+	if (request_sz != sizeof(u32))
+		return -EINVAL;
+
+	request_pl = *(u32 *)request;
+	requested_index = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX, request_pl);
+
+	mutex_lock(&protocols_mutex);
+
+	/* Count protocols in xarray */
+	xa_for_each(&pci_doe_protocols, index, protocol)
+		xarray_count++;
+
+	if (requested_index > xarray_count) {
+		mutex_unlock(&protocols_mutex);
+		return -EINVAL;
+	}
+
+	/* Index 0 is always Discovery protocol */
+	if (requested_index == 0) {
+		vendor = PCI_VENDOR_ID_PCI_SIG;
+		type = PCI_DOE_FEATURE_DISCOVERY;
+	} else {
+		protocol = xa_load(&pci_doe_protocols, (unsigned long)(requested_index - 1));
+		if (!protocol) {
+			mutex_unlock(&protocols_mutex);
+			return -EINVAL;
+		}
+		vendor = protocol->vid;
+		type = protocol->type;
+	}
+	mutex_unlock(&protocols_mutex);
+
+	/* Calculate next index */
+	next_index = (requested_index < xarray_count) ? requested_index + 1 : 0;
+
+	response_pl = kzalloc(sizeof(*response_pl), GFP_KERNEL);
+	if (!response_pl)
+		return -ENOMEM;
+
+	/* Build response */
+	*response_pl = FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID, vendor) |
+		       FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE, type) |
+		       FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX, next_index);
+
+	*response = response_pl;
+	*response_sz = sizeof(*response_pl);
+
+	return 0;
+}
+
+static void signal_task_complete(struct pci_ep_doe_task *task, int status)
+{
+	task->task_status = status;
+	destroy_work_on_stack(&task->work);
+	task->complete(task);
+}
+
+/**
+ * doe_ep_task_work() - Work function for processing DOE EP tasks
+ * @work: Work structure
+ *
+ * Process a DOE request by calling the appropriate protocol handler.
+ */
+static void doe_ep_task_work(struct work_struct *work)
+{
+	struct pci_ep_doe_task *task = container_of(work, struct pci_ep_doe_task,
+						    work);
+	struct pci_ep_doe_mb *doe_mb = task->doe_mb;
+	pci_doe_protocol_handler_t handler;
+	int rc;
+
+	if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) {
+		signal_task_complete(task, -EIO);
+		return;
+	}
+
+	/* Check if request was aborted */
+	if (test_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags)) {
+		signal_task_complete(task, -ECANCELED);
+		return;
+	}
+
+	/* Handle Discovery protocol directly */
+	if (task->feat.vid == PCI_VENDOR_ID_PCI_SIG &&
+	    task->feat.type == PCI_DOE_FEATURE_DISCOVERY) {
+		rc = pci_ep_doe_handle_discovery(task->request_pl,
+						 task->request_pl_sz,
+						 task->response_pl,
+						 task->response_pl_sz);
+		signal_task_complete(task, rc);
+		return;
+	}
+
+	/* Find protocol handler in xarray */
+	handler = pci_ep_doe_find_protocol(task->feat.vid, task->feat.type);
+	if (!handler) {
+		dev_warn(doe_mb->epc->dev.parent,
+			 "[%d:%x] Unsupported protocol VID=%04x TYPE=%02x\n",
+			 doe_mb->func_no, doe_mb->cap_offset,
+			 task->feat.vid, task->feat.type);
+		signal_task_complete(task, -EOPNOTSUPP);
+		return;
+	}
+
+	/* Call protocol handler */
+	rc = handler(task->request_pl, task->request_pl_sz,
+		     task->response_pl, task->response_pl_sz);
+
+	signal_task_complete(task, rc);
+}
+
+/**
+ * pci_ep_doe_submit_task() - Submit a task to be processed
+ * @doe_mb: DOE mailbox
+ * @task: Task to submit
+ *
+ * Submit a DOE task to the workqueue for processing.
+ * Task must be allocated on the stack.
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+static int pci_ep_doe_submit_task(struct pci_ep_doe_mb *doe_mb,
+				  struct pci_ep_doe_task *task)
+{
+	if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags))
+		return -EIO;
+
+	task->doe_mb = doe_mb;
+	INIT_WORK_ONSTACK(&task->work, doe_ep_task_work);
+	queue_work(doe_mb->work_queue, &task->work);
+	return 0;
+}
+
+static void pci_ep_doe_task_complete(struct pci_ep_doe_task *task)
+{
+	complete(task->private);
+}
+
+/**
+ * pci_ep_doe_process_request() - Process DOE request on endpoint
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: DOE capability offset
+ * @vendor: Vendor ID from request header
+ * @type: Protocol type from request header
+ * @request: Request payload in CPU-native format
+ * @request_sz: Size of request payload (bytes)
+ * @response: Output pointer for response buffer in CPU-native format
+ * @response_sz: Output pointer for response size (bytes)
+ *
+ * Process a DOE request received on the endpoint. The request payload should
+ * not include the DOE header (vendor/type/length). The protocol handler will
+ * allocate the response buffer, which the caller (controller driver) must
+ * free after use.
+ *
+ * As per DOE specification, a mailbox processes one request at a time.
+ * Therefore, this function will never be called concurrently for the same
+ * mailbox by different callers.
+ *
+ * The caller is responsible for the conversion of the received DOE request
+ * with le32_to_cpu() before calling this function.
+ * Similarly, it is responsible for converting the response payload with
+ * cpu_to_le32() before sending it back over the DOE mailbox.
+ *
+ * The caller is also responsible for ensuring that the request size
+ * is within the limits defined by PCI_DOE_MAX_LENGTH.
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
+			       u16 vendor, u8 type, const void *request,
+			       size_t request_sz, void **response, size_t *response_sz)
+{
+	DECLARE_COMPLETION_ONSTACK(c);
+	struct pci_ep_doe_mb *doe_mb;
+	struct pci_ep_doe_task task = {
+		.feat.vid = vendor,
+		.feat.type = type,
+		.request_pl = request,
+		.request_pl_sz = request_sz,
+		.response_pl = response,
+		.response_pl_sz = response_sz,
+		.complete = pci_ep_doe_task_complete,
+		.private = &c,
+	};
+	int rc;
+
+	doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
+	if (!doe_mb)
+		return -ENODEV;
+
+	rc = pci_ep_doe_submit_task(doe_mb, &task);
+	if (rc)
+		return rc;
+
+	wait_for_completion(&c);
+
+	return task.task_status;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_process_request);
+
+/**
+ * pci_ep_doe_abort() - Abort DOE operations on a mailbox
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: DOE capability offset
+ *
+ * Abort all queued and wait for in-flight DOE operations to complete for the
+ * specified mailbox. This function is called by the EP controller driver
+ * when the RC sets the ABORT bit in the DOE Control register.
+ *
+ * The function will:
+ *
+ * - Set CANCEL flag to prevent new requests in the queue from starting
+ * - Wait for the currently executing handler to complete (cannot interrupt)
+ * - Flush the workqueue to wait for all requests to be handled appropriately
+ * - Clear CANCEL flag to prepare for new requests
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset)
+{
+	struct pci_ep_doe_mb *doe_mb;
+
+	if (!epc)
+		return -EINVAL;
+
+	doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
+	if (!doe_mb)
+		return -ENODEV;
+
+	/* Set CANCEL flag - worker will abort queued requests */
+	set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
+	flush_workqueue(doe_mb->work_queue);
+
+	/* Clear CANCEL flag - mailbox ready for new requests */
+	clear_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
+
+	dev_dbg(epc->dev.parent,
+		"DOE mailbox aborted: PF%d offset 0x%x\n",
+		func_no, cap_offset);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_abort);
+
+/**
+ * pci_ep_doe_destroy_mb() - Destroy a single DOE mailbox
+ * @doe_mb: DOE mailbox to destroy
+ *
+ * Internal function to destroy a mailbox and free its resources.
+ */
+static void pci_ep_doe_destroy_mb(struct pci_ep_doe_mb *doe_mb)
+{
+	if (!doe_mb)
+		return;
+
+	pci_ep_doe_cancel_tasks(doe_mb);
+
+	if (doe_mb->work_queue)
+		destroy_workqueue(doe_mb->work_queue);
+
+	kfree(doe_mb);
+}
+
+/**
+ * pci_ep_doe_destroy() - Destroy all DOE mailboxes
+ * @epc: PCI endpoint controller
+ *
+ * Destroy all DOE mailboxes and free associated resources.
+ *
+ * This function should be called during controller cleanup to free all
+ * DOE resources.
+ */
+void pci_ep_doe_destroy(struct pci_epc *epc)
+{
+	struct pci_ep_doe_mb *doe_mb;
+	unsigned long index;
+
+	if (!epc)
+		return;
+
+	xa_for_each(&epc->doe_mbs, index, doe_mb)
+		pci_ep_doe_destroy_mb(doe_mb);
+
+	xa_destroy(&epc->doe_mbs);
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_destroy);
diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
index a966626c47f9..b353c3a1aa82 100644
--- a/include/linux/pci-doe.h
+++ b/include/linux/pci-doe.h
@@ -13,6 +13,8 @@
 #ifndef LINUX_PCI_DOE_H
 #define LINUX_PCI_DOE_H
 
+#include <linux/types.h>
+
 /* Mailbox state flags */
 #define PCI_DOE_FLAG_CANCEL		0
 #define PCI_DOE_FLAG_DEAD		1
@@ -24,12 +26,19 @@
 #define PCI_DOE_FEATURE_CMA		1
 #define PCI_DOE_FEATURE_SSESSION	2
 
+typedef int (*pci_doe_protocol_handler_t)(const void *request,
+					  size_t request_sz,
+					  void **response,
+					  size_t *response_sz);
+
 struct pci_doe_feature {
 	u16 vid;
 	u8 type;
 };
 
 struct pci_doe_mb;
+struct pci_dev;
+struct pci_epc;
 
 #ifdef CONFIG_PCI_DOE
 struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
@@ -54,4 +63,64 @@ static inline int pci_doe(struct pci_doe_mb *doe_mb, u16 vendor, u8 type,
 }
 #endif /* CONFIG_PCI_DOE */
 
+#ifdef CONFIG_PCI_DOE_EP
+int pci_ep_doe_init(struct pci_epc *epc);
+
+int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset);
+
+int pci_ep_doe_register_protocol(u16 vendor, u8 type,
+				 pci_doe_protocol_handler_t handler);
+
+void pci_ep_doe_unregister_protocol(u16 vendor, u8 type);
+
+int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
+			       u16 vendor, u8 type, const void *request,
+			       size_t request_sz, void **response, size_t *response_sz);
+
+int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset);
+
+void pci_ep_doe_destroy(struct pci_epc *epc);
+
+#else
+static inline int pci_ep_doe_init(struct pci_epc *epc)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no,
+					 u16 cap_offset)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int pci_ep_doe_register_protocol(u16 vendor, u8 type,
+					       pci_doe_protocol_handler_t handler)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void pci_ep_doe_unregister_protocol(u16 vendor, u8 type)
+{
+}
+
+static inline int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
+					     u16 vendor, u8 type,
+					     const void *request, size_t request_sz,
+					     void **response, size_t *response_sz)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no,
+				   u16 cap_offset)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void pci_ep_doe_destroy(struct pci_epc *epc)
+{
+}
+
+#endif /* CONFIG_PCI_DOE_EP */
+
 #endif /* LINUX_PCI_DOE_H */
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 4286bfdbfdfa..ad4de4d33da3 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -165,6 +165,7 @@ struct pci_epc_mem {
  * @domain_nr: PCI domain number of the endpoint controller
  * @init_complete: flag to indicate whether the EPC initialization is complete
  *                 or not
+ * @doe_mbs: array of DOE mailboxes (CONFIG_PCI_DOE_EP)
  */
 struct pci_epc {
 	struct device			dev;
@@ -182,6 +183,9 @@ struct pci_epc {
 	unsigned long			function_num_map;
 	int				domain_nr;
 	bool				init_complete;
+#ifdef CONFIG_PCI_DOE_EP
+	struct xarray			doe_mbs;
+#endif
 };
 
 /**
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [RFC PATCH 4/4] PCI: Document APIs for endpoint DOE implementation
  2026-02-13 12:35 [RFC PATCH 0/4] PCI: Add DOE support for endpoint Aksh Garg
                   ` (2 preceding siblings ...)
  2026-02-13 12:36 ` [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions Aksh Garg
@ 2026-02-13 12:36 ` Aksh Garg
  2026-02-13 13:16 ` [RFC PATCH 0/4] PCI: Add DOE support for endpoint Niklas Cassel
  2026-03-04 14:22 ` Manivannan Sadhasivam
  5 siblings, 0 replies; 19+ messages in thread
From: Aksh Garg @ 2026-02-13 12:36 UTC (permalink / raw)
  To: linux-pci, linux-doc, bhelgaas, corbet, cassel
  Cc: linux-kernel, linux-arm-kernel, s-vadapalli, danishanwar, srk,
	a-garg7

From: Aksh Garg <a-garg7@ti.com>

Add kernel-doc API reference section to the DOE endpoint documentation,
documenting the exported functions from drivers/pci/doe-ep.c for use by
endpoint controller drivers and protocol handler modules.

Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
 Documentation/PCI/pci-doe-ep.rst | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/PCI/pci-doe-ep.rst b/Documentation/PCI/pci-doe-ep.rst
index d6715944c117..6ad064e838e7 100644
--- a/Documentation/PCI/pci-doe-ep.rst
+++ b/Documentation/PCI/pci-doe-ep.rst
@@ -289,3 +289,9 @@ When the controller driver receives an error code from
 ``pci_ep_doe_process_request()``, it sets the ERROR bit in the DOE Status
 register instead of writing a response to the read mailbox register,
 and frees the buffers.
+
+API Reference
+=============
+
+.. kernel-doc:: drivers/pci/doe-ep.c
+   :export:
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 0/4] PCI: Add DOE support for endpoint
  2026-02-13 12:35 [RFC PATCH 0/4] PCI: Add DOE support for endpoint Aksh Garg
                   ` (3 preceding siblings ...)
  2026-02-13 12:36 ` [RFC PATCH 4/4] PCI: Document APIs for endpoint DOE implementation Aksh Garg
@ 2026-02-13 13:16 ` Niklas Cassel
  2026-03-04 14:22 ` Manivannan Sadhasivam
  5 siblings, 0 replies; 19+ messages in thread
From: Niklas Cassel @ 2026-02-13 13:16 UTC (permalink / raw)
  To: Aksh Garg
  Cc: linux-pci, linux-doc, bhelgaas, corbet, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk

Hello Aksh,

On Fri, Feb 13, 2026 at 06:05:59PM +0530, Aksh Garg wrote:

(snip)

>  create mode 100644 Documentation/PCI/pci-doe-ep.rst

I think it would make much more sense to create this file as:
Documentation/PCI/endpoint/pci-endpoint-doe.rst
(To match the existing naming scheme.)


>  create mode 100644 drivers/pci/doe-ep.c

I think it would make much more sense to create this file as:
drivers/pci/endpoint/pci-ep-doe.c
(To match the existing naming scheme.)


Kind regards,
Niklas

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions
  2026-02-13 12:36 ` [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions Aksh Garg
@ 2026-02-13 13:21   ` Niklas Cassel
  2026-02-18  4:28   ` Alistair Francis
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 19+ messages in thread
From: Niklas Cassel @ 2026-02-13 13:21 UTC (permalink / raw)
  To: Aksh Garg
  Cc: linux-pci, linux-doc, bhelgaas, corbet, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk

On Fri, Feb 13, 2026 at 06:06:02PM +0530, Aksh Garg wrote:

(snip)

> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> index e3f848ffb52a..2a57a314054c 100644
> --- a/drivers/pci/Kconfig
> +++ b/drivers/pci/Kconfig
> @@ -146,6 +146,20 @@ config PCI_DOE
>  	  Say Y here if you want be able to communicate with PCIe DOE
>  	  mailboxes.
>  
> +config PCI_DOE_EP
> +	bool "PCI Endpoint Data Object Exchange (DOE) support"
> +	depends on PCI_ENDPOINT
> +	help
> +	  This enables support for Data Object Exchange (DOE) protocol
> +	  on PCI Endpoint controllers. It provides a communication
> +	  mechanism through mailboxes, primarily used for PCIe security
> +	  features.
> +
> +	  Say Y here if you want be able to communicate using PCIe DOE
> +	  mailboxes.
> +
> +	  If unsure, say N.
> +

I think this new Kconfig should live in:
drivers/pci/endpoint/Kconfig


Kind regards,
Niklas

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 1/4] PCI: Add documentation for DOE endpoint support
  2026-02-13 12:36 ` [RFC PATCH 1/4] PCI: Add documentation for DOE endpoint support Aksh Garg
@ 2026-02-13 20:33   ` Niklas Cassel
  2026-02-18 11:21     ` Aksh Garg
  0 siblings, 1 reply; 19+ messages in thread
From: Niklas Cassel @ 2026-02-13 20:33 UTC (permalink / raw)
  To: Aksh Garg
  Cc: linux-pci, linux-doc, bhelgaas, corbet, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk

On Fri, Feb 13, 2026 at 06:06:00PM +0530, Aksh Garg wrote:
> From: Aksh Garg <a-garg7@ti.com>
> 
> Document the architecture and implementation details for the Data Object
> Exchange (DOE) framework for PCIe Endpoint devices.
> 
> Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
> Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
> Signed-off-by: Aksh Garg <a-garg7@ti.com>
> ---

Usually documentation is added as the final patch in a series.

I think this patch should just be squashed with patch 4/4, and the
squashed patch then being the final patch in the series - patch 3/3.


Kind regards,
Niklas

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions
  2026-02-13 12:36 ` [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions Aksh Garg
  2026-02-13 13:21   ` Niklas Cassel
@ 2026-02-18  4:28   ` Alistair Francis
  2026-03-04 14:18     ` Manivannan Sadhasivam
  2026-02-22 13:06   ` Lukas Wunner
  2026-03-04 14:17   ` Manivannan Sadhasivam
  3 siblings, 1 reply; 19+ messages in thread
From: Alistair Francis @ 2026-02-18  4:28 UTC (permalink / raw)
  To: Aksh Garg, linux-pci, linux-doc, bhelgaas, corbet, cassel
  Cc: linux-kernel, linux-arm-kernel, s-vadapalli, danishanwar, srk

On 13/2/26 22:36, Aksh Garg wrote:
> From: Aksh Garg <a-garg7@ti.com>
> 
> DOE (Data Object Exchange) is a standard PCIe extended capability
> feature introduced in the Data Object Exchange (DOE) ECN for
> PCIe r5.0. It provides a communication mechanism primarily used for
> implementing PCIe security features such as device authentication, and
> secure link establishment. Think of DOE as a sophisticated mailbox
> system built into PCIe. The root complex can send structured requests
> to the endpoint device through DOE mailboxes, and the endpoint device
> responds with appropriate data.
> 
> Add the DOE support for PCIe endpoint devices, enabling endpoint
> functions to process the DOE requests from the host. The implementation
> provides framework APIs for controller drivers to register mailboxes,
> protocol handler registration for different DOE data object types, and
> request processing with workqueues ensuring sequential handling per
> mailbox. The Discovery protocol is handled internally by the DOE core.
> 
> This implementation complements the existing DOE implementation for
> root complex in drivers/pci/doe.c.

This looks good to me!

I would love to see a handler implementation and integration with a 
driver as well.

For SPDM the handler could even be in userspace

Alistair

> 
> Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
> Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
> Signed-off-by: Aksh Garg <a-garg7@ti.com>
> ---
>   drivers/pci/Kconfig     |  14 +
>   drivers/pci/Makefile    |   1 +
>   drivers/pci/doe-ep.c    | 671 ++++++++++++++++++++++++++++++++++++++++
>   include/linux/pci-doe.h |  69 +++++
>   include/linux/pci-epc.h |   4 +
>   5 files changed, 759 insertions(+)
>   create mode 100644 drivers/pci/doe-ep.c
> 
> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> index e3f848ffb52a..2a57a314054c 100644
> --- a/drivers/pci/Kconfig
> +++ b/drivers/pci/Kconfig
> @@ -146,6 +146,20 @@ config PCI_DOE
>   	  Say Y here if you want be able to communicate with PCIe DOE
>   	  mailboxes.
>   
> +config PCI_DOE_EP
> +	bool "PCI Endpoint Data Object Exchange (DOE) support"
> +	depends on PCI_ENDPOINT
> +	help
> +	  This enables support for Data Object Exchange (DOE) protocol
> +	  on PCI Endpoint controllers. It provides a communication
> +	  mechanism through mailboxes, primarily used for PCIe security
> +	  features.
> +
> +	  Say Y here if you want be able to communicate using PCIe DOE
> +	  mailboxes.
> +
> +	  If unsure, say N.
> +
>   config PCI_ECAM
>   	bool
>   
> diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
> index e10cfe5a280b..66084e2ef9b2 100644
> --- a/drivers/pci/Makefile
> +++ b/drivers/pci/Makefile
> @@ -34,6 +34,7 @@ obj-$(CONFIG_PCI_P2PDMA)	+= p2pdma.o
>   obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
>   obj-$(CONFIG_VGA_ARB)		+= vgaarb.o
>   obj-$(CONFIG_PCI_DOE)		+= doe.o
> +obj-$(CONFIG_PCI_DOE_EP)	+= doe-ep.o
>   obj-$(CONFIG_PCI_IDE)		+= ide.o
>   obj-$(CONFIG_PCI_TSM)		+= tsm.o
>   obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
> diff --git a/drivers/pci/doe-ep.c b/drivers/pci/doe-ep.c
> new file mode 100644
> index 000000000000..ff0551644f54
> --- /dev/null
> +++ b/drivers/pci/doe-ep.c
> @@ -0,0 +1,671 @@
> +// SPDX-License-Identifier: GPL-2.0-only or MIT
> +/*
> + * Data Object Exchange for PCIe Endpoint
> + *	PCIe r7.0, sec 6.30 DOE
> + *
> + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com
> + *	Aksh Garg <a-garg7@ti.com>
> + *	Siddharth Vadapalli <s-vadapalli@ti.com>
> + */
> +
> +#define dev_fmt(fmt) "DOE EP: " fmt
> +
> +#include <linux/bitfield.h>
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/mutex.h>
> +#include <linux/pci.h>
> +#include <linux/pci-epc.h>
> +#include <linux/pci-doe.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#include <linux/xarray.h>
> +
> +/*
> + * Combines function number and capability offset into a unique lookup key
> + * for storing/retrieving DOE mailboxes in an xarray.
> + */
> +#define PCI_DOE_MB_KEY(func, offset) \
> +	(((unsigned long)(func) << 16) | (offset))
> +
> +/**
> + * struct pci_doe_protocol - DOE protocol handler entry
> + * @vid: Vendor ID
> + * @type: Protocol type
> + * @handler: Handler function pointer
> + */
> +struct pci_doe_protocol {
> +	u16 vid;
> +	u8 type;
> +	pci_doe_protocol_handler_t handler;
> +};
> +
> +/*
> + * Global registry of protocol handlers,
> + * holding struct pci_doe_protocol
> + */
> +static DEFINE_XARRAY_ALLOC(pci_doe_protocols);
> +
> +/* Serializes concurrent accesses to pci_doe_protocols xarray */
> +static DEFINE_MUTEX(protocols_mutex);
> +
> +/**
> + * struct pci_ep_doe_mb - State for a single DOE mailbox on EP
> + *
> + * This state is used to manage a single DOE mailbox capability on the
> + * endpoint side.
> + *
> + * @epc: PCI endpoint controller this mailbox belongs to
> + * @func_no: Physical function number of the function this mailbox belongs to
> + * @cap_offset: Capability offset
> + * @work_queue: Queue of work items
> + * @flags: Bit array of PCI_DOE_FLAG_* flags
> + */
> +struct pci_ep_doe_mb {
> +	struct pci_epc *epc;
> +	u8 func_no;
> +	u16 cap_offset;
> +	struct workqueue_struct *work_queue;
> +	unsigned long flags;
> +};
> +
> +/**
> + * struct pci_ep_doe_task - Represents a single DOE request/response task
> + *
> + * @feat: DOE feature (vendor ID and type)
> + * @request_pl: Request payload
> + * @request_pl_sz: Size of request payload in bytes
> + * @response_pl: Output pointer for response buffer
> + * @response_pl_sz: Output pointer for response size
> + * @task_status: Task completion status (0 on success, -errno on failure)
> + * @complete: Completion callback
> + * @private: Private data for completion
> + * @work: Work structure for workqueue
> + * @doe_mb: DOE mailbox handling this task
> + */
> +struct pci_ep_doe_task {
> +	struct pci_doe_feature feat;
> +	const void *request_pl;
> +	size_t request_pl_sz;
> +	void **response_pl;
> +	size_t *response_pl_sz;
> +	int task_status;
> +	void (*complete)(struct pci_ep_doe_task *task);
> +	void *private;
> +
> +	/* Initialized by pci_ep_doe_submit_task() */
> +	struct work_struct work;
> +	struct pci_ep_doe_mb *doe_mb;
> +};
> +
> +/**
> + * pci_ep_doe_init() - Initialize the DOE framework for a controller in EP mode
> + * @epc: PCI endpoint controller
> + *
> + * Initialize the DOE framework data structures. This only initializes
> + * the xarray that will hold the mailboxes.
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +int pci_ep_doe_init(struct pci_epc *epc)
> +{
> +	if (!epc)
> +		return -EINVAL;
> +
> +	xa_init(&epc->doe_mbs);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_init);
> +
> +/**
> + * pci_ep_doe_add_mailbox() - Add a DOE mailbox for a physical function
> + * @epc: PCI endpoint controller
> + * @func_no: Physical function number
> + * @cap_offset: Offset of the DOE capability
> + *
> + * Create and register a DOE mailbox for the specified physical function
> + * and capability offset. The controller driver should call this for each
> + * DOE capability it finds in its config space.
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset)
> +{
> +	struct pci_ep_doe_mb *doe_mb;
> +	unsigned long key;
> +	int ret;
> +
> +	if (!epc)
> +		return -EINVAL;
> +
> +	doe_mb = kzalloc(sizeof(*doe_mb), GFP_KERNEL);
> +	if (!doe_mb)
> +		return -ENOMEM;
> +
> +	doe_mb->epc = epc;
> +	doe_mb->func_no = func_no;
> +	doe_mb->cap_offset = cap_offset;
> +
> +	doe_mb->work_queue = alloc_ordered_workqueue("pci_ep_doe[%s:pf%d:offset%x]", 0,
> +						     dev_name(&epc->dev),
> +						     func_no, cap_offset);
> +	if (!doe_mb->work_queue) {
> +		dev_err(epc->dev.parent,
> +			"[pf%d:offset%x] failed to allocate work queue\n",
> +			func_no, cap_offset);
> +		ret = -ENOMEM;
> +		goto err_free;
> +	}
> +
> +	/* Add to xarray with composite key */
> +	key = PCI_DOE_MB_KEY(func_no, cap_offset);
> +	ret = xa_insert(&epc->doe_mbs, key, doe_mb, GFP_KERNEL);
> +	if (ret) {
> +		dev_err(epc->dev.parent,
> +			"[pf%d:offset%x] failed to insert mailbox: %d\n",
> +			func_no, cap_offset, ret);
> +		goto err_destroy;
> +	}
> +
> +	dev_dbg(epc->dev.parent,
> +		"DOE mailbox added: pf%d offset 0x%x\n",
> +		func_no, cap_offset);
> +
> +	return 0;
> +
> +err_destroy:
> +	destroy_workqueue(doe_mb->work_queue);
> +err_free:
> +	kfree(doe_mb);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_add_mailbox);
> +
> +/**
> + * pci_ep_doe_register_protocol() - Register a protocol handler
> + * @vendor: Vendor ID
> + * @type: Protocol type
> + * @handler: Handler function pointer
> + *
> + * Register a protocol handler that will be called when DOE requests
> + * with the specified vendor ID and type are received. Protocol libraries
> + * should call this during module initialization.
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +int pci_ep_doe_register_protocol(u16 vendor, u8 type,
> +				 pci_doe_protocol_handler_t handler)
> +{
> +	struct pci_doe_protocol *protocol;
> +	unsigned long index;
> +	int ret;
> +	u32 id;
> +
> +	if (!handler)
> +		return -EINVAL;
> +
> +	/* Don't allow registering discovery protocol */
> +	if (vendor == PCI_VENDOR_ID_PCI_SIG && type == PCI_DOE_FEATURE_DISCOVERY)
> +		return -EINVAL;
> +
> +	mutex_lock(&protocols_mutex);
> +
> +	/* Check if already registered */
> +	xa_for_each(&pci_doe_protocols, index, protocol) {
> +		if (protocol->vid == vendor && protocol->type == type) {
> +			ret = -EEXIST;
> +			goto err;
> +		}
> +	}
> +
> +	protocol = kzalloc(sizeof(*protocol), GFP_KERNEL);
> +	if (!protocol) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	protocol->vid = vendor;
> +	protocol->type = type;
> +	protocol->handler = handler;
> +
> +	ret = xa_alloc(&pci_doe_protocols, &id, protocol,
> +		       xa_limit_32b, GFP_KERNEL);
> +	if (ret) {
> +		kfree(protocol);
> +		goto err;
> +	}
> +
> +	mutex_unlock(&protocols_mutex);
> +
> +	pr_debug("DOE EP: Registered protocol %04x:%02x at index %u\n",
> +		 vendor, type, id);
> +
> +	return 0;
> +
> +err:
> +	mutex_unlock(&protocols_mutex);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_register_protocol);
> +
> +/**
> + * pci_ep_doe_unregister_protocol() - Unregister a protocol handler
> + * @vendor: Vendor ID
> + * @type: Protocol type
> + *
> + * Unregister a previously registered protocol handler. Protocol libraries
> + * should call this during module cleanup.
> + */
> +void pci_ep_doe_unregister_protocol(u16 vendor, u8 type)
> +{
> +	struct pci_doe_protocol *protocol;
> +	unsigned long index;
> +
> +	mutex_lock(&protocols_mutex);
> +
> +	/* Find and remove the protocol entry */
> +	xa_for_each(&pci_doe_protocols, index, protocol) {
> +		if (protocol->vid == vendor && protocol->type == type) {
> +			xa_erase(&pci_doe_protocols, index);
> +			kfree(protocol);
> +			mutex_unlock(&protocols_mutex);
> +			pr_debug("DOE EP: Unregistered protocol %04x:%02x\n",
> +				 vendor, type);
> +			return;
> +		}
> +	}
> +
> +	mutex_unlock(&protocols_mutex);
> +	pr_warn("DOE EP: Attempted to unregister non-existent protocol %04x:%02x\n",
> +		vendor, type);
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_unregister_protocol);
> +
> +/**
> + * pci_ep_doe_cancel_tasks() - Cancel all pending tasks
> + * @doe_mb: DOE mailbox
> + *
> + * Cancel all pending tasks in the mailbox. Mark the mailbox as dead
> + * so no new tasks can be submitted.
> + */
> +static void pci_ep_doe_cancel_tasks(struct pci_ep_doe_mb *doe_mb)
> +{
> +	if (!doe_mb)
> +		return;
> +
> +	/* Mark the mailbox as dead */
> +	set_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags);
> +
> +	/* Stop all pending work items from starting */
> +	set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
> +}
> +
> +/**
> + * pci_ep_doe_get_mailbox() - Get DOE mailbox by function and offset
> + * @epc: PCI endpoint controller
> + * @func_no: Physical function number
> + * @cap_offset: Offset of the DOE capability
> + *
> + * Internal helper to look up a DOE mailbox by its function number and
> + * capability offset.
> + *
> + * RETURNS: Pointer to the mailbox or NULL if not found
> + */
> +static struct pci_ep_doe_mb *pci_ep_doe_get_mailbox(struct pci_epc *epc,
> +						    u8 func_no, u16 cap_offset)
> +{
> +	unsigned long key;
> +
> +	if (!epc)
> +		return NULL;
> +
> +	key = PCI_DOE_MB_KEY(func_no, cap_offset);
> +	return xa_load(&epc->doe_mbs, key);
> +}
> +
> +/**
> + * pci_ep_doe_find_protocol() - Find protocol handler
> + * @vendor: Vendor ID
> + * @type: Protocol type
> + *
> + * Look up a protocol handler in the registered protocols.
> + *
> + * RETURNS: Handler function pointer or NULL if not found
> + */
> +static pci_doe_protocol_handler_t pci_ep_doe_find_protocol(u16 vendor, u8 type)
> +{
> +	pci_doe_protocol_handler_t handler = NULL;
> +	struct pci_doe_protocol *protocol;
> +	unsigned long index;
> +
> +	mutex_lock(&protocols_mutex);
> +
> +	xa_for_each(&pci_doe_protocols, index, protocol) {
> +		if (protocol->vid == vendor && protocol->type == type) {
> +			handler = protocol->handler;
> +			break;
> +		}
> +	}
> +
> +	mutex_unlock(&protocols_mutex);
> +	return handler;
> +}
> +
> +/**
> + * pci_ep_doe_handle_discovery() - Handle Discovery protocol request
> + * @request: Request payload
> + * @request_sz: Request size
> + * @response: Output pointer for response buffer
> + * @response_sz: Output pointer for response size
> + *
> + * Handle the DOE Discovery protocol. The request contains an index specifying
> + * which protocol to query. This function creates a response containing the
> + * vendor ID and protocol type for the requested index, along with the next
> + * index value for further discovery:
> + *
> + * - next_index = 0: Signals this is the last protocol supported
> + * - next_index = n (non-zero): Signals more protocols available,
> + *   query index n next
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +static int pci_ep_doe_handle_discovery(const void *request, size_t request_sz,
> +				       void **response, size_t *response_sz)
> +{
> +	struct pci_doe_protocol *protocol;
> +	u8 requested_index, next_index;
> +	unsigned long index;
> +	u8 xarray_count = 0;
> +	u32 *response_pl;
> +	u32 request_pl;
> +	u16 vendor;
> +	u8 type;
> +
> +	if (request_sz != sizeof(u32))
> +		return -EINVAL;
> +
> +	request_pl = *(u32 *)request;
> +	requested_index = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX, request_pl);
> +
> +	mutex_lock(&protocols_mutex);
> +
> +	/* Count protocols in xarray */
> +	xa_for_each(&pci_doe_protocols, index, protocol)
> +		xarray_count++;
> +
> +	if (requested_index > xarray_count) {
> +		mutex_unlock(&protocols_mutex);
> +		return -EINVAL;
> +	}
> +
> +	/* Index 0 is always Discovery protocol */
> +	if (requested_index == 0) {
> +		vendor = PCI_VENDOR_ID_PCI_SIG;
> +		type = PCI_DOE_FEATURE_DISCOVERY;
> +	} else {
> +		protocol = xa_load(&pci_doe_protocols, (unsigned long)(requested_index - 1));
> +		if (!protocol) {
> +			mutex_unlock(&protocols_mutex);
> +			return -EINVAL;
> +		}
> +		vendor = protocol->vid;
> +		type = protocol->type;
> +	}
> +	mutex_unlock(&protocols_mutex);
> +
> +	/* Calculate next index */
> +	next_index = (requested_index < xarray_count) ? requested_index + 1 : 0;
> +
> +	response_pl = kzalloc(sizeof(*response_pl), GFP_KERNEL);
> +	if (!response_pl)
> +		return -ENOMEM;
> +
> +	/* Build response */
> +	*response_pl = FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID, vendor) |
> +		       FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE, type) |
> +		       FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX, next_index);
> +
> +	*response = response_pl;
> +	*response_sz = sizeof(*response_pl);
> +
> +	return 0;
> +}
> +
> +static void signal_task_complete(struct pci_ep_doe_task *task, int status)
> +{
> +	task->task_status = status;
> +	destroy_work_on_stack(&task->work);
> +	task->complete(task);
> +}
> +
> +/**
> + * doe_ep_task_work() - Work function for processing DOE EP tasks
> + * @work: Work structure
> + *
> + * Process a DOE request by calling the appropriate protocol handler.
> + */
> +static void doe_ep_task_work(struct work_struct *work)
> +{
> +	struct pci_ep_doe_task *task = container_of(work, struct pci_ep_doe_task,
> +						    work);
> +	struct pci_ep_doe_mb *doe_mb = task->doe_mb;
> +	pci_doe_protocol_handler_t handler;
> +	int rc;
> +
> +	if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) {
> +		signal_task_complete(task, -EIO);
> +		return;
> +	}
> +
> +	/* Check if request was aborted */
> +	if (test_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags)) {
> +		signal_task_complete(task, -ECANCELED);
> +		return;
> +	}
> +
> +	/* Handle Discovery protocol directly */
> +	if (task->feat.vid == PCI_VENDOR_ID_PCI_SIG &&
> +	    task->feat.type == PCI_DOE_FEATURE_DISCOVERY) {
> +		rc = pci_ep_doe_handle_discovery(task->request_pl,
> +						 task->request_pl_sz,
> +						 task->response_pl,
> +						 task->response_pl_sz);
> +		signal_task_complete(task, rc);
> +		return;
> +	}
> +
> +	/* Find protocol handler in xarray */
> +	handler = pci_ep_doe_find_protocol(task->feat.vid, task->feat.type);
> +	if (!handler) {
> +		dev_warn(doe_mb->epc->dev.parent,
> +			 "[%d:%x] Unsupported protocol VID=%04x TYPE=%02x\n",
> +			 doe_mb->func_no, doe_mb->cap_offset,
> +			 task->feat.vid, task->feat.type);
> +		signal_task_complete(task, -EOPNOTSUPP);
> +		return;
> +	}
> +
> +	/* Call protocol handler */
> +	rc = handler(task->request_pl, task->request_pl_sz,
> +		     task->response_pl, task->response_pl_sz);
> +
> +	signal_task_complete(task, rc);
> +}
> +
> +/**
> + * pci_ep_doe_submit_task() - Submit a task to be processed
> + * @doe_mb: DOE mailbox
> + * @task: Task to submit
> + *
> + * Submit a DOE task to the workqueue for processing.
> + * Task must be allocated on the stack.
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +static int pci_ep_doe_submit_task(struct pci_ep_doe_mb *doe_mb,
> +				  struct pci_ep_doe_task *task)
> +{
> +	if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags))
> +		return -EIO;
> +
> +	task->doe_mb = doe_mb;
> +	INIT_WORK_ONSTACK(&task->work, doe_ep_task_work);
> +	queue_work(doe_mb->work_queue, &task->work);
> +	return 0;
> +}
> +
> +static void pci_ep_doe_task_complete(struct pci_ep_doe_task *task)
> +{
> +	complete(task->private);
> +}
> +
> +/**
> + * pci_ep_doe_process_request() - Process DOE request on endpoint
> + * @epc: PCI endpoint controller
> + * @func_no: Physical function number
> + * @cap_offset: DOE capability offset
> + * @vendor: Vendor ID from request header
> + * @type: Protocol type from request header
> + * @request: Request payload in CPU-native format
> + * @request_sz: Size of request payload (bytes)
> + * @response: Output pointer for response buffer in CPU-native format
> + * @response_sz: Output pointer for response size (bytes)
> + *
> + * Process a DOE request received on the endpoint. The request payload should
> + * not include the DOE header (vendor/type/length). The protocol handler will
> + * allocate the response buffer, which the caller (controller driver) must
> + * free after use.
> + *
> + * As per DOE specification, a mailbox processes one request at a time.
> + * Therefore, this function will never be called concurrently for the same
> + * mailbox by different callers.
> + *
> + * The caller is responsible for the conversion of the received DOE request
> + * with le32_to_cpu() before calling this function.
> + * Similarly, it is responsible for converting the response payload with
> + * cpu_to_le32() before sending it back over the DOE mailbox.
> + *
> + * The caller is also responsible for ensuring that the request size
> + * is within the limits defined by PCI_DOE_MAX_LENGTH.
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
> +			       u16 vendor, u8 type, const void *request,
> +			       size_t request_sz, void **response, size_t *response_sz)
> +{
> +	DECLARE_COMPLETION_ONSTACK(c);
> +	struct pci_ep_doe_mb *doe_mb;
> +	struct pci_ep_doe_task task = {
> +		.feat.vid = vendor,
> +		.feat.type = type,
> +		.request_pl = request,
> +		.request_pl_sz = request_sz,
> +		.response_pl = response,
> +		.response_pl_sz = response_sz,
> +		.complete = pci_ep_doe_task_complete,
> +		.private = &c,
> +	};
> +	int rc;
> +
> +	doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
> +	if (!doe_mb)
> +		return -ENODEV;
> +
> +	rc = pci_ep_doe_submit_task(doe_mb, &task);
> +	if (rc)
> +		return rc;
> +
> +	wait_for_completion(&c);
> +
> +	return task.task_status;
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_process_request);
> +
> +/**
> + * pci_ep_doe_abort() - Abort DOE operations on a mailbox
> + * @epc: PCI endpoint controller
> + * @func_no: Physical function number
> + * @cap_offset: DOE capability offset
> + *
> + * Abort all queued and wait for in-flight DOE operations to complete for the
> + * specified mailbox. This function is called by the EP controller driver
> + * when the RC sets the ABORT bit in the DOE Control register.
> + *
> + * The function will:
> + *
> + * - Set CANCEL flag to prevent new requests in the queue from starting
> + * - Wait for the currently executing handler to complete (cannot interrupt)
> + * - Flush the workqueue to wait for all requests to be handled appropriately
> + * - Clear CANCEL flag to prepare for new requests
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset)
> +{
> +	struct pci_ep_doe_mb *doe_mb;
> +
> +	if (!epc)
> +		return -EINVAL;
> +
> +	doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
> +	if (!doe_mb)
> +		return -ENODEV;
> +
> +	/* Set CANCEL flag - worker will abort queued requests */
> +	set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
> +	flush_workqueue(doe_mb->work_queue);
> +
> +	/* Clear CANCEL flag - mailbox ready for new requests */
> +	clear_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
> +
> +	dev_dbg(epc->dev.parent,
> +		"DOE mailbox aborted: PF%d offset 0x%x\n",
> +		func_no, cap_offset);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_abort);
> +
> +/**
> + * pci_ep_doe_destroy_mb() - Destroy a single DOE mailbox
> + * @doe_mb: DOE mailbox to destroy
> + *
> + * Internal function to destroy a mailbox and free its resources.
> + */
> +static void pci_ep_doe_destroy_mb(struct pci_ep_doe_mb *doe_mb)
> +{
> +	if (!doe_mb)
> +		return;
> +
> +	pci_ep_doe_cancel_tasks(doe_mb);
> +
> +	if (doe_mb->work_queue)
> +		destroy_workqueue(doe_mb->work_queue);
> +
> +	kfree(doe_mb);
> +}
> +
> +/**
> + * pci_ep_doe_destroy() - Destroy all DOE mailboxes
> + * @epc: PCI endpoint controller
> + *
> + * Destroy all DOE mailboxes and free associated resources.
> + *
> + * This function should be called during controller cleanup to free all
> + * DOE resources.
> + */
> +void pci_ep_doe_destroy(struct pci_epc *epc)
> +{
> +	struct pci_ep_doe_mb *doe_mb;
> +	unsigned long index;
> +
> +	if (!epc)
> +		return;
> +
> +	xa_for_each(&epc->doe_mbs, index, doe_mb)
> +		pci_ep_doe_destroy_mb(doe_mb);
> +
> +	xa_destroy(&epc->doe_mbs);
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_destroy);
> diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
> index a966626c47f9..b353c3a1aa82 100644
> --- a/include/linux/pci-doe.h
> +++ b/include/linux/pci-doe.h
> @@ -13,6 +13,8 @@
>   #ifndef LINUX_PCI_DOE_H
>   #define LINUX_PCI_DOE_H
>   
> +#include <linux/types.h>
> +
>   /* Mailbox state flags */
>   #define PCI_DOE_FLAG_CANCEL		0
>   #define PCI_DOE_FLAG_DEAD		1
> @@ -24,12 +26,19 @@
>   #define PCI_DOE_FEATURE_CMA		1
>   #define PCI_DOE_FEATURE_SSESSION	2
>   
> +typedef int (*pci_doe_protocol_handler_t)(const void *request,
> +					  size_t request_sz,
> +					  void **response,
> +					  size_t *response_sz);
> +
>   struct pci_doe_feature {
>   	u16 vid;
>   	u8 type;
>   };
>   
>   struct pci_doe_mb;
> +struct pci_dev;
> +struct pci_epc;
>   
>   #ifdef CONFIG_PCI_DOE
>   struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
> @@ -54,4 +63,64 @@ static inline int pci_doe(struct pci_doe_mb *doe_mb, u16 vendor, u8 type,
>   }
>   #endif /* CONFIG_PCI_DOE */
>   
> +#ifdef CONFIG_PCI_DOE_EP
> +int pci_ep_doe_init(struct pci_epc *epc);
> +
> +int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset);
> +
> +int pci_ep_doe_register_protocol(u16 vendor, u8 type,
> +				 pci_doe_protocol_handler_t handler);
> +
> +void pci_ep_doe_unregister_protocol(u16 vendor, u8 type);
> +
> +int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
> +			       u16 vendor, u8 type, const void *request,
> +			       size_t request_sz, void **response, size_t *response_sz);
> +
> +int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset);
> +
> +void pci_ep_doe_destroy(struct pci_epc *epc);
> +
> +#else
> +static inline int pci_ep_doe_init(struct pci_epc *epc)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no,
> +					 u16 cap_offset)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline int pci_ep_doe_register_protocol(u16 vendor, u8 type,
> +					       pci_doe_protocol_handler_t handler)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline void pci_ep_doe_unregister_protocol(u16 vendor, u8 type)
> +{
> +}
> +
> +static inline int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
> +					     u16 vendor, u8 type,
> +					     const void *request, size_t request_sz,
> +					     void **response, size_t *response_sz)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no,
> +				   u16 cap_offset)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline void pci_ep_doe_destroy(struct pci_epc *epc)
> +{
> +}
> +
> +#endif /* CONFIG_PCI_DOE_EP */
> +
>   #endif /* LINUX_PCI_DOE_H */
> diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
> index 4286bfdbfdfa..ad4de4d33da3 100644
> --- a/include/linux/pci-epc.h
> +++ b/include/linux/pci-epc.h
> @@ -165,6 +165,7 @@ struct pci_epc_mem {
>    * @domain_nr: PCI domain number of the endpoint controller
>    * @init_complete: flag to indicate whether the EPC initialization is complete
>    *                 or not
> + * @doe_mbs: array of DOE mailboxes (CONFIG_PCI_DOE_EP)
>    */
>   struct pci_epc {
>   	struct device			dev;
> @@ -182,6 +183,9 @@ struct pci_epc {
>   	unsigned long			function_num_map;
>   	int				domain_nr;
>   	bool				init_complete;
> +#ifdef CONFIG_PCI_DOE_EP
> +	struct xarray			doe_mbs;
> +#endif
>   };
>   
>   /**


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 1/4] PCI: Add documentation for DOE endpoint support
  2026-02-13 20:33   ` Niklas Cassel
@ 2026-02-18 11:21     ` Aksh Garg
  0 siblings, 0 replies; 19+ messages in thread
From: Aksh Garg @ 2026-02-18 11:21 UTC (permalink / raw)
  To: Niklas Cassel
  Cc: linux-pci, linux-doc, bhelgaas, corbet, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk

Hi Niklas,

On 14/02/26 02:03, Niklas Cassel wrote:
> On Fri, Feb 13, 2026 at 06:06:00PM +0530, Aksh Garg wrote:
>> From: Aksh Garg <a-garg7@ti.com>
>>
>> Document the architecture and implementation details for the Data Object
>> Exchange (DOE) framework for PCIe Endpoint devices.
>>
>> Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
>> Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
>> Signed-off-by: Aksh Garg <a-garg7@ti.com>
>> ---
> 
> Usually documentation is added as the final patch in a series.
> 

I had placed this documentation patch before the code implementation in
this RFC to allow reviewers to have a look at the architectural design
first, enabling them to provide meaningful feedback on the overall
approach before examining the specific code details.

> I think this patch should just be squashed with patch 4/4, and the
> squashed patch then being the final patch in the series - patch 3/3.
> 

I will keep this mind while posting the normal patch series.

Thanks!

> 
> Kind regards,
> Niklas


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 2/4] PCI/DOE: Move common definitions to the header file
  2026-02-13 12:36 ` [RFC PATCH 2/4] PCI/DOE: Move common definitions to the header file Aksh Garg
@ 2026-02-22 13:01   ` Lukas Wunner
  2026-02-23  7:30     ` Aksh Garg
  0 siblings, 1 reply; 19+ messages in thread
From: Lukas Wunner @ 2026-02-22 13:01 UTC (permalink / raw)
  To: Aksh Garg
  Cc: linux-pci, linux-doc, bhelgaas, corbet, cassel, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk

On Fri, Feb 13, 2026 at 06:06:01PM +0530, Aksh Garg wrote:
> Move common macros and structures from drivers/pci/doe.c to
> include/linux/pci-doe.h to allow reuse across root complex and
> endpoint DOE implementations.

Please use drivers/pci/pci.h for declarations that need not be visible
outside the PCI core.

There are users of pci_doe() & pci_find_doe_mailbox() outside the
PCI core, that's the (only) reason the declarations are in
include/linux/pci-doe.h.

> Also add CONFIG_PCI_DOE guards around the root complex DOE APIs to
> maintain proper conditional compilation.

It's not clear to me why these empty inlines are needed, please drop
them or provide an explanation in the commit message.

Thanks,

Lukas

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions
  2026-02-13 12:36 ` [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions Aksh Garg
  2026-02-13 13:21   ` Niklas Cassel
  2026-02-18  4:28   ` Alistair Francis
@ 2026-02-22 13:06   ` Lukas Wunner
  2026-02-23 10:44     ` Aksh Garg
  2026-03-04 14:17   ` Manivannan Sadhasivam
  3 siblings, 1 reply; 19+ messages in thread
From: Lukas Wunner @ 2026-02-22 13:06 UTC (permalink / raw)
  To: Aksh Garg
  Cc: linux-pci, linux-doc, bhelgaas, corbet, cassel, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk

On Fri, Feb 13, 2026 at 06:06:02PM +0530, Aksh Garg wrote:
> Add the DOE support for PCIe endpoint devices, enabling endpoint
> functions to process the DOE requests from the host. The implementation
> provides framework APIs for controller drivers to register mailboxes,
> protocol handler registration for different DOE data object types, and
> request processing with workqueues ensuring sequential handling per
> mailbox. The Discovery protocol is handled internally by the DOE core.

This looks like it is largely a duplication of drivers/pci/doe.c,
including the asynchronous request support that was originally added
but was never needed and so was clearly a mistake.

I'm wondering why the async suport is needed in the endpoint case?
Why can't this (only) be synchronous?

Thanks,

Lukas

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 2/4] PCI/DOE: Move common definitions to the header file
  2026-02-22 13:01   ` Lukas Wunner
@ 2026-02-23  7:30     ` Aksh Garg
  0 siblings, 0 replies; 19+ messages in thread
From: Aksh Garg @ 2026-02-23  7:30 UTC (permalink / raw)
  To: Lukas Wunner
  Cc: linux-pci, linux-doc, bhelgaas, corbet, cassel, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk

Hi Lukas,

On 22/02/26 18:31, Lukas Wunner wrote:
> On Fri, Feb 13, 2026 at 06:06:01PM +0530, Aksh Garg wrote:
>> Move common macros and structures from drivers/pci/doe.c to
>> include/linux/pci-doe.h to allow reuse across root complex and
>> endpoint DOE implementations.
> 
> Please use drivers/pci/pci.h for declarations that need not be visible
> outside the PCI core.
> 
> There are users of pci_doe() & pci_find_doe_mailbox() outside the
> PCI core, that's the (only) reason the declarations are in
> include/linux/pci-doe.h.
> 

Thank you for the feedback. I will follow this convention in the future
patch series.

>> Also add CONFIG_PCI_DOE guards around the root complex DOE APIs to
>> maintain proper conditional compilation.
> 
> It's not clear to me why these empty inlines are needed, please drop
> them or provide an explanation in the commit message.

I added the guards for the users who might not 'select PCI_DOE' in their
config. I missed the fact that all the current users of these functions
have 'select PCI_DOE' in their config, hence these inlines would not be
required and can be dropped.

Regards,
Aksh Garg

> 
> Thanks,
> 
> Lukas


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions
  2026-02-22 13:06   ` Lukas Wunner
@ 2026-02-23 10:44     ` Aksh Garg
  0 siblings, 0 replies; 19+ messages in thread
From: Aksh Garg @ 2026-02-23 10:44 UTC (permalink / raw)
  To: Lukas Wunner
  Cc: linux-pci, linux-doc, bhelgaas, corbet, cassel, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk



On 22/02/26 18:36, Lukas Wunner wrote:
> On Fri, Feb 13, 2026 at 06:06:02PM +0530, Aksh Garg wrote:
>> Add the DOE support for PCIe endpoint devices, enabling endpoint
>> functions to process the DOE requests from the host. The implementation
>> provides framework APIs for controller drivers to register mailboxes,
>> protocol handler registration for different DOE data object types, and
>> request processing with workqueues ensuring sequential handling per
>> mailbox. The Discovery protocol is handled internally by the DOE core.
> 
> This looks like it is largely a duplication of drivers/pci/doe.c,
> including the asynchronous request support that was originally added
> but was never needed and so was clearly a mistake.
> 
> I'm wondering why the async suport is needed in the endpoint case?
> Why can't this (only) be synchronous?

The DOE framework for the endpoint case needs to be asynchronous because
a PF can have multiple instances of DOE mailboxes, and the requests may
be interleaved across the mailboxes (the request processing of one
mailbox should not result in blocking the request processing of other
mailbox, as per the DOE ECN). Hence, the request on each of them needs
to be handled in parallel for optimization. For the EP controller driver
to handle requests on multiple mailboxes per-PF in parallel,
pci_ep_doe_process_request() needs to be asynchronous.
However, I noticed the use of wait_for_completion() within this function
makes the overall function synchronous for the caller, which I
overlooked while posting this patch.


Please provide feedback if the following framework would work instead:

The function pci_ep_doe_process_request() would be called by the EP
controller driver when a request arrives on a mailbox. Instead of
wait_for_completion() in this function, the function would return
immediately after submitting the work in the queue, hence eliminating
the need of private data for completion in the task structure.

A completion callback from the EP driver would be passed to this
function along with the current parameters, which would be passed to the
task structure as task->complete. Whenever signal_task_complete() is
invoked, the completion callback of the EP driver would be called, which
would take func_no, cap_offset (to help the EP driver to identify the
mailbox), and the status of the work as input, hence eliminating the
need of task_status in the task structure as well.

This would make pci_ep_doe_process_request() asynchronous as per the
requirement.


While going through the driver implementation of doe.c, I noticed that
the pci_doe() was made synchronous in commit 62e8b17ffc2f ("PCI/DOE:
Provide synchronous API and use it internally"), which builds on the
internal asynchronous machinery using WORK_QUEUES. As the pci_doe() is
synchronous for the callers, why the WORK_QUEUE framework was not 
removed from the doe.c implementation in that commit?

> 
> Thanks,
> 
> Lukas


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions
  2026-02-13 12:36 ` [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions Aksh Garg
                     ` (2 preceding siblings ...)
  2026-02-22 13:06   ` Lukas Wunner
@ 2026-03-04 14:17   ` Manivannan Sadhasivam
  2026-03-06  8:17     ` Aksh Garg
  3 siblings, 1 reply; 19+ messages in thread
From: Manivannan Sadhasivam @ 2026-03-04 14:17 UTC (permalink / raw)
  To: Aksh Garg
  Cc: linux-pci, linux-doc, bhelgaas, corbet, cassel, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk

On Fri, Feb 13, 2026 at 06:06:02PM +0530, Aksh Garg wrote:
> From: Aksh Garg <a-garg7@ti.com>
> 
> DOE (Data Object Exchange) is a standard PCIe extended capability
> feature introduced in the Data Object Exchange (DOE) ECN for
> PCIe r5.0. It provides a communication mechanism primarily used for
> implementing PCIe security features such as device authentication, and
> secure link establishment. Think of DOE as a sophisticated mailbox
> system built into PCIe. The root complex can send structured requests
> to the endpoint device through DOE mailboxes, and the endpoint device
> responds with appropriate data.
> 
> Add the DOE support for PCIe endpoint devices, enabling endpoint
> functions to process the DOE requests from the host. The implementation
> provides framework APIs for controller drivers to register mailboxes,
> protocol handler registration for different DOE data object types, and
> request processing with workqueues ensuring sequential handling per
> mailbox. The Discovery protocol is handled internally by the DOE core.
> 
> This implementation complements the existing DOE implementation for
> root complex in drivers/pci/doe.c.
> 
> Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
> Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
> Signed-off-by: Aksh Garg <a-garg7@ti.com>
> ---
>  drivers/pci/Kconfig     |  14 +
>  drivers/pci/Makefile    |   1 +
>  drivers/pci/doe-ep.c    | 671 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/pci-doe.h |  69 +++++
>  include/linux/pci-epc.h |   4 +
>  5 files changed, 759 insertions(+)
>  create mode 100644 drivers/pci/doe-ep.c
> 

As Niklas mentioned, this driver has to live under drivers/pci/endpoint/. It can
still share code with existing doe.c driver.

> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> index e3f848ffb52a..2a57a314054c 100644
> --- a/drivers/pci/Kconfig
> +++ b/drivers/pci/Kconfig
> @@ -146,6 +146,20 @@ config PCI_DOE
>  	  Say Y here if you want be able to communicate with PCIe DOE
>  	  mailboxes.
>  
> +config PCI_DOE_EP
> +	bool "PCI Endpoint Data Object Exchange (DOE) support"
> +	depends on PCI_ENDPOINT
> +	help
> +	  This enables support for Data Object Exchange (DOE) protocol
> +	  on PCI Endpoint controllers. It provides a communication
> +	  mechanism through mailboxes, primarily used for PCIe security
> +	  features.
> +
> +	  Say Y here if you want be able to communicate using PCIe DOE
> +	  mailboxes.
> +
> +	  If unsure, say N.
> +
>  config PCI_ECAM
>  	bool
>  
> diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
> index e10cfe5a280b..66084e2ef9b2 100644
> --- a/drivers/pci/Makefile
> +++ b/drivers/pci/Makefile
> @@ -34,6 +34,7 @@ obj-$(CONFIG_PCI_P2PDMA)	+= p2pdma.o
>  obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
>  obj-$(CONFIG_VGA_ARB)		+= vgaarb.o
>  obj-$(CONFIG_PCI_DOE)		+= doe.o
> +obj-$(CONFIG_PCI_DOE_EP)	+= doe-ep.o
>  obj-$(CONFIG_PCI_IDE)		+= ide.o
>  obj-$(CONFIG_PCI_TSM)		+= tsm.o
>  obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
> diff --git a/drivers/pci/doe-ep.c b/drivers/pci/doe-ep.c
> new file mode 100644
> index 000000000000..ff0551644f54
> --- /dev/null
> +++ b/drivers/pci/doe-ep.c
> @@ -0,0 +1,671 @@
> +// SPDX-License-Identifier: GPL-2.0-only or MIT
> +/*
> + * Data Object Exchange for PCIe Endpoint
> + *	PCIe r7.0, sec 6.30 DOE
> + *
> + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com
> + *	Aksh Garg <a-garg7@ti.com>
> + *	Siddharth Vadapalli <s-vadapalli@ti.com>
> + */
> +
> +#define dev_fmt(fmt) "DOE EP: " fmt
> +
> +#include <linux/bitfield.h>
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/mutex.h>
> +#include <linux/pci.h>
> +#include <linux/pci-epc.h>
> +#include <linux/pci-doe.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#include <linux/xarray.h>
> +
> +/*
> + * Combines function number and capability offset into a unique lookup key
> + * for storing/retrieving DOE mailboxes in an xarray.
> + */
> +#define PCI_DOE_MB_KEY(func, offset) \
> +	(((unsigned long)(func) << 16) | (offset))
> +
> +/**
> + * struct pci_doe_protocol - DOE protocol handler entry
> + * @vid: Vendor ID
> + * @type: Protocol type
> + * @handler: Handler function pointer
> + */
> +struct pci_doe_protocol {
> +	u16 vid;
> +	u8 type;
> +	pci_doe_protocol_handler_t handler;
> +};
> +
> +/*
> + * Global registry of protocol handlers,
> + * holding struct pci_doe_protocol
> + */
> +static DEFINE_XARRAY_ALLOC(pci_doe_protocols);
> +
> +/* Serializes concurrent accesses to pci_doe_protocols xarray */
> +static DEFINE_MUTEX(protocols_mutex);
> +
> +/**
> + * struct pci_ep_doe_mb - State for a single DOE mailbox on EP
> + *
> + * This state is used to manage a single DOE mailbox capability on the
> + * endpoint side.
> + *
> + * @epc: PCI endpoint controller this mailbox belongs to
> + * @func_no: Physical function number of the function this mailbox belongs to
> + * @cap_offset: Capability offset
> + * @work_queue: Queue of work items
> + * @flags: Bit array of PCI_DOE_FLAG_* flags
> + */
> +struct pci_ep_doe_mb {
> +	struct pci_epc *epc;
> +	u8 func_no;
> +	u16 cap_offset;
> +	struct workqueue_struct *work_queue;
> +	unsigned long flags;
> +};
> +
> +/**
> + * struct pci_ep_doe_task - Represents a single DOE request/response task
> + *
> + * @feat: DOE feature (vendor ID and type)
> + * @request_pl: Request payload
> + * @request_pl_sz: Size of request payload in bytes
> + * @response_pl: Output pointer for response buffer
> + * @response_pl_sz: Output pointer for response size
> + * @task_status: Task completion status (0 on success, -errno on failure)
> + * @complete: Completion callback
> + * @private: Private data for completion
> + * @work: Work structure for workqueue
> + * @doe_mb: DOE mailbox handling this task
> + */
> +struct pci_ep_doe_task {
> +	struct pci_doe_feature feat;
> +	const void *request_pl;
> +	size_t request_pl_sz;
> +	void **response_pl;
> +	size_t *response_pl_sz;
> +	int task_status;
> +	void (*complete)(struct pci_ep_doe_task *task);
> +	void *private;
> +
> +	/* Initialized by pci_ep_doe_submit_task() */
> +	struct work_struct work;
> +	struct pci_ep_doe_mb *doe_mb;
> +};
> +
> +/**
> + * pci_ep_doe_init() - Initialize the DOE framework for a controller in EP mode
> + * @epc: PCI endpoint controller
> + *
> + * Initialize the DOE framework data structures. This only initializes
> + * the xarray that will hold the mailboxes.
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +int pci_ep_doe_init(struct pci_epc *epc)
> +{
> +	if (!epc)
> +		return -EINVAL;
> +
> +	xa_init(&epc->doe_mbs);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_init);
> +
> +/**
> + * pci_ep_doe_add_mailbox() - Add a DOE mailbox for a physical function
> + * @epc: PCI endpoint controller
> + * @func_no: Physical function number
> + * @cap_offset: Offset of the DOE capability
> + *
> + * Create and register a DOE mailbox for the specified physical function
> + * and capability offset. The controller driver should call this for each
> + * DOE capability it finds in its config space.

Why can't we call this helper from the EPC core driver instead? We may have to
introduce a new callback in 'struct pci_epc_ops' to read the endpoint config
space from the EPC core driver. But it will avoid duplicating the code across
the EPC drivers.

End of the day, we can read the config space of the device and identify the
capability in a generic way.

> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset)
> +{
> +	struct pci_ep_doe_mb *doe_mb;
> +	unsigned long key;
> +	int ret;
> +
> +	if (!epc)
> +		return -EINVAL;
> +
> +	doe_mb = kzalloc(sizeof(*doe_mb), GFP_KERNEL);
> +	if (!doe_mb)
> +		return -ENOMEM;
> +
> +	doe_mb->epc = epc;
> +	doe_mb->func_no = func_no;
> +	doe_mb->cap_offset = cap_offset;
> +
> +	doe_mb->work_queue = alloc_ordered_workqueue("pci_ep_doe[%s:pf%d:offset%x]", 0,
> +						     dev_name(&epc->dev),
> +						     func_no, cap_offset);

I believe you are trying to use cap_offset to differentiate between multiple DOE
mailbox instances within a single function. In that case, I'd use a simple index
instead of cap_offset.

Rest looks OK from a high level design perspective.

- Mani

> +	if (!doe_mb->work_queue) {
> +		dev_err(epc->dev.parent,
> +			"[pf%d:offset%x] failed to allocate work queue\n",
> +			func_no, cap_offset);
> +		ret = -ENOMEM;
> +		goto err_free;
> +	}
> +
> +	/* Add to xarray with composite key */
> +	key = PCI_DOE_MB_KEY(func_no, cap_offset);
> +	ret = xa_insert(&epc->doe_mbs, key, doe_mb, GFP_KERNEL);
> +	if (ret) {
> +		dev_err(epc->dev.parent,
> +			"[pf%d:offset%x] failed to insert mailbox: %d\n",
> +			func_no, cap_offset, ret);
> +		goto err_destroy;
> +	}
> +
> +	dev_dbg(epc->dev.parent,
> +		"DOE mailbox added: pf%d offset 0x%x\n",
> +		func_no, cap_offset);
> +
> +	return 0;
> +
> +err_destroy:
> +	destroy_workqueue(doe_mb->work_queue);
> +err_free:
> +	kfree(doe_mb);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_add_mailbox);
> +
> +/**
> + * pci_ep_doe_register_protocol() - Register a protocol handler
> + * @vendor: Vendor ID
> + * @type: Protocol type
> + * @handler: Handler function pointer
> + *
> + * Register a protocol handler that will be called when DOE requests
> + * with the specified vendor ID and type are received. Protocol libraries
> + * should call this during module initialization.
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +int pci_ep_doe_register_protocol(u16 vendor, u8 type,
> +				 pci_doe_protocol_handler_t handler)
> +{
> +	struct pci_doe_protocol *protocol;
> +	unsigned long index;
> +	int ret;
> +	u32 id;
> +
> +	if (!handler)
> +		return -EINVAL;
> +
> +	/* Don't allow registering discovery protocol */
> +	if (vendor == PCI_VENDOR_ID_PCI_SIG && type == PCI_DOE_FEATURE_DISCOVERY)
> +		return -EINVAL;
> +
> +	mutex_lock(&protocols_mutex);
> +
> +	/* Check if already registered */
> +	xa_for_each(&pci_doe_protocols, index, protocol) {
> +		if (protocol->vid == vendor && protocol->type == type) {
> +			ret = -EEXIST;
> +			goto err;
> +		}
> +	}
> +
> +	protocol = kzalloc(sizeof(*protocol), GFP_KERNEL);
> +	if (!protocol) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	protocol->vid = vendor;
> +	protocol->type = type;
> +	protocol->handler = handler;
> +
> +	ret = xa_alloc(&pci_doe_protocols, &id, protocol,
> +		       xa_limit_32b, GFP_KERNEL);
> +	if (ret) {
> +		kfree(protocol);
> +		goto err;
> +	}
> +
> +	mutex_unlock(&protocols_mutex);
> +
> +	pr_debug("DOE EP: Registered protocol %04x:%02x at index %u\n",
> +		 vendor, type, id);
> +
> +	return 0;
> +
> +err:
> +	mutex_unlock(&protocols_mutex);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_register_protocol);
> +
> +/**
> + * pci_ep_doe_unregister_protocol() - Unregister a protocol handler
> + * @vendor: Vendor ID
> + * @type: Protocol type
> + *
> + * Unregister a previously registered protocol handler. Protocol libraries
> + * should call this during module cleanup.
> + */
> +void pci_ep_doe_unregister_protocol(u16 vendor, u8 type)
> +{
> +	struct pci_doe_protocol *protocol;
> +	unsigned long index;
> +
> +	mutex_lock(&protocols_mutex);
> +
> +	/* Find and remove the protocol entry */
> +	xa_for_each(&pci_doe_protocols, index, protocol) {
> +		if (protocol->vid == vendor && protocol->type == type) {
> +			xa_erase(&pci_doe_protocols, index);
> +			kfree(protocol);
> +			mutex_unlock(&protocols_mutex);
> +			pr_debug("DOE EP: Unregistered protocol %04x:%02x\n",
> +				 vendor, type);
> +			return;
> +		}
> +	}
> +
> +	mutex_unlock(&protocols_mutex);
> +	pr_warn("DOE EP: Attempted to unregister non-existent protocol %04x:%02x\n",
> +		vendor, type);
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_unregister_protocol);
> +
> +/**
> + * pci_ep_doe_cancel_tasks() - Cancel all pending tasks
> + * @doe_mb: DOE mailbox
> + *
> + * Cancel all pending tasks in the mailbox. Mark the mailbox as dead
> + * so no new tasks can be submitted.
> + */
> +static void pci_ep_doe_cancel_tasks(struct pci_ep_doe_mb *doe_mb)
> +{
> +	if (!doe_mb)
> +		return;
> +
> +	/* Mark the mailbox as dead */
> +	set_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags);
> +
> +	/* Stop all pending work items from starting */
> +	set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
> +}
> +
> +/**
> + * pci_ep_doe_get_mailbox() - Get DOE mailbox by function and offset
> + * @epc: PCI endpoint controller
> + * @func_no: Physical function number
> + * @cap_offset: Offset of the DOE capability
> + *
> + * Internal helper to look up a DOE mailbox by its function number and
> + * capability offset.
> + *
> + * RETURNS: Pointer to the mailbox or NULL if not found
> + */
> +static struct pci_ep_doe_mb *pci_ep_doe_get_mailbox(struct pci_epc *epc,
> +						    u8 func_no, u16 cap_offset)
> +{
> +	unsigned long key;
> +
> +	if (!epc)
> +		return NULL;
> +
> +	key = PCI_DOE_MB_KEY(func_no, cap_offset);
> +	return xa_load(&epc->doe_mbs, key);
> +}
> +
> +/**
> + * pci_ep_doe_find_protocol() - Find protocol handler
> + * @vendor: Vendor ID
> + * @type: Protocol type
> + *
> + * Look up a protocol handler in the registered protocols.
> + *
> + * RETURNS: Handler function pointer or NULL if not found
> + */
> +static pci_doe_protocol_handler_t pci_ep_doe_find_protocol(u16 vendor, u8 type)
> +{
> +	pci_doe_protocol_handler_t handler = NULL;
> +	struct pci_doe_protocol *protocol;
> +	unsigned long index;
> +
> +	mutex_lock(&protocols_mutex);
> +
> +	xa_for_each(&pci_doe_protocols, index, protocol) {
> +		if (protocol->vid == vendor && protocol->type == type) {
> +			handler = protocol->handler;
> +			break;
> +		}
> +	}
> +
> +	mutex_unlock(&protocols_mutex);
> +	return handler;
> +}
> +
> +/**
> + * pci_ep_doe_handle_discovery() - Handle Discovery protocol request
> + * @request: Request payload
> + * @request_sz: Request size
> + * @response: Output pointer for response buffer
> + * @response_sz: Output pointer for response size
> + *
> + * Handle the DOE Discovery protocol. The request contains an index specifying
> + * which protocol to query. This function creates a response containing the
> + * vendor ID and protocol type for the requested index, along with the next
> + * index value for further discovery:
> + *
> + * - next_index = 0: Signals this is the last protocol supported
> + * - next_index = n (non-zero): Signals more protocols available,
> + *   query index n next
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +static int pci_ep_doe_handle_discovery(const void *request, size_t request_sz,
> +				       void **response, size_t *response_sz)
> +{
> +	struct pci_doe_protocol *protocol;
> +	u8 requested_index, next_index;
> +	unsigned long index;
> +	u8 xarray_count = 0;
> +	u32 *response_pl;
> +	u32 request_pl;
> +	u16 vendor;
> +	u8 type;
> +
> +	if (request_sz != sizeof(u32))
> +		return -EINVAL;
> +
> +	request_pl = *(u32 *)request;
> +	requested_index = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX, request_pl);
> +
> +	mutex_lock(&protocols_mutex);
> +
> +	/* Count protocols in xarray */
> +	xa_for_each(&pci_doe_protocols, index, protocol)
> +		xarray_count++;
> +
> +	if (requested_index > xarray_count) {
> +		mutex_unlock(&protocols_mutex);
> +		return -EINVAL;
> +	}
> +
> +	/* Index 0 is always Discovery protocol */
> +	if (requested_index == 0) {
> +		vendor = PCI_VENDOR_ID_PCI_SIG;
> +		type = PCI_DOE_FEATURE_DISCOVERY;
> +	} else {
> +		protocol = xa_load(&pci_doe_protocols, (unsigned long)(requested_index - 1));
> +		if (!protocol) {
> +			mutex_unlock(&protocols_mutex);
> +			return -EINVAL;
> +		}
> +		vendor = protocol->vid;
> +		type = protocol->type;
> +	}
> +	mutex_unlock(&protocols_mutex);
> +
> +	/* Calculate next index */
> +	next_index = (requested_index < xarray_count) ? requested_index + 1 : 0;
> +
> +	response_pl = kzalloc(sizeof(*response_pl), GFP_KERNEL);
> +	if (!response_pl)
> +		return -ENOMEM;
> +
> +	/* Build response */
> +	*response_pl = FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID, vendor) |
> +		       FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE, type) |
> +		       FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX, next_index);
> +
> +	*response = response_pl;
> +	*response_sz = sizeof(*response_pl);
> +
> +	return 0;
> +}
> +
> +static void signal_task_complete(struct pci_ep_doe_task *task, int status)
> +{
> +	task->task_status = status;
> +	destroy_work_on_stack(&task->work);
> +	task->complete(task);
> +}
> +
> +/**
> + * doe_ep_task_work() - Work function for processing DOE EP tasks
> + * @work: Work structure
> + *
> + * Process a DOE request by calling the appropriate protocol handler.
> + */
> +static void doe_ep_task_work(struct work_struct *work)
> +{
> +	struct pci_ep_doe_task *task = container_of(work, struct pci_ep_doe_task,
> +						    work);
> +	struct pci_ep_doe_mb *doe_mb = task->doe_mb;
> +	pci_doe_protocol_handler_t handler;
> +	int rc;
> +
> +	if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) {
> +		signal_task_complete(task, -EIO);
> +		return;
> +	}
> +
> +	/* Check if request was aborted */
> +	if (test_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags)) {
> +		signal_task_complete(task, -ECANCELED);
> +		return;
> +	}
> +
> +	/* Handle Discovery protocol directly */
> +	if (task->feat.vid == PCI_VENDOR_ID_PCI_SIG &&
> +	    task->feat.type == PCI_DOE_FEATURE_DISCOVERY) {
> +		rc = pci_ep_doe_handle_discovery(task->request_pl,
> +						 task->request_pl_sz,
> +						 task->response_pl,
> +						 task->response_pl_sz);
> +		signal_task_complete(task, rc);
> +		return;
> +	}
> +
> +	/* Find protocol handler in xarray */
> +	handler = pci_ep_doe_find_protocol(task->feat.vid, task->feat.type);
> +	if (!handler) {
> +		dev_warn(doe_mb->epc->dev.parent,
> +			 "[%d:%x] Unsupported protocol VID=%04x TYPE=%02x\n",
> +			 doe_mb->func_no, doe_mb->cap_offset,
> +			 task->feat.vid, task->feat.type);
> +		signal_task_complete(task, -EOPNOTSUPP);
> +		return;
> +	}
> +
> +	/* Call protocol handler */
> +	rc = handler(task->request_pl, task->request_pl_sz,
> +		     task->response_pl, task->response_pl_sz);
> +
> +	signal_task_complete(task, rc);
> +}
> +
> +/**
> + * pci_ep_doe_submit_task() - Submit a task to be processed
> + * @doe_mb: DOE mailbox
> + * @task: Task to submit
> + *
> + * Submit a DOE task to the workqueue for processing.
> + * Task must be allocated on the stack.
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +static int pci_ep_doe_submit_task(struct pci_ep_doe_mb *doe_mb,
> +				  struct pci_ep_doe_task *task)
> +{
> +	if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags))
> +		return -EIO;
> +
> +	task->doe_mb = doe_mb;
> +	INIT_WORK_ONSTACK(&task->work, doe_ep_task_work);
> +	queue_work(doe_mb->work_queue, &task->work);
> +	return 0;
> +}
> +
> +static void pci_ep_doe_task_complete(struct pci_ep_doe_task *task)
> +{
> +	complete(task->private);
> +}
> +
> +/**
> + * pci_ep_doe_process_request() - Process DOE request on endpoint
> + * @epc: PCI endpoint controller
> + * @func_no: Physical function number
> + * @cap_offset: DOE capability offset
> + * @vendor: Vendor ID from request header
> + * @type: Protocol type from request header
> + * @request: Request payload in CPU-native format
> + * @request_sz: Size of request payload (bytes)
> + * @response: Output pointer for response buffer in CPU-native format
> + * @response_sz: Output pointer for response size (bytes)
> + *
> + * Process a DOE request received on the endpoint. The request payload should
> + * not include the DOE header (vendor/type/length). The protocol handler will
> + * allocate the response buffer, which the caller (controller driver) must
> + * free after use.
> + *
> + * As per DOE specification, a mailbox processes one request at a time.
> + * Therefore, this function will never be called concurrently for the same
> + * mailbox by different callers.
> + *
> + * The caller is responsible for the conversion of the received DOE request
> + * with le32_to_cpu() before calling this function.
> + * Similarly, it is responsible for converting the response payload with
> + * cpu_to_le32() before sending it back over the DOE mailbox.
> + *
> + * The caller is also responsible for ensuring that the request size
> + * is within the limits defined by PCI_DOE_MAX_LENGTH.
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
> +			       u16 vendor, u8 type, const void *request,
> +			       size_t request_sz, void **response, size_t *response_sz)
> +{
> +	DECLARE_COMPLETION_ONSTACK(c);
> +	struct pci_ep_doe_mb *doe_mb;
> +	struct pci_ep_doe_task task = {
> +		.feat.vid = vendor,
> +		.feat.type = type,
> +		.request_pl = request,
> +		.request_pl_sz = request_sz,
> +		.response_pl = response,
> +		.response_pl_sz = response_sz,
> +		.complete = pci_ep_doe_task_complete,
> +		.private = &c,
> +	};
> +	int rc;
> +
> +	doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
> +	if (!doe_mb)
> +		return -ENODEV;
> +
> +	rc = pci_ep_doe_submit_task(doe_mb, &task);
> +	if (rc)
> +		return rc;
> +
> +	wait_for_completion(&c);
> +
> +	return task.task_status;
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_process_request);
> +
> +/**
> + * pci_ep_doe_abort() - Abort DOE operations on a mailbox
> + * @epc: PCI endpoint controller
> + * @func_no: Physical function number
> + * @cap_offset: DOE capability offset
> + *
> + * Abort all queued and wait for in-flight DOE operations to complete for the
> + * specified mailbox. This function is called by the EP controller driver
> + * when the RC sets the ABORT bit in the DOE Control register.
> + *
> + * The function will:
> + *
> + * - Set CANCEL flag to prevent new requests in the queue from starting
> + * - Wait for the currently executing handler to complete (cannot interrupt)
> + * - Flush the workqueue to wait for all requests to be handled appropriately
> + * - Clear CANCEL flag to prepare for new requests
> + *
> + * RETURNS: 0 on success, -errno on failure
> + */
> +int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset)
> +{
> +	struct pci_ep_doe_mb *doe_mb;
> +
> +	if (!epc)
> +		return -EINVAL;
> +
> +	doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
> +	if (!doe_mb)
> +		return -ENODEV;
> +
> +	/* Set CANCEL flag - worker will abort queued requests */
> +	set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
> +	flush_workqueue(doe_mb->work_queue);
> +
> +	/* Clear CANCEL flag - mailbox ready for new requests */
> +	clear_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
> +
> +	dev_dbg(epc->dev.parent,
> +		"DOE mailbox aborted: PF%d offset 0x%x\n",
> +		func_no, cap_offset);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_abort);
> +
> +/**
> + * pci_ep_doe_destroy_mb() - Destroy a single DOE mailbox
> + * @doe_mb: DOE mailbox to destroy
> + *
> + * Internal function to destroy a mailbox and free its resources.
> + */
> +static void pci_ep_doe_destroy_mb(struct pci_ep_doe_mb *doe_mb)
> +{
> +	if (!doe_mb)
> +		return;
> +
> +	pci_ep_doe_cancel_tasks(doe_mb);
> +
> +	if (doe_mb->work_queue)
> +		destroy_workqueue(doe_mb->work_queue);
> +
> +	kfree(doe_mb);
> +}
> +
> +/**
> + * pci_ep_doe_destroy() - Destroy all DOE mailboxes
> + * @epc: PCI endpoint controller
> + *
> + * Destroy all DOE mailboxes and free associated resources.
> + *
> + * This function should be called during controller cleanup to free all
> + * DOE resources.
> + */
> +void pci_ep_doe_destroy(struct pci_epc *epc)
> +{
> +	struct pci_ep_doe_mb *doe_mb;
> +	unsigned long index;
> +
> +	if (!epc)
> +		return;
> +
> +	xa_for_each(&epc->doe_mbs, index, doe_mb)
> +		pci_ep_doe_destroy_mb(doe_mb);
> +
> +	xa_destroy(&epc->doe_mbs);
> +}
> +EXPORT_SYMBOL_GPL(pci_ep_doe_destroy);
> diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
> index a966626c47f9..b353c3a1aa82 100644
> --- a/include/linux/pci-doe.h
> +++ b/include/linux/pci-doe.h
> @@ -13,6 +13,8 @@
>  #ifndef LINUX_PCI_DOE_H
>  #define LINUX_PCI_DOE_H
>  
> +#include <linux/types.h>
> +
>  /* Mailbox state flags */
>  #define PCI_DOE_FLAG_CANCEL		0
>  #define PCI_DOE_FLAG_DEAD		1
> @@ -24,12 +26,19 @@
>  #define PCI_DOE_FEATURE_CMA		1
>  #define PCI_DOE_FEATURE_SSESSION	2
>  
> +typedef int (*pci_doe_protocol_handler_t)(const void *request,
> +					  size_t request_sz,
> +					  void **response,
> +					  size_t *response_sz);
> +
>  struct pci_doe_feature {
>  	u16 vid;
>  	u8 type;
>  };
>  
>  struct pci_doe_mb;
> +struct pci_dev;
> +struct pci_epc;
>  
>  #ifdef CONFIG_PCI_DOE
>  struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
> @@ -54,4 +63,64 @@ static inline int pci_doe(struct pci_doe_mb *doe_mb, u16 vendor, u8 type,
>  }
>  #endif /* CONFIG_PCI_DOE */
>  
> +#ifdef CONFIG_PCI_DOE_EP
> +int pci_ep_doe_init(struct pci_epc *epc);
> +
> +int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset);
> +
> +int pci_ep_doe_register_protocol(u16 vendor, u8 type,
> +				 pci_doe_protocol_handler_t handler);
> +
> +void pci_ep_doe_unregister_protocol(u16 vendor, u8 type);
> +
> +int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
> +			       u16 vendor, u8 type, const void *request,
> +			       size_t request_sz, void **response, size_t *response_sz);
> +
> +int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset);
> +
> +void pci_ep_doe_destroy(struct pci_epc *epc);
> +
> +#else
> +static inline int pci_ep_doe_init(struct pci_epc *epc)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no,
> +					 u16 cap_offset)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline int pci_ep_doe_register_protocol(u16 vendor, u8 type,
> +					       pci_doe_protocol_handler_t handler)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline void pci_ep_doe_unregister_protocol(u16 vendor, u8 type)
> +{
> +}
> +
> +static inline int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
> +					     u16 vendor, u8 type,
> +					     const void *request, size_t request_sz,
> +					     void **response, size_t *response_sz)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no,
> +				   u16 cap_offset)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline void pci_ep_doe_destroy(struct pci_epc *epc)
> +{
> +}
> +
> +#endif /* CONFIG_PCI_DOE_EP */
> +
>  #endif /* LINUX_PCI_DOE_H */
> diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
> index 4286bfdbfdfa..ad4de4d33da3 100644
> --- a/include/linux/pci-epc.h
> +++ b/include/linux/pci-epc.h
> @@ -165,6 +165,7 @@ struct pci_epc_mem {
>   * @domain_nr: PCI domain number of the endpoint controller
>   * @init_complete: flag to indicate whether the EPC initialization is complete
>   *                 or not
> + * @doe_mbs: array of DOE mailboxes (CONFIG_PCI_DOE_EP)
>   */
>  struct pci_epc {
>  	struct device			dev;
> @@ -182,6 +183,9 @@ struct pci_epc {
>  	unsigned long			function_num_map;
>  	int				domain_nr;
>  	bool				init_complete;
> +#ifdef CONFIG_PCI_DOE_EP
> +	struct xarray			doe_mbs;
> +#endif
>  };
>  
>  /**
> -- 
> 2.34.1
> 
> 

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions
  2026-02-18  4:28   ` Alistair Francis
@ 2026-03-04 14:18     ` Manivannan Sadhasivam
  2026-03-06 12:14       ` Aksh Garg
  0 siblings, 1 reply; 19+ messages in thread
From: Manivannan Sadhasivam @ 2026-03-04 14:18 UTC (permalink / raw)
  To: Alistair Francis
  Cc: Aksh Garg, linux-pci, linux-doc, bhelgaas, corbet, cassel,
	linux-kernel, linux-arm-kernel, s-vadapalli, danishanwar, srk

On Wed, Feb 18, 2026 at 02:28:23PM +1000, Alistair Francis wrote:
> On 13/2/26 22:36, Aksh Garg wrote:
> > From: Aksh Garg <a-garg7@ti.com>
> > 
> > DOE (Data Object Exchange) is a standard PCIe extended capability
> > feature introduced in the Data Object Exchange (DOE) ECN for
> > PCIe r5.0. It provides a communication mechanism primarily used for
> > implementing PCIe security features such as device authentication, and
> > secure link establishment. Think of DOE as a sophisticated mailbox
> > system built into PCIe. The root complex can send structured requests
> > to the endpoint device through DOE mailboxes, and the endpoint device
> > responds with appropriate data.
> > 
> > Add the DOE support for PCIe endpoint devices, enabling endpoint
> > functions to process the DOE requests from the host. The implementation
> > provides framework APIs for controller drivers to register mailboxes,
> > protocol handler registration for different DOE data object types, and
> > request processing with workqueues ensuring sequential handling per
> > mailbox. The Discovery protocol is handled internally by the DOE core.
> > 
> > This implementation complements the existing DOE implementation for
> > root complex in drivers/pci/doe.c.
> 
> This looks good to me!
> 
> I would love to see a handler implementation and integration with a driver
> as well.
> 
> For SPDM the handler could even be in userspace
> 

+1. We should not be introducing dead APIs.

- Mani

> Alistair
> 
> > 
> > Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
> > Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
> > Signed-off-by: Aksh Garg <a-garg7@ti.com>
> > ---
> >   drivers/pci/Kconfig     |  14 +
> >   drivers/pci/Makefile    |   1 +
> >   drivers/pci/doe-ep.c    | 671 ++++++++++++++++++++++++++++++++++++++++
> >   include/linux/pci-doe.h |  69 +++++
> >   include/linux/pci-epc.h |   4 +
> >   5 files changed, 759 insertions(+)
> >   create mode 100644 drivers/pci/doe-ep.c
> > 
> > diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> > index e3f848ffb52a..2a57a314054c 100644
> > --- a/drivers/pci/Kconfig
> > +++ b/drivers/pci/Kconfig
> > @@ -146,6 +146,20 @@ config PCI_DOE
> >   	  Say Y here if you want be able to communicate with PCIe DOE
> >   	  mailboxes.
> > +config PCI_DOE_EP
> > +	bool "PCI Endpoint Data Object Exchange (DOE) support"
> > +	depends on PCI_ENDPOINT
> > +	help
> > +	  This enables support for Data Object Exchange (DOE) protocol
> > +	  on PCI Endpoint controllers. It provides a communication
> > +	  mechanism through mailboxes, primarily used for PCIe security
> > +	  features.
> > +
> > +	  Say Y here if you want be able to communicate using PCIe DOE
> > +	  mailboxes.
> > +
> > +	  If unsure, say N.
> > +
> >   config PCI_ECAM
> >   	bool
> > diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
> > index e10cfe5a280b..66084e2ef9b2 100644
> > --- a/drivers/pci/Makefile
> > +++ b/drivers/pci/Makefile
> > @@ -34,6 +34,7 @@ obj-$(CONFIG_PCI_P2PDMA)	+= p2pdma.o
> >   obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
> >   obj-$(CONFIG_VGA_ARB)		+= vgaarb.o
> >   obj-$(CONFIG_PCI_DOE)		+= doe.o
> > +obj-$(CONFIG_PCI_DOE_EP)	+= doe-ep.o
> >   obj-$(CONFIG_PCI_IDE)		+= ide.o
> >   obj-$(CONFIG_PCI_TSM)		+= tsm.o
> >   obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
> > diff --git a/drivers/pci/doe-ep.c b/drivers/pci/doe-ep.c
> > new file mode 100644
> > index 000000000000..ff0551644f54
> > --- /dev/null
> > +++ b/drivers/pci/doe-ep.c
> > @@ -0,0 +1,671 @@
> > +// SPDX-License-Identifier: GPL-2.0-only or MIT
> > +/*
> > + * Data Object Exchange for PCIe Endpoint
> > + *	PCIe r7.0, sec 6.30 DOE
> > + *
> > + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com
> > + *	Aksh Garg <a-garg7@ti.com>
> > + *	Siddharth Vadapalli <s-vadapalli@ti.com>
> > + */
> > +
> > +#define dev_fmt(fmt) "DOE EP: " fmt
> > +
> > +#include <linux/bitfield.h>
> > +#include <linux/completion.h>
> > +#include <linux/device.h>
> > +#include <linux/mutex.h>
> > +#include <linux/pci.h>
> > +#include <linux/pci-epc.h>
> > +#include <linux/pci-doe.h>
> > +#include <linux/slab.h>
> > +#include <linux/workqueue.h>
> > +#include <linux/xarray.h>
> > +
> > +/*
> > + * Combines function number and capability offset into a unique lookup key
> > + * for storing/retrieving DOE mailboxes in an xarray.
> > + */
> > +#define PCI_DOE_MB_KEY(func, offset) \
> > +	(((unsigned long)(func) << 16) | (offset))
> > +
> > +/**
> > + * struct pci_doe_protocol - DOE protocol handler entry
> > + * @vid: Vendor ID
> > + * @type: Protocol type
> > + * @handler: Handler function pointer
> > + */
> > +struct pci_doe_protocol {
> > +	u16 vid;
> > +	u8 type;
> > +	pci_doe_protocol_handler_t handler;
> > +};
> > +
> > +/*
> > + * Global registry of protocol handlers,
> > + * holding struct pci_doe_protocol
> > + */
> > +static DEFINE_XARRAY_ALLOC(pci_doe_protocols);
> > +
> > +/* Serializes concurrent accesses to pci_doe_protocols xarray */
> > +static DEFINE_MUTEX(protocols_mutex);
> > +
> > +/**
> > + * struct pci_ep_doe_mb - State for a single DOE mailbox on EP
> > + *
> > + * This state is used to manage a single DOE mailbox capability on the
> > + * endpoint side.
> > + *
> > + * @epc: PCI endpoint controller this mailbox belongs to
> > + * @func_no: Physical function number of the function this mailbox belongs to
> > + * @cap_offset: Capability offset
> > + * @work_queue: Queue of work items
> > + * @flags: Bit array of PCI_DOE_FLAG_* flags
> > + */
> > +struct pci_ep_doe_mb {
> > +	struct pci_epc *epc;
> > +	u8 func_no;
> > +	u16 cap_offset;
> > +	struct workqueue_struct *work_queue;
> > +	unsigned long flags;
> > +};
> > +
> > +/**
> > + * struct pci_ep_doe_task - Represents a single DOE request/response task
> > + *
> > + * @feat: DOE feature (vendor ID and type)
> > + * @request_pl: Request payload
> > + * @request_pl_sz: Size of request payload in bytes
> > + * @response_pl: Output pointer for response buffer
> > + * @response_pl_sz: Output pointer for response size
> > + * @task_status: Task completion status (0 on success, -errno on failure)
> > + * @complete: Completion callback
> > + * @private: Private data for completion
> > + * @work: Work structure for workqueue
> > + * @doe_mb: DOE mailbox handling this task
> > + */
> > +struct pci_ep_doe_task {
> > +	struct pci_doe_feature feat;
> > +	const void *request_pl;
> > +	size_t request_pl_sz;
> > +	void **response_pl;
> > +	size_t *response_pl_sz;
> > +	int task_status;
> > +	void (*complete)(struct pci_ep_doe_task *task);
> > +	void *private;
> > +
> > +	/* Initialized by pci_ep_doe_submit_task() */
> > +	struct work_struct work;
> > +	struct pci_ep_doe_mb *doe_mb;
> > +};
> > +
> > +/**
> > + * pci_ep_doe_init() - Initialize the DOE framework for a controller in EP mode
> > + * @epc: PCI endpoint controller
> > + *
> > + * Initialize the DOE framework data structures. This only initializes
> > + * the xarray that will hold the mailboxes.
> > + *
> > + * RETURNS: 0 on success, -errno on failure
> > + */
> > +int pci_ep_doe_init(struct pci_epc *epc)
> > +{
> > +	if (!epc)
> > +		return -EINVAL;
> > +
> > +	xa_init(&epc->doe_mbs);
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_ep_doe_init);
> > +
> > +/**
> > + * pci_ep_doe_add_mailbox() - Add a DOE mailbox for a physical function
> > + * @epc: PCI endpoint controller
> > + * @func_no: Physical function number
> > + * @cap_offset: Offset of the DOE capability
> > + *
> > + * Create and register a DOE mailbox for the specified physical function
> > + * and capability offset. The controller driver should call this for each
> > + * DOE capability it finds in its config space.
> > + *
> > + * RETURNS: 0 on success, -errno on failure
> > + */
> > +int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset)
> > +{
> > +	struct pci_ep_doe_mb *doe_mb;
> > +	unsigned long key;
> > +	int ret;
> > +
> > +	if (!epc)
> > +		return -EINVAL;
> > +
> > +	doe_mb = kzalloc(sizeof(*doe_mb), GFP_KERNEL);
> > +	if (!doe_mb)
> > +		return -ENOMEM;
> > +
> > +	doe_mb->epc = epc;
> > +	doe_mb->func_no = func_no;
> > +	doe_mb->cap_offset = cap_offset;
> > +
> > +	doe_mb->work_queue = alloc_ordered_workqueue("pci_ep_doe[%s:pf%d:offset%x]", 0,
> > +						     dev_name(&epc->dev),
> > +						     func_no, cap_offset);
> > +	if (!doe_mb->work_queue) {
> > +		dev_err(epc->dev.parent,
> > +			"[pf%d:offset%x] failed to allocate work queue\n",
> > +			func_no, cap_offset);
> > +		ret = -ENOMEM;
> > +		goto err_free;
> > +	}
> > +
> > +	/* Add to xarray with composite key */
> > +	key = PCI_DOE_MB_KEY(func_no, cap_offset);
> > +	ret = xa_insert(&epc->doe_mbs, key, doe_mb, GFP_KERNEL);
> > +	if (ret) {
> > +		dev_err(epc->dev.parent,
> > +			"[pf%d:offset%x] failed to insert mailbox: %d\n",
> > +			func_no, cap_offset, ret);
> > +		goto err_destroy;
> > +	}
> > +
> > +	dev_dbg(epc->dev.parent,
> > +		"DOE mailbox added: pf%d offset 0x%x\n",
> > +		func_no, cap_offset);
> > +
> > +	return 0;
> > +
> > +err_destroy:
> > +	destroy_workqueue(doe_mb->work_queue);
> > +err_free:
> > +	kfree(doe_mb);
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_ep_doe_add_mailbox);
> > +
> > +/**
> > + * pci_ep_doe_register_protocol() - Register a protocol handler
> > + * @vendor: Vendor ID
> > + * @type: Protocol type
> > + * @handler: Handler function pointer
> > + *
> > + * Register a protocol handler that will be called when DOE requests
> > + * with the specified vendor ID and type are received. Protocol libraries
> > + * should call this during module initialization.
> > + *
> > + * RETURNS: 0 on success, -errno on failure
> > + */
> > +int pci_ep_doe_register_protocol(u16 vendor, u8 type,
> > +				 pci_doe_protocol_handler_t handler)
> > +{
> > +	struct pci_doe_protocol *protocol;
> > +	unsigned long index;
> > +	int ret;
> > +	u32 id;
> > +
> > +	if (!handler)
> > +		return -EINVAL;
> > +
> > +	/* Don't allow registering discovery protocol */
> > +	if (vendor == PCI_VENDOR_ID_PCI_SIG && type == PCI_DOE_FEATURE_DISCOVERY)
> > +		return -EINVAL;
> > +
> > +	mutex_lock(&protocols_mutex);
> > +
> > +	/* Check if already registered */
> > +	xa_for_each(&pci_doe_protocols, index, protocol) {
> > +		if (protocol->vid == vendor && protocol->type == type) {
> > +			ret = -EEXIST;
> > +			goto err;
> > +		}
> > +	}
> > +
> > +	protocol = kzalloc(sizeof(*protocol), GFP_KERNEL);
> > +	if (!protocol) {
> > +		ret = -ENOMEM;
> > +		goto err;
> > +	}
> > +
> > +	protocol->vid = vendor;
> > +	protocol->type = type;
> > +	protocol->handler = handler;
> > +
> > +	ret = xa_alloc(&pci_doe_protocols, &id, protocol,
> > +		       xa_limit_32b, GFP_KERNEL);
> > +	if (ret) {
> > +		kfree(protocol);
> > +		goto err;
> > +	}
> > +
> > +	mutex_unlock(&protocols_mutex);
> > +
> > +	pr_debug("DOE EP: Registered protocol %04x:%02x at index %u\n",
> > +		 vendor, type, id);
> > +
> > +	return 0;
> > +
> > +err:
> > +	mutex_unlock(&protocols_mutex);
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_ep_doe_register_protocol);
> > +
> > +/**
> > + * pci_ep_doe_unregister_protocol() - Unregister a protocol handler
> > + * @vendor: Vendor ID
> > + * @type: Protocol type
> > + *
> > + * Unregister a previously registered protocol handler. Protocol libraries
> > + * should call this during module cleanup.
> > + */
> > +void pci_ep_doe_unregister_protocol(u16 vendor, u8 type)
> > +{
> > +	struct pci_doe_protocol *protocol;
> > +	unsigned long index;
> > +
> > +	mutex_lock(&protocols_mutex);
> > +
> > +	/* Find and remove the protocol entry */
> > +	xa_for_each(&pci_doe_protocols, index, protocol) {
> > +		if (protocol->vid == vendor && protocol->type == type) {
> > +			xa_erase(&pci_doe_protocols, index);
> > +			kfree(protocol);
> > +			mutex_unlock(&protocols_mutex);
> > +			pr_debug("DOE EP: Unregistered protocol %04x:%02x\n",
> > +				 vendor, type);
> > +			return;
> > +		}
> > +	}
> > +
> > +	mutex_unlock(&protocols_mutex);
> > +	pr_warn("DOE EP: Attempted to unregister non-existent protocol %04x:%02x\n",
> > +		vendor, type);
> > +}
> > +EXPORT_SYMBOL_GPL(pci_ep_doe_unregister_protocol);
> > +
> > +/**
> > + * pci_ep_doe_cancel_tasks() - Cancel all pending tasks
> > + * @doe_mb: DOE mailbox
> > + *
> > + * Cancel all pending tasks in the mailbox. Mark the mailbox as dead
> > + * so no new tasks can be submitted.
> > + */
> > +static void pci_ep_doe_cancel_tasks(struct pci_ep_doe_mb *doe_mb)
> > +{
> > +	if (!doe_mb)
> > +		return;
> > +
> > +	/* Mark the mailbox as dead */
> > +	set_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags);
> > +
> > +	/* Stop all pending work items from starting */
> > +	set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
> > +}
> > +
> > +/**
> > + * pci_ep_doe_get_mailbox() - Get DOE mailbox by function and offset
> > + * @epc: PCI endpoint controller
> > + * @func_no: Physical function number
> > + * @cap_offset: Offset of the DOE capability
> > + *
> > + * Internal helper to look up a DOE mailbox by its function number and
> > + * capability offset.
> > + *
> > + * RETURNS: Pointer to the mailbox or NULL if not found
> > + */
> > +static struct pci_ep_doe_mb *pci_ep_doe_get_mailbox(struct pci_epc *epc,
> > +						    u8 func_no, u16 cap_offset)
> > +{
> > +	unsigned long key;
> > +
> > +	if (!epc)
> > +		return NULL;
> > +
> > +	key = PCI_DOE_MB_KEY(func_no, cap_offset);
> > +	return xa_load(&epc->doe_mbs, key);
> > +}
> > +
> > +/**
> > + * pci_ep_doe_find_protocol() - Find protocol handler
> > + * @vendor: Vendor ID
> > + * @type: Protocol type
> > + *
> > + * Look up a protocol handler in the registered protocols.
> > + *
> > + * RETURNS: Handler function pointer or NULL if not found
> > + */
> > +static pci_doe_protocol_handler_t pci_ep_doe_find_protocol(u16 vendor, u8 type)
> > +{
> > +	pci_doe_protocol_handler_t handler = NULL;
> > +	struct pci_doe_protocol *protocol;
> > +	unsigned long index;
> > +
> > +	mutex_lock(&protocols_mutex);
> > +
> > +	xa_for_each(&pci_doe_protocols, index, protocol) {
> > +		if (protocol->vid == vendor && protocol->type == type) {
> > +			handler = protocol->handler;
> > +			break;
> > +		}
> > +	}
> > +
> > +	mutex_unlock(&protocols_mutex);
> > +	return handler;
> > +}
> > +
> > +/**
> > + * pci_ep_doe_handle_discovery() - Handle Discovery protocol request
> > + * @request: Request payload
> > + * @request_sz: Request size
> > + * @response: Output pointer for response buffer
> > + * @response_sz: Output pointer for response size
> > + *
> > + * Handle the DOE Discovery protocol. The request contains an index specifying
> > + * which protocol to query. This function creates a response containing the
> > + * vendor ID and protocol type for the requested index, along with the next
> > + * index value for further discovery:
> > + *
> > + * - next_index = 0: Signals this is the last protocol supported
> > + * - next_index = n (non-zero): Signals more protocols available,
> > + *   query index n next
> > + *
> > + * RETURNS: 0 on success, -errno on failure
> > + */
> > +static int pci_ep_doe_handle_discovery(const void *request, size_t request_sz,
> > +				       void **response, size_t *response_sz)
> > +{
> > +	struct pci_doe_protocol *protocol;
> > +	u8 requested_index, next_index;
> > +	unsigned long index;
> > +	u8 xarray_count = 0;
> > +	u32 *response_pl;
> > +	u32 request_pl;
> > +	u16 vendor;
> > +	u8 type;
> > +
> > +	if (request_sz != sizeof(u32))
> > +		return -EINVAL;
> > +
> > +	request_pl = *(u32 *)request;
> > +	requested_index = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX, request_pl);
> > +
> > +	mutex_lock(&protocols_mutex);
> > +
> > +	/* Count protocols in xarray */
> > +	xa_for_each(&pci_doe_protocols, index, protocol)
> > +		xarray_count++;
> > +
> > +	if (requested_index > xarray_count) {
> > +		mutex_unlock(&protocols_mutex);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* Index 0 is always Discovery protocol */
> > +	if (requested_index == 0) {
> > +		vendor = PCI_VENDOR_ID_PCI_SIG;
> > +		type = PCI_DOE_FEATURE_DISCOVERY;
> > +	} else {
> > +		protocol = xa_load(&pci_doe_protocols, (unsigned long)(requested_index - 1));
> > +		if (!protocol) {
> > +			mutex_unlock(&protocols_mutex);
> > +			return -EINVAL;
> > +		}
> > +		vendor = protocol->vid;
> > +		type = protocol->type;
> > +	}
> > +	mutex_unlock(&protocols_mutex);
> > +
> > +	/* Calculate next index */
> > +	next_index = (requested_index < xarray_count) ? requested_index + 1 : 0;
> > +
> > +	response_pl = kzalloc(sizeof(*response_pl), GFP_KERNEL);
> > +	if (!response_pl)
> > +		return -ENOMEM;
> > +
> > +	/* Build response */
> > +	*response_pl = FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID, vendor) |
> > +		       FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE, type) |
> > +		       FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX, next_index);
> > +
> > +	*response = response_pl;
> > +	*response_sz = sizeof(*response_pl);
> > +
> > +	return 0;
> > +}
> > +
> > +static void signal_task_complete(struct pci_ep_doe_task *task, int status)
> > +{
> > +	task->task_status = status;
> > +	destroy_work_on_stack(&task->work);
> > +	task->complete(task);
> > +}
> > +
> > +/**
> > + * doe_ep_task_work() - Work function for processing DOE EP tasks
> > + * @work: Work structure
> > + *
> > + * Process a DOE request by calling the appropriate protocol handler.
> > + */
> > +static void doe_ep_task_work(struct work_struct *work)
> > +{
> > +	struct pci_ep_doe_task *task = container_of(work, struct pci_ep_doe_task,
> > +						    work);
> > +	struct pci_ep_doe_mb *doe_mb = task->doe_mb;
> > +	pci_doe_protocol_handler_t handler;
> > +	int rc;
> > +
> > +	if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) {
> > +		signal_task_complete(task, -EIO);
> > +		return;
> > +	}
> > +
> > +	/* Check if request was aborted */
> > +	if (test_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags)) {
> > +		signal_task_complete(task, -ECANCELED);
> > +		return;
> > +	}
> > +
> > +	/* Handle Discovery protocol directly */
> > +	if (task->feat.vid == PCI_VENDOR_ID_PCI_SIG &&
> > +	    task->feat.type == PCI_DOE_FEATURE_DISCOVERY) {
> > +		rc = pci_ep_doe_handle_discovery(task->request_pl,
> > +						 task->request_pl_sz,
> > +						 task->response_pl,
> > +						 task->response_pl_sz);
> > +		signal_task_complete(task, rc);
> > +		return;
> > +	}
> > +
> > +	/* Find protocol handler in xarray */
> > +	handler = pci_ep_doe_find_protocol(task->feat.vid, task->feat.type);
> > +	if (!handler) {
> > +		dev_warn(doe_mb->epc->dev.parent,
> > +			 "[%d:%x] Unsupported protocol VID=%04x TYPE=%02x\n",
> > +			 doe_mb->func_no, doe_mb->cap_offset,
> > +			 task->feat.vid, task->feat.type);
> > +		signal_task_complete(task, -EOPNOTSUPP);
> > +		return;
> > +	}
> > +
> > +	/* Call protocol handler */
> > +	rc = handler(task->request_pl, task->request_pl_sz,
> > +		     task->response_pl, task->response_pl_sz);
> > +
> > +	signal_task_complete(task, rc);
> > +}
> > +
> > +/**
> > + * pci_ep_doe_submit_task() - Submit a task to be processed
> > + * @doe_mb: DOE mailbox
> > + * @task: Task to submit
> > + *
> > + * Submit a DOE task to the workqueue for processing.
> > + * Task must be allocated on the stack.
> > + *
> > + * RETURNS: 0 on success, -errno on failure
> > + */
> > +static int pci_ep_doe_submit_task(struct pci_ep_doe_mb *doe_mb,
> > +				  struct pci_ep_doe_task *task)
> > +{
> > +	if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags))
> > +		return -EIO;
> > +
> > +	task->doe_mb = doe_mb;
> > +	INIT_WORK_ONSTACK(&task->work, doe_ep_task_work);
> > +	queue_work(doe_mb->work_queue, &task->work);
> > +	return 0;
> > +}
> > +
> > +static void pci_ep_doe_task_complete(struct pci_ep_doe_task *task)
> > +{
> > +	complete(task->private);
> > +}
> > +
> > +/**
> > + * pci_ep_doe_process_request() - Process DOE request on endpoint
> > + * @epc: PCI endpoint controller
> > + * @func_no: Physical function number
> > + * @cap_offset: DOE capability offset
> > + * @vendor: Vendor ID from request header
> > + * @type: Protocol type from request header
> > + * @request: Request payload in CPU-native format
> > + * @request_sz: Size of request payload (bytes)
> > + * @response: Output pointer for response buffer in CPU-native format
> > + * @response_sz: Output pointer for response size (bytes)
> > + *
> > + * Process a DOE request received on the endpoint. The request payload should
> > + * not include the DOE header (vendor/type/length). The protocol handler will
> > + * allocate the response buffer, which the caller (controller driver) must
> > + * free after use.
> > + *
> > + * As per DOE specification, a mailbox processes one request at a time.
> > + * Therefore, this function will never be called concurrently for the same
> > + * mailbox by different callers.
> > + *
> > + * The caller is responsible for the conversion of the received DOE request
> > + * with le32_to_cpu() before calling this function.
> > + * Similarly, it is responsible for converting the response payload with
> > + * cpu_to_le32() before sending it back over the DOE mailbox.
> > + *
> > + * The caller is also responsible for ensuring that the request size
> > + * is within the limits defined by PCI_DOE_MAX_LENGTH.
> > + *
> > + * RETURNS: 0 on success, -errno on failure
> > + */
> > +int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
> > +			       u16 vendor, u8 type, const void *request,
> > +			       size_t request_sz, void **response, size_t *response_sz)
> > +{
> > +	DECLARE_COMPLETION_ONSTACK(c);
> > +	struct pci_ep_doe_mb *doe_mb;
> > +	struct pci_ep_doe_task task = {
> > +		.feat.vid = vendor,
> > +		.feat.type = type,
> > +		.request_pl = request,
> > +		.request_pl_sz = request_sz,
> > +		.response_pl = response,
> > +		.response_pl_sz = response_sz,
> > +		.complete = pci_ep_doe_task_complete,
> > +		.private = &c,
> > +	};
> > +	int rc;
> > +
> > +	doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
> > +	if (!doe_mb)
> > +		return -ENODEV;
> > +
> > +	rc = pci_ep_doe_submit_task(doe_mb, &task);
> > +	if (rc)
> > +		return rc;
> > +
> > +	wait_for_completion(&c);
> > +
> > +	return task.task_status;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_ep_doe_process_request);
> > +
> > +/**
> > + * pci_ep_doe_abort() - Abort DOE operations on a mailbox
> > + * @epc: PCI endpoint controller
> > + * @func_no: Physical function number
> > + * @cap_offset: DOE capability offset
> > + *
> > + * Abort all queued and wait for in-flight DOE operations to complete for the
> > + * specified mailbox. This function is called by the EP controller driver
> > + * when the RC sets the ABORT bit in the DOE Control register.
> > + *
> > + * The function will:
> > + *
> > + * - Set CANCEL flag to prevent new requests in the queue from starting
> > + * - Wait for the currently executing handler to complete (cannot interrupt)
> > + * - Flush the workqueue to wait for all requests to be handled appropriately
> > + * - Clear CANCEL flag to prepare for new requests
> > + *
> > + * RETURNS: 0 on success, -errno on failure
> > + */
> > +int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset)
> > +{
> > +	struct pci_ep_doe_mb *doe_mb;
> > +
> > +	if (!epc)
> > +		return -EINVAL;
> > +
> > +	doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
> > +	if (!doe_mb)
> > +		return -ENODEV;
> > +
> > +	/* Set CANCEL flag - worker will abort queued requests */
> > +	set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
> > +	flush_workqueue(doe_mb->work_queue);
> > +
> > +	/* Clear CANCEL flag - mailbox ready for new requests */
> > +	clear_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
> > +
> > +	dev_dbg(epc->dev.parent,
> > +		"DOE mailbox aborted: PF%d offset 0x%x\n",
> > +		func_no, cap_offset);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(pci_ep_doe_abort);
> > +
> > +/**
> > + * pci_ep_doe_destroy_mb() - Destroy a single DOE mailbox
> > + * @doe_mb: DOE mailbox to destroy
> > + *
> > + * Internal function to destroy a mailbox and free its resources.
> > + */
> > +static void pci_ep_doe_destroy_mb(struct pci_ep_doe_mb *doe_mb)
> > +{
> > +	if (!doe_mb)
> > +		return;
> > +
> > +	pci_ep_doe_cancel_tasks(doe_mb);
> > +
> > +	if (doe_mb->work_queue)
> > +		destroy_workqueue(doe_mb->work_queue);
> > +
> > +	kfree(doe_mb);
> > +}
> > +
> > +/**
> > + * pci_ep_doe_destroy() - Destroy all DOE mailboxes
> > + * @epc: PCI endpoint controller
> > + *
> > + * Destroy all DOE mailboxes and free associated resources.
> > + *
> > + * This function should be called during controller cleanup to free all
> > + * DOE resources.
> > + */
> > +void pci_ep_doe_destroy(struct pci_epc *epc)
> > +{
> > +	struct pci_ep_doe_mb *doe_mb;
> > +	unsigned long index;
> > +
> > +	if (!epc)
> > +		return;
> > +
> > +	xa_for_each(&epc->doe_mbs, index, doe_mb)
> > +		pci_ep_doe_destroy_mb(doe_mb);
> > +
> > +	xa_destroy(&epc->doe_mbs);
> > +}
> > +EXPORT_SYMBOL_GPL(pci_ep_doe_destroy);
> > diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
> > index a966626c47f9..b353c3a1aa82 100644
> > --- a/include/linux/pci-doe.h
> > +++ b/include/linux/pci-doe.h
> > @@ -13,6 +13,8 @@
> >   #ifndef LINUX_PCI_DOE_H
> >   #define LINUX_PCI_DOE_H
> > +#include <linux/types.h>
> > +
> >   /* Mailbox state flags */
> >   #define PCI_DOE_FLAG_CANCEL		0
> >   #define PCI_DOE_FLAG_DEAD		1
> > @@ -24,12 +26,19 @@
> >   #define PCI_DOE_FEATURE_CMA		1
> >   #define PCI_DOE_FEATURE_SSESSION	2
> > +typedef int (*pci_doe_protocol_handler_t)(const void *request,
> > +					  size_t request_sz,
> > +					  void **response,
> > +					  size_t *response_sz);
> > +
> >   struct pci_doe_feature {
> >   	u16 vid;
> >   	u8 type;
> >   };
> >   struct pci_doe_mb;
> > +struct pci_dev;
> > +struct pci_epc;
> >   #ifdef CONFIG_PCI_DOE
> >   struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
> > @@ -54,4 +63,64 @@ static inline int pci_doe(struct pci_doe_mb *doe_mb, u16 vendor, u8 type,
> >   }
> >   #endif /* CONFIG_PCI_DOE */
> > +#ifdef CONFIG_PCI_DOE_EP
> > +int pci_ep_doe_init(struct pci_epc *epc);
> > +
> > +int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset);
> > +
> > +int pci_ep_doe_register_protocol(u16 vendor, u8 type,
> > +				 pci_doe_protocol_handler_t handler);
> > +
> > +void pci_ep_doe_unregister_protocol(u16 vendor, u8 type);
> > +
> > +int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
> > +			       u16 vendor, u8 type, const void *request,
> > +			       size_t request_sz, void **response, size_t *response_sz);
> > +
> > +int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset);
> > +
> > +void pci_ep_doe_destroy(struct pci_epc *epc);
> > +
> > +#else
> > +static inline int pci_ep_doe_init(struct pci_epc *epc)
> > +{
> > +	return -EOPNOTSUPP;
> > +}
> > +
> > +static inline int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no,
> > +					 u16 cap_offset)
> > +{
> > +	return -EOPNOTSUPP;
> > +}
> > +
> > +static inline int pci_ep_doe_register_protocol(u16 vendor, u8 type,
> > +					       pci_doe_protocol_handler_t handler)
> > +{
> > +	return -EOPNOTSUPP;
> > +}
> > +
> > +static inline void pci_ep_doe_unregister_protocol(u16 vendor, u8 type)
> > +{
> > +}
> > +
> > +static inline int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
> > +					     u16 vendor, u8 type,
> > +					     const void *request, size_t request_sz,
> > +					     void **response, size_t *response_sz)
> > +{
> > +	return -EOPNOTSUPP;
> > +}
> > +
> > +static inline int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no,
> > +				   u16 cap_offset)
> > +{
> > +	return -EOPNOTSUPP;
> > +}
> > +
> > +static inline void pci_ep_doe_destroy(struct pci_epc *epc)
> > +{
> > +}
> > +
> > +#endif /* CONFIG_PCI_DOE_EP */
> > +
> >   #endif /* LINUX_PCI_DOE_H */
> > diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
> > index 4286bfdbfdfa..ad4de4d33da3 100644
> > --- a/include/linux/pci-epc.h
> > +++ b/include/linux/pci-epc.h
> > @@ -165,6 +165,7 @@ struct pci_epc_mem {
> >    * @domain_nr: PCI domain number of the endpoint controller
> >    * @init_complete: flag to indicate whether the EPC initialization is complete
> >    *                 or not
> > + * @doe_mbs: array of DOE mailboxes (CONFIG_PCI_DOE_EP)
> >    */
> >   struct pci_epc {
> >   	struct device			dev;
> > @@ -182,6 +183,9 @@ struct pci_epc {
> >   	unsigned long			function_num_map;
> >   	int				domain_nr;
> >   	bool				init_complete;
> > +#ifdef CONFIG_PCI_DOE_EP
> > +	struct xarray			doe_mbs;
> > +#endif
> >   };
> >   /**
> 
> 

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 0/4] PCI: Add DOE support for endpoint
  2026-02-13 12:35 [RFC PATCH 0/4] PCI: Add DOE support for endpoint Aksh Garg
                   ` (4 preceding siblings ...)
  2026-02-13 13:16 ` [RFC PATCH 0/4] PCI: Add DOE support for endpoint Niklas Cassel
@ 2026-03-04 14:22 ` Manivannan Sadhasivam
  5 siblings, 0 replies; 19+ messages in thread
From: Manivannan Sadhasivam @ 2026-03-04 14:22 UTC (permalink / raw)
  To: Aksh Garg
  Cc: linux-pci, linux-doc, bhelgaas, corbet, cassel, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk

On Fri, Feb 13, 2026 at 06:05:59PM +0530, Aksh Garg wrote:
> This patch series introduces the framework for supporting the Data
> Object Exchange (DOE) feature for PCIe endpoint devices. Please refer
> to the documentation added in patch 1 for details on the feature and
> implementation architecture.
> 
> The implementation provides a common framework for all PCIe endpoint
> controllers, not specific to any particular SoC vendor.
> 
> Posting this as an RFC patch series to get feedback on the design and
> implementation.
> 

Just left some comments inline. In the next non-RFC version, please post the
EPC and handler changes as well.

It is OK if you can post them separately so that we can merge the common bits
first.

- Mani

> Aksh Garg (4):
>   PCI: Add documentation for DOE endpoint support
>   PCI/DOE: Move common definitions to the header file
>   PCI/DOE: Add DOE mailbox support for endpoint functions
>   PCI: Document APIs for endpoint DOE implementation
> 
>  Documentation/PCI/index.rst      |   1 +
>  Documentation/PCI/pci-doe-ep.rst | 297 ++++++++++++++
>  drivers/pci/Kconfig              |  14 +
>  drivers/pci/Makefile             |   1 +
>  drivers/pci/doe-ep.c             | 671 +++++++++++++++++++++++++++++++
>  drivers/pci/doe.c                |  11 -
>  include/linux/pci-doe.h          | 107 ++++-
>  include/linux/pci-epc.h          |   4 +
>  8 files changed, 1090 insertions(+), 16 deletions(-)
>  create mode 100644 Documentation/PCI/pci-doe-ep.rst
>  create mode 100644 drivers/pci/doe-ep.c
> 
> -- 
> 2.34.1
> 
> 

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions
  2026-03-04 14:17   ` Manivannan Sadhasivam
@ 2026-03-06  8:17     ` Aksh Garg
  0 siblings, 0 replies; 19+ messages in thread
From: Aksh Garg @ 2026-03-06  8:17 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: linux-pci, linux-doc, bhelgaas, corbet, cassel, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk



On 04/03/26 19:47, Manivannan Sadhasivam wrote:
> On Fri, Feb 13, 2026 at 06:06:02PM +0530, Aksh Garg wrote:
>> From: Aksh Garg <a-garg7@ti.com>
>>
>> +/**
>> + * pci_ep_doe_add_mailbox() - Add a DOE mailbox for a physical function
>> + * @epc: PCI endpoint controller
>> + * @func_no: Physical function number
>> + * @cap_offset: Offset of the DOE capability
>> + *
>> + * Create and register a DOE mailbox for the specified physical function
>> + * and capability offset. The controller driver should call this for each
>> + * DOE capability it finds in its config space.
> 
> Why can't we call this helper from the EPC core driver instead? We may have to
> introduce a new callback in 'struct pci_epc_ops' to read the endpoint config
> space from the EPC core driver. But it will avoid duplicating the code across
> the EPC drivers.
> 
> End of the day, we can read the config space of the device and identify the
> capability in a generic way.

Thank you for the feedback. This looks better to me. Please let me know
if the following flow would be correct:

'struct pci_epc_ops' would have a new callback to find the extended
capability, taking inputs as struct pci_epc *epc, u8 func_no, u8 cap.
The EPC core driver would have an API, taking struct pci_epc* as
argument. This would find the extended DOE capability offsets for the
mailboxes for all the functions in epc->max_functions, and call
pci_ep_doe_add_mailbox() for each of them.

This newly created API would be called by the controller driver if it is
DOE capable. The DOE capability feature can be added in the 'struct
pci_epc_features' as well based on which this API would be called.

> 
>> + *
>> + * RETURNS: 0 on success, -errno on failure
>> + */
>> +int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset)
>> +{
>> +	struct pci_ep_doe_mb *doe_mb;
>> +	unsigned long key;
>> +	int ret;
>> +
>> +	if (!epc)
>> +		return -EINVAL;
>> +
>> +	doe_mb = kzalloc(sizeof(*doe_mb), GFP_KERNEL);
>> +	if (!doe_mb)
>> +		return -ENOMEM;
>> +
>> +	doe_mb->epc = epc;
>> +	doe_mb->func_no = func_no;
>> +	doe_mb->cap_offset = cap_offset;
>> +
>> +	doe_mb->work_queue = alloc_ordered_workqueue("pci_ep_doe[%s:pf%d:offset%x]", 0,
>> +						     dev_name(&epc->dev),
>> +						     func_no, cap_offset);
> 
> I believe you are trying to use cap_offset to differentiate between multiple DOE
> mailbox instances within a single function. In that case, I'd use a simple index
> instead of cap_offset.

Yes, the cap_offset is used to differentiate between the mailboxes.

There were two reasons I chose this as a differentiator instead of a
simple index:
- doe.c uses cap_offset as differentiator, hence I used the same to make
   doe-ep.c code compatible with it
- Because the cap_offset would be unique across the mailboxes for a
   single function, we do not have to create and manage an index for the
   uniqueness of the mailbox, which doesn't add the redundancy of
   introducing an index variable just as a differentiator.


Regards,
Aksh Garg

> 
> Rest looks OK from a high level design perspective.
> 
> - Mani
> 


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions
  2026-03-04 14:18     ` Manivannan Sadhasivam
@ 2026-03-06 12:14       ` Aksh Garg
  0 siblings, 0 replies; 19+ messages in thread
From: Aksh Garg @ 2026-03-06 12:14 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Alistair Francis
  Cc: linux-pci, linux-doc, bhelgaas, corbet, cassel, linux-kernel,
	linux-arm-kernel, s-vadapalli, danishanwar, srk

Mani, Alistair

On 04/03/26 19:48, Manivannan Sadhasivam wrote:
> On Wed, Feb 18, 2026 at 02:28:23PM +1000, Alistair Francis wrote:
>> On 13/2/26 22:36, Aksh Garg wrote:
>>> From: Aksh Garg <a-garg7@ti.com>
>>>
>>> DOE (Data Object Exchange) is a standard PCIe extended capability
>>> feature introduced in the Data Object Exchange (DOE) ECN for
>>> PCIe r5.0. It provides a communication mechanism primarily used for
>>> implementing PCIe security features such as device authentication, and
>>> secure link establishment. Think of DOE as a sophisticated mailbox
>>> system built into PCIe. The root complex can send structured requests
>>> to the endpoint device through DOE mailboxes, and the endpoint device
>>> responds with appropriate data.
>>>
>>> Add the DOE support for PCIe endpoint devices, enabling endpoint
>>> functions to process the DOE requests from the host. The implementation
>>> provides framework APIs for controller drivers to register mailboxes,
>>> protocol handler registration for different DOE data object types, and
>>> request processing with workqueues ensuring sequential handling per
>>> mailbox. The Discovery protocol is handled internally by the DOE core.
>>>
>>> This implementation complements the existing DOE implementation for
>>> root complex in drivers/pci/doe.c.
>>
>> This looks good to me!
>>
>> I would love to see a handler implementation and integration with a driver
>> as well.
>>
>> For SPDM the handler could even be in userspace
>>
> 
> +1. We should not be introducing dead APIs.
> 
> - Mani
> 

I am planning to remove the register/unregister protocol APIs, and add a
static array of 'struct pci_doe_protocol' instead of a dynamic xarray.
By this, we would not be relying on someone to call the register
protocol on behalf of a library.
Whenever a new library comes up for DOE protocol, the library would have
the handler function, and the static array simply needs to populated
with this handler function in doe-ep.c

Please share your thoughts on this approach.

>> Alistair
>>
>>>
>>> Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
>>> Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
>>> Signed-off-by: Aksh Garg <a-garg7@ti.com>
>>> ---

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-03-06 12:14 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-13 12:35 [RFC PATCH 0/4] PCI: Add DOE support for endpoint Aksh Garg
2026-02-13 12:36 ` [RFC PATCH 1/4] PCI: Add documentation for DOE endpoint support Aksh Garg
2026-02-13 20:33   ` Niklas Cassel
2026-02-18 11:21     ` Aksh Garg
2026-02-13 12:36 ` [RFC PATCH 2/4] PCI/DOE: Move common definitions to the header file Aksh Garg
2026-02-22 13:01   ` Lukas Wunner
2026-02-23  7:30     ` Aksh Garg
2026-02-13 12:36 ` [RFC PATCH 3/4] PCI/DOE: Add DOE mailbox support for endpoint functions Aksh Garg
2026-02-13 13:21   ` Niklas Cassel
2026-02-18  4:28   ` Alistair Francis
2026-03-04 14:18     ` Manivannan Sadhasivam
2026-03-06 12:14       ` Aksh Garg
2026-02-22 13:06   ` Lukas Wunner
2026-02-23 10:44     ` Aksh Garg
2026-03-04 14:17   ` Manivannan Sadhasivam
2026-03-06  8:17     ` Aksh Garg
2026-02-13 12:36 ` [RFC PATCH 4/4] PCI: Document APIs for endpoint DOE implementation Aksh Garg
2026-02-13 13:16 ` [RFC PATCH 0/4] PCI: Add DOE support for endpoint Niklas Cassel
2026-03-04 14:22 ` Manivannan Sadhasivam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox