summaryrefslogtreecommitdiffstats
path: root/drivers/clk/pxa/clk-pxa27x.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/pxa/clk-pxa27x.c')
-rw-r--r--drivers/clk/pxa/clk-pxa27x.c168
1 files changed, 135 insertions, 33 deletions
diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c
index c40b180..25a3019 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();
}
OpenPOWER on IntegriCloud