aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/at91
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/at91')
-rw-r--r--drivers/clk/at91/Makefile1
-rw-r--r--drivers/clk/at91/at91rm9200.c3
-rw-r--r--drivers/clk/at91/at91sam9260.c3
-rw-r--r--drivers/clk/at91/at91sam9g45.c5
-rw-r--r--drivers/clk/at91/at91sam9n12.c7
-rw-r--r--drivers/clk/at91/at91sam9rl.c3
-rw-r--r--drivers/clk/at91/at91sam9x5.c7
-rw-r--r--drivers/clk/at91/clk-generated.c44
-rw-r--r--drivers/clk/at91/clk-main.c6
-rw-r--r--drivers/clk/at91/clk-master.c310
-rw-r--r--drivers/clk/at91/clk-peripheral.c111
-rw-r--r--drivers/clk/at91/clk-programmable.c11
-rw-r--r--drivers/clk/at91/clk-sam9x60-pll.c547
-rw-r--r--drivers/clk/at91/clk-system.c4
-rw-r--r--drivers/clk/at91/clk-utmi.c103
-rw-r--r--drivers/clk/at91/dt-compat.c25
-rw-r--r--drivers/clk/at91/pmc.h43
-rw-r--r--drivers/clk/at91/sam9x60.c66
-rw-r--r--drivers/clk/at91/sama5d2.c41
-rw-r--r--drivers/clk/at91/sama5d3.c8
-rw-r--r--drivers/clk/at91/sama5d4.c7
-rw-r--r--drivers/clk/at91/sama7g5.c1059
-rw-r--r--drivers/clk/at91/sckc.c5
23 files changed, 2140 insertions, 279 deletions
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/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..c88ee20bee31 100644
--- a/drivers/clk/at91/at91sam9g45.c
+++ b/drivers/clk/at91/at91sam9g45.c
@@ -111,7 +111,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
return;
mainxtal_name = of_clk_get_parent_name(np, i);
- regmap = syscon_node_to_regmap(np);
+ regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
@@ -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 630dc5d87171..93f7eb216122 100644
--- a/drivers/clk/at91/at91sam9n12.c
+++ b/drivers/clk/at91/at91sam9n12.c
@@ -124,7 +124,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
return;
mainxtal_name = of_clk_get_parent_name(np, i);
- regmap = syscon_node_to_regmap(np);
+ regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
@@ -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;
@@ -222,7 +223,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/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 0ce3da080287..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;
@@ -257,7 +258,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 +271,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-generated.c b/drivers/clk/at91/clk-generated.c
index 44a46dcc0518..b4fc8d71daf2 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -18,18 +18,17 @@
#define GENERATED_MAX_DIV 255
-#define GCK_INDEX_DT_AUDIO_PLL 5
-
struct clk_generated {
struct clk_hw hw;
struct regmap *regmap;
struct clk_range range;
spinlock_t *lock;
+ u32 *mux_table;
u32 id;
u32 gckdiv;
const struct clk_pcr_layout *layout;
u8 parent_id;
- bool audio_pll_allowed;
+ int chg_pid;
};
#define to_clk_generated(hw) \
@@ -83,7 +82,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
@@ -109,7 +108,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 +128,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,16 +163,17 @@ 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;
for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
req_parent.rate = req->rate * div;
- __clk_determine_rate(parent, &req_parent);
+ if (__clk_determine_rate(parent, &req_parent))
+ continue;
clk_generated_best_diff(req, parent, req_parent.rate, div,
&best_diff, &best_rate);
@@ -184,8 +187,8 @@ end:
__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;
@@ -199,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;
}
@@ -271,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, bool pll_audio,
- const struct clk_range *range)
+ 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;
@@ -287,16 +295,18 @@ 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;
+ gck->mux_table = mux_table;
clk_generated_startup(gck);
hw = &gck->hw;
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..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 ? 1 : 0;
+ 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/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
index c2ab4860a2bf..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) \
@@ -208,7 +209,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
@@ -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/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/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
index e699803986e5..b473298ef7e6 100644
--- a/drivers/clk/at91/clk-sam9x60-pll.c
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -15,26 +15,41 @@
#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 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
#define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
-#define PLL_MAX_ID 1
+#define FCORE_MIN (600000000)
+#define FCORE_MAX (1200000000)
-struct sam9x60_pll {
- struct clk_hw hw;
+#define PLL_MAX_ID 7
+
+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)
{
@@ -45,41 +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;
+ 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);
+ 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)) {
- 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));
+ (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);
@@ -94,221 +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;
+
+ spin_lock_irqsave(core->lock, flags);
- return sam9x60_pll_ready(pll->regmap, pll->id);
+ 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);
- spin_lock_irqsave(pll->lock, flags);
+ return sam9x60_pll_ready(core->regmap, core->id);
+}
- regmap_write(pll->regmap, AT91_PMC_PLL_UPDT, pll->id);
+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_update_bits(pll->regmap, AT91_PMC_PLL_CTRL0,
- AT91_PMC_PLL_CTRL0_ENPLLCK, 0);
+ if (rate < FCORE_MIN || rate > FCORE_MAX)
+ return -ERANGE;
- regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
+ /*
+ * 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_CTRL0,
- AT91_PMC_PLL_CTRL0_ENPLL, 0);
+ if (remainder) {
+ nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
+ parent_rate);
- if (pll->characteristics->upll)
- regmap_update_bits(pll->regmap, AT91_PMC_PLL_ACR,
- AT91_PMC_PLL_ACR_UTMIBG |
- AT91_PMC_PLL_ACR_UTMIVR, 0);
+ tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
+ (1 << 22));
+ }
- regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT,
- AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
+ /* Check if resulted rate is a valid. */
+ if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
+ return -ERANGE;
- spin_unlock_irqrestore(pll->lock, flags);
+ if (update) {
+ frac->mul = nmul - 1;
+ frac->frac = nfrac;
+ }
+
+ 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 (parent_rate * (pll->mul + 1)) / (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;
+ 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();
- /*
- * 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;
+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);
+
+ 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;
+
+ 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 (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/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 = {
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/dt-compat.c b/drivers/clk/at91/dt-compat.c
index aa1754eac59f..a50084de97d4 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,13 @@ 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);
+ parent_names, NULL,
+ num_parents, id, &range,
+ chg_pid);
if (IS_ERR(hw))
continue;
@@ -460,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))
@@ -673,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;
@@ -707,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;
@@ -717,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 df616f2937e7..7b86affc6d7c 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;
@@ -122,8 +128,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);
+ u32 *mux_table, 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,
@@ -155,13 +161,21 @@ 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
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,
@@ -173,14 +187,23 @@ 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,
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,
@@ -213,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/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c
index 3e20aa68259f..ab6318c0589e 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 = {
@@ -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;
@@ -178,7 +193,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
return;
mainxtal_name = of_clk_get_parent_name(np, i);
- regmap = syscon_node_to_regmap(np);
+ regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
@@ -189,7 +204,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;
@@ -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];
@@ -254,7 +286,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;
@@ -277,7 +310,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;
@@ -288,10 +321,9 @@ 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,
- 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..8b220762941a 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 = {
@@ -269,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;
@@ -292,7 +292,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;
@@ -305,7 +305,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;
@@ -322,10 +323,10 @@ 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].pll,
- &sama5d2_gck[i].r);
+ &sama5d2_gck[i].r,
+ sama5d2_gck[i].chg_pid);
if (IS_ERR(hw))
goto err_free;
diff --git a/drivers/clk/at91/sama5d3.c b/drivers/clk/at91/sama5d3.c
index 5e4e44dd4c37..7c6e0a5b9dc8 100644
--- a/drivers/clk/at91/sama5d3.c
+++ b/drivers/clk/at91/sama5d3.c
@@ -121,7 +121,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
return;
mainxtal_name = of_clk_get_parent_name(np, i);
- regmap = syscon_node_to_regmap(np);
+ regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
@@ -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;
@@ -223,7 +224,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..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;
@@ -246,7 +247,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 +260,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;
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 <claudiu.beznea@microchip.com>
+ *
+ */
+#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);
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;