Commit 11128726 authored by Nicolas Ferre's avatar Nicolas Ferre

ARM: at91/at91sam9x5: clock management for at91sam9x5 chip family

Several changes to PMC have to be managed for adding this support:
- alternate prescaler location for both MCKR and PCKR
- alternate CSS length for PCKR
- added cpu_is_at91sam9x5() to functional switches
- manage UTMI bias like sam9g45 chip family
Signed-off-by: default avatarNicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: default avatarJean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
parent cbd5c78e
...@@ -48,24 +48,37 @@ ...@@ -48,24 +48,37 @@
* Chips have some kind of clocks : group them by functionality * Chips have some kind of clocks : group them by functionality
*/ */
#define cpu_has_utmi() ( cpu_is_at91sam9rl() \ #define cpu_has_utmi() ( cpu_is_at91sam9rl() \
|| cpu_is_at91sam9g45()) || cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5())
#define cpu_has_800M_plla() ( cpu_is_at91sam9g20() \ #define cpu_has_800M_plla() ( cpu_is_at91sam9g20() \
|| cpu_is_at91sam9g45()) || cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5())
#define cpu_has_300M_plla() (cpu_is_at91sam9g10()) #define cpu_has_300M_plla() (cpu_is_at91sam9g10())
#define cpu_has_pllb() (!(cpu_is_at91sam9rl() \ #define cpu_has_pllb() (!(cpu_is_at91sam9rl() \
|| cpu_is_at91sam9g45())) || cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5()))
#define cpu_has_upll() (cpu_is_at91sam9g45()) #define cpu_has_upll() (cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5())
/* USB host HS & FS */ /* USB host HS & FS */
#define cpu_has_uhp() (!cpu_is_at91sam9rl()) #define cpu_has_uhp() (!cpu_is_at91sam9rl())
/* USB device FS only */ /* USB device FS only */
#define cpu_has_udpfs() (!(cpu_is_at91sam9rl() \ #define cpu_has_udpfs() (!(cpu_is_at91sam9rl() \
|| cpu_is_at91sam9g45())) || cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5()))
#define cpu_has_plladiv2() (cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5())
#define cpu_has_mdiv3() (cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5())
#define cpu_has_alt_prescaler() (cpu_is_at91sam9x5())
static LIST_HEAD(clocks); static LIST_HEAD(clocks);
static DEFINE_SPINLOCK(clk_lock); static DEFINE_SPINLOCK(clk_lock);
...@@ -138,13 +151,6 @@ static void pmc_uckr_mode(struct clk *clk, int is_on) ...@@ -138,13 +151,6 @@ static void pmc_uckr_mode(struct clk *clk, int is_on)
{ {
unsigned int uckr = at91_sys_read(AT91_CKGR_UCKR); unsigned int uckr = at91_sys_read(AT91_CKGR_UCKR);
if (cpu_is_at91sam9g45()) {
if (is_on)
uckr |= AT91_PMC_BIASEN;
else
uckr &= ~AT91_PMC_BIASEN;
}
if (is_on) { if (is_on) {
is_on = AT91_PMC_LOCKU; is_on = AT91_PMC_LOCKU;
at91_sys_write(AT91_CKGR_UCKR, uckr | clk->pmc_mask); at91_sys_write(AT91_CKGR_UCKR, uckr | clk->pmc_mask);
...@@ -209,11 +215,24 @@ static struct clk __init *at91_css_to_clk(unsigned long css) ...@@ -209,11 +215,24 @@ static struct clk __init *at91_css_to_clk(unsigned long css)
return &utmi_clk; return &utmi_clk;
else if (cpu_has_pllb()) else if (cpu_has_pllb())
return &pllb; return &pllb;
break;
/* alternate PMC: can use master clock */
case AT91_PMC_CSS_MASTER:
return &mck;
} }
return NULL; return NULL;
} }
static int pmc_prescaler_divider(u32 reg)
{
if (cpu_has_alt_prescaler()) {
return 1 << ((reg & AT91_PMC_ALT_PRES) >> PMC_ALT_PRES_OFFSET);
} else {
return 1 << ((reg & AT91_PMC_PRES) >> PMC_PRES_OFFSET);
}
}
static void __clk_enable(struct clk *clk) static void __clk_enable(struct clk *clk)
{ {
if (clk->parent) if (clk->parent)
...@@ -315,12 +334,22 @@ int clk_set_rate(struct clk *clk, unsigned long rate) ...@@ -315,12 +334,22 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
{ {
unsigned long flags; unsigned long flags;
unsigned prescale; unsigned prescale;
unsigned long prescale_offset, css_mask;
unsigned long actual; unsigned long actual;
if (!clk_is_programmable(clk)) if (!clk_is_programmable(clk))
return -EINVAL; return -EINVAL;
if (clk->users) if (clk->users)
return -EBUSY; return -EBUSY;
if (cpu_has_alt_prescaler()) {
prescale_offset = PMC_ALT_PRES_OFFSET;
css_mask = AT91_PMC_ALT_PCKR_CSS;
} else {
prescale_offset = PMC_PRES_OFFSET;
css_mask = AT91_PMC_CSS;
}
spin_lock_irqsave(&clk_lock, flags); spin_lock_irqsave(&clk_lock, flags);
actual = clk->parent->rate_hz; actual = clk->parent->rate_hz;
...@@ -329,8 +358,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate) ...@@ -329,8 +358,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
u32 pckr; u32 pckr;
pckr = at91_sys_read(AT91_PMC_PCKR(clk->id)); pckr = at91_sys_read(AT91_PMC_PCKR(clk->id));
pckr &= AT91_PMC_CSS; /* clock selection */ pckr &= css_mask; /* keep clock selection */
pckr |= prescale << 2; pckr |= prescale << prescale_offset;
at91_sys_write(AT91_PMC_PCKR(clk->id), pckr); at91_sys_write(AT91_PMC_PCKR(clk->id), pckr);
clk->rate_hz = actual; clk->rate_hz = actual;
break; break;
...@@ -377,11 +406,17 @@ static void __init init_programmable_clock(struct clk *clk) ...@@ -377,11 +406,17 @@ static void __init init_programmable_clock(struct clk *clk)
{ {
struct clk *parent; struct clk *parent;
u32 pckr; u32 pckr;
unsigned int css_mask;
if (cpu_has_alt_prescaler())
css_mask = AT91_PMC_ALT_PCKR_CSS;
else
css_mask = AT91_PMC_CSS;
pckr = at91_sys_read(AT91_PMC_PCKR(clk->id)); pckr = at91_sys_read(AT91_PMC_PCKR(clk->id));
parent = at91_css_to_clk(pckr & AT91_PMC_CSS); parent = at91_css_to_clk(pckr & css_mask);
clk->parent = parent; clk->parent = parent;
clk->rate_hz = parent->rate_hz / (1 << ((pckr & AT91_PMC_PRES) >> 2)); clk->rate_hz = parent->rate_hz / pmc_prescaler_divider(pckr);
} }
#endif /* CONFIG_AT91_PROGRAMMABLE_CLOCKS */ #endif /* CONFIG_AT91_PROGRAMMABLE_CLOCKS */
...@@ -663,7 +698,7 @@ int __init at91_clock_init(unsigned long main_clock) ...@@ -663,7 +698,7 @@ int __init at91_clock_init(unsigned long main_clock)
if (pll_overclock) if (pll_overclock)
pr_info("Clocks: PLLA overclocked, %ld MHz\n", plla.rate_hz / 1000000); pr_info("Clocks: PLLA overclocked, %ld MHz\n", plla.rate_hz / 1000000);
if (cpu_is_at91sam9g45()) { if (cpu_has_plladiv2()) {
mckr = at91_sys_read(AT91_PMC_MCKR); mckr = at91_sys_read(AT91_PMC_MCKR);
plla.rate_hz /= (1 << ((mckr & AT91_PMC_PLLADIV2) >> 12)); /* plla divisor by 2 */ plla.rate_hz /= (1 << ((mckr & AT91_PMC_PLLADIV2) >> 12)); /* plla divisor by 2 */
} }
...@@ -685,6 +720,10 @@ int __init at91_clock_init(unsigned long main_clock) ...@@ -685,6 +720,10 @@ int __init at91_clock_init(unsigned long main_clock)
* (obtain the USB High Speed 480 MHz when input is 12 MHz) * (obtain the USB High Speed 480 MHz when input is 12 MHz)
*/ */
utmi_clk.rate_hz = 40 * utmi_clk.parent->rate_hz; utmi_clk.rate_hz = 40 * utmi_clk.parent->rate_hz;
/* UTMI bias and PLL are managed at the same time */
if (cpu_has_upll())
utmi_clk.pmc_mask |= AT91_PMC_BIASEN;
} }
/* /*
...@@ -703,7 +742,7 @@ int __init at91_clock_init(unsigned long main_clock) ...@@ -703,7 +742,7 @@ int __init at91_clock_init(unsigned long main_clock)
mckr = at91_sys_read(AT91_PMC_MCKR); mckr = at91_sys_read(AT91_PMC_MCKR);
mck.parent = at91_css_to_clk(mckr & AT91_PMC_CSS); mck.parent = at91_css_to_clk(mckr & AT91_PMC_CSS);
freq = mck.parent->rate_hz; freq = mck.parent->rate_hz;
freq /= (1 << ((mckr & AT91_PMC_PRES) >> 2)); /* prescale */ freq /= pmc_prescaler_divider(mckr); /* prescale */
if (cpu_is_at91rm9200()) { if (cpu_is_at91rm9200()) {
mck.rate_hz = freq / (1 + ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */ mck.rate_hz = freq / (1 + ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */
} else if (cpu_is_at91sam9g20()) { } else if (cpu_is_at91sam9g20()) {
...@@ -711,13 +750,19 @@ int __init at91_clock_init(unsigned long main_clock) ...@@ -711,13 +750,19 @@ int __init at91_clock_init(unsigned long main_clock)
freq / ((mckr & AT91_PMC_MDIV) >> 7) : freq; /* mdiv ; (x >> 7) = ((x >> 8) * 2) */ freq / ((mckr & AT91_PMC_MDIV) >> 7) : freq; /* mdiv ; (x >> 7) = ((x >> 8) * 2) */
if (mckr & AT91_PMC_PDIV) if (mckr & AT91_PMC_PDIV)
freq /= 2; /* processor clock division */ freq /= 2; /* processor clock division */
} else if (cpu_is_at91sam9g45()) { } else if (cpu_has_mdiv3()) {
mck.rate_hz = (mckr & AT91_PMC_MDIV) == AT91SAM9_PMC_MDIV_3 ? mck.rate_hz = (mckr & AT91_PMC_MDIV) == AT91SAM9_PMC_MDIV_3 ?
freq / 3 : freq / (1 << ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */ freq / 3 : freq / (1 << ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */
} else { } else {
mck.rate_hz = freq / (1 << ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */ mck.rate_hz = freq / (1 << ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */
} }
if (cpu_has_alt_prescaler()) {
/* Programmable clocks can use MCK */
mck.type |= CLK_TYPE_PRIMARY;
mck.id = 4;
}
/* Register the PMC's standard clocks */ /* Register the PMC's standard clocks */
for (i = 0; i < ARRAY_SIZE(standard_pmc_clocks); i++) for (i = 0; i < ARRAY_SIZE(standard_pmc_clocks); i++)
at91_clk_add(standard_pmc_clocks[i]); at91_clk_add(standard_pmc_clocks[i]);
......
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