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

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

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

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

* clk-unused:
  clk: drop unused function __clk_get_flags

* clk-at91:
  clk: at91: sama7g5: add clock support for sama7g5
  clk: at91: clk-utmi: add utmi support for sama7g5
  clk: at91: clk-sam9x60-pll: re-factor to support plls with multiple outputs
  clk: at91: add macro for pll ids mask
  clk: at91: clk-programmable: add mux_table option
  clk: at91: clk-peripheral: add support for changeable parent rate
  clk: at91: clk-master: add master clock support for SAMA7G5
  clk: at91: clk-generated: add mux_table option
  clk: at91: clk-generated: pass the id of changeable parent at registration
  clk: at91: replace conditional operator with double logical not
  clk: at91: sckc: register slow_rc with accuracy option
  clk: at91: sam9x60: fix main rc oscillator frequency
  clk: at91: sam9x60-pll: use frac when setting frequency
  clk: at91: sam9x60-pll: check fcore against ranges
  clk: at91: sam9x60-pll: use logical or for range check
  clk: at91: clk-sam9x60-pll: fix mul mask
  clk: at91: clk-generated: check best_rate against ranges
  clk: at91: clk-generated: continue if __clk_determine_rate() returns error
  clk: at91: fix possible dead lock in new drivers
This commit is contained in:
Stephen Boyd 2020-08-03 15:07:18 -07:00
Коммит dd9c697a94
31 изменённых файлов: 2471 добавлений и 292 удалений

Просмотреть файл

@ -28,6 +28,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_SPARX5) += clk-sparx5.o
obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o
obj-$(CONFIG_COMMON_CLK_FSL_SAI) += clk-fsl-sai.o
obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o

Просмотреть файл

@ -23,3 +23,4 @@ obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o
obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o
obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o
obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o
obj-$(CONFIG_SOC_SAMA7G5) += sama7g5.o

Просмотреть файл

@ -160,7 +160,8 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name,
parent_names, 4, i,
&at91rm9200_programmable_layout);
&at91rm9200_programmable_layout,
NULL);
if (IS_ERR(hw))
goto err_free;

Просмотреть файл

@ -436,7 +436,8 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
hw = at91_clk_register_programmable(regmap, name,
parent_names, 4, i,
&at91rm9200_programmable_layout);
&at91rm9200_programmable_layout,
NULL);
if (IS_ERR(hw))
goto err_free;

Просмотреть файл

@ -111,7 +111,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
return;
mainxtal_name = of_clk_get_parent_name(np, i);
regmap = syscon_node_to_regmap(np);
regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
@ -181,7 +181,8 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
&at91sam9g45_programmable_layout);
&at91sam9g45_programmable_layout,
NULL);
if (IS_ERR(hw))
goto err_free;

Просмотреть файл

@ -124,7 +124,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
return;
mainxtal_name = of_clk_get_parent_name(np, i);
regmap = syscon_node_to_regmap(np);
regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
@ -199,7 +199,8 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
&at91sam9x5_programmable_layout);
&at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw))
goto err_free;
@ -222,7 +223,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
at91sam9n12_periphck[i].n,
"masterck",
at91sam9n12_periphck[i].id,
&range);
&range, INT_MIN);
if (IS_ERR(hw))
goto err_free;

Просмотреть файл

@ -137,7 +137,8 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
&at91rm9200_programmable_layout);
&at91rm9200_programmable_layout,
NULL);
if (IS_ERR(hw))
goto err_free;

Просмотреть файл

@ -226,7 +226,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
&at91sam9x5_programmable_layout);
&at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw))
goto err_free;
@ -257,7 +258,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
at91sam9x5_periphck[i].n,
"masterck",
at91sam9x5_periphck[i].id,
&range);
&range, INT_MIN);
if (IS_ERR(hw))
goto err_free;
@ -270,7 +271,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
extra_pcks[i].n,
"masterck",
extra_pcks[i].id,
&range);
&range, INT_MIN);
if (IS_ERR(hw))
goto err_free;

Просмотреть файл

@ -18,18 +18,17 @@
#define GENERATED_MAX_DIV 255
#define GCK_INDEX_DT_AUDIO_PLL 5
struct clk_generated {
struct clk_hw hw;
struct regmap *regmap;
struct clk_range range;
spinlock_t *lock;
u32 *mux_table;
u32 id;
u32 gckdiv;
const struct clk_pcr_layout *layout;
u8 parent_id;
bool audio_pll_allowed;
int chg_pid;
};
#define to_clk_generated(hw) \
@ -83,7 +82,7 @@ static int clk_generated_is_enabled(struct clk_hw *hw)
regmap_read(gck->regmap, gck->layout->offset, &status);
spin_unlock_irqrestore(gck->lock, flags);
return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
return !!(status & AT91_PMC_PCR_GCKEN);
}
static unsigned long
@ -109,7 +108,7 @@ static void clk_generated_best_diff(struct clk_rate_request *req,
tmp_rate = parent_rate / div;
tmp_diff = abs(req->rate - tmp_rate);
if (*best_diff < 0 || *best_diff > tmp_diff) {
if (*best_diff < 0 || *best_diff >= tmp_diff) {
*best_rate = tmp_rate;
*best_diff = tmp_diff;
req->best_parent_rate = parent_rate;
@ -129,7 +128,10 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
int i;
u32 div;
for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) {
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
if (gck->chg_pid == i)
continue;
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
@ -161,16 +163,17 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
* that the only clks able to modify gck rate are those of audio IPs.
*/
if (!gck->audio_pll_allowed)
if (gck->chg_pid < 0)
goto end;
parent = clk_hw_get_parent_by_index(hw, GCK_INDEX_DT_AUDIO_PLL);
parent = clk_hw_get_parent_by_index(hw, gck->chg_pid);
if (!parent)
goto end;
for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
req_parent.rate = req->rate * div;
__clk_determine_rate(parent, &req_parent);
if (__clk_determine_rate(parent, &req_parent))
continue;
clk_generated_best_diff(req, parent, req_parent.rate, div,
&best_diff, &best_rate);
@ -184,8 +187,8 @@ end:
__clk_get_name((req->best_parent_hw)->clk),
req->best_parent_rate);
if (best_rate < 0)
return best_rate;
if (best_rate < 0 || (gck->range.max && best_rate > gck->range.max))
return -EINVAL;
req->rate = best_rate;
return 0;
@ -199,7 +202,11 @@ static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
if (index >= clk_hw_get_num_parents(hw))
return -EINVAL;
gck->parent_id = index;
if (gck->mux_table)
gck->parent_id = clk_mux_index_to_val(gck->mux_table, 0, index);
else
gck->parent_id = index;
return 0;
}
@ -271,8 +278,9 @@ struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
u8 num_parents, u8 id, bool pll_audio,
const struct clk_range *range)
u32 *mux_table, u8 num_parents, u8 id,
const struct clk_range *range,
int chg_pid)
{
struct clk_generated *gck;
struct clk_init_data init;
@ -287,16 +295,18 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
init.ops = &generated_ops;
init.parent_names = parent_names;
init.num_parents = num_parents;
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
CLK_SET_RATE_PARENT;
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
if (chg_pid >= 0)
init.flags |= CLK_SET_RATE_PARENT;
gck->id = id;
gck->hw.init = &init;
gck->regmap = regmap;
gck->lock = lock;
gck->range = *range;
gck->audio_pll_allowed = pll_audio;
gck->chg_pid = chg_pid;
gck->layout = layout;
gck->mux_table = mux_table;
clk_generated_startup(gck);
hw = &gck->hw;

Просмотреть файл

@ -175,7 +175,7 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap)
regmap_read(regmap, AT91_PMC_SR, &status);
return status & AT91_PMC_MOSCRCS;
return !!(status & AT91_PMC_MOSCRCS);
}
static int clk_main_rc_osc_prepare(struct clk_hw *hw)
@ -336,7 +336,7 @@ static int clk_rm9200_main_is_prepared(struct clk_hw *hw)
regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status);
return status & AT91_PMC_MAINRDY ? 1 : 0;
return !!(status & AT91_PMC_MAINRDY);
}
static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw,
@ -398,7 +398,7 @@ static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
regmap_read(regmap, AT91_PMC_SR, &status);
return status & AT91_PMC_MOSCSELS ? 1 : 0;
return !!(status & AT91_PMC_MOSCSELS);
}
static int clk_sam9x5_main_prepare(struct clk_hw *hw)

Просмотреть файл

@ -17,30 +17,49 @@
#define MASTER_DIV_SHIFT 8
#define MASTER_DIV_MASK 0x3
#define PMC_MCR 0x30
#define PMC_MCR_ID_MSK GENMASK(3, 0)
#define PMC_MCR_CMD BIT(7)
#define PMC_MCR_DIV GENMASK(10, 8)
#define PMC_MCR_CSS GENMASK(20, 16)
#define PMC_MCR_CSS_SHIFT (16)
#define PMC_MCR_EN BIT(28)
#define PMC_MCR_ID(x) ((x) & PMC_MCR_ID_MSK)
#define MASTER_MAX_ID 4
#define to_clk_master(hw) container_of(hw, struct clk_master, hw)
struct clk_master {
struct clk_hw hw;
struct regmap *regmap;
spinlock_t *lock;
const struct clk_master_layout *layout;
const struct clk_master_characteristics *characteristics;
u32 *mux_table;
u32 mckr;
int chg_pid;
u8 id;
u8 parent;
u8 div;
};
static inline bool clk_master_ready(struct regmap *regmap)
static inline bool clk_master_ready(struct clk_master *master)
{
unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
unsigned int status;
regmap_read(regmap, AT91_PMC_SR, &status);
regmap_read(master->regmap, AT91_PMC_SR, &status);
return status & AT91_PMC_MCKRDY ? 1 : 0;
return !!(status & bit);
}
static int clk_master_prepare(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);
while (!clk_master_ready(master->regmap))
while (!clk_master_ready(master))
cpu_relax();
return 0;
@ -50,7 +69,7 @@ static int clk_master_is_prepared(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);
return clk_master_ready(master->regmap);
return clk_master_ready(master);
}
static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
@ -143,6 +162,287 @@ at91_clk_register_master(struct regmap *regmap,
return hw;
}
static unsigned long
clk_sama7g5_master_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_master *master = to_clk_master(hw);
return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div));
}
static void clk_sama7g5_master_best_diff(struct clk_rate_request *req,
struct clk_hw *parent,
unsigned long parent_rate,
long *best_rate,
long *best_diff,
u32 div)
{
unsigned long tmp_rate, tmp_diff;
if (div == MASTER_PRES_MAX)
tmp_rate = parent_rate / 3;
else
tmp_rate = parent_rate >> div;
tmp_diff = abs(req->rate - tmp_rate);
if (*best_diff < 0 || *best_diff >= tmp_diff) {
*best_rate = tmp_rate;
*best_diff = tmp_diff;
req->best_parent_rate = parent_rate;
req->best_parent_hw = parent;
}
}
static int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_master *master = to_clk_master(hw);
struct clk_rate_request req_parent = *req;
struct clk_hw *parent;
long best_rate = LONG_MIN, best_diff = LONG_MIN;
unsigned long parent_rate;
unsigned int div, i;
/* First: check the dividers of MCR. */
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
parent_rate = clk_hw_get_rate(parent);
if (!parent_rate)
continue;
for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
clk_sama7g5_master_best_diff(req, parent, parent_rate,
&best_rate, &best_diff,
div);
if (!best_diff)
break;
}
if (!best_diff)
break;
}
/* Second: try to request rate form changeable parent. */
if (master->chg_pid < 0)
goto end;
parent = clk_hw_get_parent_by_index(hw, master->chg_pid);
if (!parent)
goto end;
for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
if (div == MASTER_PRES_MAX)
req_parent.rate = req->rate * 3;
else
req_parent.rate = req->rate << div;
if (__clk_determine_rate(parent, &req_parent))
continue;
clk_sama7g5_master_best_diff(req, parent, req_parent.rate,
&best_rate, &best_diff, div);
if (!best_diff)
break;
}
end:
pr_debug("MCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
__func__, best_rate,
__clk_get_name((req->best_parent_hw)->clk),
req->best_parent_rate);
if (best_rate < 0)
return -EINVAL;
req->rate = best_rate;
return 0;
}
static u8 clk_sama7g5_master_get_parent(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);
unsigned long flags;
u8 index;
spin_lock_irqsave(master->lock, flags);
index = clk_mux_val_to_index(&master->hw, master->mux_table, 0,
master->parent);
spin_unlock_irqrestore(master->lock, flags);
return index;
}
static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_master *master = to_clk_master(hw);
unsigned long flags;
if (index >= clk_hw_get_num_parents(hw))
return -EINVAL;
spin_lock_irqsave(master->lock, flags);
master->parent = clk_mux_index_to_val(master->mux_table, 0, index);
spin_unlock_irqrestore(master->lock, flags);
return 0;
}
static int clk_sama7g5_master_enable(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);
unsigned long flags;
unsigned int val, cparent;
spin_lock_irqsave(master->lock, flags);
regmap_write(master->regmap, PMC_MCR, PMC_MCR_ID(master->id));
regmap_read(master->regmap, PMC_MCR, &val);
regmap_update_bits(master->regmap, PMC_MCR,
PMC_MCR_EN | PMC_MCR_CSS | PMC_MCR_DIV |
PMC_MCR_CMD | PMC_MCR_ID_MSK,
PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) |
(master->div << MASTER_DIV_SHIFT) |
PMC_MCR_CMD | PMC_MCR_ID(master->id));
cparent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
/* Wait here only if parent is being changed. */
while ((cparent != master->parent) && !clk_master_ready(master))
cpu_relax();
spin_unlock_irqrestore(master->lock, flags);
return 0;
}
static void clk_sama7g5_master_disable(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);
unsigned long flags;
spin_lock_irqsave(master->lock, flags);
regmap_write(master->regmap, PMC_MCR, master->id);
regmap_update_bits(master->regmap, PMC_MCR,
PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
PMC_MCR_CMD | PMC_MCR_ID(master->id));
spin_unlock_irqrestore(master->lock, flags);
}
static int clk_sama7g5_master_is_enabled(struct clk_hw *hw)
{
struct clk_master *master = to_clk_master(hw);
unsigned long flags;
unsigned int val;
spin_lock_irqsave(master->lock, flags);
regmap_write(master->regmap, PMC_MCR, master->id);
regmap_read(master->regmap, PMC_MCR, &val);
spin_unlock_irqrestore(master->lock, flags);
return !!(val & PMC_MCR_EN);
}
static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_master *master = to_clk_master(hw);
unsigned long div, flags;
div = DIV_ROUND_CLOSEST(parent_rate, rate);
if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1)))
return -EINVAL;
if (div == 3)
div = MASTER_PRES_MAX;
else
div = ffs(div) - 1;
spin_lock_irqsave(master->lock, flags);
master->div = div;
spin_unlock_irqrestore(master->lock, flags);
return 0;
}
static const struct clk_ops sama7g5_master_ops = {
.enable = clk_sama7g5_master_enable,
.disable = clk_sama7g5_master_disable,
.is_enabled = clk_sama7g5_master_is_enabled,
.recalc_rate = clk_sama7g5_master_recalc_rate,
.determine_rate = clk_sama7g5_master_determine_rate,
.set_rate = clk_sama7g5_master_set_rate,
.get_parent = clk_sama7g5_master_get_parent,
.set_parent = clk_sama7g5_master_set_parent,
};
struct clk_hw * __init
at91_clk_sama7g5_register_master(struct regmap *regmap,
const char *name, int num_parents,
const char **parent_names,
u32 *mux_table,
spinlock_t *lock, u8 id,
bool critical, int chg_pid)
{
struct clk_master *master;
struct clk_hw *hw;
struct clk_init_data init;
unsigned long flags;
unsigned int val;
int ret;
if (!name || !num_parents || !parent_names || !mux_table ||
!lock || id > MASTER_MAX_ID)
return ERR_PTR(-EINVAL);
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &sama7g5_master_ops;
init.parent_names = parent_names;
init.num_parents = num_parents;
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
if (chg_pid >= 0)
init.flags |= CLK_SET_RATE_PARENT;
if (critical)
init.flags |= CLK_IS_CRITICAL;
master->hw.init = &init;
master->regmap = regmap;
master->id = id;
master->chg_pid = chg_pid;
master->lock = lock;
master->mux_table = mux_table;
spin_lock_irqsave(master->lock, flags);
regmap_write(master->regmap, PMC_MCR, master->id);
regmap_read(master->regmap, PMC_MCR, &val);
master->parent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
master->div = (val & PMC_MCR_DIV) >> MASTER_DIV_SHIFT;
spin_unlock_irqrestore(master->lock, flags);
hw = &master->hw;
ret = clk_hw_register(NULL, &master->hw);
if (ret) {
kfree(master);
hw = ERR_PTR(ret);
}
return hw;
}
const struct clk_master_layout at91rm9200_master_layout = {
.mask = 0x31F,
.pres_shift = 2,

Просмотреть файл

@ -38,6 +38,7 @@ struct clk_sam9x5_peripheral {
u32 div;
const struct clk_pcr_layout *layout;
bool auto_div;
int chg_pid;
};
#define to_clk_sam9x5_peripheral(hw) \
@ -208,7 +209,7 @@ static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
regmap_read(periph->regmap, periph->layout->offset, &status);
spin_unlock_irqrestore(periph->lock, flags);
return status & AT91_PMC_PCR_EN ? 1 : 0;
return !!(status & AT91_PMC_PCR_EN);
}
static unsigned long
@ -238,6 +239,87 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
return parent_rate >> periph->div;
}
static void clk_sam9x5_peripheral_best_diff(struct clk_rate_request *req,
struct clk_hw *parent,
unsigned long parent_rate,
u32 shift, long *best_diff,
long *best_rate)
{
unsigned long tmp_rate = parent_rate >> shift;
unsigned long tmp_diff = abs(req->rate - tmp_rate);
if (*best_diff < 0 || *best_diff >= tmp_diff) {
*best_rate = tmp_rate;
*best_diff = tmp_diff;
req->best_parent_rate = parent_rate;
req->best_parent_hw = parent;
}
}
static int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
struct clk_hw *parent = clk_hw_get_parent(hw);
struct clk_rate_request req_parent = *req;
unsigned long parent_rate = clk_hw_get_rate(parent);
unsigned long tmp_rate;
long best_rate = LONG_MIN;
long best_diff = LONG_MIN;
u32 shift;
if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
return parent_rate;
/* Fist step: check the available dividers. */
for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
tmp_rate = parent_rate >> shift;
if (periph->range.max && tmp_rate > periph->range.max)
continue;
clk_sam9x5_peripheral_best_diff(req, parent, parent_rate,
shift, &best_diff, &best_rate);
if (!best_diff || best_rate <= req->rate)
break;
}
if (periph->chg_pid < 0)
goto end;
/* Step two: try to request rate from parent. */
parent = clk_hw_get_parent_by_index(hw, periph->chg_pid);
if (!parent)
goto end;
for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
req_parent.rate = req->rate << shift;
if (__clk_determine_rate(parent, &req_parent))
continue;
clk_sam9x5_peripheral_best_diff(req, parent, req_parent.rate,
shift, &best_diff, &best_rate);
if (!best_diff)
break;
}
end:
if (best_rate < 0 ||
(periph->range.max && best_rate > periph->range.max))
return -EINVAL;
pr_debug("PCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
__func__, best_rate,
__clk_get_name((req->best_parent_hw)->clk),
req->best_parent_rate);
req->rate = best_rate;
return 0;
}
static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *parent_rate)
@ -320,11 +402,21 @@ static const struct clk_ops sam9x5_peripheral_ops = {
.set_rate = clk_sam9x5_peripheral_set_rate,
};
static const struct clk_ops sam9x5_peripheral_chg_ops = {
.enable = clk_sam9x5_peripheral_enable,
.disable = clk_sam9x5_peripheral_disable,
.is_enabled = clk_sam9x5_peripheral_is_enabled,
.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
.determine_rate = clk_sam9x5_peripheral_determine_rate,
.set_rate = clk_sam9x5_peripheral_set_rate,
};
struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
u32 id, const struct clk_range *range)
u32 id, const struct clk_range *range,
int chg_pid)
{
struct clk_sam9x5_peripheral *periph;
struct clk_init_data init;
@ -339,10 +431,16 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &sam9x5_peripheral_ops;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
init.flags = 0;
init.parent_names = &parent_name;
init.num_parents = 1;
if (chg_pid < 0) {
init.flags = 0;
init.ops = &sam9x5_peripheral_ops;
} else {
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
CLK_SET_RATE_PARENT;
init.ops = &sam9x5_peripheral_chg_ops;
}
periph->id = id;
periph->hw.init = &init;
@ -353,6 +451,7 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
periph->auto_div = true;
periph->layout = layout;
periph->range = *range;
periph->chg_pid = chg_pid;
hw = &periph->hw;
ret = clk_hw_register(NULL, &periph->hw);

Просмотреть файл

@ -21,6 +21,7 @@
struct clk_programmable {
struct clk_hw hw;
struct regmap *regmap;
u32 *mux_table;
u8 id;
const struct clk_programmable_layout *layout;
};
@ -108,6 +109,9 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
if (layout->have_slck_mck)
mask |= AT91_PMC_CSSMCK_MCK;
if (prog->mux_table)
pckr = clk_mux_index_to_val(prog->mux_table, 0, index);
if (index > layout->css_mask) {
if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
return -EINVAL;
@ -134,6 +138,9 @@ static u8 clk_programmable_get_parent(struct clk_hw *hw)
if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret)
ret = PROG_MAX_RM9200_CSS + 1;
if (prog->mux_table)
ret = clk_mux_val_to_index(&prog->hw, prog->mux_table, 0, ret);
return ret;
}
@ -182,7 +189,8 @@ struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap,
const char *name, const char **parent_names,
u8 num_parents, u8 id,
const struct clk_programmable_layout *layout)
const struct clk_programmable_layout *layout,
u32 *mux_table)
{
struct clk_programmable *prog;
struct clk_hw *hw;
@ -206,6 +214,7 @@ at91_clk_register_programmable(struct regmap *regmap,
prog->layout = layout;
prog->hw.init = &init;
prog->regmap = regmap;
prog->mux_table = mux_table;
hw = &prog->hw;
ret = clk_hw_register(NULL, &prog->hw);

Просмотреть файл

@ -15,26 +15,41 @@
#include "pmc.h"
#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
#define PMC_PLL_CTRL1_MUL_MSK GENMASK(30, 24)
#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
#define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
#define UPLL_DIV 2
#define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
#define PLL_MAX_ID 1
#define FCORE_MIN (600000000)
#define FCORE_MAX (1200000000)
struct sam9x60_pll {
struct clk_hw hw;
#define PLL_MAX_ID 7
struct sam9x60_pll_core {
struct regmap *regmap;
spinlock_t *lock;
const struct clk_pll_characteristics *characteristics;
u32 frac;
const struct clk_pll_layout *layout;
struct clk_hw hw;
u8 id;
u8 div;
};
struct sam9x60_frac {
struct sam9x60_pll_core core;
u32 frac;
u16 mul;
};
#define to_sam9x60_pll(hw) container_of(hw, struct sam9x60_pll, hw)
struct sam9x60_div {
struct sam9x60_pll_core core;
u8 div;
};
#define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw)
#define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core)
#define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core)
static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
{
@ -45,41 +60,53 @@ static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
return !!(status & BIT(id));
}
static int sam9x60_pll_prepare(struct clk_hw *hw)
static bool sam9x60_frac_pll_ready(struct regmap *regmap, u8 id)
{
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
struct regmap *regmap = pll->regmap;
return sam9x60_pll_ready(regmap, id);
}
static unsigned long sam9x60_frac_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_frac *frac = to_sam9x60_frac(core);
return (parent_rate * (frac->mul + 1) +
((u64)parent_rate * frac->frac >> 22));
}
static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_frac *frac = to_sam9x60_frac(core);
struct regmap *regmap = core->regmap;
unsigned int val, cfrac, cmul;
unsigned long flags;
u8 div;
u16 mul;
u32 val;
spin_lock_irqsave(pll->lock, flags);
regmap_write(regmap, AT91_PMC_PLL_UPDT, pll->id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
spin_lock_irqsave(core->lock, flags);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;
if (sam9x60_pll_ready(regmap, pll->id) &&
(div == pll->div && mul == pll->mul)) {
spin_unlock_irqrestore(pll->lock, flags);
return 0;
}
if (sam9x60_frac_pll_ready(regmap, core->id) &&
(cmul == frac->mul && cfrac == frac->frac))
goto unlock;
/* Recommended value for AT91_PMC_PLL_ACR */
if (pll->characteristics->upll)
/* Recommended value for PMC_PLL_ACR */
if (core->characteristics->upll)
val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
else
val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
regmap_write(regmap, AT91_PMC_PLL_ACR, val);
regmap_write(regmap, AT91_PMC_PLL_CTRL1,
FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul));
(frac->mul << core->layout->mul_shift) |
(frac->frac << core->layout->frac_shift));
if (pll->characteristics->upll) {
if (core->characteristics->upll) {
/* Enable the UTMI internal bandgap */
val |= AT91_PMC_PLL_ACR_UTMIBG;
regmap_write(regmap, AT91_PMC_PLL_ACR, val);
@ -94,221 +121,409 @@ static int sam9x60_pll_prepare(struct clk_hw *hw)
}
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | core->id);
regmap_write(regmap, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL |
AT91_PMC_PLL_CTRL0_ENPLLCK | pll->div);
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | core->id);
while (!sam9x60_pll_ready(regmap, pll->id))
while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
spin_unlock_irqrestore(pll->lock, flags);
unlock:
spin_unlock_irqrestore(core->lock, flags);
return 0;
}
static int sam9x60_pll_is_prepared(struct clk_hw *hw)
static void sam9x60_frac_pll_unprepare(struct clk_hw *hw)
{
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
return sam9x60_pll_ready(pll->regmap, pll->id);
}
static void sam9x60_pll_unprepare(struct clk_hw *hw)
{
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct regmap *regmap = core->regmap;
unsigned long flags;
spin_lock_irqsave(pll->lock, flags);
spin_lock_irqsave(core->lock, flags);
regmap_write(pll->regmap, AT91_PMC_PLL_UPDT, pll->id);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_update_bits(pll->regmap, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENPLLCK, 0);
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0, AT91_PMC_PLL_CTRL0_ENPLL, 0);
regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
if (core->characteristics->upll)
regmap_update_bits(regmap, AT91_PMC_PLL_ACR,
AT91_PMC_PLL_ACR_UTMIBG | AT91_PMC_PLL_ACR_UTMIVR, 0);
regmap_update_bits(pll->regmap, AT91_PMC_PLL_CTRL0,
AT91_PMC_PLL_CTRL0_ENPLL, 0);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | core->id);
if (pll->characteristics->upll)
regmap_update_bits(pll->regmap, AT91_PMC_PLL_ACR,
AT91_PMC_PLL_ACR_UTMIBG |
AT91_PMC_PLL_ACR_UTMIVR, 0);
regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE);
spin_unlock_irqrestore(pll->lock, flags);
spin_unlock_irqrestore(core->lock, flags);
}
static unsigned long sam9x60_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
static int sam9x60_frac_pll_is_prepared(struct clk_hw *hw)
{
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
return (parent_rate * (pll->mul + 1)) / (pll->div + 1);
return sam9x60_pll_ready(core->regmap, core->id);
}
static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
unsigned long rate,
unsigned long parent_rate,
bool update)
static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core,
unsigned long rate,
unsigned long parent_rate,
bool update)
{
struct sam9x60_frac *frac = to_sam9x60_frac(core);
unsigned long tmprate, remainder;
unsigned long nmul = 0;
unsigned long nfrac = 0;
if (rate < FCORE_MIN || rate > FCORE_MAX)
return -ERANGE;
/*
* Calculate the multiplier associated with the current
* divider that provide the closest rate to the requested one.
*/
nmul = mult_frac(rate, 1, parent_rate);
tmprate = mult_frac(parent_rate, nmul, 1);
remainder = rate - tmprate;
if (remainder) {
nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
parent_rate);
tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
(1 << 22));
}
/* Check if resulted rate is a valid. */
if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
return -ERANGE;
if (update) {
frac->mul = nmul - 1;
frac->frac = nfrac;
}
return tmprate;
}
static long sam9x60_frac_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
return sam9x60_frac_pll_compute_mul_frac(core, rate, *parent_rate, false);
}
static int sam9x60_frac_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
return sam9x60_frac_pll_compute_mul_frac(core, rate, parent_rate, true);
}
static const struct clk_ops sam9x60_frac_pll_ops = {
.prepare = sam9x60_frac_pll_prepare,
.unprepare = sam9x60_frac_pll_unprepare,
.is_prepared = sam9x60_frac_pll_is_prepared,
.recalc_rate = sam9x60_frac_pll_recalc_rate,
.round_rate = sam9x60_frac_pll_round_rate,
.set_rate = sam9x60_frac_pll_set_rate,
};
static int sam9x60_div_pll_prepare(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_div *div = to_sam9x60_div(core);
struct regmap *regmap = core->regmap;
unsigned long flags;
unsigned int val, cdiv;
spin_lock_irqsave(core->lock, flags);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
cdiv = (val & core->layout->div_mask) >> core->layout->div_shift;
/* Stop if enabled an nothing changed. */
if (!!(val & core->layout->endiv_mask) && cdiv == div->div)
goto unlock;
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
core->layout->div_mask | core->layout->endiv_mask,
(div->div << core->layout->div_shift) |
(1 << core->layout->endiv_shift));
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | core->id);
while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
unlock:
spin_unlock_irqrestore(core->lock, flags);
return 0;
}
static void sam9x60_div_pll_unprepare(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct regmap *regmap = core->regmap;
unsigned long flags;
spin_lock_irqsave(core->lock, flags);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
core->layout->endiv_mask, 0);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | core->id);
spin_unlock_irqrestore(core->lock, flags);
}
static int sam9x60_div_pll_is_prepared(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct regmap *regmap = core->regmap;
unsigned long flags;
unsigned int val;
spin_lock_irqsave(core->lock, flags);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_ID_MSK, core->id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
spin_unlock_irqrestore(core->lock, flags);
return !!(val & core->layout->endiv_mask);
}
static unsigned long sam9x60_div_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_div *div = to_sam9x60_div(core);
return DIV_ROUND_CLOSEST_ULL(parent_rate, (div->div + 1));
}
static long sam9x60_div_pll_compute_div(struct sam9x60_pll_core *core,
unsigned long *parent_rate,
unsigned long rate)
{
const struct clk_pll_characteristics *characteristics =
pll->characteristics;
unsigned long bestremainder = ULONG_MAX;
unsigned long maxdiv, mindiv, tmpdiv;
long bestrate = -ERANGE;
unsigned long bestdiv = 0;
unsigned long bestmul = 0;
unsigned long bestfrac = 0;
core->characteristics;
struct clk_hw *parent = clk_hw_get_parent(&core->hw);
unsigned long tmp_rate, tmp_parent_rate, tmp_diff;
long best_diff = -1, best_rate = -EINVAL;
u32 divid, best_div;
if (!rate)
return 0;
if (rate < characteristics->output[0].min ||
rate > characteristics->output[0].max)
return -ERANGE;
if (!pll->characteristics->upll) {
mindiv = parent_rate / rate;
if (mindiv < 2)
mindiv = 2;
for (divid = 1; divid < core->layout->div_mask; divid++) {
tmp_parent_rate = clk_hw_round_rate(parent, rate * divid);
if (!tmp_parent_rate)
continue;
maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX, rate);
if (maxdiv > PLL_DIV_MAX)
maxdiv = PLL_DIV_MAX;
} else {
mindiv = maxdiv = UPLL_DIV;
}
tmp_rate = DIV_ROUND_CLOSEST_ULL(tmp_parent_rate, divid);
tmp_diff = abs(rate - tmp_rate);
for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
unsigned long remainder;
unsigned long tmprate;
unsigned long tmpmul;
unsigned long tmpfrac = 0;
/*
* Calculate the multiplier associated with the current
* divider that provide the closest rate to the requested one.
*/
tmpmul = mult_frac(rate, tmpdiv, parent_rate);
tmprate = mult_frac(parent_rate, tmpmul, tmpdiv);
remainder = rate - tmprate;
if (remainder) {
tmpfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * tmpdiv * (1 << 22),
parent_rate);
tmprate += DIV_ROUND_CLOSEST_ULL((u64)tmpfrac * parent_rate,
tmpdiv * (1 << 22));
if (tmprate > rate)
remainder = tmprate - rate;
else
remainder = rate - tmprate;
if (best_diff < 0 || best_diff > tmp_diff) {
*parent_rate = tmp_parent_rate;
best_rate = tmp_rate;
best_diff = tmp_diff;
best_div = divid;
}
/*
* Compare the remainder with the best remainder found until
* now and elect a new best multiplier/divider pair if the
* current remainder is smaller than the best one.
*/
if (remainder < bestremainder) {
bestremainder = remainder;
bestdiv = tmpdiv;
bestmul = tmpmul;
bestrate = tmprate;
bestfrac = tmpfrac;
}
/* We've found a perfect match! */
if (!remainder)
if (!best_diff)
break;
}
/* Check if bestrate is a valid output rate */
if (bestrate < characteristics->output[0].min &&
bestrate > characteristics->output[0].max)
if (best_rate < characteristics->output[0].min ||
best_rate > characteristics->output[0].max)
return -ERANGE;
if (update) {
pll->div = bestdiv - 1;
pll->mul = bestmul - 1;
pll->frac = bestfrac;
}
return bestrate;
return best_rate;
}
static long sam9x60_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
static long sam9x60_div_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
return sam9x60_pll_get_best_div_mul(pll, rate, *parent_rate, false);
return sam9x60_div_pll_compute_div(core, parent_rate, rate);
}
static int sam9x60_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
static int sam9x60_div_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_div *div = to_sam9x60_div(core);
return sam9x60_pll_get_best_div_mul(pll, rate, parent_rate, true);
div->div = DIV_ROUND_CLOSEST(parent_rate, rate) - 1;
return 0;
}
static const struct clk_ops pll_ops = {
.prepare = sam9x60_pll_prepare,
.unprepare = sam9x60_pll_unprepare,
.is_prepared = sam9x60_pll_is_prepared,
.recalc_rate = sam9x60_pll_recalc_rate,
.round_rate = sam9x60_pll_round_rate,
.set_rate = sam9x60_pll_set_rate,
static const struct clk_ops sam9x60_div_pll_ops = {
.prepare = sam9x60_div_pll_prepare,
.unprepare = sam9x60_div_pll_unprepare,
.is_prepared = sam9x60_div_pll_is_prepared,
.recalc_rate = sam9x60_div_pll_recalc_rate,
.round_rate = sam9x60_div_pll_round_rate,
.set_rate = sam9x60_div_pll_set_rate,
};
struct clk_hw * __init
sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics)
sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name,
struct clk_hw *parent_hw, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, bool critical)
{
struct sam9x60_pll *pll;
struct sam9x60_frac *frac;
struct clk_hw *hw;
struct clk_init_data init;
unsigned int pllr;
unsigned long parent_rate, flags;
unsigned int val;
int ret;
if (id > PLL_MAX_ID)
if (id > PLL_MAX_ID || !lock || !parent_hw)
return ERR_PTR(-EINVAL);
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
frac = kzalloc(sizeof(*frac), GFP_KERNEL);
if (!frac)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &pll_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.ops = &sam9x60_frac_pll_ops;
init.flags = CLK_SET_RATE_GATE;
if (critical)
init.flags |= CLK_IS_CRITICAL;
pll->id = id;
pll->hw.init = &init;
pll->characteristics = characteristics;
pll->regmap = regmap;
pll->lock = lock;
frac->core.id = id;
frac->core.hw.init = &init;
frac->core.characteristics = characteristics;
frac->core.layout = layout;
frac->core.regmap = regmap;
frac->core.lock = lock;
regmap_write(regmap, AT91_PMC_PLL_UPDT, id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &pllr);
pll->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, pllr);
regmap_read(regmap, AT91_PMC_PLL_CTRL1, &pllr);
pll->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, pllr);
spin_lock_irqsave(frac->core.lock, flags);
if (sam9x60_pll_ready(regmap, id)) {
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_ID_MSK, id);
regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val);
frac->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
frac->frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val);
} else {
/*
* This means the PLL is not setup by bootloaders. In this
* case we need to set the minimum rate for it. Otherwise
* a clock child of this PLL may be enabled before setting
* its rate leading to enabling this PLL with unsupported
* rate. This will lead to PLL not being locked at all.
*/
parent_rate = clk_hw_get_rate(parent_hw);
if (!parent_rate) {
hw = ERR_PTR(-EINVAL);
goto free;
}
hw = &pll->hw;
ret = sam9x60_frac_pll_compute_mul_frac(&frac->core, FCORE_MIN,
parent_rate, true);
if (ret <= 0) {
hw = ERR_PTR(ret);
goto free;
}
}
spin_unlock_irqrestore(frac->core.lock, flags);
hw = &frac->core.hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(pll);
kfree(frac);
hw = ERR_PTR(ret);
}
return hw;
free:
spin_unlock_irqrestore(frac->core.lock, flags);
kfree(frac);
return hw;
}
struct clk_hw * __init
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, bool critical)
{
struct sam9x60_div *div;
struct clk_hw *hw;
struct clk_init_data init;
unsigned long flags;
unsigned int val;
int ret;
if (id > PLL_MAX_ID || !lock)
return ERR_PTR(-EINVAL);
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
return ERR_PTR(-ENOMEM);
init.name = name;
init.parent_names = &parent_name;
init.num_parents = 1;
init.ops = &sam9x60_div_pll_ops;
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
CLK_SET_RATE_PARENT;
if (critical)
init.flags |= CLK_IS_CRITICAL;
div->core.id = id;
div->core.hw.init = &init;
div->core.characteristics = characteristics;
div->core.layout = layout;
div->core.regmap = regmap;
div->core.lock = lock;
spin_lock_irqsave(div->core.lock, flags);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_ID_MSK, id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
div->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
spin_unlock_irqrestore(div->core.lock, flags);
hw = &div->core.hw;
ret = clk_hw_register(NULL, hw);
if (ret) {
kfree(div);
hw = ERR_PTR(ret);
}

Просмотреть файл

@ -34,7 +34,7 @@ static inline bool clk_system_ready(struct regmap *regmap, int id)
regmap_read(regmap, AT91_PMC_SR, &status);
return status & (1 << id) ? 1 : 0;
return !!(status & (1 << id));
}
static int clk_system_prepare(struct clk_hw *hw)
@ -74,7 +74,7 @@ static int clk_system_is_prepared(struct clk_hw *hw)
regmap_read(sys->regmap, AT91_PMC_SR, &status);
return status & (1 << sys->id) ? 1 : 0;
return !!(status & (1 << sys->id));
}
static const struct clk_ops system_ops = {

Просмотреть файл

@ -120,9 +120,11 @@ static const struct clk_ops utmi_ops = {
.recalc_rate = clk_utmi_recalc_rate,
};
struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
const char *name, const char *parent_name)
static struct clk_hw * __init
at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
struct regmap *regmap_sfr,
const char *name, const char *parent_name,
const struct clk_ops *ops, unsigned long flags)
{
struct clk_utmi *utmi;
struct clk_hw *hw;
@ -134,10 +136,10 @@ at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &utmi_ops;
init.ops = ops;
init.parent_names = parent_name ? &parent_name : NULL;
init.num_parents = parent_name ? 1 : 0;
init.flags = CLK_SET_RATE_GATE;
init.flags = flags;
utmi->hw.init = &init;
utmi->regmap_pmc = regmap_pmc;
@ -152,3 +154,94 @@ at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
return hw;
}
struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
const char *name, const char *parent_name)
{
return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name,
parent_name, &utmi_ops, CLK_SET_RATE_GATE);
}
static int clk_utmi_sama7g5_prepare(struct clk_hw *hw)
{
struct clk_utmi *utmi = to_clk_utmi(hw);
struct clk_hw *hw_parent;
unsigned long parent_rate;
unsigned int val;
hw_parent = clk_hw_get_parent(hw);
parent_rate = clk_hw_get_rate(hw_parent);
switch (parent_rate) {
case 16000000:
val = 0;
break;
case 20000000:
val = 2;
break;
case 24000000:
val = 3;
break;
case 32000000:
val = 5;
break;
default:
pr_err("UTMICK: unsupported main_xtal rate\n");
return -EINVAL;
}
regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val);
return 0;
}
static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
{
struct clk_utmi *utmi = to_clk_utmi(hw);
struct clk_hw *hw_parent;
unsigned long parent_rate;
unsigned int val;
hw_parent = clk_hw_get_parent(hw);
parent_rate = clk_hw_get_rate(hw_parent);
regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val);
switch (val & 0x7) {
case 0:
if (parent_rate == 16000000)
return 1;
break;
case 2:
if (parent_rate == 20000000)
return 1;
break;
case 3:
if (parent_rate == 24000000)
return 1;
break;
case 5:
if (parent_rate == 32000000)
return 1;
break;
default:
break;
}
return 0;
}
static const struct clk_ops sama7g5_utmi_ops = {
.prepare = clk_utmi_sama7g5_prepare,
.is_prepared = clk_utmi_sama7g5_is_prepared,
.recalc_rate = clk_utmi_recalc_rate,
};
struct clk_hw * __init
at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name,
const char *parent_name)
{
return at91_clk_register_utmi_internal(regmap_pmc, NULL, name,
parent_name, &sama7g5_utmi_ops, 0);
}

Просмотреть файл

@ -22,6 +22,8 @@
#define SYSTEM_MAX_ID 31
#define GCK_INDEX_DT_AUDIO_PLL 5
#ifdef CONFIG_HAVE_AT91_AUDIO_PLL
static void __init of_sama5d2_clk_audio_pll_frac_setup(struct device_node *np)
{
@ -135,7 +137,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
return;
for_each_child_of_node(np, gcknp) {
bool pll_audio = false;
int chg_pid = INT_MIN;
if (of_property_read_u32(gcknp, "reg", &id))
continue;
@ -152,12 +154,13 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
if (of_device_is_compatible(np, "atmel,sama5d2-clk-generated") &&
(id == GCK_ID_I2S0 || id == GCK_ID_I2S1 ||
id == GCK_ID_CLASSD))
pll_audio = true;
chg_pid = GCK_INDEX_DT_AUDIO_PLL;
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&dt_pcr_layout, name,
parent_names, num_parents,
id, pll_audio, &range);
parent_names, NULL,
num_parents, id, &range,
chg_pid);
if (IS_ERR(hw))
continue;
@ -460,7 +463,8 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
&dt_pcr_layout,
name,
parent_name,
id, &range);
id, &range,
INT_MIN);
}
if (IS_ERR(hw))
@ -673,7 +677,8 @@ CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv",
static void __init
of_at91_clk_prog_setup(struct device_node *np,
const struct clk_programmable_layout *layout)
const struct clk_programmable_layout *layout,
u32 *mux_table)
{
int num;
u32 id;
@ -707,7 +712,7 @@ of_at91_clk_prog_setup(struct device_node *np,
hw = at91_clk_register_programmable(regmap, name,
parent_names, num_parents,
id, layout);
id, layout, mux_table);
if (IS_ERR(hw))
continue;
@ -717,21 +722,21 @@ of_at91_clk_prog_setup(struct device_node *np,
static void __init of_at91rm9200_clk_prog_setup(struct device_node *np)
{
of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout);
of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout, NULL);
}
CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable",
of_at91rm9200_clk_prog_setup);
static void __init of_at91sam9g45_clk_prog_setup(struct device_node *np)
{
of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout);
of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout, NULL);
}
CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable",
of_at91sam9g45_clk_prog_setup);
static void __init of_at91sam9x5_clk_prog_setup(struct device_node *np)
{
of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout);
of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout, NULL);
}
CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable",
of_at91sam9x5_clk_prog_setup);

Просмотреть файл

@ -54,8 +54,14 @@ struct clk_master_characteristics {
struct clk_pll_layout {
u32 pllr_mask;
u16 mul_mask;
u32 mul_mask;
u32 frac_mask;
u32 div_mask;
u32 endiv_mask;
u8 mul_shift;
u8 frac_shift;
u8 div_shift;
u8 endiv_shift;
};
extern const struct clk_pll_layout at91rm9200_pll_layout;
@ -122,8 +128,8 @@ struct clk_hw * __init
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char **parent_names,
u8 num_parents, u8 id, bool pll_audio,
const struct clk_range *range);
u32 *mux_table, u8 num_parents, u8 id,
const struct clk_range *range, int chg_pid);
struct clk_hw * __init
at91_clk_register_h32mx(struct regmap *regmap, const char *name,
@ -154,6 +160,13 @@ at91_clk_register_master(struct regmap *regmap, const char *name,
const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics);
struct clk_hw * __init
at91_clk_sama7g5_register_master(struct regmap *regmap,
const char *name, int num_parents,
const char **parent_names, u32 *mux_table,
spinlock_t *lock, u8 id, bool critical,
int chg_pid);
struct clk_hw * __init
at91_clk_register_peripheral(struct regmap *regmap, const char *name,
const char *parent_name, u32 id);
@ -161,7 +174,8 @@ struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const struct clk_pcr_layout *layout,
const char *name, const char *parent_name,
u32 id, const struct clk_range *range);
u32 id, const struct clk_range *range,
int chg_pid);
struct clk_hw * __init
at91_clk_register_pll(struct regmap *regmap, const char *name,
@ -173,14 +187,23 @@ at91_clk_register_plldiv(struct regmap *regmap, const char *name,
const char *parent_name);
struct clk_hw * __init
sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics);
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, bool critical);
struct clk_hw * __init
sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name,
struct clk_hw *parent_hw, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, bool critical);
struct clk_hw * __init
at91_clk_register_programmable(struct regmap *regmap, const char *name,
const char **parent_names, u8 num_parents, u8 id,
const struct clk_programmable_layout *layout);
const struct clk_programmable_layout *layout,
u32 *mux_table);
struct clk_hw * __init
at91_clk_register_sam9260_slow(struct regmap *regmap,
@ -213,6 +236,10 @@ struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
const char *name, const char *parent_name);
struct clk_hw * __init
at91_clk_sama7g5_register_utmi(struct regmap *regmap, const char *name,
const char *parent_name);
#ifdef CONFIG_PM
void pmc_register_id(u8 id);
void pmc_register_pck(u8 pck);

Просмотреть файл

@ -22,7 +22,7 @@ static const struct clk_master_layout sam9x60_master_layout = {
};
static const struct clk_range plla_outputs[] = {
{ .min = 300000000, .max = 600000000 },
{ .min = 2343750, .max = 1200000000 },
};
static const struct clk_pll_characteristics plla_characteristics = {
@ -42,6 +42,20 @@ static const struct clk_pll_characteristics upll_characteristics = {
.upll = true,
};
static const struct clk_pll_layout pll_frac_layout = {
.mul_mask = GENMASK(31, 24),
.frac_mask = GENMASK(21, 0),
.mul_shift = 24,
.frac_shift = 0,
};
static const struct clk_pll_layout pll_div_layout = {
.div_mask = GENMASK(7, 0),
.endiv_mask = BIT(29),
.div_shift = 0,
.endiv_shift = 29,
};
static const struct clk_programmable_layout sam9x60_programmable_layout = {
.pres_mask = 0xff,
.pres_shift = 8,
@ -156,6 +170,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
const char *td_slck_name, *md_slck_name, *mainxtal_name;
struct pmc_data *sam9x60_pmc;
const char *parent_names[6];
struct clk_hw *main_osc_hw;
struct regmap *regmap;
struct clk_hw *hw;
int i;
@ -178,7 +193,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
return;
mainxtal_name = of_clk_get_parent_name(np, i);
regmap = syscon_node_to_regmap(np);
regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
@ -189,7 +204,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
if (!sam9x60_pmc)
return;
hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 24000000,
hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000,
50000000);
if (IS_ERR(hw))
goto err_free;
@ -200,6 +215,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
bypass);
if (IS_ERR(hw))
goto err_free;
main_osc_hw = hw;
parent_names[0] = "main_rc_osc";
parent_names[1] = "main_osc";
@ -209,15 +225,31 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
sam9x60_pmc->chws[PMC_MAIN] = hw;
hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "pllack",
"mainck", 0, &plla_characteristics);
hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "pllack_fracck",
"mainck", sam9x60_pmc->chws[PMC_MAIN],
0, &plla_characteristics,
&pll_frac_layout, true);
if (IS_ERR(hw))
goto err_free;
hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "pllack_divck",
"pllack_fracck", 0, &plla_characteristics,
&pll_div_layout, true);
if (IS_ERR(hw))
goto err_free;
sam9x60_pmc->chws[PMC_PLLACK] = hw;
hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "upllck",
"main_osc", 1, &upll_characteristics);
hw = sam9x60_clk_register_frac_pll(regmap, &pmc_pll_lock, "upllck_fracck",
"main_osc", main_osc_hw, 1,
&upll_characteristics,
&pll_frac_layout, false);
if (IS_ERR(hw))
goto err_free;
hw = sam9x60_clk_register_div_pll(regmap, &pmc_pll_lock, "upllck_divck",
"upllck_fracck", 1, &upll_characteristics,
&pll_div_layout, false);
if (IS_ERR(hw))
goto err_free;
@ -225,7 +257,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
parent_names[0] = md_slck_name;
parent_names[1] = "mainck";
parent_names[2] = "pllack";
parent_names[2] = "pllack_divck";
hw = at91_clk_register_master(regmap, "masterck", 3, parent_names,
&sam9x60_master_layout,
&mck_characteristics);
@ -234,8 +266,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
sam9x60_pmc->chws[PMC_MCK] = hw;
parent_names[0] = "pllack";
parent_names[1] = "upllck";
parent_names[0] = "pllack_divck";
parent_names[1] = "upllck_divck";
parent_names[2] = "main_osc";
hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3);
if (IS_ERR(hw))
@ -245,8 +277,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
parent_names[1] = td_slck_name;
parent_names[2] = "mainck";
parent_names[3] = "masterck";
parent_names[4] = "pllack";
parent_names[5] = "upllck";
parent_names[4] = "pllack_divck";
parent_names[5] = "upllck_divck";
for (i = 0; i < 8; i++) {
char name[6];
@ -254,7 +286,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i,
&sam9x60_programmable_layout);
&sam9x60_programmable_layout,
NULL);
if (IS_ERR(hw))
goto err_free;
@ -277,7 +310,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
sam9x60_periphck[i].n,
"masterck",
sam9x60_periphck[i].id,
&range);
&range, INT_MIN);
if (IS_ERR(hw))
goto err_free;
@ -288,10 +321,9 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sam9x60_pcr_layout,
sam9x60_gck[i].n,
parent_names, 6,
parent_names, NULL, 6,
sam9x60_gck[i].id,
false,
&sam9x60_gck[i].r);
&sam9x60_gck[i].r, INT_MIN);
if (IS_ERR(hw))
goto err_free;

Просмотреть файл

@ -116,21 +116,20 @@ static const struct {
char *n;
u8 id;
struct clk_range r;
bool pll;
int chg_pid;
} sama5d2_gck[] = {
{ .n = "sdmmc0_gclk", .id = 31, },
{ .n = "sdmmc1_gclk", .id = 32, },
{ .n = "tcb0_gclk", .id = 35, .r = { .min = 0, .max = 83000000 }, },
{ .n = "tcb1_gclk", .id = 36, .r = { .min = 0, .max = 83000000 }, },
{ .n = "pwm_gclk", .id = 38, .r = { .min = 0, .max = 83000000 }, },
{ .n = "isc_gclk", .id = 46, },
{ .n = "pdmic_gclk", .id = 48, },
{ .n = "i2s0_gclk", .id = 54, .pll = true },
{ .n = "i2s1_gclk", .id = 55, .pll = true },
{ .n = "can0_gclk", .id = 56, .r = { .min = 0, .max = 80000000 }, },
{ .n = "can1_gclk", .id = 57, .r = { .min = 0, .max = 80000000 }, },
{ .n = "classd_gclk", .id = 59, .r = { .min = 0, .max = 100000000 },
.pll = true },
{ .n = "sdmmc0_gclk", .id = 31, .chg_pid = INT_MIN, },
{ .n = "sdmmc1_gclk", .id = 32, .chg_pid = INT_MIN, },
{ .n = "tcb0_gclk", .id = 35, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
{ .n = "tcb1_gclk", .id = 36, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
{ .n = "pwm_gclk", .id = 38, .chg_pid = INT_MIN, .r = { .min = 0, .max = 83000000 }, },
{ .n = "isc_gclk", .id = 46, .chg_pid = INT_MIN, },
{ .n = "pdmic_gclk", .id = 48, .chg_pid = INT_MIN, },
{ .n = "i2s0_gclk", .id = 54, .chg_pid = 5, },
{ .n = "i2s1_gclk", .id = 55, .chg_pid = 5, },
{ .n = "can0_gclk", .id = 56, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, },
{ .n = "can1_gclk", .id = 57, .chg_pid = INT_MIN, .r = { .min = 0, .max = 80000000 }, },
{ .n = "classd_gclk", .id = 59, .chg_pid = 5, .r = { .min = 0, .max = 100000000 }, },
};
static const struct clk_programmable_layout sama5d2_programmable_layout = {
@ -269,7 +268,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name,
parent_names, 6, i,
&sama5d2_programmable_layout);
&sama5d2_programmable_layout,
NULL);
if (IS_ERR(hw))
goto err_free;
@ -292,7 +292,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
sama5d2_periphck[i].n,
"masterck",
sama5d2_periphck[i].id,
&range);
&range, INT_MIN);
if (IS_ERR(hw))
goto err_free;
@ -305,7 +305,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
sama5d2_periph32ck[i].n,
"h32mxck",
sama5d2_periph32ck[i].id,
&sama5d2_periph32ck[i].r);
&sama5d2_periph32ck[i].r,
INT_MIN);
if (IS_ERR(hw))
goto err_free;
@ -322,10 +323,10 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
&sama5d2_pcr_layout,
sama5d2_gck[i].n,
parent_names, 6,
parent_names, NULL, 6,
sama5d2_gck[i].id,
sama5d2_gck[i].pll,
&sama5d2_gck[i].r);
&sama5d2_gck[i].r,
sama5d2_gck[i].chg_pid);
if (IS_ERR(hw))
goto err_free;

Просмотреть файл

@ -121,7 +121,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
return;
mainxtal_name = of_clk_get_parent_name(np, i);
regmap = syscon_node_to_regmap(np);
regmap = device_node_to_regmap(np);
if (IS_ERR(regmap))
return;
@ -200,7 +200,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
&at91sam9x5_programmable_layout);
&at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw))
goto err_free;
@ -223,7 +224,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
sama5d3_periphck[i].n,
"masterck",
sama5d3_periphck[i].id,
&sama5d3_periphck[i].r);
&sama5d3_periphck[i].r,
INT_MIN);
if (IS_ERR(hw))
goto err_free;

Просмотреть файл

@ -223,7 +223,8 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
hw = at91_clk_register_programmable(regmap, name,
parent_names, 5, i,
&at91sam9x5_programmable_layout);
&at91sam9x5_programmable_layout,
NULL);
if (IS_ERR(hw))
goto err_free;
@ -246,7 +247,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
sama5d4_periphck[i].n,
"masterck",
sama5d4_periphck[i].id,
&range);
&range, INT_MIN);
if (IS_ERR(hw))
goto err_free;
@ -259,7 +260,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
sama5d4_periph32ck[i].n,
"h32mxck",
sama5d4_periph32ck[i].id,
&range);
&range, INT_MIN);
if (IS_ERR(hw))
goto err_free;

1059
drivers/clk/at91/sama7g5.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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;

295
drivers/clk/clk-sparx5.c Normal file
Просмотреть файл

@ -0,0 +1,295 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Microchip Sparx5 SoC Clock driver.
*
* Copyright (c) 2019 Microchip Inc.
*
* Author: Lars Povlsen <lars.povlsen@microchip.com>
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/clk-provider.h>
#include <linux/bitfield.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/microchip,sparx5.h>
#define PLL_DIV GENMASK(7, 0)
#define PLL_PRE_DIV GENMASK(10, 8)
#define PLL_ROT_DIR BIT(11)
#define PLL_ROT_SEL GENMASK(13, 12)
#define PLL_ROT_ENA BIT(14)
#define PLL_CLK_ENA BIT(15)
#define MAX_SEL 4
#define MAX_PRE BIT(3)
static const u8 sel_rates[MAX_SEL] = { 0, 2*8, 2*4, 2*2 };
static const char *clk_names[N_CLOCKS] = {
"core", "ddr", "cpu2", "arm2",
"aux1", "aux2", "aux3", "aux4",
"synce",
};
struct s5_hw_clk {
struct clk_hw hw;
void __iomem *reg;
};
struct s5_clk_data {
void __iomem *base;
struct s5_hw_clk s5_hw[N_CLOCKS];
};
struct s5_pll_conf {
unsigned long freq;
u8 div;
bool rot_ena;
u8 rot_sel;
u8 rot_dir;
u8 pre_div;
};
#define to_s5_pll(hw) container_of(hw, struct s5_hw_clk, hw)
static unsigned long s5_calc_freq(unsigned long parent_rate,
const struct s5_pll_conf *conf)
{
unsigned long rate = parent_rate / conf->div;
if (conf->rot_ena) {
int sign = conf->rot_dir ? -1 : 1;
int divt = sel_rates[conf->rot_sel] * (1 + conf->pre_div);
int divb = divt + sign;
rate = mult_frac(rate, divt, divb);
rate = roundup(rate, 1000);
}
return rate;
}
static void s5_search_fractional(unsigned long rate,
unsigned long parent_rate,
int div,
struct s5_pll_conf *conf)
{
struct s5_pll_conf best;
ulong cur_offset, best_offset = rate;
int d, i, j;
memset(conf, 0, sizeof(*conf));
conf->div = div;
conf->rot_ena = 1; /* Fractional rate */
for (d = 0; best_offset > 0 && d <= 1 ; d++) {
conf->rot_dir = !!d;
for (i = 0; best_offset > 0 && i < MAX_PRE; i++) {
conf->pre_div = i;
for (j = 1; best_offset > 0 && j < MAX_SEL; j++) {
conf->rot_sel = j;
conf->freq = s5_calc_freq(parent_rate, conf);
cur_offset = abs(rate - conf->freq);
if (cur_offset < best_offset) {
best_offset = cur_offset;
best = *conf;
}
}
}
}
/* Best match */
*conf = best;
}
static unsigned long s5_calc_params(unsigned long rate,
unsigned long parent_rate,
struct s5_pll_conf *conf)
{
if (parent_rate % rate) {
struct s5_pll_conf alt1, alt2;
int div;
div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate);
s5_search_fractional(rate, parent_rate, div, &alt1);
/* Straight match? */
if (alt1.freq == rate) {
*conf = alt1;
} else {
/* Try without rounding divider */
div = parent_rate / rate;
if (div != alt1.div) {
s5_search_fractional(rate, parent_rate, div,
&alt2);
/* Select the better match */
if (abs(rate - alt1.freq) <
abs(rate - alt2.freq))
*conf = alt1;
else
*conf = alt2;
}
}
} else {
/* Straight fit */
memset(conf, 0, sizeof(*conf));
conf->div = parent_rate / rate;
}
return conf->freq;
}
static int s5_pll_enable(struct clk_hw *hw)
{
struct s5_hw_clk *pll = to_s5_pll(hw);
u32 val = readl(pll->reg);
val |= PLL_CLK_ENA;
writel(val, pll->reg);
return 0;
}
static void s5_pll_disable(struct clk_hw *hw)
{
struct s5_hw_clk *pll = to_s5_pll(hw);
u32 val = readl(pll->reg);
val &= ~PLL_CLK_ENA;
writel(val, pll->reg);
}
static int s5_pll_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
struct s5_hw_clk *pll = to_s5_pll(hw);
struct s5_pll_conf conf;
unsigned long eff_rate;
u32 val;
eff_rate = s5_calc_params(rate, parent_rate, &conf);
if (eff_rate != rate)
return -EOPNOTSUPP;
val = readl(pll->reg) & PLL_CLK_ENA;
val |= FIELD_PREP(PLL_DIV, conf.div);
if (conf.rot_ena) {
val |= PLL_ROT_ENA;
val |= FIELD_PREP(PLL_ROT_SEL, conf.rot_sel);
val |= FIELD_PREP(PLL_PRE_DIV, conf.pre_div);
if (conf.rot_dir)
val |= PLL_ROT_DIR;
}
writel(val, pll->reg);
return 0;
}
static unsigned long s5_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct s5_hw_clk *pll = to_s5_pll(hw);
struct s5_pll_conf conf;
u32 val;
val = readl(pll->reg);
if (val & PLL_CLK_ENA) {
conf.div = FIELD_GET(PLL_DIV, val);
conf.pre_div = FIELD_GET(PLL_PRE_DIV, val);
conf.rot_ena = FIELD_GET(PLL_ROT_ENA, val);
conf.rot_dir = FIELD_GET(PLL_ROT_DIR, val);
conf.rot_sel = FIELD_GET(PLL_ROT_SEL, val);
conf.freq = s5_calc_freq(parent_rate, &conf);
} else {
conf.freq = 0;
}
return conf.freq;
}
static long s5_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct s5_pll_conf conf;
return s5_calc_params(rate, *parent_rate, &conf);
}
static const struct clk_ops s5_pll_ops = {
.enable = s5_pll_enable,
.disable = s5_pll_disable,
.set_rate = s5_pll_set_rate,
.round_rate = s5_pll_round_rate,
.recalc_rate = s5_pll_recalc_rate,
};
static struct clk_hw *s5_clk_hw_get(struct of_phandle_args *clkspec, void *data)
{
struct s5_clk_data *s5_clk = data;
unsigned int idx = clkspec->args[0];
if (idx >= N_CLOCKS) {
pr_err("%s: invalid index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
return &s5_clk->s5_hw[idx].hw;
}
static int s5_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int i, ret;
struct s5_clk_data *s5_clk;
struct clk_parent_data pdata = { .index = 0 };
struct clk_init_data init = {
.ops = &s5_pll_ops,
.num_parents = 1,
.parent_data = &pdata,
};
s5_clk = devm_kzalloc(dev, sizeof(*s5_clk), GFP_KERNEL);
if (!s5_clk)
return -ENOMEM;
s5_clk->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(s5_clk->base))
return PTR_ERR(s5_clk->base);
for (i = 0; i < N_CLOCKS; i++) {
struct s5_hw_clk *s5_hw = &s5_clk->s5_hw[i];
init.name = clk_names[i];
s5_hw->reg = s5_clk->base + (i * 4);
s5_hw->hw.init = &init;
ret = devm_clk_hw_register(dev, &s5_hw->hw);
if (ret) {
dev_err(dev, "failed to register %s clock\n",
init.name);
return ret;
}
}
return devm_of_clk_add_hw_provider(dev, s5_clk_hw_get, s5_clk);
}
static const struct of_device_id s5_clk_dt_ids[] = {
{ .compatible = "microchip,sparx5-dpll", },
{ }
};
MODULE_DEVICE_TABLE(of, s5_clk_dt_ids);
static struct platform_driver s5_clk_driver = {
.probe = s5_clk_probe,
.driver = {
.name = "sparx5-clk",
.of_match_table = s5_clk_dt_ids,
},
};
builtin_platform_driver(s5_clk_driver);

Просмотреть файл

@ -500,12 +500,6 @@ static unsigned long clk_core_get_accuracy_no_lock(struct clk_core *core)
return core->accuracy;
}
unsigned long __clk_get_flags(struct clk *clk)
{
return !clk ? 0 : clk->core->flags;
}
EXPORT_SYMBOL_GPL(__clk_get_flags);
unsigned long clk_hw_get_flags(const struct clk_hw *hw)
{
return hw->core->flags;

Просмотреть файл

@ -10,6 +10,7 @@
*/
#include <linux/clk.h>
#include <linux/clk/mmp.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>

Просмотреть файл

@ -10,6 +10,7 @@
*/
#include <linux/clk.h>
#include <linux/clk/mmp.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>

Просмотреть файл

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019 Microchip Inc.
*
* Author: Lars Povlsen <lars.povlsen@microchip.com>
*/
#ifndef _DT_BINDINGS_CLK_SPARX5_H
#define _DT_BINDINGS_CLK_SPARX5_H
#define CLK_ID_CORE 0
#define CLK_ID_DDR 1
#define CLK_ID_CPU2 2
#define CLK_ID_ARM2 3
#define CLK_ID_AUX1 4
#define CLK_ID_AUX2 5
#define CLK_ID_AUX3 6
#define CLK_ID_AUX4 7
#define CLK_ID_SYNCE 8
#define N_CLOCKS 9
#endif

Просмотреть файл

@ -1096,7 +1096,6 @@ int clk_hw_get_parent_index(struct clk_hw *hw);
int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *new_parent);
unsigned int __clk_get_enable_count(struct clk *clk);
unsigned long clk_hw_get_rate(const struct clk_hw *hw);
unsigned long __clk_get_flags(struct clk *clk);
unsigned long clk_hw_get_flags(const struct clk_hw *hw);
#define clk_hw_can_set_rate_parent(hw) \
(clk_hw_get_flags((hw)) & CLK_SET_RATE_PARENT)

Просмотреть файл

@ -59,6 +59,7 @@
#define AT91_PMC_PLL_UPDT 0x1C /* PMC PLL update register [for SAM9X60] */
#define AT91_PMC_PLL_UPDT_UPDATE (1 << 8) /* Update PLL settings */
#define AT91_PMC_PLL_UPDT_ID (1 << 0) /* PLL ID */
#define AT91_PMC_PLL_UPDT_ID_MSK (0xf) /* PLL ID mask */
#define AT91_PMC_PLL_UPDT_STUPTIM (0xff << 16) /* Startup time */
#define AT91_CKGR_MOR 0x20 /* Main Oscillator Register [not on SAM9RL] */
@ -136,6 +137,8 @@
#define AT91_PMC_PLLADIV2_ON (1 << 12)
#define AT91_PMC_H32MXDIV BIT(24)
#define AT91_PMC_XTALF 0x34 /* Main XTAL Frequency Register [SAMA7G5 only] */
#define AT91_PMC_USB 0x38 /* USB Clock Register [some SAM9 only] */
#define AT91_PMC_USBS (0x1 << 0) /* USB OHCI Input clock selection */
#define AT91_PMC_USBS_PLLA (0 << 0)
@ -174,6 +177,7 @@
#define AT91_PMC_MOSCRCS (1 << 17) /* Main On-Chip RC [some SAM9] */
#define AT91_PMC_CFDEV (1 << 18) /* Clock Failure Detector Event [some SAM9] */
#define AT91_PMC_GCKRDY (1 << 24) /* Generated Clocks */
#define AT91_PMC_MCKXRDY (1 << 26) /* Master Clock x [x=1..4] Ready Status */
#define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */
#define AT91_PMC_FSMR 0x70 /* Fast Startup Mode Register */