|
@@ -48,24 +48,37 @@
|
|
|
* Chips have some kind of clocks : group them by functionality
|
|
|
*/
|
|
|
#define cpu_has_utmi() ( cpu_is_at91sam9rl() \
|
|
|
- || cpu_is_at91sam9g45())
|
|
|
+ || cpu_is_at91sam9g45() \
|
|
|
+ || cpu_is_at91sam9x5())
|
|
|
|
|
|
#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_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 */
|
|
|
#define cpu_has_uhp() (!cpu_is_at91sam9rl())
|
|
|
|
|
|
/* USB device FS only */
|
|
|
#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 DEFINE_SPINLOCK(clk_lock);
|
|
@@ -138,13 +151,6 @@ static void pmc_uckr_mode(struct clk *clk, int is_on)
|
|
|
{
|
|
|
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) {
|
|
|
is_on = AT91_PMC_LOCKU;
|
|
|
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)
|
|
|
return &utmi_clk;
|
|
|
else if (cpu_has_pllb())
|
|
|
return &pllb;
|
|
|
+ break;
|
|
|
+ /* alternate PMC: can use master clock */
|
|
|
+ case AT91_PMC_CSS_MASTER:
|
|
|
+ return &mck;
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
{
|
|
|
if (clk->parent)
|
|
@@ -315,12 +334,22 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
unsigned prescale;
|
|
|
+ unsigned long prescale_offset, css_mask;
|
|
|
unsigned long actual;
|
|
|
|
|
|
if (!clk_is_programmable(clk))
|
|
|
return -EINVAL;
|
|
|
if (clk->users)
|
|
|
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);
|
|
|
|
|
|
actual = clk->parent->rate_hz;
|
|
@@ -329,8 +358,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
|
|
|
u32 pckr;
|
|
|
|
|
|
pckr = at91_sys_read(AT91_PMC_PCKR(clk->id));
|
|
|
- pckr &= AT91_PMC_CSS; /* clock selection */
|
|
|
- pckr |= prescale << 2;
|
|
|
+ pckr &= css_mask; /* keep clock selection */
|
|
|
+ pckr |= prescale << prescale_offset;
|
|
|
at91_sys_write(AT91_PMC_PCKR(clk->id), pckr);
|
|
|
clk->rate_hz = actual;
|
|
|
break;
|
|
@@ -377,11 +406,17 @@ static void __init init_programmable_clock(struct clk *clk)
|
|
|
{
|
|
|
struct clk *parent;
|
|
|
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));
|
|
|
- parent = at91_css_to_clk(pckr & AT91_PMC_CSS);
|
|
|
+ parent = at91_css_to_clk(pckr & css_mask);
|
|
|
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 */
|
|
@@ -663,7 +698,7 @@ int __init at91_clock_init(unsigned long main_clock)
|
|
|
if (pll_overclock)
|
|
|
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);
|
|
|
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)
|
|
|
* (obtain the USB High Speed 480 MHz when input is 12 MHz)
|
|
|
*/
|
|
|
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)
|
|
|
mckr = at91_sys_read(AT91_PMC_MCKR);
|
|
|
mck.parent = at91_css_to_clk(mckr & AT91_PMC_CSS);
|
|
|
freq = mck.parent->rate_hz;
|
|
|
- freq /= (1 << ((mckr & AT91_PMC_PRES) >> 2)); /* prescale */
|
|
|
+ freq /= pmc_prescaler_divider(mckr); /* prescale */
|
|
|
if (cpu_is_at91rm9200()) {
|
|
|
mck.rate_hz = freq / (1 + ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */
|
|
|
} else if (cpu_is_at91sam9g20()) {
|
|
@@ -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) */
|
|
|
if (mckr & AT91_PMC_PDIV)
|
|
|
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 ?
|
|
|
freq / 3 : freq / (1 << ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */
|
|
|
} else {
|
|
|
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 */
|
|
|
for (i = 0; i < ARRAY_SIZE(standard_pmc_clocks); i++)
|
|
|
at91_clk_add(standard_pmc_clocks[i]);
|