[PATCH v5 0/4] PCI: Add DOE support for endpoint

Aksh Garg a-garg7 at ti.com
Fri Jun 12 00:58:06 PDT 2026



On 12/06/26 02:17, Frank Li wrote:
> 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.
>>

Hi Frank,

> 
> General question, does DOE generate irq when received msg for HOST? I have
> not related irq handle code.
> 

The EPC hardware is expected to raise IRQ when it receives DOE signals
from the host. The example IRQ code handler have been provided below.

When the response DOE is ready for the host, the signal_task_complete()
in pci-ep-doe.c invokes completion callback function, through which the
EPC driver handles to send the response back to the host using the DOE
mailbox.

> Any program to test it? such as pci_endpoint_test, need at least one real
> user to use it.
> 

Currently there is no EPC driver upstream which support DOE yet.
However, you can refer to the conversation at [1] where the plan to add
user for this framework has been discussed.

Regards,
Aksh Garg

> 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