Re: [PATCH v2 3/5] phy: renesas: rcar-gen3-usb2: Lock around hardware registers and driver data

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Feb 25, 2025 at 11:01 AM Claudiu <claudiu.beznea@xxxxxxxxx> wrote:
>
> From: Claudiu Beznea <claudiu.beznea.uj@xxxxxxxxxxxxxx>
>
> The phy-rcar-gen3-usb2 driver exposes four individual PHYs that are
> requested and configured by PHY users. The struct phy_ops APIs access the
> same set of registers to configure all PHYs. Additionally, PHY settings can
> be modified through sysfs or an IRQ handler. While some struct phy_ops APIs
> are protected by a driver-wide mutex, others rely on individual
> PHY-specific mutexes.
>
> This approach can lead to various issues, including:
> 1/ the IRQ handler may interrupt PHY settings in progress, racing with
>    hardware configuration protected by a mutex lock
> 2/ due to msleep(20) in rcar_gen3_init_otg(), while a configuration thread
>    suspends to wait for the delay, another thread may try to configure
>    another PHY (with phy_init() + phy_power_on()); re-running the
>    phy_init() goes to the exact same configuration code, re-running the
>    same hardware configuration on the same set of registers (and bits)
>    which might impact the result of the msleep for the 1st configuring
>    thread
> 3/ sysfs can configure the hardware (though role_store()) and it can
>    still race with the phy_init()/phy_power_on() APIs calling into the
>    drivers struct phy_ops
>
> To address these issues, add a spinlock to protect hardware register access
> and driver private data structures (e.g., calls to
> rcar_gen3_is_any_rphy_initialized()). Checking driver-specific data remains
> necessary as all PHY instances share common settings. With this change,
> the existing mutex protection is removed and the cleanup.h helpers are
> used.
>
> While at it, to keep the code simpler, do not skip
> regulator_enable()/regulator_disable() APIs in
> rcar_gen3_phy_usb2_power_on()/rcar_gen3_phy_usb2_power_off() as the
> regulators enable/disable operations are reference counted anyway.
>
> Fixes: f3b5a8d9b50d ("phy: rcar-gen3-usb2: Add R-Car Gen3 USB2 PHY driver")
> Cc: stable@xxxxxxxxxxxxxxx
> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@xxxxxxxxxxx>
> Tested-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@xxxxxxxxxxx>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@xxxxxxxxxxxxxx>
> ---
>
> Changes in v2:
> - collected tags
>
>  drivers/phy/renesas/phy-rcar-gen3-usb2.c | 49 +++++++++++++-----------
>  1 file changed, 26 insertions(+), 23 deletions(-)
>
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx>

Cheers,
Prabhakar

> diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> index 826c9c4dd4c0..5c0ceba09b67 100644
> --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> @@ -9,6 +9,7 @@
>   * Copyright (C) 2014 Cogent Embedded, Inc.
>   */
>
> +#include <linux/cleanup.h>
>  #include <linux/extcon-provider.h>
>  #include <linux/interrupt.h>
>  #include <linux/io.h>
> @@ -118,7 +119,7 @@ struct rcar_gen3_chan {
>         struct regulator *vbus;
>         struct reset_control *rstc;
>         struct work_struct work;
> -       struct mutex lock;      /* protects rphys[...].powered */
> +       spinlock_t lock;        /* protects access to hardware and driver data structure. */
>         enum usb_dr_mode dr_mode;
>         u32 obint_enable_bits;
>         bool extcon_host;
> @@ -348,6 +349,8 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
>         bool is_b_device;
>         enum phy_mode cur_mode, new_mode;
>
> +       guard(spinlock_irqsave)(&ch->lock);
> +
>         if (!ch->is_otg_channel || !rcar_gen3_is_any_otg_rphy_initialized(ch))
>                 return -EIO;
>
> @@ -415,7 +418,7 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
>                 val = readl(usb2_base + USB2_ADPCTRL);
>                 writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
>         }
> -       msleep(20);
> +       mdelay(20);
>
>         writel(0xffffffff, usb2_base + USB2_OBINTSTA);
>         writel(ch->obint_enable_bits, usb2_base + USB2_OBINTEN);
> @@ -436,12 +439,14 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
>         if (pm_runtime_suspended(dev))
>                 goto rpm_put;
>
> -       status = readl(usb2_base + USB2_OBINTSTA);
> -       if (status & ch->obint_enable_bits) {
> -               dev_vdbg(dev, "%s: %08x\n", __func__, status);
> -               writel(ch->obint_enable_bits, usb2_base + USB2_OBINTSTA);
> -               rcar_gen3_device_recognition(ch);
> -               ret = IRQ_HANDLED;
> +       scoped_guard(spinlock, &ch->lock) {
> +               status = readl(usb2_base + USB2_OBINTSTA);
> +               if (status & ch->obint_enable_bits) {
> +                       dev_vdbg(dev, "%s: %08x\n", __func__, status);
> +                       writel(ch->obint_enable_bits, usb2_base + USB2_OBINTSTA);
> +                       rcar_gen3_device_recognition(ch);
> +                       ret = IRQ_HANDLED;
> +               }
>         }
>
>  rpm_put:
> @@ -456,6 +461,8 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
>         void __iomem *usb2_base = channel->base;
>         u32 val;
>
> +       guard(spinlock_irqsave)(&channel->lock);
> +
>         /* Initialize USB2 part */
>         val = readl(usb2_base + USB2_INT_ENABLE);
>         val |= USB2_INT_ENABLE_UCOM_INTEN | rphy->int_enable_bits;
> @@ -479,6 +486,8 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p)
>         void __iomem *usb2_base = channel->base;
>         u32 val;
>
> +       guard(spinlock_irqsave)(&channel->lock);
> +
>         rphy->initialized = false;
>
>         val = readl(usb2_base + USB2_INT_ENABLE);
> @@ -498,16 +507,17 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
>         u32 val;
>         int ret = 0;
>
> -       mutex_lock(&channel->lock);
> -       if (!rcar_gen3_are_all_rphys_power_off(channel))
> -               goto out;
> -
>         if (channel->vbus) {
>                 ret = regulator_enable(channel->vbus);
>                 if (ret)
> -                       goto out;
> +                       return ret;
>         }
>
> +       guard(spinlock_irqsave)(&channel->lock);
> +
> +       if (!rcar_gen3_are_all_rphys_power_off(channel))
> +               goto out;
> +
>         val = readl(usb2_base + USB2_USBCTR);
>         val |= USB2_USBCTR_PLL_RST;
>         writel(val, usb2_base + USB2_USBCTR);
> @@ -517,7 +527,6 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
>  out:
>         /* The powered flag should be set for any other phys anyway */
>         rphy->powered = true;
> -       mutex_unlock(&channel->lock);
>
>         return 0;
>  }
> @@ -528,18 +537,12 @@ static int rcar_gen3_phy_usb2_power_off(struct phy *p)
>         struct rcar_gen3_chan *channel = rphy->ch;
>         int ret = 0;
>
> -       mutex_lock(&channel->lock);
> -       rphy->powered = false;
> -
> -       if (!rcar_gen3_are_all_rphys_power_off(channel))
> -               goto out;
> +       scoped_guard(spinlock_irqsave, &channel->lock)
> +               rphy->powered = false;
>
>         if (channel->vbus)
>                 ret = regulator_disable(channel->vbus);
>
> -out:
> -       mutex_unlock(&channel->lock);
> -
>         return ret;
>  }
>
> @@ -750,7 +753,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
>         if (phy_data->no_adp_ctrl)
>                 channel->obint_enable_bits = USB2_OBINT_IDCHG_EN;
>
> -       mutex_init(&channel->lock);
> +       spin_lock_init(&channel->lock);
>         for (i = 0; i < NUM_OF_PHYS; i++) {
>                 channel->rphys[i].phy = devm_phy_create(dev, NULL,
>                                                         phy_data->phy_usb2_ops);
> --
> 2.43.0
>
>





[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux