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
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 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
__clk_get_name((req->best_parent_hw)->clk),
req->best_parent_rate);
if (best_rate < 0)
return best_rate;
if (best_rate < 0 || (gck->range.max && best_rate > gck->range.max))
return -EINVAL;
req->rate = best_rate;
return 0;
......@@ -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;
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.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;
unsigned long flags;
u8 div;
u16 mul;
u32 val;
return sam9x60_pll_ready(regmap, id);
}
spin_lock_irqsave(pll->lock, flags);
regmap_write(regmap, AT91_PMC_PLL_UPDT, pll->id);
static unsigned long sam9x60_frac_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_frac *frac = to_sam9x60_frac(core);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
return (parent_rate * (frac->mul + 1) +
((u64)parent_rate * frac->frac >> 22));
}
static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_frac *frac = to_sam9x60_frac(core);
struct regmap *regmap = core->regmap;
unsigned int val, cfrac, cmul;
unsigned long flags;
spin_lock_irqsave(core->lock, flags);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;
if (sam9x60_pll_ready(regmap, pll->id) &&
(div == pll->div && mul == pll->mul)) {
spin_unlock_irqrestore(pll->lock, flags);
return 0;
}
if (sam9x60_frac_pll_ready(regmap, core->id) &&
(cmul == frac->mul && cfrac == frac->frac))
goto unlock;
/* Recommended value for AT91_PMC_PLL_ACR */
if (pll->characteristics->upll)
/* Recommended value for PMC_PLL_ACR */
if (core->characteristics->upll)
val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
else
val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
regmap_write(regmap, AT91_PMC_PLL_ACR, val);
regmap_write(regmap, AT91_PMC_PLL_CTRL1,
FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul));
(frac->mul << core->layout->mul_shift) |
(frac->frac << core->layout->frac_shift));
if (pll->characteristics->upll) {
if (core->characteristics->upll) {
/* Enable the UTMI internal bandgap */
val |= AT91_PMC_PLL_ACR_UTMIBG;
regmap_write(regmap, AT91_PMC_PLL_ACR, val);
......@@ -94,221 +121,409 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
}
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | core->id);
regmap_write(regmap, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL |
AT91_PMC_PLL_CTRL0_ENPLLCK | pll->div);
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | core->id);
while (!sam9x60_pll_ready(regmap, pll->id))
while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
spin_unlock_irqrestore(pll->lock, flags);
unlock:
spin_unlock_irqrestore(core->lock, flags);
return 0;
}
static int sam9x60_pll_is_prepared(struct clk_hw *hw)
static void sam9x60_frac_pll_unprepare(struct clk_hw *hw)
{
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
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);
regmap_write(pll->regmap, AT91_PMC_PLL_UPDT, pll->id);
spin_lock_irqsave(core->lock, flags);
regmap_update_bits(pll->regmap, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENPLLCK, 0);
regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
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_ENPLL, 0);
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, AT91_PMC_PLL_CTRL0_ENPLL, 0);
if (pll->characteristics->upll)
regmap_update_bits(pll->regmap, AT91_PMC_PLL_ACR,
AT91_PMC_PLL_ACR_UTMIBG |
AT91_PMC_PLL_ACR_UTMIVR, 0);
if (core->characteristics->upll)
regmap_update_bits(regmap, AT91_PMC_PLL_ACR,
AT91_PMC_PLL_ACR_UTMIBG | AT91_PMC_PLL_ACR_UTMIVR, 0);
regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
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(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,
static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core,
unsigned long rate,
unsigned long parent_rate,
bool update)
{
const struct clk_pll_characteristics *characteristics =
pll->characteristics;
unsigned long bestremainder = ULONG_MAX;
unsigned long maxdiv, mindiv, tmpdiv;
long bestrate = -ERANGE;
unsigned long bestdiv = 0;
unsigned long bestmul = 0;
unsigned long bestfrac = 0;
struct sam9x60_frac *frac = to_sam9x60_frac(core);
unsigned long tmprate, remainder;
unsigned long nmul = 0;
unsigned long nfrac = 0;
if (rate < characteristics->output[0].min ||
rate > characteristics->output[0].max)
if (rate < FCORE_MIN || rate > FCORE_MAX)
return -ERANGE;
if (!pll->characteristics->upll) {
mindiv = parent_rate / rate;
if (mindiv < 2)
mindiv = 2;
maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX, rate);
if (maxdiv > PLL_DIV_MAX)
maxdiv = PLL_DIV_MAX;
} else {
mindiv = maxdiv = UPLL_DIV;
}
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);
nmul = mult_frac(rate, 1, parent_rate);
tmprate = mult_frac(parent_rate, nmul, 1);
remainder = rate - tmprate;
if (remainder) {
tmpfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * tmpdiv * (1 << 22),
nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
parent_rate);
tmprate += DIV_ROUND_CLOSEST_ULL((u64)tmpfrac * parent_rate,
tmpdiv * (1 << 22));
tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
(1 << 22));
}
if (tmprate > rate)
remainder = tmprate - rate;
else
remainder = rate - tmprate;
/* 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;
}
/*
* 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;
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 =
core->characteristics;
struct clk_hw *parent = clk_hw_get_parent(&core->hw);
unsigned long tmp_rate, tmp_parent_rate, tmp_diff;
long best_diff = -1, best_rate = -EINVAL;
u32 divid, best_div;
if (!rate)
return 0;
if (rate < characteristics->output[0].min ||
rate > characteristics->output[0].max)
return -ERANGE;
for (divid = 1; divid < core->layout->div_mask; divid++) {
tmp_parent_rate = clk_hw_round_rate(parent, rate * divid);
if (!tmp_parent_rate)
continue;
tmp_rate = DIV_ROUND_CLOSEST_ULL(tmp_parent_rate, divid);
tmp_diff = abs(rate - tmp_rate);
if (best_diff < 0 || best_diff > tmp_diff) {
*parent_rate = tmp_parent_rate;
best_rate = tmp_rate;
best_diff = tmp_diff;
best_div = divid;
}
/* We've found a perfect match! */
if (!remainder)
if (!best_diff)
break;
}
/* Check if bestrate is a valid output rate */
if (bestrate < characteristics->output[0].min &&
bestrate > characteristics->output[0].max)
if (best_rate < characteristics->output[0].min ||
best_rate > characteristics->output[0].max)
return -ERANGE;
if (update) {
pll->div = bestdiv - 1;
pll->mul = bestmul - 1;
pll->frac = bestfrac;
}
return bestrate;
return best_rate;
}
static long sam9x60_pll_round_rate(struct clk_hw *hw, unsigned long rate,
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,
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;
frac->core.id = id;
frac->core.hw.init = &init;
frac->core.characteristics = characteristics;
frac->core.layout = layout;
frac->core.regmap = regmap;
frac->core.lock = lock;
spin_lock_irqsave(frac->core.lock, flags);
if (sam9x60_pll_ready(regmap, id)) {
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_ID_MSK, id);
regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
frac->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
frac->frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val);
} else {
/*
* This means the PLL is not setup by bootloaders. In this
* case we need to set the minimum rate for it. Otherwise
* a clock child of this PLL may be enabled before setting
* its rate leading to enabling this PLL with unsupported
* rate. This will lead to PLL not being locked at all.
*/
parent_rate = clk_hw_get_rate(parent_hw);
if (!parent_rate) {
hw = ERR_PTR(-EINVAL);
goto free;
}
ret = sam9x60_frac_pll_compute_mul_frac(&frac->core, FCORE_MIN,
parent_rate, true);
if (ret <= 0) {
hw = ERR_PTR(ret);
goto free;
}
}
spin_unlock_irqrestore(frac->core.lock, flags);
hw = &frac->core.hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(frac);
hw = ERR_PTR(ret);
}
return hw;
free:
spin_unlock_irqrestore(frac->core.lock, flags);
kfree(frac);
return hw;
}
pll->id = id;
pll->hw.init = &init;
pll->characteristics = characteristics;
pll->regmap = regmap;
pll->lock = lock;
struct clk_hw * __init
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, bool critical)
{
struct sam9x60_div *div;
struct clk_hw *hw;
struct clk_init_data init;
unsigned long flags;
unsigned int val;
int ret;
if (id > PLL_MAX_ID || !lock)
return ERR_PTR(-EINVAL);
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
return ERR_PTR(-ENOMEM);
init.name = name;
init.parent_names = &parent_name;
init.num_parents = 1;
init.ops = &sam9x60_div_pll_ops;
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
CLK_SET_RATE_PARENT;
if (critical)
init.flags |= CLK_IS_CRITICAL;
div->core.id = id;
div->core.hw.init = &init;
div->core.characteristics = characteristics;
div->core.layout = layout;
div->core.regmap = regmap;
div->core.lock = lock;
spin_lock_irqsave(div->core.lock, flags);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_ID_MSK, id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
div->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
regmap_write(regmap, AT91_PMC_PLL_UPDT, id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &pllr);
pll->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, pllr);
regmap_read(regmap, AT91_PMC_PLL_CTRL1, &pllr);
pll->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, pllr);
spin_unlock_irqrestore(div->core.lock, flags);
hw = &pll->hw;
hw = &div->core.hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(pll);
kfree(div);
hw = ERR_PTR(ret);
}
......
......@@ -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,
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_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;
......
// SPDX-License-Identifier: GPL-2.0
/*
* SAMA7G5 PMC code.
*
* Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/slab.h>
#include <dt-bindings/clock/at91.h>
#include "pmc.h"
#define SAMA7G5_INIT_TABLE(_table, _count) \
do { \
u8 _i; \
for (_i = 0; _i < (_count); _i++) \
(_table)[_i] = _i; \
} while (0)
#define SAMA7G5_FILL_TABLE(_to, _from, _count) \
do { \
u8 _i; \
for (_i = 0; _i < (_count); _i++) { \
(_to)[_i] = (_from)[_i]; \
} \
} while (0)
static DEFINE_SPINLOCK(pmc_pll_lock);
static DEFINE_SPINLOCK(pmc_mckX_lock);
/**
* PLL clocks identifiers
* @PLL_ID_CPU: CPU PLL identifier
* @PLL_ID_SYS: System PLL identifier
* @PLL_ID_DDR: DDR PLL identifier
* @PLL_ID_IMG: Image subsystem PLL identifier
* @PLL_ID_BAUD: Baud PLL identifier
* @PLL_ID_AUDIO: Audio PLL identifier
* @PLL_ID_ETH: Ethernet PLL identifier
*/
enum pll_ids {
PLL_ID_CPU,
PLL_ID_SYS,
PLL_ID_DDR,
PLL_ID_IMG,
PLL_ID_BAUD,
PLL_ID_AUDIO,
PLL_ID_ETH,
PLL_ID_MAX,
};
/**
* PLL type identifiers
* @PLL_TYPE_FRAC: fractional PLL identifier
* @PLL_TYPE_DIV: divider PLL identifier
*/
enum pll_type {
PLL_TYPE_FRAC,
PLL_TYPE_DIV,
};
/* Layout for fractional PLLs. */
static const struct clk_pll_layout pll_layout_frac = {
.mul_mask = GENMASK(31, 24),
.frac_mask = GENMASK(21, 0),
.mul_shift = 24,
.frac_shift = 0,
};
/* Layout for DIVPMC dividers. */
static const struct clk_pll_layout pll_layout_divpmc = {
.div_mask = GENMASK(7, 0),
.endiv_mask = BIT(29),
.div_shift = 0,
.endiv_shift = 29,
};
/* Layout for DIVIO dividers. */
static const struct clk_pll_layout pll_layout_divio = {
.div_mask = GENMASK(19, 12),
.endiv_mask = BIT(30),
.div_shift = 12,
.endiv_shift = 30,
};
/**
* PLL clocks description
* @n: clock name
* @p: clock parent
* @l: clock layout
* @t: clock type
* @f: true if clock is critical and cannot be disabled
* @eid: export index in sama7g5->chws[] array
*/
static const struct {
const char *n;
const char *p;
const struct clk_pll_layout *l;
u8 t;
u8 c;
u8 eid;
} sama7g5_plls[][PLL_ID_MAX] = {
[PLL_ID_CPU] = {
{ .n = "cpupll_fracck",
.p = "mainck",
.l = &pll_layout_frac,
.t = PLL_TYPE_FRAC,
.c = 1, },
{ .n = "cpupll_divpmcck",
.p = "cpupll_fracck",
.l = &pll_layout_divpmc,
.t = PLL_TYPE_DIV,
.c = 1, },
},
[PLL_ID_SYS] = {
{ .n = "syspll_fracck",
.p = "mainck",
.l = &pll_layout_frac,
.t = PLL_TYPE_FRAC,
.c = 1, },
{ .n = "syspll_divpmcck",
.p = "syspll_fracck",
.l = &pll_layout_divpmc,
.t = PLL_TYPE_DIV,
.c = 1, },
},
[PLL_ID_DDR] = {
{ .n = "ddrpll_fracck",
.p = "mainck",
.l = &pll_layout_frac,
.t = PLL_TYPE_FRAC,
.c = 1, },
{ .n = "ddrpll_divpmcck",
.p = "ddrpll_fracck",
.l = &pll_layout_divpmc,
.t = PLL_TYPE_DIV,
.c = 1, },
},
[PLL_ID_IMG] = {
{ .n = "imgpll_fracck",
.p = "mainck",
.l = &pll_layout_frac,
.t = PLL_TYPE_FRAC, },
{ .n = "imgpll_divpmcck",
.p = "imgpll_fracck",
.l = &pll_layout_divpmc,
.t = PLL_TYPE_DIV, },
},
[PLL_ID_BAUD] = {
{ .n = "baudpll_fracck",
.p = "mainck",
.l = &pll_layout_frac,
.t = PLL_TYPE_FRAC, },
{ .n = "baudpll_divpmcck",
.p = "baudpll_fracck",
.l = &pll_layout_divpmc,
.t = PLL_TYPE_DIV, },
},
[PLL_ID_AUDIO] = {
{ .n = "audiopll_fracck",
.p = "main_xtal",
.l = &pll_layout_frac,
.t = PLL_TYPE_FRAC, },
{ .n = "audiopll_divpmcck",
.p = "audiopll_fracck",
.l = &pll_layout_divpmc,
.t = PLL_TYPE_DIV,
.eid = PMC_I2S0_MUX, },
{ .n = "audiopll_diviock",
.p = "audiopll_fracck",
.l = &pll_layout_divio,
.t = PLL_TYPE_DIV,
.eid = PMC_I2S1_MUX, },
},
[PLL_ID_ETH] = {
{ .n = "ethpll_fracck",
.p = "main_xtal",
.l = &pll_layout_frac,
.t = PLL_TYPE_FRAC, },
{ .n = "ethpll_divpmcck",
.p = "ethpll_fracck",
.l = &pll_layout_divpmc,
.t = PLL_TYPE_DIV, },
},
};
/**
* Master clock (MCK[1..4]) description
* @n: clock name
* @ep: extra parents names array
* @ep_chg_chg_id: index in parents array that specifies the changeable
* parent
* @ep_count: extra parents count
* @ep_mux_table: mux table for extra parents
* @id: clock id
* @c: true if clock is critical and cannot be disabled
*/
static const struct {
const char *n;
const char *ep[4];
int ep_chg_id;
u8 ep_count;
u8 ep_mux_table[4];
u8 id;
u8 c;
} sama7g5_mckx[] = {
{ .n = "mck1",
.id = 1,
.ep = { "syspll_divpmcck", },
.ep_mux_table = { 5, },
.ep_count = 1,
.ep_chg_id = INT_MIN,
.c = 1, },
{ .n = "mck2",
.id = 2,
.ep = { "ddrpll_divpmcck", },
.ep_mux_table = { 6, },
.ep_count = 1,
.ep_chg_id = INT_MIN,
.c = 1, },
{ .n = "mck3",
.id = 3,
.ep = { "syspll_divpmcck", "ddrpll_divpmcck", "imgpll_divpmcck", },
.ep_mux_table = { 5, 6, 7, },
.ep_count = 3,
.ep_chg_id = 6, },
{ .n = "mck4",
.id = 4,
.ep = { "syspll_divpmcck", },
.ep_mux_table = { 5, },
.ep_count = 1,
.ep_chg_id = INT_MIN,
.c = 1, },
};
/**
* System clock description
* @n: clock name
* @p: clock parent name
* @id: clock id
*/
static const struct {
const char *n;
const char *p;
u8 id;
} sama7g5_systemck[] = {
{ .n = "pck0", .p = "prog0", .id = 8, },
{ .n = "pck1", .p = "prog1", .id = 9, },
{ .n = "pck2", .p = "prog2", .id = 10, },
{ .n = "pck3", .p = "prog3", .id = 11, },
{ .n = "pck4", .p = "prog4", .id = 12, },
{ .n = "pck5", .p = "prog5", .id = 13, },
{ .n = "pck6", .p = "prog6", .id = 14, },
{ .n = "pck7", .p = "prog7", .id = 15, },
};
/* Mux table for programmable clocks. */
static u32 sama7g5_prog_mux_table[] = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, };
/**
* Peripheral clock description
* @n: clock name
* @p: clock parent name
* @r: clock range values
* @id: clock id
* @chgp: index in parent array of the changeable parent
*/
static const struct {
const char *n;
const char *p;
struct clk_range r;
u8 chgp;
u8 id;
} sama7g5_periphck[] = {
{ .n = "pioA_clk", .p = "mck0", .id = 11, },
{ .n = "sfr_clk", .p = "mck1", .id = 19, },
{ .n = "hsmc_clk", .p = "mck1", .id = 21, },
{ .n = "xdmac0_clk", .p = "mck1", .id = 22, },
{ .n = "xdmac1_clk", .p = "mck1", .id = 23, },
{ .n = "xdmac2_clk", .p = "mck1", .id = 24, },
{ .n = "acc_clk", .p = "mck1", .id = 25, },
{ .n = "aes_clk", .p = "mck1", .id = 27, },
{ .n = "tzaesbasc_clk", .p = "mck1", .id = 28, },
{ .n = "asrc_clk", .p = "mck1", .id = 30, .r = { .max = 200000000, }, },
{ .n = "cpkcc_clk", .p = "mck0", .id = 32, },
{ .n = "csi_clk", .p = "mck3", .id = 33, .r = { .max = 266000000, }, .chgp = 1, },
{ .n = "csi2dc_clk", .p = "mck3", .id = 34, .r = { .max = 266000000, }, .chgp = 1, },
{ .n = "eic_clk", .p = "mck1", .id = 37, },
{ .n = "flex0_clk", .p = "mck1", .id = 38, },
{ .n = "flex1_clk", .p = "mck1", .id = 39, },
{ .n = "flex2_clk", .p = "mck1", .id = 40, },
{ .n = "flex3_clk", .p = "mck1", .id = 41, },
{ .n = "flex4_clk", .p = "mck1", .id = 42, },
{ .n = "flex5_clk", .p = "mck1", .id = 43, },
{ .n = "flex6_clk", .p = "mck1", .id = 44, },
{ .n = "flex7_clk", .p = "mck1", .id = 45, },
{ .n = "flex8_clk", .p = "mck1", .id = 46, },
{ .n = "flex9_clk", .p = "mck1", .id = 47, },
{ .n = "flex10_clk", .p = "mck1", .id = 48, },
{ .n = "flex11_clk", .p = "mck1", .id = 49, },
{ .n = "gmac0_clk", .p = "mck1", .id = 51, },
{ .n = "gmac1_clk", .p = "mck1", .id = 52, },
{ .n = "icm_clk", .p = "mck1", .id = 55, },
{ .n = "isc_clk", .p = "mck3", .id = 56, .r = { .max = 266000000, }, .chgp = 1, },
{ .n = "i2smcc0_clk", .p = "mck1", .id = 57, .r = { .max = 200000000, }, },
{ .n = "i2smcc1_clk", .p = "mck1", .id = 58, .r = { .max = 200000000, }, },
{ .n = "matrix_clk", .p = "mck1", .id = 60, },
{ .n = "mcan0_clk", .p = "mck1", .id = 61, .r = { .max = 200000000, }, },
{ .n = "mcan1_clk", .p = "mck1", .id = 62, .r = { .max = 200000000, }, },
{ .n = "mcan2_clk", .p = "mck1", .id = 63, .r = { .max = 200000000, }, },
{ .n = "mcan3_clk", .p = "mck1", .id = 64, .r = { .max = 200000000, }, },
{ .n = "mcan4_clk", .p = "mck1", .id = 65, .r = { .max = 200000000, }, },
{ .n = "mcan5_clk", .p = "mck1", .id = 66, .r = { .max = 200000000, }, },
{ .n = "pdmc0_clk", .p = "mck1", .id = 68, .r = { .max = 200000000, }, },
{ .n = "pdmc1_clk", .p = "mck1", .id = 69, .r = { .max = 200000000, }, },
{ .n = "pit64b0_clk", .p = "mck1", .id = 70, },
{ .n = "pit64b1_clk", .p = "mck1", .id = 71, },
{ .n = "pit64b2_clk", .p = "mck1", .id = 72, },
{ .n = "pit64b3_clk", .p = "mck1", .id = 73, },
{ .n = "pit64b4_clk", .p = "mck1", .id = 74, },
{ .n = "pit64b5_clk", .p = "mck1", .id = 75, },
{ .n = "pwm_clk", .p = "mck1", .id = 77, },
{ .n = "qspi0_clk", .p = "mck1", .id = 78, },
{ .n = "qspi1_clk", .p = "mck1", .id = 79, },
{ .n = "sdmmc0_clk", .p = "mck1", .id = 80, },
{ .n = "sdmmc1_clk", .p = "mck1", .id = 81, },
{ .n = "sdmmc2_clk", .p = "mck1", .id = 82, },
{ .n = "sha_clk", .p = "mck1", .id = 83, },
{ .n = "spdifrx_clk", .p = "mck1", .id = 84, .r = { .max = 200000000, }, },
{ .n = "spdiftx_clk", .p = "mck1", .id = 85, .r = { .max = 200000000, }, },
{ .n = "ssc0_clk", .p = "mck1", .id = 86, .r = { .max = 200000000, }, },
{ .n = "ssc1_clk", .p = "mck1", .id = 87, .r = { .max = 200000000, }, },
{ .n = "tcb0_ch0_clk", .p = "mck1", .id = 88, .r = { .max = 200000000, }, },
{ .n = "tcb0_ch1_clk", .p = "mck1", .id = 89, .r = { .max = 200000000, }, },
{ .n = "tcb0_ch2_clk", .p = "mck1", .id = 90, .r = { .max = 200000000, }, },
{ .n = "tcb1_ch0_clk", .p = "mck1", .id = 91, .r = { .max = 200000000, }, },
{ .n = "tcb1_ch1_clk", .p = "mck1", .id = 92, .r = { .max = 200000000, }, },
{ .n = "tcb1_ch2_clk", .p = "mck1", .id = 93, .r = { .max = 200000000, }, },
{ .n = "tcpca_clk", .p = "mck1", .id = 94, },
{ .n = "tcpcb_clk", .p = "mck1", .id = 95, },
{ .n = "tdes_clk", .p = "mck1", .id = 96, },
{ .n = "trng_clk", .p = "mck1", .id = 97, },
{ .n = "udphsa_clk", .p = "mck1", .id = 104, },
{ .n = "udphsb_clk", .p = "mck1", .id = 105, },
{ .n = "uhphs_clk", .p = "mck1", .id = 106, },
};
/**
* Generic clock description
* @n: clock name
* @pp: PLL parents
* @pp_mux_table: PLL parents mux table
* @r: clock output range
* @pp_chg_id: id in parrent array of changeable PLL parent
* @pp_count: PLL parents count
* @id: clock id
*/
static const struct {
const char *n;
const char *pp[8];
const char pp_mux_table[8];
struct clk_range r;
int pp_chg_id;
u8 pp_count;
u8 id;
} sama7g5_gck[] = {
{ .n = "adc_gclk",
.id = 26,
.r = { .max = 100000000, },
.pp = { "syspll_divpmcck", "imgpll_divpmcck", "audiopll_divpmcck", },
.pp_mux_table = { 5, 7, 9, },
.pp_count = 3,
.pp_chg_id = INT_MIN, },
{ .n = "asrc_gclk",
.id = 30,
.r = { .max = 200000000 },
.pp = { "audiopll_divpmcck", },
.pp_mux_table = { 9, },
.pp_count = 1,
.pp_chg_id = 4, },
{ .n = "csi_gclk",
.id = 33,
.r = { .max = 27000000 },
.pp = { "ddrpll_divpmcck", "imgpll_divpmcck", },
.pp_mux_table = { 6, 7, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex0_gclk",
.id = 38,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex1_gclk",
.id = 39,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex2_gclk",
.id = 40,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex3_gclk",
.id = 41,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex4_gclk",
.id = 42,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex5_gclk",
.id = 43,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex6_gclk",
.id = 44,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex7_gclk",
.id = 45,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex8_gclk",
.id = 46,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex9_gclk",
.id = 47,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex10_gclk",
.id = 48,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "flex11_gclk",
.id = 49,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "gmac0_gclk",
.id = 51,
.r = { .max = 125000000 },
.pp = { "ethpll_divpmcck", },
.pp_mux_table = { 10, },
.pp_count = 1,
.pp_chg_id = 4, },
{ .n = "gmac1_gclk",
.id = 52,
.r = { .max = 50000000 },
.pp = { "ethpll_divpmcck", },
.pp_mux_table = { 10, },
.pp_count = 1,
.pp_chg_id = INT_MIN, },
{ .n = "gmac0_tsu_gclk",
.id = 53,
.r = { .max = 300000000 },
.pp = { "audiopll_divpmcck", "ethpll_divpmcck", },
.pp_mux_table = { 9, 10, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "gmac1_tsu_gclk",
.id = 54,
.r = { .max = 300000000 },
.pp = { "audiopll_divpmcck", "ethpll_divpmcck", },
.pp_mux_table = { 9, 10, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "i2smcc0_gclk",
.id = 57,
.r = { .max = 100000000 },
.pp = { "syspll_divpmcck", "audiopll_divpmcck", },
.pp_mux_table = { 5, 9, },
.pp_count = 2,
.pp_chg_id = 5, },
{ .n = "i2smcc1_gclk",
.id = 58,
.r = { .max = 100000000 },
.pp = { "syspll_divpmcck", "audiopll_divpmcck", },
.pp_mux_table = { 5, 9, },
.pp_count = 2,
.pp_chg_id = 5, },
{ .n = "mcan0_gclk",
.id = 61,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "mcan1_gclk",
.id = 62,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "mcan2_gclk",
.id = 63,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "mcan3_gclk",
.id = 64,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "mcan4_gclk",
.id = 65,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "mcan5_gclk",
.id = 66,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "pdmc0_gclk",
.id = 68,
.r = { .max = 50000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "pdmc1_gclk",
.id = 69,
.r = { .max = 50000000, },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "pit64b0_gclk",
.id = 70,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
"audiopll_divpmcck", "ethpll_divpmcck", },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
{ .n = "pit64b1_gclk",
.id = 71,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
"audiopll_divpmcck", "ethpll_divpmcck", },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
{ .n = "pit64b2_gclk",
.id = 72,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
"audiopll_divpmcck", "ethpll_divpmcck", },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
{ .n = "pit64b3_gclk",
.id = 73,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
"audiopll_divpmcck", "ethpll_divpmcck", },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
{ .n = "pit64b4_gclk",
.id = 74,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
"audiopll_divpmcck", "ethpll_divpmcck", },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
{ .n = "pit64b5_gclk",
.id = 75,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
"audiopll_divpmcck", "ethpll_divpmcck", },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
{ .n = "qspi0_gclk",
.id = 78,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "qspi1_gclk",
.id = 79,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = INT_MIN, },
{ .n = "sdmmc0_gclk",
.id = 80,
.r = { .max = 208000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = 5, },
{ .n = "sdmmc1_gclk",
.id = 81,
.r = { .max = 208000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = 5, },
{ .n = "sdmmc2_gclk",
.id = 82,
.r = { .max = 208000000 },
.pp = { "syspll_divpmcck", "baudpll_divpmcck", },
.pp_mux_table = { 5, 8, },
.pp_count = 2,
.pp_chg_id = 5, },
{ .n = "spdifrx_gclk",
.id = 84,
.r = { .max = 150000000 },
.pp = { "syspll_divpmcck", "audiopll_divpmcck", },
.pp_mux_table = { 5, 9, },
.pp_count = 2,
.pp_chg_id = 5, },
{ .n = "spdiftx_gclk",
.id = 85,
.r = { .max = 25000000 },
.pp = { "syspll_divpmcck", "audiopll_divpmcck", },
.pp_mux_table = { 5, 9, },
.pp_count = 2,
.pp_chg_id = 5, },
{ .n = "tcb0_ch0_gclk",
.id = 88,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
"audiopll_divpmcck", "ethpll_divpmcck", },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
{ .n = "tcb1_ch0_gclk",
.id = 91,
.r = { .max = 200000000 },
.pp = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
"audiopll_divpmcck", "ethpll_divpmcck", },
.pp_mux_table = { 5, 7, 8, 9, 10, },
.pp_count = 5,
.pp_chg_id = INT_MIN, },
{ .n = "tcpca_gclk",
.id = 94,
.r = { .max = 32768, },
.pp_chg_id = INT_MIN, },
{ .n = "tcpcb_gclk",
.id = 95,
.r = { .max = 32768, },
.pp_chg_id = INT_MIN, },
};
/* PLL output range. */
static const struct clk_range pll_outputs[] = {
{ .min = 2343750, .max = 1200000000 },
};
/* PLL characteristics. */
static const struct clk_pll_characteristics pll_characteristics = {
.input = { .min = 12000000, .max = 50000000 },
.num_output = ARRAY_SIZE(pll_outputs),
.output = pll_outputs,
};
/* MCK0 characteristics. */
static const struct clk_master_characteristics mck0_characteristics = {
.output = { .min = 140000000, .max = 200000000 },
.divisors = { 1, 2, 4, 3 },
.have_div3_pres = 1,
};
/* MCK0 layout. */
static const struct clk_master_layout mck0_layout = {
.mask = 0x373,
.pres_shift = 4,
.offset = 0x28,
};
/* Programmable clock layout. */
static const struct clk_programmable_layout programmable_layout = {
.pres_mask = 0xff,
.pres_shift = 8,
.css_mask = 0x1f,
.have_slck_mck = 0,
.is_pres_direct = 1,
};
/* Peripheral clock layout. */
static const struct clk_pcr_layout sama7g5_pcr_layout = {
.offset = 0x88,
.cmd = BIT(31),
.gckcss_mask = GENMASK(12, 8),
.pid_mask = GENMASK(6, 0),
};
static void __init sama7g5_pmc_setup(struct device_node *np)
{
const char *td_slck_name, *md_slck_name, *mainxtal_name;
struct pmc_data *sama7g5_pmc;
const char *parent_names[10];
void **alloc_mem = NULL;
int alloc_mem_size = 0;
struct regmap *regmap;
struct clk_hw *hw;
bool bypass;
int i, j;
i = of_property_match_string(np, "clock-names", "td_slck");
if (i < 0)
return;
td_slck_name = of_clk_get_parent_name(np, i);
i = of_property_match_string(np, "clock-names", "md_slck");
if (i < 0)
return;
md_slck_name = of_clk_get_parent_name(np, i);
i = of_property_match_string(np, "clock-names", "main_xtal");
if (i < 0)
return;
mainxtal_name = of_clk_get_parent_name(np, i);
regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
sama7g5_pmc = pmc_data_allocate(PMC_I2S1_MUX + 1,
nck(sama7g5_systemck),
nck(sama7g5_periphck),
nck(sama7g5_gck));
if (!sama7g5_pmc)
return;
alloc_mem = kmalloc(sizeof(void *) *
(ARRAY_SIZE(sama7g5_mckx) + ARRAY_SIZE(sama7g5_gck)),
GFP_KERNEL);
if (!alloc_mem)
goto err_free;
hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
50000000);
if (IS_ERR(hw))
goto err_free;
bypass = of_property_read_bool(np, "atmel,osc-bypass");
hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
bypass);
if (IS_ERR(hw))
goto err_free;
parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
if (IS_ERR(hw))
goto err_free;
sama7g5_pmc->chws[PMC_MAIN] = hw;
for (i = 0; i < PLL_ID_MAX; i++) {
for (j = 0; j < 3; j++) {
struct clk_hw *parent_hw;
if (!sama7g5_plls[i][j].n)
continue;
switch (sama7g5_plls[i][j].t) {
case PLL_TYPE_FRAC:
if (!strcmp(sama7g5_plls[i][j].p, "mainck"))
parent_hw = sama7g5_pmc->chws[PMC_MAIN];
else
parent_hw = __clk_get_hw(of_clk_get_by_name(np,
sama7g5_plls[i][j].p));
hw = sam9x60_clk_register_frac_pll(regmap,
&pmc_pll_lock, sama7g5_plls[i][j].n,
sama7g5_plls[i][j].p, parent_hw, i,
&pll_characteristics,
sama7g5_plls[i][j].l,
sama7g5_plls[i][j].c);
break;
case PLL_TYPE_DIV:
hw = sam9x60_clk_register_div_pll(regmap,
&pmc_pll_lock, sama7g5_plls[i][j].n,
sama7g5_plls[i][j].p, i,
&pll_characteristics,
sama7g5_plls[i][j].l,
sama7g5_plls[i][j].c);
break;
default:
continue;
}
if (IS_ERR(hw))
goto err_free;
if (sama7g5_plls[i][j].eid)
sama7g5_pmc->chws[sama7g5_plls[i][j].eid] = hw;
}
}
parent_names[0] = md_slck_name;
parent_names[1] = "mainck";
parent_names[2] = "cpupll_divpmcck";
parent_names[3] = "syspll_divpmcck";
hw = at91_clk_register_master(regmap, "mck0", 4, parent_names,
&mck0_layout, &mck0_characteristics);
if (IS_ERR(hw))
goto err_free;
sama7g5_pmc->chws[PMC_MCK] = hw;
parent_names[0] = md_slck_name;
parent_names[1] = td_slck_name;
parent_names[2] = "mainck";
parent_names[3] = "mck0";
for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) {
u8 num_parents = 4 + sama7g5_mckx[i].ep_count;
u32 *mux_table;
mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
GFP_KERNEL);
if (!mux_table)
goto err_free;
SAMA7G5_INIT_TABLE(mux_table, 4);
SAMA7G5_FILL_TABLE(&mux_table[4], sama7g5_mckx[i].ep_mux_table,
sama7g5_mckx[i].ep_count);
SAMA7G5_FILL_TABLE(&parent_names[4], sama7g5_mckx[i].ep,
sama7g5_mckx[i].ep_count);
hw = at91_clk_sama7g5_register_master(regmap, sama7g5_mckx[i].n,
num_parents, parent_names, mux_table,
&pmc_mckX_lock, sama7g5_mckx[i].id,
sama7g5_mckx[i].c,
sama7g5_mckx[i].ep_chg_id);
if (IS_ERR(hw))
goto err_free;
alloc_mem[alloc_mem_size++] = mux_table;
}
hw = at91_clk_sama7g5_register_utmi(regmap, "utmick", "main_xtal");
if (IS_ERR(hw))
goto err_free;
sama7g5_pmc->chws[PMC_UTMI] = hw;
parent_names[0] = md_slck_name;
parent_names[1] = td_slck_name;
parent_names[2] = "mainck";
parent_names[3] = "mck0";
parent_names[4] = "syspll_divpmcck";
parent_names[5] = "ddrpll_divpmcck";
parent_names[6] = "imgpll_divpmcck";
parent_names[7] = "baudpll_divpmcck";
parent_names[8] = "audiopll_divpmcck";
parent_names[9] = "ethpll_divpmcck";
for (i = 0; i < 8; i++) {
char name[6];
snprintf(name, sizeof(name), "prog%d", i);
hw = at91_clk_register_programmable(regmap, name, parent_names,
10, i,
&programmable_layout,
sama7g5_prog_mux_table);
if (IS_ERR(hw))
goto err_free;
}
for (i = 0; i < ARRAY_SIZE(sama7g5_systemck); i++) {
hw = at91_clk_register_system(regmap, sama7g5_systemck[i].n,
sama7g5_systemck[i].p,
sama7g5_systemck[i].id);
if (IS_ERR(hw))
goto err_free;
sama7g5_pmc->shws[sama7g5_systemck[i].id] = hw;
}
for (i = 0; i < ARRAY_SIZE(sama7g5_periphck); i++) {
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
&sama7g5_pcr_layout,
sama7g5_periphck[i].n,
sama7g5_periphck[i].p,
sama7g5_periphck[i].id,
&sama7g5_periphck[i].r,
sama7g5_periphck[i].chgp ? 0 :
INT_MIN);
if (IS_ERR(hw))
goto err_free;
sama7g5_pmc->phws[sama7g5_periphck[i].id] = hw;
}
parent_names[0] = md_slck_name;
parent_names[1] = td_slck_name;
parent_names[2] = "mainck";
parent_names[3] = "mck0";
for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) {
u8 num_parents = 4 + sama7g5_gck[i].pp_count;
u32 *mux_table;
mux_table = kmalloc_array(num_parents, sizeof(*mux_table),
GFP_KERNEL);
if (!mux_table)
goto err_free;
SAMA7G5_INIT_TABLE(mux_table, 4);
SAMA7G5_FILL_TABLE(&mux_table[4], sama7g5_gck[i].pp_mux_table,
sama7g5_gck[i].pp_count);
SAMA7G5_FILL_TABLE(&parent_names[4], sama7g5_gck[i].pp,
sama7g5_gck[i].pp_count);
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sama7g5_pcr_layout,
sama7g5_gck[i].n,
parent_names, mux_table,
num_parents,
sama7g5_gck[i].id,
&sama7g5_gck[i].r,
sama7g5_gck[i].pp_chg_id);
if (IS_ERR(hw))
goto err_free;
sama7g5_pmc->ghws[sama7g5_gck[i].id] = hw;
alloc_mem[alloc_mem_size++] = mux_table;
}
of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sama7g5_pmc);
return;
err_free:
if (alloc_mem) {
for (i = 0; i < alloc_mem_size; i++)
kfree(alloc_mem[i]);
kfree(alloc_mem);
}
pmc_data_free(sama7g5_pmc);
}
/* Some clks are used for a clocksource */
CLK_OF_DECLARE(sama7g5_pmc, "microchip,sama7g5-pmc", sama7g5_pmc_setup);
......@@ -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;
......
// 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>
......
/* 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 */
......
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