summary refs log tree commit diff
path: root/arch
diff options
context:
space:
mode:
authorRussell King <rmk@dyn-67.arm.linux.org.uk>2006-06-24 22:21:35 +0100
committerRussell King <rmk+kernel@arm.linux.org.uk>2006-06-24 22:21:35 +0100
commitc1e08adc86f886ba0ad7c8f29c240d6e833e7ec7 (patch)
tree67e66ff524c6d51c61475a3c0df7d8d4850b4284 /arch
parent52e3e772a098274df3c6f5f1ad58cd7fe24089bf (diff)
parent1f64eb379cfc95d5f627b779685f7ac6721df322 (diff)
downloadlinux-c1e08adc86f886ba0ad7c8f29c240d6e833e7ec7.tar.gz
Merge ep93xx branch
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-ep93xx/Makefile2
-rw-r--r--arch/arm/mach-ep93xx/clock.c156
-rw-r--r--arch/arm/mach-ep93xx/core.c28
3 files changed, 185 insertions, 1 deletions
diff --git a/arch/arm/mach-ep93xx/Makefile b/arch/arm/mach-ep93xx/Makefile
index 5393af989e94..05a48a21038e 100644
--- a/arch/arm/mach-ep93xx/Makefile
+++ b/arch/arm/mach-ep93xx/Makefile
@@ -1,7 +1,7 @@
 #
 # Makefile for the linux kernel.
 #
-obj-y			:= core.o
+obj-y			:= core.o clock.o
 obj-m			:=
 obj-n			:=
 obj-			:=
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
new file mode 100644
index 000000000000..08ad782c1649
--- /dev/null
+++ b/arch/arm/mach-ep93xx/clock.c
@@ -0,0 +1,156 @@
+/*
+ * arch/arm/mach-ep93xx/clock.c
+ * Clock control for Cirrus EP93xx chips.
+ *
+ * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <asm/div64.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+
+struct clk {
+	char		*name;
+	unsigned long	rate;
+	int		users;
+	u32		enable_reg;
+	u32		enable_mask;
+};
+
+static struct clk clk_pll1 = {
+	.name		= "pll1",
+};
+static struct clk clk_f = {
+	.name		= "fclk",
+};
+static struct clk clk_h = {
+	.name		= "hclk",
+};
+static struct clk clk_p = {
+	.name		= "pclk",
+};
+static struct clk clk_pll2 = {
+	.name		= "pll2",
+};
+static struct clk clk_usb_host = {
+	.name		= "usb_host",
+	.enable_reg	= EP93XX_SYSCON_CLOCK_CONTROL,
+	.enable_mask	= EP93XX_SYSCON_CLOCK_USH_EN,
+};
+
+
+static struct clk *clocks[] = {
+	&clk_pll1,
+	&clk_f,
+	&clk_h,
+	&clk_p,
+	&clk_pll2,
+	&clk_usb_host,
+};
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(clocks); i++) {
+		if (!strcmp(clocks[i]->name, id))
+			return clocks[i];
+	}
+
+	return ERR_PTR(-ENOENT);
+}
+
+int clk_enable(struct clk *clk)
+{
+	if (!clk->users++ && clk->enable_reg) {
+		u32 value;
+
+		value = __raw_readl(clk->enable_reg);
+		__raw_writel(value | clk->enable_mask, clk->enable_reg);
+	}
+
+	return 0;
+}
+
+void clk_disable(struct clk *clk)
+{
+	if (!--clk->users && clk->enable_reg) {
+		u32 value;
+
+		value = __raw_readl(clk->enable_reg);
+		__raw_writel(value & ~clk->enable_mask, clk->enable_reg);
+	}
+}
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+	return clk->rate;
+}
+
+void clk_put(struct clk *clk)
+{
+}
+
+
+
+static char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 };
+static char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 };
+static char pclk_divisors[] = { 1, 2, 4, 8 };
+
+/*
+ * PLL rate = 14.7456 MHz * (X1FBD + 1) * (X2FBD + 1) / (X2IPD + 1) / 2^PS
+ */
+static unsigned long calc_pll_rate(u32 config_word)
+{
+	unsigned long long rate;
+	int i;
+
+	rate = 14745600;
+	rate *= ((config_word >> 11) & 0x1f) + 1;		/* X1FBD */
+	rate *= ((config_word >> 5) & 0x3f) + 1;		/* X2FBD */
+	do_div(rate, (config_word & 0x1f) + 1);			/* X2IPD */
+	for (i = 0; i < ((config_word >> 16) & 3); i++)		/* PS */
+		rate >>= 1;
+
+	return (unsigned long)rate;
+}
+
+void ep93xx_clock_init(void)
+{
+	u32 value;
+
+	value = __raw_readl(EP93XX_SYSCON_CLOCK_SET1);
+	if (!(value & 0x00800000)) {			/* PLL1 bypassed?  */
+		clk_pll1.rate = 14745600;
+	} else {
+		clk_pll1.rate = calc_pll_rate(value);
+	}
+	clk_f.rate = clk_pll1.rate / fclk_divisors[(value >> 25) & 0x7];
+	clk_h.rate = clk_pll1.rate / hclk_divisors[(value >> 20) & 0x7];
+	clk_p.rate = clk_h.rate / pclk_divisors[(value >> 18) & 0x3];
+
+	value = __raw_readl(EP93XX_SYSCON_CLOCK_SET2);
+	if (!(value & 0x00080000)) {			/* PLL2 bypassed?  */
+		clk_pll2.rate = 14745600;
+	} else if (value & 0x00040000) {		/* PLL2 enabled?  */
+		clk_pll2.rate = calc_pll_rate(value);
+	} else {
+		clk_pll2.rate = 0;
+	}
+	clk_usb_host.rate = clk_pll2.rate / (((value >> 28) & 0xf) + 1);
+
+	printk(KERN_INFO "ep93xx: PLL1 running at %ld MHz, PLL2 at %ld MHz\n",
+		clk_pll1.rate / 1000000, clk_pll2.rate / 1000000);
+	printk(KERN_INFO "ep93xx: FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n",
+		clk_f.rate / 1000000, clk_h.rate / 1000000,
+		clk_p.rate / 1000000);
+}
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index bf6bd71bdd08..1fe73c0a9d01 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -433,10 +433,37 @@ static struct platform_device ep93xx_rtc_device = {
 };
 
 
+static struct resource ep93xx_ohci_resources[] = {
+	[0] = {
+		.start	= EP93XX_USB_PHYS_BASE,
+		.end	= EP93XX_USB_PHYS_BASE + 0x0fff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_EP93XX_USB,
+		.end	= IRQ_EP93XX_USB,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device ep93xx_ohci_device = {
+	.name		= "ep93xx-ohci",
+	.id		= -1,
+	.dev		= {
+		.dma_mask		= (void *)0xffffffff,
+		.coherent_dma_mask	= 0xffffffff,
+	},
+	.num_resources	= ARRAY_SIZE(ep93xx_ohci_resources),
+	.resource	= ep93xx_ohci_resources,
+};
+
+
 void __init ep93xx_init_devices(void)
 {
 	unsigned int v;
 
+	ep93xx_clock_init();
+
 	/*
 	 * Disallow access to MaverickCrunch initially.
 	 */
@@ -450,4 +477,5 @@ void __init ep93xx_init_devices(void)
 	amba_device_register(&uart3_device, &iomem_resource);
 
 	platform_device_register(&ep93xx_rtc_device);
+	platform_device_register(&ep93xx_ohci_device);
 }