[PATCH v11 3/5] clk: en7523: protect concurrent access to reset and PCI register
From: Christian Marangi
Date: Fri Nov 06 2026 - 07:04:47 EST
Use spinlock for concurrent access to reset and PCI register to catch case
when clock is enabled/disabled while a reset is also operated at the same
time.
Signed-off-by: Christian Marangi <ansuelsmth@xxxxxxxxx>
---
drivers/clk/clk-en7523.c | 86 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 84 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c
index 2baec734df9f..e534a5c91e61 100644
--- a/drivers/clk/clk-en7523.c
+++ b/drivers/clk/clk-en7523.c
@@ -577,44 +577,87 @@ static u32 en7523_get_div(const struct en_clk_desc *desc, u32 val)
static int en7523_pci_is_enabled(struct clk_hw *hw)
{
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
+ struct device *dev = clk_hw_get_dev(hw);
+ struct en_clk_priv *priv;
+ unsigned long flags;
+ u32 val;
- return !!(readl(cg->base + REG_PCI_CONTROL) & REG_PCI_CONTROL_REFCLK_EN1);
+ priv = dev_get_drvdata(dev);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ val = readl(cg->base + REG_PCI_CONTROL);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return !!(val & REG_PCI_CONTROL_REFCLK_EN1);
}
static int en7523_pci_prepare(struct clk_hw *hw)
{
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
+ struct device *dev = clk_hw_get_dev(hw);
void __iomem *np_base = cg->base;
+ struct en_clk_priv *priv;
+ unsigned long flags;
u32 val, mask;
+ priv = dev_get_drvdata(dev);
+
/* Need to pull device low before reset */
+ spin_lock_irqsave(&priv->lock, flags);
val = readl(np_base + REG_PCI_CONTROL);
val &= ~(REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT);
writel(val, np_base + REG_PCI_CONTROL);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
usleep_range(1000, 2000);
/* Enable PCIe port 1 */
+ spin_lock_irqsave(&priv->lock, flags);
+ val = readl(np_base + REG_PCI_CONTROL);
val |= REG_PCI_CONTROL_REFCLK_EN1;
writel(val, np_base + REG_PCI_CONTROL);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
usleep_range(1000, 2000);
/* Reset to default */
+ spin_lock_irqsave(&priv->lock, flags);
val = readl(np_base + REG_RESET_CONTROL1);
mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 |
REG_RESET_CONTROL_PCIEHB;
writel(val & ~mask, np_base + REG_RESET_CONTROL1);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
usleep_range(1000, 2000);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ val = readl(np_base + REG_RESET_CONTROL1);
writel(val | mask, np_base + REG_RESET_CONTROL1);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
msleep(100);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ val = readl(np_base + REG_RESET_CONTROL1);
writel(val & ~mask, np_base + REG_RESET_CONTROL1);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
usleep_range(5000, 10000);
/* Release device */
- mask = REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT;
+ spin_lock_irqsave(&priv->lock, flags);
val = readl(np_base + REG_PCI_CONTROL);
+ mask = REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT;
writel(val & ~mask, np_base + REG_PCI_CONTROL);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
usleep_range(1000, 2000);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ val = readl(np_base + REG_PCI_CONTROL);
writel(val | mask, np_base + REG_PCI_CONTROL);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
msleep(250);
return 0;
@@ -623,12 +666,19 @@ static int en7523_pci_prepare(struct clk_hw *hw)
static void en7523_pci_unprepare(struct clk_hw *hw)
{
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
+ struct device *dev = clk_hw_get_dev(hw);
void __iomem *np_base = cg->base;
+ struct en_clk_priv *priv;
+ unsigned long flags;
u32 val;
+ priv = dev_get_drvdata(dev);
+
+ spin_lock_irqsave(&priv->lock, flags);
val = readl(np_base + REG_PCI_CONTROL);
val &= ~REG_PCI_CONTROL_REFCLK_EN1;
writel(val, np_base + REG_PCI_CONTROL);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
static struct clk_hw *en7523_register_pcie_clk(struct device *dev,
@@ -660,22 +710,37 @@ static struct clk_hw *en7523_register_pcie_clk(struct device *dev,
static int en7581_pci_is_enabled(struct clk_hw *hw)
{
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
+ struct device *dev = clk_hw_get_dev(hw);
+ struct en_clk_priv *priv;
+ unsigned long flags;
u32 val, mask;
+ priv = dev_get_drvdata(dev);
mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
+
+ spin_lock_irqsave(&priv->lock, flags);
val = readl(cg->base + REG_PCI_CONTROL);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
return (val & mask) == mask;
}
static int en7581_pci_enable(struct clk_hw *hw)
{
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
+ struct device *dev = clk_hw_get_dev(hw);
void __iomem *np_base = cg->base;
+ struct en_clk_priv *priv;
+ unsigned long flags;
u32 val, mask;
+ priv = dev_get_drvdata(dev);
mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
+
+ spin_lock_irqsave(&priv->lock, flags);
val = readl(np_base + REG_PCI_CONTROL);
writel(val | mask, np_base + REG_PCI_CONTROL);
+ spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
@@ -683,12 +748,20 @@ static int en7581_pci_enable(struct clk_hw *hw)
static void en7581_pci_disable(struct clk_hw *hw)
{
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
+ struct device *dev = clk_hw_get_dev(hw);
void __iomem *np_base = cg->base;
+ struct en_clk_priv *priv;
+ unsigned long flags;
u32 val, mask;
+ priv = dev_get_drvdata(dev);
mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
+
+ spin_lock_irqsave(&priv->lock, flags);
val = readl(np_base + REG_PCI_CONTROL);
writel(val & ~mask, np_base + REG_PCI_CONTROL);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
usleep_range(1000, 2000);
}
@@ -788,21 +861,25 @@ static int en7523_reset_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev);
+ struct en_clk_priv *priv = dev_get_drvdata(rcdev->dev);
u32 offset = rst_data->bank_ofs[id / RST_NR_PER_BANK];
void __iomem *addr = rst_data->base + offset;
bool inverted = false;
+ unsigned long flags;
u32 val;
/* For PCIC reset logic is inverted, 0:assert 1:deassert */
if (offset == REG_NP_SCU_PCIC)
inverted = true;
+ spin_lock_irqsave(&priv->lock, flags);
val = readl(addr);
if (assert ^ inverted)
val |= BIT(id % RST_NR_PER_BANK);
else
val &= ~BIT(id % RST_NR_PER_BANK);
writel(val, addr);
+ spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
@@ -823,16 +900,21 @@ static int en7523_reset_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev);
+ struct en_clk_priv *priv = dev_get_drvdata(rcdev->dev);
u32 offset = rst_data->bank_ofs[id / RST_NR_PER_BANK];
void __iomem *addr = rst_data->base + offset;
bool inverted = false;
+ unsigned long flags;
u32 val;
/* For PCIC reset logic is inverted, 0:assert 1:deassert */
if (offset == REG_NP_SCU_PCIC)
inverted = true;
+ spin_lock_irqsave(&priv->lock, flags);
val = readl(addr) & BIT(id % RST_NR_PER_BANK);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
return inverted ? !val : !!val;
}
--
2.55.0