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
This commit is contained in:
commit
dd9c697a94
|
@ -28,6 +28,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o
|
|||
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
|
||||
obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.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_FSL_SAI) += clk-fsl-sai.o
|
||||
obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
return sam9x60_pll_ready(regmap, 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);
|
||||
|
||||
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;
|
||||
u8 div;
|
||||
u16 mul;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(pll->lock, flags);
|
||||
regmap_write(regmap, AT91_PMC_PLL_UPDT, pll->id);
|
||||
|
||||
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
|
||||
div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, 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_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);
|
||||
|
||||
return sam9x60_pll_ready(pll->regmap, pll->id);
|
||||
}
|
||||
|
||||
static void sam9x60_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(pll->lock, flags);
|
||||
spin_lock_irqsave(core->lock, flags);
|
||||
|
||||
regmap_write(pll->regmap, AT91_PMC_PLL_UPDT, pll->id);
|
||||
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
|
||||
AT91_PMC_PLL_UPDT_ID_MSK, core->id);
|
||||
|
||||
regmap_update_bits(pll->regmap, AT91_PMC_PLL_CTRL0,
|
||||
AT91_PMC_PLL_CTRL0_ENPLLCK, 0);
|
||||
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, AT91_PMC_PLL_CTRL0_ENPLL, 0);
|
||||
|
||||
regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT,
|
||||
AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
|
||||
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(pll->regmap, AT91_PMC_PLL_CTRL0,
|
||||
AT91_PMC_PLL_CTRL0_ENPLL, 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);
|
||||
|
||||
if (pll->characteristics->upll)
|
||||
regmap_update_bits(pll->regmap, AT91_PMC_PLL_ACR,
|
||||
AT91_PMC_PLL_ACR_UTMIBG |
|
||||
AT91_PMC_PLL_ACR_UTMIVR, 0);
|
||||
|
||||
regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT,
|
||||
AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
|
||||
|
||||
spin_unlock_irqrestore(pll->lock, flags);
|
||||
spin_unlock_irqrestore(core->lock, flags);
|
||||
}
|
||||
|
||||
static unsigned long sam9x60_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
static int sam9x60_frac_pll_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
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_pll_ready(core->regmap, core->id);
|
||||
}
|
||||
|
||||
static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
bool update)
|
||||
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;
|
||||
|
||||
if (rate < FCORE_MIN || rate > FCORE_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
if (remainder) {
|
||||
nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
|
||||
parent_rate);
|
||||
|
||||
tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
|
||||
(1 << 22));
|
||||
}
|
||||
|
||||
/* Check if resulted rate is a valid. */
|
||||
if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
if (update) {
|
||||
frac->mul = nmul - 1;
|
||||
frac->frac = nfrac;
|
||||
}
|
||||
|
||||
return tmprate;
|
||||
}
|
||||
|
||||
static long sam9x60_frac_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
|
||||
|
||||
return sam9x60_frac_pll_compute_mul_frac(core, rate, *parent_rate, false);
|
||||
}
|
||||
|
||||
static int sam9x60_frac_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
|
||||
|
||||
return sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
/* Stop if enabled an nothing changed. */
|
||||
if (!!(val & core->layout->endiv_mask) && cdiv == div->div)
|
||||
goto unlock;
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
|
||||
while (!sam9x60_pll_ready(regmap, core->id))
|
||||
cpu_relax();
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(core->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sam9x60_div_pll_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
|
||||
struct regmap *regmap = core->regmap;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(core->lock, flags);
|
||||
|
||||
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
|
||||
AT91_PMC_PLL_UPDT_ID_MSK, core->id);
|
||||
|
||||
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
|
||||
core->layout->endiv_mask, 0);
|
||||
|
||||
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
|
||||
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
|
||||
AT91_PMC_PLL_UPDT_UPDATE | core->id);
|
||||
|
||||
spin_unlock_irqrestore(core->lock, flags);
|
||||
}
|
||||
|
||||
static int sam9x60_div_pll_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
|
||||
struct regmap *regmap = core->regmap;
|
||||
unsigned long flags;
|
||||
unsigned int val;
|
||||
|
||||
spin_lock_irqsave(core->lock, flags);
|
||||
|
||||
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
|
||||
AT91_PMC_PLL_UPDT_ID_MSK, core->id);
|
||||
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
|
||||
|
||||
spin_unlock_irqrestore(core->lock, flags);
|
||||
|
||||
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 =
|
||||
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;
|
||||
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;
|
||||
|
||||
if (!pll->characteristics->upll) {
|
||||
mindiv = parent_rate / rate;
|
||||
if (mindiv < 2)
|
||||
mindiv = 2;
|
||||
for (divid = 1; divid < core->layout->div_mask; divid++) {
|
||||
tmp_parent_rate = clk_hw_round_rate(parent, rate * divid);
|
||||
if (!tmp_parent_rate)
|
||||
continue;
|
||||
|
||||
maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX, rate);
|
||||
if (maxdiv > PLL_DIV_MAX)
|
||||
maxdiv = PLL_DIV_MAX;
|
||||
} else {
|
||||
mindiv = maxdiv = UPLL_DIV;
|
||||
}
|
||||
tmp_rate = DIV_ROUND_CLOSEST_ULL(tmp_parent_rate, divid);
|
||||
tmp_diff = abs(rate - tmp_rate);
|
||||
|
||||
for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
|
||||
unsigned long remainder;
|
||||
unsigned long tmprate;
|
||||
unsigned long tmpmul;
|
||||
unsigned long tmpfrac = 0;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
if (remainder) {
|
||||
tmpfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * tmpdiv * (1 << 22),
|
||||
parent_rate);
|
||||
|
||||
tmprate += DIV_ROUND_CLOSEST_ULL((u64)tmpfrac * parent_rate,
|
||||
tmpdiv * (1 << 22));
|
||||
|
||||
if (tmprate > rate)
|
||||
remainder = tmprate - rate;
|
||||
else
|
||||
remainder = rate - tmprate;
|
||||
if (best_diff < 0 || best_diff > tmp_diff) {
|
||||
*parent_rate = tmp_parent_rate;
|
||||
best_rate = tmp_rate;
|
||||
best_diff = tmp_diff;
|
||||
best_div = divid;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
return sam9x60_pll_get_best_div_mul(pll, rate, parent_rate, true);
|
||||
div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1;
|
||||
|
||||
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;
|
||||
|
||||
pll->id = id;
|
||||
pll->hw.init = &init;
|
||||
pll->characteristics = characteristics;
|
||||
pll->regmap = regmap;
|
||||
pll->lock = lock;
|
||||
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;
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
hw = &pll->hw;
|
||||
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(pll);
|
||||
kfree(frac);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
spin_unlock_irqrestore(div->core.lock, flags);
|
||||
|
||||
hw = &div->core.hw;
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
if (ret) {
|
||||
kfree(div);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
@ -154,6 +160,13 @@ at91_clk_register_master(struct regmap *regmap, const char *name,
|
|||
const struct clk_master_layout *layout,
|
||||
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);
|
||||
|
@ -161,7 +174,8 @@ struct clk_hw * __init
|
|||
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
|
||||
const struct clk_pcr_layout *layout,
|
||||
const char *name, const char *parent_name,
|
||||
u32 id, const struct clk_range *range);
|
||||
u32 id, const struct clk_range *range,
|
||||
int chg_pid);
|
||||
|
||||
struct clk_hw * __init
|
||||
at91_clk_register_pll(struct regmap *regmap, const char *name,
|
||||
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
// 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)
|
|||
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)
|
||||
{
|
||||
return hw->core->flags;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/mmp.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/mmp.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/* 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);
|
|||
int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *new_parent);
|
||||
unsigned int __clk_get_enable_count(struct clk *clk);
|
||||
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);
|
||||
#define clk_hw_can_set_rate_parent(hw) \
|
||||
(clk_hw_get_flags((hw)) & CLK_SET_RATE_PARENT)
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
#define AT91_PMC_PLL_UPDT 0x1C /* PMC PLL update register [for SAM9X60] */
|
||||
#define AT91_PMC_PLL_UPDT_UPDATE (1 << 8) /* Update PLL settings */
|
||||
#define AT91_PMC_PLL_UPDT_ID (1 << 0) /* PLL ID */
|
||||
#define AT91_PMC_PLL_UPDT_ID_MSK (0xf) /* PLL ID mask */
|
||||
#define AT91_PMC_PLL_UPDT_STUPTIM (0xff << 16) /* Startup time */
|
||||
|
||||
#define AT91_CKGR_MOR 0x20 /* Main Oscillator Register [not on SAM9RL] */
|
||||
|
@ -136,6 +137,8 @@
|
|||
#define AT91_PMC_PLLADIV2_ON (1 << 12)
|
||||
#define AT91_PMC_H32MXDIV BIT(24)
|
||||
|
||||
#define AT91_PMC_XTALF 0x34 /* Main XTAL Frequency Register [SAMA7G5 only] */
|
||||
|
||||
#define AT91_PMC_USB 0x38 /* USB Clock Register [some SAM9 only] */
|
||||
#define AT91_PMC_USBS (0x1 << 0) /* USB OHCI Input clock selection */
|
||||
#define AT91_PMC_USBS_PLLA (0 << 0)
|
||||
|
@ -174,6 +177,7 @@
|
|||
#define AT91_PMC_MOSCRCS (1 << 17) /* Main On-Chip RC [some SAM9] */
|
||||
#define AT91_PMC_CFDEV (1 << 18) /* Clock Failure Detector Event [some SAM9] */
|
||||
#define AT91_PMC_GCKRDY (1 << 24) /* Generated Clocks */
|
||||
#define AT91_PMC_MCKXRDY (1 << 26) /* Master Clock x [x=1..4] Ready Status */
|
||||
#define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */
|
||||
|
||||
#define AT91_PMC_FSMR 0x70 /* Fast Startup Mode Register */
|
||||
|
|
Loading…
Reference in New Issue