Commit dd9c697a authored by Stephen Boyd's avatar Stephen Boyd

Merge branches 'clk-microchip', 'clk-mmp', 'clk-unused' and 'clk-at91' into clk-next

 - Add support for SAMA7G5 SoC clks
 - Microchip Sparx5 DPLL clk

* clk-microchip:
  clk: sparx5: Add Sparx5 SoC DPLL clock driver
  dt-bindings: clock: sparx5: Add bindings include file

* clk-mmp:
  clk: mmp: avoid missing prototype warning

* clk-unused:
  clk: drop unused function __clk_get_flags

* clk-at91:
  clk: at91: sama7g5: add clock support for sama7g5
  clk: at91: clk-utmi: add utmi support for sama7g5
  clk: at91: clk-sam9x60-pll: re-factor to support plls with multiple outputs
  clk: at91: add macro for pll ids mask
  clk: at91: clk-programmable: add mux_table option
  clk: at91: clk-peripheral: add support for changeable parent rate
  clk: at91: clk-master: add master clock support for SAMA7G5
  clk: at91: clk-generated: add mux_table option
  clk: at91: clk-generated: pass the id of changeable parent at registration
  clk: at91: replace conditional operator with double logical not
  clk: at91: sckc: register slow_rc with accuracy option
  clk: at91: sam9x60: fix main rc oscillator frequency
  clk: at91: sam9x60-pll: use frac when setting frequency
  clk: at91: sam9x60-pll: check fcore against ranges
  clk: at91: sam9x60-pll: use logical or for range check
  clk: at91: clk-sam9x60-pll: fix mul mask
  clk: at91: clk-generated: check best_rate against ranges
  clk: at91: clk-generated: continue if __clk_determine_rate() returns error
  clk: at91: fix possible dead lock in new drivers
...@@ -28,6 +28,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o ...@@ -28,6 +28,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_SPARX5) += clk-sparx5.o
obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o
obj-$(CONFIG_COMMON_CLK_FSL_SAI) += clk-fsl-sai.o obj-$(CONFIG_COMMON_CLK_FSL_SAI) += clk-fsl-sai.o
obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o
......
...@@ -23,3 +23,4 @@ obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o ...@@ -23,3 +23,4 @@ obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o
obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o
obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o
obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o
obj-$(CONFIG_SOC_SAMA7G5) += sama7g5.o
...@@ -160,7 +160,8 @@ static void __init at91rm9200_pmc_setup(struct device_node *np) ...@@ -160,7 +160,8 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 4, i, parent_names, 4, i,
&at91rm9200_programmable_layout); &at91rm9200_programmable_layout,
NULL);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
......
...@@ -436,7 +436,8 @@ static void __init at91sam926x_pmc_setup(struct device_node *np, ...@@ -436,7 +436,8 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 4, i, parent_names, 4, i,
&at91rm9200_programmable_layout); &at91rm9200_programmable_layout,
NULL);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
......
...@@ -111,7 +111,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np) ...@@ -111,7 +111,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
return; return;
mainxtal_name = of_clk_get_parent_name(np, i); 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)) if (IS_ERR(regmap))
return; return;
...@@ -181,7 +181,8 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np) ...@@ -181,7 +181,8 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i, parent_names, 5, i,
&at91sam9g45_programmable_layout); &at91sam9g45_programmable_layout,
NULL);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
......
...@@ -124,7 +124,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np) ...@@ -124,7 +124,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
return; return;
mainxtal_name = of_clk_get_parent_name(np, i); 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)) if (IS_ERR(regmap))
return; return;
...@@ -199,7 +199,8 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np) ...@@ -199,7 +199,8 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i, parent_names, 5, i,
&at91sam9x5_programmable_layout); &at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -222,7 +223,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np) ...@@ -222,7 +223,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
at91sam9n12_periphck[i].n, at91sam9n12_periphck[i].n,
"masterck", "masterck",
at91sam9n12_periphck[i].id, at91sam9n12_periphck[i].id,
&range); &range, INT_MIN);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
......
...@@ -137,7 +137,8 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np) ...@@ -137,7 +137,8 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i, parent_names, 5, i,
&at91rm9200_programmable_layout); &at91rm9200_programmable_layout,
NULL);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
......
...@@ -226,7 +226,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, ...@@ -226,7 +226,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i, parent_names, 5, i,
&at91sam9x5_programmable_layout); &at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -257,7 +258,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, ...@@ -257,7 +258,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
at91sam9x5_periphck[i].n, at91sam9x5_periphck[i].n,
"masterck", "masterck",
at91sam9x5_periphck[i].id, at91sam9x5_periphck[i].id,
&range); &range, INT_MIN);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -270,7 +271,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, ...@@ -270,7 +271,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
extra_pcks[i].n, extra_pcks[i].n,
"masterck", "masterck",
extra_pcks[i].id, extra_pcks[i].id,
&range); &range, INT_MIN);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
......
...@@ -18,18 +18,17 @@ ...@@ -18,18 +18,17 @@
#define GENERATED_MAX_DIV 255 #define GENERATED_MAX_DIV 255
#define GCK_INDEX_DT_AUDIO_PLL 5
struct clk_generated { struct clk_generated {
struct clk_hw hw; struct clk_hw hw;
struct regmap *regmap; struct regmap *regmap;
struct clk_range range; struct clk_range range;
spinlock_t *lock; spinlock_t *lock;
u32 *mux_table;
u32 id; u32 id;
u32 gckdiv; u32 gckdiv;
const struct clk_pcr_layout *layout; const struct clk_pcr_layout *layout;
u8 parent_id; u8 parent_id;
bool audio_pll_allowed; int chg_pid;
}; };
#define to_clk_generated(hw) \ #define to_clk_generated(hw) \
...@@ -83,7 +82,7 @@ static int clk_generated_is_enabled(struct clk_hw *hw) ...@@ -83,7 +82,7 @@ static int clk_generated_is_enabled(struct clk_hw *hw)
regmap_read(gck->regmap, gck->layout->offset, &status); regmap_read(gck->regmap, gck->layout->offset, &status);
spin_unlock_irqrestore(gck->lock, flags); spin_unlock_irqrestore(gck->lock, flags);
return status & AT91_PMC_PCR_GCKEN ? 1 : 0; return !!(status & AT91_PMC_PCR_GCKEN);
} }
static unsigned long static unsigned long
...@@ -109,7 +108,7 @@ static void clk_generated_best_diff(struct clk_rate_request *req, ...@@ -109,7 +108,7 @@ static void clk_generated_best_diff(struct clk_rate_request *req,
tmp_rate = parent_rate / div; tmp_rate = parent_rate / div;
tmp_diff = abs(req->rate - tmp_rate); 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_rate = tmp_rate;
*best_diff = tmp_diff; *best_diff = tmp_diff;
req->best_parent_rate = parent_rate; req->best_parent_rate = parent_rate;
...@@ -129,7 +128,10 @@ static int clk_generated_determine_rate(struct clk_hw *hw, ...@@ -129,7 +128,10 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
int i; int i;
u32 div; 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); parent = clk_hw_get_parent_by_index(hw, i);
if (!parent) if (!parent)
continue; continue;
...@@ -161,16 +163,17 @@ static int clk_generated_determine_rate(struct clk_hw *hw, ...@@ -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. * 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; 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) if (!parent)
goto end; goto end;
for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
req_parent.rate = req->rate * 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, clk_generated_best_diff(req, parent, req_parent.rate, div,
&best_diff, &best_rate); &best_diff, &best_rate);
...@@ -184,8 +187,8 @@ static int clk_generated_determine_rate(struct clk_hw *hw, ...@@ -184,8 +187,8 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
__clk_get_name((req->best_parent_hw)->clk), __clk_get_name((req->best_parent_hw)->clk),
req->best_parent_rate); req->best_parent_rate);
if (best_rate < 0) if (best_rate < 0 || (gck->range.max && best_rate > gck->range.max))
return best_rate; return -EINVAL;
req->rate = best_rate; req->rate = best_rate;
return 0; return 0;
...@@ -199,7 +202,11 @@ static int clk_generated_set_parent(struct clk_hw *hw, u8 index) ...@@ -199,7 +202,11 @@ static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
if (index >= clk_hw_get_num_parents(hw)) if (index >= clk_hw_get_num_parents(hw))
return -EINVAL; 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; return 0;
} }
...@@ -271,8 +278,9 @@ struct clk_hw * __init ...@@ -271,8 +278,9 @@ struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout, const struct clk_pcr_layout *layout,
const char *name, const char **parent_names, const char *name, const char **parent_names,
u8 num_parents, u8 id, bool pll_audio, u32 *mux_table, u8 num_parents, u8 id,
const struct clk_range *range) const struct clk_range *range,
int chg_pid)
{ {
struct clk_generated *gck; struct clk_generated *gck;
struct clk_init_data init; struct clk_init_data init;
...@@ -287,16 +295,18 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, ...@@ -287,16 +295,18 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
init.ops = &generated_ops; init.ops = &generated_ops;
init.parent_names = parent_names; init.parent_names = parent_names;
init.num_parents = num_parents; init.num_parents = num_parents;
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
CLK_SET_RATE_PARENT; if (chg_pid >= 0)
init.flags |= CLK_SET_RATE_PARENT;
gck->id = id; gck->id = id;
gck->hw.init = &init; gck->hw.init = &init;
gck->regmap = regmap; gck->regmap = regmap;
gck->lock = lock; gck->lock = lock;
gck->range = *range; gck->range = *range;
gck->audio_pll_allowed = pll_audio; gck->chg_pid = chg_pid;
gck->layout = layout; gck->layout = layout;
gck->mux_table = mux_table;
clk_generated_startup(gck); clk_generated_startup(gck);
hw = &gck->hw; hw = &gck->hw;
......
...@@ -175,7 +175,7 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap) ...@@ -175,7 +175,7 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap)
regmap_read(regmap, AT91_PMC_SR, &status); 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) 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) ...@@ -336,7 +336,7 @@ static int clk_rm9200_main_is_prepared(struct clk_hw *hw)
regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status); 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, 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) ...@@ -398,7 +398,7 @@ static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
regmap_read(regmap, AT91_PMC_SR, &status); 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) static int clk_sam9x5_main_prepare(struct clk_hw *hw)
......
...@@ -17,30 +17,49 @@ ...@@ -17,30 +17,49 @@
#define MASTER_DIV_SHIFT 8 #define MASTER_DIV_SHIFT 8
#define MASTER_DIV_MASK 0x3 #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) #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
struct clk_master { struct clk_master {
struct clk_hw hw; struct clk_hw hw;
struct regmap *regmap; struct regmap *regmap;
spinlock_t *lock;
const struct clk_master_layout *layout; const struct clk_master_layout *layout;
const struct clk_master_characteristics *characteristics; const struct clk_master_characteristics *characteristics;
u32 *mux_table;
u32 mckr; 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; 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) static int clk_master_prepare(struct clk_hw *hw)
{ {
struct clk_master *master = to_clk_master(hw); struct clk_master *master = to_clk_master(hw);
while (!clk_master_ready(master->regmap)) while (!clk_master_ready(master))
cpu_relax(); cpu_relax();
return 0; return 0;
...@@ -50,7 +69,7 @@ static int clk_master_is_prepared(struct clk_hw *hw) ...@@ -50,7 +69,7 @@ static int clk_master_is_prepared(struct clk_hw *hw)
{ {
struct clk_master *master = to_clk_master(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, static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
...@@ -143,6 +162,287 @@ at91_clk_register_master(struct regmap *regmap, ...@@ -143,6 +162,287 @@ at91_clk_register_master(struct regmap *regmap,
return hw; 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 = { const struct clk_master_layout at91rm9200_master_layout = {
.mask = 0x31F, .mask = 0x31F,
.pres_shift = 2, .pres_shift = 2,
......
...@@ -38,6 +38,7 @@ struct clk_sam9x5_peripheral { ...@@ -38,6 +38,7 @@ struct clk_sam9x5_peripheral {
u32 div; u32 div;
const struct clk_pcr_layout *layout; const struct clk_pcr_layout *layout;
bool auto_div; bool auto_div;
int chg_pid;
}; };
#define to_clk_sam9x5_peripheral(hw) \ #define to_clk_sam9x5_peripheral(hw) \
...@@ -208,7 +209,7 @@ static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw) ...@@ -208,7 +209,7 @@ static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
regmap_read(periph->regmap, periph->layout->offset, &status); regmap_read(periph->regmap, periph->layout->offset, &status);
spin_unlock_irqrestore(periph->lock, flags); spin_unlock_irqrestore(periph->lock, flags);
return status & AT91_PMC_PCR_EN ? 1 : 0; return !!(status & AT91_PMC_PCR_EN);
} }
static unsigned long static unsigned long
...@@ -238,6 +239,87 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw, ...@@ -238,6 +239,87 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
return parent_rate >> periph->div; 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, static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
unsigned long rate, unsigned long rate,
unsigned long *parent_rate) unsigned long *parent_rate)
...@@ -320,11 +402,21 @@ static const struct clk_ops sam9x5_peripheral_ops = { ...@@ -320,11 +402,21 @@ static const struct clk_ops sam9x5_peripheral_ops = {
.set_rate = clk_sam9x5_peripheral_set_rate, .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 struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout, const struct clk_pcr_layout *layout,
const char *name, const char *parent_name, 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_sam9x5_peripheral *periph;
struct clk_init_data init; struct clk_init_data init;
...@@ -339,10 +431,16 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, ...@@ -339,10 +431,16 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
init.name = name; init.name = name;
init.ops = &sam9x5_peripheral_ops; init.parent_names = &parent_name;
init.parent_names = (parent_name ? &parent_name : NULL); init.num_parents = 1;
init.num_parents = (parent_name ? 1 : 0); if (chg_pid < 0) {
init.flags = 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->id = id;
periph->hw.init = &init; periph->hw.init = &init;
...@@ -353,6 +451,7 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, ...@@ -353,6 +451,7 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
periph->auto_div = true; periph->auto_div = true;
periph->layout = layout; periph->layout = layout;
periph->range = *range; periph->range = *range;
periph->chg_pid = chg_pid;
hw = &periph->hw; hw = &periph->hw;
ret = clk_hw_register(NULL, &periph->hw); ret = clk_hw_register(NULL, &periph->hw);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
struct clk_programmable { struct clk_programmable {
struct clk_hw hw; struct clk_hw hw;
struct regmap *regmap; struct regmap *regmap;
u32 *mux_table;
u8 id; u8 id;
const struct clk_programmable_layout *layout; const struct clk_programmable_layout *layout;
}; };
...@@ -108,6 +109,9 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index) ...@@ -108,6 +109,9 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
if (layout->have_slck_mck) if (layout->have_slck_mck)
mask |= AT91_PMC_CSSMCK_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 > layout->css_mask) {
if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck) if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
return -EINVAL; return -EINVAL;
...@@ -134,6 +138,9 @@ static u8 clk_programmable_get_parent(struct clk_hw *hw) ...@@ -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) if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret)
ret = PROG_MAX_RM9200_CSS + 1; 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; return ret;
} }
...@@ -182,7 +189,8 @@ struct clk_hw * __init ...@@ -182,7 +189,8 @@ struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap, at91_clk_register_programmable(struct regmap *regmap,
const char *name, const char **parent_names, const char *name, const char **parent_names,
u8 num_parents, u8 id, 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_programmable *prog;
struct clk_hw *hw; struct clk_hw *hw;
...@@ -206,6 +214,7 @@ at91_clk_register_programmable(struct regmap *regmap, ...@@ -206,6 +214,7 @@ at91_clk_register_programmable(struct regmap *regmap,
prog->layout = layout; prog->layout = layout;
prog->hw.init = &init; prog->hw.init = &init;
prog->regmap = regmap; prog->regmap = regmap;
prog->mux_table = mux_table;
hw = &prog->hw; hw = &prog->hw;
ret = clk_hw_register(NULL, &prog->hw); ret = clk_hw_register(NULL, &prog->hw);
......
This diff is collapsed.
...@@ -34,7 +34,7 @@ static inline bool clk_system_ready(struct regmap *regmap, int id) ...@@ -34,7 +34,7 @@ static inline bool clk_system_ready(struct regmap *regmap, int id)
regmap_read(regmap, AT91_PMC_SR, &status); 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) static int clk_system_prepare(struct clk_hw *hw)
...@@ -74,7 +74,7 @@ static int clk_system_is_prepared(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); 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 = { static const struct clk_ops system_ops = {
......
...@@ -120,9 +120,11 @@ static const struct clk_ops utmi_ops = { ...@@ -120,9 +120,11 @@ static const struct clk_ops utmi_ops = {
.recalc_rate = clk_utmi_recalc_rate, .recalc_rate = clk_utmi_recalc_rate,
}; };
struct clk_hw * __init static struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
const char *name, const char *parent_name) 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_utmi *utmi;
struct clk_hw *hw; struct clk_hw *hw;
...@@ -134,10 +136,10 @@ at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, ...@@ -134,10 +136,10 @@ at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
init.name = name; init.name = name;
init.ops = &utmi_ops; init.ops = ops;
init.parent_names = parent_name ? &parent_name : NULL; init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0; init.num_parents = parent_name ? 1 : 0;
init.flags = CLK_SET_RATE_GATE; init.flags = flags;
utmi->hw.init = &init; utmi->hw.init = &init;
utmi->regmap_pmc = regmap_pmc; utmi->regmap_pmc = regmap_pmc;
...@@ -152,3 +154,94 @@ at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, ...@@ -152,3 +154,94 @@ at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
return hw; 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);
}
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#define SYSTEM_MAX_ID 31 #define SYSTEM_MAX_ID 31
#define GCK_INDEX_DT_AUDIO_PLL 5
#ifdef CONFIG_HAVE_AT91_AUDIO_PLL #ifdef CONFIG_HAVE_AT91_AUDIO_PLL
static void __init of_sama5d2_clk_audio_pll_frac_setup(struct device_node *np) 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) ...@@ -135,7 +137,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
return; return;
for_each_child_of_node(np, gcknp) { for_each_child_of_node(np, gcknp) {
bool pll_audio = false; int chg_pid = INT_MIN;
if (of_property_read_u32(gcknp, "reg", &id)) if (of_property_read_u32(gcknp, "reg", &id))
continue; continue;
...@@ -152,12 +154,13 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np) ...@@ -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") && if (of_device_is_compatible(np, "atmel,sama5d2-clk-generated") &&
(id == GCK_ID_I2S0 || id == GCK_ID_I2S1 || (id == GCK_ID_I2S0 || id == GCK_ID_I2S1 ||
id == GCK_ID_CLASSD)) id == GCK_ID_CLASSD))
pll_audio = true; chg_pid = GCK_INDEX_DT_AUDIO_PLL;
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&dt_pcr_layout, name, &dt_pcr_layout, name,
parent_names, num_parents, parent_names, NULL,
id, pll_audio, &range); num_parents, id, &range,
chg_pid);
if (IS_ERR(hw)) if (IS_ERR(hw))
continue; continue;
...@@ -460,7 +463,8 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type) ...@@ -460,7 +463,8 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
&dt_pcr_layout, &dt_pcr_layout,
name, name,
parent_name, parent_name,
id, &range); id, &range,
INT_MIN);
} }
if (IS_ERR(hw)) if (IS_ERR(hw))
...@@ -673,7 +677,8 @@ CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv", ...@@ -673,7 +677,8 @@ CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv",
static void __init static void __init
of_at91_clk_prog_setup(struct device_node *np, 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; int num;
u32 id; u32 id;
...@@ -707,7 +712,7 @@ of_at91_clk_prog_setup(struct device_node *np, ...@@ -707,7 +712,7 @@ of_at91_clk_prog_setup(struct device_node *np,
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, num_parents, parent_names, num_parents,
id, layout); id, layout, mux_table);
if (IS_ERR(hw)) if (IS_ERR(hw))
continue; continue;
...@@ -717,21 +722,21 @@ of_at91_clk_prog_setup(struct device_node *np, ...@@ -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) 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", CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable",
of_at91rm9200_clk_prog_setup); of_at91rm9200_clk_prog_setup);
static void __init of_at91sam9g45_clk_prog_setup(struct device_node *np) 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", CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable",
of_at91sam9g45_clk_prog_setup); of_at91sam9g45_clk_prog_setup);
static void __init of_at91sam9x5_clk_prog_setup(struct device_node *np) 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", CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable",
of_at91sam9x5_clk_prog_setup); of_at91sam9x5_clk_prog_setup);
......
...@@ -54,8 +54,14 @@ struct clk_master_characteristics { ...@@ -54,8 +54,14 @@ struct clk_master_characteristics {
struct clk_pll_layout { struct clk_pll_layout {
u32 pllr_mask; u32 pllr_mask;
u16 mul_mask; u32 mul_mask;
u32 frac_mask;
u32 div_mask;
u32 endiv_mask;
u8 mul_shift; u8 mul_shift;
u8 frac_shift;
u8 div_shift;
u8 endiv_shift;
}; };
extern const struct clk_pll_layout at91rm9200_pll_layout; extern const struct clk_pll_layout at91rm9200_pll_layout;
...@@ -122,8 +128,8 @@ struct clk_hw * __init ...@@ -122,8 +128,8 @@ struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout, const struct clk_pcr_layout *layout,
const char *name, const char **parent_names, const char *name, const char **parent_names,
u8 num_parents, u8 id, bool pll_audio, u32 *mux_table, u8 num_parents, u8 id,
const struct clk_range *range); const struct clk_range *range, int chg_pid);
struct clk_hw * __init struct clk_hw * __init
at91_clk_register_h32mx(struct regmap *regmap, const char *name, at91_clk_register_h32mx(struct regmap *regmap, const char *name,
...@@ -154,6 +160,13 @@ at91_clk_register_master(struct regmap *regmap, const char *name, ...@@ -154,6 +160,13 @@ at91_clk_register_master(struct regmap *regmap, const char *name,
const struct clk_master_layout *layout, const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics); 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 struct clk_hw * __init
at91_clk_register_peripheral(struct regmap *regmap, const char *name, at91_clk_register_peripheral(struct regmap *regmap, const char *name,
const char *parent_name, u32 id); const char *parent_name, u32 id);
...@@ -161,7 +174,8 @@ struct clk_hw * __init ...@@ -161,7 +174,8 @@ struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout, const struct clk_pcr_layout *layout,
const char *name, const char *parent_name, 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 struct clk_hw * __init
at91_clk_register_pll(struct regmap *regmap, const char *name, at91_clk_register_pll(struct regmap *regmap, const char *name,
...@@ -173,14 +187,23 @@ at91_clk_register_plldiv(struct regmap *regmap, const char *name, ...@@ -173,14 +187,23 @@ at91_clk_register_plldiv(struct regmap *regmap, const char *name,
const char *parent_name); const char *parent_name);
struct clk_hw * __init struct clk_hw * __init
sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock, sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name, u8 id, const char *name, const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics); 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 struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap, const char *name, at91_clk_register_programmable(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents, u8 id, 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 struct clk_hw * __init
at91_clk_register_sam9260_slow(struct regmap *regmap, at91_clk_register_sam9260_slow(struct regmap *regmap,
...@@ -213,6 +236,10 @@ struct clk_hw * __init ...@@ -213,6 +236,10 @@ struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
const char *name, const char *parent_name); 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 #ifdef CONFIG_PM
void pmc_register_id(u8 id); void pmc_register_id(u8 id);
void pmc_register_pck(u8 pck); void pmc_register_pck(u8 pck);
......
...@@ -22,7 +22,7 @@ static const struct clk_master_layout sam9x60_master_layout = { ...@@ -22,7 +22,7 @@ static const struct clk_master_layout sam9x60_master_layout = {
}; };
static const struct clk_range plla_outputs[] = { static const struct clk_range plla_outputs[] = {
{ .min = 300000000, .max = 600000000 }, { .min = 2343750, .max = 1200000000 },
}; };
static const struct clk_pll_characteristics plla_characteristics = { static const struct clk_pll_characteristics plla_characteristics = {
...@@ -42,6 +42,20 @@ static const struct clk_pll_characteristics upll_characteristics = { ...@@ -42,6 +42,20 @@ static const struct clk_pll_characteristics upll_characteristics = {
.upll = true, .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 = { static const struct clk_programmable_layout sam9x60_programmable_layout = {
.pres_mask = 0xff, .pres_mask = 0xff,
.pres_shift = 8, .pres_shift = 8,
...@@ -156,6 +170,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -156,6 +170,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
const char *td_slck_name, *md_slck_name, *mainxtal_name; const char *td_slck_name, *md_slck_name, *mainxtal_name;
struct pmc_data *sam9x60_pmc; struct pmc_data *sam9x60_pmc;
const char *parent_names[6]; const char *parent_names[6];
struct clk_hw *main_osc_hw;
struct regmap *regmap; struct regmap *regmap;
struct clk_hw *hw; struct clk_hw *hw;
int i; int i;
...@@ -178,7 +193,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -178,7 +193,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
return; return;
mainxtal_name = of_clk_get_parent_name(np, i); 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)) if (IS_ERR(regmap))
return; return;
...@@ -189,7 +204,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -189,7 +204,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
if (!sam9x60_pmc) if (!sam9x60_pmc)
return; 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); 50000000);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -200,6 +215,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -200,6 +215,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
bypass); bypass);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
main_osc_hw = hw;
parent_names[0] = "main_rc_osc"; parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc"; parent_names[1] = "main_osc";
...@@ -209,15 +225,31 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -209,15 +225,31 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
sam9x60_pmc->chws[PMC_MAIN] = hw; sam9x60_pmc->chws[PMC_MAIN] = hw;
hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "pllack", hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "pllack_fracck",
"mainck", 0, &plla_characteristics); "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)) if (IS_ERR(hw))
goto err_free; goto err_free;
sam9x60_pmc->chws[PMC_PLLACK] = hw; sam9x60_pmc->chws[PMC_PLLACK] = hw;
hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "upllck", hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "upllck_fracck",
"main_osc", 1, &upll_characteristics); "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)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -225,7 +257,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -225,7 +257,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
parent_names[0] = md_slck_name; parent_names[0] = md_slck_name;
parent_names[1] = "mainck"; parent_names[1] = "mainck";
parent_names[2] = "pllack"; parent_names[2] = "pllack_divck";
hw = at91_clk_register_master(regmap, "masterck", 3, parent_names, hw = at91_clk_register_master(regmap, "masterck", 3, parent_names,
&sam9x60_master_layout, &sam9x60_master_layout,
&mck_characteristics); &mck_characteristics);
...@@ -234,8 +266,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -234,8 +266,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
sam9x60_pmc->chws[PMC_MCK] = hw; sam9x60_pmc->chws[PMC_MCK] = hw;
parent_names[0] = "pllack"; parent_names[0] = "pllack_divck";
parent_names[1] = "upllck"; parent_names[1] = "upllck_divck";
parent_names[2] = "main_osc"; parent_names[2] = "main_osc";
hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3); hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
if (IS_ERR(hw)) if (IS_ERR(hw))
...@@ -245,8 +277,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -245,8 +277,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
parent_names[1] = td_slck_name; parent_names[1] = td_slck_name;
parent_names[2] = "mainck"; parent_names[2] = "mainck";
parent_names[3] = "masterck"; parent_names[3] = "masterck";
parent_names[4] = "pllack"; parent_names[4] = "pllack_divck";
parent_names[5] = "upllck"; parent_names[5] = "upllck_divck";
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
char name[6]; char name[6];
...@@ -254,7 +286,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -254,7 +286,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i, parent_names, 6, i,
&sam9x60_programmable_layout); &sam9x60_programmable_layout,
NULL);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -277,7 +310,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -277,7 +310,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
sam9x60_periphck[i].n, sam9x60_periphck[i].n,
"masterck", "masterck",
sam9x60_periphck[i].id, sam9x60_periphck[i].id,
&range); &range, INT_MIN);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -288,10 +321,9 @@ static void __init sam9x60_pmc_setup(struct device_node *np) ...@@ -288,10 +321,9 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sam9x60_pcr_layout, &sam9x60_pcr_layout,
sam9x60_gck[i].n, sam9x60_gck[i].n,
parent_names, 6, parent_names, NULL, 6,
sam9x60_gck[i].id, sam9x60_gck[i].id,
false, &sam9x60_gck[i].r, INT_MIN);
&sam9x60_gck[i].r);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
......
...@@ -116,21 +116,20 @@ static const struct { ...@@ -116,21 +116,20 @@ static const struct {
char *n; char *n;
u8 id; u8 id;
struct clk_range r; struct clk_range r;
bool pll; int chg_pid;
} sama5d2_gck[] = { } sama5d2_gck[] = {
{ .n = "sdmmc0_gclk", .id = 31, }, { .n = "sdmmc0_gclk", .id = 31, .chg_pid = INT_MIN, },
{ .n = "sdmmc1_gclk", .id = 32, }, { .n = "sdmmc1_gclk", .id = 32, .chg_pid = INT_MIN, },
{ .n = "tcb0_gclk", .id = 35, .r = { .min = 0, .max = 83000000 }, }, { .n = "tcb0_gclk", .id = 35, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
{ .n = "tcb1_gclk", .id = 36, .r = { .min = 0, .max = 83000000 }, }, { .n = "tcb1_gclk", .id = 36, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
{ .n = "pwm_gclk", .id = 38, .r = { .min = 0, .max = 83000000 }, }, { .n = "pwm_gclk", .id = 38, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
{ .n = "isc_gclk", .id = 46, }, { .n = "isc_gclk", .id = 46, .chg_pid = INT_MIN, },
{ .n = "pdmic_gclk", .id = 48, }, { .n = "pdmic_gclk", .id = 48, .chg_pid = INT_MIN, },
{ .n = "i2s0_gclk", .id = 54, .pll = true }, { .n = "i2s0_gclk", .id = 54, .chg_pid = 5, },
{ .n = "i2s1_gclk", .id = 55, .pll = true }, { .n = "i2s1_gclk", .id = 55, .chg_pid = 5, },
{ .n = "can0_gclk", .id = 56, .r = { .min = 0, .max = 80000000 }, }, { .n = "can0_gclk", .id = 56, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, },
{ .n = "can1_gclk", .id = 57, .r = { .min = 0, .max = 80000000 }, }, { .n = "can1_gclk", .id = 57, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, },
{ .n = "classd_gclk", .id = 59, .r = { .min = 0, .max = 100000000 }, { .n = "classd_gclk", .id = 59, .chg_pid = 5, .r = { .min = 0, .max = 100000000 }, },
.pll = true },
}; };
static const struct clk_programmable_layout sama5d2_programmable_layout = { static const struct clk_programmable_layout sama5d2_programmable_layout = {
...@@ -269,7 +268,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np) ...@@ -269,7 +268,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i, parent_names, 6, i,
&sama5d2_programmable_layout); &sama5d2_programmable_layout,
NULL);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -292,7 +292,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np) ...@@ -292,7 +292,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
sama5d2_periphck[i].n, sama5d2_periphck[i].n,
"masterck", "masterck",
sama5d2_periphck[i].id, sama5d2_periphck[i].id,
&range); &range, INT_MIN);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -305,7 +305,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np) ...@@ -305,7 +305,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
sama5d2_periph32ck[i].n, sama5d2_periph32ck[i].n,
"h32mxck", "h32mxck",
sama5d2_periph32ck[i].id, sama5d2_periph32ck[i].id,
&sama5d2_periph32ck[i].r); &sama5d2_periph32ck[i].r,
INT_MIN);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -322,10 +323,10 @@ static void __init sama5d2_pmc_setup(struct device_node *np) ...@@ -322,10 +323,10 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout, &sama5d2_pcr_layout,
sama5d2_gck[i].n, sama5d2_gck[i].n,
parent_names, 6, parent_names, NULL, 6,
sama5d2_gck[i].id, 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)) if (IS_ERR(hw))
goto err_free; goto err_free;
......
...@@ -121,7 +121,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np) ...@@ -121,7 +121,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
return; return;
mainxtal_name = of_clk_get_parent_name(np, i); 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)) if (IS_ERR(regmap))
return; return;
...@@ -200,7 +200,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np) ...@@ -200,7 +200,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i, parent_names, 5, i,
&at91sam9x5_programmable_layout); &at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -223,7 +224,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np) ...@@ -223,7 +224,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
sama5d3_periphck[i].n, sama5d3_periphck[i].n,
"masterck", "masterck",
sama5d3_periphck[i].id, sama5d3_periphck[i].id,
&sama5d3_periphck[i].r); &sama5d3_periphck[i].r,
INT_MIN);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
......
...@@ -223,7 +223,8 @@ static void __init sama5d4_pmc_setup(struct device_node *np) ...@@ -223,7 +223,8 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name, hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i, parent_names, 5, i,
&at91sam9x5_programmable_layout); &at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -246,7 +247,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np) ...@@ -246,7 +247,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
sama5d4_periphck[i].n, sama5d4_periphck[i].n,
"masterck", "masterck",
sama5d4_periphck[i].id, sama5d4_periphck[i].id,
&range); &range, INT_MIN);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
...@@ -259,7 +260,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np) ...@@ -259,7 +260,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
sama5d4_periph32ck[i].n, sama5d4_periph32ck[i].n,
"h32mxck", "h32mxck",
sama5d4_periph32ck[i].id, sama5d4_periph32ck[i].id,
&range); &range, INT_MIN);
if (IS_ERR(hw)) if (IS_ERR(hw))
goto err_free; goto err_free;
......
This diff is collapsed.
...@@ -471,8 +471,9 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np) ...@@ -471,8 +471,9 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np)
if (!regbase) if (!regbase)
return; return;
slow_rc = clk_hw_register_fixed_rate(NULL, parent_names[0], NULL, 0, slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
32768); NULL, 0, 32768,
93750000);
if (IS_ERR(slow_rc)) if (IS_ERR(slow_rc))
return; return;
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Microchip Sparx5 SoC Clock driver.
*
* Copyright (c) 2019 Microchip Inc.
*
* Author: Lars Povlsen <lars.povlsen@microchip.com>
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/clk-provider.h>
#include <linux/bitfield.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/microchip,sparx5.h>
#define PLL_DIV GENMASK(7, 0)
#define PLL_PRE_DIV GENMASK(10, 8)
#define PLL_ROT_DIR BIT(11)
#define PLL_ROT_SEL GENMASK(13, 12)
#define PLL_ROT_ENA BIT(14)
#define PLL_CLK_ENA BIT(15)
#define MAX_SEL 4
#define MAX_PRE BIT(3)
static const u8 sel_rates[MAX_SEL] = { 0, 2*8, 2*4, 2*2 };
static const char *clk_names[N_CLOCKS] = {
"core", "ddr", "cpu2", "arm2",
"aux1", "aux2", "aux3", "aux4",
"synce",
};
struct s5_hw_clk {
struct clk_hw hw;
void __iomem *reg;
};
struct s5_clk_data {
void __iomem *base;
struct s5_hw_clk s5_hw[N_CLOCKS];
};
struct s5_pll_conf {
unsigned long freq;
u8 div;
bool rot_ena;
u8 rot_sel;
u8 rot_dir;
u8 pre_div;
};
#define to_s5_pll(hw) container_of(hw, struct s5_hw_clk, hw)
static unsigned long s5_calc_freq(unsigned long parent_rate,
const struct s5_pll_conf *conf)
{
unsigned long rate = parent_rate / conf->div;
if (conf->rot_ena) {
int sign = conf->rot_dir ? -1 : 1;
int divt = sel_rates[conf->rot_sel] * (1 + conf->pre_div);
int divb = divt + sign;
rate = mult_frac(rate, divt, divb);
rate = roundup(rate, 1000);
}
return rate;
}
static void s5_search_fractional(unsigned long rate,
unsigned long parent_rate,
int div,
struct s5_pll_conf *conf)
{
struct s5_pll_conf best;
ulong cur_offset, best_offset = rate;
int d, i, j;
memset(conf, 0, sizeof(*conf));
conf->div = div;
conf->rot_ena = 1; /* Fractional rate */
for (d = 0; best_offset > 0 && d <= 1 ; d++) {
conf->rot_dir = !!d;
for (i = 0; best_offset > 0 && i < MAX_PRE; i++) {
conf->pre_div = i;
for (j = 1; best_offset > 0 && j < MAX_SEL; j++) {
conf->rot_sel = j;
conf->freq = s5_calc_freq(parent_rate, conf);
cur_offset = abs(rate - conf->freq);
if (cur_offset < best_offset) {
best_offset = cur_offset;
best = *conf;
}
}
}
}
/* Best match */
*conf = best;
}
static unsigned long s5_calc_params(unsigned long rate,
unsigned long parent_rate,
struct s5_pll_conf *conf)
{
if (parent_rate % rate) {
struct s5_pll_conf alt1, alt2;
int div;
div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate);
s5_search_fractional(rate, parent_rate, div, &alt1);
/* Straight match? */
if (alt1.freq == rate) {
*conf = alt1;
} else {
/* Try without rounding divider */
div = parent_rate / rate;
if (div != alt1.div) {
s5_search_fractional(rate, parent_rate, div,
&alt2);
/* Select the better match */
if (abs(rate - alt1.freq) <
abs(rate - alt2.freq))
*conf = alt1;
else
*conf = alt2;
}
}
} else {
/* Straight fit */
memset(conf, 0, sizeof(*conf));
conf->div = parent_rate / rate;
}
return conf->freq;
}
static int s5_pll_enable(struct clk_hw *hw)
{
struct s5_hw_clk *pll = to_s5_pll(hw);
u32 val = readl(pll->reg);
val |= PLL_CLK_ENA;
writel(val, pll->reg);
return 0;
}
static void s5_pll_disable(struct clk_hw *hw)
{
struct s5_hw_clk *pll = to_s5_pll(hw);
u32 val = readl(pll->reg);
val &= ~PLL_CLK_ENA;
writel(val, pll->reg);
}
static int s5_pll_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
struct s5_hw_clk *pll = to_s5_pll(hw);
struct s5_pll_conf conf;
unsigned long eff_rate;
u32 val;
eff_rate = s5_calc_params(rate, parent_rate, &conf);
if (eff_rate != rate)
return -EOPNOTSUPP;
val = readl(pll->reg) & PLL_CLK_ENA;
val |= FIELD_PREP(PLL_DIV, conf.div);
if (conf.rot_ena) {
val |= PLL_ROT_ENA;
val |= FIELD_PREP(PLL_ROT_SEL, conf.rot_sel);
val |= FIELD_PREP(PLL_PRE_DIV, conf.pre_div);
if (conf.rot_dir)
val |= PLL_ROT_DIR;
}
writel(val, pll->reg);
return 0;
}
static unsigned long s5_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct s5_hw_clk *pll = to_s5_pll(hw);
struct s5_pll_conf conf;
u32 val;
val = readl(pll->reg);
if (val & PLL_CLK_ENA) {
conf.div = FIELD_GET(PLL_DIV, val);
conf.pre_div = FIELD_GET(PLL_PRE_DIV, val);
conf.rot_ena = FIELD_GET(PLL_ROT_ENA, val);
conf.rot_dir = FIELD_GET(PLL_ROT_DIR, val);
conf.rot_sel = FIELD_GET(PLL_ROT_SEL, val);
conf.freq = s5_calc_freq(parent_rate, &conf);
} else {
conf.freq = 0;
}
return conf.freq;
}
static long s5_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct s5_pll_conf conf;
return s5_calc_params(rate, *parent_rate, &conf);
}
static const struct clk_ops s5_pll_ops = {
.enable = s5_pll_enable,
.disable = s5_pll_disable,
.set_rate = s5_pll_set_rate,
.round_rate = s5_pll_round_rate,
.recalc_rate = s5_pll_recalc_rate,
};
static struct clk_hw *s5_clk_hw_get(struct of_phandle_args *clkspec, void *data)
{
struct s5_clk_data *s5_clk = data;
unsigned int idx = clkspec->args[0];
if (idx >= N_CLOCKS) {
pr_err("%s: invalid index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return &s5_clk->s5_hw[idx].hw;
}
static int s5_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int i, ret;
struct s5_clk_data *s5_clk;
struct clk_parent_data pdata = { .index = 0 };
struct clk_init_data init = {
.ops = &s5_pll_ops,
.num_parents = 1,
.parent_data = &pdata,
};
s5_clk = devm_kzalloc(dev, sizeof(*s5_clk), GFP_KERNEL);
if (!s5_clk)
return -ENOMEM;
s5_clk->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(s5_clk->base))
return PTR_ERR(s5_clk->base);
for (i = 0; i < N_CLOCKS; i++) {
struct s5_hw_clk *s5_hw = &s5_clk->s5_hw[i];
init.name = clk_names[i];
s5_hw->reg = s5_clk->base + (i * 4);
s5_hw->hw.init = &init;
ret = devm_clk_hw_register(dev, &s5_hw->hw);
if (ret) {
dev_err(dev, "failed to register %s clock\n",
init.name);
return ret;
}
}
return devm_of_clk_add_hw_provider(dev, s5_clk_hw_get, s5_clk);
}
static const struct of_device_id s5_clk_dt_ids[] = {
{ .compatible = "microchip,sparx5-dpll", },
{ }
};
MODULE_DEVICE_TABLE(of, s5_clk_dt_ids);
static struct platform_driver s5_clk_driver = {
.probe = s5_clk_probe,
.driver = {
.name = "sparx5-clk",
.of_match_table = s5_clk_dt_ids,
},
};
builtin_platform_driver(s5_clk_driver);
...@@ -500,12 +500,6 @@ static unsigned long clk_core_get_accuracy_no_lock(struct clk_core *core) ...@@ -500,12 +500,6 @@ static unsigned long clk_core_get_accuracy_no_lock(struct clk_core *core)
return core->accuracy; return core->accuracy;
} }
unsigned long __clk_get_flags(struct clk *clk)
{
return !clk ? 0 : clk->core->flags;
}
EXPORT_SYMBOL_GPL(__clk_get_flags);
unsigned long clk_hw_get_flags(const struct clk_hw *hw) unsigned long clk_hw_get_flags(const struct clk_hw *hw)
{ {
return hw->core->flags; return hw->core->flags;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk/mmp.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk/mmp.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
......
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019 Microchip Inc.
*
* Author: Lars Povlsen <lars.povlsen@microchip.com>
*/
#ifndef _DT_BINDINGS_CLK_SPARX5_H
#define _DT_BINDINGS_CLK_SPARX5_H
#define CLK_ID_CORE 0
#define CLK_ID_DDR 1
#define CLK_ID_CPU2 2
#define CLK_ID_ARM2 3
#define CLK_ID_AUX1 4
#define CLK_ID_AUX2 5
#define CLK_ID_AUX3 6
#define CLK_ID_AUX4 7
#define CLK_ID_SYNCE 8
#define N_CLOCKS 9
#endif
...@@ -1096,7 +1096,6 @@ int clk_hw_get_parent_index(struct clk_hw *hw); ...@@ -1096,7 +1096,6 @@ int clk_hw_get_parent_index(struct clk_hw *hw);
int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *new_parent); int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *new_parent);
unsigned int __clk_get_enable_count(struct clk *clk); unsigned int __clk_get_enable_count(struct clk *clk);
unsigned long clk_hw_get_rate(const struct clk_hw *hw); unsigned long clk_hw_get_rate(const struct clk_hw *hw);
unsigned long __clk_get_flags(struct clk *clk);
unsigned long clk_hw_get_flags(const struct clk_hw *hw); unsigned long clk_hw_get_flags(const struct clk_hw *hw);
#define clk_hw_can_set_rate_parent(hw) \ #define clk_hw_can_set_rate_parent(hw) \
(clk_hw_get_flags((hw)) & CLK_SET_RATE_PARENT) (clk_hw_get_flags((hw)) & CLK_SET_RATE_PARENT)
......
...@@ -59,6 +59,7 @@ ...@@ -59,6 +59,7 @@
#define AT91_PMC_PLL_UPDT 0x1C /* PMC PLL update register [for SAM9X60] */ #define AT91_PMC_PLL_UPDT 0x1C /* PMC PLL update register [for SAM9X60] */
#define AT91_PMC_PLL_UPDT_UPDATE (1 << 8) /* Update PLL settings */ #define AT91_PMC_PLL_UPDT_UPDATE (1 << 8) /* Update PLL settings */
#define AT91_PMC_PLL_UPDT_ID (1 << 0) /* PLL ID */ #define AT91_PMC_PLL_UPDT_ID (1 << 0) /* PLL ID */
#define AT91_PMC_PLL_UPDT_ID_MSK (0xf) /* PLL ID mask */
#define AT91_PMC_PLL_UPDT_STUPTIM (0xff << 16) /* Startup time */ #define AT91_PMC_PLL_UPDT_STUPTIM (0xff << 16) /* Startup time */
#define AT91_CKGR_MOR 0x20 /* Main Oscillator Register [not on SAM9RL] */ #define AT91_CKGR_MOR 0x20 /* Main Oscillator Register [not on SAM9RL] */
...@@ -136,6 +137,8 @@ ...@@ -136,6 +137,8 @@
#define AT91_PMC_PLLADIV2_ON (1 << 12) #define AT91_PMC_PLLADIV2_ON (1 << 12)
#define AT91_PMC_H32MXDIV BIT(24) #define AT91_PMC_H32MXDIV BIT(24)
#define AT91_PMC_XTALF 0x34 /* Main XTAL Frequency Register [SAMA7G5 only] */
#define AT91_PMC_USB 0x38 /* USB Clock Register [some SAM9 only] */ #define AT91_PMC_USB 0x38 /* USB Clock Register [some SAM9 only] */
#define AT91_PMC_USBS (0x1 << 0) /* USB OHCI Input clock selection */ #define AT91_PMC_USBS (0x1 << 0) /* USB OHCI Input clock selection */
#define AT91_PMC_USBS_PLLA (0 << 0) #define AT91_PMC_USBS_PLLA (0 << 0)
...@@ -174,6 +177,7 @@ ...@@ -174,6 +177,7 @@
#define AT91_PMC_MOSCRCS (1 << 17) /* Main On-Chip RC [some SAM9] */ #define AT91_PMC_MOSCRCS (1 << 17) /* Main On-Chip RC [some SAM9] */
#define AT91_PMC_CFDEV (1 << 18) /* Clock Failure Detector Event [some SAM9] */ #define AT91_PMC_CFDEV (1 << 18) /* Clock Failure Detector Event [some SAM9] */
#define AT91_PMC_GCKRDY (1 << 24) /* Generated Clocks */ #define AT91_PMC_GCKRDY (1 << 24) /* Generated Clocks */
#define AT91_PMC_MCKXRDY (1 << 26) /* Master Clock x [x=1..4] Ready Status */
#define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */ #define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */
#define AT91_PMC_FSMR 0x70 /* Fast Startup Mode Register */ #define AT91_PMC_FSMR 0x70 /* Fast Startup Mode Register */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment