Messages in this thread | | | From | Aisheng Dong <> | Subject | RE: [PATCH V2 07/10] clk: imx: add mux ops for i.MX8M composite clk | Date | Sun, 26 Apr 2020 05:08:23 +0000 |
| |
> From: Peng Fan <peng.fan@nxp.com> > Sent: Thursday, March 12, 2020 6:20 PM > > The CORE/BUS root slice has following design, simplied graph: > The difference is core not have pre_div block. > A composite core/bus clk has 8 inputs for mux to select, saying clk[0-7]. > > SEL_A GA > +--+ +-+ > | +->+ +------+ > CLK[0-7]--->+ | +-+ | > | | | +----v---+ +----+ > | +--+ |pre_diva+----> | +---------+ > | +--------+ |mux +--+post_div | > | +--+ |pre_divb+--->+ | +---------+ > | | | +----^---+ +----+ > +--->+ | +-+ | > | +->+ +------+ > +--+ +-+ > SEL_B GB > > There will be system hang, when doing the following steps: > 1. switch mux from clk0 to clk1 > 2. gate off clk0 > 3. swtich from clk1 to clk2, or gate off clk1 > > Step 3 triggers system hang.
Why Step 3 triggers system hang? Is this a HW limitation?
> > If we skip step2, keep clk0 on, step 3 will not trigger system hang. > However we have CLK_OPS_PARENT_ENABLE flag, which will unprepare disable > the clk0 which will not be used. > > To address this issue, we could use following simplied software flow: > After the first target register set > wait the target register set finished > set the target register set again > wait the target register set finished > > The upper flow will make sure SEL_A and SEL_B both set the new mux, but with > only one path gate on. > > And there will be no system hang anymore with step3.
Is this IC proposed solution?
> > Signed-off-by: Peng Fan <peng.fan@nxp.com> > --- > > V2: > Drop wait after write, add one line comment for write twice. > > drivers/clk/imx/clk-composite-8m.c | 62 > +++++++++++++++++++++++++++++++++++++- > 1 file changed, 61 insertions(+), 1 deletion(-) > > diff --git a/drivers/clk/imx/clk-composite-8m.c > b/drivers/clk/imx/clk-composite-8m.c > index 99773519b5a5..eae02c151ced 100644 > --- a/drivers/clk/imx/clk-composite-8m.c > +++ b/drivers/clk/imx/clk-composite-8m.c > @@ -24,6 +24,12 @@ > > #define PCG_CGC_SHIFT 28 > > +#define PRE_REG_OFF 0x30 > +#define PRE_MUXA_SHIFT 24 > +#define PRE_MUXA_MASK 0x7 > +#define PRE_MUXB_SHIFT 8 > +#define PRE_MUXB_MASK 0x7
Are those macros used somewhere?
> + > static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk_hw > *hw, > unsigned long parent_rate) > { > @@ -124,6 +130,57 @@ static const struct clk_ops > imx8m_clk_composite_divider_ops = { > .set_rate = imx8m_clk_composite_divider_set_rate, > }; > > +static u8 imx8m_clk_composite_mux_get_parent(struct clk_hw *hw) { > + struct clk_mux *mux = to_clk_mux(hw); > + u32 val; > + > + val = readl(mux->reg) >> mux->shift; > + val &= mux->mask; > + > + return clk_mux_val_to_index(hw, mux->table, mux->flags, val); }
You don't have to redefinition them if they're the same as clk_mux_ops. You have the access to clk_mux_ops.
> + > +static int imx8m_clk_composite_mux_set_parent(struct clk_hw *hw, u8 > +index) { > + struct clk_mux *mux = to_clk_mux(hw); > + u32 val = clk_mux_index_to_val(mux->table, mux->flags, index); > + unsigned long flags = 0; > + u32 reg; > + > + if (mux->lock) > + spin_lock_irqsave(mux->lock, flags); > + > + reg = readl(mux->reg); > + reg &= ~(mux->mask << mux->shift); > + val = val << mux->shift; > + reg |= val; > + /* write twice to make sure SEL_A/B point the same mux */ > + writel(reg, mux->reg); > + writel(reg, mux->reg);
Why this affects both SEL_A/B? Very tricky and may worth more comments.
Besides that, I'd like to see Abel's comments on this patch.
Regards Aisheng
> + > + if (mux->lock) > + spin_unlock_irqrestore(mux->lock, flags); > + > + return 0; > +} > + > +static int > +imx8m_clk_composite_mux_determine_rate(struct clk_hw *hw, > + struct clk_rate_request *req) { > + struct clk_mux *mux = to_clk_mux(hw); > + > + return clk_mux_determine_rate_flags(hw, req, mux->flags); }
Same as bove.
> + > + > +const struct clk_ops imx8m_clk_composite_mux_ops = { > + .get_parent = imx8m_clk_composite_mux_get_parent, > + .set_parent = imx8m_clk_composite_mux_set_parent, > + .determine_rate = imx8m_clk_composite_mux_determine_rate, > +}; > + > struct clk_hw *imx8m_clk_hw_composite_flags(const char *name, > const char * const *parent_names, > int num_parents, void __iomem *reg, @@ -136,6 > +193,7 @@ struct clk_hw *imx8m_clk_hw_composite_flags(const char *name, > struct clk_gate *gate = NULL; > struct clk_mux *mux = NULL; > const struct clk_ops *divider_ops; > + const struct clk_ops *mux_ops; > > mux = kzalloc(sizeof(*mux), GFP_KERNEL); > if (!mux) > @@ -157,10 +215,12 @@ struct clk_hw > *imx8m_clk_hw_composite_flags(const char *name, > div->shift = PCG_DIV_SHIFT; > div->width = PCG_CORE_DIV_WIDTH; > divider_ops = &clk_divider_ops; > + mux_ops = &imx8m_clk_composite_mux_ops; > } else { > div->shift = PCG_PREDIV_SHIFT; > div->width = PCG_PREDIV_WIDTH; > divider_ops = &imx8m_clk_composite_divider_ops; > + mux_ops = &clk_mux_ops; > } > > div->lock = &imx_ccm_lock; > @@ -176,7 +236,7 @@ struct clk_hw *imx8m_clk_hw_composite_flags(const > char *name, > gate->lock = &imx_ccm_lock; > > hw = clk_hw_register_composite(NULL, name, parent_names, > num_parents, > - mux_hw, &clk_mux_ops, div_hw, > + mux_hw, mux_ops, div_hw, > divider_ops, gate_hw, &clk_gate_ops, flags); > if (IS_ERR(hw)) > goto fail; > -- > 2.16.4
| |