[PATCH v5 0/4] PCI: Add DOE support for endpoint
Frank Li
Frank.li at oss.nxp.com
Thu Jun 11 13:47:18 PDT 2026
On Wed, Jun 10, 2026 at 03:32:52PM +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 4 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.
>
General question, does DOE generate irq when received msg for HOST? I have
not related irq handle code.
Any program to test it? such as pci_endpoint_test, need at least one real
user to use it.
Frank
> The changes since v1 are documented in the respective patch descriptions.
>
> v4: https://lore.kernel.org/all/20260522052434.802034-1-a-garg7@ti.com/
> v3: https://lore.kernel.org/all/20260427051725.223704-1-a-garg7@ti.com/
> v2: https://lore.kernel.org/all/20260401073022.215805-1-a-garg7@ti.com/
> v1 (RFC): https://lore.kernel.org/all/20260213123603.420941-1-a-garg7@ti.com/
>
> Below is a code demonstration showing the integration of DOE-EP APIs with
> EPC drivers.
>
> Note: The provided code is just to show how an EPC driver is expected to
> utilize the pci_ep_doe_process_request() and pci_ep_doe_abort() APIs,
> and might not cover all the corner cases. The below implementation
> also expects the EPC hardware to have some memory buffer to store the
> data from(for) write_mailbox(read_mailbox) DOE capability registers.
>
> ============================================================================
>
> /* ========== DOE Completion Callback (invoked by DOE-EP core) ========== */
>
> static void doe_completion_cb(struct pci_epc *epc, u8 func_no, u16 cap_offset,
> int status, u16 vendor, u8 type,
> void *response_pl, size_t response_pl_sz)
> {
> struct epc_driver *drv = epc_get_drvdata(epc);
> u32 *response = (u32 *)response_pl;
> u32 header1, header2;
> int payload_dw, i;
>
> if (readl(drv->base + PF_DOE_CTRL_REG(func_no, cap_offset)) & DOE_CTRL_ABORT) {
> /* Aborted: do not send response */
> goto free;
> }
>
> if (status < 0) {
> /* Error: set ERROR bit in DOE Status register */
> writel(1 << DOE_STATUS_ERROR,
> drv->base + PF_DOE_STATUS_REG(func_no, cap_offset));
> goto free;
> }
>
> /* Success: write DOE headers first, then response to the read memory */
>
> /* Header 1: Vendor ID (bits 15:0) | Type (bits 23:16) */
> header1 = (type << 16) | vendor;
> writel(header1, drv->base + PF_DOE_RD_MEMORY_WR_REG(func_no, cap_offset));
>
> /* Header 2: Length in DW (including 2 DW of headers + payload) */
> payload_dw = DIV_ROUND_UP(response_pl_sz, sizeof(u32));
> header2 = 2 + payload_dw; /* 2 header DWs + payload */
> writel(header2, drv->base + PF_DOE_RD_MEMORY_WR_REG(func_no, cap_offset));
>
> /* Set READY bit to signal response ready */
> writel(1 << DOE_STATUS_READY,
> drv->base + PF_DOE_STATUS_REG(func_no, cap_offset));
>
> /* Write response payload DWORDs to Read memory */
> for (i = 0; i < payload_dw; i++)
> writel(response[i],
> drv->base + PF_DOE_RD_MEMORY_WR_REG(func_no, cap_offset));
>
> /* Wait for the memory to empty before clearing the READY bit */
> while (!RD_MEMORY_EMPTY()) {/* wait */}
>
> writel(0 << DOE_STATUS_READY,
> drv->base + PF_DOE_STATUS_REG(func_no, cap_offset));
>
> free:
> /* unset BUSY bit */
> writel(0 << DOE_STATUS_BUSY,
> drv->base + PF_DOE_STATUS_REG(func_no, cap_offset));
>
> kfree(response_pl);
> }
>
> /* ========== DOE Interrupt Handler (triggered on GO bit from root complex) ========== */
>
> static irqreturn_t doe_interrupt_handler(int irq, void *priv)
> {
> struct epc_driver *drv = priv;
> u16 cap_offset = extract_cap_offset_from_irq(irq);
> u8 func_no = extract_func_from_irq(irq);
> u32 header1, header2, length_dw, *request;
> u16 vendor;
> u8 type;
> int i, ret;
>
> /* Read first header DWORD: Vendor ID (bits 15:0) | Type (bits 23:16) */
> header1 = readl(drv->base + PF_DOE_WR_MEMORY_RD_REG(func_no, cap_offset));
> vendor = header1 & 0xFFFF;
> type = (header1 >> 16) & 0xFF;
>
> /* Read second header DWORD: Length in DW (includes 2 DW of headers) */
> header2 = readl(drv->base + PF_DOE_WR_MEMORY_RD_REG(func_no, cap_offset));
> length_dw = header2 & 0x3FFFF; /* Bits 17:0 */
>
> if (!length_dw)
> length_dw = PCI_DOE_MAX_LENGTH;
>
> length_dw -= 2; /* Subtract 2 DW of headers to get payload length */
> /* Allocate buffer for complete request (headers + payload) */
> request = kzalloc(length_dw * sizeof(u32), GFP_ATOMIC);
> if (!request) {
> writel(1 << DOE_STATUS_ERROR,
> drv->base + PF_DOE_STATUS_REG(func_no, cap_offset));
> return IRQ_HANDLED;
> }
>
> /* Read remaining payload DWORDs from Write memory */
> for (i = 0; i < length_dw; i++) {
> while (WR_MEMORY_EMPTY()) { /* wait */ }
> request[i] = readl(drv->base + PF_DOE_WR_MEMORY_RD_REG(func_no, cap_offset));
> }
>
> mutex_lock(&lock);
> /* Check the ABORT bit, if set then return */
> if (readl(drv->base + PF_DOE_CTRL_REG(func_no, cap_offset)) & DOE_CTRL_ABORT) {
> kfree(request);
> mutex_unlock(&lock);
> return IRQ_HANDLED;
> }
>
> /* Set BUSY bit */
> writel(1 << DOE_STATUS_BUSY,
> drv->base + PF_DOE_STATUS_REG(func_no, cap_offset));
> mutex_unlock(&lock);
>
> /* Hand off to DOE-EP core for asynchronous processing */
> ret = pci_ep_doe_process_request(drv->epc, func_no, cap_offset,
> vendor, type, (void *)request,
> length_dw * sizeof(u32),
> doe_completion_cb);
> if (ret) {
> writel(1 << DOE_STATUS_ERROR,
> drv->base + PF_DOE_STATUS_REG(func_no, cap_offset));
> kfree(request);
> }
>
> return IRQ_HANDLED;
> }
>
> /* ========== Abort Handler (triggered on ABORT bit from root complex) ========== */
>
> static irqreturn_t doe_abort_handler(int irq, void *priv)
> {
> struct epc_driver *drv = priv;
> u16 cap_offset = extract_cap_offset_from_irq(irq);
> u8 func_no = extract_func_from_irq(irq);
>
> mutex_lock(&lock);
>
> /* call abort API only if BUSY bit set (pci_ep_doe_process_request() called) */
> if (readl(drv->base + PF_DOE_STATUS_REG(func_no, cap_offset)) & DOE_STATUS_BUSY)
> pci_ep_doe_abort(drv->epc, func_no, cap_offset);
>
> mutex_unlock(&lock);
>
> /* Discard Write memory contents */
> writel(DOE_WR_MEMORY_CTRL_DISCARD,
> drv->base + PF_DOE_WR_MEMORY_CTRL_REG(func_no, cap_offset));
>
> /* Clear status bits */
> writel((0 << DOE_STATUS_ERROR) | (0 << DOE_STATUS_READY),
> drv->base + PF_DOE_STATUS_REG(func_no, cap_offset));
>
> return IRQ_HANDLED;
> }
>
> ====================================================================================
>
> Aksh Garg (4):
> PCI/DOE: Move common definitions to the header file
> PCI: endpoint: Add DOE mailbox support for endpoint functions
> PCI: endpoint: Add support for DOE initialization and setup in EPC
> core
> Documentation: PCI: Add documentation for DOE endpoint support
>
> Documentation/PCI/endpoint/index.rst | 1 +
> .../PCI/endpoint/pci-endpoint-doe.rst | 333 ++++++++++
> drivers/pci/doe.c | 11 -
> drivers/pci/endpoint/Kconfig | 14 +
> drivers/pci/endpoint/Makefile | 1 +
> drivers/pci/endpoint/pci-ep-doe.c | 594 ++++++++++++++++++
> drivers/pci/endpoint/pci-epc-core.c | 104 +++
> drivers/pci/pci.h | 48 ++
> include/linux/pci-doe.h | 8 +
> include/linux/pci-epc.h | 9 +
> 10 files changed, 1112 insertions(+), 11 deletions(-)
> create mode 100644 Documentation/PCI/endpoint/pci-endpoint-doe.rst
> create mode 100644 drivers/pci/endpoint/pci-ep-doe.c
>
> --
> 2.34.1
>
More information about the linux-arm-kernel
mailing list