Here's a first pass at better modeling of clocks and PLLs. The longer
term goal being the ability model the clock tree well enough to do
DVFS on parts that support it.
- generalize PLLs
- add concept of clock parents
- drop 'div_by_*' in favor of 'fixed_divider' attribute of clock
- misc. other minor cleanups
TODO:
- add PLL2 which can have programmable dividers.
Known problems:
- doesn't boot on dm646x, and can't see why because DEBUG_LL doesn't work either
Basic boot testing on dm6446 and dm355.
---
arch/arm/mach-davinci/clock.c | 273 +++++++++++++++++++++++++++++------------
arch/arm/mach-davinci/clock.h | 17 +++-
2 files changed, 211 insertions(+), 79 deletions(-)
diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c
index 67ab35d..aeee633 100644
--- a/arch/arm/mach-davinci/clock.c
+++ b/arch/arm/mach-davinci/clock.c
@@ -25,22 +25,31 @@
#include <mach/cpu.h>
#include "clock.h"
-#define DAVINCI_PLL_CNTRL0_BASE 0x01C40800
+#define DAVINCI_PLL1_BASE 0x01c40800
+#define DAVINCI_PLL2_BASE 0x01c40c00
+#define MAX_PLL 2
/* PLL/Reset register offsets */
+#define PLLCTL 0x100
+#define PLLCTL_PLLEN BIT(0)
+#define PLLCTL_CLKMODE BIT(8)
+
#define PLLM 0x110
+#define PLLM_PLLM_MASK 0x1f
+
+#define PREDIV 0x114
+#define PLLDIV1 0x118
+#define PLLDIV2 0x11c
+#define PLLDIV3 0x120
+#define POSTDIV 0x128
+#define BPDIV 0x12c
+#define PLLDIV_EN BIT(15)
+#define PLLDIV_RATIO_MASK 0xf
static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
static DEFINE_SPINLOCK(clockfw_lock);
-static unsigned int commonrate;
-static unsigned int div_by_four;
-static unsigned int div_by_six;
-static unsigned int div_by_eight;
-static unsigned int armrate;
-static unsigned int fixedrate = 27000000; /* 27 MHZ */
-
extern void davinci_psc_config(unsigned int domain, unsigned int id, char
enable);
/*
@@ -213,7 +222,7 @@ unsigned long clk_get_rate(struct clk *clk)
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
- return *(clk->rate);
+ return clk->rate;
}
EXPORT_SYMBOL(clk_get_rate);
@@ -222,7 +231,7 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
- return *(clk->rate);
+ return clk->rate;
}
EXPORT_SYMBOL(clk_round_rate);
@@ -241,6 +250,12 @@ int clk_register(struct clk *clk)
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
+ if (clk->fixed_divisor) {
+ BUG_ON(!clk->parent);
+ BUG_ON(!clk->parent->rate);
+ clk->rate = clk->parent->rate / clk->fixed_divisor;
+ }
+
mutex_lock(&clocks_mutex);
list_add(&clk->node, &clocks);
mutex_unlock(&clocks_mutex);
@@ -260,223 +275,287 @@ void clk_unregister(struct clk *clk)
}
EXPORT_SYMBOL(clk_unregister);
+static struct pll_data pll1_data = {
+ .phys_base = DAVINCI_PLL1_BASE,
+};
+
+static struct clk osc_clk = {
+ .name = "osc_clk",
+ .flags = ALWAYS_ENABLED,
+};
+
+static struct clk aux_clk = {
+ .name = "aux_clk",
+ .flags = ALWAYS_ENABLED,
+};
+
+static struct clk pll1_clk = {
+ .name = "pll1",
+ .parent = &osc_clk,
+ .pll_data = &pll1_data,
+};
+
+static struct clk *pll_list[] = {
+ &pll1_clk,
+ NULL,
+};
+
static struct clk davinci_clks[] = {
{
.name = "ARMCLK",
- .rate = &armrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 2,
.lpsc = -1,
.flags = ALWAYS_ENABLED,
},
{
.name = "UART0",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_UART0,
},
{
.name = "UART1",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_UART1,
},
{
.name = "UART2",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_UART2,
},
{
.name = "EMACCLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_EMAC_WRAPPER,
},
{
.name = "I2CCLK",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_I2C,
},
{
.name = "IDECLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_ATA,
},
{
.name = "McBSPCLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_McBSP,
},
{
.name = "MMCSDCLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_MMC_SD,
},
{
.name = "SPICLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_SPI,
},
{
.name = "gpio",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_GPIO,
},
{
.name = "USBCLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_USB,
},
{
.name = "VLYNQCLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_VLYNQ,
},
{
.name = "AEMIFCLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_AEMIF,
.usecount = 1,
}
};
+
static struct clk davinci_dm646x_clks[] = {
{
.name = "ARMCLK",
- .rate = &armrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 2,
.lpsc = -1,
.flags = ALWAYS_ENABLED,
},
{
.name = "UART0",
- .rate = &fixedrate,
+ .parent = &aux_clk,
+ .fixed_divisor = 1,
.lpsc = DM646X_LPSC_UART0,
},
{
.name = "UART1",
- .rate = &fixedrate,
+ .parent = &aux_clk,
+ .fixed_divisor = 1,
.lpsc = DM646X_LPSC_UART1,
},
{
.name = "UART2",
- .rate = &fixedrate,
+ .parent = &aux_clk,
+ .fixed_divisor = 1,
.lpsc = DM646X_LPSC_UART2,
},
{
.name = "I2CCLK",
- .rate = &div_by_four,
+ .parent = &pll1_clk,
+ .fixed_divisor = 4,
.lpsc = DM646X_LPSC_I2C,
},
{
.name = "gpio",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 4,
.lpsc = DM646X_LPSC_GPIO,
},
{
.name = "AEMIFCLK",
- .rate = &div_by_four,
+ .parent = &pll1_clk,
+ .fixed_divisor = 4,
.lpsc = DM646X_LPSC_AEMIF,
.usecount = 1,
},
{
.name = "EMACCLK",
- .rate = &div_by_four,
+ .parent = &pll1_clk,
+ .fixed_divisor = 4,
.lpsc = DM646X_LPSC_EMAC,
},
};
static struct clk davinci_dm355_clks[] = {
{
.name = "ARMCLK",
- .rate = &armrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 2,
.lpsc = -1,
.flags = ALWAYS_ENABLED,
},
{
.name = "UART0",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_UART0,
- .usecount = 1,
},
{
.name = "UART1",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_UART1,
- .usecount = 1,
},
{
.name = "UART2",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_UART2,
- .usecount = 1,
},
{
.name = "I2CCLK",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_I2C,
},
{
.name = "McBSPCLK0",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_McBSP,
},
{
.name = "McBSPCLK1",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DM355_LPSC_McBSP1,
},
{
.name = "MMCSDCLK0",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_MMC_SD,
},
{
.name = "MMCSDCLK1",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DM355_LPSC_MMC_SD1,
},
{
.name = "SPICLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_SPI,
},
{
.name = "SPICLK1",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DM355_LPSC_SPI1,
},
{
.name = "SPICLK2",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DM355_LPSC_SPI2,
},
{
.name = "gpio",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_GPIO,
},
{
.name = "AEMIFCLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_AEMIF,
.usecount = 1,
},
{
.name = "PWM0_CLK",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_PWM0,
},
{
.name = "PWM1_CLK",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_PWM1,
},
{
.name = "PWM2_CLK",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DAVINCI_LPSC_PWM2,
},
{
.name = "PWM3_CLK",
- .rate = &fixedrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 1,
.lpsc = DM355_LPSC_PWM3,
},
{
.name = "USBCLK",
- .rate = &commonrate,
+ .parent = &pll1_clk,
+ .fixed_divisor = 6,
.lpsc = DAVINCI_LPSC_USB,
},
};
@@ -505,42 +584,82 @@ static int __init clk_disable_unused(void)
late_initcall(clk_disable_unused);
#endif
+static void __init davinci_pll_init(void)
+{
+ int i;
+
+ if (cpu_is_davinci_dm646x()) {
+ osc_clk.rate = DM646X_OSC_FREQ;
+ aux_clk.rate = DM646X_AUX_OSC_FREQ;
+ } else if (cpu_is_davinci_dm355())
+ osc_clk.rate = DM355_OSC_FREQ;
+ else
+ osc_clk.rate = DM644X_OSC_FREQ;
+ clk_register(&osc_clk);
+ clk_register(&aux_clk);
+
+ for(i = 0; i < MAX_PLL; i++) {
+ u32 ctrl, mult, prediv = 1, postdiv = 1;
+ u8 bypass;
+ void __iomem *base;
+ struct clk *pll_clk = pll_list[i];
+
+ if (!pll_clk)
+ continue;
+
+ base = IO_ADDRESS(pll_clk->pll_data->phys_base);
+ ctrl = __raw_readl(base + PLLCTL);
+ bypass = !(ctrl & PLLCTL_PLLEN);
+ pll_clk->rate = pll_clk->parent->rate;
+
+ mult = __raw_readl(base + PLLM) & PLLM_PLLM_MASK;
+ mult += 1;
+
+ postdiv = __raw_readl(base + POSTDIV);
+ if (postdiv & PLLDIV_EN)
+ postdiv = (postdiv & PLLDIV_RATIO_MASK) + 1;
+
+ if (!cpu_is_davinci_dm644x()) {
+ prediv = __raw_readl(base + PREDIV);
+ if (prediv & PLLDIV_EN)
+ prediv = (prediv & PLLDIV_RATIO_MASK) + 1;
+ }
+
+ if (!bypass) {
+ pll_clk->rate /= prediv;
+ pll_clk->rate *= mult;
+ pll_clk->rate /= postdiv;
+ }
+
+ printk(KERN_INFO "PLL%d: input = %lu MHz [ ",
+ i + 1, pll_clk->parent->rate / 1000000);
+ if (bypass)
+ printk("bypass ");
+ if (prediv > 1)
+ printk("/ %d ", prediv);
+ if (mult > 1)
+ printk("* %d ", mult);
+ if (postdiv > 1)
+ printk("/ %d ", postdiv);
+ printk("] --> %lu MHz output.\n", pll_clk->rate / 1000000);
+ }
+}
+
int __init davinci_clk_init(void)
{
struct clk *clkp;
static struct clk *board_clks;
int count = 0, num_clks;
- u32 pll_mult;
- pll_mult = davinci_readl(DAVINCI_PLL_CNTRL0_BASE + PLLM);
- commonrate = ((pll_mult + 1) * DM646X_OSC_FREQ) / 6;
- armrate = ((pll_mult + 1) * DM646X_OSC_FREQ) / 2;
+ davinci_pll_init();
if (cpu_is_davinci_dm646x()) {
- fixedrate = 24000000;
- div_by_four = ((pll_mult + 1) * DM646X_OSC_FREQ) / 4;
- div_by_six = ((pll_mult + 1) * DM646X_OSC_FREQ) / 6;
- div_by_eight = ((pll_mult + 1) * DM646X_OSC_FREQ) / 8;
- armrate = ((pll_mult + 1) * DM646X_OSC_FREQ) / 2;
-
board_clks = davinci_dm646x_clks;
num_clks = ARRAY_SIZE(davinci_dm646x_clks);
} else if (cpu_is_davinci_dm355()) {
- unsigned long postdiv;
-
- postdiv = (davinci_readl(DAVINCI_PLL_CNTRL0_BASE + 0x128)
- & 0x1f) + 1;
-
- fixedrate = 24000000;
- armrate = (pll_mult + 1) * (fixedrate / (16 * postdiv));
- commonrate = armrate / 2;
board_clks = davinci_dm355_clks;
num_clks = ARRAY_SIZE(davinci_dm355_clks);
} else {
- fixedrate = DM646X_OSC_FREQ;
- armrate = (pll_mult + 1) * (fixedrate / 2);
- commonrate = armrate / 3;
-
board_clks = davinci_clks;
num_clks = ARRAY_SIZE(davinci_clks);
}
@@ -581,7 +700,7 @@ static int davinci_ck_show(struct seq_file *m, void *v)
struct clk *cp;
list_for_each_entry(cp, &clocks, node)
- seq_printf(m,"%s %d %d\n", cp->name, *(cp->rate), cp->usecount);
+ seq_printf(m,"%s %lu %d\n", cp->name, cp->rate, cp->usecount);
return 0;
}
diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h
index b7f111d..b5d2be1 100644
--- a/arch/arm/mach-davinci/clock.h
+++ b/arch/arm/mach-davinci/clock.h
@@ -11,15 +11,24 @@
#ifndef __ARCH_ARM_DAVINCI_CLOCK_H
#define __ARCH_ARM_DAVINCI_CLOCK_H
+#define MAX_PLL_DIVISORS 8
+
+struct pll_data {
+ u32 phys_base;
+};
+
struct clk {
struct list_head node;
struct module *owner;
const char *name;
- unsigned int *rate;
+ unsigned long rate;
int id;
__s8 usecount;
__u8 flags;
__u8 lpsc;
+ __u8 fixed_divisor;
+ struct clk *parent;
+ struct pll_data *pll_data;
};
/* Clock flags */
@@ -31,10 +40,14 @@ struct clk {
#define ENABLE_REG_32BIT 32
/* various clock frequencies */
+#define DM644X_OSC_FREQ 27000000
+
#define DM646X_OSC_FREQ 27000000
#define DM646X_AUX_OSC_FREQ 24000000
#define DM646X_CLOCK_TICK_RATE 148500000
-#define DM355_CLOCK_TICK_RATE 24000000
+
+#define DM355_OSC_FREQ 24000000
+#define DM355_CLOCK_TICK_RATE DM355_OSC_FREQ
int davinci_clk_associate(struct device *dev, const char *logical_clockname,
const char *physical_clockname);
--
1.6.0.3
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source