summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2008-07-05 10:02:46 +0200
committerRobert Schwebel <r.schwebel@pengutronix.de>2008-07-05 10:02:46 +0200
commit38a41fdf94c449c165213e4665c3f8a0d30f8aba (patch)
treecde7f6c0da8c9877736a6b66f245e163fa21c2f7 /arch/arm/mach-imx
parentdbff4e9ea2e83fda89143389bfb229cb29425a32 (diff)
IMX: introduce clock API
This patch introduces the clock API for i.MX and converts all in-Kernel drivers to use it. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch/arm/mach-imx')
-rw-r--r--arch/arm/mach-imx/Makefile2
-rw-r--r--arch/arm/mach-imx/clock.c205
-rw-r--r--arch/arm/mach-imx/cpufreq.c20
-rw-r--r--arch/arm/mach-imx/generic.c76
-rw-r--r--arch/arm/mach-imx/time.c23
5 files changed, 240 insertions, 86 deletions
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index 88d5e61a2e1..b047c7e795a 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -4,7 +4,7 @@
# Object file lists.
-obj-y += irq.o time.o dma.o generic.o
+obj-y += irq.o time.o dma.o generic.o clock.o
obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o
diff --git a/arch/arm/mach-imx/clock.c b/arch/arm/mach-imx/clock.c
new file mode 100644
index 00000000000..6a90fe5578d
--- /dev/null
+++ b/arch/arm/mach-imx/clock.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/arch/imx-regs.h>
+
+/*
+ * Very simple approach: We can't disable clocks, so we do
+ * not need refcounting
+ */
+
+struct clk {
+ struct list_head node;
+ const char *name;
+ unsigned long (*get_rate)(void);
+};
+
+/*
+ * get the system pll clock in Hz
+ *
+ * mfi + mfn / (mfd +1)
+ * f = 2 * f_ref * --------------------
+ * pd + 1
+ */
+static unsigned long imx_decode_pll(unsigned int pll, u32 f_ref)
+{
+ unsigned long long ll;
+ unsigned long quot;
+
+ u32 mfi = (pll >> 10) & 0xf;
+ u32 mfn = pll & 0x3ff;
+ u32 mfd = (pll >> 16) & 0x3ff;
+ u32 pd = (pll >> 26) & 0xf;
+
+ mfi = mfi <= 5 ? 5 : mfi;
+
+ ll = 2 * (unsigned long long)f_ref *
+ ((mfi << 16) + (mfn << 16) / (mfd + 1));
+ quot = (pd + 1) * (1 << 16);
+ ll += quot / 2;
+ do_div(ll, quot);
+ return (unsigned long)ll;
+}
+
+static unsigned long imx_get_system_clk(void)
+{
+ u32 f_ref = (CSCR & CSCR_SYSTEM_SEL) ? 16000000 : (CLK32 * 512);
+
+ return imx_decode_pll(SPCTL0, f_ref);
+}
+
+static unsigned long imx_get_mcu_clk(void)
+{
+ return imx_decode_pll(MPCTL0, CLK32 * 512);
+}
+
+/*
+ * get peripheral clock 1 ( UART[12], Timer[12], PWM )
+ */
+static unsigned long imx_get_perclk1(void)
+{
+ return imx_get_system_clk() / (((PCDR) & 0xf)+1);
+}
+
+/*
+ * get peripheral clock 2 ( LCD, SD, SPI[12] )
+ */
+static unsigned long imx_get_perclk2(void)
+{
+ return imx_get_system_clk() / (((PCDR>>4) & 0xf)+1);
+}
+
+/*
+ * get peripheral clock 3 ( SSI )
+ */
+static unsigned long imx_get_perclk3(void)
+{
+ return imx_get_system_clk() / (((PCDR>>16) & 0x7f)+1);
+}
+
+/*
+ * get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA )
+ */
+static unsigned long imx_get_hclk(void)
+{
+ return imx_get_system_clk() / (((CSCR>>10) & 0xf)+1);
+}
+
+static struct clk clk_system_clk = {
+ .name = "system_clk",
+ .get_rate = imx_get_system_clk,
+};
+
+static struct clk clk_hclk = {
+ .name = "hclk",
+ .get_rate = imx_get_hclk,
+};
+
+static struct clk clk_mcu_clk = {
+ .name = "mcu_clk",
+ .get_rate = imx_get_mcu_clk,
+};
+
+static struct clk clk_perclk1 = {
+ .name = "perclk1",
+ .get_rate = imx_get_perclk1,
+};
+
+static struct clk clk_uart_clk = {
+ .name = "uart_clk",
+ .get_rate = imx_get_perclk1,
+};
+
+static struct clk clk_perclk2 = {
+ .name = "perclk2",
+ .get_rate = imx_get_perclk2,
+};
+
+static struct clk clk_perclk3 = {
+ .name = "perclk3",
+ .get_rate = imx_get_perclk3,
+};
+
+static struct clk *clks[] = {
+ &clk_perclk1,
+ &clk_perclk2,
+ &clk_perclk3,
+ &clk_system_clk,
+ &clk_hclk,
+ &clk_mcu_clk,
+ &clk_uart_clk,
+};
+
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_mutex);
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+ struct clk *p, *clk = ERR_PTR(-ENOENT);
+
+ mutex_lock(&clocks_mutex);
+ list_for_each_entry(p, &clocks, node) {
+ if (!strcmp(p->name, id)) {
+ clk = p;
+ goto found;
+ }
+ }
+
+found:
+ mutex_unlock(&clocks_mutex);
+
+ return clk;
+}
+
+void clk_put(struct clk *clk)
+{
+}
+
+int clk_enable(struct clk *clk)
+{
+ return 0;
+}
+
+void clk_disable(struct clk *clk)
+{
+}
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ return clk->get_rate();
+}
+
+int imx_clocks_init(void)
+{
+ int i;
+
+ mutex_lock(&clocks_mutex);
+ for (i = 0; i < ARRAY_SIZE(clks); i++)
+ list_add(&clks[i]->node, &clocks);
+ mutex_unlock(&clocks_mutex);
+
+ return 0;
+}
+
diff --git a/arch/arm/mach-imx/cpufreq.c b/arch/arm/mach-imx/cpufreq.c
index e548ba74a4d..be0809b33e0 100644
--- a/arch/arm/mach-imx/cpufreq.c
+++ b/arch/arm/mach-imx/cpufreq.c
@@ -32,6 +32,8 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
#include <asm/system.h>
#include <asm/hardware.h>
@@ -52,6 +54,8 @@
static u32 mpctl0_at_boot;
static u32 bclk_div_at_boot;
+static struct clk *system_clk, *mcu_clk;
+
static void imx_set_async_mode(void)
{
adjust_cr(CR_920T_CLOCK_MODE, CR_920T_ASYNC_MODE);
@@ -160,10 +164,10 @@ static unsigned int imx_get_speed(unsigned int cpu)
cr = get_cr();
if((cr & CR_920T_CLOCK_MODE) == CR_920T_FASTBUS_MODE) {
- freq = imx_get_system_clk();
+ freq = clk_get_rate(system_clk);
freq = (freq + bclk_div/2) / bclk_div;
} else {
- freq = imx_get_mcu_clk();
+ freq = clk_get_rate(mcu_clk);
if (cscr & CSCR_MPU_PRESC)
freq /= 2;
}
@@ -201,7 +205,7 @@ static int imx_set_target(struct cpufreq_policy *policy,
pr_debug(KERN_DEBUG "imx: requested frequency %ld Hz, mpctl0 at boot 0x%08x\n",
freq, mpctl0_at_boot);
- sysclk = imx_get_system_clk();
+ sysclk = clk_get_rate(system_clk);
if (freq > sysclk / bclk_div_at_boot + 1000000) {
freq = imx_compute_mpctl(&mpctl0, mpctl0_at_boot, CLK32 * 512, freq, relation);
@@ -290,6 +294,16 @@ static int __init imx_cpufreq_init(void)
bclk_div_at_boot = __mfld2val(CSCR_BCLK_DIV, CSCR) + 1;
mpctl0_at_boot = 0;
+ system_clk = clk_get(NULL, "system_clk");
+ if (IS_ERR(system_clk))
+ return PTR_ERR(system_clk);
+
+ mcu_clk = clk_get(NULL, "mcu_clk");
+ if (IS_ERR(mcu_clk)) {
+ clk_put(system_clk);
+ return PTR_ERR(mcu_clk);
+ }
+
if((CSCR & CSCR_MPEN) &&
((get_cr() & CR_920T_CLOCK_MODE) != CR_920T_FASTBUS_MODE))
mpctl0_at_boot = MPCTL0;
diff --git a/arch/arm/mach-imx/generic.c b/arch/arm/mach-imx/generic.c
index 4cfc9d3af28..98ddd8a6d05 100644
--- a/arch/arm/mach-imx/generic.c
+++ b/arch/arm/mach-imx/generic.c
@@ -214,82 +214,6 @@ int imx_irq_to_gpio(unsigned irq)
EXPORT_SYMBOL(imx_irq_to_gpio);
-/*
- * get the system pll clock in Hz
- *
- * mfi + mfn / (mfd +1)
- * f = 2 * f_ref * --------------------
- * pd + 1
- */
-static unsigned int imx_decode_pll(unsigned int pll, u32 f_ref)
-{
- unsigned long long ll;
- unsigned long quot;
-
- u32 mfi = (pll >> 10) & 0xf;
- u32 mfn = pll & 0x3ff;
- u32 mfd = (pll >> 16) & 0x3ff;
- u32 pd = (pll >> 26) & 0xf;
-
- mfi = mfi <= 5 ? 5 : mfi;
-
- ll = 2 * (unsigned long long)f_ref * ( (mfi<<16) + (mfn<<16) / (mfd+1) );
- quot = (pd+1) * (1<<16);
- ll += quot / 2;
- do_div(ll, quot);
- return (unsigned int) ll;
-}
-
-unsigned int imx_get_system_clk(void)
-{
- u32 f_ref = (CSCR & CSCR_SYSTEM_SEL) ? 16000000 : (CLK32 * 512);
-
- return imx_decode_pll(SPCTL0, f_ref);
-}
-EXPORT_SYMBOL(imx_get_system_clk);
-
-unsigned int imx_get_mcu_clk(void)
-{
- return imx_decode_pll(MPCTL0, CLK32 * 512);
-}
-EXPORT_SYMBOL(imx_get_mcu_clk);
-
-/*
- * get peripheral clock 1 ( UART[12], Timer[12], PWM )
- */
-unsigned int imx_get_perclk1(void)
-{
- return imx_get_system_clk() / (((PCDR) & 0xf)+1);
-}
-EXPORT_SYMBOL(imx_get_perclk1);
-
-/*
- * get peripheral clock 2 ( LCD, SD, SPI[12] )
- */
-unsigned int imx_get_perclk2(void)
-{
- return imx_get_system_clk() / (((PCDR>>4) & 0xf)+1);
-}
-EXPORT_SYMBOL(imx_get_perclk2);
-
-/*
- * get peripheral clock 3 ( SSI )
- */
-unsigned int imx_get_perclk3(void)
-{
- return imx_get_system_clk() / (((PCDR>>16) & 0x7f)+1);
-}
-EXPORT_SYMBOL(imx_get_perclk3);
-
-/*
- * get hclk ( SDRAM, CSI, Memory Stick, I2C, DMA )
- */
-unsigned int imx_get_hclk(void)
-{
- return imx_get_system_clk() / (((CSCR>>10) & 0xf)+1);
-}
-EXPORT_SYMBOL(imx_get_hclk);
-
static struct resource imx_mmc_resources[] = {
[0] = {
.start = 0x00214000,
diff --git a/arch/arm/mach-imx/time.c b/arch/arm/mach-imx/time.c
index d86d124aea2..5a41e96e858 100644
--- a/arch/arm/mach-imx/time.c
+++ b/arch/arm/mach-imx/time.c
@@ -17,6 +17,7 @@
#include <linux/time.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
+#include <linux/clk.h>
#include <asm/hardware.h>
#include <asm/io.h>
@@ -86,10 +87,10 @@ static struct clocksource clocksource_imx = {
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
-static int __init imx_clocksource_init(void)
+static int __init imx_clocksource_init(unsigned long rate)
{
clocksource_imx.mult =
- clocksource_hz2mult(imx_get_perclk1(), clocksource_imx.shift);
+ clocksource_hz2mult(rate, clocksource_imx.shift);
clocksource_register(&clocksource_imx);
return 0;
@@ -174,9 +175,9 @@ static struct clock_event_device clockevent_imx = {
.rating = 200,
};
-static int __init imx_clockevent_init(void)
+static int __init imx_clockevent_init(unsigned long rate)
{
- clockevent_imx.mult = div_sc(imx_get_perclk1(), NSEC_PER_SEC,
+ clockevent_imx.mult = div_sc(rate, NSEC_PER_SEC,
clockevent_imx.shift);
clockevent_imx.max_delta_ns =
clockevent_delta2ns(0xfffffffe, &clockevent_imx);
@@ -190,13 +191,23 @@ static int __init imx_clockevent_init(void)
return 0;
}
+extern int imx_clocks_init(void);
static void __init imx_timer_init(void)
{
+ struct clk *clk;
+ unsigned long rate;
+
+ imx_clocks_init();
+
+ clk = clk_get(NULL, "perclk1");
+ clk_enable(clk);
+ rate = clk_get_rate(clk);
+
imx_timer_hardware_init();
- imx_clocksource_init();
+ imx_clocksource_init(rate);
- imx_clockevent_init();
+ imx_clockevent_init(rate);
/*
* Make irqs happen for the system timer