From: sashiko-bot@kernel.org
To: "Muhammad Mahdi Amirpour" <ampr.mhd@gmail.com>
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH v2] usb: ohci-omap: handle root hub overcurrent
Date: Sun, 18 Oct 2026 11:27:36 +0000 [thread overview]
Message-ID: <sashiko-outbox-176080@kernel.org> (raw)
In-Reply-To: <20261018111124.32979-1-ampr.mhd@gmail.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] usb: ohci-omap: broken overcurrent status bits in ohci_omap_hub_control()
- [High] usb: ohci-omap: power GPIO enabled instead of disabled on overcurrent
- [High] usb: ohci-omap: missing clock and PHY cleanup in ohci_hcd_omap_probe()
- [Medium] usb: ohci-omap: false overcurrent_changed and race in ohci_hcd_omap_probe()
--
commit 1225a9a245304878b12998496dea4744efeb5ced
Author: Muhammad Mahdi Amirpour <ampr.mhd@gmail.com>
usb: ohci-omap: handle root hub overcurrent
This commit adds GPIO-based overcurrent interrupt handling for the OMAP
OHCI root hub, disables port power upon overcurrent detection, and
reports overcurrent status through the root hub port status callbacks.
> diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
> index f6e56c4b99148..2a08147dc5fa1 100644
> --- a/drivers/usb/host/ohci-omap.c
> +++ b/drivers/usb/host/ohci-omap.c
[ ... ]
> @@ -181,11 +189,110 @@ static int ohci_omap_reset(struct usb_hcd *hcd)
> return 0;
> }
>
> +static irqreturn_t ohci_omap_overcurrent_irq(int irq, void *data)
> +{
> + struct usb_hcd *hcd = data;
> + struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
> + unsigned long flags;
> + int status;
> +
> + status = gpiod_get_value_cansleep(priv->overcurrent);
> + if (status < 0)
> + return IRQ_HANDLED;
> +
> + if (status && priv->power)
> + gpiod_set_value_cansleep(priv->power, 0);
[Severity: High]
Does setting priv->power to 0 here actually enable VBUS power rather than
disabling it?
On OMAP1 OSK (arch/arm/mach-omap1/board-osk.c), the TPS65010 USB VBUS
power enable line (n_vbus_en) is active-low in hardware, registered with
GPIO_ACTIVE_HIGH, and initialized to GPIOD_OUT_HIGH (1) to keep VBUS off.
Consistent with that polarity, ohci_omap_reset() turns VBUS power on by
setting priv->power to 0:
if (priv->power)
gpiod_set_value_cansleep(priv->power, 0);
When an overcurrent condition is detected in ohci_omap_overcurrent_irq()
and in ohci_hcd_omap_probe(), shouldn't priv->power be set to 1 to shut
off VBUS power?
Additionally, since ohci_omap_hub_control() does not handle
SetPortFeature or ClearPortFeature for USB_PORT_FEAT_POWER to drive
priv->power, how can hub_power_on() re-enable VBUS power after the
overcurrent condition clears?
> +
> + spin_lock_irqsave(&priv->overcurrent_lock, flags);
> + if (!!status != priv->overcurrent_status)
> + priv->overcurrent_changed = true;
> + priv->overcurrent_status = !!status;
> + spin_unlock_irqrestore(&priv->overcurrent_lock, flags);
> +
> + usb_hcd_poll_rh_status(hcd);
> +
> + return IRQ_HANDLED;
> +}
[ ... ]
> +static int ohci_omap_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
> + u16 wIndex, char *buf, u16 wLength)
> +{
> + struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
> + unsigned long flags;
> + bool status, changed;
> + __le32 *portstatus;
> + u32 value;
> + int ret;
> +
> + ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
> + if (ret)
> + return ret;
> +
> + if (typeReq == ClearPortFeature && wIndex == 1 &&
> + wValue == USB_PORT_FEAT_C_OVER_CURRENT) {
> + unsigned long flags;
> +
> + spin_lock_irqsave(&priv->overcurrent_lock, flags);
> + priv->overcurrent_changed = false;
> + spin_unlock_irqrestore(&priv->overcurrent_lock, flags);
> + return 0;
> + }
> +
> + if (typeReq != GetPortStatus || wIndex != 1)
> + return ret;
> +
> + spin_lock_irqsave(&priv->overcurrent_lock, flags);
> + status = priv->overcurrent_status;
> + changed = priv->overcurrent_changed;
> + spin_unlock_irqrestore(&priv->overcurrent_lock, flags);
> +
> + portstatus = (__le32 *)buf;
> + value = le32_to_cpu(*portstatus);
> +
> + if (status)
> + value |= USB_PORT_STAT_OVERCURRENT;
> + else
> + value &= ~USB_PORT_STAT_OVERCURRENT;
> +
> + if (changed)
> + value |= USB_PORT_STAT_C_OVERCURRENT;
> + else
> + value &= ~USB_PORT_STAT_C_OVERCURRENT;
> +
> + *portstatus = cpu_to_le32(value);
> +
> + return 0;
> +}
[Severity: High]
Isn't USB_PORT_STAT_C_OVERCURRENT overwriting USB_PORT_STAT_OVERCURRENT in
wPortStatus instead of setting the change bit in wPortChange?
In ohci_hub_control(), GetPortStatus writes a 32-bit word into buf where
the lower 16 bits are wPortStatus (RH_PS_POCI = 0x00000008) and the upper
16 bits are wPortChange (RH_PS_OCIC = 0x00080000). Both
USB_PORT_STAT_OVERCURRENT and USB_PORT_STAT_C_OVERCURRENT are defined as
0x0008 in include/uapi/linux/usb/ch11.h for their respective 16-bit
fields.
Applying USB_PORT_STAT_C_OVERCURRENT unshifted to the 32-bit value
overwrites bit 3 (wPortStatus) with changed and never sets bit 19
(wPortChange). Because USB_PORT_STAT_C_OVERCURRENT is never set in the
upper 16 bits, port_event() in drivers/usb/core/hub.c will never see the
overcurrent change or send ClearPortFeature(USB_PORT_FEAT_C_OVER_CURRENT)
to clear priv->overcurrent_changed, causing ohci_omap_hub_status_data()
to report BIT(1) on every poll cycle indefinitely.
Also, if priv->overcurrent is NULL on a board without the optional GPIO,
won't this unconditionally clear the hardware's RH_PS_POCI bit in buf?
And as a minor note, the inner ClearPortFeature block redeclares unsigned
long flags, shadowing the outer declaration in ohci_omap_hub_control().
[ ... ]
> @@ -277,23 +387,77 @@ static int ohci_hcd_omap_probe(struct platform_device *pdev)
> if (!hcd->regs) {
> dev_err(&pdev->dev, "can't ioremap OHCI HCD\n");
> retval = -ENOMEM;
> - goto err2;
> + goto err_release_mem;
> }
>
> irq = platform_get_irq(pdev, 0);
> if (irq < 0) {
> retval = irq;
> - goto err3;
> + goto err_iounmap;
> }
> +
> retval = usb_add_hcd(hcd, irq, 0);
> if (retval)
> - goto err3;
> + goto err_iounmap;
> +
> + if (priv->overcurrent) {
> + int status;
> + unsigned long flags;
> +
> + priv->overcurrent_irq = gpiod_to_irq(priv->overcurrent);
> + if (priv->overcurrent_irq < 0) {
> + retval = priv->overcurrent_irq;
> + goto err_remove_hcd;
> + }
> +
> + retval = request_threaded_irq(priv->overcurrent_irq, NULL,
> + ohci_omap_overcurrent_irq,
> + IRQF_ONESHOT |
> + IRQF_TRIGGER_RISING |
> + IRQF_TRIGGER_FALLING,
> + "ohci-omap-overcurrent", hcd);
> + if (retval) {
> + priv->overcurrent_irq = -1;
> + goto err_remove_hcd;
> + }
> +
> + /*
> + * Synchronize the initial GPIO sample with the threaded
> + * handler so it cannot overwrite the sampled state.
> + */
> + disable_irq(priv->overcurrent_irq);
> +
> + status = gpiod_get_value_cansleep(priv->overcurrent);
> + if (status < 0) {
> + retval = status;
> + enable_irq(priv->overcurrent_irq);
> + goto err_remove_hcd;
> + }
> +
> + spin_lock_irqsave(&priv->overcurrent_lock, flags);
> + priv->overcurrent_status = !!status;
> + priv->overcurrent_changed = true;
> + spin_unlock_irqrestore(&priv->overcurrent_lock, flags);
[Severity: Medium]
Why is priv->overcurrent_changed set to true unconditionally during probe
even when status is 0?
When no overcurrent is present at boot (status == 0), setting
priv->overcurrent_changed = true and calling usb_hcd_poll_rh_status(hcd)
reports a false overcurrent change event on port 1 on every probe.
In addition, usb_add_hcd() registers the root hub and starts hub_wq
polling before priv->overcurrent is sampled, and request_threaded_irq()
is called without IRQF_NO_AUTOEN, leaving a window before disable_irq()
where ohci_omap_overcurrent_irq() can run and have its state overwritten
by this initial sample.
> +
> + if (status && priv->power)
> + gpiod_set_value_cansleep(priv->power, 0);
> +
> + enable_irq(priv->overcurrent_irq);
> + usb_hcd_poll_rh_status(hcd);
> + }
>
> device_wakeup_enable(hcd->self.controller);
> return 0;
> -err3:
> +
> +err_remove_hcd:
> + if (priv->overcurrent_irq >= 0) {
> + free_irq(priv->overcurrent_irq, hcd);
> + priv->overcurrent_irq = -1;
> + }
> + usb_remove_hcd(hcd);
> +err_iounmap:
> iounmap(hcd->regs);
> -err2:
> +err_release_mem:
> release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
> err_unprepare_dc_ck:
> clk_unprepare(priv->usb_dc_ck);
[Severity: High]
If probe fails after usb_add_hcd() and jumps to err_remove_hcd, doesn't
this miss disabling the clocks and cleaning up hcd->usb_phy?
During usb_add_hcd(), ohci_omap_reset() calls omap_ohci_clock_power(priv, 1)
to enable priv->usb_dc_ck and priv->usb_host_ck, and when OTG is used it
acquires hcd->usb_phy and sets otg_set_host(hcd->usb_phy->otg,
&ohci_to_hcd(ohci)->self).
Unlike ohci_hcd_omap_remove():
usb_remove_hcd(hcd);
omap_ohci_clock_power(priv, 0);
if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
(void) otg_set_host(hcd->usb_phy->otg, 0);
usb_put_phy(hcd->usb_phy);
}
the err_remove_hcd path proceeds directly from usb_remove_hcd() to
clk_unprepare() while the clocks are still enabled, and leaves
hcd->usb_phy leaked with hcd->usb_phy->otg->host pointing to the freed
hcd->self.
[ ... ]
> @@ -413,6 +581,8 @@ static int __init ohci_omap_init(void)
> return -ENODEV;
>
> ohci_init_driver(&ohci_omap_hc_driver, &omap_overrides);
> + ohci_omap_hc_driver.hub_status_data = ohci_omap_hub_status_data;
> + ohci_omap_hc_driver.hub_control = ohci_omap_hub_control;
> return platform_driver_register(&ohci_hcd_omap_driver);
> }
> module_init(ohci_omap_init);
ওয়াট
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018111124.32979-1-ampr.mhd@gmail.com?part=1
next prev parent reply other threads:[~2026-10-18 11:27 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-18 1:57 [PATCH] usb: ohci-omap: handle root hub overcurrent m-amirpour
2026-10-18 2:32 ` Alan Stern
2026-10-18 6:41 ` Greg KH
2026-10-18 11:11 ` [PATCH v2] " Muhammad Mahdi Amirpour
2026-10-18 11:27 ` sashiko-bot [this message]
2026-10-18 11:54 ` Greg KH
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-176080@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=ampr.mhd@gmail.com \
--cc=linux-usb@vger.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