Linux USB
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Muhammad Mahdi Amirpour" <ampr.mhd@gmail.com>
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH v3] usb: ohci-omap: handle root hub overcurrent via GPIO
Date: Sun, 18 Oct 2026 16:18:47 +0000	[thread overview]
Message-ID: <sashiko-outbox-176275@kernel.org> (raw)
In-Reply-To: <20261018155355.116290-1-ampr.mhd@gmail.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] usb: ohci-omap: sleeping GPIO call under spinlock in ohci_omap_hub_control()
- [High] usb: ohci-omap: VBUS power race in ohci_omap_overcurrent_irq()
- [Medium] usb: ohci-omap: VBUS power left enabled before OC init and on teardown
- [Medium] usb: ohci-omap: overcurrent state and IRQ initialized after usb_add_hcd()
--

commit 5181df0775d84a40d846a175267467ebbcb131d6
Author: Muhammad Mahdi Amirpour <ampr.mhd@gmail.com>

usb: ohci-omap: handle root hub overcurrent via GPIO

This commit adds root hub overcurrent handling for OMAP OHCI using the
optional overcurrent GPIO interrupt and updates hub_status_data and
hub_control to report overcurrent state and control the VBUS power GPIO.

> diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
> index f6e56c4b99148..a3ff50c70d41e 100644
> --- a/drivers/usb/host/ohci-omap.c
> +++ b/drivers/usb/host/ohci-omap.c

[ ... ]

> @@ -181,11 +189,148 @@ 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, 1);
> +
> +	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);

[Severity: High]
Can ohci_omap_overcurrent_irq() race with ohci_omap_hub_control() and leave
VBUS power enabled during an active overcurrent condition?

When an overcurrent interrupt fires, ohci_omap_overcurrent_irq() drives
priv->power to 1 before acquiring priv->overcurrent_lock and setting
priv->overcurrent_status = true.

If ohci_omap_hub_control() handles SetPortFeature(USB_PORT_FEAT_POWER) at
the same time (for instance during root hub activation or port power
recovery), it can check priv->overcurrent_status after
ohci_omap_overcurrent_irq() cuts VBUS power, still see
priv->overcurrent_status == false, and call
gpiod_set_value_cansleep(priv->power, 0) to turn VBUS back on just before
ohci_omap_overcurrent_irq() records priv->overcurrent_status = true.

Should the overcurrent_status update and the VBUS power GPIO state changes
be serialized under a sleeping lock such as a mutex?

[ ... ]

> +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;
> +	int ret;
> +
> +	/*
> +	 * Drive the VBUS power GPIO alongside the OHCI hardware register.
> +	 * On boards like the OSK, the OHCI PPS bit does not control the
> +	 * real power switch — the GPIO does.
> +	 */
> +	if (priv->power && wIndex == 1) {
> +		if (typeReq == SetPortFeature &&
> +		    wValue == USB_PORT_FEAT_POWER) {
> +			ret = ohci_hub_control(hcd, typeReq, wValue,
> +					       wIndex, buf, wLength);
> +			if (!ret) {
> +				spin_lock_irqsave(&priv->overcurrent_lock,
> +						  flags);
> +				if (!priv->overcurrent_status)
> +					gpiod_set_value_cansleep(priv->power,
> +								 0);
> +				spin_unlock_irqrestore(&priv->overcurrent_lock,
> +						       flags);
> +			}
> +			return ret;
> +		}

[Severity: High]
Doesn't calling gpiod_set_value_cansleep() while holding
priv->overcurrent_lock in ohci_omap_hub_control() sleep in atomic context?

During probe (usb_add_hcd() -> register_root_hub() -> hub_probe() ->
hub_activate() -> hub_power_on()) or port power recovery, USB core sends
SetPortFeature(USB_PORT_FEAT_POWER) for port 1.

gpiod_set_value_cansleep() calls might_sleep(), and on OMAP1 OSK the
"power" GPIO resides on the I2C-attached TPS65010 PMIC:

arch/arm/mach-omap1/board-osk.c:osk_usb_gpio_table:
	/* Power GPIO on the I2C-attached TPS65010 */
	GPIO_LOOKUP("tps65010", OSK_TPS_GPIO_USB_PWR_EN, "power",
		    GPIO_ACTIVE_HIGH),

drivers/mfd/tps65010.c:tps65010_set_gpio_out_value():
	mutex_lock(&the_tps->lock);

	defgpio = i2c_smbus_read_byte_data(the_tps->client, TPS_DEFGPIO);

Calling this inside spin_lock_irqsave(&priv->overcurrent_lock, flags)
acquires a mutex and performs blocking I2C SMBus transfers with preemption
and local interrupts disabled.

[ ... ]

> @@ -277,23 +425,76 @@ 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_NO_AUTOEN |
> +					      IRQF_TRIGGER_RISING |
> +					      IRQF_TRIGGER_FALLING,
> +					      "ohci-omap-overcurrent", hcd);
> +		if (retval) {
> +			priv->overcurrent_irq = -1;
> +			goto err_remove_hcd;
> +		}
> +
> +		status = gpiod_get_value_cansleep(priv->overcurrent);
> +		if (status < 0) {
> +			retval = status;
> +			goto err_remove_hcd;
> +		}
> +
> +		spin_lock_irqsave(&priv->overcurrent_lock, flags);
> +		priv->overcurrent_status = !!status;
> +		priv->overcurrent_changed = !!status;
> +		spin_unlock_irqrestore(&priv->overcurrent_lock, flags);
> +
> +		if (status && priv->power)
> +			gpiod_set_value_cansleep(priv->power, 1);
> +
> +		enable_irq(priv->overcurrent_irq);
> +		if (status)
> +			usb_hcd_poll_rh_status(hcd);
> +	}

[Severity: Medium]
Should the initial overcurrent GPIO state and IRQ setup happen before
usb_add_hcd() enables VBUS and registers the root hub?

Inside usb_add_hcd():
1. hcd->driver->reset() calls ohci_omap_reset(), which unconditionally
   enables VBUS via gpiod_set_value_cansleep(priv->power, 0) before
   priv->overcurrent is sampled.
2. hcd->driver->start() calls ohci_run(), which sleeps with VBUS enabled
   and no overcurrent IRQ registered.
3. register_root_hub() runs hub_probe() -> hub_configure() ->
   hub_activate() -> hub_power_on(), issuing
   SetPortFeature(USB_PORT_FEAT_POWER) to ohci_omap_hub_control() while
   priv->overcurrent_status is still false, so priv->power is driven to 0
   even if an overcurrent condition is already present.

Furthermore, because the root hub is already registered and exposed to
hub_wq before usb_add_hcd() returns, unconditionally assigning
priv->overcurrent_status and priv->overcurrent_changed here can overwrite
concurrent root hub status queries or ClearPortFeature requests.

>  
>  	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);
> +	omap_ohci_clock_power(priv, 0);
> +	if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
> +		(void)otg_set_host(hcd->usb_phy->otg, NULL);
> +		usb_put_phy(hcd->usb_phy);
> +	}
> +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);
> @@ -327,6 +528,10 @@ static void ohci_hcd_omap_remove(struct platform_device *pdev)
>  	struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd);
>  
>  	dev_dbg(hcd->self.controller, "stopping USB Controller\n");
> +	if (priv->overcurrent_irq >= 0) {
> +		free_irq(priv->overcurrent_irq, hcd);
> +		priv->overcurrent_irq = -1;
> +	}
>  	usb_remove_hcd(hcd);
>  	omap_ohci_clock_power(priv, 0);
>  	if (!IS_ERR_OR_NULL(hcd->usb_phy)) {

[Severity: Medium]
Does VBUS power remain enabled via priv->power if probe fails at
err_remove_hcd or when ohci_hcd_omap_remove() unbinds the driver?

During usb_add_hcd(), ohci_omap_reset() and hub_power_on() drive
priv->power to 0 to turn on VBUS. When usb_remove_hcd() runs in
err_remove_hcd or ohci_hcd_omap_remove(), neither hub_disconnect() nor
ohci_stop() issues ClearPortFeature(USB_PORT_FEAT_POWER), and resetting the
OHCI registers does not turn off the external GPIO power switch.

Should err_remove_hcd and ohci_hcd_omap_remove() drive priv->power to 1
after usb_remove_hcd() so VBUS is not left powered on without an active HCD
or overcurrent interrupt handler?

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261018155355.116290-1-ampr.mhd@gmail.com?part=1

      parent reply	other threads:[~2026-10-18 16:18 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-18 15:53 [PATCH v3] usb: ohci-omap: handle root hub overcurrent via GPIO Muhammad Mahdi Amirpour
2026-10-18 16:14 ` Greg Kroah-Hartman
2026-10-18 16:18 ` 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-176275@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