From: sashiko-bot@kernel.org
To: "Petar Stepanovic" <pstepanovic@axiado.com>
Cc: linux-usb@vger.kernel.org, devicetree@vger.kernel.org,
robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v3 6/6] usb: gadget: udc: crg: Add core driver and build integration
Date: Tue, 20 Oct 2026 09:52:39 +0000 [thread overview]
Message-ID: <sashiko-outbox-178767@kernel.org> (raw)
In-Reply-To: <20261020-axiado-ax3000-usb-device-controller-v3-6-3012ecfc0a93@axiado.com>
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] usb: gadget: crg: crg_udc_reset() clears ep->enabled before disconnect()
- [High] usb: gadget: crg: premature resetting clear and missing checks in crg_udc_reset()
- [Medium] usb: gadget: crg: premature IRQ and data init before ep setup in crg_udc_probe()
- [Medium] usb: gadget: crg: premature unlocked state reset in crg_udc_remove()
- [Medium] usb: gadget: crg: race in crg_udc_shutdown() on uccr->control and pullup_on
- [Medium] usb: gadget: udc: crg: 32-bit dma_addr_t shift overflow under COMPILE_TEST
- [Medium] usb: gadget: crg: crg_udc_issue_command() overwrites pending command when RUN is 0
- [Low] usb: gadget: udc: crg: deprecated SIMPLE_DEV_PM_OPS and missing pm_sleep_ptr()
--
commit 0ef71bda637190f9d2012396d35661e2929b2b80
Author: Petar Stepanovic <pstepanovic@axiado.com>
usb: gadget: udc: crg: Add core driver and build integration
This patch adds the core probe, remove, reset, and interrupt handling
functions for the Axiado AX3000/AX3005 UDC driver and wires the driver
into Kconfig and Makefile.
> diff --git a/drivers/usb/gadget/udc/crg/Kconfig b/drivers/usb/gadget/udc/crg/Kconfig
> new file mode 100644
> index 0000000000000..7167c4943389b
> --- /dev/null
> +++ b/drivers/usb/gadget/udc/crg/Kconfig
> @@ -0,0 +1,16 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config USB_CRG_UDC
> + tristate "Corigine / Axiado USB Device Controller"
> + depends on USB_GADGET
> + depends on HAS_DMA
> + depends on ARCH_AXIADO || COMPILE_TEST
[Severity: Medium]
Can enabling COMPILE_TEST here break 32-bit builds where dma_addr_t is
32 bits wide (CONFIG_ARCH_DMA_ADDR_T_64BIT=n)?
In drivers/usb/gadget/udc/crg/crg-udc-gadget.c, crg_udc_ep_dequeue() and
crg_udc_handle_xfer_event() shift values cast to dma_addr_t left by 32 bits:
drivers/usb/gadget/udc/crg/crg-udc-gadget.c:crg_udc_ep_dequeue() {
...
dq_pt_addr = (dma_addr_t)deq_pt_lo | ((dma_addr_t)deq_pt_hi << 32);
...
}
drivers/usb/gadget/udc/crg/crg-udc-gadget.c:crg_udc_handle_xfer_event() {
...
trb = crg_udc_tran_trb_dma_to_virt(
udc_ep_ptr,
(dma_addr_t)le32_to_cpu(event->dw0) |
((dma_addr_t)le32_to_cpu(event->dw1) << 32));
...
}
On 32-bit architectures without 64-bit dma_addr_t, shifting a 32-bit type
by 32 bits triggers -Wshift-count-overflow. Should those shifts cast to u64
first (like is_request_dequeued() and crg_udc_update_dequeue_pt() already
do)?
[ ... ]
> diff --git a/drivers/usb/gadget/udc/crg/crg-udc-core.c b/drivers/usb/gadget/udc/crg/crg-udc-core.c
> new file mode 100644
> index 0000000000000..80a4fc6a61e88
> --- /dev/null
> +++ b/drivers/usb/gadget/udc/crg/crg-udc-core.c
> @@ -0,0 +1,706 @@
[ ... ]
> +/*
> + * command related ops
> + */
> +int crg_udc_issue_command(struct crg_gadget_dev *crg_udc,
> + enum crg_cmd_type type, u32 param0, u32 param1)
> +{
> + struct crg_uccr __iomem *uccr = crg_udc->uccr;
> + u32 status;
> + bool check_complete = false;
> + u32 tmp;
> +
> + /*
> + * The command state machine only runs while the controller itself is
> + * running. Once RUN is cleared (e.g. during udc_stop()/reset()), the
> + * hardware never processes queued commands, so ACTIVE never clears.
> + * Skip the busy/completion checks in that case instead of getting
> + * stuck on a command the hardware will never service.
> + */
> + tmp = readl(&uccr->control);
> + if (tmp & CRG_U3DC_CTRL_RUN)
> + check_complete = true;
> +
> + if (check_complete) {
> + tmp = readl(&uccr->cmd_control);
> + if (tmp & CRG_U3DC_CMD_CTRL_ACTIVE) {
> + dev_err(crg_udc->dev,
> + "previous command is not complete\n");
> + return -EBUSY;
> + }
> + }
> + /* Ensure that everything is written before issuing new command */
> + dma_wmb();
> +
> + writel(param0, &uccr->cmd_param0);
> + writel(param1, &uccr->cmd_param1);
> +
> + tmp = CRG_U3DC_CMD_CTRL_ACTIVE |
> + FIELD_PREP(CRG_U3DC_CMD_CTRL_TYPE_MASK, type);
> + writel(tmp, &uccr->cmd_control);
> +
> + if (!check_complete)
> + return 0;
[Severity: Medium]
What happens when multiple commands are issued while CRG_U3DC_CTRL_RUN is 0,
or when a command is issued immediately after CRG_U3DC_CTRL_RUN is asserted?
Because the controller has a single set of command registers
(uccr->cmd_param0, uccr->cmd_param1, uccr->cmd_control) and the hardware
does not clear CRG_U3DC_CMD_CTRL_ACTIVE while CRG_U3DC_CTRL_RUN is 0:
1. crg_udc_init_ep0() issues CRG_CMD_INIT_EP0 while CRG_U3DC_CTRL_RUN is 0
in crg_udc_probe(), crg_gadget_stop(), crg_udc_handle_port_status() on
disconnect, and crg_gadget_handle_interrupt() on CRG_U3DC_STATUS_SYS_ERR,
leaving CRG_CMD_INIT_EP0 pending in uccr->cmd_control. Similarly,
crg_udc_set_pullup(crg_udc, false) in crg_gadget_pullup() and
crg_udc_suspend() clears CRG_U3DC_CTRL_RUN while endpoints remain
enabled.
2. If another command is issued before CRG_U3DC_CTRL_RUN is set (for
example via crg_udc_ep_enable(), crg_udc_ep_dequeue() issuing
CRG_CMD_STOP_EP followed by CRG_CMD_SET_TR_DQPTR, or crg_udc_ep_halt()
issuing CRG_CMD_RESET_SEQNUM, CRG_CMD_CLEAR_HALT, and
CRG_CMD_SET_TR_DQPTR back-to-back), check_complete is false and
crg_udc_issue_command() overwrites the unserviced command in
uccr->cmd_control.
3. When crg_udc_set_pullup(crg_udc, true) later sets CRG_U3DC_CTRL_RUN, it
does not wait for a pending CRG_U3DC_CMD_CTRL_ACTIVE command to finish,
so a command issued right after RUN is set can see
CRG_U3DC_CMD_CTRL_ACTIVE still set and fail with -EBUSY.
Can this cause CRG_CMD_INIT_EP0 or endpoint commands to be lost or fail
with -EBUSY?
[ ... ]
> +static int crg_udc_probe(struct platform_device *pdev)
> +{
[ ... ]
> + spin_lock_irqsave(&crg_udc->udc_lock, flags);
> + ret = crg_udc_reset(crg_udc, &flags);
> + spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
> + if (ret)
> + goto err_free_resources;
> +
> + crg_udc_clear_portpm(crg_udc);
> +
> + ret = crg_gadget_irq_init(pdev, crg_udc);
> + if (ret)
> + goto err_free_resources;
> +
> + ret = crg_udc_reset_data_struct(crg_udc);
> + if (ret) {
> + dev_err(crg_udc->dev, "crg_udc_reset_data_struct error\n");
> + goto err_free_resources;
> + }
> +
> + crg_udc_init_ep_info(crg_udc);
> + ret = crg_udc_init_ep0(crg_udc);
> + if (ret)
> + goto err_free_resources;
[Severity: Medium]
Should crg_udc_init_ep_info(), crg_udc_reset_data_struct(), and
crg_udc_init_ep0() run before crg_gadget_irq_init(), with
crg_udc_init_ep_info() called before crg_udc_reset_data_struct()?
With the current ordering in crg_udc_probe():
1. crg_udc_reset_data_struct() runs before crg_udc_init_ep_info() has
initialized crg_udc->udc_ep[0], and calls:
crg_udc_reset_data_struct()
-> crg_udc_alloc_request(&crg_udc->udc_ep[0].usb_ep, GFP_ATOMIC)
-> trace_crg_udc_alloc_request(udc_ep, udc_req_ptr)
where udc_ep[0].usb_ep.name, ops, and crg_udc are still NULL.
2. crg_gadget_irq_init() registers the shared IRQ (IRQF_SHARED |
IRQF_ONESHOT) before crg_udc_reset_data_struct(), crg_udc_init_ep_info(),
and crg_udc_init_ep0() have initialized the event rings, endpoint list
heads, and EP0 transfer ring. Because crg_udc_common_irq() only checks
uccr->status for CRG_U3DC_STATUS_EINT | CRG_U3DC_STATUS_SYS_ERR without
checking CRG_U3DC_CTRL_INT_EN, an interrupt arriving in this window runs
crg_gadget_handle_interrupt() under spin_lock_irqsave(&crg_udc->udc_lock,
flags) while udc_ep[0].tran_ring_info.cpu_addr is still NULL, causing
crg_udc_init_ep0() to call dma_alloc_coherent(..., GFP_KERNEL) while
holding the spinlock:
drivers/usb/gadget/udc/crg/crg-udc-ep0.c:crg_udc_init_ep0() {
...
if (!udc_ep_ptr->tran_ring_info.cpu_addr) {
...
vaddr = dma_alloc_coherent(crg_udc->dev, len, &dma, GFP_KERNEL);
...
}
> +
> + crg_udc->udc_ep[0].desc = &crg_udc->ep0_desc;
> +
> + ret = usb_add_gadget(&crg_udc->gadget);
> + if (ret)
> + goto err_free_resources;
> +
> + return 0;
> +
> +err_free_resources:
> + crg_udc_free_resources(crg_udc);
> +err_put_gadget:
> + usb_put_gadget(&crg_udc->gadget);
> + return ret;
> +}
> +
> +static void crg_udc_remove(struct platform_device *pdev)
> +{
> + struct crg_gadget_dev *crg_udc = platform_get_drvdata(pdev);
> + u32 tmp;
> +
> + crg_udc->device_state = USB_STATE_ATTACHED;
> + crg_udc_set_vbus_detect(crg_udc, false);
> +
> + usb_del_gadget(&crg_udc->gadget);
> + cancel_work_sync(&crg_udc->udc_reset_work);
> + crg_udc_free_resources(crg_udc);
[Severity: Medium]
In crg_udc_remove(), why are crg_udc->device_state and VBUS detection
updated before usb_del_gadget(&crg_udc->gadget) and without holding
crg_udc->udc_lock?
While a gadget driver is bound and active, the controller and its interrupt
handler remain active until usb_del_gadget() unbinds the gadget driver and
invokes crg_gadget_stop() (which already sets crg_udc->device_state =
USB_STATE_ATTACHED and calls crg_udc_set_vbus_detect(crg_udc, false)).
Setting crg_udc->device_state = USB_STATE_ATTACHED before usb_del_gadget()
causes any concurrent crg_udc_handle_event() execution for
TRB_TYPE_EVT_TRANSFER to see crg_udc->device_state < USB_STATE_RECONNECTING
and drop in-flight transfer completion events:
drivers/usb/gadget/udc/crg/crg-udc-gadget.c:crg_udc_handle_event() {
...
case TRB_TYPE_EVT_TRANSFER:
if (crg_udc->device_state < USB_STATE_RECONNECTING) {
dev_err(crg_udc->dev,
"Xfer compl event rcved when dev state=%d !\n",
crg_udc->device_state);
break;
}
ret = crg_udc_handle_xfer_event(crg_udc, event, flags);
...
}
> +
> + /* set controller host role */
> + tmp = readl(crg_udc->mmio_virt_base + CRG_UDC_MODE_REG) & ~0x1;
> + writel(tmp, crg_udc->mmio_virt_base + CRG_UDC_MODE_REG);
> +
> + platform_set_drvdata(pdev, NULL);
> + usb_put_gadget(&crg_udc->gadget);
> +}
> +
> +static void crg_udc_shutdown(struct platform_device *pdev)
> +{
> + struct crg_gadget_dev *crg_udc = platform_get_drvdata(pdev);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&crg_udc->udc_lock, flags);
> + crg_udc->device_state = USB_STATE_ATTACHED;
> + crg_udc_set_pullup(crg_udc, false);
> + spin_unlock_irqrestore(&crg_udc->udc_lock, flags);
> + crg_udc_set_vbus_detect(crg_udc, false);
> + if (crg_udc->uccr) {
> + u32 val = readl(&crg_udc->uccr->control);
> +
> + val &= ~CRG_U3DC_CTRL_INT_EN;
> + writel(val, &crg_udc->uccr->control);
> + }
> + if (crg_udc->irq >= 0)
> + synchronize_irq(crg_udc->irq);
> +}
[Severity: Medium]
Can crg_udc_shutdown() race with crg_gadget_handle_interrupt() and leave
the controller running with interrupts enabled?
In crg_udc_shutdown():
1. crg_udc_set_pullup(crg_udc, false) clears CRG_U3DC_CTRL_RUN, but
crg_udc->pullup_on is not cleared.
2. &crg_udc->uccr->control is read-modify-written to clear
CRG_U3DC_CTRL_INT_EN after dropping crg_udc->udc_lock and before
synchronize_irq(crg_udc->irq).
3. If crg_udc_irq_thread() -> crg_gadget_handle_interrupt() is running
concurrently (for instance, recovering from CRG_U3DC_STATUS_SYS_ERR where
crg_udc_reset() drops and reacquires udc_lock), it observes
crg_udc->pullup_on == true and calls crg_udc_set_pullup(crg_udc, true),
which sets CRG_U3DC_CTRL_SYSERR_EN | CRG_U3DC_CTRL_INT_EN |
CRG_U3DC_CTRL_RUN in uccr->control again.
4. crg_udc->udc_reset_work is not cancelled with cancel_work_sync().
Should crg_udc->pullup_on = false and the CRG_U3DC_CTRL_INT_EN clear be
done under crg_udc->udc_lock, followed by synchronize_irq() and
cancel_work_sync(&crg_udc->udc_reset_work)?
[ ... ]
> +static void crg_gadget_handle_interrupt(struct crg_gadget_dev *crg_udc)
> +{
> + struct crg_uccr __iomem *uccr = crg_udc->uccr;
> + unsigned long flags = 0;
> + u32 tmp_status;
> + int ret;
> +
> + spin_lock_irqsave(&crg_udc->udc_lock, flags);
> +
> + tmp_status = readl(&uccr->status);
> +
> + if (tmp_status & CRG_U3DC_STATUS_SYS_ERR) {
> + bool was_connected = crg_udc->connected;
> +
> + dev_err(crg_udc->dev, "controller system error\n");
> + writel(CRG_U3DC_STATUS_SYS_ERR, &uccr->status);
> + ret = crg_udc_reset(crg_udc, &flags);
> + if (!ret)
> + ret = crg_udc_reset_data_struct(crg_udc);
> + if (!ret)
> + ret = crg_udc_init_ep0(crg_udc);
> + if (ret)
> + dev_err(crg_udc->dev,
> + "failed to recover from controller error: %d\n",
> + ret);
> + crg_udc->gadget.speed = USB_SPEED_UNKNOWN;
> + crg_udc->connected = 0;
> + if (was_connected)
> + crg_udc_notify_disconnect(crg_udc, &flags);
[Severity: High]
Does calling crg_udc_reset() before crg_udc_notify_disconnect() here
prevent gadget function drivers from cleaning up on disconnect?
In crg_udc_reset(), udc_ep_ptr->usb_ep.enabled is cleared to 0 for all
non-EP0 endpoints:
for (i = 2; i < CRG_NUM_EP_CX; i++) {
...
udc_ep_ptr->ep_state = EP_STATE_DISABLED;
udc_ep_ptr->usb_ep.enabled = 0;
}
When crg_udc_notify_disconnect() then invokes
crg_udc->gadget_driver->disconnect() -> composite_disconnect() ->
reset_config(), function drivers whose ->disable() callbacks guard teardown
on ep->enabled (such as rndis_disable() checking rndis->notify->enabled, or
ecm_disable() and eem_disable() checking port.in_ep->enabled) see
enabled == 0 and return without calling rndis_uninit() or
gether_disconnect(). On subsequent re-enumeration, their ->set_alt()
callbacks also check in_ep->enabled before calling gether_disconnect(),
leaking the previous session's usb_request objects.
Should crg_udc_notify_disconnect() be called before crg_udc_reset(), matching
the disconnect path in crg_udc_handle_port_status()?
[ ... ]
> +int crg_udc_reset(struct crg_gadget_dev *crg_udc, unsigned long *flags)
> +{
> + struct crg_uccr __iomem *uccr = crg_udc->uccr;
> + u32 i, tmp;
> + struct crg_udc_ep *udc_ep_ptr;
> + u32 val;
> +
> + val = readl(&uccr->control);
> + val |= CRG_U3DC_CTRL_SWRST;
> + writel(val, &uccr->control);
> + crg_udc->resetting = true;
> + spin_unlock_irqrestore(&crg_udc->udc_lock, *flags);
> + /* Poll until SWRST clears; hardware reset takes up to 10 ms */
> + if (readl_poll_timeout_atomic(&uccr->control, tmp,
> + !(tmp & CRG_U3DC_CTRL_SWRST), 10, 10000)) {
> + spin_lock_irqsave(&crg_udc->udc_lock, *flags);
> + crg_udc->resetting = false;
> + dev_err(crg_udc->dev, "reset error\n");
> + return -ETIMEDOUT;
> + }
> + spin_lock_irqsave(&crg_udc->udc_lock, *flags);
> + crg_udc->resetting = false;
> +
> + crg_udc_clear_portpm(crg_udc);
> +
> + crg_udc->setup_status = WAIT_FOR_SETUP;
> + /* Base on Figure 9-1, default USB_STATE is attached */
> + crg_udc->device_state = USB_STATE_ATTACHED;
> + crg_udc->dev_addr = 0;
> +
> + /* Complete any reqs on EP1-EP15 queue */
> + for (i = 2; i < CRG_NUM_EP_CX; i++) {
> + udc_ep_ptr = &crg_udc->udc_ep[i];
> +
> + if (udc_ep_ptr->desc) {
> + udc_ep_ptr->desc = NULL;
> + udc_ep_ptr->comp_desc = NULL;
> + crg_udc_ep_nuke(udc_ep_ptr, -ESHUTDOWN);
> + }
> + udc_ep_ptr->tran_ring_full = false;
> + udc_ep_ptr->ep_state = EP_STATE_DISABLED;
> + /*
> + * This bypasses the generic usb_ep_disable() API, so the
> + * framework's own usb_ep.enabled bookkeeping needs to be
> + * cleared directly here too (crg_udc_reinit() already does
> + * this for the live bus-reset case). Otherwise a later
> + * usb_ep_enable() call for this endpoint short-circuits on
> + * the stale "already enabled" flag without ever calling our
> + * .enable callback, leaving desc/the ring never set up for
> + * the new session.
> + */
> + udc_ep_ptr->usb_ep.enabled = 0;
> + }
> + crg_udc->num_enabled_eps = 0;
> +
> + /* Complete any reqs on EP0 queue */
> + udc_ep_ptr = &crg_udc->udc_ep[0];
> + if (udc_ep_ptr->desc) {
> + udc_ep_ptr->desc = NULL;
> + udc_ep_ptr->comp_desc = NULL;
> + crg_udc_ep_nuke(udc_ep_ptr, -ESHUTDOWN);
> + }
> +
> + return 0;
> +}
[Severity: High]
Can dropping crg_udc->udc_lock during crg_udc_reset() allow other threads or
completion callbacks to touch registers and ring doorbells while the
controller is mid-reset or before its rings and contexts are reinitialized?
There appear to be two windows here when crg_udc_reset() is called at
runtime (from crg_gadget_handle_interrupt(), crg_udc_handle_port_status(),
or crg_gadget_stop()):
1. While udc_lock is dropped polling for CRG_U3DC_CTRL_SWRST to clear, all
endpoints still have desc != NULL and ep_state == EP_STATE_RUNNING, and
only crg_udc_ep_queue() checks crg_udc->resetting. Concurrent calls to
crg_udc_ep_dequeue(), crg_udc_ep_disable(), crg_udc_ep_enable(),
crg_udc_ep_set_halt(), crg_udc_ep_set_wedge(), crg_gadget_pullup(),
crg_gadget_wakeup(), crg_udc_suspend(), or crg_udc_resume() can acquire
udc_lock and issue commands or write registers while SWRST is active.
2. crg_udc->resetting is set back to false immediately after SWRST clears,
before the loop over udc_ep[2..CRG_NUM_EP_CX - 1] and udc_ep[0], and
before the caller runs crg_udc_reset_data_struct() and
crg_udc_init_ep0(). Inside the loop, crg_udc_ep_nuke() ->
crg_udc_req_done() drops udc_lock around usb_gadget_giveback_request()
while crg_udc->resetting is already false and remaining endpoints j > i
(as well as udc_ep[0]) still have desc != NULL:
drivers/usb/gadget/udc/crg/crg-udc-gadget.c:crg_udc_ep_queue() {
...
if (udc_ep_ptr->desc != desc || !udc_req_ptr->usb_req.complete ||
!list_empty(&udc_req_ptr->queue) || crg_udc->resetting) {
...
}
A completion callback or another CPU waiting in crg_udc_ep_queue() for a
later endpoint j > i (or EP0) will see both udc_ep_ptr->desc == desc and
crg_udc->resetting == false, and will queue TRBs and ring the doorbell on
a wiped controller before crg_udc_reset_data_struct() and
crg_udc_init_ep0() have reprogrammed DCBAP, the event rings, and EP0.
> +
> +static const struct crg_udc_priv crg_udc_data = {
> + .quirks = CRG_UDC_QUIRKS_DEFAULT,
> +};
> +
> +static const struct of_device_id of_crg_udc_match[] = {
> + { .compatible = "axiado,ax3000-udc", .data = &crg_udc_data },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, of_crg_udc_match);
> +
> +static SIMPLE_DEV_PM_OPS(crg_udc_pm_ops, crg_udc_suspend, crg_udc_resume);
> +
> +static struct platform_driver crg_udc_driver = {
> + .probe = crg_udc_probe,
> + .remove = crg_udc_remove,
> + .shutdown = crg_udc_shutdown,
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .of_match_table = of_crg_udc_match,
> + .pm = &crg_udc_pm_ops,
> + },
> +};
[Severity: Low]
SIMPLE_DEV_PM_OPS() is deprecated in include/linux/pm.h. Could this use
DEFINE_SIMPLE_DEV_PM_OPS(crg_udc_pm_ops, crg_udc_suspend, crg_udc_resume)
with pm_sleep_ptr(&crg_udc_pm_ops) (and drop __maybe_unused from
crg_udc_suspend() and crg_udc_resume()) so the PM callbacks and ops struct
are omitted when CONFIG_PM_SLEEP is disabled?
[ ... ]
>
endasi
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261020-axiado-ax3000-usb-device-controller-v3-0-3012ecfc0a93@axiado.com?part=6
prev parent reply other threads:[~2026-10-20 9:52 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 7:18 [PATCH v3 0/6] Axiado AX3000 and AX3005 USB Device Controller (UDC) Petar Stepanovic
2026-10-20 7:18 ` [PATCH v3 1/6] dt-bindings: usb: gadget: udc: add Axiado AX3000 and AX3005 UDC Petar Stepanovic
2026-10-20 7:18 ` [PATCH v3 2/6] usb: gadget: udc: crg: Add hardware definitions and structs Petar Stepanovic
2026-10-20 7:18 ` [PATCH v3 3/6] usb: gadget: udc: crg: Add ring and transfer management Petar Stepanovic
2026-10-20 8:58 ` sashiko-bot
2026-10-20 7:18 ` [PATCH v3 4/6] usb: gadget: udc: crg: Add endpoint 0 handling Petar Stepanovic
2026-10-20 10:01 ` sashiko-bot
2026-10-20 7:18 ` [PATCH v3 5/6] usb: gadget: udc: crg: Add gadget and endpoint operations Petar Stepanovic
2026-10-20 10:01 ` sashiko-bot
2026-10-20 7:19 ` [PATCH v3 6/6] usb: gadget: udc: crg: Add core driver and build integration Petar Stepanovic
2026-10-20 9:52 ` sashiko-bot [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-178767@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=pstepanovic@axiado.com \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox