2020-07-15 12:02:45

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 00/19] clk: at91: add sama7g5 clock support

Hi,

This series adds clock support for SAMA7G5. The first patches in
series, patches 1/19-9/19, contains some fixes (let me know if
you want to send them as a separate series).

For SAMA7G5 clock support some AT91 clock drivers needed changes
because:
1/ some of generated, master and peripheral clocks could have
changeable parents (being able to request frequency changes
from parent)
2/ generated and programmable clocks parents needed a mux table
as the hardware parent index doesn't correspond with software
parent index
3/ there are 4 new master clocks, MCK1..4 (compared with previous
AT91 architectures) which are controlled separately from MCK0
4/ some of the PLLs have 2 outputs the internal block schema being
as follows:

+------+ +--------+
| FRAC |-----+----->| DIVPMC |--->
+------+ | +--------+
|
| +--------+
+----->| DIVIO |--->
+--------+

For this, the clk-sam9x60-pll driver has been re-factored.

Claudiu Beznea (19):
clk: at91: clk-generated: continue if __clk_determine_rate() returns
error
clk: at91: clk-generated: check best_rate against ranges
clk: at91: clk-sam9x60-pll: fix mul mask
clk: at91: sam9x60-pll: use frac when computing pll frequency
clk: at91: sam9x60-pll: use logical or for range check
clk: at91: sam9x60-pll: check fcore against ranges
clk: at91: sam9x60-pll: use frac when setting frequency
clk: at91: sam9x60: fix main rc oscillator frequency
clk: at91: sckc: register slow_rc with accuracy option
clk: at91: replace conditional operator with double logical not
clk: at91: clk-generated: pass the id of changeable parent at
registration
clk: at91: clk-generated: add mux_table option
clk: at91: clk-master: add master clock support for SAMA7G5
clk: at91: clk-peripheral: add support for changeable parent rate
clk: at91: clk-programmable: add mux_table option
clk: at91: add macro for pll ids mask
clk: at91: clk-sam9x60-pll: re-factor to support plls with multiple
outputs
clk: at91: clk-utmi: add utmi support for sama7g5
clk: at91: sama7g5: add clock support for sama7g5

drivers/clk/at91/Makefile | 1 +
drivers/clk/at91/at91rm9200.c | 3 +-
drivers/clk/at91/at91sam9260.c | 3 +-
drivers/clk/at91/at91sam9g45.c | 3 +-
drivers/clk/at91/at91sam9n12.c | 5 +-
drivers/clk/at91/at91sam9rl.c | 3 +-
drivers/clk/at91/at91sam9x5.c | 7 +-
drivers/clk/at91/clk-generated.c | 44 +-
drivers/clk/at91/clk-main.c | 6 +-
drivers/clk/at91/clk-master.c | 310 +++++++++-
drivers/clk/at91/clk-peripheral.c | 111 +++-
drivers/clk/at91/clk-programmable.c | 11 +-
drivers/clk/at91/clk-sam9x60-pll.c | 547 ++++++++++++------
drivers/clk/at91/clk-system.c | 4 +-
drivers/clk/at91/clk-utmi.c | 103 +++-
drivers/clk/at91/dt-compat.c | 25 +-
drivers/clk/at91/pmc.h | 43 +-
drivers/clk/at91/sam9x60.c | 64 ++-
drivers/clk/at91/sama5d2.c | 41 +-
drivers/clk/at91/sama5d3.c | 6 +-
drivers/clk/at91/sama5d4.c | 7 +-
drivers/clk/at91/sama7g5.c | 1059 +++++++++++++++++++++++++++++++++++
drivers/clk/at91/sckc.c | 5 +-
include/linux/clk/at91_pmc.h | 4 +
24 files changed, 2140 insertions(+), 275 deletions(-)
create mode 100644 drivers/clk/at91/sama7g5.c

--
2.7.4


2020-07-15 12:02:58

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 02/19] clk: at91: clk-generated: check best_rate against ranges

Check best_rate against available clock ranges.

Fixes: df70aeef6083 ("clk: at91: add generated clock driver")
Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-generated.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index 995a13133cfb..f8e557e0e1b8 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -185,8 +185,8 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
__clk_get_name((req->best_parent_hw)->clk),
req->best_parent_rate);

- if (best_rate < 0)
- return best_rate;
+ if (best_rate < 0 || (gck->range.max && best_rate > gck->range.max))
+ return -EINVAL;

req->rate = best_rate;
return 0;
--
2.7.4

2020-07-15 12:04:12

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 03/19] clk: at91: clk-sam9x60-pll: fix mul mask

According to datasheet mul mask is on bits 31..24.

Fixes: a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-sam9x60-pll.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index e699803986e5..3522eae2edd6 100644
--- a/drivers/clk/at91/clk-sam9x60-pll.c
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -15,7 +15,7 @@
#include "pmc.h"

#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
-#define PMC_PLL_CTRL1_MUL_MSK GENMASK(30, 24)
+#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)

#define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
#define UPLL_DIV 2
--
2.7.4

2020-07-15 12:04:14

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 04/19] clk: at91: sam9x60-pll: use frac when computing pll frequency

According to datasheet the PLL frequency is computed using the following
formula: parent_rate * (mul + 1 + frac/2^22)/(div + 1). Use this
formula.

Fixes: a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-sam9x60-pll.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index 3522eae2edd6..d3152c0afcbf 100644
--- a/drivers/clk/at91/clk-sam9x60-pll.c
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -152,7 +152,8 @@ static unsigned long sam9x60_pll_recalc_rate(struct clk_hw *hw,
{
struct sam9x60_pll *pll = to_sam9x60_pll(hw);

- return (parent_rate * (pll->mul + 1)) / (pll->div + 1);
+ return DIV_ROUND_CLOSEST_ULL((parent_rate * (pll->mul + 1) +
+ ((u64)parent_rate * pll->frac >> 22)), (pll->div + 1));
}

static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
--
2.7.4

2020-07-15 12:04:51

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 05/19] clk: at91: sam9x60-pll: use logical or for range check

Use logical or for range check. In case bestrate is not in
characteristics->output[0].min..characteristics->output[0].max
range we should return -ERANGE.

Fixes: a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-sam9x60-pll.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index d3152c0afcbf..dfb91c190bd1 100644
--- a/drivers/clk/at91/clk-sam9x60-pll.c
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -232,7 +232,7 @@ static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
}

/* Check if bestrate is a valid output rate */
- if (bestrate < characteristics->output[0].min &&
+ if (bestrate < characteristics->output[0].min ||
bestrate > characteristics->output[0].max)
return -ERANGE;

--
2.7.4

2020-07-15 12:04:53

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 07/19] clk: at91: sam9x60-pll: use frac when setting frequency

In commit a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
the fractional part of PLL wasn't set on registers but it was
calculated and taken into account for determining div and mul
(see sam9x60_pll_get_best_div_mul()).

Fixes: a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-sam9x60-pll.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index 00f2afd6e9b6..13e15bd48770 100644
--- a/drivers/clk/at91/clk-sam9x60-pll.c
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -16,6 +16,7 @@

#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
+#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)

#define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
#define UPLL_DIV 2
@@ -55,7 +56,7 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
unsigned long flags;
u8 div;
u16 mul;
- u32 val;
+ u32 val, frac;

spin_lock_irqsave(pll->lock, flags);
regmap_write(regmap, AT91_PMC_PLL_UPDT, pll->id);
@@ -65,9 +66,10 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)

regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
+ frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val);

if (sam9x60_pll_ready(regmap, pll->id) &&
- (div == pll->div && mul == pll->mul)) {
+ (div == pll->div && mul == pll->mul && frac == pll->frac)) {
spin_unlock_irqrestore(pll->lock, flags);
return 0;
}
@@ -80,7 +82,8 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
regmap_write(regmap, AT91_PMC_PLL_ACR, val);

regmap_write(regmap, AT91_PMC_PLL_CTRL1,
- FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul));
+ FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul) |
+ FIELD_PREP(PMC_PLL_CTRL1_FRACR_MSK, pll->frac));

if (pll->characteristics->upll) {
/* Enable the UTMI internal bandgap */
--
2.7.4

2020-07-15 12:04:54

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 08/19] clk: at91: sam9x60: fix main rc oscillator frequency

Main RC oscillator frequency is 12MHz according to datasheet
(chapter 27.2).

Fixes: 01e2113de9a52 ("clk: at91: add sam9x60 pmc driver")
Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/sam9x60.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index 633891b98d43..c8703d2a0886 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -189,7 +189,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
if (!sam9x60_pmc)
return;

- hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 24000000,
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
50000000);
if (IS_ERR(hw))
goto err_free;
--
2.7.4

2020-07-15 12:06:07

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 06/19] clk: at91: sam9x60-pll: check fcore against ranges

According to datasheet the range of 600-1200MHz is for the the
frequency generated by the fractional part of the PLL (namely
Fcorepllck according to datasheet). With this in mind the output
range of the PLL itself (fractional + div), taking into account
that the diverder is 8 bits wide, is 600/256-1200Hz=2.3-1200MHz.

Fixes: a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-sam9x60-pll.c | 12 +++++++++++-
drivers/clk/at91/sam9x60.c | 2 +-
2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index dfb91c190bd1..00f2afd6e9b6 100644
--- a/drivers/clk/at91/clk-sam9x60-pll.c
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -21,6 +21,9 @@
#define UPLL_DIV 2
#define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)

+#define FCORE_MIN (600000000)
+#define FCORE_MAX (1200000000)
+
#define PLL_MAX_ID 1

struct sam9x60_pll {
@@ -169,6 +172,7 @@ static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
unsigned long bestdiv = 0;
unsigned long bestmul = 0;
unsigned long bestfrac = 0;
+ unsigned long long fcore = 0;

if (rate < characteristics->output[0].min ||
rate > characteristics->output[0].max)
@@ -213,6 +217,11 @@ static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
remainder = rate - tmprate;
}

+ fcore = parent_rate * (tmpmul + 1) +
+ ((u64)parent_rate * tmpfrac >> 22);
+ if (fcore < FCORE_MIN || fcore > FCORE_MAX)
+ continue;
+
/*
* Compare the remainder with the best remainder found until
* now and elect a new best multiplier/divider pair if the
@@ -232,7 +241,8 @@ static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
}

/* Check if bestrate is a valid output rate */
- if (bestrate < characteristics->output[0].min ||
+ if (fcore < FCORE_MIN || fcore > FCORE_MAX ||
+ bestrate < characteristics->output[0].min ||
bestrate > characteristics->output[0].max)
return -ERANGE;

diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index 3e20aa68259f..633891b98d43 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -22,7 +22,7 @@ static const struct clk_master_layout sam9x60_master_layout = {
};

static const struct clk_range plla_outputs[] = {
- { .min = 300000000, .max = 600000000 },
+ { .min = 2343750, .max = 1200000000 },
};

static const struct clk_pll_characteristics plla_characteristics = {
--
2.7.4

2020-07-15 12:06:45

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 09/19] clk: at91: sckc: register slow_rc with accuracy option

Register slow rc with accuracy option.

Fixes: 04bcc4275e601 ("clk: at91: sckc: add support for SAM9X60")
Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/sckc.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
index 15dc4cd86d76..2d65770d8665 100644
--- a/drivers/clk/at91/sckc.c
+++ b/drivers/clk/at91/sckc.c
@@ -471,8 +471,9 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
if (!regbase)
return;

- slow_rc = clk_hw_register_fixed_rate(NULL, parent_names[0], NULL, 0,
- 32768);
+ slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
+ NULL, 0, 32768,
+ 93750000);
if (IS_ERR(slow_rc))
return;

--
2.7.4

2020-07-15 12:07:12

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 10/19] clk: at91: replace conditional operator with double logical not

Replace conditional operator with double logical not.

Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-generated.c | 2 +-
drivers/clk/at91/clk-main.c | 6 +++---
drivers/clk/at91/clk-master.c | 2 +-
drivers/clk/at91/clk-peripheral.c | 2 +-
drivers/clk/at91/clk-system.c | 4 ++--
5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index f8e557e0e1b8..2448bdc63425 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -83,7 +83,7 @@ static int clk_generated_is_enabled(struct clk_hw *hw)
regmap_read(gck->regmap, gck->layout->offset, &status);
spin_unlock_irqrestore(gck->lock, flags);

- return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
+ return !!(status & AT91_PMC_PCR_GCKEN);
}

static unsigned long
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index 37c22667e831..5c83e899084f 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -175,7 +175,7 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap)

regmap_read(regmap, AT91_PMC_SR, &status);

- return status & AT91_PMC_MOSCRCS;
+ return !!(status & AT91_PMC_MOSCRCS);
}

static int clk_main_rc_osc_prepare(struct clk_hw *hw)
@@ -336,7 +336,7 @@ static int clk_rm9200_main_is_prepared(struct clk_hw *hw)

regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status);

- return status & AT91_PMC_MAINRDY ? 1 : 0;
+ return !!(status & AT91_PMC_MAINRDY);
}

static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw,
@@ -398,7 +398,7 @@ static inline bool clk_sam9x5_main_ready(struct regmap *regmap)

regmap_read(regmap, AT91_PMC_SR, &status);

- return status & AT91_PMC_MOSCSELS ? 1 : 0;
+ return !!(status & AT91_PMC_MOSCSELS);
}

static int clk_sam9x5_main_prepare(struct clk_hw *hw)
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index e7e0ba652de1..88d545b1698c 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -33,7 +33,7 @@ static inline bool clk_master_ready(struct regmap *regmap)

regmap_read(regmap, AT91_PMC_SR, &status);

- return status & AT91_PMC_MCKRDY ? 1 : 0;
+ return !!(status & AT91_PMC_MCKRDY);
}

static int clk_master_prepare(struct clk_hw *hw)
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
index c2ab4860a2bf..4c9a4147dfe5 100644
--- a/drivers/clk/at91/clk-peripheral.c
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -208,7 +208,7 @@ static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
regmap_read(periph->regmap, periph->layout->offset, &status);
spin_unlock_irqrestore(periph->lock, flags);

- return status & AT91_PMC_PCR_EN ? 1 : 0;
+ return !!(status & AT91_PMC_PCR_EN);
}

static unsigned long
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index c4b3877aa445..f83ec0de86c3 100644
--- a/drivers/clk/at91/clk-system.c
+++ b/drivers/clk/at91/clk-system.c
@@ -34,7 +34,7 @@ static inline bool clk_system_ready(struct regmap *regmap, int id)

regmap_read(regmap, AT91_PMC_SR, &status);

- return status & (1 << id) ? 1 : 0;
+ return !!(status & (1 << id));
}

static int clk_system_prepare(struct clk_hw *hw)
@@ -74,7 +74,7 @@ static int clk_system_is_prepared(struct clk_hw *hw)

regmap_read(sys->regmap, AT91_PMC_SR, &status);

- return status & (1 << sys->id) ? 1 : 0;
+ return !!(status & (1 << sys->id));
}

static const struct clk_ops system_ops = {
--
2.7.4

2020-07-15 12:07:48

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 11/19] clk: at91: clk-generated: pass the id of changeable parent at registration

Pass the ID of changeable parent at registration. This will allow
the scalability of this clock driver with regards to the changeable
parent ID for versions of this IP where changeable parent is not the
last one in the parents list (e.g. SAMA7G5). In
clk_generated_best_diff() the *best_diff variable is check against
tmp_diff variable using ">=" operator instead of ">" so that in case
the requested frequency could be obtained using fix parents + gck
dividers but the clock also supports changeable parent to be able
to force the usage of the changeable parent.

Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-generated.c | 26 ++++++++++++++------------
drivers/clk/at91/dt-compat.c | 8 +++++---
drivers/clk/at91/pmc.h | 4 ++--
drivers/clk/at91/sam9x60.c | 3 +--
drivers/clk/at91/sama5d2.c | 31 +++++++++++++++----------------
5 files changed, 37 insertions(+), 35 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index 2448bdc63425..f9ca04c97128 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -18,8 +18,6 @@

#define GENERATED_MAX_DIV 255

-#define GCK_INDEX_DT_AUDIO_PLL 5
-
struct clk_generated {
struct clk_hw hw;
struct regmap *regmap;
@@ -29,7 +27,7 @@ struct clk_generated {
u32 gckdiv;
const struct clk_pcr_layout *layout;
u8 parent_id;
- bool audio_pll_allowed;
+ int chg_pid;
};

#define to_clk_generated(hw) \
@@ -109,7 +107,7 @@ static void clk_generated_best_diff(struct clk_rate_request *req,
tmp_rate = parent_rate / div;
tmp_diff = abs(req->rate - tmp_rate);

- if (*best_diff < 0 || *best_diff > tmp_diff) {
+ if (*best_diff < 0 || *best_diff >= tmp_diff) {
*best_rate = tmp_rate;
*best_diff = tmp_diff;
req->best_parent_rate = parent_rate;
@@ -129,7 +127,10 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
int i;
u32 div;

- for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) {
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ if (gck->chg_pid == i)
+ continue;
+
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
@@ -161,10 +162,10 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
* that the only clks able to modify gck rate are those of audio IPs.
*/

- if (!gck->audio_pll_allowed)
+ if (gck->chg_pid < 0)
goto end;

- parent = clk_hw_get_parent_by_index(hw, GCK_INDEX_DT_AUDIO_PLL);
+ parent = clk_hw_get_parent_by_index(hw, gck->chg_pid);
if (!parent)
goto end;

@@ -272,8 +273,8 @@ struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
- u8 num_parents, u8 id, bool pll_audio,
- const struct clk_range *range)
+ u8 num_parents, u8 id,
+ const struct clk_range *range, int chg_pid)
{
struct clk_generated *gck;
struct clk_init_data init;
@@ -288,15 +289,16 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
init.ops = &generated_ops;
init.parent_names = parent_names;
init.num_parents = num_parents;
- init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
- CLK_SET_RATE_PARENT;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+ if (chg_pid >= 0)
+ init.flags |= CLK_SET_RATE_PARENT;

gck->id = id;
gck->hw.init = &init;
gck->regmap = regmap;
gck->lock = lock;
gck->range = *range;
- gck->audio_pll_allowed = pll_audio;
+ gck->chg_pid = chg_pid;
gck->layout = layout;

clk_generated_startup(gck);
diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c
index aa1754eac59f..8a652c44c25a 100644
--- a/drivers/clk/at91/dt-compat.c
+++ b/drivers/clk/at91/dt-compat.c
@@ -22,6 +22,8 @@

#define SYSTEM_MAX_ID 31

+#define GCK_INDEX_DT_AUDIO_PLL 5
+
#ifdef CONFIG_HAVE_AT91_AUDIO_PLL
static void __init of_sama5d2_clk_audio_pll_frac_setup(struct device_node *np)
{
@@ -135,7 +137,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
return;

for_each_child_of_node(np, gcknp) {
- bool pll_audio = false;
+ int chg_pid = INT_MIN;

if (of_property_read_u32(gcknp, "reg", &id))
continue;
@@ -152,12 +154,12 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
if (of_device_is_compatible(np, "atmel,sama5d2-clk-generated") &&
(id == GCK_ID_I2S0 || id == GCK_ID_I2S1 ||
id == GCK_ID_CLASSD))
- pll_audio = true;
+ chg_pid = GCK_INDEX_DT_AUDIO_PLL;

hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&dt_pcr_layout, name,
parent_names, num_parents,
- id, pll_audio, &range);
+ id, &range, chg_pid);
if (IS_ERR(hw))
continue;

diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index df616f2937e7..949b1a7b91e5 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -122,8 +122,8 @@ struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
- u8 num_parents, u8 id, bool pll_audio,
- const struct clk_range *range);
+ u8 num_parents, u8 id,
+ const struct clk_range *range, int chg_pid);

struct clk_hw * __init
at91_clk_register_h32mx(struct regmap *regmap, const char *name,
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index c8703d2a0886..c4e34c0909e3 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -290,8 +290,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
sam9x60_gck[i].n,
parent_names, 6,
sam9x60_gck[i].id,
- false,
- &sam9x60_gck[i].r);
+ &sam9x60_gck[i].r, INT_MIN);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c
index d69421d71daf..42637d9531d3 100644
--- a/drivers/clk/at91/sama5d2.c
+++ b/drivers/clk/at91/sama5d2.c
@@ -116,21 +116,20 @@ static const struct {
char *n;
u8 id;
struct clk_range r;
- bool pll;
+ int chg_pid;
} sama5d2_gck[] = {
- { .n = "sdmmc0_gclk", .id = 31, },
- { .n = "sdmmc1_gclk", .id = 32, },
- { .n = "tcb0_gclk", .id = 35, .r = { .min = 0, .max = 83000000 }, },
- { .n = "tcb1_gclk", .id = 36, .r = { .min = 0, .max = 83000000 }, },
- { .n = "pwm_gclk", .id = 38, .r = { .min = 0, .max = 83000000 }, },
- { .n = "isc_gclk", .id = 46, },
- { .n = "pdmic_gclk", .id = 48, },
- { .n = "i2s0_gclk", .id = 54, .pll = true },
- { .n = "i2s1_gclk", .id = 55, .pll = true },
- { .n = "can0_gclk", .id = 56, .r = { .min = 0, .max = 80000000 }, },
- { .n = "can1_gclk", .id = 57, .r = { .min = 0, .max = 80000000 }, },
- { .n = "classd_gclk", .id = 59, .r = { .min = 0, .max = 100000000 },
- .pll = true },
+ { .n = "sdmmc0_gclk", .id = 31, .chg_pid = INT_MIN, },
+ { .n = "sdmmc1_gclk", .id = 32, .chg_pid = INT_MIN, },
+ { .n = "tcb0_gclk", .id = 35, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "tcb1_gclk", .id = 36, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "pwm_gclk", .id = 38, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
+ { .n = "isc_gclk", .id = 46, .chg_pid = INT_MIN, },
+ { .n = "pdmic_gclk", .id = 48, .chg_pid = INT_MIN, },
+ { .n = "i2s0_gclk", .id = 54, .chg_pid = 5, },
+ { .n = "i2s1_gclk", .id = 55, .chg_pid = 5, },
+ { .n = "can0_gclk", .id = 56, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, },
+ { .n = "can1_gclk", .id = 57, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, },
+ { .n = "classd_gclk", .id = 59, .chg_pid = 5, .r = { .min = 0, .max = 100000000 }, },
};

static const struct clk_programmable_layout sama5d2_programmable_layout = {
@@ -324,8 +323,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
sama5d2_gck[i].n,
parent_names, 6,
sama5d2_gck[i].id,
- sama5d2_gck[i].pll,
- &sama5d2_gck[i].r);
+ &sama5d2_gck[i].r,
+ sama5d2_gck[i].chg_pid);
if (IS_ERR(hw))
goto err_free;

--
2.7.4

2020-07-15 12:09:46

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 13/19] clk: at91: clk-master: add master clock support for SAMA7G5

Add master clock support (MCK1..4) for SAMA7G5. SAMA7G5's PMC has
multiple master clocks feeding different subsystems. One of them
feeds image subsystem and is changeable based on image subsystem
needs.

Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-master.c | 310 +++++++++++++++++++++++++++++++++++++++++-
drivers/clk/at91/pmc.h | 7 +
include/linux/clk/at91_pmc.h | 1 +
3 files changed, 313 insertions(+), 5 deletions(-)

diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index 88d545b1698c..bd0d8a69a2cf 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -17,30 +17,49 @@
#define MASTER_DIV_SHIFT 8
#define MASTER_DIV_MASK 0x3

+#define PMC_MCR 0x30
+#define PMC_MCR_ID_MSK GENMASK(3, 0)
+#define PMC_MCR_CMD BIT(7)
+#define PMC_MCR_DIV GENMASK(10, 8)
+#define PMC_MCR_CSS GENMASK(20, 16)
+#define PMC_MCR_CSS_SHIFT (16)
+#define PMC_MCR_EN BIT(28)
+
+#define PMC_MCR_ID(x) ((x) & PMC_MCR_ID_MSK)
+
+#define MASTER_MAX_ID 4
+
#define to_clk_master(hw) container_of(hw, struct clk_master, hw)

struct clk_master {
struct clk_hw hw;
struct regmap *regmap;
+ spinlock_t *lock;
const struct clk_master_layout *layout;
const struct clk_master_characteristics *characteristics;
+ u32 *mux_table;
u32 mckr;
+ int chg_pid;
+ u8 id;
+ u8 parent;
+ u8 div;
};

-static inline bool clk_master_ready(struct regmap *regmap)
+static inline bool clk_master_ready(struct clk_master *master)
{
+ unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
unsigned int status;

- regmap_read(regmap, AT91_PMC_SR, &status);
+ regmap_read(master->regmap, AT91_PMC_SR, &status);

- return !!(status & AT91_PMC_MCKRDY);
+ return !!(status & bit);
}

static int clk_master_prepare(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);

- while (!clk_master_ready(master->regmap))
+ while (!clk_master_ready(master))
cpu_relax();

return 0;
@@ -50,7 +69,7 @@ static int clk_master_is_prepared(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);

- return clk_master_ready(master->regmap);
+ return clk_master_ready(master);
}

static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
@@ -143,6 +162,287 @@ at91_clk_register_master(struct regmap *regmap,
return hw;
}

+static unsigned long
+clk_sama7g5_master_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_master *master = to_clk_master(hw);
+
+ return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div));
+}
+
+static void clk_sama7g5_master_best_diff(struct clk_rate_request *req,
+ struct clk_hw *parent,
+ unsigned long parent_rate,
+ long *best_rate,
+ long *best_diff,
+ u32 div)
+{
+ unsigned long tmp_rate, tmp_diff;
+
+ if (div == MASTER_PRES_MAX)
+ tmp_rate = parent_rate / 3;
+ else
+ tmp_rate = parent_rate >> div;
+
+ tmp_diff = abs(req->rate - tmp_rate);
+
+ if (*best_diff < 0 || *best_diff >= tmp_diff) {
+ *best_rate = tmp_rate;
+ *best_diff = tmp_diff;
+ req->best_parent_rate = parent_rate;
+ req->best_parent_hw = parent;
+ }
+}
+
+static int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_master *master = to_clk_master(hw);
+ struct clk_rate_request req_parent = *req;
+ struct clk_hw *parent;
+ long best_rate = LONG_MIN, best_diff = LONG_MIN;
+ unsigned long parent_rate;
+ unsigned int div, i;
+
+ /* First: check the dividers of MCR. */
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ parent = clk_hw_get_parent_by_index(hw, i);
+ if (!parent)
+ continue;
+
+ parent_rate = clk_hw_get_rate(parent);
+ if (!parent_rate)
+ continue;
+
+ for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
+ clk_sama7g5_master_best_diff(req, parent, parent_rate,
+ &best_rate, &best_diff,
+ div);
+ if (!best_diff)
+ break;
+ }
+
+ if (!best_diff)
+ break;
+ }
+
+ /* Second: try to request rate form changeable parent. */
+ if (master->chg_pid < 0)
+ goto end;
+
+ parent = clk_hw_get_parent_by_index(hw, master->chg_pid);
+ if (!parent)
+ goto end;
+
+ for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
+ if (div == MASTER_PRES_MAX)
+ req_parent.rate = req->rate * 3;
+ else
+ req_parent.rate = req->rate << div;
+
+ if (__clk_determine_rate(parent, &req_parent))
+ continue;
+
+ clk_sama7g5_master_best_diff(req, parent, req_parent.rate,
+ &best_rate, &best_diff, div);
+
+ if (!best_diff)
+ break;
+ }
+
+end:
+ pr_debug("MCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
+ __func__, best_rate,
+ __clk_get_name((req->best_parent_hw)->clk),
+ req->best_parent_rate);
+
+ if (best_rate < 0)
+ return -EINVAL;
+
+ req->rate = best_rate;
+
+ return 0;
+}
+
+static u8 clk_sama7g5_master_get_parent(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+ u8 index;
+
+ spin_lock_irqsave(master->lock, flags);
+ index = clk_mux_val_to_index(&master->hw, master->mux_table, 0,
+ master->parent);
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return index;
+}
+
+static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+
+ if (index >= clk_hw_get_num_parents(hw))
+ return -EINVAL;
+
+ spin_lock_irqsave(master->lock, flags);
+ master->parent = clk_mux_index_to_val(master->mux_table, 0, index);
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return 0;
+}
+
+static int clk_sama7g5_master_enable(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+ unsigned int val, cparent;
+
+ spin_lock_irqsave(master->lock, flags);
+
+ regmap_write(master->regmap, PMC_MCR, PMC_MCR_ID(master->id));
+ regmap_read(master->regmap, PMC_MCR, &val);
+ regmap_update_bits(master->regmap, PMC_MCR,
+ PMC_MCR_EN | PMC_MCR_CSS | PMC_MCR_DIV |
+ PMC_MCR_CMD | PMC_MCR_ID_MSK,
+ PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) |
+ (master->div << MASTER_DIV_SHIFT) |
+ PMC_MCR_CMD | PMC_MCR_ID(master->id));
+
+ cparent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
+
+ /* Wait here only if parent is being changed. */
+ while ((cparent != master->parent) && !clk_master_ready(master))
+ cpu_relax();
+
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return 0;
+}
+
+static void clk_sama7g5_master_disable(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(master->lock, flags);
+
+ regmap_write(master->regmap, PMC_MCR, master->id);
+ regmap_update_bits(master->regmap, PMC_MCR,
+ PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
+ PMC_MCR_CMD | PMC_MCR_ID(master->id));
+
+ spin_unlock_irqrestore(master->lock, flags);
+}
+
+static int clk_sama7g5_master_is_enabled(struct clk_hw *hw)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(master->lock, flags);
+
+ regmap_write(master->regmap, PMC_MCR, master->id);
+ regmap_read(master->regmap, PMC_MCR, &val);
+
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return !!(val & PMC_MCR_EN);
+}
+
+static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_master *master = to_clk_master(hw);
+ unsigned long div, flags;
+
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1)))
+ return -EINVAL;
+
+ if (div == 3)
+ div = MASTER_PRES_MAX;
+ else
+ div = ffs(div) - 1;
+
+ spin_lock_irqsave(master->lock, flags);
+ master->div = div;
+ spin_unlock_irqrestore(master->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops sama7g5_master_ops = {
+ .enable = clk_sama7g5_master_enable,
+ .disable = clk_sama7g5_master_disable,
+ .is_enabled = clk_sama7g5_master_is_enabled,
+ .recalc_rate = clk_sama7g5_master_recalc_rate,
+ .determine_rate = clk_sama7g5_master_determine_rate,
+ .set_rate = clk_sama7g5_master_set_rate,
+ .get_parent = clk_sama7g5_master_get_parent,
+ .set_parent = clk_sama7g5_master_set_parent,
+};
+
+struct clk_hw * __init
+at91_clk_sama7g5_register_master(struct regmap *regmap,
+ const char *name, int num_parents,
+ const char **parent_names,
+ u32 *mux_table,
+ spinlock_t *lock, u8 id,
+ bool critical, int chg_pid)
+{
+ struct clk_master *master;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ unsigned long flags;
+ unsigned int val;
+ int ret;
+
+ if (!name || !num_parents || !parent_names || !mux_table ||
+ !lock || id > MASTER_MAX_ID)
+ return ERR_PTR(-EINVAL);
+
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &sama7g5_master_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+ if (chg_pid >= 0)
+ init.flags |= CLK_SET_RATE_PARENT;
+ if (critical)
+ init.flags |= CLK_IS_CRITICAL;
+
+ master->hw.init = &init;
+ master->regmap = regmap;
+ master->id = id;
+ master->chg_pid = chg_pid;
+ master->lock = lock;
+ master->mux_table = mux_table;
+
+ spin_lock_irqsave(master->lock, flags);
+ regmap_write(master->regmap, PMC_MCR, master->id);
+ regmap_read(master->regmap, PMC_MCR, &val);
+ master->parent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
+ master->div = (val & PMC_MCR_DIV) >> MASTER_DIV_SHIFT;
+ spin_unlock_irqrestore(master->lock, flags);
+
+ hw = &master->hw;
+ ret = clk_hw_register(NULL, &master->hw);
+ if (ret) {
+ kfree(master);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
+
const struct clk_master_layout at91rm9200_master_layout = {
.mask = 0x31F,
.pres_shift = 2,
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 2bfe1405f9f8..29d150feaa46 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -155,6 +155,13 @@ at91_clk_register_master(struct regmap *regmap, const char *name,
const struct clk_master_characteristics *characteristics);

struct clk_hw * __init
+at91_clk_sama7g5_register_master(struct regmap *regmap,
+ const char *name, int num_parents,
+ const char **parent_names, u32 *mux_table,
+ spinlock_t *lock, u8 id, bool critical,
+ int chg_pid);
+
+struct clk_hw * __init
at91_clk_register_peripheral(struct regmap *regmap, const char *name,
const char *parent_name, u32 id);
struct clk_hw * __init
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index 49a53a137610..77d6dabc4c3c 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -174,6 +174,7 @@
#define AT91_PMC_MOSCRCS (1 << 17) /* Main On-Chip RC [some SAM9] */
#define AT91_PMC_CFDEV (1 << 18) /* Clock Failure Detector Event [some SAM9] */
#define AT91_PMC_GCKRDY (1 << 24) /* Generated Clocks */
+#define AT91_PMC_MCKXRDY (1 << 26) /* Master Clock x [x=1..4] Ready Status */
#define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */

#define AT91_PMC_FSMR 0x70 /* Fast Startup Mode Register */
--
2.7.4

2020-07-15 12:09:47

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 12/19] clk: at91: clk-generated: add mux_table option

Add mux table option. This is necessary for IP versions where
software parent indexes are different than hardware parent
indexes (e.g. SAMA7G5).

Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-generated.c | 13 ++++++++++---
drivers/clk/at91/dt-compat.c | 5 +++--
drivers/clk/at91/pmc.h | 2 +-
drivers/clk/at91/sam9x60.c | 2 +-
drivers/clk/at91/sama5d2.c | 2 +-
5 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index f9ca04c97128..b4fc8d71daf2 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -23,6 +23,7 @@ struct clk_generated {
struct regmap *regmap;
struct clk_range range;
spinlock_t *lock;
+ u32 *mux_table;
u32 id;
u32 gckdiv;
const struct clk_pcr_layout *layout;
@@ -201,7 +202,11 @@ static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
if (index >= clk_hw_get_num_parents(hw))
return -EINVAL;

- gck->parent_id = index;
+ if (gck->mux_table)
+ gck->parent_id = clk_mux_index_to_val(gck->mux_table, 0, index);
+ else
+ gck->parent_id = index;
+
return 0;
}

@@ -273,8 +278,9 @@ struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
- u8 num_parents, u8 id,
- const struct clk_range *range, int chg_pid)
+ u32 *mux_table, u8 num_parents, u8 id,
+ const struct clk_range *range,
+ int chg_pid)
{
struct clk_generated *gck;
struct clk_init_data init;
@@ -300,6 +306,7 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
gck->range = *range;
gck->chg_pid = chg_pid;
gck->layout = layout;
+ gck->mux_table = mux_table;

clk_generated_startup(gck);
hw = &gck->hw;
diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c
index 8a652c44c25a..cc95d42f4d53 100644
--- a/drivers/clk/at91/dt-compat.c
+++ b/drivers/clk/at91/dt-compat.c
@@ -158,8 +158,9 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)

hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&dt_pcr_layout, name,
- parent_names, num_parents,
- id, &range, chg_pid);
+ parent_names, NULL,
+ num_parents, id, &range,
+ chg_pid);
if (IS_ERR(hw))
continue;

diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 949b1a7b91e5..2bfe1405f9f8 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -122,7 +122,7 @@ struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
- u8 num_parents, u8 id,
+ u32 *mux_table, u8 num_parents, u8 id,
const struct clk_range *range, int chg_pid);

struct clk_hw * __init
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index c4e34c0909e3..3ad16d74577e 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -288,7 +288,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sam9x60_pcr_layout,
sam9x60_gck[i].n,
- parent_names, 6,
+ parent_names, NULL, 6,
sam9x60_gck[i].id,
&sam9x60_gck[i].r, INT_MIN);
if (IS_ERR(hw))
diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c
index 42637d9531d3..6a685d00f16d 100644
--- a/drivers/clk/at91/sama5d2.c
+++ b/drivers/clk/at91/sama5d2.c
@@ -321,7 +321,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout,
sama5d2_gck[i].n,
- parent_names, 6,
+ parent_names, NULL, 6,
sama5d2_gck[i].id,
&sama5d2_gck[i].r,
sama5d2_gck[i].chg_pid);
--
2.7.4

2020-07-15 12:09:48

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 14/19] clk: at91: clk-peripheral: add support for changeable parent rate

Some peripheral clocks on SAMA7G5 supports requesting parent to change
its rate (image related clocks: csi, csi2dc, isc). Add support
so that if registered with this option the clock rate to be
requested from parent.

Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/at91sam9n12.c | 2 +-
drivers/clk/at91/at91sam9x5.c | 4 +-
drivers/clk/at91/clk-peripheral.c | 109 ++++++++++++++++++++++++++++++++++++--
drivers/clk/at91/dt-compat.c | 3 +-
drivers/clk/at91/pmc.h | 3 +-
drivers/clk/at91/sam9x60.c | 2 +-
drivers/clk/at91/sama5d2.c | 5 +-
drivers/clk/at91/sama5d3.c | 3 +-
drivers/clk/at91/sama5d4.c | 4 +-
9 files changed, 119 insertions(+), 16 deletions(-)

diff --git a/drivers/clk/at91/at91sam9n12.c b/drivers/clk/at91/at91sam9n12.c
index 630dc5d87171..f4c36e6e4deb 100644
--- a/drivers/clk/at91/at91sam9n12.c
+++ b/drivers/clk/at91/at91sam9n12.c
@@ -222,7 +222,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
at91sam9n12_periphck[i].n,
"masterck",
at91sam9n12_periphck[i].id,
- &range);
+ &range, INT_MIN);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/at91sam9x5.c b/drivers/clk/at91/at91sam9x5.c
index 0ce3da080287..52a9d2f7ec83 100644
--- a/drivers/clk/at91/at91sam9x5.c
+++ b/drivers/clk/at91/at91sam9x5.c
@@ -257,7 +257,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
at91sam9x5_periphck[i].n,
"masterck",
at91sam9x5_periphck[i].id,
- &range);
+ &range, INT_MIN);
if (IS_ERR(hw))
goto err_free;

@@ -270,7 +270,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
extra_pcks[i].n,
"masterck",
extra_pcks[i].id,
- &range);
+ &range, INT_MIN);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
index 4c9a4147dfe5..7867eaf0447f 100644
--- a/drivers/clk/at91/clk-peripheral.c
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -38,6 +38,7 @@ struct clk_sam9x5_peripheral {
u32 div;
const struct clk_pcr_layout *layout;
bool auto_div;
+ int chg_pid;
};

#define to_clk_sam9x5_peripheral(hw) \
@@ -238,6 +239,87 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
return parent_rate >> periph->div;
}

+static void clk_sam9x5_peripheral_best_diff(struct clk_rate_request *req,
+ struct clk_hw *parent,
+ unsigned long parent_rate,
+ u32 shift, long *best_diff,
+ long *best_rate)
+{
+ unsigned long tmp_rate = parent_rate >> shift;
+ unsigned long tmp_diff = abs(req->rate - tmp_rate);
+
+ if (*best_diff < 0 || *best_diff >= tmp_diff) {
+ *best_rate = tmp_rate;
+ *best_diff = tmp_diff;
+ req->best_parent_rate = parent_rate;
+ req->best_parent_hw = parent;
+ }
+}
+
+static int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ struct clk_rate_request req_parent = *req;
+ unsigned long parent_rate = clk_hw_get_rate(parent);
+ unsigned long tmp_rate;
+ long best_rate = LONG_MIN;
+ long best_diff = LONG_MIN;
+ u32 shift;
+
+ if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
+ return parent_rate;
+
+ /* Fist step: check the available dividers. */
+ for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
+ tmp_rate = parent_rate >> shift;
+
+ if (periph->range.max && tmp_rate > periph->range.max)
+ continue;
+
+ clk_sam9x5_peripheral_best_diff(req, parent, parent_rate,
+ shift, &best_diff, &best_rate);
+
+ if (!best_diff || best_rate <= req->rate)
+ break;
+ }
+
+ if (periph->chg_pid < 0)
+ goto end;
+
+ /* Step two: try to request rate from parent. */
+ parent = clk_hw_get_parent_by_index(hw, periph->chg_pid);
+ if (!parent)
+ goto end;
+
+ for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
+ req_parent.rate = req->rate << shift;
+
+ if (__clk_determine_rate(parent, &req_parent))
+ continue;
+
+ clk_sam9x5_peripheral_best_diff(req, parent, req_parent.rate,
+ shift, &best_diff, &best_rate);
+
+ if (!best_diff)
+ break;
+ }
+end:
+ if (best_rate < 0 ||
+ (periph->range.max && best_rate > periph->range.max))
+ return -EINVAL;
+
+ pr_debug("PCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
+ __func__, best_rate,
+ __clk_get_name((req->best_parent_hw)->clk),
+ req->best_parent_rate);
+
+ req->rate = best_rate;
+
+ return 0;
+}
+
static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *parent_rate)
@@ -320,11 +402,21 @@ static const struct clk_ops sam9x5_peripheral_ops = {
.set_rate = clk_sam9x5_peripheral_set_rate,
};

+static const struct clk_ops sam9x5_peripheral_chg_ops = {
+ .enable = clk_sam9x5_peripheral_enable,
+ .disable = clk_sam9x5_peripheral_disable,
+ .is_enabled = clk_sam9x5_peripheral_is_enabled,
+ .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
+ .determine_rate = clk_sam9x5_peripheral_determine_rate,
+ .set_rate = clk_sam9x5_peripheral_set_rate,
+};
+
struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
- u32 id, const struct clk_range *range)
+ u32 id, const struct clk_range *range,
+ int chg_pid)
{
struct clk_sam9x5_peripheral *periph;
struct clk_init_data init;
@@ -339,10 +431,16 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
return ERR_PTR(-ENOMEM);

init.name = name;
- init.ops = &sam9x5_peripheral_ops;
- init.parent_names = (parent_name ? &parent_name : NULL);
- init.num_parents = (parent_name ? 1 : 0);
- init.flags = 0;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ if (chg_pid < 0) {
+ init.flags = 0;
+ init.ops = &sam9x5_peripheral_ops;
+ } else {
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
+ init.ops = &sam9x5_peripheral_chg_ops;
+ }

periph->id = id;
periph->hw.init = &init;
@@ -353,6 +451,7 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
periph->auto_div = true;
periph->layout = layout;
periph->range = *range;
+ periph->chg_pid = chg_pid;

hw = &periph->hw;
ret = clk_hw_register(NULL, &periph->hw);
diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c
index cc95d42f4d53..1b90c4f7b1d1 100644
--- a/drivers/clk/at91/dt-compat.c
+++ b/drivers/clk/at91/dt-compat.c
@@ -463,7 +463,8 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
&dt_pcr_layout,
name,
parent_name,
- id, &range);
+ id, &range,
+ INT_MIN);
}

if (IS_ERR(hw))
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 29d150feaa46..34c9506e6275 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -168,7 +168,8 @@ struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
- u32 id, const struct clk_range *range);
+ u32 id, const struct clk_range *range,
+ int chg_pid);

struct clk_hw * __init
at91_clk_register_pll(struct regmap *regmap, const char *name,
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index 3ad16d74577e..25b57c6105cd 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -277,7 +277,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
sam9x60_periphck[i].n,
"masterck",
sam9x60_periphck[i].id,
- &range);
+ &range, INT_MIN);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c
index 6a685d00f16d..c7765b664940 100644
--- a/drivers/clk/at91/sama5d2.c
+++ b/drivers/clk/at91/sama5d2.c
@@ -291,7 +291,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
sama5d2_periphck[i].n,
"masterck",
sama5d2_periphck[i].id,
- &range);
+ &range, INT_MIN);
if (IS_ERR(hw))
goto err_free;

@@ -304,7 +304,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
sama5d2_periph32ck[i].n,
"h32mxck",
sama5d2_periph32ck[i].id,
- &sama5d2_periph32ck[i].r);
+ &sama5d2_periph32ck[i].r,
+ INT_MIN);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/sama5d3.c b/drivers/clk/at91/sama5d3.c
index 5e4e44dd4c37..1597124609e5 100644
--- a/drivers/clk/at91/sama5d3.c
+++ b/drivers/clk/at91/sama5d3.c
@@ -223,7 +223,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
sama5d3_periphck[i].n,
"masterck",
sama5d3_periphck[i].id,
- &sama5d3_periphck[i].r);
+ &sama5d3_periphck[i].r,
+ INT_MIN);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/sama5d4.c b/drivers/clk/at91/sama5d4.c
index 662ff5fa6e98..fa121897d95d 100644
--- a/drivers/clk/at91/sama5d4.c
+++ b/drivers/clk/at91/sama5d4.c
@@ -246,7 +246,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
sama5d4_periphck[i].n,
"masterck",
sama5d4_periphck[i].id,
- &range);
+ &range, INT_MIN);
if (IS_ERR(hw))
goto err_free;

@@ -259,7 +259,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
sama5d4_periph32ck[i].n,
"h32mxck",
sama5d4_periph32ck[i].id,
- &range);
+ &range, INT_MIN);
if (IS_ERR(hw))
goto err_free;

--
2.7.4

2020-07-15 12:10:56

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 16/19] clk: at91: add macro for pll ids mask

Add macro for PLL IDs mask.

Signed-off-by: Claudiu Beznea <[email protected]>
---
include/linux/clk/at91_pmc.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index 77d6dabc4c3c..dc5e85f124e0 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -59,6 +59,7 @@
#define AT91_PMC_PLL_UPDT 0x1C /* PMC PLL update register [for SAM9X60] */
#define AT91_PMC_PLL_UPDT_UPDATE (1 << 8) /* Update PLL settings */
#define AT91_PMC_PLL_UPDT_ID (1 << 0) /* PLL ID */
+#define AT91_PMC_PLL_UPDT_ID_MSK (0xf) /* PLL ID mask */
#define AT91_PMC_PLL_UPDT_STUPTIM (0xff << 16) /* Startup time */

#define AT91_CKGR_MOR 0x20 /* Main Oscillator Register [not on SAM9RL] */
--
2.7.4

2020-07-15 12:11:20

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 15/19] clk: at91: clk-programmable: add mux_table option

Add mux table option. This is necessary for IP versions where
software parent indexes are different than hardware parent
indexes (e.g. SAMA7G5).

Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/at91rm9200.c | 3 ++-
drivers/clk/at91/at91sam9260.c | 3 ++-
drivers/clk/at91/at91sam9g45.c | 3 ++-
drivers/clk/at91/at91sam9n12.c | 3 ++-
drivers/clk/at91/at91sam9rl.c | 3 ++-
drivers/clk/at91/at91sam9x5.c | 3 ++-
drivers/clk/at91/clk-programmable.c | 11 ++++++++++-
drivers/clk/at91/dt-compat.c | 11 ++++++-----
drivers/clk/at91/pmc.h | 3 ++-
drivers/clk/at91/sam9x60.c | 3 ++-
drivers/clk/at91/sama5d2.c | 3 ++-
drivers/clk/at91/sama5d3.c | 3 ++-
drivers/clk/at91/sama5d4.c | 3 ++-
13 files changed, 38 insertions(+), 17 deletions(-)

diff --git a/drivers/clk/at91/at91rm9200.c b/drivers/clk/at91/at91rm9200.c
index 38bdb4981315..2c3d8e6ca63c 100644
--- a/drivers/clk/at91/at91rm9200.c
+++ b/drivers/clk/at91/at91rm9200.c
@@ -160,7 +160,8 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)

hw = at91_clk_register_programmable(regmap, name,
parent_names, 4, i,
- &at91rm9200_programmable_layout);
+ &at91rm9200_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/at91sam9260.c b/drivers/clk/at91/at91sam9260.c
index 6d0723aa8b13..bb81ff731ad8 100644
--- a/drivers/clk/at91/at91sam9260.c
+++ b/drivers/clk/at91/at91sam9260.c
@@ -436,7 +436,8 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,

hw = at91_clk_register_programmable(regmap, name,
parent_names, 4, i,
- &at91rm9200_programmable_layout);
+ &at91rm9200_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/at91sam9g45.c b/drivers/clk/at91/at91sam9g45.c
index 9873b583c260..6b538f90fa45 100644
--- a/drivers/clk/at91/at91sam9g45.c
+++ b/drivers/clk/at91/at91sam9g45.c
@@ -181,7 +181,8 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)

hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9g45_programmable_layout);
+ &at91sam9g45_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/at91sam9n12.c b/drivers/clk/at91/at91sam9n12.c
index f4c36e6e4deb..bd07542abd4b 100644
--- a/drivers/clk/at91/at91sam9n12.c
+++ b/drivers/clk/at91/at91sam9n12.c
@@ -199,7 +199,8 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)

hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9x5_programmable_layout);
+ &at91sam9x5_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/at91sam9rl.c b/drivers/clk/at91/at91sam9rl.c
index 0d1cc44b056f..a343eb69bb35 100644
--- a/drivers/clk/at91/at91sam9rl.c
+++ b/drivers/clk/at91/at91sam9rl.c
@@ -137,7 +137,8 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)

hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91rm9200_programmable_layout);
+ &at91rm9200_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/at91sam9x5.c b/drivers/clk/at91/at91sam9x5.c
index 52a9d2f7ec83..22b9aad9efb8 100644
--- a/drivers/clk/at91/at91sam9x5.c
+++ b/drivers/clk/at91/at91sam9x5.c
@@ -226,7 +226,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,

hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9x5_programmable_layout);
+ &at91sam9x5_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index 8ee66fbee3d9..fcf8f6a1c2c6 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -21,6 +21,7 @@
struct clk_programmable {
struct clk_hw hw;
struct regmap *regmap;
+ u32 *mux_table;
u8 id;
const struct clk_programmable_layout *layout;
};
@@ -108,6 +109,9 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
if (layout->have_slck_mck)
mask |= AT91_PMC_CSSMCK_MCK;

+ if (prog->mux_table)
+ pckr = clk_mux_index_to_val(prog->mux_table, 0, index);
+
if (index > layout->css_mask) {
if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
return -EINVAL;
@@ -134,6 +138,9 @@ static u8 clk_programmable_get_parent(struct clk_hw *hw)
if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret)
ret = PROG_MAX_RM9200_CSS + 1;

+ if (prog->mux_table)
+ ret = clk_mux_val_to_index(&prog->hw, prog->mux_table, 0, ret);
+
return ret;
}

@@ -182,7 +189,8 @@ struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap,
const char *name, const char **parent_names,
u8 num_parents, u8 id,
- const struct clk_programmable_layout *layout)
+ const struct clk_programmable_layout *layout,
+ u32 *mux_table)
{
struct clk_programmable *prog;
struct clk_hw *hw;
@@ -206,6 +214,7 @@ at91_clk_register_programmable(struct regmap *regmap,
prog->layout = layout;
prog->hw.init = &init;
prog->regmap = regmap;
+ prog->mux_table = mux_table;

hw = &prog->hw;
ret = clk_hw_register(NULL, &prog->hw);
diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c
index 1b90c4f7b1d1..a50084de97d4 100644
--- a/drivers/clk/at91/dt-compat.c
+++ b/drivers/clk/at91/dt-compat.c
@@ -677,7 +677,8 @@ CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv",

static void __init
of_at91_clk_prog_setup(struct device_node *np,
- const struct clk_programmable_layout *layout)
+ const struct clk_programmable_layout *layout,
+ u32 *mux_table)
{
int num;
u32 id;
@@ -711,7 +712,7 @@ of_at91_clk_prog_setup(struct device_node *np,

hw = at91_clk_register_programmable(regmap, name,
parent_names, num_parents,
- id, layout);
+ id, layout, mux_table);
if (IS_ERR(hw))
continue;

@@ -721,21 +722,21 @@ of_at91_clk_prog_setup(struct device_node *np,

static void __init of_at91rm9200_clk_prog_setup(struct device_node *np)
{
- of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout);
+ of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout, NULL);
}
CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable",
of_at91rm9200_clk_prog_setup);

static void __init of_at91sam9g45_clk_prog_setup(struct device_node *np)
{
- of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout);
+ of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout, NULL);
}
CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable",
of_at91sam9g45_clk_prog_setup);

static void __init of_at91sam9x5_clk_prog_setup(struct device_node *np)
{
- of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout);
+ of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout, NULL);
}
CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable",
of_at91sam9x5_clk_prog_setup);
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 34c9506e6275..8d3e22f566cf 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -188,7 +188,8 @@ sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock,
struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents, u8 id,
- const struct clk_programmable_layout *layout);
+ const struct clk_programmable_layout *layout,
+ u32 *mux_table);

struct clk_hw * __init
at91_clk_register_sam9260_slow(struct regmap *regmap,
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index 25b57c6105cd..f2306615c7e7 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -254,7 +254,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)

hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i,
- &sam9x60_programmable_layout);
+ &sam9x60_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/sama5d2.c b/drivers/clk/at91/sama5d2.c
index c7765b664940..8b220762941a 100644
--- a/drivers/clk/at91/sama5d2.c
+++ b/drivers/clk/at91/sama5d2.c
@@ -268,7 +268,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)

hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i,
- &sama5d2_programmable_layout);
+ &sama5d2_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/sama5d3.c b/drivers/clk/at91/sama5d3.c
index 1597124609e5..c500924dd414 100644
--- a/drivers/clk/at91/sama5d3.c
+++ b/drivers/clk/at91/sama5d3.c
@@ -200,7 +200,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np)

hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9x5_programmable_layout);
+ &at91sam9x5_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;

diff --git a/drivers/clk/at91/sama5d4.c b/drivers/clk/at91/sama5d4.c
index fa121897d95d..92d8d4141b43 100644
--- a/drivers/clk/at91/sama5d4.c
+++ b/drivers/clk/at91/sama5d4.c
@@ -223,7 +223,8 @@ static void __init sama5d4_pmc_setup(struct device_node *np)

hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
- &at91sam9x5_programmable_layout);
+ &at91sam9x5_programmable_layout,
+ NULL);
if (IS_ERR(hw))
goto err_free;

--
2.7.4

2020-07-15 12:11:22

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 17/19] clk: at91: clk-sam9x60-pll: re-factor to support plls with multiple outputs

Some of the SAMA7G5 PLLs support multiple outputs (e.g. AUDIO PLL).
For these, split the PLL clock in two: fractional clock and
divider clock. In case PLLs supports multiple outputs (since these
outputs are dividers (with different settings) sharing the same
fractional part), it will register one fractional clock and multiple
divider clocks (dividers sharing the fractional clock).

Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-sam9x60-pll.c | 547 +++++++++++++++++++++++++------------
drivers/clk/at91/pmc.h | 22 +-
drivers/clk/at91/sam9x60.c | 50 +++-
3 files changed, 433 insertions(+), 186 deletions(-)

diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index 13e15bd48770..b473298ef7e6 100644
--- a/drivers/clk/at91/clk-sam9x60-pll.c
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -25,20 +25,31 @@
#define FCORE_MIN (600000000)
#define FCORE_MAX (1200000000)

-#define PLL_MAX_ID 1
+#define PLL_MAX_ID 7

-struct sam9x60_pll {
- struct clk_hw hw;
+struct sam9x60_pll_core {
struct regmap *regmap;
spinlock_t *lock;
const struct clk_pll_characteristics *characteristics;
- u32 frac;
+ const struct clk_pll_layout *layout;
+ struct clk_hw hw;
u8 id;
- u8 div;
+};
+
+struct sam9x60_frac {
+ struct sam9x60_pll_core core;
+ u32 frac;
u16 mul;
};

-#define to_sam9x60_pll(hw) container_of(hw, struct sam9x60_pll, hw)
+struct sam9x60_div {
+ struct sam9x60_pll_core core;
+ u8 div;
+};
+
+#define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw)
+#define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core)
+#define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core)

static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
{
@@ -49,43 +60,53 @@ static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
return !!(status & BIT(id));
}

-static int sam9x60_pll_prepare(struct clk_hw *hw)
+static bool sam9x60_frac_pll_ready(struct regmap *regmap, u8 id)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
- struct regmap *regmap = pll->regmap;
- unsigned long flags;
- u8 div;
- u16 mul;
- u32 val, frac;
+ return sam9x60_pll_ready(regmap, id);
+}

- spin_lock_irqsave(pll->lock, flags);
- regmap_write(regmap, AT91_PMC_PLL_UPDT, pll->id);
+static unsigned long sam9x60_frac_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);

- regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
- div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
+ return (parent_rate * (frac->mul + 1) +
+ ((u64)parent_rate * frac->frac >> 22));
+}

+static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);
+ struct regmap *regmap = core->regmap;
+ unsigned int val, cfrac, cmul;
+ unsigned long flags;
+
+ spin_lock_irqsave(core->lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
- mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
- frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val);
+ cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
+ cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;

- if (sam9x60_pll_ready(regmap, pll->id) &&
- (div == pll->div && mul == pll->mul && frac == pll->frac)) {
- spin_unlock_irqrestore(pll->lock, flags);
- return 0;
- }
+ if (sam9x60_frac_pll_ready(regmap, core->id) &&
+ (cmul == frac->mul && cfrac == frac->frac))
+ goto unlock;

- /* Recommended value for AT91_PMC_PLL_ACR */
- if (pll->characteristics->upll)
+ /* Recommended value for PMC_PLL_ACR */
+ if (core->characteristics->upll)
val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
else
val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
regmap_write(regmap, AT91_PMC_PLL_ACR, val);

regmap_write(regmap, AT91_PMC_PLL_CTRL1,
- FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul) |
- FIELD_PREP(PMC_PLL_CTRL1_FRACR_MSK, pll->frac));
+ (frac->mul << core->layout->mul_shift) |
+ (frac->frac << core->layout->frac_shift));

- if (pll->characteristics->upll) {
+ if (core->characteristics->upll) {
/* Enable the UTMI internal bandgap */
val |= AT91_PMC_PLL_ACR_UTMIBG;
regmap_write(regmap, AT91_PMC_PLL_ACR, val);
@@ -100,229 +121,409 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
}

regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);

- regmap_write(regmap, AT91_PMC_PLL_CTRL0,
- AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL |
- AT91_PMC_PLL_CTRL0_ENPLLCK | pll->div);
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
+ AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL);

regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);

- while (!sam9x60_pll_ready(regmap, pll->id))
+ while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();

- spin_unlock_irqrestore(pll->lock, flags);
+unlock:
+ spin_unlock_irqrestore(core->lock, flags);

return 0;
}

-static int sam9x60_pll_is_prepared(struct clk_hw *hw)
+static void sam9x60_frac_pll_unprepare(struct clk_hw *hw)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;

- return sam9x60_pll_ready(pll->regmap, pll->id);
+ spin_lock_irqsave(core->lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, AT91_PMC_PLL_CTRL0_ENPLL, 0);
+
+ if (core->characteristics->upll)
+ regmap_update_bits(regmap, AT91_PMC_PLL_ACR,
+ AT91_PMC_PLL_ACR_UTMIBG | AT91_PMC_PLL_ACR_UTMIVR, 0);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
+
+ spin_unlock_irqrestore(core->lock, flags);
}

-static void sam9x60_pll_unprepare(struct clk_hw *hw)
+static int sam9x60_frac_pll_is_prepared(struct clk_hw *hw)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
- unsigned long flags;
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+
+ return sam9x60_pll_ready(core->regmap, core->id);
+}

- spin_lock_irqsave(pll->lock, flags);
+static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core,
+ unsigned long rate,
+ unsigned long parent_rate,
+ bool update)
+{
+ struct sam9x60_frac *frac = to_sam9x60_frac(core);
+ unsigned long tmprate, remainder;
+ unsigned long nmul = 0;
+ unsigned long nfrac = 0;

- regmap_write(pll->regmap, AT91_PMC_PLL_UPDT, pll->id);
+ if (rate < FCORE_MIN || rate > FCORE_MAX)
+ return -ERANGE;

- regmap_update_bits(pll->regmap, AT91_PMC_PLL_CTRL0,
- AT91_PMC_PLL_CTRL0_ENPLLCK, 0);
+ /*
+ * Calculate the multiplier associated with the current
+ * divider that provide the closest rate to the requested one.
+ */
+ nmul = mult_frac(rate, 1, parent_rate);
+ tmprate = mult_frac(parent_rate, nmul, 1);
+ remainder = rate - tmprate;

- regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
+ if (remainder) {
+ nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
+ parent_rate);

- regmap_update_bits(pll->regmap, AT91_PMC_PLL_CTRL0,
- AT91_PMC_PLL_CTRL0_ENPLL, 0);
+ tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
+ (1 << 22));
+ }

- if (pll->characteristics->upll)
- regmap_update_bits(pll->regmap, AT91_PMC_PLL_ACR,
- AT91_PMC_PLL_ACR_UTMIBG |
- AT91_PMC_PLL_ACR_UTMIVR, 0);
+ /* Check if resulted rate is a valid. */
+ if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
+ return -ERANGE;

- regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
+ if (update) {
+ frac->mul = nmul - 1;
+ frac->frac = nfrac;
+ }

- spin_unlock_irqrestore(pll->lock, flags);
+ return tmprate;
}

-static unsigned long sam9x60_pll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+static long sam9x60_frac_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);

- return DIV_ROUND_CLOSEST_ULL((parent_rate * (pll->mul + 1) +
- ((u64)parent_rate * pll->frac >> 22)), (pll->div + 1));
+ return sam9x60_frac_pll_compute_mul_frac(core, rate, *parent_rate, false);
}

-static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
- unsigned long rate,
- unsigned long parent_rate,
- bool update)
+static int sam9x60_frac_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
- const struct clk_pll_characteristics *characteristics =
- pll->characteristics;
- unsigned long bestremainder = ULONG_MAX;
- unsigned long maxdiv, mindiv, tmpdiv;
- long bestrate = -ERANGE;
- unsigned long bestdiv = 0;
- unsigned long bestmul = 0;
- unsigned long bestfrac = 0;
- unsigned long long fcore = 0;
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);

- if (rate < characteristics->output[0].min ||
- rate > characteristics->output[0].max)
- return -ERANGE;
+ return sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true);
+}

- if (!pll->characteristics->upll) {
- mindiv = parent_rate / rate;
- if (mindiv < 2)
- mindiv = 2;
+static const struct clk_ops sam9x60_frac_pll_ops = {
+ .prepare = sam9x60_frac_pll_prepare,
+ .unprepare = sam9x60_frac_pll_unprepare,
+ .is_prepared = sam9x60_frac_pll_is_prepared,
+ .recalc_rate = sam9x60_frac_pll_recalc_rate,
+ .round_rate = sam9x60_frac_pll_round_rate,
+ .set_rate = sam9x60_frac_pll_set_rate,
+};

- maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX, rate);
- if (maxdiv > PLL_DIV_MAX)
- maxdiv = PLL_DIV_MAX;
- } else {
- mindiv = maxdiv = UPLL_DIV;
- }
+static int sam9x60_div_pll_prepare(struct clk_hw *hw)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_div *div = to_sam9x60_div(core);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
+ unsigned int val, cdiv;

- for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
- unsigned long remainder;
- unsigned long tmprate;
- unsigned long tmpmul;
- unsigned long tmpfrac = 0;
+ spin_lock_irqsave(core->lock, flags);
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+ cdiv = (val & core->layout->div_mask) >> core->layout->div_shift;

- /*
- * Calculate the multiplier associated with the current
- * divider that provide the closest rate to the requested one.
- */
- tmpmul = mult_frac(rate, tmpdiv, parent_rate);
- tmprate = mult_frac(parent_rate, tmpmul, tmpdiv);
- remainder = rate - tmprate;
+ /* Stop if enabled an nothing changed. */
+ if (!!(val & core->layout->endiv_mask) && cdiv == div->div)
+ goto unlock;

- if (remainder) {
- tmpfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * tmpdiv * (1 << 22),
- parent_rate);
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ core->layout->div_mask | core->layout->endiv_mask,
+ (div->div << core->layout->div_shift) |
+ (1 << core->layout->endiv_shift));

- tmprate += DIV_ROUND_CLOSEST_ULL((u64)tmpfrac * parent_rate,
- tmpdiv * (1 << 22));
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);

- if (tmprate > rate)
- remainder = tmprate - rate;
- else
- remainder = rate - tmprate;
- }
+ while (!sam9x60_pll_ready(regmap, core->id))
+ cpu_relax();
+
+unlock:
+ spin_unlock_irqrestore(core->lock, flags);
+
+ return 0;
+}
+
+static void sam9x60_div_pll_unprepare(struct clk_hw *hw)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
+
+ spin_lock_irqsave(core->lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
+ core->layout->endiv_mask, 0);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+ AT91_PMC_PLL_UPDT_UPDATE | core->id);
+
+ spin_unlock_irqrestore(core->lock, flags);
+}
+
+static int sam9x60_div_pll_is_prepared(struct clk_hw *hw)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct regmap *regmap = core->regmap;
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(core->lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, core->id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+
+ spin_unlock_irqrestore(core->lock, flags);

- fcore = parent_rate * (tmpmul + 1) +
- ((u64)parent_rate * tmpfrac >> 22);
- if (fcore < FCORE_MIN || fcore > FCORE_MAX)
+ return !!(val & core->layout->endiv_mask);
+}
+
+static unsigned long sam9x60_div_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_div *div = to_sam9x60_div(core);
+
+ return DIV_ROUND_CLOSEST_ULL(parent_rate, (div->div + 1));
+}
+
+static long sam9x60_div_pll_compute_div(struct sam9x60_pll_core *core,
+ unsigned long *parent_rate,
+ unsigned long rate)
+{
+ const struct clk_pll_characteristics *characteristics =
+ core->characteristics;
+ struct clk_hw *parent = clk_hw_get_parent(&core->hw);
+ unsigned long tmp_rate, tmp_parent_rate, tmp_diff;
+ long best_diff = -1, best_rate = -EINVAL;
+ u32 divid, best_div;
+
+ if (!rate)
+ return 0;
+
+ if (rate < characteristics->output[0].min ||
+ rate > characteristics->output[0].max)
+ return -ERANGE;
+
+ for (divid = 1; divid < core->layout->div_mask; divid++) {
+ tmp_parent_rate = clk_hw_round_rate(parent, rate * divid);
+ if (!tmp_parent_rate)
continue;

- /*
- * Compare the remainder with the best remainder found until
- * now and elect a new best multiplier/divider pair if the
- * current remainder is smaller than the best one.
- */
- if (remainder < bestremainder) {
- bestremainder = remainder;
- bestdiv = tmpdiv;
- bestmul = tmpmul;
- bestrate = tmprate;
- bestfrac = tmpfrac;
+ tmp_rate = DIV_ROUND_CLOSEST_ULL(tmp_parent_rate, divid);
+ tmp_diff = abs(rate - tmp_rate);
+
+ if (best_diff < 0 || best_diff > tmp_diff) {
+ *parent_rate = tmp_parent_rate;
+ best_rate = tmp_rate;
+ best_diff = tmp_diff;
+ best_div = divid;
}

- /* We've found a perfect match! */
- if (!remainder)
+ if (!best_diff)
break;
}

- /* Check if bestrate is a valid output rate */
- if (fcore < FCORE_MIN || fcore > FCORE_MAX ||
- bestrate < characteristics->output[0].min ||
- bestrate > characteristics->output[0].max)
+ if (best_rate < characteristics->output[0].min ||
+ best_rate > characteristics->output[0].max)
return -ERANGE;

- if (update) {
- pll->div = bestdiv - 1;
- pll->mul = bestmul - 1;
- pll->frac = bestfrac;
- }
-
- return bestrate;
+ return best_rate;
}

-static long sam9x60_pll_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static long sam9x60_div_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);

- return sam9x60_pll_get_best_div_mul(pll, rate, *parent_rate, false);
+ return sam9x60_div_pll_compute_div(core, parent_rate, rate);
}

-static int sam9x60_pll_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
+static int sam9x60_div_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
{
- struct sam9x60_pll *pll = to_sam9x60_pll(hw);
+ struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
+ struct sam9x60_div *div = to_sam9x60_div(core);
+
+ div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1;

- return sam9x60_pll_get_best_div_mul(pll, rate, parent_rate, true);
+ return 0;
}

-static const struct clk_ops pll_ops = {
- .prepare = sam9x60_pll_prepare,
- .unprepare = sam9x60_pll_unprepare,
- .is_prepared = sam9x60_pll_is_prepared,
- .recalc_rate = sam9x60_pll_recalc_rate,
- .round_rate = sam9x60_pll_round_rate,
- .set_rate = sam9x60_pll_set_rate,
+static const struct clk_ops sam9x60_div_pll_ops = {
+ .prepare = sam9x60_div_pll_prepare,
+ .unprepare = sam9x60_div_pll_unprepare,
+ .is_prepared = sam9x60_div_pll_is_prepared,
+ .recalc_rate = sam9x60_div_pll_recalc_rate,
+ .round_rate = sam9x60_div_pll_round_rate,
+ .set_rate = sam9x60_div_pll_set_rate,
};

struct clk_hw * __init
-sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock,
- const char *name, const char *parent_name, u8 id,
- const struct clk_pll_characteristics *characteristics)
+sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name,
+ struct clk_hw *parent_hw, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, bool critical)
{
- struct sam9x60_pll *pll;
+ struct sam9x60_frac *frac;
struct clk_hw *hw;
struct clk_init_data init;
- unsigned int pllr;
+ unsigned long parent_rate, flags;
+ unsigned int val;
int ret;

- if (id > PLL_MAX_ID)
+ if (id > PLL_MAX_ID || !lock || !parent_hw)
return ERR_PTR(-EINVAL);

- pll = kzalloc(sizeof(*pll), GFP_KERNEL);
- if (!pll)
+ frac = kzalloc(sizeof(*frac), GFP_KERNEL);
+ if (!frac)
return ERR_PTR(-ENOMEM);

init.name = name;
- init.ops = &pll_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
+ init.ops = &sam9x60_frac_pll_ops;
init.flags = CLK_SET_RATE_GATE;
+ if (critical)
+ init.flags |= CLK_IS_CRITICAL;
+
+ frac->core.id = id;
+ frac->core.hw.init = &init;
+ frac->core.characteristics = characteristics;
+ frac->core.layout = layout;
+ frac->core.regmap = regmap;
+ frac->core.lock = lock;
+
+ spin_lock_irqsave(frac->core.lock, flags);
+ if (sam9x60_pll_ready(regmap, id)) {
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
+ frac->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
+ frac->frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val);
+ } else {
+ /*
+ * This means the PLL is not setup by bootloaders. In this
+ * case we need to set the minimum rate for it. Otherwise
+ * a clock child of this PLL may be enabled before setting
+ * its rate leading to enabling this PLL with unsupported
+ * rate. This will lead to PLL not being locked at all.
+ */
+ parent_rate = clk_hw_get_rate(parent_hw);
+ if (!parent_rate) {
+ hw = ERR_PTR(-EINVAL);
+ goto free;
+ }
+
+ ret = sam9x60_frac_pll_compute_mul_frac(&frac->core, FCORE_MIN,
+ parent_rate, true);
+ if (ret <= 0) {
+ hw = ERR_PTR(ret);
+ goto free;
+ }
+ }
+ spin_unlock_irqrestore(frac->core.lock, flags);
+
+ hw = &frac->core.hw;
+ ret = clk_hw_register(NULL, hw);
+ if (ret) {
+ kfree(frac);
+ hw = ERR_PTR(ret);
+ }

- pll->id = id;
- pll->hw.init = &init;
- pll->characteristics = characteristics;
- pll->regmap = regmap;
- pll->lock = lock;
+ return hw;
+
+free:
+ spin_unlock_irqrestore(frac->core.lock, flags);
+ kfree(frac);
+ return hw;
+}
+
+struct clk_hw * __init
+sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, bool critical)
+{
+ struct sam9x60_div *div;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ unsigned long flags;
+ unsigned int val;
+ int ret;
+
+ if (id > PLL_MAX_ID || !lock)
+ return ERR_PTR(-EINVAL);
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.ops = &sam9x60_div_pll_ops;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
+ if (critical)
+ init.flags |= CLK_IS_CRITICAL;
+
+ div->core.id = id;
+ div->core.hw.init = &init;
+ div->core.characteristics = characteristics;
+ div->core.layout = layout;
+ div->core.regmap = regmap;
+ div->core.lock = lock;
+
+ spin_lock_irqsave(div->core.lock, flags);
+
+ regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
+ AT91_PMC_PLL_UPDT_ID_MSK, id);
+ regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
+ div->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);

- regmap_write(regmap, AT91_PMC_PLL_UPDT, id);
- regmap_read(regmap, AT91_PMC_PLL_CTRL0, &pllr);
- pll->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, pllr);
- regmap_read(regmap, AT91_PMC_PLL_CTRL1, &pllr);
- pll->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, pllr);
+ spin_unlock_irqrestore(div->core.lock, flags);

- hw = &pll->hw;
+ hw = &div->core.hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
- kfree(pll);
+ kfree(div);
hw = ERR_PTR(ret);
}

diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 8d3e22f566cf..6340b9be8205 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -54,8 +54,14 @@ struct clk_master_characteristics {

struct clk_pll_layout {
u32 pllr_mask;
- u16 mul_mask;
+ u32 mul_mask;
+ u32 frac_mask;
+ u32 div_mask;
+ u32 endiv_mask;
u8 mul_shift;
+ u8 frac_shift;
+ u8 div_shift;
+ u8 endiv_shift;
};

extern const struct clk_pll_layout at91rm9200_pll_layout;
@@ -181,9 +187,17 @@ at91_clk_register_plldiv(struct regmap *regmap, const char *name,
const char *parent_name);

struct clk_hw * __init
-sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock,
- const char *name, const char *parent_name, u8 id,
- const struct clk_pll_characteristics *characteristics);
+sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, bool critical);
+
+struct clk_hw * __init
+sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char *parent_name,
+ struct clk_hw *parent_hw, u8 id,
+ const struct clk_pll_characteristics *characteristics,
+ const struct clk_pll_layout *layout, bool critical);

struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap, const char *name,
diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index f2306615c7e7..88574d799208 100644
--- a/drivers/clk/at91/sam9x60.c
+++ b/drivers/clk/at91/sam9x60.c
@@ -42,6 +42,20 @@ static const struct clk_pll_characteristics upll_characteristics = {
.upll = true,
};

+static const struct clk_pll_layout pll_frac_layout = {
+ .mul_mask = GENMASK(31, 24),
+ .frac_mask = GENMASK(21, 0),
+ .mul_shift = 24,
+ .frac_shift = 0,
+};
+
+static const struct clk_pll_layout pll_div_layout = {
+ .div_mask = GENMASK(7, 0),
+ .endiv_mask = BIT(29),
+ .div_shift = 0,
+ .endiv_shift = 29,
+};
+
static const struct clk_programmable_layout sam9x60_programmable_layout = {
.pres_mask = 0xff,
.pres_shift = 8,
@@ -156,6 +170,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
const char *td_slck_name, *md_slck_name, *mainxtal_name;
struct pmc_data *sam9x60_pmc;
const char *parent_names[6];
+ struct clk_hw *main_osc_hw;
struct regmap *regmap;
struct clk_hw *hw;
int i;
@@ -200,6 +215,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
bypass);
if (IS_ERR(hw))
goto err_free;
+ main_osc_hw = hw;

parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
@@ -209,15 +225,31 @@ static void __init sam9x60_pmc_setup(struct device_node *np)

sam9x60_pmc->chws[PMC_MAIN] = hw;

- hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "pllack",
- "mainck", 0, &plla_characteristics);
+ hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "pllack_fracck",
+ "mainck", sam9x60_pmc->chws[PMC_MAIN],
+ 0, &plla_characteristics,
+ &pll_frac_layout, true);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "pllack_divck",
+ "pllack_fracck", 0, &plla_characteristics,
+ &pll_div_layout, true);
if (IS_ERR(hw))
goto err_free;

sam9x60_pmc->chws[PMC_PLLACK] = hw;

- hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "upllck",
- "main_osc", 1, &upll_characteristics);
+ hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "upllck_fracck",
+ "main_osc", main_osc_hw, 1,
+ &upll_characteristics,
+ &pll_frac_layout, false);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "upllck_divck",
+ "upllck_fracck", 1, &upll_characteristics,
+ &pll_div_layout, false);
if (IS_ERR(hw))
goto err_free;

@@ -225,7 +257,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)

parent_names[0] = md_slck_name;
parent_names[1] = "mainck";
- parent_names[2] = "pllack";
+ parent_names[2] = "pllack_divck";
hw = at91_clk_register_master(regmap, "masterck", 3, parent_names,
&sam9x60_master_layout,
&mck_characteristics);
@@ -234,8 +266,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)

sam9x60_pmc->chws[PMC_MCK] = hw;

- parent_names[0] = "pllack";
- parent_names[1] = "upllck";
+ parent_names[0] = "pllack_divck";
+ parent_names[1] = "upllck_divck";
parent_names[2] = "main_osc";
hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
if (IS_ERR(hw))
@@ -245,8 +277,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
parent_names[1] = td_slck_name;
parent_names[2] = "mainck";
parent_names[3] = "masterck";
- parent_names[4] = "pllack";
- parent_names[5] = "upllck";
+ parent_names[4] = "pllack_divck";
+ parent_names[5] = "upllck_divck";
for (i = 0; i < 8; i++) {
char name[6];

--
2.7.4

2020-07-15 12:11:33

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 18/19] clk: at91: clk-utmi: add utmi support for sama7g5

Add UTMI support for SAMA7G5. SAMA7G5's UTMI control is done via
XTALF register. Values written at bits 2..0 in this register
correspond to the on board crystal oscillator frequency.

Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/clk-utmi.c | 103 ++++++++++++++++++++++++++++++++++++++++---
drivers/clk/at91/pmc.h | 4 ++
include/linux/clk/at91_pmc.h | 2 +
3 files changed, 104 insertions(+), 5 deletions(-)

diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
index f1ef4e1f41a9..df9f3fc3b6a6 100644
--- a/drivers/clk/at91/clk-utmi.c
+++ b/drivers/clk/at91/clk-utmi.c
@@ -120,9 +120,11 @@ static const struct clk_ops utmi_ops = {
.recalc_rate = clk_utmi_recalc_rate,
};

-struct clk_hw * __init
-at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
- const char *name, const char *parent_name)
+static struct clk_hw * __init
+at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
+ struct regmap *regmap_sfr,
+ const char *name, const char *parent_name,
+ const struct clk_ops *ops, unsigned long flags)
{
struct clk_utmi *utmi;
struct clk_hw *hw;
@@ -134,10 +136,10 @@ at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
return ERR_PTR(-ENOMEM);

init.name = name;
- init.ops = &utmi_ops;
+ init.ops = ops;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
- init.flags = CLK_SET_RATE_GATE;
+ init.flags = flags;

utmi->hw.init = &init;
utmi->regmap_pmc = regmap_pmc;
@@ -152,3 +154,94 @@ at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,

return hw;
}
+
+struct clk_hw * __init
+at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
+ const char *name, const char *parent_name)
+{
+ return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name,
+ parent_name, &utmi_ops, CLK_SET_RATE_GATE);
+}
+
+static int clk_utmi_sama7g5_prepare(struct clk_hw *hw)
+{
+ struct clk_utmi *utmi = to_clk_utmi(hw);
+ struct clk_hw *hw_parent;
+ unsigned long parent_rate;
+ unsigned int val;
+
+ hw_parent = clk_hw_get_parent(hw);
+ parent_rate = clk_hw_get_rate(hw_parent);
+
+ switch (parent_rate) {
+ case 16000000:
+ val = 0;
+ break;
+ case 20000000:
+ val = 2;
+ break;
+ case 24000000:
+ val = 3;
+ break;
+ case 32000000:
+ val = 5;
+ break;
+ default:
+ pr_err("UTMICK: unsupported main_xtal rate\n");
+ return -EINVAL;
+ }
+
+ regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val);
+
+ return 0;
+
+}
+
+static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
+{
+ struct clk_utmi *utmi = to_clk_utmi(hw);
+ struct clk_hw *hw_parent;
+ unsigned long parent_rate;
+ unsigned int val;
+
+ hw_parent = clk_hw_get_parent(hw);
+ parent_rate = clk_hw_get_rate(hw_parent);
+
+ regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val);
+ switch (val & 0x7) {
+ case 0:
+ if (parent_rate == 16000000)
+ return 1;
+ break;
+ case 2:
+ if (parent_rate == 20000000)
+ return 1;
+ break;
+ case 3:
+ if (parent_rate == 24000000)
+ return 1;
+ break;
+ case 5:
+ if (parent_rate == 32000000)
+ return 1;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct clk_ops sama7g5_utmi_ops = {
+ .prepare = clk_utmi_sama7g5_prepare,
+ .is_prepared = clk_utmi_sama7g5_is_prepared,
+ .recalc_rate = clk_utmi_recalc_rate,
+};
+
+struct clk_hw * __init
+at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name,
+ const char *parent_name)
+{
+ return at91_clk_register_utmi_internal(regmap_pmc, NULL, name,
+ parent_name, &sama7g5_utmi_ops, 0);
+}
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 6340b9be8205..7b86affc6d7c 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -236,6 +236,10 @@ struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
const char *name, const char *parent_name);

+struct clk_hw * __init
+at91_clk_sama7g5_register_utmi(struct regmap *regmap, const char *name,
+ const char *parent_name);
+
#ifdef CONFIG_PM
void pmc_register_id(u8 id);
void pmc_register_pck(u8 pck);
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index dc5e85f124e0..a4f82e836a7c 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -137,6 +137,8 @@
#define AT91_PMC_PLLADIV2_ON (1 << 12)
#define AT91_PMC_H32MXDIV BIT(24)

+#define AT91_PMC_XTALF 0x34 /* Main XTAL Frequency Register [SAMA7G5 only] */
+
#define AT91_PMC_USB 0x38 /* USB Clock Register [some SAM9 only] */
#define AT91_PMC_USBS (0x1 << 0) /* USB OHCI Input clock selection */
#define AT91_PMC_USBS_PLLA (0 << 0)
--
2.7.4

2020-07-15 12:12:08

by Claudiu Beznea

[permalink] [raw]
Subject: [PATCH 19/19] clk: at91: sama7g5: add clock support for sama7g5

Add clock support for SAMA7G5.

Signed-off-by: Claudiu Beznea <[email protected]>
---
drivers/clk/at91/Makefile | 1 +
drivers/clk/at91/sama7g5.c | 1059 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1060 insertions(+)
create mode 100644 drivers/clk/at91/sama7g5.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 8b90357f2a93..79301e1c1c36 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o
obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o
obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o
obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o
+obj-$(CONFIG_SOC_SAMA7G5) += sama7g5.o
diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c
new file mode 100644
index 000000000000..0db2ab3eca14
--- /dev/null
+++ b/drivers/clk/at91/sama7g5.c
@@ -0,0 +1,1059 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SAMA7G5 PMC code.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <[email protected]>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/at91.h>
+
+#include "pmc.h"
+
+#define SAMA7G5_INIT_TABLE(_table, _count) \
+ do { \
+ u8 _i; \
+ for (_i = 0; _i < (_count); _i++) \
+ (_table)[_i] = _i; \
+ } while (0)
+
+#define SAMA7G5_FILL_TABLE(_to, _from, _count) \
+ do { \
+ u8 _i; \
+ for (_i = 0; _i < (_count); _i++) { \
+ (_to)[_i] = (_from)[_i]; \
+ } \
+ } while (0)
+
+static DEFINE_SPINLOCK(pmc_pll_lock);
+static DEFINE_SPINLOCK(pmc_mckX_lock);
+
+/**
+ * PLL clocks identifiers
+ * @PLL_ID_CPU: CPU PLL identifier
+ * @PLL_ID_SYS: System PLL identifier
+ * @PLL_ID_DDR: DDR PLL identifier
+ * @PLL_ID_IMG: Image subsystem PLL identifier
+ * @PLL_ID_BAUD: Baud PLL identifier
+ * @PLL_ID_AUDIO: Audio PLL identifier
+ * @PLL_ID_ETH: Ethernet PLL identifier
+ */
+enum pll_ids {
+ PLL_ID_CPU,
+ PLL_ID_SYS,
+ PLL_ID_DDR,
+ PLL_ID_IMG,
+ PLL_ID_BAUD,
+ PLL_ID_AUDIO,
+ PLL_ID_ETH,
+ PLL_ID_MAX,
+};
+
+/**
+ * PLL type identifiers
+ * @PLL_TYPE_FRAC: fractional PLL identifier
+ * @PLL_TYPE_DIV: divider PLL identifier
+ */
+enum pll_type {
+ PLL_TYPE_FRAC,
+ PLL_TYPE_DIV,
+};
+
+/* Layout for fractional PLLs. */
+static const struct clk_pll_layout pll_layout_frac = {
+ .mul_mask = GENMASK(31, 24),
+ .frac_mask = GENMASK(21, 0),
+ .mul_shift = 24,
+ .frac_shift = 0,
+};
+
+/* Layout for DIVPMC dividers. */
+static const struct clk_pll_layout pll_layout_divpmc = {
+ .div_mask = GENMASK(7, 0),
+ .endiv_mask = BIT(29),
+ .div_shift = 0,
+ .endiv_shift = 29,
+};
+
+/* Layout for DIVIO dividers. */
+static const struct clk_pll_layout pll_layout_divio = {
+ .div_mask = GENMASK(19, 12),
+ .endiv_mask = BIT(30),
+ .div_shift = 12,
+ .endiv_shift = 30,
+};
+
+/**
+ * PLL clocks description
+ * @n: clock name
+ * @p: clock parent
+ * @l: clock layout
+ * @t: clock type
+ * @f: true if clock is critical and cannot be disabled
+ * @eid: export index in sama7g5->chws[] array
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ const struct clk_pll_layout *l;
+ u8 t;
+ u8 c;
+ u8 eid;
+} sama7g5_plls[][PLL_ID_MAX] = {
+ [PLL_ID_CPU] = {
+ { .n = "cpupll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .t = PLL_TYPE_FRAC,
+ .c = 1, },
+
+ { .n = "cpupll_divpmcck",
+ .p = "cpupll_fracck",
+ .l = &pll_layout_divpmc,
+ .t = PLL_TYPE_DIV,
+ .c = 1, },
+ },
+
+ [PLL_ID_SYS] = {
+ { .n = "syspll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .t = PLL_TYPE_FRAC,
+ .c = 1, },
+
+ { .n = "syspll_divpmcck",
+ .p = "syspll_fracck",
+ .l = &pll_layout_divpmc,
+ .t = PLL_TYPE_DIV,
+ .c = 1, },
+ },
+
+ [PLL_ID_DDR] = {
+ { .n = "ddrpll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .t = PLL_TYPE_FRAC,
+ .c = 1, },
+
+ { .n = "ddrpll_divpmcck",
+ .p = "ddrpll_fracck",
+ .l = &pll_layout_divpmc,
+ .t = PLL_TYPE_DIV,
+ .c = 1, },
+ },
+
+ [PLL_ID_IMG] = {
+ { .n = "imgpll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .t = PLL_TYPE_FRAC, },
+
+ { .n = "imgpll_divpmcck",
+ .p = "imgpll_fracck",
+ .l = &pll_layout_divpmc,
+ .t = PLL_TYPE_DIV, },
+ },
+
+ [PLL_ID_BAUD] = {
+ { .n = "baudpll_fracck",
+ .p = "mainck",
+ .l = &pll_layout_frac,
+ .t = PLL_TYPE_FRAC, },
+
+ { .n = "baudpll_divpmcck",
+ .p = "baudpll_fracck",
+ .l = &pll_layout_divpmc,
+ .t = PLL_TYPE_DIV, },
+ },
+
+ [PLL_ID_AUDIO] = {
+ { .n = "audiopll_fracck",
+ .p = "main_xtal",
+ .l = &pll_layout_frac,
+ .t = PLL_TYPE_FRAC, },
+
+ { .n = "audiopll_divpmcck",
+ .p = "audiopll_fracck",
+ .l = &pll_layout_divpmc,
+ .t = PLL_TYPE_DIV,
+ .eid = PMC_I2S0_MUX, },
+
+ { .n = "audiopll_diviock",
+ .p = "audiopll_fracck",
+ .l = &pll_layout_divio,
+ .t = PLL_TYPE_DIV,
+ .eid = PMC_I2S1_MUX, },
+ },
+
+ [PLL_ID_ETH] = {
+ { .n = "ethpll_fracck",
+ .p = "main_xtal",
+ .l = &pll_layout_frac,
+ .t = PLL_TYPE_FRAC, },
+
+ { .n = "ethpll_divpmcck",
+ .p = "ethpll_fracck",
+ .l = &pll_layout_divpmc,
+ .t = PLL_TYPE_DIV, },
+ },
+};
+
+/**
+ * Master clock (MCK[1..4]) description
+ * @n: clock name
+ * @ep: extra parents names array
+ * @ep_chg_chg_id: index in parents array that specifies the changeable
+ * parent
+ * @ep_count: extra parents count
+ * @ep_mux_table: mux table for extra parents
+ * @id: clock id
+ * @c: true if clock is critical and cannot be disabled
+ */
+static const struct {
+ const char *n;
+ const char *ep[4];
+ int ep_chg_id;
+ u8 ep_count;
+ u8 ep_mux_table[4];
+ u8 id;
+ u8 c;
+} sama7g5_mckx[] = {
+ { .n = "mck1",
+ .id = 1,
+ .ep = { "syspll_divpmcck", },
+ .ep_mux_table = { 5, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .c = 1, },
+
+ { .n = "mck2",
+ .id = 2,
+ .ep = { "ddrpll_divpmcck", },
+ .ep_mux_table = { 6, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .c = 1, },
+
+ { .n = "mck3",
+ .id = 3,
+ .ep = { "syspll_divpmcck", "ddrpll_divpmcck", "imgpll_divpmcck", },
+ .ep_mux_table = { 5, 6, 7, },
+ .ep_count = 3,
+ .ep_chg_id = 6, },
+
+ { .n = "mck4",
+ .id = 4,
+ .ep = { "syspll_divpmcck", },
+ .ep_mux_table = { 5, },
+ .ep_count = 1,
+ .ep_chg_id = INT_MIN,
+ .c = 1, },
+};
+
+/**
+ * System clock description
+ * @n: clock name
+ * @p: clock parent name
+ * @id: clock id
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ u8 id;
+} sama7g5_systemck[] = {
+ { .n = "pck0", .p = "prog0", .id = 8, },
+ { .n = "pck1", .p = "prog1", .id = 9, },
+ { .n = "pck2", .p = "prog2", .id = 10, },
+ { .n = "pck3", .p = "prog3", .id = 11, },
+ { .n = "pck4", .p = "prog4", .id = 12, },
+ { .n = "pck5", .p = "prog5", .id = 13, },
+ { .n = "pck6", .p = "prog6", .id = 14, },
+ { .n = "pck7", .p = "prog7", .id = 15, },
+};
+
+/* Mux table for programmable clocks. */
+static u32 sama7g5_prog_mux_table[] = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, };
+
+/**
+ * Peripheral clock description
+ * @n: clock name
+ * @p: clock parent name
+ * @r: clock range values
+ * @id: clock id
+ * @chgp: index in parent array of the changeable parent
+ */
+static const struct {
+ const char *n;
+ const char *p;
+ struct clk_range r;
+ u8 chgp;
+ u8 id;
+} sama7g5_periphck[] = {
+ { .n = "pioA_clk", .p = "mck0", .id = 11, },
+ { .n = "sfr_clk", .p = "mck1", .id = 19, },
+ { .n = "hsmc_clk", .p = "mck1", .id = 21, },
+ { .n = "xdmac0_clk", .p = "mck1", .id = 22, },
+ { .n = "xdmac1_clk", .p = "mck1", .id = 23, },
+ { .n = "xdmac2_clk", .p = "mck1", .id = 24, },
+ { .n = "acc_clk", .p = "mck1", .id = 25, },
+ { .n = "aes_clk", .p = "mck1", .id = 27, },
+ { .n = "tzaesbasc_clk", .p = "mck1", .id = 28, },
+ { .n = "asrc_clk", .p = "mck1", .id = 30, .r = { .max = 200000000, }, },
+ { .n = "cpkcc_clk", .p = "mck0", .id = 32, },
+ { .n = "csi_clk", .p = "mck3", .id = 33, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "csi2dc_clk", .p = "mck3", .id = 34, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "eic_clk", .p = "mck1", .id = 37, },
+ { .n = "flex0_clk", .p = "mck1", .id = 38, },
+ { .n = "flex1_clk", .p = "mck1", .id = 39, },
+ { .n = "flex2_clk", .p = "mck1", .id = 40, },
+ { .n = "flex3_clk", .p = "mck1", .id = 41, },
+ { .n = "flex4_clk", .p = "mck1", .id = 42, },
+ { .n = "flex5_clk", .p = "mck1", .id = 43, },
+ { .n = "flex6_clk", .p = "mck1", .id = 44, },
+ { .n = "flex7_clk", .p = "mck1", .id = 45, },
+ { .n = "flex8_clk", .p = "mck1", .id = 46, },
+ { .n = "flex9_clk", .p = "mck1", .id = 47, },
+ { .n = "flex10_clk", .p = "mck1", .id = 48, },
+ { .n = "flex11_clk", .p = "mck1", .id = 49, },
+ { .n = "gmac0_clk", .p = "mck1", .id = 51, },
+ { .n = "gmac1_clk", .p = "mck1", .id = 52, },
+ { .n = "icm_clk", .p = "mck1", .id = 55, },
+ { .n = "isc_clk", .p = "mck3", .id = 56, .r = { .max = 266000000, }, .chgp = 1, },
+ { .n = "i2smcc0_clk", .p = "mck1", .id = 57, .r = { .max = 200000000, }, },
+ { .n = "i2smcc1_clk", .p = "mck1", .id = 58, .r = { .max = 200000000, }, },
+ { .n = "matrix_clk", .p = "mck1", .id = 60, },
+ { .n = "mcan0_clk", .p = "mck1", .id = 61, .r = { .max = 200000000, }, },
+ { .n = "mcan1_clk", .p = "mck1", .id = 62, .r = { .max = 200000000, }, },
+ { .n = "mcan2_clk", .p = "mck1", .id = 63, .r = { .max = 200000000, }, },
+ { .n = "mcan3_clk", .p = "mck1", .id = 64, .r = { .max = 200000000, }, },
+ { .n = "mcan4_clk", .p = "mck1", .id = 65, .r = { .max = 200000000, }, },
+ { .n = "mcan5_clk", .p = "mck1", .id = 66, .r = { .max = 200000000, }, },
+ { .n = "pdmc0_clk", .p = "mck1", .id = 68, .r = { .max = 200000000, }, },
+ { .n = "pdmc1_clk", .p = "mck1", .id = 69, .r = { .max = 200000000, }, },
+ { .n = "pit64b0_clk", .p = "mck1", .id = 70, },
+ { .n = "pit64b1_clk", .p = "mck1", .id = 71, },
+ { .n = "pit64b2_clk", .p = "mck1", .id = 72, },
+ { .n = "pit64b3_clk", .p = "mck1", .id = 73, },
+ { .n = "pit64b4_clk", .p = "mck1", .id = 74, },
+ { .n = "pit64b5_clk", .p = "mck1", .id = 75, },
+ { .n = "pwm_clk", .p = "mck1", .id = 77, },
+ { .n = "qspi0_clk", .p = "mck1", .id = 78, },
+ { .n = "qspi1_clk", .p = "mck1", .id = 79, },
+ { .n = "sdmmc0_clk", .p = "mck1", .id = 80, },
+ { .n = "sdmmc1_clk", .p = "mck1", .id = 81, },
+ { .n = "sdmmc2_clk", .p = "mck1", .id = 82, },
+ { .n = "sha_clk", .p = "mck1", .id = 83, },
+ { .n = "spdifrx_clk", .p = "mck1", .id = 84, .r = { .max = 200000000, }, },
+ { .n = "spdiftx_clk", .p = "mck1", .id = 85, .r = { .max = 200000000, }, },
+ { .n = "ssc0_clk", .p = "mck1", .id = 86, .r = { .max = 200000000, }, },
+ { .n = "ssc1_clk", .p = "mck1", .id = 87, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch0_clk", .p = "mck1", .id = 88, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch1_clk", .p = "mck1", .id = 89, .r = { .max = 200000000, }, },
+ { .n = "tcb0_ch2_clk", .p = "mck1", .id = 90, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch0_clk", .p = "mck1", .id = 91, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch1_clk", .p = "mck1", .id = 92, .r = { .max = 200000000, }, },
+ { .n = "tcb1_ch2_clk", .p = "mck1", .id = 93, .r = { .max = 200000000, }, },
+ { .n = "tcpca_clk", .p = "mck1", .id = 94, },
+ { .n = "tcpcb_clk", .p = "mck1", .id = 95, },
+ { .n = "tdes_clk", .p = "mck1", .id = 96, },
+ { .n = "trng_clk", .p = "mck1", .id = 97, },
+ { .n = "udphsa_clk", .p = "mck1", .id = 104, },
+ { .n = "udphsb_clk", .p = "mck1", .id = 105, },
+ { .n = "uhphs_clk", .p = "mck1", .id = 106, },
+};
+
+/**
+ * Generic clock description
+ * @n: clock name
+ * @pp: PLL parents
+ * @pp_mux_table: PLL parents mux table
+ * @r: clock output range
+ * @pp_chg_id: id in parrent array of changeable PLL parent
+ * @pp_count: PLL parents count
+ * @id: clock id
+ */
+static const struct {
+ const char *n;
+ const char *pp[8];
+ const char pp_mux_table[8];
+ struct clk_range r;
+ int pp_chg_id;
+ u8 pp_count;
+ u8 id;
+} sama7g5_gck[] = {
+ { .n = "adc_gclk",
+ .id = 26,
+ .r = { .max = 100000000, },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 7, 9, },
+ .pp_count = 3,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "asrc_gclk",
+ .id = 30,
+ .r = { .max = 200000000 },
+ .pp = { "audiopll_divpmcck", },
+ .pp_mux_table = { 9, },
+ .pp_count = 1,
+ .pp_chg_id = 4, },
+
+ { .n = "csi_gclk",
+ .id = 33,
+ .r = { .max = 27000000 },
+ .pp = { "ddrpll_divpmcck", "imgpll_divpmcck", },
+ .pp_mux_table = { 6, 7, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex0_gclk",
+ .id = 38,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex1_gclk",
+ .id = 39,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex2_gclk",
+ .id = 40,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex3_gclk",
+ .id = 41,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex4_gclk",
+ .id = 42,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex5_gclk",
+ .id = 43,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex6_gclk",
+ .id = 44,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex7_gclk",
+ .id = 45,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex8_gclk",
+ .id = 46,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex9_gclk",
+ .id = 47,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex10_gclk",
+ .id = 48,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "flex11_gclk",
+ .id = 49,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac0_gclk",
+ .id = 51,
+ .r = { .max = 125000000 },
+ .pp = { "ethpll_divpmcck", },
+ .pp_mux_table = { 10, },
+ .pp_count = 1,
+ .pp_chg_id = 4, },
+
+ { .n = "gmac1_gclk",
+ .id = 52,
+ .r = { .max = 50000000 },
+ .pp = { "ethpll_divpmcck", },
+ .pp_mux_table = { 10, },
+ .pp_count = 1,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac0_tsu_gclk",
+ .id = 53,
+ .r = { .max = 300000000 },
+ .pp = { "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 9, 10, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "gmac1_tsu_gclk",
+ .id = 54,
+ .r = { .max = 300000000 },
+ .pp = { "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 9, 10, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "i2smcc0_gclk",
+ .id = 57,
+ .r = { .max = 100000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 5, },
+
+ { .n = "i2smcc1_gclk",
+ .id = 58,
+ .r = { .max = 100000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 5, },
+
+ { .n = "mcan0_gclk",
+ .id = 61,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan1_gclk",
+ .id = 62,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan2_gclk",
+ .id = 63,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan3_gclk",
+ .id = 64,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan4_gclk",
+ .id = 65,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "mcan5_gclk",
+ .id = 66,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pdmc0_gclk",
+ .id = 68,
+ .r = { .max = 50000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pdmc1_gclk",
+ .id = 69,
+ .r = { .max = 50000000, },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b0_gclk",
+ .id = 70,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b1_gclk",
+ .id = 71,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b2_gclk",
+ .id = 72,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b3_gclk",
+ .id = 73,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b4_gclk",
+ .id = 74,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "pit64b5_gclk",
+ .id = 75,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "qspi0_gclk",
+ .id = 78,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "qspi1_gclk",
+ .id = 79,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "sdmmc0_gclk",
+ .id = 80,
+ .r = { .max = 208000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = 5, },
+
+ { .n = "sdmmc1_gclk",
+ .id = 81,
+ .r = { .max = 208000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = 5, },
+
+ { .n = "sdmmc2_gclk",
+ .id = 82,
+ .r = { .max = 208000000 },
+ .pp = { "syspll_divpmcck", "baudpll_divpmcck", },
+ .pp_mux_table = { 5, 8, },
+ .pp_count = 2,
+ .pp_chg_id = 5, },
+
+ { .n = "spdifrx_gclk",
+ .id = 84,
+ .r = { .max = 150000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 5, },
+
+ { .n = "spdiftx_gclk",
+ .id = 85,
+ .r = { .max = 25000000 },
+ .pp = { "syspll_divpmcck", "audiopll_divpmcck", },
+ .pp_mux_table = { 5, 9, },
+ .pp_count = 2,
+ .pp_chg_id = 5, },
+
+ { .n = "tcb0_ch0_gclk",
+ .id = 88,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcb1_ch0_gclk",
+ .id = 91,
+ .r = { .max = 200000000 },
+ .pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+ "audiopll_divpmcck", "ethpll_divpmcck", },
+ .pp_mux_table = { 5, 7, 8, 9, 10, },
+ .pp_count = 5,
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcpca_gclk",
+ .id = 94,
+ .r = { .max = 32768, },
+ .pp_chg_id = INT_MIN, },
+
+ { .n = "tcpcb_gclk",
+ .id = 95,
+ .r = { .max = 32768, },
+ .pp_chg_id = INT_MIN, },
+};
+
+/* PLL output range. */
+static const struct clk_range pll_outputs[] = {
+ { .min = 2343750, .max = 1200000000 },
+};
+
+/* PLL characteristics. */
+static const struct clk_pll_characteristics pll_characteristics = {
+ .input = { .min = 12000000, .max = 50000000 },
+ .num_output = ARRAY_SIZE(pll_outputs),
+ .output = pll_outputs,
+};
+
+/* MCK0 characteristics. */
+static const struct clk_master_characteristics mck0_characteristics = {
+ .output = { .min = 140000000, .max = 200000000 },
+ .divisors = { 1, 2, 4, 3 },
+ .have_div3_pres = 1,
+};
+
+/* MCK0 layout. */
+static const struct clk_master_layout mck0_layout = {
+ .mask = 0x373,
+ .pres_shift = 4,
+ .offset = 0x28,
+};
+
+/* Programmable clock layout. */
+static const struct clk_programmable_layout programmable_layout = {
+ .pres_mask = 0xff,
+ .pres_shift = 8,
+ .css_mask = 0x1f,
+ .have_slck_mck = 0,
+ .is_pres_direct = 1,
+};
+
+/* Peripheral clock layout. */
+static const struct clk_pcr_layout sama7g5_pcr_layout = {
+ .offset = 0x88,
+ .cmd = BIT(31),
+ .gckcss_mask = GENMASK(12, 8),
+ .pid_mask = GENMASK(6, 0),
+};
+
+static void __init sama7g5_pmc_setup(struct device_node *np)
+{
+ const char *td_slck_name, *md_slck_name, *mainxtal_name;
+ struct pmc_data *sama7g5_pmc;
+ const char *parent_names[10];
+ void **alloc_mem = NULL;
+ int alloc_mem_size = 0;
+ struct regmap *regmap;
+ struct clk_hw *hw;
+ bool bypass;
+ int i, j;
+
+ i = of_property_match_string(np, "clock-names", "td_slck");
+ if (i < 0)
+ return;
+
+ td_slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "md_slck");
+ if (i < 0)
+ return;
+
+ md_slck_name = of_clk_get_parent_name(np, i);
+
+ i = of_property_match_string(np, "clock-names", "main_xtal");
+ if (i < 0)
+ return;
+
+ mainxtal_name = of_clk_get_parent_name(np, i);
+
+ regmap = device_node_to_regmap(np);
+ if (IS_ERR(regmap))
+ return;
+
+ sama7g5_pmc = pmc_data_allocate(PMC_I2S1_MUX + 1,
+ nck(sama7g5_systemck),
+ nck(sama7g5_periphck),
+ nck(sama7g5_gck));
+ if (!sama7g5_pmc)
+ return;
+
+ alloc_mem = kmalloc(sizeof(void *) *
+ (ARRAY_SIZE(sama7g5_mckx) + ARRAY_SIZE(sama7g5_gck)),
+ GFP_KERNEL);
+ if (!alloc_mem)
+ goto err_free;
+
+ hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
+ 50000000);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
+ bypass);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ parent_names[0] = "main_rc_osc";
+ parent_names[1] = "main_osc";
+ hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->chws[PMC_MAIN] = hw;
+
+ for (i = 0; i < PLL_ID_MAX; i++) {
+ for (j = 0; j < 3; j++) {
+ struct clk_hw *parent_hw;
+
+ if (!sama7g5_plls[i][j].n)
+ continue;
+
+ switch (sama7g5_plls[i][j].t) {
+ case PLL_TYPE_FRAC:
+ if (!strcmp(sama7g5_plls[i][j].p, "mainck"))
+ parent_hw = sama7g5_pmc->chws[PMC_MAIN];
+ else
+ parent_hw = __clk_get_hw(of_clk_get_by_name(np,
+ sama7g5_plls[i][j].p));
+
+ hw = sam9x60_clk_register_frac_pll(regmap,
+ &pmc_pll_lock, sama7g5_plls[i][j].n,
+ sama7g5_plls[i][j].p, parent_hw, i,
+ &pll_characteristics,
+ sama7g5_plls[i][j].l,
+ sama7g5_plls[i][j].c);
+ break;
+
+ case PLL_TYPE_DIV:
+ hw = sam9x60_clk_register_div_pll(regmap,
+ &pmc_pll_lock, sama7g5_plls[i][j].n,
+ sama7g5_plls[i][j].p, i,
+ &pll_characteristics,
+ sama7g5_plls[i][j].l,
+ sama7g5_plls[i][j].c);
+ break;
+
+ default:
+ continue;
+ }
+
+ if (IS_ERR(hw))
+ goto err_free;
+
+ if (sama7g5_plls[i][j].eid)
+ sama7g5_pmc->chws[sama7g5_plls[i][j].eid] = hw;
+ }
+ }
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = "mainck";
+ parent_names[2] = "cpupll_divpmcck";
+ parent_names[3] = "syspll_divpmcck";
+ hw = at91_clk_register_master(regmap, "mck0", 4, parent_names,
+ &mck0_layout, &mck0_characteristics);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->chws[PMC_MCK] = hw;
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ parent_names[3] = "mck0";
+ for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) {
+ u8 num_parents = 4 + sama7g5_mckx[i].ep_count;
+ u32 *mux_table;
+
+ mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
+ GFP_KERNEL);
+ if (!mux_table)
+ goto err_free;
+
+ SAMA7G5_INIT_TABLE(mux_table, 4);
+ SAMA7G5_FILL_TABLE(&mux_table[4], sama7g5_mckx[i].ep_mux_table,
+ sama7g5_mckx[i].ep_count);
+ SAMA7G5_FILL_TABLE(&parent_names[4], sama7g5_mckx[i].ep,
+ sama7g5_mckx[i].ep_count);
+
+ hw = at91_clk_sama7g5_register_master(regmap, sama7g5_mckx[i].n,
+ num_parents, parent_names, mux_table,
+ &pmc_mckX_lock, sama7g5_mckx[i].id,
+ sama7g5_mckx[i].c,
+ sama7g5_mckx[i].ep_chg_id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ alloc_mem[alloc_mem_size++] = mux_table;
+ }
+
+ hw = at91_clk_sama7g5_register_utmi(regmap, "utmick", "main_xtal");
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->chws[PMC_UTMI] = hw;
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ parent_names[3] = "mck0";
+ parent_names[4] = "syspll_divpmcck";
+ parent_names[5] = "ddrpll_divpmcck";
+ parent_names[6] = "imgpll_divpmcck";
+ parent_names[7] = "baudpll_divpmcck";
+ parent_names[8] = "audiopll_divpmcck";
+ parent_names[9] = "ethpll_divpmcck";
+ for (i = 0; i < 8; i++) {
+ char name[6];
+
+ snprintf(name, sizeof(name), "prog%d", i);
+
+ hw = at91_clk_register_programmable(regmap, name, parent_names,
+ 10, i,
+ &programmable_layout,
+ sama7g5_prog_mux_table);
+ if (IS_ERR(hw))
+ goto err_free;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama7g5_systemck); i++) {
+ hw = at91_clk_register_system(regmap, sama7g5_systemck[i].n,
+ sama7g5_systemck[i].p,
+ sama7g5_systemck[i].id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->shws[sama7g5_systemck[i].id] = hw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sama7g5_periphck); i++) {
+ hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
+ &sama7g5_pcr_layout,
+ sama7g5_periphck[i].n,
+ sama7g5_periphck[i].p,
+ sama7g5_periphck[i].id,
+ &sama7g5_periphck[i].r,
+ sama7g5_periphck[i].chgp ? 0 :
+ INT_MIN);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->phws[sama7g5_periphck[i].id] = hw;
+ }
+
+ parent_names[0] = md_slck_name;
+ parent_names[1] = td_slck_name;
+ parent_names[2] = "mainck";
+ parent_names[3] = "mck0";
+ for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) {
+ u8 num_parents = 4 + sama7g5_gck[i].pp_count;
+ u32 *mux_table;
+
+ mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
+ GFP_KERNEL);
+ if (!mux_table)
+ goto err_free;
+
+ SAMA7G5_INIT_TABLE(mux_table, 4);
+ SAMA7G5_FILL_TABLE(&mux_table[4], sama7g5_gck[i].pp_mux_table,
+ sama7g5_gck[i].pp_count);
+ SAMA7G5_FILL_TABLE(&parent_names[4], sama7g5_gck[i].pp,
+ sama7g5_gck[i].pp_count);
+
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
+ &sama7g5_pcr_layout,
+ sama7g5_gck[i].n,
+ parent_names, mux_table,
+ num_parents,
+ sama7g5_gck[i].id,
+ &sama7g5_gck[i].r,
+ sama7g5_gck[i].pp_chg_id);
+ if (IS_ERR(hw))
+ goto err_free;
+
+ sama7g5_pmc->ghws[sama7g5_gck[i].id] = hw;
+ alloc_mem[alloc_mem_size++] = mux_table;
+ }
+
+ of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama7g5_pmc);
+
+ return;
+
+err_free:
+ if (alloc_mem) {
+ for (i = 0; i < alloc_mem_size; i++)
+ kfree(alloc_mem[i]);
+ kfree(alloc_mem);
+ }
+
+ pmc_data_free(sama7g5_pmc);
+}
+
+/* Some clks are used for a clocksource */
+CLK_OF_DECLARE(sama7g5_pmc, "microchip,sama7g5-pmc", sama7g5_pmc_setup);
--
2.7.4

2020-07-15 15:42:12

by Claudiu Beznea

[permalink] [raw]
Subject: Re: [PATCH 09/19] clk: at91: sckc: register slow_rc with accuracy option



On 15.07.2020 14:24, Claudiu Beznea wrote:
> Register slow rc with accuracy option.
>
> Fixes: 04bcc4275e601 ("clk: at91: sckc: add support for SAM9X60")
> Signed-off-by: Claudiu Beznea <[email protected]>
> ---
> drivers/clk/at91/sckc.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
> index 15dc4cd86d76..2d65770d8665 100644
> --- a/drivers/clk/at91/sckc.c
> +++ b/drivers/clk/at91/sckc.c
> @@ -471,8 +471,9 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
> if (!regbase)
> return;
>
> - slow_rc = clk_hw_register_fixed_rate(NULL, parent_names[0], NULL, 0,
> - 32768);
> + slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
> + NULL, 0, 32768,
> + 93750000);

I forgot to mention in commit message (I'll do it in v2 once I'll
gather more comments on this series):

Chapter 57.7.5 of SAM9X60 datasheet specifies that the maximum drift of
this oscillator is +- 3KHz. I used that value and the formula at [1]
or the calculator at [2] to compute the PPB value.

[1] https://www.everythingrf.com/rf-calculators/ppm-to-hz-calculator
[2] https://www.changpuak.ch/electronics/ppm_to_Hz_converter.php


Thank you,
Claudiu Beznea

> if (IS_ERR(slow_rc))
> return;
>
>

2020-07-16 16:58:05

by Codrin Ciubotariu

[permalink] [raw]
Subject: Re: [PATCH 11/19] clk: at91: clk-generated: pass the id of changeable parent at registration

Hi Claudiu,

On 15.07.2020 14:24, Claudiu Beznea wrote:
> Pass the ID of changeable parent at registration. This will allow
> the scalability of this clock driver with regards to the changeable
> parent ID for versions of this IP where changeable parent is not the
> last one in the parents list (e.g. SAMA7G5). In
> clk_generated_best_diff() the *best_diff variable is check against
> tmp_diff variable using ">=" operator instead of ">" so that in case
> the requested frequency could be obtained using fix parents + gck
> dividers but the clock also supports changeable parent to be able
> to force the usage of the changeable parent.

This is a great feature!

>
> Signed-off-by: Claudiu Beznea <[email protected]>
> ---
> drivers/clk/at91/clk-generated.c | 26 ++++++++++++++------------
> drivers/clk/at91/dt-compat.c | 8 +++++---
> drivers/clk/at91/pmc.h | 4 ++--
> drivers/clk/at91/sam9x60.c | 3 +--
> drivers/clk/at91/sama5d2.c | 31 +++++++++++++++----------------
> 5 files changed, 37 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
> index 2448bdc63425..f9ca04c97128 100644
> --- a/drivers/clk/at91/clk-generated.c
> +++ b/drivers/clk/at91/clk-generated.c
> @@ -18,8 +18,6 @@
>
> #define GENERATED_MAX_DIV 255
>
> -#define GCK_INDEX_DT_AUDIO_PLL 5
> -
> struct clk_generated {
> struct clk_hw hw;
> struct regmap *regmap;
> @@ -29,7 +27,7 @@ struct clk_generated {
> u32 gckdiv;
> const struct clk_pcr_layout *layout;
> u8 parent_id;
> - bool audio_pll_allowed;
> + int chg_pid;
> };
>
> #define to_clk_generated(hw) \
> @@ -109,7 +107,7 @@ static void clk_generated_best_diff(struct clk_rate_request *req,
> tmp_rate = parent_rate / div;
> tmp_diff = abs(req->rate - tmp_rate);
>
> - if (*best_diff < 0 || *best_diff > tmp_diff) {
> + if (*best_diff < 0 || *best_diff >= tmp_diff) {
> *best_rate = tmp_rate;
> *best_diff = tmp_diff;
> req->best_parent_rate = parent_rate;
> @@ -129,7 +127,10 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
> int i;
> u32 div;
>
> - for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) {
> + for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
> + if (gck->chg_pid == i)
> + continue;
> +

One thing that the previous loop was preventing was to not allow other
gcks take clk_hw_get_num_parents(hw) - 1 as a parent. So the audio pll
(last one) was reserved for the gck of the audio peripherals only. With
this change, any peripheral can use chg_pid as a parent, preventing thus
its correct use by the peripherals that can actually need and change the
rate of chg_pid.

> parent = clk_hw_get_parent_by_index(hw, i);
> if (!parent)
> continue;
> @@ -161,10 +162,10 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
> * that the only clks able to modify gck rate are those of audio IPs.
> */

The above comment should be updated.

>
> - if (!gck->audio_pll_allowed)
> + if (gck->chg_pid < 0)
> goto end;

Best regards,
Codrin

2020-07-17 09:11:30

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH 02/19] clk: at91: clk-generated: check best_rate against ranges

On 15/07/2020 14:24:10+0300, Claudiu Beznea wrote:
> Check best_rate against available clock ranges.
>
> Fixes: df70aeef6083 ("clk: at91: add generated clock driver")
> Signed-off-by: Claudiu Beznea <[email protected]>
Reviewed-by: Alexandre Belloni <[email protected]>

> ---
> drivers/clk/at91/clk-generated.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
> index 995a13133cfb..f8e557e0e1b8 100644
> --- a/drivers/clk/at91/clk-generated.c
> +++ b/drivers/clk/at91/clk-generated.c
> @@ -185,8 +185,8 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
> __clk_get_name((req->best_parent_hw)->clk),
> req->best_parent_rate);
>
> - if (best_rate < 0)
> - return best_rate;
> + if (best_rate < 0 || (gck->range.max && best_rate > gck->range.max))
> + return -EINVAL;
>
> req->rate = best_rate;
> return 0;
> --
> 2.7.4
>

--
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

2020-07-17 09:11:51

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH 03/19] clk: at91: clk-sam9x60-pll: fix mul mask

On 15/07/2020 14:24:11+0300, Claudiu Beznea wrote:
> According to datasheet mul mask is on bits 31..24.
>
> Fixes: a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
> Signed-off-by: Claudiu Beznea <[email protected]>
Reviewed-by: Alexandre Belloni <[email protected]>

> ---
> drivers/clk/at91/clk-sam9x60-pll.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
> index e699803986e5..3522eae2edd6 100644
> --- a/drivers/clk/at91/clk-sam9x60-pll.c
> +++ b/drivers/clk/at91/clk-sam9x60-pll.c
> @@ -15,7 +15,7 @@
> #include "pmc.h"
>
> #define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
> -#define PMC_PLL_CTRL1_MUL_MSK GENMASK(30, 24)
> +#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
>
> #define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
> #define UPLL_DIV 2
> --
> 2.7.4
>

--
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

2020-07-17 09:15:30

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH 07/19] clk: at91: sam9x60-pll: use frac when setting frequency

On 15/07/2020 14:24:15+0300, Claudiu Beznea wrote:
> In commit a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
> the fractional part of PLL wasn't set on registers but it was
> calculated and taken into account for determining div and mul
> (see sam9x60_pll_get_best_div_mul()).
>

I think this becomes an issue only once 4/19 is applied so you should
probably squash those two together.

> Fixes: a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
> Signed-off-by: Claudiu Beznea <[email protected]>
> ---
> drivers/clk/at91/clk-sam9x60-pll.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
> index 00f2afd6e9b6..13e15bd48770 100644
> --- a/drivers/clk/at91/clk-sam9x60-pll.c
> +++ b/drivers/clk/at91/clk-sam9x60-pll.c
> @@ -16,6 +16,7 @@
>
> #define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
> #define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
> +#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
>
> #define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
> #define UPLL_DIV 2
> @@ -55,7 +56,7 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
> unsigned long flags;
> u8 div;
> u16 mul;
> - u32 val;
> + u32 val, frac;
>
> spin_lock_irqsave(pll->lock, flags);
> regmap_write(regmap, AT91_PMC_PLL_UPDT, pll->id);
> @@ -65,9 +66,10 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
>
> regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
> mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
> + frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val);
>
> if (sam9x60_pll_ready(regmap, pll->id) &&
> - (div == pll->div && mul == pll->mul)) {
> + (div == pll->div && mul == pll->mul && frac == pll->frac)) {
> spin_unlock_irqrestore(pll->lock, flags);
> return 0;
> }
> @@ -80,7 +82,8 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
> regmap_write(regmap, AT91_PMC_PLL_ACR, val);
>
> regmap_write(regmap, AT91_PMC_PLL_CTRL1,
> - FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul));
> + FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul) |
> + FIELD_PREP(PMC_PLL_CTRL1_FRACR_MSK, pll->frac));
>
> if (pll->characteristics->upll) {
> /* Enable the UTMI internal bandgap */
> --
> 2.7.4
>

--
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

2020-07-17 09:15:55

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH 05/19] clk: at91: sam9x60-pll: use logical or for range check

On 15/07/2020 14:24:13+0300, Claudiu Beznea wrote:
> Use logical or for range check. In case bestrate is not in
> characteristics->output[0].min..characteristics->output[0].max
> range we should return -ERANGE.
>
> Fixes: a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
> Signed-off-by: Claudiu Beznea <[email protected]>
Reviewed-by: Alexandre Belloni <[email protected]>

> ---
> drivers/clk/at91/clk-sam9x60-pll.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
> index d3152c0afcbf..dfb91c190bd1 100644
> --- a/drivers/clk/at91/clk-sam9x60-pll.c
> +++ b/drivers/clk/at91/clk-sam9x60-pll.c
> @@ -232,7 +232,7 @@ static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
> }
>
> /* Check if bestrate is a valid output rate */
> - if (bestrate < characteristics->output[0].min &&
> + if (bestrate < characteristics->output[0].min ||
> bestrate > characteristics->output[0].max)
> return -ERANGE;
>
> --
> 2.7.4
>

--
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

2020-07-17 09:19:14

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH 08/19] clk: at91: sam9x60: fix main rc oscillator frequency

On 15/07/2020 14:24:16+0300, Claudiu Beznea wrote:
> Main RC oscillator frequency is 12MHz according to datasheet
> (chapter 27.2).
>
> Fixes: 01e2113de9a52 ("clk: at91: add sam9x60 pmc driver")
> Signed-off-by: Claudiu Beznea <[email protected]>
Reviewed-by: Alexandre Belloni <[email protected]>

For the record I got that value from:
https://github.com/linux4sam/linux-at91/blob/c5fcb086200dc3b2e4a2b306778c6c7283d95755/arch/arm/boot/dts/sam9x60.dtsi#L701

> ---
> drivers/clk/at91/sam9x60.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
> index 633891b98d43..c8703d2a0886 100644
> --- a/drivers/clk/at91/sam9x60.c
> +++ b/drivers/clk/at91/sam9x60.c
> @@ -189,7 +189,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
> if (!sam9x60_pmc)
> return;
>
> - hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 24000000,
> + hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
> 50000000);
> if (IS_ERR(hw))
> goto err_free;
> --
> 2.7.4
>

--
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

2020-07-17 09:26:26

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH 06/19] clk: at91: sam9x60-pll: check fcore against ranges

On 15/07/2020 14:24:14+0300, Claudiu Beznea wrote:
> According to datasheet the range of 600-1200MHz is for the the

the is repeated here

> frequency generated by the fractional part of the PLL (namely
> Fcorepllck according to datasheet). With this in mind the output
> range of the PLL itself (fractional + div), taking into account
> that the diverder is 8 bits wide, is 600/256-1200Hz=2.3-1200MHz.
>

divider

> Fixes: a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
> Signed-off-by: Claudiu Beznea <[email protected]>
> ---
> drivers/clk/at91/clk-sam9x60-pll.c | 12 +++++++++++-
> drivers/clk/at91/sam9x60.c | 2 +-
> 2 files changed, 12 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
> index dfb91c190bd1..00f2afd6e9b6 100644
> --- a/drivers/clk/at91/clk-sam9x60-pll.c
> +++ b/drivers/clk/at91/clk-sam9x60-pll.c
> @@ -21,6 +21,9 @@
> #define UPLL_DIV 2
> #define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
>
> +#define FCORE_MIN (600000000)
> +#define FCORE_MAX (1200000000)
> +
> #define PLL_MAX_ID 1
>
> struct sam9x60_pll {
> @@ -169,6 +172,7 @@ static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
> unsigned long bestdiv = 0;
> unsigned long bestmul = 0;
> unsigned long bestfrac = 0;
> + unsigned long long fcore = 0;

maybe it would be best to go for u64 here as this is what you cast into
later.

>
> if (rate < characteristics->output[0].min ||
> rate > characteristics->output[0].max)
> @@ -213,6 +217,11 @@ static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
> remainder = rate - tmprate;
> }
>
> + fcore = parent_rate * (tmpmul + 1) +
> + ((u64)parent_rate * tmpfrac >> 22);
> + if (fcore < FCORE_MIN || fcore > FCORE_MAX)
> + continue;
> +
> /*
> * Compare the remainder with the best remainder found until
> * now and elect a new best multiplier/divider pair if the
> @@ -232,7 +241,8 @@ static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
> }
>
> /* Check if bestrate is a valid output rate */
> - if (bestrate < characteristics->output[0].min ||
> + if (fcore < FCORE_MIN || fcore > FCORE_MAX ||
> + bestrate < characteristics->output[0].min ||
> bestrate > characteristics->output[0].max)
> return -ERANGE;
>
> diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
> index 3e20aa68259f..633891b98d43 100644
> --- a/drivers/clk/at91/sam9x60.c
> +++ b/drivers/clk/at91/sam9x60.c
> @@ -22,7 +22,7 @@ static const struct clk_master_layout sam9x60_master_layout = {
> };
>
> static const struct clk_range plla_outputs[] = {
> - { .min = 300000000, .max = 600000000 },
> + { .min = 2343750, .max = 1200000000 },
> };
>
> static const struct clk_pll_characteristics plla_characteristics = {
> --
> 2.7.4
>

--
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

2020-07-17 15:10:30

by Alexandre Belloni

[permalink] [raw]
Subject: Re: [PATCH 10/19] clk: at91: replace conditional operator with double logical not

Hi,

On 15/07/2020 14:24:18+0300, Claudiu Beznea wrote:
> Replace conditional operator with double logical not.

I think you need to elaborate why you do it as the generated
instructions are exactly the same.

>
> Signed-off-by: Claudiu Beznea <[email protected]>
> ---
> drivers/clk/at91/clk-generated.c | 2 +-
> drivers/clk/at91/clk-main.c | 6 +++---
> drivers/clk/at91/clk-master.c | 2 +-
> drivers/clk/at91/clk-peripheral.c | 2 +-
> drivers/clk/at91/clk-system.c | 4 ++--
> 5 files changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
> index f8e557e0e1b8..2448bdc63425 100644
> --- a/drivers/clk/at91/clk-generated.c
> +++ b/drivers/clk/at91/clk-generated.c
> @@ -83,7 +83,7 @@ static int clk_generated_is_enabled(struct clk_hw *hw)
> regmap_read(gck->regmap, gck->layout->offset, &status);
> spin_unlock_irqrestore(gck->lock, flags);
>
> - return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
> + return !!(status & AT91_PMC_PCR_GCKEN);
> }
>
> static unsigned long
> diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
> index 37c22667e831..5c83e899084f 100644
> --- a/drivers/clk/at91/clk-main.c
> +++ b/drivers/clk/at91/clk-main.c
> @@ -175,7 +175,7 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap)
>
> regmap_read(regmap, AT91_PMC_SR, &status);
>
> - return status & AT91_PMC_MOSCRCS;
> + return !!(status & AT91_PMC_MOSCRCS);
> }
>
> static int clk_main_rc_osc_prepare(struct clk_hw *hw)
> @@ -336,7 +336,7 @@ static int clk_rm9200_main_is_prepared(struct clk_hw *hw)
>
> regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status);
>
> - return status & AT91_PMC_MAINRDY ? 1 : 0;
> + return !!(status & AT91_PMC_MAINRDY);
> }
>
> static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw,
> @@ -398,7 +398,7 @@ static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
>
> regmap_read(regmap, AT91_PMC_SR, &status);
>
> - return status & AT91_PMC_MOSCSELS ? 1 : 0;
> + return !!(status & AT91_PMC_MOSCSELS);
> }
>
> static int clk_sam9x5_main_prepare(struct clk_hw *hw)
> diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
> index e7e0ba652de1..88d545b1698c 100644
> --- a/drivers/clk/at91/clk-master.c
> +++ b/drivers/clk/at91/clk-master.c
> @@ -33,7 +33,7 @@ static inline bool clk_master_ready(struct regmap *regmap)
>
> regmap_read(regmap, AT91_PMC_SR, &status);
>
> - return status & AT91_PMC_MCKRDY ? 1 : 0;
> + return !!(status & AT91_PMC_MCKRDY);
> }
>
> static int clk_master_prepare(struct clk_hw *hw)
> diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
> index c2ab4860a2bf..4c9a4147dfe5 100644
> --- a/drivers/clk/at91/clk-peripheral.c
> +++ b/drivers/clk/at91/clk-peripheral.c
> @@ -208,7 +208,7 @@ static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
> regmap_read(periph->regmap, periph->layout->offset, &status);
> spin_unlock_irqrestore(periph->lock, flags);
>
> - return status & AT91_PMC_PCR_EN ? 1 : 0;
> + return !!(status & AT91_PMC_PCR_EN);
> }
>
> static unsigned long
> diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
> index c4b3877aa445..f83ec0de86c3 100644
> --- a/drivers/clk/at91/clk-system.c
> +++ b/drivers/clk/at91/clk-system.c
> @@ -34,7 +34,7 @@ static inline bool clk_system_ready(struct regmap *regmap, int id)
>
> regmap_read(regmap, AT91_PMC_SR, &status);
>
> - return status & (1 << id) ? 1 : 0;
> + return !!(status & (1 << id));
> }
>
> static int clk_system_prepare(struct clk_hw *hw)
> @@ -74,7 +74,7 @@ static int clk_system_is_prepared(struct clk_hw *hw)
>
> regmap_read(sys->regmap, AT91_PMC_SR, &status);
>
> - return status & (1 << sys->id) ? 1 : 0;
> + return !!(status & (1 << sys->id));
> }
>
> static const struct clk_ops system_ops = {
> --
> 2.7.4
>

--
Alexandre Belloni, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

2020-07-20 10:35:12

by Claudiu Beznea

[permalink] [raw]
Subject: Re: [PATCH 07/19] clk: at91: sam9x60-pll: use frac when setting frequency



On 17.07.2020 12:12, Alexandre Belloni wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
> On 15/07/2020 14:24:15+0300, Claudiu Beznea wrote:
>> In commit a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
>> the fractional part of PLL wasn't set on registers but it was
>> calculated and taken into account for determining div and mul
>> (see sam9x60_pll_get_best_div_mul()).
>>
>
> I think this becomes an issue only once 4/19 is applied so you should
> probably squash those two together.

I kept it separate as I was thinking about the scenario where
the bootloaders set up the frequency using also fractional part.

But at the same time I was thinking about squashing them together.
Now that there is also someone thinking about having them
together (you) I will squash them.

Thank you,
Claudiu Beznea

>
>> Fixes: a436c2a447e59 ("clk: at91: add sam9x60 PLL driver")
>> Signed-off-by: Claudiu Beznea <[email protected]>
>> ---
>> drivers/clk/at91/clk-sam9x60-pll.c | 9 ++++++---
>> 1 file changed, 6 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
>> index 00f2afd6e9b6..13e15bd48770 100644
>> --- a/drivers/clk/at91/clk-sam9x60-pll.c
>> +++ b/drivers/clk/at91/clk-sam9x60-pll.c
>> @@ -16,6 +16,7 @@
>>
>> #define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
>> #define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
>> +#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
>>
>> #define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
>> #define UPLL_DIV 2
>> @@ -55,7 +56,7 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
>> unsigned long flags;
>> u8 div;
>> u16 mul;
>> - u32 val;
>> + u32 val, frac;
>>
>> spin_lock_irqsave(pll->lock, flags);
>> regmap_write(regmap, AT91_PMC_PLL_UPDT, pll->id);
>> @@ -65,9 +66,10 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
>>
>> regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
>> mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
>> + frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val);
>>
>> if (sam9x60_pll_ready(regmap, pll->id) &&
>> - (div == pll->div && mul == pll->mul)) {
>> + (div == pll->div && mul == pll->mul && frac == pll->frac)) {
>> spin_unlock_irqrestore(pll->lock, flags);
>> return 0;
>> }
>> @@ -80,7 +82,8 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
>> regmap_write(regmap, AT91_PMC_PLL_ACR, val);
>>
>> regmap_write(regmap, AT91_PMC_PLL_CTRL1,
>> - FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul));
>> + FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul) |
>> + FIELD_PREP(PMC_PLL_CTRL1_FRACR_MSK, pll->frac));
>>
>> if (pll->characteristics->upll) {
>> /* Enable the UTMI internal bandgap */
>> --
>> 2.7.4
>>
>
> --
> Alexandre Belloni, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
>

2020-07-20 10:37:22

by Claudiu Beznea

[permalink] [raw]
Subject: Re: [PATCH 10/19] clk: at91: replace conditional operator with double logical not



On 17.07.2020 18:07, Alexandre Belloni wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
> Hi,
>
> On 15/07/2020 14:24:18+0300, Claudiu Beznea wrote:
>> Replace conditional operator with double logical not.
>
> I think you need to elaborate why you do it as the generated
> instructions are exactly the same.

I don't have strong support for this one. In general I'm seeing
the usage of !! instead of ?: for 0 and 1 results as being simpler
in terms of code reading.

>
>>
>> Signed-off-by: Claudiu Beznea <[email protected]>
>> ---
>> drivers/clk/at91/clk-generated.c | 2 +-
>> drivers/clk/at91/clk-main.c | 6 +++---
>> drivers/clk/at91/clk-master.c | 2 +-
>> drivers/clk/at91/clk-peripheral.c | 2 +-
>> drivers/clk/at91/clk-system.c | 4 ++--
>> 5 files changed, 8 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
>> index f8e557e0e1b8..2448bdc63425 100644
>> --- a/drivers/clk/at91/clk-generated.c
>> +++ b/drivers/clk/at91/clk-generated.c
>> @@ -83,7 +83,7 @@ static int clk_generated_is_enabled(struct clk_hw *hw)
>> regmap_read(gck->regmap, gck->layout->offset, &status);
>> spin_unlock_irqrestore(gck->lock, flags);
>>
>> - return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
>> + return !!(status & AT91_PMC_PCR_GCKEN);
>> }
>>
>> static unsigned long
>> diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
>> index 37c22667e831..5c83e899084f 100644
>> --- a/drivers/clk/at91/clk-main.c
>> +++ b/drivers/clk/at91/clk-main.c
>> @@ -175,7 +175,7 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap)
>>
>> regmap_read(regmap, AT91_PMC_SR, &status);
>>
>> - return status & AT91_PMC_MOSCRCS;
>> + return !!(status & AT91_PMC_MOSCRCS);
>> }
>>
>> static int clk_main_rc_osc_prepare(struct clk_hw *hw)
>> @@ -336,7 +336,7 @@ static int clk_rm9200_main_is_prepared(struct clk_hw *hw)
>>
>> regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status);
>>
>> - return status & AT91_PMC_MAINRDY ? 1 : 0;
>> + return !!(status & AT91_PMC_MAINRDY);
>> }
>>
>> static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw,
>> @@ -398,7 +398,7 @@ static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
>>
>> regmap_read(regmap, AT91_PMC_SR, &status);
>>
>> - return status & AT91_PMC_MOSCSELS ? 1 : 0;
>> + return !!(status & AT91_PMC_MOSCSELS);
>> }
>>
>> static int clk_sam9x5_main_prepare(struct clk_hw *hw)
>> diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
>> index e7e0ba652de1..88d545b1698c 100644
>> --- a/drivers/clk/at91/clk-master.c
>> +++ b/drivers/clk/at91/clk-master.c
>> @@ -33,7 +33,7 @@ static inline bool clk_master_ready(struct regmap *regmap)
>>
>> regmap_read(regmap, AT91_PMC_SR, &status);
>>
>> - return status & AT91_PMC_MCKRDY ? 1 : 0;
>> + return !!(status & AT91_PMC_MCKRDY);
>> }
>>
>> static int clk_master_prepare(struct clk_hw *hw)
>> diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
>> index c2ab4860a2bf..4c9a4147dfe5 100644
>> --- a/drivers/clk/at91/clk-peripheral.c
>> +++ b/drivers/clk/at91/clk-peripheral.c
>> @@ -208,7 +208,7 @@ static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
>> regmap_read(periph->regmap, periph->layout->offset, &status);
>> spin_unlock_irqrestore(periph->lock, flags);
>>
>> - return status & AT91_PMC_PCR_EN ? 1 : 0;
>> + return !!(status & AT91_PMC_PCR_EN);
>> }
>>
>> static unsigned long
>> diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
>> index c4b3877aa445..f83ec0de86c3 100644
>> --- a/drivers/clk/at91/clk-system.c
>> +++ b/drivers/clk/at91/clk-system.c
>> @@ -34,7 +34,7 @@ static inline bool clk_system_ready(struct regmap *regmap, int id)
>>
>> regmap_read(regmap, AT91_PMC_SR, &status);
>>
>> - return status & (1 << id) ? 1 : 0;
>> + return !!(status & (1 << id));
>> }
>>
>> static int clk_system_prepare(struct clk_hw *hw)
>> @@ -74,7 +74,7 @@ static int clk_system_is_prepared(struct clk_hw *hw)
>>
>> regmap_read(sys->regmap, AT91_PMC_SR, &status);
>>
>> - return status & (1 << sys->id) ? 1 : 0;
>> + return !!(status & (1 << sys->id));
>> }
>>
>> static const struct clk_ops system_ops = {
>> --
>> 2.7.4
>>
>
> --
> Alexandre Belloni, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
>