diff mbox series

[v3,9/9] clk: imx: Add the pcc reset controller support on imx8ulp

Message ID 20210914065208.3582128-10-ping.bai@nxp.com
State Accepted
Commit 3fa36200a43f508ee49895e74d86b511fcd8ff3f
Headers show
Series Add imx8ulp clock & reset driver support | expand

Commit Message

Jacky Bai Sept. 14, 2021, 6:52 a.m. UTC
On i.MX8ULP, for some of the PCCs, it has a peripheral SW RST bit
resides in the same registers as the clock controller. So add this
SW RST controller support alongs with the pcc clock initialization.

the reset and clock shared the same register, to avoid  accessing
the same register by reset control and clock control concurrently,
locking is necessary, so reuse the imx_ccm_lock spinlock to simplify
the code.

Suggested-by: Liu Ying <victor.liu@nxp.com>
Signed-off-by: Jacky Bai <ping.bai@nxp.com>
---
  v3 changes:
    - remove the explict selection of 'RESET_CONTROLLER' in kconfig
    - add some comments for the spinlock of reset driver

  v2 changes:
    - add 'Suggested-by' as suggested by Victor Liu
---
 drivers/clk/imx/clk-composite-7ulp.c |  10 +++
 drivers/clk/imx/clk-imx8ulp.c        | 116 ++++++++++++++++++++++++++-
 2 files changed, 123 insertions(+), 3 deletions(-)

Comments

Jacky Bai Sept. 14, 2021, 1:07 p.m. UTC | #1
> Subject: Re: [PATCH v3 9/9] clk: imx: Add the pcc reset controller support on

> imx8ulp

> 

> On 21-09-14 14:52:08, Jacky Bai wrote:

> > diff --git a/drivers/clk/imx/clk-imx8ulp.c

> > b/drivers/clk/imx/clk-imx8ulp.c index 6aad04114658..6699437e17b8

> > 100644

> > --- a/drivers/clk/imx/clk-imx8ulp.c

> > +++ b/drivers/clk/imx/clk-imx8ulp.c

> > @@ -9,6 +9,7 @@

> >  #include <linux/module.h>

> >  #include <linux/of_device.h>

> >  #include <linux/platform_device.h>

> > +#include <linux/reset-controller.h>

> >  #include <linux/slab.h>

> >

> >  #include "clk.h"

> > @@ -48,6 +49,99 @@ static const char * const nic_per_divplat[] = {

> > "nic_per_divplat" };  static const char * const lpav_axi_div[] = {

> > "lpav_axi_div" };  static const char * const lpav_bus_div[] = {

> > "lpav_bus_div" };

> >

> > +struct pcc_reset_dev {

> > +	void __iomem *base;

> > +	struct reset_controller_dev rcdev;

> > +	const u32 *resets;

> > +	/* Set to imx_ccm_lock to protect register access shared with clock

> control */

> > +	spinlock_t *lock;

> > +};

> > +

> > +#define PCC_SW_RST	BIT(28)

> > +#define to_pcc_reset_dev(_rcdev)	container_of(_rcdev, struct

> pcc_reset_dev, rcdev)

> > +

> > +static const u32 pcc3_resets[] = {

> > +	0xa8, 0xac, 0xc8, 0xcc, 0xd0,

> > +	0xd4, 0xd8, 0xdc, 0xe0, 0xe4,

> > +	0xe8, 0xec, 0xf0

> > +};

> > +

> > +static const u32 pcc4_resets[] = {

> > +	0x4, 0x8, 0xc, 0x10, 0x14,

> > +	0x18, 0x1c, 0x20, 0x24, 0x34,

> > +	0x38, 0x3c, 0x40, 0x44, 0x48,

> > +	0x4c, 0x54

> > +};

> > +

> > +static const u32 pcc5_resets[] = {

> > +	0xa0, 0xa4, 0xa8, 0xac, 0xb0,

> > +	0xb4, 0xbc, 0xc0, 0xc8, 0xcc,

> > +	0xd0, 0xf0, 0xf4, 0xf8

> > +};

> > +

> 

> I believe this could be avoided entirely by having something like:

> 

> PCCx_RESETS_OFFSET + (index * 4)

> 

> and then:

> 

> #define PCC3_RESETS_OFFSET	0xa8

> #define PCC4_RESETS_OFFSET	0x4

> #define PCC5_RESETS_OFFSET	0xa0

> 

> #define PCC3_RESETS_NUM		13

> #define PCC4_RESETS_NUM		17

> #define PCC5_RESETS_NUM		14

> 

> Then we could pass on the PCCx_RESETS_OFFSET and the

> PCCx_RESETS_NUM and that's it.

> 


As you can see that the PCC offset that has reset is not consecutive in the register region.
So it will introduce hole in the index. And we can NOT use linear formula to calculate the offset directly.

BR
Jacky Bai

> > +static int imx8ulp_pcc_assert(struct reset_controller_dev *rcdev,

> > +unsigned long id) {

> > +	struct pcc_reset_dev *pcc_reset = to_pcc_reset_dev(rcdev);

> > +	u32 offset = pcc_reset->resets[id];

> > +	unsigned long flags;

> > +	u32 val;

> > +

> > +	spin_lock_irqsave(pcc_reset->lock, flags);

> > +

> > +	val = readl(pcc_reset->base + offset);

> > +	val &= ~PCC_SW_RST;

> > +	writel(val, pcc_reset->base + offset);

> > +

> > +	spin_unlock_irqrestore(pcc_reset->lock, flags);

> > +

> > +	return 0;

> > +}

> > +

> > +static int imx8ulp_pcc_deassert(struct reset_controller_dev *rcdev,

> > +unsigned long id) {

> > +	struct pcc_reset_dev *pcc_reset = to_pcc_reset_dev(rcdev);

> > +	u32 offset = pcc_reset->resets[id];

> > +	unsigned long flags;

> > +	u32 val;

> > +

> > +	spin_lock_irqsave(pcc_reset->lock, flags);

> > +

> > +	val = readl(pcc_reset->base + offset);

> > +	val |= PCC_SW_RST;

> > +	writel(val, pcc_reset->base + offset);

> > +

> > +	spin_unlock_irqrestore(pcc_reset->lock, flags);

> > +

> > +	return 0;

> > +}

> > +

> > +static const struct reset_control_ops imx8ulp_pcc_reset_ops = {

> > +	.assert = imx8ulp_pcc_assert,

> > +	.deassert = imx8ulp_pcc_deassert,

> > +};

> > +

> > +static int imx8ulp_pcc_reset_init(struct platform_device *pdev, void

> __iomem *base,

> > +	 const u32 *resets, unsigned int nr_resets) {

> > +	struct device_node *np = pdev->dev.of_node;

> > +	struct device *dev = &pdev->dev;

> > +	struct pcc_reset_dev *pcc_reset;

> > +

> > +	pcc_reset = devm_kzalloc(dev, sizeof(*pcc_reset), GFP_KERNEL);

> > +	if (!pcc_reset)

> > +		return -ENOMEM;

> > +

> > +	pcc_reset->base = base;

> > +	pcc_reset->lock = &imx_ccm_lock;

> > +	pcc_reset->resets = resets;

> > +	pcc_reset->rcdev.owner = THIS_MODULE;

> > +	pcc_reset->rcdev.nr_resets = nr_resets;

> > +	pcc_reset->rcdev.ops = &imx8ulp_pcc_reset_ops;

> > +	pcc_reset->rcdev.of_node = np;

> > +

> > +	return devm_reset_controller_register(dev, &pcc_reset->rcdev); }

> > +

> 

> ...

> 

> > --

> > 2.26.2

> >
Abel Vesa Sept. 14, 2021, 3:19 p.m. UTC | #2
On 21-09-14 16:07:50, Jacky Bai wrote:
> > Subject: Re: [PATCH v3 9/9] clk: imx: Add the pcc reset controller support on
> > imx8ulp
> > 
> > On 21-09-14 14:52:08, Jacky Bai wrote:
> > > diff --git a/drivers/clk/imx/clk-imx8ulp.c
> > > b/drivers/clk/imx/clk-imx8ulp.c index 6aad04114658..6699437e17b8
> > > 100644
> > > --- a/drivers/clk/imx/clk-imx8ulp.c
> > > +++ b/drivers/clk/imx/clk-imx8ulp.c
> > > @@ -9,6 +9,7 @@
> > >  #include <linux/module.h>
> > >  #include <linux/of_device.h>
> > >  #include <linux/platform_device.h>
> > > +#include <linux/reset-controller.h>
> > >  #include <linux/slab.h>
> > >
> > >  #include "clk.h"
> > > @@ -48,6 +49,99 @@ static const char * const nic_per_divplat[] = {
> > > "nic_per_divplat" };  static const char * const lpav_axi_div[] = {
> > > "lpav_axi_div" };  static const char * const lpav_bus_div[] = {
> > > "lpav_bus_div" };
> > >
> > > +struct pcc_reset_dev {
> > > +	void __iomem *base;
> > > +	struct reset_controller_dev rcdev;
> > > +	const u32 *resets;
> > > +	/* Set to imx_ccm_lock to protect register access shared with clock
> > control */
> > > +	spinlock_t *lock;
> > > +};
> > > +
> > > +#define PCC_SW_RST	BIT(28)
> > > +#define to_pcc_reset_dev(_rcdev)	container_of(_rcdev, struct
> > pcc_reset_dev, rcdev)
> > > +
> > > +static const u32 pcc3_resets[] = {
> > > +	0xa8, 0xac, 0xc8, 0xcc, 0xd0,
> > > +	0xd4, 0xd8, 0xdc, 0xe0, 0xe4,
> > > +	0xe8, 0xec, 0xf0
> > > +};
> > > +
> > > +static const u32 pcc4_resets[] = {
> > > +	0x4, 0x8, 0xc, 0x10, 0x14,
> > > +	0x18, 0x1c, 0x20, 0x24, 0x34,
> > > +	0x38, 0x3c, 0x40, 0x44, 0x48,
> > > +	0x4c, 0x54
> > > +};
> > > +
> > > +static const u32 pcc5_resets[] = {
> > > +	0xa0, 0xa4, 0xa8, 0xac, 0xb0,
> > > +	0xb4, 0xbc, 0xc0, 0xc8, 0xcc,
> > > +	0xd0, 0xf0, 0xf4, 0xf8
> > > +};
> > > +
> > 
> > I believe this could be avoided entirely by having something like:
> > 
> > PCCx_RESETS_OFFSET + (index * 4)
> > 
> > and then:
> > 
> > #define PCC3_RESETS_OFFSET	0xa8
> > #define PCC4_RESETS_OFFSET	0x4
> > #define PCC5_RESETS_OFFSET	0xa0
> > 
> > #define PCC3_RESETS_NUM		13
> > #define PCC4_RESETS_NUM		17
> > #define PCC5_RESETS_NUM		14
> > 
> > Then we could pass on the PCCx_RESETS_OFFSET and the
> > PCCx_RESETS_NUM and that's it.
> > 
> 
> As you can see that the PCC offset that has reset is not consecutive in the register region.
> So it will introduce hole in the index. And we can NOT use linear formula to calculate the offset directly.
> 

You are right, they don't seem to be consecutive, on a closer look.

I don't have any other comments, so:

Reviewed-by: Abel Vesa <abel.vesa@nxp.com>

> BR
> Jacky Bai
> 
> > > +static int imx8ulp_pcc_assert(struct reset_controller_dev *rcdev,
> > > +unsigned long id) {
> > > +	struct pcc_reset_dev *pcc_reset = to_pcc_reset_dev(rcdev);
> > > +	u32 offset = pcc_reset->resets[id];
> > > +	unsigned long flags;
> > > +	u32 val;
> > > +
> > > +	spin_lock_irqsave(pcc_reset->lock, flags);
> > > +
> > > +	val = readl(pcc_reset->base + offset);
> > > +	val &= ~PCC_SW_RST;
> > > +	writel(val, pcc_reset->base + offset);
> > > +
> > > +	spin_unlock_irqrestore(pcc_reset->lock, flags);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int imx8ulp_pcc_deassert(struct reset_controller_dev *rcdev,
> > > +unsigned long id) {
> > > +	struct pcc_reset_dev *pcc_reset = to_pcc_reset_dev(rcdev);
> > > +	u32 offset = pcc_reset->resets[id];
> > > +	unsigned long flags;
> > > +	u32 val;
> > > +
> > > +	spin_lock_irqsave(pcc_reset->lock, flags);
> > > +
> > > +	val = readl(pcc_reset->base + offset);
> > > +	val |= PCC_SW_RST;
> > > +	writel(val, pcc_reset->base + offset);
> > > +
> > > +	spin_unlock_irqrestore(pcc_reset->lock, flags);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct reset_control_ops imx8ulp_pcc_reset_ops = {
> > > +	.assert = imx8ulp_pcc_assert,
> > > +	.deassert = imx8ulp_pcc_deassert,
> > > +};
> > > +
> > > +static int imx8ulp_pcc_reset_init(struct platform_device *pdev, void
> > __iomem *base,
> > > +	 const u32 *resets, unsigned int nr_resets) {
> > > +	struct device_node *np = pdev->dev.of_node;
> > > +	struct device *dev = &pdev->dev;
> > > +	struct pcc_reset_dev *pcc_reset;
> > > +
> > > +	pcc_reset = devm_kzalloc(dev, sizeof(*pcc_reset), GFP_KERNEL);
> > > +	if (!pcc_reset)
> > > +		return -ENOMEM;
> > > +
> > > +	pcc_reset->base = base;
> > > +	pcc_reset->lock = &imx_ccm_lock;
> > > +	pcc_reset->resets = resets;
> > > +	pcc_reset->rcdev.owner = THIS_MODULE;
> > > +	pcc_reset->rcdev.nr_resets = nr_resets;
> > > +	pcc_reset->rcdev.ops = &imx8ulp_pcc_reset_ops;
> > > +	pcc_reset->rcdev.of_node = np;
> > > +
> > > +	return devm_reset_controller_register(dev, &pcc_reset->rcdev); }
> > > +
> > 
> > ...
> > 
> > > --
> > > 2.26.2
> > >
diff mbox series

Patch

diff --git a/drivers/clk/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c
index 9ce8c630ee32..89106de16a3f 100644
--- a/drivers/clk/imx/clk-composite-7ulp.c
+++ b/drivers/clk/imx/clk-composite-7ulp.c
@@ -29,6 +29,7 @@ 
 static int pcc_gate_enable(struct clk_hw *hw)
 {
 	struct clk_gate *gate = to_clk_gate(hw);
+	unsigned long flags;
 	u32 val;
 	int ret;
 
@@ -36,6 +37,7 @@  static int pcc_gate_enable(struct clk_hw *hw)
 	if (ret)
 		return ret;
 
+	spin_lock_irqsave(gate->lock, flags);
 	/*
 	 * release the sw reset for peripherals associated with
 	 * with this pcc clock.
@@ -44,6 +46,8 @@  static int pcc_gate_enable(struct clk_hw *hw)
 	val |= SW_RST;
 	writel(val, gate->reg);
 
+	spin_unlock_irqrestore(gate->lock, flags);
+
 	return 0;
 }
 
@@ -84,6 +88,8 @@  static struct clk_hw *imx_ulp_clk_hw_composite(const char *name,
 		mux->reg = reg;
 		mux->shift = PCG_PCS_SHIFT;
 		mux->mask = PCG_PCS_MASK;
+		if (has_swrst)
+			mux->lock = &imx_ccm_lock;
 	}
 
 	if (rate_present) {
@@ -101,6 +107,8 @@  static struct clk_hw *imx_ulp_clk_hw_composite(const char *name,
 		fd->nwidth = PCG_PCD_WIDTH;
 		fd->nmask = PCG_PCD_MASK;
 		fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED;
+		if (has_swrst)
+			fd->lock = &imx_ccm_lock;
 	}
 
 	if (gate_present) {
@@ -113,6 +121,8 @@  static struct clk_hw *imx_ulp_clk_hw_composite(const char *name,
 		gate_hw = &gate->hw;
 		gate->reg = reg;
 		gate->bit_idx = PCG_CGC_SHIFT;
+		if (has_swrst)
+			gate->lock = &imx_ccm_lock;
 		/*
 		 * make sure clock is gated during clock tree initialization,
 		 * the HW ONLY allow clock parent/rate changed with clock gated,
diff --git a/drivers/clk/imx/clk-imx8ulp.c b/drivers/clk/imx/clk-imx8ulp.c
index 6aad04114658..6699437e17b8 100644
--- a/drivers/clk/imx/clk-imx8ulp.c
+++ b/drivers/clk/imx/clk-imx8ulp.c
@@ -9,6 +9,7 @@ 
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/reset-controller.h>
 #include <linux/slab.h>
 
 #include "clk.h"
@@ -48,6 +49,99 @@  static const char * const nic_per_divplat[] = { "nic_per_divplat" };
 static const char * const lpav_axi_div[] = { "lpav_axi_div" };
 static const char * const lpav_bus_div[] = { "lpav_bus_div" };
 
+struct pcc_reset_dev {
+	void __iomem *base;
+	struct reset_controller_dev rcdev;
+	const u32 *resets;
+	/* Set to imx_ccm_lock to protect register access shared with clock control */
+	spinlock_t *lock;
+};
+
+#define PCC_SW_RST	BIT(28)
+#define to_pcc_reset_dev(_rcdev)	container_of(_rcdev, struct pcc_reset_dev, rcdev)
+
+static const u32 pcc3_resets[] = {
+	0xa8, 0xac, 0xc8, 0xcc, 0xd0,
+	0xd4, 0xd8, 0xdc, 0xe0, 0xe4,
+	0xe8, 0xec, 0xf0
+};
+
+static const u32 pcc4_resets[] = {
+	0x4, 0x8, 0xc, 0x10, 0x14,
+	0x18, 0x1c, 0x20, 0x24, 0x34,
+	0x38, 0x3c, 0x40, 0x44, 0x48,
+	0x4c, 0x54
+};
+
+static const u32 pcc5_resets[] = {
+	0xa0, 0xa4, 0xa8, 0xac, 0xb0,
+	0xb4, 0xbc, 0xc0, 0xc8, 0xcc,
+	0xd0, 0xf0, 0xf4, 0xf8
+};
+
+static int imx8ulp_pcc_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+	struct pcc_reset_dev *pcc_reset = to_pcc_reset_dev(rcdev);
+	u32 offset = pcc_reset->resets[id];
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(pcc_reset->lock, flags);
+
+	val = readl(pcc_reset->base + offset);
+	val &= ~PCC_SW_RST;
+	writel(val, pcc_reset->base + offset);
+
+	spin_unlock_irqrestore(pcc_reset->lock, flags);
+
+	return 0;
+}
+
+static int imx8ulp_pcc_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+	struct pcc_reset_dev *pcc_reset = to_pcc_reset_dev(rcdev);
+	u32 offset = pcc_reset->resets[id];
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(pcc_reset->lock, flags);
+
+	val = readl(pcc_reset->base + offset);
+	val |= PCC_SW_RST;
+	writel(val, pcc_reset->base + offset);
+
+	spin_unlock_irqrestore(pcc_reset->lock, flags);
+
+	return 0;
+}
+
+static const struct reset_control_ops imx8ulp_pcc_reset_ops = {
+	.assert = imx8ulp_pcc_assert,
+	.deassert = imx8ulp_pcc_deassert,
+};
+
+static int imx8ulp_pcc_reset_init(struct platform_device *pdev, void __iomem *base,
+	 const u32 *resets, unsigned int nr_resets)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	struct pcc_reset_dev *pcc_reset;
+
+	pcc_reset = devm_kzalloc(dev, sizeof(*pcc_reset), GFP_KERNEL);
+	if (!pcc_reset)
+		return -ENOMEM;
+
+	pcc_reset->base = base;
+	pcc_reset->lock = &imx_ccm_lock;
+	pcc_reset->resets = resets;
+	pcc_reset->rcdev.owner = THIS_MODULE;
+	pcc_reset->rcdev.nr_resets = nr_resets;
+	pcc_reset->rcdev.ops = &imx8ulp_pcc_reset_ops;
+	pcc_reset->rcdev.of_node = np;
+
+	return devm_reset_controller_register(dev, &pcc_reset->rcdev);
+}
+
 static int imx8ulp_clk_cgc1_init(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -288,10 +382,13 @@  static int imx8ulp_clk_pcc3_init(struct platform_device *pdev)
 	imx_check_clk_hws(clks, clk_data->num);
 
 	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+	if (ret)
+		return ret;
 
 	imx_register_uart_clocks(1);
 
-	return ret;
+	/* register the pcc3 reset controller */
+	return imx8ulp_pcc_reset_init(pdev, base, pcc3_resets, ARRAY_SIZE(pcc3_resets));
 }
 
 static int imx8ulp_clk_pcc4_init(struct platform_device *pdev)
@@ -300,6 +397,7 @@  static int imx8ulp_clk_pcc4_init(struct platform_device *pdev)
 	struct clk_hw_onecell_data *clk_data;
 	struct clk_hw **clks;
 	void __iomem *base;
+	int ret;
 
 	clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_PCC4_END),
 			   GFP_KERNEL);
@@ -339,7 +437,13 @@  static int imx8ulp_clk_pcc4_init(struct platform_device *pdev)
 
 	imx_check_clk_hws(clks, clk_data->num);
 
-	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+	if (ret)
+		return ret;
+
+	/* register the pcc4 reset controller */
+	return imx8ulp_pcc_reset_init(pdev, base, pcc4_resets, ARRAY_SIZE(pcc4_resets));
+
 }
 
 static int imx8ulp_clk_pcc5_init(struct platform_device *pdev)
@@ -348,6 +452,7 @@  static int imx8ulp_clk_pcc5_init(struct platform_device *pdev)
 	struct clk_hw_onecell_data *clk_data;
 	struct clk_hw **clks;
 	void __iomem *base;
+	int ret;
 
 	clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_PCC5_END),
 			   GFP_KERNEL);
@@ -420,7 +525,12 @@  static int imx8ulp_clk_pcc5_init(struct platform_device *pdev)
 
 	imx_check_clk_hws(clks, clk_data->num);
 
-	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
+	if (ret)
+		return ret;
+
+	/* register the pcc5 reset controller */
+	return imx8ulp_pcc_reset_init(pdev, base, pcc5_resets, ARRAY_SIZE(pcc5_resets));
 }
 
 static int imx8ulp_clk_probe(struct platform_device *pdev)