Message ID | 1599556487-31364-1-git-send-email-peng.fan@nxp.com (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
Series | clk: imx: lpcg-scu: SW workaround for errata (e10858) | expand |
On 20-09-08 17:14:47, peng.fan@nxp.com wrote: > From: Peng Fan <peng.fan@nxp.com> > > Back-to-back LPCG writes can be ignored by the LPCG register due to > a HW bug. The writes need to be separated by at least 4 cycles of > the gated clock. > > The workaround is implemented as follows: > 1. For clocks running greater than or equal to 24MHz, a read > followed by the write will provide sufficient delay. > 2. For clocks running below 24MHz, add a delay of 4 clock cylces > after the write to the LPCG register. > > Signed-off-by: Peng Fan <peng.fan@nxp.com> > --- > drivers/clk/imx/clk-lpcg-scu.c | 31 +++++++++++++++++++++++++++++-- > 1 file changed, 29 insertions(+), 2 deletions(-) > > diff --git a/drivers/clk/imx/clk-lpcg-scu.c b/drivers/clk/imx/clk-lpcg-scu.c > index 1f0e44f921ae..6ee9d2caedf2 100644 > --- a/drivers/clk/imx/clk-lpcg-scu.c > +++ b/drivers/clk/imx/clk-lpcg-scu.c > @@ -6,6 +6,7 @@ > > #include <linux/bits.h> > #include <linux/clk-provider.h> > +#include <linux/delay.h> > #include <linux/err.h> > #include <linux/io.h> > #include <linux/slab.h> > @@ -38,6 +39,31 @@ struct clk_lpcg_scu { > > #define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw) > > +/* e10858 -LPCG clock gating register synchronization errata */ > +static void do_lpcg_workaround(u32 rate, void __iomem *reg, u32 val) > +{ > + writel(val, reg); > + > + if (rate >= 24000000 || rate == 0) { > + u32 reg1; > + > + /* > + * The time taken to access the LPCG registers from the AP core > + * through the interconnect is longer than the minimum delay > + * of 4 clock cycles required by the errata. > + * Adding a readl will provide sufficient delay to prevent > + * back-to-back writes. > + */ > + reg1 = readl(reg); > + } else { > + /* > + * For clocks running below 24MHz, wait a minimum of > + * 4 clock cycles. > + */ > + ndelay(4 * (DIV_ROUND_UP(1000000000, rate))); > + } Just to make sure this is totally understood, if the lpcg consumer needs to two enables/disables in less than 4 multiplied by the clock period, the second enable/disable will be delayed. If we're fine with this, then here is my R-b. Reviewed-by: Abel Vesa <abel.vesa@nxp.com> > +} > + > static int clk_lpcg_scu_enable(struct clk_hw *hw) > { > struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw); > @@ -54,7 +80,8 @@ static int clk_lpcg_scu_enable(struct clk_hw *hw) > val |= CLK_GATE_SCU_LPCG_HW_SEL; > > reg |= val << clk->bit_idx; > - writel(reg, clk->reg); > + > + do_lpcg_workaround(clk_hw_get_rate(hw), clk->reg, reg); > > spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags); > > @@ -71,7 +98,7 @@ static void clk_lpcg_scu_disable(struct clk_hw *hw) > > reg = readl_relaxed(clk->reg); > reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx); > - writel(reg, clk->reg); > + do_lpcg_workaround(clk_hw_get_rate(hw), clk->reg, reg); > > spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags); > } > -- > 2.28.0 >
> Subject: Re: [PATCH] clk: imx: lpcg-scu: SW workaround for errata (e10858) > > On 20-09-08 17:14:47, peng.fan@nxp.com wrote: > > From: Peng Fan <peng.fan@nxp.com> > > > > Back-to-back LPCG writes can be ignored by the LPCG register due to a > > HW bug. The writes need to be separated by at least 4 cycles of the > > gated clock. > > > > The workaround is implemented as follows: > > 1. For clocks running greater than or equal to 24MHz, a read followed > > by the write will provide sufficient delay. > > 2. For clocks running below 24MHz, add a delay of 4 clock cylces after > > the write to the LPCG register. > > > > Signed-off-by: Peng Fan <peng.fan@nxp.com> > > --- > > drivers/clk/imx/clk-lpcg-scu.c | 31 +++++++++++++++++++++++++++++-- > > 1 file changed, 29 insertions(+), 2 deletions(-) > > > > diff --git a/drivers/clk/imx/clk-lpcg-scu.c > > b/drivers/clk/imx/clk-lpcg-scu.c index 1f0e44f921ae..6ee9d2caedf2 > > 100644 > > --- a/drivers/clk/imx/clk-lpcg-scu.c > > +++ b/drivers/clk/imx/clk-lpcg-scu.c > > @@ -6,6 +6,7 @@ > > > > #include <linux/bits.h> > > #include <linux/clk-provider.h> > > +#include <linux/delay.h> > > #include <linux/err.h> > > #include <linux/io.h> > > #include <linux/slab.h> > > @@ -38,6 +39,31 @@ struct clk_lpcg_scu { > > > > #define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, > > hw) > > > > +/* e10858 -LPCG clock gating register synchronization errata */ > > +static void do_lpcg_workaround(u32 rate, void __iomem *reg, u32 val) > > +{ > > + writel(val, reg); > > + > > + if (rate >= 24000000 || rate == 0) { > > + u32 reg1; > > + > > + /* > > + * The time taken to access the LPCG registers from the AP core > > + * through the interconnect is longer than the minimum delay > > + * of 4 clock cycles required by the errata. > > + * Adding a readl will provide sufficient delay to prevent > > + * back-to-back writes. > > + */ > > + reg1 = readl(reg); > > + } else { > > + /* > > + * For clocks running below 24MHz, wait a minimum of > > + * 4 clock cycles. > > + */ > > + ndelay(4 * (DIV_ROUND_UP(1000000000, rate))); > > + } > > Just to make sure this is totally understood, if the lpcg consumer needs to two > enables/disables in less than 4 multiplied by the clock period, the second > enable/disable will be delayed. You surely wanna this following errata description. Thanks, Peng. > > If we're fine with this, then here is my R-b. > > Reviewed-by: Abel Vesa <abel.vesa@nxp.com> > > > +} > > + > > static int clk_lpcg_scu_enable(struct clk_hw *hw) { > > struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw); @@ -54,7 +80,8 @@ > > static int clk_lpcg_scu_enable(struct clk_hw *hw) > > val |= CLK_GATE_SCU_LPCG_HW_SEL; > > > > reg |= val << clk->bit_idx; > > - writel(reg, clk->reg); > > + > > + do_lpcg_workaround(clk_hw_get_rate(hw), clk->reg, reg); > > > > spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags); > > > > @@ -71,7 +98,7 @@ static void clk_lpcg_scu_disable(struct clk_hw *hw) > > > > reg = readl_relaxed(clk->reg); > > reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx); > > - writel(reg, clk->reg); > > + do_lpcg_workaround(clk_hw_get_rate(hw), clk->reg, reg); > > > > spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags); } > > -- > > 2.28.0 > >
diff --git a/drivers/clk/imx/clk-lpcg-scu.c b/drivers/clk/imx/clk-lpcg-scu.c index 1f0e44f921ae..6ee9d2caedf2 100644 --- a/drivers/clk/imx/clk-lpcg-scu.c +++ b/drivers/clk/imx/clk-lpcg-scu.c @@ -6,6 +6,7 @@ #include <linux/bits.h> #include <linux/clk-provider.h> +#include <linux/delay.h> #include <linux/err.h> #include <linux/io.h> #include <linux/slab.h> @@ -38,6 +39,31 @@ struct clk_lpcg_scu { #define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw) +/* e10858 -LPCG clock gating register synchronization errata */ +static void do_lpcg_workaround(u32 rate, void __iomem *reg, u32 val) +{ + writel(val, reg); + + if (rate >= 24000000 || rate == 0) { + u32 reg1; + + /* + * The time taken to access the LPCG registers from the AP core + * through the interconnect is longer than the minimum delay + * of 4 clock cycles required by the errata. + * Adding a readl will provide sufficient delay to prevent + * back-to-back writes. + */ + reg1 = readl(reg); + } else { + /* + * For clocks running below 24MHz, wait a minimum of + * 4 clock cycles. + */ + ndelay(4 * (DIV_ROUND_UP(1000000000, rate))); + } +} + static int clk_lpcg_scu_enable(struct clk_hw *hw) { struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw); @@ -54,7 +80,8 @@ static int clk_lpcg_scu_enable(struct clk_hw *hw) val |= CLK_GATE_SCU_LPCG_HW_SEL; reg |= val << clk->bit_idx; - writel(reg, clk->reg); + + do_lpcg_workaround(clk_hw_get_rate(hw), clk->reg, reg); spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags); @@ -71,7 +98,7 @@ static void clk_lpcg_scu_disable(struct clk_hw *hw) reg = readl_relaxed(clk->reg); reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx); - writel(reg, clk->reg); + do_lpcg_workaround(clk_hw_get_rate(hw), clk->reg, reg); spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags); }