summaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
authorMike Turquette <mturquette@linaro.org>2014-09-30 12:49:42 -0700
committerMike Turquette <mturquette@linaro.org>2014-09-30 12:49:42 -0700
commit38bf3a79785f50ecad01e0ea60bf779507032984 (patch)
treee97036f8b9accbc1a5fae496a4fab1b33f4e0b8a /drivers/clk
parent44b4aa97bea84fa8ac179155f147e3483cc7a6e0 (diff)
parent9ff25d7b58d8a4374886843ed3ed21f1ef17bf16 (diff)
downloadlinux-38bf3a79785f50ecad01e0ea60bf779507032984.tar.gz
linux-38bf3a79785f50ecad01e0ea60bf779507032984.tar.bz2
linux-38bf3a79785f50ecad01e0ea60bf779507032984.zip
Merge branch 'clk-pxa27x' into clk-next
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/pxa/Makefile2
-rw-r--r--drivers/clk/pxa/clk-pxa.c97
-rw-r--r--drivers/clk/pxa/clk-pxa.h107
-rw-r--r--drivers/clk/pxa/clk-pxa27x.c370
5 files changed, 577 insertions, 0 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 92a7f6c02394..8f3c04a91511 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_PLAT_ORION) += mvebu/
obj-$(CONFIG_ARCH_MXS) += mxs/
+obj-$(CONFIG_ARCH_PXA) += pxa/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
diff --git a/drivers/clk/pxa/Makefile b/drivers/clk/pxa/Makefile
new file mode 100644
index 000000000000..4ff2abcd500b
--- /dev/null
+++ b/drivers/clk/pxa/Makefile
@@ -0,0 +1,2 @@
+obj-y += clk-pxa.o
+obj-$(CONFIG_PXA27x) += clk-pxa27x.o
diff --git a/drivers/clk/pxa/clk-pxa.c b/drivers/clk/pxa/clk-pxa.c
new file mode 100644
index 000000000000..ef3c05389c0a
--- /dev/null
+++ b/drivers/clk/pxa/clk-pxa.c
@@ -0,0 +1,97 @@
+/*
+ * Marvell PXA family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * Common clock code for PXA clocks ("CKEN" type clocks + DT)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/pxa-clock.h>
+#include "clk-pxa.h"
+
+DEFINE_SPINLOCK(lock);
+
+static struct clk *pxa_clocks[CLK_MAX];
+static struct clk_onecell_data onecell_data = {
+ .clks = pxa_clocks,
+ .clk_num = CLK_MAX,
+};
+
+#define to_pxa_clk(_hw) container_of(_hw, struct pxa_clk_cken, hw)
+
+static unsigned long cken_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct pxa_clk_cken *pclk = to_pxa_clk(hw);
+ struct clk_fixed_factor *fix;
+
+ if (!pclk->is_in_low_power || pclk->is_in_low_power())
+ fix = &pclk->lp;
+ else
+ fix = &pclk->hp;
+ fix->hw.clk = hw->clk;
+ return clk_fixed_factor_ops.recalc_rate(&fix->hw, parent_rate);
+}
+
+static struct clk_ops cken_rate_ops = {
+ .recalc_rate = cken_recalc_rate,
+};
+
+static u8 cken_get_parent(struct clk_hw *hw)
+{
+ struct pxa_clk_cken *pclk = to_pxa_clk(hw);
+
+ if (!pclk->is_in_low_power)
+ return 0;
+ return pclk->is_in_low_power() ? 0 : 1;
+}
+
+static struct clk_ops cken_mux_ops = {
+ .get_parent = cken_get_parent,
+ .set_parent = dummy_clk_set_parent,
+};
+
+void __init clkdev_pxa_register(int ckid, const char *con_id,
+ const char *dev_id, struct clk *clk)
+{
+ if (!IS_ERR(clk) && (ckid != CLK_NONE))
+ pxa_clocks[ckid] = clk;
+ if (!IS_ERR(clk))
+ clk_register_clkdev(clk, con_id, dev_id);
+}
+
+int __init clk_pxa_cken_init(struct pxa_clk_cken *clks, int nb_clks)
+{
+ int i;
+ struct pxa_clk_cken *pclk;
+ struct clk *clk;
+
+ for (i = 0; i < nb_clks; i++) {
+ pclk = clks + i;
+ pclk->gate.lock = &lock;
+ clk = clk_register_composite(NULL, pclk->name,
+ pclk->parent_names, 2,
+ &pclk->hw, &cken_mux_ops,
+ &pclk->hw, &cken_rate_ops,
+ &pclk->gate.hw, &clk_gate_ops,
+ pclk->flags);
+ clkdev_pxa_register(pclk->ckid, pclk->con_id, pclk->dev_id,
+ clk);
+ }
+ return 0;
+}
+
+static void __init pxa_dt_clocks_init(struct device_node *np)
+{
+ of_clk_add_provider(np, of_clk_src_onecell_get, &onecell_data);
+}
+CLK_OF_DECLARE(pxa_clks, "marvell,pxa-clocks", pxa_dt_clocks_init);
diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h
new file mode 100644
index 000000000000..5fe219d06b49
--- /dev/null
+++ b/drivers/clk/pxa/clk-pxa.h
@@ -0,0 +1,107 @@
+/*
+ * Marvell PXA family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * Common clock code for PXA clocks ("CKEN" type clocks + DT)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+#ifndef _CLK_PXA_
+#define _CLK_PXA_
+
+#define PARENTS(name) \
+ static const char *name ## _parents[] __initconst
+#define MUX_RO_RATE_RO_OPS(name, clk_name) \
+ static struct clk_hw name ## _mux_hw; \
+ static struct clk_hw name ## _rate_hw; \
+ static struct clk_ops name ## _mux_ops = { \
+ .get_parent = name ## _get_parent, \
+ .set_parent = dummy_clk_set_parent, \
+ }; \
+ static struct clk_ops name ## _rate_ops = { \
+ .recalc_rate = name ## _get_rate, \
+ }; \
+ static struct clk *clk_register_ ## name(void) \
+ { \
+ return clk_register_composite(NULL, clk_name, \
+ name ## _parents, \
+ ARRAY_SIZE(name ## _parents), \
+ &name ## _mux_hw, &name ## _mux_ops, \
+ &name ## _rate_hw, &name ## _rate_ops, \
+ NULL, NULL, CLK_GET_RATE_NOCACHE); \
+ }
+
+#define RATE_RO_OPS(name, clk_name) \
+ static struct clk_hw name ## _rate_hw; \
+ static struct clk_ops name ## _rate_ops = { \
+ .recalc_rate = name ## _get_rate, \
+ }; \
+ static struct clk *clk_register_ ## name(void) \
+ { \
+ return clk_register_composite(NULL, clk_name, \
+ name ## _parents, \
+ ARRAY_SIZE(name ## _parents), \
+ NULL, NULL, \
+ &name ## _rate_hw, &name ## _rate_ops, \
+ NULL, NULL, CLK_GET_RATE_NOCACHE); \
+ }
+
+/*
+ * CKEN clock type
+ * This clock takes it source from 2 possible parents :
+ * - a low power parent
+ * - a normal parent
+ *
+ * +------------+ +-----------+
+ * | Low Power | --- | x mult_lp |
+ * | Clock | | / div_lp |\
+ * +------------+ +-----------+ \+-----+ +-----------+
+ * | Mux |---| CKEN gate |
+ * +------------+ +-----------+ /+-----+ +-----------+
+ * | High Power | | x mult_hp |/
+ * | Clock | --- | / div_hp |
+ * +------------+ +-----------+
+ */
+struct pxa_clk_cken {
+ struct clk_hw hw;
+ int ckid;
+ const char *name;
+ const char *dev_id;
+ const char *con_id;
+ const char **parent_names;
+ struct clk_fixed_factor lp;
+ struct clk_fixed_factor hp;
+ struct clk_gate gate;
+ bool (*is_in_low_power)(void);
+ const unsigned long flags;
+};
+
+#define PXA_CKEN(_dev_id, _con_id, _name, parents, _mult_lp, _div_lp, \
+ _mult_hp, _div_hp, is_lp, _cken_reg, _cken_bit, flag) \
+ { .ckid = CLK_ ## _name, .name = #_name, \
+ .dev_id = _dev_id, .con_id = _con_id, .parent_names = parents,\
+ .lp = { .mult = _mult_lp, .div = _div_lp }, \
+ .hp = { .mult = _mult_hp, .div = _div_hp }, \
+ .is_in_low_power = is_lp, \
+ .gate = { .reg = (void __iomem *)_cken_reg, .bit_idx = _cken_bit }, \
+ .flags = flag, \
+ }
+#define PXA_CKEN_1RATE(dev_id, con_id, name, parents, cken_reg, \
+ cken_bit, flag) \
+ PXA_CKEN(dev_id, con_id, name, parents, 1, 1, 1, 1, \
+ NULL, cken_reg, cken_bit, flag)
+
+static int dummy_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ return 0;
+}
+
+extern void clkdev_pxa_register(int ckid, const char *con_id,
+ const char *dev_id, struct clk *clk);
+extern int clk_pxa_cken_init(struct pxa_clk_cken *clks, int nb_clks);
+
+#endif
diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c
new file mode 100644
index 000000000000..b345cc791e5d
--- /dev/null
+++ b/drivers/clk/pxa/clk-pxa27x.c
@@ -0,0 +1,370 @@
+/*
+ * Marvell PXA27x family clocks
+ *
+ * Copyright (C) 2014 Robert Jarzmik
+ *
+ * Heavily inspired from former arch/arm/mach-pxa/clock.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+#include <linux/clk-provider.h>
+#include <mach/pxa2xx-regs.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+
+#include <dt-bindings/clock/pxa-clock.h>
+#include "clk-pxa.h"
+
+#define KHz 1000
+#define MHz (1000 * 1000)
+
+enum {
+ PXA_CORE_13Mhz = 0,
+ PXA_CORE_RUN,
+ PXA_CORE_TURBO,
+};
+
+enum {
+ PXA_BUS_13Mhz = 0,
+ PXA_BUS_RUN,
+};
+
+enum {
+ PXA_LCD_13Mhz = 0,
+ PXA_LCD_RUN,
+};
+
+enum {
+ PXA_MEM_13Mhz = 0,
+ PXA_MEM_SYSTEM_BUS,
+ PXA_MEM_RUN,
+};
+
+static const char * const get_freq_khz[] = {
+ "core", "run", "cpll", "memory",
+ "system_bus"
+};
+
+/*
+ * Get the clock frequency as reflected by CCSR and the turbo flag.
+ * We assume these values have been applied via a fcs.
+ * If info is not 0 we also display the current settings.
+ */
+unsigned int pxa27x_get_clk_frequency_khz(int info)
+{
+ struct clk *clk;
+ unsigned long clks[5];
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ clk = clk_get(NULL, get_freq_khz[i]);
+ if (IS_ERR(clk)) {
+ clks[i] = 0;
+ } else {
+ clks[i] = clk_get_rate(clk);
+ clk_put(clk);
+ }
+ }
+ if (info) {
+ pr_info("Run Mode clock: %ld.%02ldMHz\n",
+ clks[1] / 1000000, (clks[1] % 1000000) / 10000);
+ pr_info("Turbo Mode clock: %ld.%02ldMHz\n",
+ clks[2] / 1000000, (clks[2] % 1000000) / 10000);
+ pr_info("Memory clock: %ld.%02ldMHz\n",
+ clks[3] / 1000000, (clks[3] % 1000000) / 10000);
+ pr_info("System bus clock: %ld.%02ldMHz\n",
+ clks[4] / 1000000, (clks[4] % 1000000) / 10000);
+ }
+ return (unsigned int)clks[0];
+}
+
+bool pxa27x_is_ppll_disabled(void)
+{
+ unsigned long ccsr = CCSR;
+
+ return ccsr & (1 << CCCR_PPDIS_BIT);
+}
+
+#define PXA27X_CKEN(dev_id, con_id, parents, mult_hp, div_hp, \
+ bit, is_lp, flags) \
+ PXA_CKEN(dev_id, con_id, bit, parents, 1, 1, mult_hp, div_hp, \
+ is_lp, &CKEN, CKEN_ ## bit, flags)
+#define PXA27X_PBUS_CKEN(dev_id, con_id, bit, mult_hp, div_hp, delay) \
+ PXA27X_CKEN(dev_id, con_id, pxa27x_pbus_parents, mult_hp, \
+ div_hp, bit, pxa27x_is_ppll_disabled, 0)
+
+PARENTS(pxa27x_pbus) = { "osc_13mhz", "ppll_312mhz" };
+PARENTS(pxa27x_sbus) = { "system_bus", "system_bus" };
+PARENTS(pxa27x_32Mhz_bus) = { "osc_32_768khz", "osc_32_768khz" };
+PARENTS(pxa27x_lcd_bus) = { "lcd_base", "lcd_base" };
+PARENTS(pxa27x_membus) = { "lcd_base", "lcd_base" };
+
+#define PXA27X_CKEN_1RATE(dev_id, con_id, bit, parents, delay) \
+ PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \
+ &CKEN, CKEN_ ## bit, 0)
+#define PXA27X_CKEN_1RATE_AO(dev_id, con_id, bit, parents, delay) \
+ PXA_CKEN_1RATE(dev_id, con_id, bit, parents, \
+ &CKEN, CKEN_ ## bit, CLK_IGNORE_UNUSED)
+
+static struct pxa_clk_cken pxa27x_clocks[] = {
+ PXA27X_PBUS_CKEN("pxa2xx-uart.0", NULL, FFUART, 2, 42, 1),
+ PXA27X_PBUS_CKEN("pxa2xx-uart.1", NULL, BTUART, 2, 42, 1),
+ PXA27X_PBUS_CKEN("pxa2xx-uart.2", NULL, STUART, 2, 42, 1),
+ PXA27X_PBUS_CKEN("pxa2xx-i2s", NULL, I2S, 2, 51, 0),
+ PXA27X_PBUS_CKEN("pxa2xx-i2c.0", NULL, I2C, 2, 19, 0),
+ PXA27X_PBUS_CKEN("pxa27x-udc", NULL, USB, 2, 13, 5),
+ PXA27X_PBUS_CKEN("pxa2xx-mci.0", NULL, MMC, 2, 32, 0),
+ PXA27X_PBUS_CKEN("pxa2xx-ir", "FICPCLK", FICP, 2, 13, 0),
+ PXA27X_PBUS_CKEN("pxa27x-ohci", NULL, USBHOST, 2, 13, 0),
+ PXA27X_PBUS_CKEN("pxa2xx-i2c.1", NULL, PWRI2C, 1, 24, 0),
+ PXA27X_PBUS_CKEN("pxa27x-ssp.0", NULL, SSP1, 1, 24, 0),
+ PXA27X_PBUS_CKEN("pxa27x-ssp.1", NULL, SSP2, 1, 24, 0),
+ PXA27X_PBUS_CKEN("pxa27x-ssp.2", NULL, SSP3, 1, 24, 0),
+ PXA27X_PBUS_CKEN("pxa27x-pwm.0", NULL, PWM0, 1, 24, 0),
+ PXA27X_PBUS_CKEN("pxa27x-pwm.1", NULL, PWM1, 1, 24, 0),
+ PXA27X_PBUS_CKEN(NULL, "MSLCLK", MSL, 2, 13, 0),
+ PXA27X_PBUS_CKEN(NULL, "USIMCLK", USIM, 2, 13, 0),
+ PXA27X_PBUS_CKEN(NULL, "MSTKCLK", MEMSTK, 2, 32, 0),
+ PXA27X_PBUS_CKEN(NULL, "AC97CLK", AC97, 1, 1, 0),
+ PXA27X_PBUS_CKEN(NULL, "AC97CONFCLK", AC97CONF, 1, 1, 0),
+ PXA27X_PBUS_CKEN(NULL, "OSTIMER0", OSTIMER, 1, 96, 0),
+
+ PXA27X_CKEN_1RATE("pxa27x-keypad", NULL, KEYPAD,
+ pxa27x_32Mhz_bus_parents, 0),
+ PXA27X_CKEN_1RATE(NULL, "IMCLK", IM, pxa27x_sbus_parents, 0),
+ PXA27X_CKEN_1RATE("pxa2xx-fb", NULL, LCD, pxa27x_lcd_bus_parents, 0),
+ PXA27X_CKEN_1RATE("pxa27x-camera.0", NULL, CAMERA,
+ pxa27x_lcd_bus_parents, 0),
+ PXA27X_CKEN_1RATE_AO("pxa2xx-pcmcia", NULL, MEMC,
+ pxa27x_membus_parents, 0),
+
+};
+
+static unsigned long clk_pxa27x_cpll_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long clkcfg;
+ unsigned int t, ht;
+ unsigned int l, L, n2, N;
+ unsigned long ccsr = CCSR;
+
+ asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+ t = clkcfg & (1 << 0);
+ ht = clkcfg & (1 << 2);
+
+ l = ccsr & CCSR_L_MASK;
+ n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT;
+ L = l * parent_rate;
+ N = (L * n2) / 2;
+
+ return t ? N : L;
+}
+PARENTS(clk_pxa27x_cpll) = { "osc_13mhz" };
+RATE_RO_OPS(clk_pxa27x_cpll, "cpll");
+
+static unsigned long clk_pxa27x_lcd_base_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned int l, osc_forced;
+ unsigned long ccsr = CCSR;
+ unsigned long cccr = CCCR;
+
+ l = ccsr & CCSR_L_MASK;
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ if (osc_forced) {
+ if (cccr & (1 << CCCR_LCD_26_BIT))
+ return parent_rate * 2;
+ else
+ return parent_rate;
+ }
+
+ if (l <= 7)
+ return parent_rate;
+ if (l <= 16)
+ return parent_rate / 2;
+ return parent_rate / 4;
+}
+
+static u8 clk_pxa27x_lcd_base_get_parent(struct clk_hw *hw)
+{
+ unsigned int osc_forced;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ if (osc_forced)
+ return PXA_LCD_13Mhz;
+ else
+ return PXA_LCD_RUN;
+}
+
+PARENTS(clk_pxa27x_lcd_base) = { "osc_13mhz", "run" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_lcd_base, "lcd_base");
+
+static void __init pxa27x_register_plls(void)
+{
+ clk_register_fixed_rate(NULL, "osc_13mhz", NULL,
+ CLK_GET_RATE_NOCACHE | CLK_IS_ROOT,
+ 13 * MHz);
+ clk_register_fixed_rate(NULL, "osc_32_768khz", NULL,
+ CLK_GET_RATE_NOCACHE | CLK_IS_ROOT,
+ 32768 * KHz);
+ clk_register_fixed_rate(NULL, "clk_dummy", NULL, CLK_IS_ROOT, 0);
+ 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 = 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 long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ if (osc_forced)
+ return PXA_CORE_13Mhz;
+
+ 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;
+}
+PARENTS(clk_pxa27x_core) = { "osc_13mhz", "run", "cpll" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_core, "core");
+
+static unsigned long clk_pxa27x_run_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long ccsr = CCSR;
+ unsigned int n2 = (ccsr & CCSR_N2_MASK) >> CCSR_N2_SHIFT;
+
+ return (parent_rate / n2) * 2;
+}
+PARENTS(clk_pxa27x_run) = { "cpll" };
+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_CORE, "core", NULL,
+ clk_register_clk_pxa27x_core());
+}
+
+static unsigned long clk_pxa27x_system_bus_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long clkcfg;
+ unsigned int b, osc_forced;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ asm("mrc\tp14, 0, %0, c6, c0, 0" : "=r" (clkcfg));
+ b = clkcfg & (1 << 3);
+
+ if (osc_forced)
+ return parent_rate;
+ if (b)
+ return parent_rate / 2;
+ else
+ return parent_rate;
+}
+
+static u8 clk_pxa27x_system_bus_get_parent(struct clk_hw *hw)
+{
+ unsigned int osc_forced;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ if (osc_forced)
+ return PXA_BUS_13Mhz;
+ else
+ return PXA_BUS_RUN;
+}
+
+PARENTS(clk_pxa27x_system_bus) = { "osc_13mhz", "run" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_system_bus, "system_bus");
+
+static unsigned long clk_pxa27x_memory_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned int a, l, osc_forced;
+ unsigned long cccr = CCCR;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ a = cccr & CCCR_A_BIT;
+ l = ccsr & CCSR_L_MASK;
+
+ if (osc_forced || a)
+ return parent_rate;
+ if (l <= 10)
+ return parent_rate;
+ if (l <= 20)
+ return parent_rate / 2;
+ return parent_rate / 4;
+}
+
+static u8 clk_pxa27x_memory_get_parent(struct clk_hw *hw)
+{
+ unsigned int osc_forced, a;
+ unsigned long cccr = CCCR;
+ unsigned long ccsr = CCSR;
+
+ osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
+ a = cccr & CCCR_A_BIT;
+ if (osc_forced)
+ return PXA_MEM_13Mhz;
+ if (a)
+ return PXA_MEM_SYSTEM_BUS;
+ else
+ return PXA_MEM_RUN;
+}
+
+PARENTS(clk_pxa27x_memory) = { "osc_13mhz", "system_bus", "run" };
+MUX_RO_RATE_RO_OPS(clk_pxa27x_memory, "memory");
+
+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();
+ clk_register_clk_pxa27x_lcd_base();
+}
+
+static int __init pxa27x_clocks_init(void)
+{
+ pxa27x_base_clocks_init();
+ return clk_pxa_cken_init(pxa27x_clocks, ARRAY_SIZE(pxa27x_clocks));
+}
+postcore_initcall(pxa27x_clocks_init);