diff options
Diffstat (limited to 'drivers/clk/pxa/clk-pxa27x.c')
-rw-r--r-- | drivers/clk/pxa/clk-pxa27x.c | 168 |
1 files changed, 135 insertions, 33 deletions
diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c index c40b1804f58c..25a30194d27a 100644 --- a/drivers/clk/pxa/clk-pxa27x.c +++ b/drivers/clk/pxa/clk-pxa27x.c @@ -17,6 +17,8 @@ #include <linux/clkdev.h> #include <linux/of.h> +#include <mach/smemc.h> + #include <dt-bindings/clock/pxa-clock.h> #include "clk-pxa.h" @@ -45,11 +47,52 @@ enum { PXA_MEM_RUN, }; +#define PXA27x_CLKCFG(B, HT, T) \ + (CLKCFG_FCS | \ + ((B) ? CLKCFG_FASTBUS : 0) | \ + ((HT) ? CLKCFG_HALFTURBO : 0) | \ + ((T) ? CLKCFG_TURBO : 0)) +#define PXA27x_CCCR(A, L, N2) (A << 25 | N2 << 7 | L) + +#define MDCNFG_DRAC2(mdcnfg) (((mdcnfg) >> 21) & 0x3) +#define MDCNFG_DRAC0(mdcnfg) (((mdcnfg) >> 5) & 0x3) + +/* Define the refresh period in mSec for the SDRAM and the number of rows */ +#define SDRAM_TREF 64 /* standard 64ms SDRAM */ + static const char * const get_freq_khz[] = { "core", "run", "cpll", "memory", "system_bus" }; +static int get_sdram_rows(void) +{ + static int sdram_rows; + unsigned int drac2 = 0, drac0 = 0; + u32 mdcnfg; + + if (sdram_rows) + return sdram_rows; + + mdcnfg = readl_relaxed(MDCNFG); + + if (mdcnfg & (MDCNFG_DE2 | MDCNFG_DE3)) + drac2 = MDCNFG_DRAC2(mdcnfg); + + if (mdcnfg & (MDCNFG_DE0 | MDCNFG_DE1)) + drac0 = MDCNFG_DRAC0(mdcnfg); + + sdram_rows = 1 << (11 + max(drac0, drac2)); + return sdram_rows; +} + +static u32 mdrefr_dri(unsigned int freq_khz) +{ + u32 interval = freq_khz * SDRAM_TREF / get_sdram_rows(); + + return (interval - 31) / 32; +} + /* * Get the clock frequency as reflected by CCSR and the turbo flag. * We assume these values have been applied via a fcs. @@ -145,6 +188,42 @@ static struct desc_clk_cken pxa27x_clocks[] __initdata = { }; +/* + * PXA270 definitions + * + * For the PXA27x: + * Control variables are A, L, 2N for CCCR; B, HT, T for CLKCFG. + * + * A = 0 => memory controller clock from table 3-7, + * A = 1 => memory controller clock = system bus clock + * Run mode frequency = 13 MHz * L + * Turbo mode frequency = 13 MHz * L * N + * System bus frequency = 13 MHz * L / (B + 1) + * + * In CCCR: + * A = 1 + * L = 16 oscillator to run mode ratio + * 2N = 6 2 * (turbo mode to run mode ratio) + * + * In CCLKCFG: + * B = 1 Fast bus mode + * HT = 0 Half-Turbo mode + * T = 1 Turbo mode + * + * For now, just support some of the combinations in table 3-7 of + * PXA27x Processor Family Developer's Manual to simplify frequency + * change sequences. + */ +static struct pxa2xx_freq pxa27x_freqs[] = { + {104000000, 104000, PXA27x_CCCR(1, 8, 2), 0, PXA27x_CLKCFG(1, 0, 1) }, + {156000000, 104000, PXA27x_CCCR(1, 8, 3), 0, PXA27x_CLKCFG(1, 0, 1) }, + {208000000, 208000, PXA27x_CCCR(0, 16, 2), 1, PXA27x_CLKCFG(0, 0, 1) }, + {312000000, 208000, PXA27x_CCCR(1, 16, 3), 1, PXA27x_CLKCFG(1, 0, 1) }, + {416000000, 208000, PXA27x_CCCR(1, 16, 4), 1, PXA27x_CLKCFG(1, 0, 1) }, + {520000000, 208000, PXA27x_CCCR(1, 16, 5), 1, PXA27x_CLKCFG(1, 0, 1) }, + {624000000, 208000, PXA27x_CCCR(1, 16, 6), 1, PXA27x_CLKCFG(1, 0, 1) }, +}; + static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -162,10 +241,35 @@ static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw, L = l * parent_rate; N = (L * n2) / 2; - return t ? N : L; + return N; +} + +static int clk_pxa27x_cpll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return pxa2xx_determine_rate(req, pxa27x_freqs, + ARRAY_SIZE(pxa27x_freqs)); +} + +static int clk_pxa27x_cpll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int i; + + pr_debug("%s(rate=%lu parent_rate=%lu)\n", __func__, rate, parent_rate); + for (i = 0; i < ARRAY_SIZE(pxa27x_freqs); i++) + if (pxa27x_freqs[i].cpll == rate) + break; + + if (i >= ARRAY_SIZE(pxa27x_freqs)) + return -EINVAL; + + pxa2xx_cpll_change(&pxa27x_freqs[i], mdrefr_dri, MDREFR, CCCR); + return 0; } + PARENTS(clk_pxa27x_cpll) = { "osc_13mhz" }; -RATE_RO_OPS(clk_pxa27x_cpll, "cpll"); +RATE_OPS(clk_pxa27x_cpll, "cpll"); static unsigned long clk_pxa27x_lcd_base_get_rate(struct clk_hw *hw, unsigned long parent_rate) @@ -217,31 +321,10 @@ static void __init pxa27x_register_plls(void) clk_register_fixed_factor(NULL, "ppll_312mhz", "osc_13mhz", 0, 24, 1); } -static unsigned long clk_pxa27x_core_get_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - unsigned long clkcfg; - unsigned int t, ht, b, osc_forced; - unsigned long ccsr = readl(CCSR); - - osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); - asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); - t = clkcfg & (1 << 0); - ht = clkcfg & (1 << 2); - b = clkcfg & (1 << 3); - - if (osc_forced) - return parent_rate; - if (ht) - return parent_rate / 2; - else - return parent_rate; -} - static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw) { unsigned long clkcfg; - unsigned int t, ht, b, osc_forced; + unsigned int t, ht, osc_forced; unsigned long ccsr = readl(CCSR); osc_forced = ccsr & (1 << CCCR_CPDIS_BIT); @@ -251,14 +334,30 @@ static u8 clk_pxa27x_core_get_parent(struct clk_hw *hw) asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg)); t = clkcfg & (1 << 0); ht = clkcfg & (1 << 2); - b = clkcfg & (1 << 3); if (ht || t) return PXA_CORE_TURBO; return PXA_CORE_RUN; } + +static int clk_pxa27x_core_set_parent(struct clk_hw *hw, u8 index) +{ + if (index > PXA_CORE_TURBO) + return -EINVAL; + + pxa2xx_core_turbo_switch(index == PXA_CORE_TURBO); + + return 0; +} + +static int clk_pxa27x_core_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return __clk_mux_determine_rate(hw, req); +} + PARENTS(clk_pxa27x_core) = { "osc_13mhz", "run", "cpll" }; -MUX_RO_RATE_RO_OPS(clk_pxa27x_core, "core"); +MUX_OPS(clk_pxa27x_core, "core", CLK_SET_RATE_PARENT); static unsigned long clk_pxa27x_run_get_rate(struct clk_hw *hw, unsigned long parent_rate) @@ -273,9 +372,10 @@ RATE_RO_OPS(clk_pxa27x_run, "run"); static void __init pxa27x_register_core(void) { - clk_register_clk_pxa27x_cpll(); - clk_register_clk_pxa27x_run(); - + clkdev_pxa_register(CLK_NONE, "cpll", NULL, + clk_register_clk_pxa27x_cpll()); + clkdev_pxa_register(CLK_NONE, "run", NULL, + clk_register_clk_pxa27x_run()); clkdev_pxa_register(CLK_CORE, "core", NULL, clk_register_clk_pxa27x_core()); } @@ -294,9 +394,9 @@ static unsigned long clk_pxa27x_system_bus_get_rate(struct clk_hw *hw, if (osc_forced) return parent_rate; if (b) - return parent_rate / 2; - else return parent_rate; + else + return parent_rate / 2; } static u8 clk_pxa27x_system_bus_get_parent(struct clk_hw *hw) @@ -385,8 +485,10 @@ static void __init pxa27x_base_clocks_init(void) { pxa27x_register_plls(); pxa27x_register_core(); - clk_register_clk_pxa27x_system_bus(); - clk_register_clk_pxa27x_memory(); + clkdev_pxa_register(CLK_NONE, "system_bus", NULL, + clk_register_clk_pxa27x_system_bus()); + clkdev_pxa_register(CLK_NONE, "memory", NULL, + clk_register_clk_pxa27x_memory()); clk_register_clk_pxa27x_lcd_base(); } |