* [PATCH v6 2/4] PCI: endpoint: Add DOE mailbox support for endpoint functions
From: Aksh Garg @ 2026-06-23 9:07 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, rdunlap, Frank.Li, s-vadapalli,
danishanwar, srk, a-garg7
In-Reply-To: <20260623090737.711656-1-a-garg7@ti.com>
DOE (Data Object Exchange) is a standard PCIe extended capability
feature defined in PCI Express Base Specification Revision 7.0,
Section 6.30. 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 EPC core driver and controller drivers to
register mailboxes, and request processing with workqueues ensuring
sequential handling per mailbox, and parallel handling across mailboxes.
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>
---
Changes from v5 yo v6:
- Addressed the review comments provided by Bjorn Helgaas at v5
- Used xa_lock_irqsave() instead of xa_lock() in pci_ep_doe_get_mailbox()
- Added a spinlock to protect the work_queue of the doe_mb struct from
being destroyed while being called
Changes from v4 to v5:
- Addressed the review comments by Sashiko
- Added refcount per DOE Mailbox to fix Use-After-Free bug
- Change in the Abort Sequence:
* Instead of waiting on flush_workqueue() to clear the CANCEL flag,
return immediately after setting the CANCEL flag. The CANCEL flag
gets cleared in signal_task_complete(), allowing the mailbox to
accept new requests
* Abort sequence handling in various scenarios is updated and explained
in the documentation at PATCH 4/4
Changes from v3 to v4:
- Used 'Returns' instead of 'RETURNS' in the function docstrings to
comply with kernel-doc format, as suggested by Manivannan Sadhasivam.
- In pci_ep_doe_process_request(), changed the type of request buffer
from "const void *" to "void *", as the ownership is transferred to
DOE-EP framework, which is responsible to free the buffer.
- Added "struct pci_epc *epc" to typedef "pci_ep_doe_complete_t", to be
used by the EPC driver.
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes since v1:
- Moved the DOE-EP core file to drivers/pci/endpoint/pci-ep-doe.c, and
corresponding Kconfig and Makefile to match the existing naming scheme,
as suggested by Niklas Cassel.
- Renamed the config from PCI_DOE_EP to PCI_ENDPOINT_DOE
- Moved the function declarations that need not be visible outside the
PCI core to drivers/pci/pci.h instead to include/linux/pci-doe.h as
suggested by Lukas Wunner
- Converted from synchronous to asynchronous request processing:
* Removed wait_for_completion() from pci_ep_doe_process_request()
* Function returns immediately after queuing to workqueue, hence
removed private data for completion in the task structure
* Added completion callback as an additional argument to
pci_ep_doe_process_request(), which takes the response and status
parameters as arguments (along with other required arguments), hence
removed task_status in the task structure
* Created a typedef pci_ep_doe_complete_t for completion callback
* Removed the pci_ep_doe_task_complete() function, as it would not be
required anymore with these changes
* Moved from INIT_WORK_ONSTACK() to INIT_WORK(), to initialize the work
on heap instead of stack
* signal_task_complete() now invokes the completion callback, once the
protocol handler completes its task
- Changed from dynamic xarray-based protocol registration to static array:
* Removed the register/unregister protocol APIs
* Replaced the dynamic xarray with static array of struct pci_doe_protocol
* Added discovery protocol to static array, instead of treating it specially,
hence removed the special handling for Discovery protocol in
doe_ep_task_work()
* Updated pci_ep_doe_handle_discovery() and pci_ep_doe_find_protocol()
accordingly.
- Memory Management:
* DOE core frees request buffer in signal_task_complete()
or during error handling
* pci_ep_doe_process_request() defines response_pl and response_pl_sz
as NULL and 0 respectively, whose pointer is passed to the protocol
handler, hence removed the arguments void **response, size_t *response_sz
to this function.
- Task structure refactoring:
* Response buffer: void **response_pl to void *response_pl
* Response size: size_t *response_pl_sz to size_t response_pl_sz
* Changed the completion callback to type pci_ep_doe_complete_t
* Removed void *private and int task_status
- Updated documentation comments of the functions according to the changes
v5: https://lore.kernel.org/all/20260610100256.1889111-3-a-garg7@ti.com/
v4: https://lore.kernel.org/all/20260522052434.802034-3-a-garg7@ti.com/
v3: https://lore.kernel.org/all/20260427051725.223704-3-a-garg7@ti.com/
v2: https://lore.kernel.org/all/20260401073022.215805-3-a-garg7@ti.com/
v1: https://lore.kernel.org/all/20260213123603.420941-4-a-garg7@ti.com/
drivers/pci/endpoint/Kconfig | 14 +
drivers/pci/endpoint/Makefile | 1 +
drivers/pci/endpoint/pci-ep-doe.c | 591 ++++++++++++++++++++++++++++++
drivers/pci/pci.h | 42 +++
include/linux/pci-doe.h | 5 +
include/linux/pci-epc.h | 3 +
6 files changed, 656 insertions(+)
create mode 100644 drivers/pci/endpoint/pci-ep-doe.c
diff --git a/drivers/pci/endpoint/Kconfig b/drivers/pci/endpoint/Kconfig
index 8dad291be8b8..15ae16aaa58f 100644
--- a/drivers/pci/endpoint/Kconfig
+++ b/drivers/pci/endpoint/Kconfig
@@ -36,6 +36,20 @@ config PCI_ENDPOINT_MSI_DOORBELL
doorbell. The RC can trigger doorbell in EP by writing data to a
dedicated BAR, which the EP maps to the controller's message address.
+config PCI_ENDPOINT_DOE
+ 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.
+
source "drivers/pci/endpoint/functions/Kconfig"
endmenu
diff --git a/drivers/pci/endpoint/Makefile b/drivers/pci/endpoint/Makefile
index b4869d52053a..1fa176b6792b 100644
--- a/drivers/pci/endpoint/Makefile
+++ b/drivers/pci/endpoint/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS) += pci-ep-cfs.o
obj-$(CONFIG_PCI_ENDPOINT) += pci-epc-core.o pci-epf-core.o\
pci-epc-mem.o functions/
obj-$(CONFIG_PCI_ENDPOINT_MSI_DOORBELL) += pci-ep-msi.o
+obj-$(CONFIG_PCI_ENDPOINT_DOE) += pci-ep-doe.o
diff --git a/drivers/pci/endpoint/pci-ep-doe.c b/drivers/pci/endpoint/pci-ep-doe.c
new file mode 100644
index 000000000000..b2832253eaca
--- /dev/null
+++ b/drivers/pci/endpoint/pci-ep-doe.c
@@ -0,0 +1,591 @@
+// 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/device.h>
+#include <linux/pci.h>
+#include <linux/pci-epc.h>
+#include <linux/pci-doe.h>
+#include <linux/refcount.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/xarray.h>
+
+#include "../pci.h"
+
+/* Forward declaration of discovery protocol handler */
+static int pci_ep_doe_handle_discovery(const void *request, size_t request_sz,
+ void **response, size_t *response_sz);
+
+/**
+ * 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;
+};
+
+/**
+ * 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
+ * @refs: Refcount to manage mailbox lifetime and ensure safe cleanup
+ * @lock: Spinlock protecting work_queue access and DEAD flag checks
+ */
+struct pci_ep_doe_mb {
+ struct pci_epc *epc;
+ u8 func_no;
+ u16 cap_offset;
+ struct workqueue_struct *work_queue;
+ unsigned long flags;
+ refcount_t refs;
+ spinlock_t lock; /* Serialize work queue access */
+};
+
+/**
+ * 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: Response buffer
+ * @response_pl_sz: Size of response buffer in bytes
+ * @complete: Completion callback
+ * @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;
+ pci_ep_doe_complete_t complete;
+
+ /* Initialized by pci_ep_doe_submit_task() */
+ struct work_struct work;
+ struct pci_ep_doe_mb *doe_mb;
+};
+
+/*
+ * Global registry of protocol handlers.
+ * When a new DOE protocol, library is added, add an entry to this array.
+ */
+static const struct pci_doe_protocol pci_doe_protocols[] = {
+ {
+ .vid = PCI_VENDOR_ID_PCI_SIG,
+ .type = PCI_DOE_FEATURE_DISCOVERY,
+ .handler = pci_ep_doe_handle_discovery,
+ },
+};
+
+/*
+ * Combine 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))
+#define PCI_DOE_PROTOCOL_COUNT ARRAY_SIZE(pci_doe_protocols)
+
+/**
+ * pci_ep_doe_init() - Initialize the DOE framework for a controller in EP mode
+ * @epc: PCI endpoint controller
+ *
+ * Initialize the xarray that will hold the mailboxes.
+ */
+void pci_ep_doe_init(struct pci_epc *epc)
+{
+ xa_init(&epc->doe_mbs);
+}
+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.
+ *
+ * EPC core driver calls this for each DOE capability discovered in the config
+ * space of each endpoint function if DOE support is available for the EPC.
+ *
+ * Return: 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;
+
+ doe_mb = kzalloc_obj(*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;
+ }
+
+ refcount_set(&doe_mb->refs, 1);
+ spin_lock_init(&doe_mb->lock);
+
+ 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_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)
+{
+ /* 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.
+ *
+ * Return: 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)
+{
+ struct pci_ep_doe_mb *doe_mb;
+ unsigned long key, flags;
+
+ key = PCI_DOE_MB_KEY(func_no, cap_offset);
+
+ xa_lock_irqsave(&epc->doe_mbs, flags);
+
+ doe_mb = xa_load(&epc->doe_mbs, key);
+ if (doe_mb && !refcount_inc_not_zero(&doe_mb->refs))
+ doe_mb = NULL;
+
+ xa_unlock_irqrestore(&epc->doe_mbs, flags);
+
+ return doe_mb;
+}
+
+/**
+ * pci_ep_doe_put_mailbox() - Release a reference to a DOE mailbox
+ * @doe_mb: The mailbox structure to release
+ *
+ * Drop the reference count. Free the memory allocated to the mailbox structure
+ * if this was the last active reference.
+ */
+static void pci_ep_doe_put_mailbox(struct pci_ep_doe_mb *doe_mb)
+{
+ if (refcount_dec_and_test(&doe_mb->refs))
+ kfree(doe_mb);
+}
+
+/**
+ * pci_ep_doe_find_protocol() - Find protocol handler in static array
+ * @vendor: Vendor ID
+ * @type: Protocol Type
+ *
+ * Look up a protocol handler in the static protocol array by matching
+ * Vendor ID and Protocol Type.
+ *
+ * Return: Handler function pointer or NULL if not found
+ */
+static pci_doe_protocol_handler_t pci_ep_doe_find_protocol(u16 vendor, u8 type)
+{
+ int i;
+
+ /* Search static protocol array */
+ for (i = 0; i < PCI_DOE_PROTOCOL_COUNT; i++) {
+ if (pci_doe_protocols[i].vid == vendor &&
+ pci_doe_protocols[i].type == type)
+ return pci_doe_protocols[i].handler;
+ }
+
+ return NULL;
+}
+
+/**
+ * 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
+ *
+ * Return: 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;
+ 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);
+
+ if (requested_index >= PCI_DOE_PROTOCOL_COUNT) {
+ /* No more protocols to report */
+ vendor = 0;
+ type = 0;
+ } else {
+ /* Get protocol from array at requested_index */
+ protocol = pci_doe_protocols[requested_index];
+ vendor = protocol.vid;
+ type = protocol.type;
+ }
+
+ /* Calculate next index */
+ next_index = (requested_index + 1 < PCI_DOE_PROTOCOL_COUNT) ?
+ requested_index + 1 : 0;
+
+ response_pl = kzalloc_obj(*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)
+{
+ struct pci_ep_doe_mb *doe_mb = task->doe_mb;
+
+ task->complete(doe_mb->epc, doe_mb->func_no, doe_mb->cap_offset,
+ status, task->feat.vid, task->feat.type,
+ task->response_pl, task->response_pl_sz);
+
+ /* Clear the CANCEL flag for next DOE request */
+ clear_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
+
+ kfree(task->request_pl);
+ kfree(task);
+
+ /* Release the mailbox reference acquired during process_request */
+ pci_ep_doe_put_mailbox(doe_mb);
+}
+
+/**
+ * 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;
+ }
+
+ handler = pci_ep_doe_find_protocol(task->feat.vid, task->feat.type);
+ if (!handler) {
+ dev_warn_ratelimited(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;
+ }
+
+ 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 asynchronous processing.
+ *
+ * Return: 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)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&doe_mb->lock, flags);
+ if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ task->doe_mb = doe_mb;
+ INIT_WORK(&task->work, doe_ep_task_work);
+ queue_work(doe_mb->work_queue, &task->work);
+
+out:
+ spin_unlock_irqrestore(&doe_mb->lock, flags);
+ return ret;
+}
+
+/**
+ * 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)
+ * @complete: Callback to invoke upon completion
+ *
+ * Asynchronously process a DOE request received on the endpoint. The request
+ * payload should not include the DOE header (vendor/type/length). Ownership
+ * of the request buffer is transferred to DOE EP core, which frees the buffer
+ * either on error or by signal_task_complete() after the completion callback
+ * fires. The protocol handler will allocate the response buffer, which the
+ * caller (controller driver) must free after use.
+ *
+ * This function returns immediately after queuing the request. The completion
+ * callback will be invoked asynchronously from workqueue context once the
+ * request is processed. The callback receives the function number and
+ * capability offset to identify the mailbox, along with a status code
+ * (0 on success, -errno on failure), and other required arguments.
+ *
+ * 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 also
+ * 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.
+ *
+ * Return: 0 if the request was successfully queued, -errno on failure
+ */
+int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
+ u16 vendor, u8 type, void *request,
+ size_t request_sz,
+ pci_ep_doe_complete_t complete)
+{
+ struct pci_ep_doe_mb *doe_mb;
+ struct pci_ep_doe_task *task;
+ int ret;
+
+ doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
+ if (!doe_mb) {
+ kfree(request);
+ return -ENODEV;
+ }
+
+ task = kzalloc_obj(*task, GFP_ATOMIC);
+ if (!task) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ task->feat.vid = vendor;
+ task->feat.type = type;
+ task->request_pl = request;
+ task->request_pl_sz = request_sz;
+ task->response_pl = NULL;
+ task->response_pl_sz = 0;
+ task->complete = complete;
+
+ ret = pci_ep_doe_submit_task(doe_mb, task);
+ if (ret)
+ goto err_task;
+
+ return 0;
+
+err_task:
+ kfree(task);
+err_free:
+ kfree(request);
+ pci_ep_doe_put_mailbox(doe_mb);
+ return ret;
+}
+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 the queued or in-flight DOE operation for the specified mailbox.
+ * This function is called by the EP controller driver when the RC sets the
+ * DOE Abort bit in the DOE Control Register, and the DOE Busy bit is set in
+ * the DOE Status Register.
+ *
+ * Set the CANCEL flag on the mailbox to prevent queued requests
+ * from starting, and return immediately. The CANCEL flag gets cleared in
+ * signal_task_complete(), allowing the mailbox to accept new requests.
+ *
+ * Return: 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;
+
+ 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);
+
+ dev_dbg_ratelimited(epc->dev.parent,
+ "DOE mailbox abort initialized: PF%d offset 0x%x\n",
+ func_no, cap_offset);
+
+ pci_ep_doe_put_mailbox(doe_mb);
+ 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)
+{
+ struct workqueue_struct *wq;
+ unsigned long flags;
+
+ pci_ep_doe_cancel_tasks(doe_mb);
+
+ spin_lock_irqsave(&doe_mb->lock, flags);
+ wq = doe_mb->work_queue;
+ doe_mb->work_queue = NULL;
+ spin_unlock_irqrestore(&doe_mb->lock, flags);
+
+ if (wq)
+ destroy_workqueue(wq);
+
+ pci_ep_doe_put_mailbox(doe_mb);
+}
+
+/**
+ * pci_ep_doe_destroy() - Destroy all DOE mailboxes
+ * @epc: PCI endpoint controller
+ *
+ * Destroy all DOE mailboxes and free associated resources.
+ *
+ * The EPC core driver calls this to free all DOE resources,
+ * if DOE support is available for the EPC.
+ */
+void pci_ep_doe_destroy(struct pci_epc *epc)
+{
+ struct pci_ep_doe_mb *doe_mb;
+ unsigned long index;
+
+ xa_for_each(&epc->doe_mbs, index, doe_mb) {
+ xa_erase(&epc->doe_mbs, index);
+ pci_ep_doe_destroy_mb(doe_mb);
+ }
+
+ xa_destroy(&epc->doe_mbs);
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_destroy);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 5844deee2b5f..6d3b4b779d15 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -692,6 +692,13 @@ struct pci_doe_feature {
u8 type;
};
+struct pci_epc;
+
+typedef void (*pci_ep_doe_complete_t)(struct pci_epc *epc, u8 func_no,
+ u16 cap_offset, int status,
+ u16 vendor, u8 type,
+ void *response_pl, size_t response_pl_sz);
+
#ifdef CONFIG_PCI_DOE
void pci_doe_init(struct pci_dev *pdev);
void pci_doe_destroy(struct pci_dev *pdev);
@@ -702,6 +709,41 @@ static inline void pci_doe_destroy(struct pci_dev *pdev) { }
static inline void pci_doe_disconnected(struct pci_dev *pdev) { }
#endif
+#ifdef CONFIG_PCI_ENDPOINT_DOE
+void 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_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
+ u16 vendor, u8 type, void *request,
+ size_t request_sz,
+ pci_ep_doe_complete_t complete);
+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 void pci_ep_doe_init(struct pci_epc *epc) { }
+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_process_request(struct pci_epc *epc,
+ u8 func_no, u16 cap_offset,
+ u16 vendor, u8 type,
+ void *request, size_t request_sz,
+ pci_ep_doe_complete_t complete)
+{
+ 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
+
#ifdef CONFIG_PCI_NPEM
void pci_npem_create(struct pci_dev *dev);
void pci_npem_remove(struct pci_dev *dev);
diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
index abb9b7ae8029..c46e42f3ce78 100644
--- a/include/linux/pci-doe.h
+++ b/include/linux/pci-doe.h
@@ -22,6 +22,11 @@ struct pci_doe_mb;
/* Max data object length is 2^18 dwords */
#define PCI_DOE_MAX_LENGTH (1 << 18)
+typedef int (*pci_doe_protocol_handler_t)(const void *request,
+ size_t request_sz,
+ void **response,
+ size_t *response_sz);
+
struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
u8 type);
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 1eca1264815b..dd26294c8175 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -182,6 +182,9 @@ struct pci_epc {
unsigned long function_num_map;
int domain_nr;
bool init_complete;
+#ifdef CONFIG_PCI_ENDPOINT_DOE
+ struct xarray doe_mbs;
+#endif
};
/**
--
2.34.1
^ permalink raw reply related
* [PATCH v6 4/4] Documentation: PCI: Add documentation for DOE endpoint support
From: Aksh Garg @ 2026-06-23 9:07 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, rdunlap, Frank.Li, s-vadapalli,
danishanwar, srk, a-garg7
In-Reply-To: <20260623090737.711656-1-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>
Tested-by: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
Changes from v5 to v6:
- Addressed the review comments provided by Bjorn Helgaas at v5
Changes from v4 to v5:
- Updated the DOE Abort handling setion.
Changes from v3 to v4:
- Updated the maximum size of the DOE object from 256KB to 1MB,
as per PCIe spec.
- Updated the DOE setup and cleanup sections.
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes since v1:
- Squashed the patches [1] and [2], and moved the documentation file
to Documentation/PCI/endpoint/pci-endpoint-doe.rst to match the existing
naming scheme, as suggested by Niklas Cassel
- Updated the documentation as per the design and implementaion changes
made to previous patches in this series:
* Updated for static protocol array instead of dynamic registration
* Documented asynchronous callback model
* Updated request/response flow with new callback signature
* Updated memory ownership: DOE core frees request, driver frees response
* Updated initialization and cleanup sections for new APIs
v5: https://lore.kernel.org/all/20260610100256.1889111-5-a-garg7@ti.com/
v4: https://lore.kernel.org/all/20260522052434.802034-5-a-garg7@ti.com/
v3: https://lore.kernel.org/all/20260427051725.223704-5-a-garg7@ti.com/
v2: https://lore.kernel.org/all/20260401073022.215805-5-a-garg7@ti.com/
v1: [1] https://lore.kernel.org/all/20260213123603.420941-2-a-garg7@ti.com/
[2] https://lore.kernel.org/all/20260213123603.420941-5-a-garg7@ti.com/
Documentation/PCI/endpoint/index.rst | 1 +
.../PCI/endpoint/pci-endpoint-doe.rst | 352 ++++++++++++++++++
2 files changed, 353 insertions(+)
create mode 100644 Documentation/PCI/endpoint/pci-endpoint-doe.rst
diff --git a/Documentation/PCI/endpoint/index.rst b/Documentation/PCI/endpoint/index.rst
index dd1f62e731c9..7c03d5abd2ef 100644
--- a/Documentation/PCI/endpoint/index.rst
+++ b/Documentation/PCI/endpoint/index.rst
@@ -9,6 +9,7 @@ PCI Endpoint Framework
pci-endpoint
pci-endpoint-cfs
+ pci-endpoint-doe
pci-test-function
pci-test-howto
pci-ntb-function
diff --git a/Documentation/PCI/endpoint/pci-endpoint-doe.rst b/Documentation/PCI/endpoint/pci-endpoint-doe.rst
new file mode 100644
index 000000000000..49bb1d8236f0
--- /dev/null
+++ b/Documentation/PCI/endpoint/pci-endpoint-doe.rst
@@ -0,0 +1,352 @@
+.. 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
+defined in PCI Express Base Specification Revision 7.0, Section 6.30.
+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. These
+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 Data Mailbox Register (one DWORD at
+ a time) of the endpoint function's DOE Capability and sets the DOE Go bit
+ in the DOE Control Register to indicate that a request is ready for
+ processing.
+2. **Endpoint processes**: The endpoint function reads the request from DOE
+ Write Data Mailbox Register, sets the DOE 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 Data Mailbox Register (one DWORD at a time), and
+ sets the Data Object 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 DOE
+ Error bit in the DOE Status Register instead of the Data Object Ready bit.
+4. **Host reads response**: The root complex retrieves the response data from
+ the DOE Read Data Mailbox Register once the Data Object Ready bit is set
+ in the DOE Status Register, and then writes any value to this register to
+ indicate a successful read. If the DOE 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 1MB
+(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 | | +--------------+ | |
+ | | |--------------------------+ | | | |
+ | +--------^---------+ | | | | |
+ | | | | | | |
+ | | | | | | |
+ | | pci_ep_doe_process_request() | | | | |
+ | | | | | | |
+ | +--------v---------+ Free | | | | |
+ | | |----------------------------+ | DDR | |
+ | | DOE EP Core |<----+ | | | |
+ | | (pci-ep-doe.c) | | Discovery | | | |
+ | | |-----+ Protocol Handler | | | |
+ | +--------^---------+ | | | |
+ | | | | | |
+ | | protocol_handler() | | | |
+ | | | | | |
+ | +--------v---------+ | | | |
+ | | | | | +--------------+ | |
+ | | Protocol Handler | +---->| Response | | |
+ | | Module |------------------------------->| Buffer | | |
+ | | (CMA/SPDM/Other) | Allocate | +--------------+ | |
+ | | | | | |
+ | +------------------+ | | |
+ | +------------------+ |
+ +------------------------------------------------------------------------+
+
+Initialization and Cleanup
+--------------------------
+
+**Framework Initialization and DOE Setup**
+
+The EPC core automatically initializes and sets up DOE mailboxes through the
+``pci_epc_init_capabilities()`` internal function, which is invoked during
+``pci_epc_init_notify()`` when the controller driver calls this API.
+Controller drivers do not need to explicitly handle DOE initialization,
+rather the EPC core manages this transparently.
+
+DOE initialization only occurs when the EPC driver reports DOE Capability
+through the ``doe_capable`` flag in its ``pci_epc_features``.
+
+This internal function performs the following steps:
+
+1. Calls ``pci_ep_doe_init(epc)`` to initialize 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``.
+2. Calls ``pci_epc_doe_setup(epc)`` to discover all DOE capabilities in the
+ endpoint function's configuration space for each function. For each
+ discovered DOE Capability, calls ``pci_ep_doe_add_mailbox(epc, func_no,
+ cap_offset)`` to register the mailbox.
+
+Each DOE mailbox structure created by ``pci_ep_doe_add_mailbox()`` gets an
+ordered workqueue allocated 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**
+
+The EPC core automatically cleans up DOE mailboxes through the
+``pci_epc_deinit_capabilities()`` internal function, which is invoked during
+``pci_epc_deinit_notify()`` when the controller driver calls this API.
+Controller drivers do not need to explicitly handle DOE cleanup, rather
+the EPC core manages this transparently.
+
+DOE cleanup only occurs when the EPC device reported DOE Capability
+through the ``doe_capable`` flag in its ``pci_epc_features``.
+
+This internal function calls ``pci_ep_doe_destroy(epc)``, which destroys all
+registered mailboxes, cancels any pending tasks, flushes and destroys the
+workqueues, and frees all memory allocated to the mailboxes.
+
+Protocol Handler Support
+------------------------
+
+Protocol implementations (such as CMA, SPDM, or vendor-specific protocols)
+are supported through a static array of protocol handlers.
+
+When a new DOE protocol library is introduced, its handler function
+is added to the static ``pci_doe_protocols`` array in
+``drivers/pci/endpoint/pci-ep-doe.c``. The discovery protocol
+(VID = 0x0001 (PCI-SIG Vendor ID), Type = 0x00 (discovery protocol)) is
+included in this static array and handled internally by the DOE EP core.
+
+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 Data Mailbox Register in the endpoint function's DOE Capability
+and sets the DOE 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 Data 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 defines a
+completion callback function with signature ``void (*complete)(struct pci_epc
+*epc, u8 func_no, u16 cap_offset, int status, u16 vendor, u8 type, void
+*response_pl, size_t response_pl_sz)`` to be invoked when the request
+processing completes. The driver then calls ``pci_ep_doe_process_request(epc,
+func_no, cap_offset, vendor, type, request, request_sz, complete)`` to
+hand off the request to the DOE EP core. This function returns immediately
+after queuing the work (without blocking), and the driver sets the DOE Busy
+bit in the DOE Status Register.
+
+**Step 3: DOE EP Core Processing**
+
+The DOE EP core 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. It looks
+for the protocol handler based on the Vendor ID and Type from the request
+header, and executes the handler function.
+
+**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**
+
+After the protocol handler completes, the DOE EP core frees the request
+buffer, and invokes the completion callback provided by the controller
+driver asynchronously. The callback receives the struct pci_epc, function
+number, capability offset (to identify the mailbox), status code indicating
+the result of request processing, Vendor ID and Type of the data object,
+the response buffer, and its size.
+
+**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 Data Mailbox Register, and sets the Data Object Ready bit in the DOE
+Status Register. The root complex then reads the response from the DOE Read
+Data Mailbox Register. Finally, the controller driver frees the response
+buffer (which the handler allocated).
+
+Asynchronous Request Processing
+-------------------------------
+
+The DOE-EP framework implements asynchronous request processing because an
+endpoint function can have multiple instances of DOE mailboxes, and requests
+may be interleaved across these mailboxes. Request processing of one mailbox
+should not result in blocking request processing of other mailboxes. Hence,
+requests on each mailbox need to be handled in parallel for optimization.
+
+For the EP controller driver to handle requests on multiple mailboxes in
+parallel, ``pci_ep_doe_process_request()`` must be asynchronous. The function
+returns immediately after submitting the request to the mailbox's workqueue,
+without waiting for the request to complete. A completion callback provided
+by the controller driver is invoked asynchronously when request processing
+finishes. This asynchronous design enables concurrent processing of requests
+across different mailboxes.
+
+Abort Handling
+--------------
+
+The DOE specification allows the root complex to abort ongoing DOE operations
+by setting the DOE Abort bit in the DOE Control Register.
+
+**Trigger**
+
+When the root complex sets the DOE 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 before request transfer**: If the DOE Abort bit is set before the
+ root complex transfers the request to the mailbox registers, the controller
+ driver should not call ``pci_ep_doe_abort()`` API.
+
+- **ABORT during request transfer**: If the DOE Abort bit is set while the
+ root complex is still transferring the request to the mailbox registers,
+ the controller driver should discard the request, and should not call
+ ``pci_ep_doe_abort()`` and ``pci_ep_doe_process_request()`` APIs in the
+ respective IRQ handlers.
+
+- **ABORT after request submission**: If the DOE 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 sets the CANCEL flag on the mailbox to prevent queued
+requests from starting. Instead of waiting for the workqueue to flush,
+it returns immediately.
+
+The CANCEL flag gets cleared after invoking the completion callback,
+allowing 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 DOE Abort bit is triggered, the controller
+ driver must clear the DOE 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 either through the
+``pci_ep_doe_process_request()`` return value, or the status code passed
+to the completion callback.
+
+**Error Response**
+
+When the controller driver receives an error code, it sets the DOE Error bit
+in the DOE Status Register instead of writing a response to the DOE Read Data
+Mailbox Register, and frees the buffers.
+
+API Reference
+=============
+
+.. kernel-doc:: drivers/pci/endpoint/pci-ep-doe.c
+ :export:
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net-next] net: sparx5: change ndo_set_rx_mode_async return type to int
From: Robert Marko @ 2026-06-23 9:14 UTC (permalink / raw)
To: Jakub Kicinski
Cc: andrew+netdev, davem, edumazet, pabeni, Steen.Hegelund,
daniel.machon, UNGLinuxDriver, sdf.kernel, netdev,
linux-arm-kernel, linux-kernel, luka.perkov
In-Reply-To: <20260613112940.05bba5ff@kernel.org>
On Sat, Jun 13, 2026 at 8:29 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Thu, 11 Jun 2026 12:11:13 +0200 Robert Marko wrote:
> > Commit ("net: add retry mechanism to ndo_set_rx_mode_async") changed the
> > ndo_set_rx_mode_async return type to int, however it did not update the
> > SparX-5 driver.
> >
> > So, simply update the sparx5_set_rx_mode return type to int, propagate
> > return from __hw_addr_sync_dev or simply return 0.
> >
> > Fixes: d90b85c23b3d ("net: add retry mechanism to ndo_set_rx_mode_async")
>
> This commit does not exist, as I said in:
> https://lore.kernel.org/all/20260507091012.7eeb17f5@kernel.org/
> the first two patches of that series were _not_ applied.
Ahh, sorry for this then, I had those patches applied locally.
Regards,
Robert
> --
> pw-bot: reject
--
Robert Marko
Staff Embedded Linux Engineer
Sartura d.d.
Lendavska ulica 16a
10000 Zagreb, Croatia
Email: robert.marko@sartura.hr
Web: www.sartura.hr
^ permalink raw reply
* [PATCH] usb: mtu3: unmap request DMA on queue failure
From: Haoxiang Li @ 2026-06-23 9:33 UTC (permalink / raw)
To: chunfeng.yun, gregkh
Cc: linux-usb, linux-arm-kernel, linux-mediatek, linux-kernel,
Haoxiang Li, stable
mtu3_gadget_queue() maps the request before checking whether
the QMU GPD ring can accept another transfer. the request is
returned with -EAGAIN before it is linked on the endpoint
request list if mtu3_prepare_transfer() fails.
Normal completion and dequeue paths unmap requests from
mtu3_req_complete(), but this error path never reaches that
helper, so the DMA mapping is left active. Unmap the request
before returning from the failed queue path.
Fixes: df2069acb005 ("usb: Add MediaTek USB3 DRD driver")
Cc: stable@vger.kernel.org
Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
---
drivers/usb/mtu3/mtu3_gadget.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index da29f467943f..f224f2ee379a 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -305,6 +305,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep,
if (mtu3_prepare_transfer(mep)) {
ret = -EAGAIN;
+ usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
goto error;
}
--
2.25.1
^ permalink raw reply related
* Re: [PATCH] iommu/qcom: Remove sysfs device on probe failure path
From: Konrad Dybcio @ 2026-06-23 9:40 UTC (permalink / raw)
To: Haoxiang Li, robin.clark, will, robin.murphy, joro, jroedel
Cc: iommu, linux-arm-msm, linux-arm-kernel, linux-kernel
In-Reply-To: <20260623071245.1985938-1-haoxiang_li2024@163.com>
On 6/23/26 9:12 AM, Haoxiang Li wrote:
> In qcom_iommu_device_probe(), if iommu_device_register()
> fails, the sysfs device created by iommu_device_sysfs_add()
> is not released. Add a goto label to do the cleanup.
>
> Fixes: 0ae349a0f33f ("iommu/qcom: Add qcom_iommu")
> Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
> ---
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Konrad
^ permalink raw reply
* [PATCH] clk: mediatek: mt6735: Unregister PLLs on probe failure
From: Myeonghun Pak @ 2026-06-23 9:41 UTC (permalink / raw)
To: Yassine Oudjana, Michael Turquette, Stephen Boyd
Cc: Matthias Brugger, AngeloGioacchino Del Regno, linux-clk,
linux-mediatek, linux-kernel, linux-arm-kernel, Myeonghun Pak,
Ijae Kim
mtk_clk_register_plls() registers the apmixedsys PLL clocks manually, while
clk_mt6735_apmixed_remove() unregisters them on driver removal.
If devm_of_clk_add_hw_provider() fails after the PLL registration succeeds,
probe returns the error directly and the remove callback is not run. This
leaves the registered PLL clocks behind on the probe failure path.
Add an unregister_plls error path so provider registration failures unwind the
PLLs before returning the error.
Fixes: 43c04ed79189 ("clk: mediatek: Add drivers for MediaTek MT6735 main clock and reset drivers")
Co-developed-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
---
drivers/clk/mediatek/clk-mt6735-apmixedsys.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
index 9e30c089a2..04cf9665ec 100644
--- a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
+++ b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
@@ -102,10 +102,17 @@ static int clk_mt6735_apmixed_probe(struct platform_device *pdev)
ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
clk_data);
- if (ret)
+ if (ret) {
dev_err(&pdev->dev,
"Failed to register clock provider: %d\n", ret);
+ goto unregister_plls;
+ }
+
+ return 0;
+unregister_plls:
+ mtk_clk_unregister_plls(apmixedsys_plls, ARRAY_SIZE(apmixedsys_plls),
+ clk_data);
return ret;
}
--
2.47.1
^ permalink raw reply related
* Re: [PATCH] iio: stm32-dfsdm: Treat flags as booleans
From: Olivier MOYSAN @ 2026-06-23 9:43 UTC (permalink / raw)
To: Jonathan Cameron, Andy Shevchenko
Cc: Rob Herring (Arm), David Lechner, Nuno Sá, Andy Shevchenko,
Maxime Coquelin, Alexandre Torgue, linux-iio, linux-stm32,
linux-arm-kernel, linux-kernel
In-Reply-To: <20260621151026.69714694@jic23-huawei>
Hi Andy, Jonathan,
Sorry for the late answer.
On 6/21/26 16:10, Jonathan Cameron wrote:
> On Sat, 13 Jun 2026 16:39:16 +0300
> Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
>
>> On Fri, Jun 12, 2026 at 04:51:50PM -0500, Rob Herring (Arm) wrote:
>>> The "st,adc-alt-channel" and "st,filter0-sync" properties are
>>> documented as boolean flags. The legacy parser read them as integer
>>> cells, unlike the child-node parser which already checks only for
>>> presence.
>>>
>>> Use presence and boolean helpers so both parsers follow the binding and
>>> the property type checker no longer reports the flags.
>>
>> For the patch
>> Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
>>
>> However one interesting remark below.
>>
>> ...
>>
>>> - ret = of_property_read_u32_index(indio_dev->dev.of_node,
>>> - "st,adc-alt-channel", chan_idx,
>>> - &df_ch->alt_si);
>>
>>> + df_ch->alt_si = of_property_present(indio_dev->dev.of_node,
>>
>> I believe it still has another (serious?) issue. We usually don't use indio_dev
>> for device properties. It's not a device that is described in DT.
>> It seems the only driver in IIO that does that. Note, I haven't conducted any
>> deeper research, it might be (however I'm quite in doubt) that this is correct
>> use and one device registers a few indio_dev:s.
>
> It is curious. The registration sequence in this driver is complex, but I'm not
> seeing anything that sets the fwnode for the struct iio_dev->dev before calling
> the init() callbacks that end up in this code. It is set later by iio_device_register()
> (iirc that has something to do with consumers turning up later).
>
> St folk could you take a look at this and see what we are missing
> if it does currently work?
>
> For now I'll apply this patch but might need to drop it if a fix clashes
> with it.
>
> Thanks,
>
> Jonathan
>
>
I confirm that the current legacy path is functional
(With the st,adc-alt-channel property fix applied)
It currently works because the driver initializes np from dev->of_node
in probe, and that value is then used in init callbacks.
I agree that this approach is not robust, as it depends on
initialization sequencing and on using an IIO object that is not the DT
owner object. I will prepare a patch to use the DT device directly as
the single source for DT properties.
I also suggest keeping a fallback path for st,adc-alt-channel so we do
not break legacy DTs that have not yet migrated to the new binding.
I prepare this also.
BRs
Olivier
>
>>
>>> + "st,adc-alt-channel");
>>
>
>
^ permalink raw reply
* Re: [PATCH v3 0/4] ROCK 4D audio enablement
From: Alexandre Belloni @ 2026-06-23 9:47 UTC (permalink / raw)
To: Nicolas Frattaroli
Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Heiko Stuebner, kernel, linux-input, devicetree, linux-kernel,
linux-arm-kernel, linux-rockchip, Krzysztof Kozlowski,
Cristian Ciocaltea
In-Reply-To: <wE1x9P2vQlC8kihOSm9uOA@collabora.com>
Hello Nicolas,
I guess Dmitry is the one that would take patches 1 to 3. You should
probably resend once the merge window has closed.
On 11/05/2026 18:21:40+0200, Nicolas Frattaroli wrote:
> Hi Alexandre, and other maintainers,
>
> On Wednesday, 8 April 2026 19:49:38 Central European Summer Time Nicolas Frattaroli wrote:
> > The ROCK 4D uses an ADC input to distinguish between a headphone (i.e.,
> > no mic) and a headset (i.e., with mic). After some searching, it appears
> > that the closest we can get to modelling this is by sending a particular
> > switch input event.
> >
> > So this series modifies the adc-keys bindings, extends the adc-keys
> > driver to allow sending other input types as well, and then adds the
> > analog audio nodes to ROCK 4D's device tree.
> >
> > It should be noted that analog capture from the TRRS jack currently
> > results in completely digitally silent audio for me, i.e. no data other
> > than 0xFF. There's a few reasons why this could happen, chief among them
> > that my SAI driver is broken or that the ES8328 codec driver is once
> > again broken. The DAPM routes when graphed out look fine though. So the
> > DTS part is correct, and I can fix the broken capture in a separate
> > follow-up patch that doesn't have to include DT people.
> >
> > Another possibility is that my phone headset, despite being 4 rings and
> > having a little pin hole at the back of the volume doodad, does not
> > actually have a microphone, but in that case I'd still expect some noise
> > in the PCM. Maybe it's just shy.
> >
> > Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
> > ---
> > Changes in v3:
> > - bindings: use unevaluatedProperties instead of explicitly mentioning
> > linux,input-type.
> > - Link to v2: https://lore.kernel.org/r/20251215-rock4d-audio-v2-0-82a61de39b4c@collabora.com
> >
> > Changes in v2:
> > - Drop HDMI audio patch, as it was already merged.
> > - adc-keys: rename "keycode" to "code".
> > - adc-keys: make the keycode (now "code") local a u32 instead of an int
> > - adc-keys: only allow EV_KEY and EV_SW for now. Rename patch
> > accordingly.
> > - adc-keys: Add another patch to rework probe function error logging.
> > - Link to v1: https://lore.kernel.org/r/20250630-rock4d-audio-v1-0-0b3c8e8fda9c@collabora.com
> >
> > ---
> > Nicolas Frattaroli (4):
> > dt-bindings: input: adc-keys: allow all input properties
> > Input: adc-keys - support EV_SW as well, not just EV_KEY.
> > Input: adc-keys - Use dev_err_probe in probe function
> > arm64: dts: rockchip: add analog audio to ROCK 4D
> >
> > .../devicetree/bindings/input/adc-keys.yaml | 17 ++--
> > arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts | 90 ++++++++++++++++++++++
> > drivers/input/keyboard/adc-keys.c | 88 ++++++++++-----------
> > 3 files changed, 147 insertions(+), 48 deletions(-)
> > ---
> > base-commit: 8de395f35e79d9168a78504fed495578ec7bac52
> > change-id: 20250627-rock4d-audio-cfc07f168a08
> >
> > Best regards,
> > --
> > Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
> >
> >
>
> What's the path forward here? All the patches are reviewed, but it
> has been almost a month without them being applied now.
>
> Which tree(s) would this be applied to, and who should I poke?
>
> Thanks :)
>
> Kind regards,
> Nicolas Frattaroli
>
>
>
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH v2] PCI: cadence: skip the link polling when endpoint not connected
From: Aksh Garg @ 2026-06-23 9:51 UTC (permalink / raw)
To: linux-pci, vigneshr, s-vadapalli, lpieralisi, kwilczynski, mani,
robh, bhelgaas, mpillai, unicorn_wang, me, 18255117159
Cc: linux-arm-kernel, linux-kernel, danishanwar, a-garg7
cdns_pcie_host_wait_for_link() polls on link-up for 10 retries with a
delay of 90-100ms each (~1 second). A call to cdns_pcie_host_link_setup()
during the resume operation blocks the resume operation unnecessarily for
~1s even when no endpoint device is connected.
Add link_down_no_hotplug flag to track link state across suspend/resume
cycles (for the platforms that do not support hotplug). If link was
down before suspend in such platforms, skip the expensive polling in
resume since no endpoint was present.
Reviewed-by: Chen Wang <unicorn_wang@outlook.com>
Reviewed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
Changes from v1 to v2:
- Updated the flag name from 'skip_link_polling' to 'link_down_no_hotplug'
v1: https://lore.kernel.org/all/20260605071922.1724499-1-a-garg7@ti.com/
drivers/pci/controller/cadence/pci-j721e.c | 5 +++++
drivers/pci/controller/cadence/pcie-cadence-host-hpa.c | 3 +++
drivers/pci/controller/cadence/pcie-cadence-host.c | 3 +++
drivers/pci/controller/cadence/pcie-cadence.h | 3 +++
4 files changed, 14 insertions(+)
diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c
index bfdfe98d5aba..48db7a6cf754 100644
--- a/drivers/pci/controller/cadence/pci-j721e.c
+++ b/drivers/pci/controller/cadence/pci-j721e.c
@@ -686,6 +686,11 @@ static int j721e_pcie_suspend_noirq(struct device *dev)
struct j721e_pcie *pcie = dev_get_drvdata(dev);
if (pcie->mode == PCI_MODE_RC) {
+ struct cdns_pcie_rc *rc = cdns_pcie_to_rc(pcie->cdns_pcie);
+
+ /* If link is down before suspend, skip polling in resume */
+ rc->link_down_no_hotplug = !j721e_pcie_link_up(pcie->cdns_pcie);
+
gpiod_set_value_cansleep(pcie->reset_gpio, 0);
clk_disable_unprepare(pcie->refclk);
}
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
index 0f540bed58e8..31cf50cff8f8 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
@@ -301,6 +301,9 @@ int cdns_pcie_hpa_host_link_setup(struct cdns_pcie_rc *rc)
return ret;
}
+ if (rc->link_down_no_hotplug)
+ return 0;
+
ret = cdns_pcie_host_wait_for_link(pcie, cdns_pcie_hpa_link_up);
if (ret)
dev_dbg(dev, "PCIe link never came up\n");
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c
index 0bc9e6e90e0e..50abc657a871 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-host.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-host.c
@@ -352,6 +352,9 @@ int cdns_pcie_host_link_setup(struct cdns_pcie_rc *rc)
return ret;
}
+ if (rc->link_down_no_hotplug)
+ return 0;
+
ret = cdns_pcie_host_start_link(rc, cdns_pcie_link_up);
if (ret)
dev_dbg(dev, "PCIe link never came up\n");
diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h
index 574e9cf4d003..1561022c1a8b 100644
--- a/drivers/pci/controller/cadence/pcie-cadence.h
+++ b/drivers/pci/controller/cadence/pcie-cadence.h
@@ -117,6 +117,8 @@ struct cdns_pcie {
* @no_inbound_map: Whether inbound mapping is supported
* @quirk_broken_aspm_l0s: Disable ASPM L0s support as quirk
* @quirk_broken_aspm_l1: Disable ASPM L1 support as quirk
+ * @link_down_no_hotplug: Skip link polling during resume on no-hotplug
+ * platforms when link was down before suspend
*/
struct cdns_pcie_rc {
struct cdns_pcie pcie;
@@ -131,6 +133,7 @@ struct cdns_pcie_rc {
unsigned int no_inbound_map:1;
unsigned int quirk_broken_aspm_l0s:1;
unsigned int quirk_broken_aspm_l1:1;
+ unsigned int link_down_no_hotplug:1;
};
/**
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v10 0/5] Add Qualcomm extended CTI support
From: Yingchao Deng (Consultant) @ 2026-06-23 9:52 UTC (permalink / raw)
To: Leo Yan, Yingchao Deng
Cc: Suzuki K Poulose, Mike Leach, James Clark, Alexander Shishkin,
coresight, linux-arm-kernel, linux-kernel, tingwei.zhang,
Jinlong Mao, jie.gan, Yingchao Deng
In-Reply-To: <20260623090432.GG31870@e132581.arm.com>
On 6/23/2026 5:04 PM, Leo Yan wrote:
> This series looks good to me and I tested:
>
> - Confirmed no any change for standard CTI;
> - Perf command;
> - Build bisection.
>
> Tested-by: Leo Yan<leo.yan@arm.com>
>
> Mike may also want to take a look in case there is any overlap with his
> claim tags init patch [1]. I don't have a strong preference on which
> series goes in first, as the claim tag handling in this series looks
> clean enough to me.
>
> @Yingchao, just a small suggestion: rc1–rc4 is usually the best window
> for getting patches merged. If you receive comments and are able to
> address them quickly, it may be worth respin patches during that
> period to try catching the next merge window. This is no guarantees,
> just thought it might be useful to share as a general tip 🙂
Thanks Leo for testing and the helpful suggestion.
Thanks,
Yingchao
^ permalink raw reply
* Re: [PATCH] iio: stm32-dfsdm: Treat flags as booleans
From: Andy Shevchenko @ 2026-06-23 9:54 UTC (permalink / raw)
To: Olivier MOYSAN
Cc: Jonathan Cameron, Rob Herring (Arm), David Lechner, Nuno Sá,
Andy Shevchenko, Maxime Coquelin, Alexandre Torgue, linux-iio,
linux-stm32, linux-arm-kernel, linux-kernel
In-Reply-To: <46fce99d-9dd5-435b-95cd-86ed4771aa83@foss.st.com>
On Tue, Jun 23, 2026 at 11:43:49AM +0200, Olivier MOYSAN wrote:
> On 6/21/26 16:10, Jonathan Cameron wrote:
> > On Sat, 13 Jun 2026 16:39:16 +0300
> > Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> > > On Fri, Jun 12, 2026 at 04:51:50PM -0500, Rob Herring (Arm) wrote:
...
> > > > - ret = of_property_read_u32_index(indio_dev->dev.of_node,
> > > > - "st,adc-alt-channel", chan_idx,
> > > > - &df_ch->alt_si);
> > >
> > > > + df_ch->alt_si = of_property_present(indio_dev->dev.of_node,
> > >
> > > I believe it still has another (serious?) issue. We usually don't use indio_dev
> > > for device properties. It's not a device that is described in DT.
> > > It seems the only driver in IIO that does that. Note, I haven't conducted any
> > > deeper research, it might be (however I'm quite in doubt) that this is correct
> > > use and one device registers a few indio_dev:s.
> >
> > It is curious. The registration sequence in this driver is complex, but I'm not
> > seeing anything that sets the fwnode for the struct iio_dev->dev before calling
> > the init() callbacks that end up in this code. It is set later by iio_device_register()
> > (iirc that has something to do with consumers turning up later).
> >
> > St folk could you take a look at this and see what we are missing
> > if it does currently work?
> >
> > For now I'll apply this patch but might need to drop it if a fix clashes
> > with it.
>
> I confirm that the current legacy path is functional
> (With the st,adc-alt-channel property fix applied)
Yeah, it's here
https://elixir.bootlin.com/linux/v7.1.1/source/drivers/iio/adc/stm32-dfsdm-adc.c#L1772
and should gone. Basically one wants to replace all these to use device and
fwnode propery APIs and proper device node, without that hack.
> It currently works because the driver initializes np from dev->of_node in
> probe, and that value is then used in init callbacks.
>
> I agree that this approach is not robust, as it depends on initialization
> sequencing and on using an IIO object that is not the DT owner object. I
> will prepare a patch to use the DT device directly as the single source for
> DT properties.
>
> I also suggest keeping a fallback path for st,adc-alt-channel so we do not
> break legacy DTs that have not yet migrated to the new binding.
> I prepare this also.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH] usb: dwc3: imx8mp: make dwc3_imx_glue_ops static and rename to imx8mp
From: Ben Dooks @ 2026-06-23 10:05 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, linux-usb, imx,
linux-arm-kernel, linux-kernel
Cc: Ben Dooks
The dwc3_imx_glue_ops is not used outside this file, and technically this
is the dwc3-imx8mp driver so whilst making this static to avoid the
following warning, rename it dwc3_imx8mp_glue_ops to distinguish it from
the other driver which also has dwc3_imx_glue_ops.
Fixes:
drivers/usb/dwc3/dwc3-imx8mp.c:176:22: warning: symbol 'dwc3_imx_glue_ops' was not declared. Should it be static?
Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
---
drivers/usb/dwc3/dwc3-imx8mp.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c
index 1cf96540b66e..bc61a89b66b1 100644
--- a/drivers/usb/dwc3/dwc3-imx8mp.c
+++ b/drivers/usb/dwc3/dwc3-imx8mp.c
@@ -173,8 +173,8 @@ static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role)
pm_runtime_use_autosuspend(dwc->dev);
}
-struct dwc3_glue_ops dwc3_imx_glue_ops = {
- .pre_set_role = dwc3_imx_pre_set_role,
+statc struct dwc3_glue_ops dwc3_imx8mp_glue_ops = {
+ .pre_set_role = dwc3_imx8mp_pre_set_role,
};
static int dwc3_imx8mp_probe(struct platform_device *pdev)
@@ -266,7 +266,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
goto put_dwc3;
}
- dwc3->glue_ops = &dwc3_imx_glue_ops;
+ dwc3->glue_ops = &dwc3_imx8mp_glue_ops;
if (dwc3->dr_mode == USB_DR_MODE_HOST)
pm_runtime_dont_use_autosuspend(dwc3->dev);
--
2.37.2.352.g3c44437643
^ permalink raw reply related
* Re: [PATCH] usb: dwc3: imx8mp: make dwc3_imx_glue_ops static and rename to imx8mp
From: Ahmad Fatoum @ 2026-06-23 10:07 UTC (permalink / raw)
To: Ben Dooks, Thinh Nguyen, Greg Kroah-Hartman, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, linux-usb,
imx, linux-arm-kernel, linux-kernel
In-Reply-To: <20260623100554.735080-1-ben.dooks@codethink.co.uk>
On 6/23/26 12:05 PM, Ben Dooks wrote:
> The dwc3_imx_glue_ops is not used outside this file, and technically this
> is the dwc3-imx8mp driver so whilst making this static to avoid the
> following warning, rename it dwc3_imx8mp_glue_ops to distinguish it from
> the other driver which also has dwc3_imx_glue_ops.
>
> Fixes:
> drivers/usb/dwc3/dwc3-imx8mp.c:176:22: warning: symbol 'dwc3_imx_glue_ops' was not declared. Should it be static?
>
> Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
> ---
> drivers/usb/dwc3/dwc3-imx8mp.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c
> index 1cf96540b66e..bc61a89b66b1 100644
> --- a/drivers/usb/dwc3/dwc3-imx8mp.c
> +++ b/drivers/usb/dwc3/dwc3-imx8mp.c
> @@ -173,8 +173,8 @@ static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role)
> pm_runtime_use_autosuspend(dwc->dev);
> }
>
> -struct dwc3_glue_ops dwc3_imx_glue_ops = {
> - .pre_set_role = dwc3_imx_pre_set_role,
> +statc struct dwc3_glue_ops dwc3_imx8mp_glue_ops = {
Typo
> + .pre_set_role = dwc3_imx8mp_pre_set_role,
> };
>
> static int dwc3_imx8mp_probe(struct platform_device *pdev)
> @@ -266,7 +266,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
> goto put_dwc3;
> }
>
> - dwc3->glue_ops = &dwc3_imx_glue_ops;
> + dwc3->glue_ops = &dwc3_imx8mp_glue_ops;
>
> if (dwc3->dr_mode == USB_DR_MODE_HOST)
> pm_runtime_dont_use_autosuspend(dwc3->dev);
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* Re: [PATCH 1/4] device property: Introduce fwnode_graph_for_each_endpoint_scoped()
From: Andy Shevchenko @ 2026-06-23 10:09 UTC (permalink / raw)
To: Frank.Li
Cc: Daniel Scally, Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Mauro Carvalho Chehab,
Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner,
Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
driver-core, linux-acpi, linux-kernel, linux-media,
linux-rockchip, linux-arm-kernel, linux-arm-msm, imx, Guoniu Zhou,
Frank Li
In-Reply-To: <20260622-fw_scoped-v1-1-a37d0aac0a68@nxp.com>
On Mon, Jun 22, 2026 at 10:30:11AM -0400, Frank.Li@oss.nxp.com wrote:
> From: Frank Li <Frank.Li@nxp.com>
>
> Similar to recently propose for_each_child_of_node_scoped() this new
> version of the loop macro instantiates a new local struct fwnode_handle *
> that uses the __free(fwnode_handle) auto cleanup handling so that if a
> reference to a node is held on early exit from the loop the reference will
> be released. If the loop runs to completion, the child pointer will be NULL
> and no action will be taken.
>
> The reason this is useful is that it removes the need for
> fwnode_handle_put() on early loop exits. If there is a need to retain the
Just be consistent with 1-space versus 2-spaces gaps in the same text.
> reference, then return_ptr(child) or no_free_ptr(child) may be used to
> safely disable the auto cleanup.
No objections from me.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
See one nit-pick below.
But you will need driver core maintainers to Ack this.
...
> +#define fwnode_graph_for_each_endpoint_scoped(fwnode, child) \
> + for (struct fwnode_handle *child __free(fwnode_handle) = \
> + fwnode_graph_get_next_endpoint(fwnode, NULL); \
You should follow the existing style, the 'f' in fwnode should be under 'u' in
struct.
> + child; child = fwnode_graph_get_next_endpoint(fwnode, child))
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v2 0/5] mm: reduce mmap_lock contention and improve page fault performance
From: Hongru Zhang @ 2026-06-23 10:10 UTC (permalink / raw)
To: david
Cc: akpm, baohua, bhe, chentao, chrisl, jack, kasong, kunwu.chan,
liam, lianux.mm, linux-arm-kernel, linux-kernel, linux-mm,
linux-riscv, linux-s390, linuxppc-dev, liyangouwen1, ljs,
loongarch, mhocko, nphamcs, nzzhao, pfalcato, rppt, shikemeng,
surenb, vbabka, wanglian, willy, youngjun.park, zhanghongru06
In-Reply-To: <d04f745d-eb3e-4d2c-8ea2-5fdcf2cf27b8@kernel.org>
On 6/23/26 10:02, David Hildenbrand wrote:
> I know that especially browser usually use fork servers: a tiny
> (single-threaded) process just to create new child processes. Any information
> regarding the apps above that use fork() on small vs. large processes?
I wrote a second BPF tool (fork_info) that captures nr_threads and
map_count (VMA count) from the calling process at the exact moment
fork() is triggered. Results from 3 representative apps:
App (category) Fork caller Threads VMAs
-----------------------------------------------------------
Taobao (shopping) DaemonThread-6 526 8,987
Amap (navigation) DaemonThread-6 289 7,120
UC Browser (browser) OneNativeThread 350 8,144
These are all heavyweight multi-threaded processes (hundreds of threads,
7,000-9,000 VMAs), not fork servers.
> Above you write "some call fork() from multiple threads". Any further
> information on that?
Xiaohongshu (com.xingin.xhs, social media) is a clear example. In just
tens of seconds of normal usage, fork() was called 22 times from 4
different threads:
PID COMM THREADS VMAS
4206 com.xingin.xhs 85 4,140
4216 Thread-2208 85 4,157
4208 Thread-2208 90 4,211
5200 Thread-3200 337 6,519
5200 Thread-3200 343 6,563
5200 Thread-3200 361 6,769
5200 Thread-3200 453 7,793
5200 Thread-3200 450 7,779
5202 Thread-2219 459 7,846
5202 Thread-2219 462 7,875
5202 Thread-2219 465 7,899
4219 Thread-2219 465 7,903
4219 Thread-2219 468 7,922
5202 Thread-2219 467 7,917
4219 Thread-2219 467 7,921
4219 Thread-2219 468 7,929
5202 Thread-2219 464 7,909
5202 Thread-2219 460 7,889
5202 Thread-2219 459 7,884
4219 Thread-2219 433 7,771
4219 Thread-2219 433 7,771
4219 Thread-2219 434 7,778
The process grew from 85 threads / 4,140 VMAs at first fork to
434 threads / 7,778 VMAs at last fork, showing these are long-lived
heavyweight processes that fork repeatedly throughout their lifecycle.
Tracing tool:
https://gist.github.com/zhr250/ba7725d0ea55594bcafd3cd4806eed98
Hongru
^ permalink raw reply
* [PATCH v2] usb: dwc3: imx8mp: make dwc3_imx_glue_ops static and rename to imx8mp
From: Ben Dooks @ 2026-06-23 10:10 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, linux-usb, imx,
linux-arm-kernel, linux-kernel
Cc: Ben Dooks
The dwc3_imx_glue_ops is not used outside this file, and technically this
is the dwc3-imx8mp driver so whilst making this static to avoid the
following warning, rename it dwc3_imx8mp_glue_ops to distinguish it from
the other driver which also has dwc3_imx_glue_ops.
Fixes:
drivers/usb/dwc3/dwc3-imx8mp.c:176:22: warning: symbol 'dwc3_imx_glue_ops' was not declared. Should it be static?
Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
---
drivers/usb/dwc3/dwc3-imx8mp.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c
index 1cf96540b66e..de8c17bc940d 100644
--- a/drivers/usb/dwc3/dwc3-imx8mp.c
+++ b/drivers/usb/dwc3/dwc3-imx8mp.c
@@ -158,7 +158,7 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
return IRQ_HANDLED;
}
-static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role)
+static void dwc3_imx8mp_pre_set_role(struct dwc3 *dwc, enum usb_role role)
{
if (role == USB_ROLE_HOST)
/*
@@ -173,8 +173,8 @@ static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role)
pm_runtime_use_autosuspend(dwc->dev);
}
-struct dwc3_glue_ops dwc3_imx_glue_ops = {
- .pre_set_role = dwc3_imx_pre_set_role,
+static struct dwc3_glue_ops dwc3_imx8mp_glue_ops = {
+ .pre_set_role = dwc3_imx8mp_pre_set_role,
};
static int dwc3_imx8mp_probe(struct platform_device *pdev)
@@ -266,7 +266,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
goto put_dwc3;
}
- dwc3->glue_ops = &dwc3_imx_glue_ops;
+ dwc3->glue_ops = &dwc3_imx8mp_glue_ops;
if (dwc3->dr_mode == USB_DR_MODE_HOST)
pm_runtime_dont_use_autosuspend(dwc3->dev);
--
2.37.2.352.g3c44437643
^ permalink raw reply related
* Re: [PATCH] usb: dwc3: imx8mp: make dwc3_imx_glue_ops static and rename to imx8mp
From: Ben Dooks @ 2026-06-23 10:11 UTC (permalink / raw)
To: Ahmad Fatoum, Thinh Nguyen, Greg Kroah-Hartman, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, linux-usb,
imx, linux-arm-kernel, linux-kernel
In-Reply-To: <39a5d53d-2eb9-4f72-aca9-95def11edf25@pengutronix.de>
On 23/06/2026 11:07, Ahmad Fatoum wrote:
>
>
> On 6/23/26 12:05 PM, Ben Dooks wrote:
>> The dwc3_imx_glue_ops is not used outside this file, and technically this
>> is the dwc3-imx8mp driver so whilst making this static to avoid the
>> following warning, rename it dwc3_imx8mp_glue_ops to distinguish it from
>> the other driver which also has dwc3_imx_glue_ops.
>>
>> Fixes:
>> drivers/usb/dwc3/dwc3-imx8mp.c:176:22: warning: symbol 'dwc3_imx_glue_ops' was not declared. Should it be static?
>>
>> Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
>> ---
>> drivers/usb/dwc3/dwc3-imx8mp.c | 6 +++---
>> 1 file changed, 3 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c
>> index 1cf96540b66e..bc61a89b66b1 100644
>> --- a/drivers/usb/dwc3/dwc3-imx8mp.c
>> +++ b/drivers/usb/dwc3/dwc3-imx8mp.c
>> @@ -173,8 +173,8 @@ static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role)
>> pm_runtime_use_autosuspend(dwc->dev);
>> }
>>
>> -struct dwc3_glue_ops dwc3_imx_glue_ops = {
>> - .pre_set_role = dwc3_imx_pre_set_role,
>> +statc struct dwc3_glue_ops dwc3_imx8mp_glue_ops = {
>
> Typo
Thanks, forgot to re-run check after this. Sent v2
>
>> + .pre_set_role = dwc3_imx8mp_pre_set_role,
>> };
>>
>> static int dwc3_imx8mp_probe(struct platform_device *pdev)
>> @@ -266,7 +266,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
>> goto put_dwc3;
>> }
>>
>> - dwc3->glue_ops = &dwc3_imx_glue_ops;
>> + dwc3->glue_ops = &dwc3_imx8mp_glue_ops;
>>
>> if (dwc3->dr_mode == USB_DR_MODE_HOST)
>> pm_runtime_dont_use_autosuspend(dwc3->dev);
>
--
Ben Dooks http://www.codethink.co.uk/
Senior Engineer Codethink - Providing Genius
https://www.codethink.co.uk/privacy.html
^ permalink raw reply
* Re: [PATCH 3/4] media: rkisp1: use fwnode_graph_for_each_endpoint_scoped() to simplify code
From: Andy Shevchenko @ 2026-06-23 10:16 UTC (permalink / raw)
To: Frank.Li
Cc: Daniel Scally, Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Mauro Carvalho Chehab,
Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner,
Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
driver-core, linux-acpi, linux-kernel, linux-media,
linux-rockchip, linux-arm-kernel, linux-arm-msm, imx, Guoniu Zhou,
Frank Li
In-Reply-To: <20260622-fw_scoped-v1-3-a37d0aac0a68@nxp.com>
On Mon, Jun 22, 2026 at 10:30:13AM -0400, Frank.Li@oss.nxp.com wrote:
> Use fwnode_graph_for_each_endpoint_scoped() to simplify code.
>
> No functional changes.
...
> - fwnode_graph_for_each_endpoint(fwnode, ep) {
> + fwnode_graph_for_each_endpoint_scoped(fwnode, ep) {
> struct fwnode_handle *port;
> struct v4l2_fwnode_endpoint vep = { };
> struct rkisp1_sensor_async *rk_asd;
> @@ -286,7 +285,6 @@ static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1)
> }
>
> if (ret) {
> - fwnode_handle_put(ep);
> v4l2_async_nf_cleanup(ntf);
> return ret;
> }
In this case you can go further and actually replace all the
ret = -Exxx;
break;
with
v4l2_async_nf_cleanup(ntf);
return -Exx;
in the above loop.
but I assume the original is also fine as it's a common denominator for all of
them (and only one case has something in addition to that).
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH 0/4] media: add and use fwnode_graph_for_each_endpoint_scoped()
From: Andy Shevchenko @ 2026-06-23 10:17 UTC (permalink / raw)
To: Frank.Li
Cc: Daniel Scally, Heikki Krogerus, Sakari Ailus, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Mauro Carvalho Chehab,
Dafna Hirschfeld, Laurent Pinchart, Heiko Stuebner,
Bryan O'Donoghue, Vladimir Zapolskiy, Loic Poulain,
driver-core, linux-acpi, linux-kernel, linux-media,
linux-rockchip, linux-arm-kernel, linux-arm-msm, imx, Guoniu Zhou,
Frank Li
In-Reply-To: <20260622-fw_scoped-v1-0-a37d0aac0a68@nxp.com>
On Mon, Jun 22, 2026 at 10:30:10AM -0400, Frank.Li@oss.nxp.com wrote:
> Add new helper macro fwnode_graph_for_each_endpoint_scoped() and use it
> simplify media code.
>
> Typical example should qualcomm's driver (camss.c), the v4l2_mc.c and
> rkisp1-dev.c only silience improvement.
>
> Anyways, *_for_each_*_scoped() already use widely and make code clean.
LGTM,
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
for patches 2, 3, and 4.
Patch 1 has individual comments.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* chipidea: usbmisc_imx: i.MX93 support
From: Stefan Wahren @ 2026-06-23 10:23 UTC (permalink / raw)
To: Xu Yang, Frank Li
Cc: Alexander Stein, Greg Kroah-Hartman, Linux ARM,
linux-usb@vger.kernel.org
Hi,
during debugging USB OTG on our custom i.MX93 board, we noticed
remarkable differences between the implementation of the
chipidea/usbmisc_imx and the official NXP i.MX93 Reference Manual [1].
Is the USB OTG part including PHY of the i.MX93 officially supported in
Linux Mainline?
According to imx91_93_common.dtsi the USB IP of the i.MX93 should be
identical to i.MX8MM [2]
usbmisc1: usbmisc@4c100200 {
compatible = "fsl,imx8mm-usbmisc", "fsl,imx7d-usbmisc",
"fsl,imx6q-usbmisc";
But looking at the PHY register definition and reset values in the NXP
i.MX93 Reference Manual,
the registers are comparable to the i.MX95 [3] ones.
Could you please clarify which source is correct (Mainline DTS vs
Reference Manual)?
Looking deeper at chipidea/usbmisc_imx shows the usage of the following
register bits
#define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3)
#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0)
#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1)
#define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29)
According to NXP i.MX93 & i.MX95 Reference Manual, these are bits reserved.
Is it correct that the chipidea/usbmisc_imx use these bits on i.MX93?
Best regards
[1] - https://www.nxp.com/docs/en/reference-manual/IMX93RM.pdf
<https://www.nxp.com/docs/en/reference-manual/IMX93RM.pdf>
[2] - https://www.nxp.com/docs/en/reference-manual/IMX8MMRM.pdf
<https://www.nxp.com/docs/en/reference-manual/IMX8MMRM.pdf>
[3] - https://www.nxp.com/docs/en/reference-manual/IMX95RM.pdf
<https://www.nxp.com/docs/en/reference-manual/IMX95RM.pdf>
^ permalink raw reply
* [PATCH] media: imx-jpeg: cancel timeout worker when streaming stops
From: Fan Wu @ 2026-06-23 10:30 UTC (permalink / raw)
To: mirela.rabulea, mchehab
Cc: shawnguo, s.hauer, kernel, festevam, imx, linux-media,
linux-arm-kernel, linux-kernel, stable, Fan Wu
Each per-fd context ctx owns a delayed_work (ctx->task_timer, callback
mxc_jpeg_device_run_timeout) armed via schedule_delayed_work() at the end
of mxc_jpeg_device_run() to recover a stalled encode/decode job. The only
existing cancellation is cancel_delayed_work() in the frame-done IRQ
handler, which de-queues a pending work item but does not wait for a
callback that has already started, and it only runs when a frame completes.
When the fd is closed while a job is in flight (the frame-done IRQ has not
fired yet), nothing syncs the worker before mxc_jpeg_release() frees ctx
with kfree() after v4l2_m2m_ctx_release(). A queued or executing
mxc_jpeg_device_run_timeout() can then recover ctx through
container_of(&ctx->task_timer) and dereference it (ctx->mxc_jpeg,
slot_data, dev_warn) after ctx has been freed.
Cancel the worker from mxc_jpeg_stop_streaming(). The cancel cannot live
in mxc_jpeg_release(): mxc_jpeg_device_run() arms the timer while holding
only hw_lock, not the mxc_jpeg->lock mutex that release holds, so a cancel
in release could still race a concurrent mxc_jpeg_device_run() that
re-arms the timer afterwards. mxc_jpeg_stop_streaming() instead runs inside
v4l2_m2m_ctx_release() -> vb2_queue_release(), i.e. after
v4l2_m2m_cancel_job() has set TRANS_ABORT and waited for any in-flight job
to finish (so __v4l2_m2m_try_queue() will not queue and v4l2_m2m_try_run()
will not run any further job for this context, which prevents
mxc_jpeg_device_run() from re-arming the timer) and before the m2m context
is freed. cancel_delayed_work_sync() removes a pending work item and waits
for a running callback, so the worker can no longer race with the
subsequent kfree(). The cancel is placed before the buffer-release loop so
a concurrently running timeout callback cannot race with it over the same
buffers. If the frame-done IRQ canceled a still-pending timer, this cancel
is a no-op; if the timeout callback has already started, it waits for the
callback to finish. The same mxc_jpeg_stop_streaming() call is also
reached from VIDIOC_STREAMOFF, which drains the worker early, although
STREAMOFF itself does not free ctx -- the use-after-free arises only
when the fd is later closed.
This bug was found by static analysis.
Fixes: cfed9632ca8e ("media: imx-jpeg: Add a timeout mechanism for each frame")
Cc: stable@vger.kernel.org
Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
---
drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index 9e4a813489c0..d85a9d196269 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -1735,6 +1735,8 @@ static void mxc_jpeg_stop_streaming(struct vb2_queue *q)
dev_dbg(ctx->mxc_jpeg->dev, "Stop streaming ctx=%p", ctx);
+ cancel_delayed_work_sync(&ctx->task_timer);
+
/* Release all active buffers */
for (;;) {
if (V4L2_TYPE_IS_OUTPUT(q->type))
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v3] drm/bridge: imx93-mipi-dsi: Fix mode validation
From: Luca Ceresoli @ 2026-06-23 10:41 UTC (permalink / raw)
To: Liu Ying, Luca Ceresoli
Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Dmitry Baryshkov, dri-devel, imx, linux-arm-kernel, linux-kernel
In-Reply-To: <ajjpuroFPI_U2Fw6@raspi>
Hello Liu,
On Mon Jun 22, 2026 at 9:52 AM CEST, Liu Ying wrote:
> On Fri, Jun 19, 2026 at 06:49:55PM +0200, Luca Ceresoli wrote:
>> Hello Liu,
>
> Hi Luca,
>
>>
>> On Fri May 15, 2026 at 8:54 AM CEST, Liu Ying wrote:
>> > i.MX93 MIPI DPHY PLL has limitation for matching with some pixel clock
>> > rates, e.g., the best DPHY PLL frequency is 445.333333MHz for a typical
>> > 1920x1080p@60Hz CEA/DMT display mode with a pixel clock rate running
>> > at 148.5MHz with 4 data lanes + RGB888 pixel in MIPI DSI sync pulse mode,
>> > while the expected PLL frequency is (148.5 * 24) / 4 / 2 MHz = 445.5MHz.
>> > Fortunately, VESA Display Monitor Timing Standard allows +/-0.5% pixel
>> > clock rate deviation for timings. So, for those display modes read
>> > from EDID through a bridge with DRM_BRIDGE_OP_DETECT and DRM_BRIDGE_OP_EDID
>> > operation bit masks set, pixel clock rate could be adjusted to match
>> > with the PLL frequency(for the above example, the pixel clock rate is
>> > adjusted to be 148.444444MHz with about -0.03% deviation from the 148.5MHz
>> > nominal rate so that the adjusted rate matches with the 445.333333MHz PLL
>> > frequency).
>> >
>> > Instead of checking the last bridge's operation bit masks against
>> > DRM_BRIDGE_OP_DETECT and DRM_BRIDGE_OP_EDID to determine if allowing
>> > +/-0.5% pixel clock rate deviation, check any bridge after this bridge,
>> > because the last bridge is usually a display connector bridge without
>> > any operation bit mask when the clock rate deviation is allowed.
>> >
>> > Fixes: ce62f8ea7e3f ("drm/bridge: imx: Add i.MX93 MIPI DSI support")
>> > Fixes: 5849eff7f067 ("drm/bridge: imx93-mipi-dsi: use drm_bridge_chain_get_last_bridge()")
>> > Reviewed-by: Frank Li <Frank.Li@nxp.com>
>> > Signed-off-by: Liu Ying <victor.liu@nxp.com>
>>
>> I'm perhaps not the most qualified to review this change, but let me try.
>
> Thanks for your review.
>
>>
>> > --- a/drivers/gpu/drm/bridge/imx/imx93-mipi-dsi.c
>> > +++ b/drivers/gpu/drm/bridge/imx/imx93-mipi-dsi.c
>> > @@ -489,25 +489,43 @@ static int imx93_dsi_get_phy_configure_opts(struct imx93_dsi *dsi,
>> > return 0;
>> > }
>> >
>> > +static inline struct drm_bridge *
>> > +imx93_dsi_get_next_bridge_in_chain(struct drm_bridge *bridge)
>> > +{
>> > + struct drm_bridge *next = drm_bridge_get_next_bridge(bridge);
>> > +
>> > + drm_bridge_put(bridge);
>> > +
>> > + return next;
>> > +}
>> > +
>> > static enum drm_mode_status
>> > imx93_dsi_validate_mode(struct imx93_dsi *dsi, const struct drm_display_mode *mode)
>> > {
>> > struct drm_bridge *dmd_bridge = dw_mipi_dsi_get_bridge(dsi->dmd);
>> > - struct drm_bridge *last_bridge __free(drm_bridge_put) =
>> > - drm_bridge_chain_get_last_bridge(dmd_bridge->encoder);
>> > + struct drm_bridge *bridge;
>> >
>> > - if ((last_bridge->ops & DRM_BRIDGE_OP_DETECT) &&
>> > - (last_bridge->ops & DRM_BRIDGE_OP_EDID)) {
>> > - unsigned long pixel_clock_rate = mode->clock * 1000;
>> > - unsigned long rounded_rate;
>> > + for (bridge = drm_bridge_get_next_bridge(dmd_bridge);
>> > + bridge;
>> > + bridge = imx93_dsi_get_next_bridge_in_chain(bridge)) {
>> > + if ((bridge->ops & DRM_BRIDGE_OP_DETECT) &&
>> > + (bridge->ops & DRM_BRIDGE_OP_EDID)) {
>> > + unsigned long pixel_clock_rate = mode->clock * 1000;
>> > + unsigned long rounded_rate;
>> >
>> > - /* Allow +/-0.5% pixel clock rate deviation */
>> > - rounded_rate = clk_round_rate(dsi->clk_pixel, pixel_clock_rate);
>> > - if (rounded_rate < pixel_clock_rate * 995 / 1000 ||
>> > - rounded_rate > pixel_clock_rate * 1005 / 1000) {
>> > - dev_dbg(dsi->dev, "failed to round clock for mode " DRM_MODE_FMT "\n",
>> > - DRM_MODE_ARG(mode));
>> > - return MODE_NOCLOCK;
>> > + /* Allow +/-0.5% pixel clock rate deviation */
>> > + rounded_rate = clk_round_rate(dsi->clk_pixel, pixel_clock_rate);
>> > + if (rounded_rate < pixel_clock_rate * 995 / 1000 ||
>> > + rounded_rate > pixel_clock_rate * 1005 / 1000) {
>> > + dev_dbg(dsi->dev,
>> > + "failed to round clock for mode " DRM_MODE_FMT "\n",
>> > + DRM_MODE_ARG(mode));
>> > + drm_bridge_put(bridge);
>> > + return MODE_NOCLOCK;
>> > + }
>> > +
>> > + drm_bridge_put(bridge);
>> > + break;
>> > }
>> > }
>>
>> Is this logic specific to the imx93 MIPI DSI host only? Or should it be
>> made generic for all dw-hdmi users, or even every DSI host?
>
> I think it's kind of specific to the i.MX93 MIPI DSI host only, because
> 1) the i.MX93 MIPI DPHY PLL(integrated into i.MX93 MIPI DPHY IP) supports
> the best DPHY PLL frequency @445.333333MHz for the typical 1920x1080p@60Hz
> display mode, which is lower than the expected/nominal frequency @445.5MHz
> and 2) the generic DW MIPI DSI driver(dw-mipi-dsi.c) is PHY-agnostic, which
> means vendors may attach different MIPI DPHY IPs to the common MIPI DSI host
> IP and likely there would be no frequency mismatch between the pixel clock
> and the PLL like i.MX93 has.
I see, OK, thanks for the clarification!
>> Also, iterating over the bridge chain is not very clean. I'm working on
>> bridge hotplug (not upstream yet) and bad things would happen if a bridge
>> were hot-unplugged during this loop.
>
> The iterating is essentially the same to drm_for_each_bridge_in_chain_from()
> except that bridge_chain_mutex is not taken(since it's already taken) and
> the starting bridge is fixed to be the next bridge. A few bridge core APIs
> like drm_bridge_chain_mode_valid() call drm_for_each_bridge_in_chain_from().
> So, the iterating looks clean to me and I'm not aware of any bad things which
> would happen when bridge hotplug is considered.
I had missed this is called from drm_bridge_chain_mode_valid(), which
already takes the bridge_chain_mutex thanks to
drm_for_each_bridge_in_chain_from().
So that means this loop is safe, but it would be nice to add a comment to
make it clear to future readers. E.g. "/* we are called by
drm_bridge_chain_mode_valid(), so the bridge_chain_mutex is locked */".
Still I'm not a fan of having a loop over the bridge chain (this function)
inside another loop over the same bridge chain (in
drm_bridge_chain_mode_valid(). And even more I'm not a fan of drivers
walking around the bridge chain on their own, IMO the core (perhaps
drm_bridge.c in this case) should to the loops . However this is a general
concern, it happens also elsewhere, and I have no immediate proposal to
improve this, so don't consider it as a blocker for this patch.
>> If the core did this sort of algorithm
>> it would be able to be more robust.
>
> Is the core dw-mipi-dsi.c?
> If yes, do you think it's worth doing that even if the frequency mismatch
> between the pixel clock and the PLL is kind of specific to the i.MX93 MIPI
> DSI host?
>
>>
>> Finally, out of my utter ignorance on the subject, is the VESA +/-0.5%
>> margin generic enough that this driver can always rely on it?
>
> I see several upstream drivers rely on it, see "git grep '0.5%' drivers/gpu/"
> output. And every display mode allows -/+ 0.5% pixel clock rate deviation
> according to VESA Display Monitor Timing Standard [1], though [1] is a found
> by a random Google search.
>
> [1] https://glenwing.github.io/docs/VESA-DMT-1.13.pdf
As I said I'm quite ignorance about this aspect, so I asked to better
understand. In reply to another part fo the thread: no, I don't have a
specific concern if this is the common practice.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* Re: [PATCH] ASoC: rockchip: rockchip_sai: Hand over hclk control exclusively to Runtime PM
From: Bui Duc Phuc @ 2026-06-23 10:53 UTC (permalink / raw)
To: Mark Brown
Cc: Heiko Stuebner, Liam Girdwood, Nicolas Frattaroli,
Krzysztof Kozlowski, Jaroslav Kysela, Takashi Iwai, linux-sound,
linux-rockchip, linux-arm-kernel, linux-kernel
In-Reply-To: <229ba136-66eb-4a30-a316-58377836b4dc@sirena.org.uk>
Hi Mark,
Thank you for your review.
> > 1 Reverting back to devm_clk_get() to remove the implicit devres
> > enable/disable behavior.
> > 2 Manually enabling and disabling hclk explicitly only around the
> > early register access before Runtime PM takes over.
> > 3 Dropping the stray clk_disable_unprepare() at the end of probe()
> > so Runtime PM solely owns hclk afterward.
>
> Note that runtime PM can be disabled at build time so we might not have
> runtime PM at all...
>
Thanks for pointing this out. You're right that with !CONFIG_PM, the
driver only relies on the
two manual calls to rokchip_sai_runtime_resume() / suspend(), so hclk
stays enabled the
whole time. I understand this is unvavoidable in that configuration,
throgh, since there's no
Runtime PM to re-enable the clock when it's needed.
I'll update the commit message to reflect that the driver uses a
combination of Runtime PM
and explicit manual enable/disable, rather than relying on Runtime PM alone.
> > Links:
> > 1 This change is based on the discussion around manual hclk handing during probe(),
> > as raised by Krysztof:
> > https://lore.kernel.org/all/20e4754b-ea9a-404d-b529-ec44a7263cbf@kernel.org/#t
> > 2 Background for the earlier devm_clk_get_enbabled() conversion:
> > https://lore.kernel.org/all/2818018.CQOukoFCf9@workhorse/
>
> > An alternative approach would be use devm_regmap_init_mmio_clk() and let regmap
> > manage clock enablement around register accesses. If preferred, I can rework the
> > driver accordingly.
>
> > - sai->hclk = devm_clk_get_enabled(&pdev->dev, "hclk");
> > + sai->hclk = devm_clk_get(&pdev->dev, "hclk");
> > if (IS_ERR(sai->hclk))
> > return dev_err_probe(&pdev->dev, PTR_ERR(sai->hclk),
> > "Failed to get hclk\n");
> >
> > + ret = clk_prepare_enable(sai->hclk);
> > + if (ret)
> > + return dev_err_probe(&pdev->dev, ret, "Failed to enable hclk\n");
> > +
>
> > @@ -1482,8 +1492,6 @@ static int rockchip_sai_probe(struct platform_device *pdev)
> > pm_runtime_use_autosuspend(&pdev->dev);
> > pm_runtime_put(&pdev->dev);
> >
> > - clk_disable_unprepare(sai->hclk);
> > -
> > return 0;
>
> Are you sure that the runtime PM state there is such that it knows a
> reference is held? The driver used pm_runtime_get_noresume() so the
> device didn't have RPM_ACTIVE set I think?
You are right, pm_runtime_get_noresume() doesn't set RPM_ACTIVE. I
think we need to add
pm_runtime_set_active() before pm_runtime_enable(). Otherwise, with CONFIG_PM,
the pm_runtime_put() at the end of probe() might skip the suspend,
since the core still considers
the device suspended .
>
> The runtime PM API really is a miserable collection of landmines :(
Yeah, plenty of landmines indeed :(
I checked, and rockchip_spdif.c does use devm_regmap_init_mmio_clk() for hclk,
rather than wrapping every register access in pm_runtime_get_sync() /
pm_runtime_put()
the way rockchip_sai does.
Best regards,
Phuc
^ permalink raw reply
* Re: [PATCH net v2] net: airoha: Add retry mechanism to airoha_qdma_set_trtcm_param()
From: Simon Horman @ 2026-06-23 10:53 UTC (permalink / raw)
To: lorenzo
Cc: Simon Horman, andrew+netdev, davem, edumazet, kuba, pabeni,
leto.liu, linux-arm-kernel, linux-mediatek, netdev, brown.huang
In-Reply-To: <20260622-airoha_qdma_set_trtcm_param-retry-fix-v2-1-95a83ac99dbd@kernel.org>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
Simon says: I'd appreciate it if you could look over the first two issues
flagged below in the context of this patch. While the third
and final issue looks like it is in possible separate follow-up
territory.
---
net: airoha: Add retry mechanism to airoha_qdma_set_trtcm_param()
This patch introduces a retry loop to airoha_qdma_set_trtcm_param() to mitigate
bus timeouts when configuring QDMA parameters. It attempts to verify that the
configuration is correctly applied to the hardware.
> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 3370c3df7c107..bb5c0599a4eed 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -2673,14 +2673,30 @@ static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel,
> FIELD_PREP(TRTCM_METER_GROUP_MASK, group) |
> FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) |
> FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode);
> + int i;
>
> - airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val);
> - airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
> + for (i = 0; i < 100; i++) {
> + u32 data;
>
> - return read_poll_timeout(airoha_qdma_rr, val,
> - val & TRTCM_PARAM_RW_DONE_MASK,
> - USEC_PER_MSEC, 10 * USEC_PER_MSEC, true,
> - qdma, REG_TRTCM_CFG_PARAM(addr));
> + airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val);
> + wmb();
> + airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
> +
> + if (read_poll_timeout(airoha_qdma_rr, data,
> + data & TRTCM_PARAM_RW_DONE_MASK,
> + USEC_PER_MSEC, 10 * USEC_PER_MSEC,
> + true, qdma, REG_TRTCM_CFG_PARAM(addr)))
> + return -ETIMEDOUT;
[Severity: High]
If a bus timeout causes the write phase's read_poll_timeout() in
airoha_qdma_set_trtcm_param() to fail, this immediately returns -ETIMEDOUT,
aborting the 100-iteration retry loop.
Doesn't this defeat the mitigation?
> +
> + if (airoha_qdma_get_trtcm_param(qdma, channel, addr, param,
> + mode, &data, NULL))
> + continue;
[Severity: High]
If airoha_qdma_get_trtcm_param() times out during the verification phase, it
indicates the hardware hasn't finished the read command (DONE bit is still 0).
The continue statement then immediately starts the next iteration, writing new
commands to DATA_LOW and CFG_PARAM.
Can writing to command registers while the hardware state machine is actively
busy violate hardware protocols and lead to unrecoverable lockups?
> +
> + if (data == val)
> + return 0;
[Severity: High]
This isn't a bug introduced by this patch, but if the bus drops the command
write to REG_TRTCM_CFG_PARAM, the hardware never executes the command and the
TRTCM_PARAM_RW_DONE_MASK bit remains set from the previous operation.
Consequently, read_poll_timeout() returns an immediate false success.
If both the set and get command writes are dropped by the bus, the get
operation will read back the newly written DATA_LOW register value.
Will the data == val check evaluate to true and falsely report success without
the hardware ever processing the configuration?
> + }
> +
> + return -EBUSY;
> }
>
[ ... ]
> static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel,
> u32 addr, enum trtcm_mode_type mode,
> bool enable, u32 enable_mask)
> {
> u32 val;
>
> if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
> mode, &val, NULL))
> return -EINVAL;
[Severity: High]
Standalone calls to airoha_qdma_get_trtcm_param(), like this one in
airoha_qdma_set_trtcm_config(), lack a retry loop entirely.
Does this leave read-modify-write cycles fully vulnerable to the same bus
timeouts the patch attempts to mitigate?
[Severity: High]
This isn't a bug introduced by this patch, but the patch mitigates bus
timeouts in airoha_qdma_set_trtcm_param() while entirely missing
airoha_qdma_set_rl_param().
The latter performs the exact same write-and-poll sequence on identical QDMA
configuration registers:
drivers/net/ethernet/airoha/airoha_eth.c:airoha_qdma_set_rl_param() {
...
return read_poll_timeout(..., val & RATE_LIMIT_PARAM_RW_DONE_MASK, ...);
}
If bus timeouts affect QDMA access broadly as stated in the commit message, are
the rate-limiting configuration paths randomly vulnerable to the same failures?
[Severity: High]
This isn't a bug introduced by this patch, but in airoha_tc_remove_htb_queue(),
hardware teardown incorrectly uses queue + 1 instead of queue:
drivers/net/ethernet/airoha/airoha_eth.c:airoha_tc_remove_htb_queue() {
...
airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
...
}
Does this leave the removed queue's hardware limits permanently active
(resource leak) and inadvertently disable the rate limits for queue + 1,
corrupting the QoS state of another active channel?
^ permalink raw reply
* [PATCH v2 00/12] Add thermal management support for sama7d65
From: Varshini Rajendran @ 2026-06-23 10:59 UTC (permalink / raw)
To: ehristev, jic23, dlechner, nuno.sa, andy, robh, krzk+dt, conor+dt,
nicolas.ferre, alexandre.belloni, claudiu.beznea, srini,
linux-iio, devicetree, linux-arm-kernel, linux-kernel
Cc: varshini.rajendran
Apologies for the significant delay in following up this series.
Thank you for your patience and the earlier reviews.
This v2 reworks the series based on the feedback received on v1.
The thermal management system of sama7d65 includes:
- Temperature sensor as a part of ADC channel
- Temperature calibration data retrieved from the OTP memory for
improved accuracy of the readings
- DVFS implementation
- Thermal system with DVFS as cooling cell.
This patch series adds support for the following:
- Tag-based packet lookup for the NVMEM OTPC driver while preserving
backward compatibility with existing ID-based access
- Temperature calibration layout handling in the ADC driver to support
different SoC-specific calibration data formats
- ADC driver adaptation for sama7d65
- DT nodes for OTP, ADC, temperature sensor, and thermal zones for
sama7d65
Changes in v2:
- Preserved backward compatibility with ID-based packet lookup to
avoid breaking existing users
- Removed sama7g5 DTS changes (not needed with backward compatible
driver - will be sent later to update to the new access method)
- Preserved the packet data structure returned not to break the
consumers
- Reworked ADC driver to use a calibration layout structure instead of
hardcoded indexes, for scalability
- Fixed kernel-doc Return section
- Removed stray blank line in mchp_otpc_read()
- Removed unnecessary UL suffix in writel_relaxed()
- Dropped unused packet types
- Fixed stray spaces before exclamation marks in error messages
- Added ASCII representation to TAG macro definition
- Removed odd MAX enum with trailing comma and refactored
- Moved DTS patches to the end of series
- Used cleanup.h helpers for NVMEM data buffer handling in ADC driver
- Combined multiple v1 patches into logical units
- Used correct subject prefixes for dt-bindings patches
- Used fixed-layout NVMEM syntax for sama7d65 DTS and binding
instead of deprecated syntax
- Added cpu-supply linkage for proper DVFS voltage scaling
- Updated stale stride=4 comment in dt-bindings header
Link to v1: https://lore.kernel.org/linux-arm-kernel/20250804100219.63325-1-varshini.rajendran@microchip.com/
Varshini Rajendran (12):
dt-bindings: iio: adc: at91-sama5d2: document sama7d65
iio: adc: at91-sama5d2_adc: rework temp calibration layout handling
iio: adc: at91-sama5d2_adc: adapt the driver for sama7d65
dt-bindings: nvmem: microchip,sama7g5-otpc: add sama7d65 and dt node
example
nvmem: microchip-otpc: add tag-based packet lookup
ARM: dts: microchip: sama7d65: add cpu opps
ARM: dts: microchip: sama7d65: Add ADC node
ARM: dts: microchip: sama7d65_curiosity: Enable ADC, DVFS
ARM: dts: microchip: sama7d65: add otpc node
ARM: dts: microchip: sama7d65: add cells for temperature calibration
ARM: dts: microchip: sama7d65: add temperature sensor
ARM: dts: microchip: sama7d65: add thermal zones node
.../bindings/iio/adc/atmel,sama5d2-adc.yaml | 1 +
.../nvmem/microchip,sama7g5-otpc.yaml | 28 +++-
.../dts/microchip/at91-sama7d65_curiosity.dts | 27 ++++
arch/arm/boot/dts/microchip/sama7d65.dtsi | 132 ++++++++++++++++
drivers/iio/adc/at91-sama5d2_adc.c | 116 ++++++++++----
drivers/nvmem/microchip-otpc.c | 142 ++++++++++++++++--
.../nvmem/microchip,sama7g5-otpc.h | 4 +-
7 files changed, 409 insertions(+), 41 deletions(-)
--
2.34.1
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox