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.
The changes since v1 are documented in the respective patch descriptions.
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 (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;
}
if (readl(drv->base + PF_DOE_CTRL_REG(func_no, cap_offset)) & DOE_CTRL_ABORT) {
/* Aborted: do not send response */
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));
/* unset BUSY bit */
writel(0 << DOE_STATUS_BUSY,
drv->base + PF_DOE_STATUS_REG(func_no, cap_offset));
free:
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));
}
/* Set BUSY bit */
writel(1 << DOE_STATUS_BUSY,
drv->base + PF_DOE_STATUS_REG(func_no, cap_offset));
/* 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);
/* Abort pending/in-flight operations in DOE-EP core */
pci_ep_doe_abort(drv->epc, func_no, cap_offset);
/* 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_BUSY) |
(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 | 329 +++++++++++
drivers/pci/doe.c | 11 -
drivers/pci/endpoint/Kconfig | 14 +
drivers/pci/endpoint/Makefile | 1 +
drivers/pci/endpoint/pci-ep-doe.c | 553 ++++++++++++++++++
drivers/pci/endpoint/pci-epc-core.c | 92 +++
drivers/pci/pci.h | 48 ++
include/linux/pci-doe.h | 8 +
include/linux/pci-epc.h | 9 +
10 files changed, 1055 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