[linux-sunxi] [PATCH 1/3] clk: sunxi: Add a driver for the CCU
Ondřej Jirman
megi at xff.cz
Wed Jun 29 08:30:51 PDT 2016
On 29.6.2016 08:51, Jean-Francois Moine wrote:
> On Tue, 28 Jun 2016 21:31:06 +0200
> Ondřej Jirman <megi at xff.cz> wrote:
>
>>
>> On 28.6.2016 17:37, Jean-Francois Moine wrote:
>>> Most of the clocks in the Allwinner's SoCs are configured in the CCU
>>> (Clock Configuration Unit).
>>>
>>> The PLL clocks are driven from the main clock. Their rates are controlled
>>> by a set of multiply and divide factors, named from the Allwinner's
>>> documentation:
>>> - multipliers: 'n' and 'k'
>>> - dividers: 'd1', 'd2', 'm' and 'p'
>>>
>>> The peripheral clocks may receive their inputs from one or more parents,
>>> thanks to a mux. Their rates are controlled by a set of divide factors
>>> only, named 'm' and 'p'.
>>>
>>> This driver also handles:
>>> - fixed clocks,
>>> - the phase delays for the MMCs,
>>> - the clock gates,
>>> - the bus gates,
>>> - and the resets.
>>>
>>> Signed-off-by: Jean-Francois Moine <moinejf at free.fr>
>>> ---
>>> drivers/clk/sunxi/Makefile | 2 +
>>> drivers/clk/sunxi/ccu.c | 980 +++++++++++++++++++++++++++++++++++++++++++++
>>> drivers/clk/sunxi/ccu.h | 153 +++++++
>>> 3 files changed, 1135 insertions(+)
>>> create mode 100644 drivers/clk/sunxi/ccu.c
>>> create mode 100644 drivers/clk/sunxi/ccu.h
> [snip]
>>> diff --git a/drivers/clk/sunxi/ccu.c b/drivers/clk/sunxi/ccu.c
>>> new file mode 100644
>>> index 0000000..5749f9c
>>> --- /dev/null
>>> +++ b/drivers/clk/sunxi/ccu.c
> [snip]
>>> +static void ccu_pll_set_flat_factors(struct ccu *ccu, u32 mask, u32 val)
>>> +{
>>> + u32 reg, m_val, p_val;
>>> + u32 m_mask = (1 << ccu->m_width) - 1;
>>> + u32 p_mask = (1 << ccu->p_width) - 1;
>>
>> I see you have shifted the factor values to be set in the caller of this
>> function, but then you have to shift masks too, to match. It might be
>> clearer to do both shifts in this function.
>
> Oops, yes. Thanks. (I did not test yet your patch about the CPU
> frequency)
>
>>> + reg = readl(ccu->base + ccu->reg);
>>> + m_val = reg & m_mask;
>>> + p_val = reg & p_mask;
>>> +
>>> + spin_lock(&ccu_lock);
>>> +
>>> + /* increase p, then m */
>>> + if (ccu->p_width && p_val < (val & p_mask)) {
>>> + reg &= ~p_mask;
>>> + reg |= val & p_mask;
>>> + writel(reg, ccu->base + ccu->reg);
>>> + udelay(10);
>>> + }
>>> + if (ccu->m_width && m_val < (val & m_mask)) {
>>> + reg &= ~m_mask;
>>> + reg |= val & m_mask;
>>> + writel(reg, ccu->base + ccu->reg);
>>> + udelay(10);
>>> + }
>>> +
>>> + /* set other factors */
>>> + reg &= ~(mask & ~(p_mask | m_mask));
>>> + reg |= val & ~(p_mask | m_mask);
>>> + writel(reg, ccu->base + ccu->reg);
>>> +
>>> + /* decrease m */
>>> + if (ccu->m_width && m_val > (val & m_mask)) {
>>> + reg &= ~m_mask;
>>> + reg |= val & m_mask;
>>> + writel(reg, ccu->base + ccu->reg);
>>> + udelay(10);
>>> + }
>>> +
>>> + /* wait for PLL stable */
>>> + if (ccu->lock_reg) {
>>> + u32 lock;
>>> +
>>> + lock = BIT(ccu->lock_bit);
>>> + WARN_ON(readl_relaxed_poll_timeout(ccu->base + ccu->lock_reg,
>>> + reg, !(reg & lock),
>>> + 100, 70000));
>>> + }
> [snip]
>>
>> Any reason why you're waiting for the lock after decreasing m and not
>> before? PLL output may till run too fast before reducing the postdivider
>> potentially causing a crash. But I might be misunderstanding what m is
>> exactly.
>
> I just followed what is done in the function
> sunxi_clk_factors_set_flat_facotrs() from Allwinnertech:
>
> 1).try to increase factor p first
> 2).try to increase factor m first
> 3. write factor n & k
> 4. do pair things for 2). decease factor m
> 5. wait for PLL state stable
> 6.do pair things for 1). decease factor p
>
> As this is written, it seems m is a pre-divider.
>
Interesting. :) I've reverse engineered their arisc code, and there the
order for setting up/changing PLL1 is:
1. try to increase factor p first
2. try to increase factor m first
3. write factor n & k
4. wait for PLL state stable
5. do pair things for 2). decease factor m
6. do pair things for 1). decease factor p
That's why I asked.
So all this depends on the design of this clock source. It may be
different for different PLLs.
Hard to know what to believe. Perhaps it would be possible to create a
PLL stress tester that would try both approaches and see whether one of
them is more stable/leads to creashes?
regards,
Ondrej
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160629/24e0a893/attachment-0001.sig>
More information about the linux-arm-kernel
mailing list