[ARM] pxa: introduce clk support for PXA SoC clocks

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
diff --git a/arch/arm/mach-pxa/clock.c b/arch/arm/mach-pxa/clock.c
index 34a31ca..83ef5ec 100644
--- a/arch/arm/mach-pxa/clock.c
+++ b/arch/arm/mach-pxa/clock.c
@@ -9,19 +9,15 @@
 #include <linux/string.h>
 #include <linux/clk.h>
 #include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 
 #include <asm/arch/pxa-regs.h>
 #include <asm/hardware.h>
 
-struct clk {
-	struct list_head	node;
-	unsigned long		rate;
-	struct module		*owner;
-	const char		*name;
-	unsigned int		enabled;
-	void			(*enable)(void);
-	void			(*disable)(void);
-};
+#include "devices.h"
+#include "generic.h"
+#include "clock.h"
 
 static LIST_HEAD(clocks);
 static DEFINE_MUTEX(clocks_mutex);
@@ -33,7 +29,8 @@
 
 	mutex_lock(&clocks_mutex);
 	list_for_each_entry(p, &clocks, node) {
-		if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
+		if (strcmp(id, p->name) == 0 &&
+		    (p->dev == NULL || p->dev == dev)) {
 			clk = p;
 			break;
 		}
@@ -46,7 +43,6 @@
 
 void clk_put(struct clk *clk)
 {
-	module_put(clk->owner);
 }
 EXPORT_SYMBOL(clk_put);
 
@@ -56,8 +52,12 @@
 
 	spin_lock_irqsave(&clocks_lock, flags);
 	if (clk->enabled++ == 0)
-		clk->enable();
+		clk->ops->enable(clk);
 	spin_unlock_irqrestore(&clocks_lock, flags);
+
+	if (clk->delay)
+		udelay(clk->delay);
+
 	return 0;
 }
 EXPORT_SYMBOL(clk_enable);
@@ -70,54 +70,75 @@
 
 	spin_lock_irqsave(&clocks_lock, flags);
 	if (--clk->enabled == 0)
-		clk->disable();
+		clk->ops->disable(clk);
 	spin_unlock_irqrestore(&clocks_lock, flags);
 }
 EXPORT_SYMBOL(clk_disable);
 
 unsigned long clk_get_rate(struct clk *clk)
 {
-	return clk->rate;
+	unsigned long rate;
+
+	rate = clk->rate;
+	if (clk->ops->getrate)
+		rate = clk->ops->getrate(clk);
+
+	return rate;
 }
 EXPORT_SYMBOL(clk_get_rate);
 
 
-static void clk_gpio27_enable(void)
+static void clk_gpio27_enable(struct clk *clk)
 {
 	pxa_gpio_mode(GPIO11_3_6MHz_MD);
 }
 
-static void clk_gpio27_disable(void)
+static void clk_gpio27_disable(struct clk *clk)
 {
 }
 
-static struct clk clk_gpio27 = {
-	.name		= "GPIO27_CLK",
-	.rate		= 3686400,
+static const struct clkops clk_gpio27_ops = {
 	.enable		= clk_gpio27_enable,
 	.disable	= clk_gpio27_disable,
 };
 
-int clk_register(struct clk *clk)
-{
-	mutex_lock(&clocks_mutex);
-	list_add(&clk->node, &clocks);
-	mutex_unlock(&clocks_mutex);
-	return 0;
-}
-EXPORT_SYMBOL(clk_register);
 
-void clk_unregister(struct clk *clk)
+void clk_cken_enable(struct clk *clk)
 {
+	CKEN |= 1 << clk->cken;
+}
+
+void clk_cken_disable(struct clk *clk)
+{
+	CKEN &= ~(1 << clk->cken);
+}
+
+const struct clkops clk_cken_ops = {
+	.enable		= clk_cken_enable,
+	.disable	= clk_cken_disable,
+};
+
+static struct clk common_clks[] = {
+	{
+		.name		= "GPIO27_CLK",
+		.ops		= &clk_gpio27_ops,
+		.rate		= 3686400,
+	},
+};
+
+void clks_register(struct clk *clks, size_t num)
+{
+	int i;
+
 	mutex_lock(&clocks_mutex);
-	list_del(&clk->node);
+	for (i = 0; i < num; i++)
+		list_add(&clks[i].node, &clocks);
 	mutex_unlock(&clocks_mutex);
 }
-EXPORT_SYMBOL(clk_unregister);
 
 static int __init clk_init(void)
 {
-	clk_register(&clk_gpio27);
+	clks_register(common_clks, ARRAY_SIZE(common_clks));
 	return 0;
 }
 arch_initcall(clk_init);
diff --git a/arch/arm/mach-pxa/clock.h b/arch/arm/mach-pxa/clock.h
new file mode 100644
index 0000000..bc6b77e
--- /dev/null
+++ b/arch/arm/mach-pxa/clock.h
@@ -0,0 +1,43 @@
+struct clk;
+
+struct clkops {
+	void			(*enable)(struct clk *);
+	void			(*disable)(struct clk *);
+	unsigned long		(*getrate)(struct clk *);
+};
+
+struct clk {
+	struct list_head	node;
+	const char		*name;
+	struct device		*dev;
+	const struct clkops	*ops;
+	unsigned long		rate;
+	unsigned int		cken;
+	unsigned int		delay;
+	unsigned int		enabled;
+};
+
+#define INIT_CKEN(_name, _cken, _rate, _delay, _dev)	\
+	{						\
+		.name	= _name,			\
+		.dev	= _dev,				\
+		.ops	= &clk_cken_ops,		\
+		.rate	= _rate,			\
+		.cken	= CKEN_##_cken,			\
+		.delay	= _delay,			\
+	}
+
+#define INIT_CK(_name, _cken, _ops, _dev)		\
+	{						\
+		.name	= _name,			\
+		.dev	= _dev,				\
+		.ops	= _ops,				\
+		.cken	= CKEN_##_cken,			\
+	}
+
+extern const struct clkops clk_cken_ops;
+
+void clk_cken_enable(struct clk *clk);
+void clk_cken_disable(struct clk *clk);
+
+void clks_register(struct clk *clks, size_t num);
diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c
index bcf3f0a..62a7701 100644
--- a/arch/arm/mach-pxa/pxa25x.c
+++ b/arch/arm/mach-pxa/pxa25x.c
@@ -30,6 +30,7 @@
 
 #include "generic.h"
 #include "devices.h"
+#include "clock.h"
 
 /*
  * Various clock factors driven by the CCCR register.
@@ -94,6 +95,41 @@
 	return L_clk_mult[(CCCR >> 0) & 0x1f] * BASE_CLK / 10000;
 }
 
+static unsigned long clk_pxa25x_lcd_getrate(struct clk *clk)
+{
+	return pxa25x_get_memclk_frequency_10khz() * 10000;
+}
+
+static const struct clkops clk_pxa25x_lcd_ops = {
+	.enable		= clk_cken_enable,
+	.disable	= clk_cken_disable,
+	.getrate	= clk_pxa25x_lcd_getrate,
+};
+
+/*
+ * 3.6864MHz -> OST, GPIO, SSP, PWM, PLLs (95.842MHz, 147.456MHz)
+ * 95.842MHz -> MMC 19.169MHz, I2C 31.949MHz, FICP 47.923MHz, USB 47.923MHz
+ * 147.456MHz -> UART 14.7456MHz, AC97 12.288MHz, I2S 5.672MHz (allegedly)
+ */
+static struct clk pxa25x_clks[] = {
+	INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_ops, &pxa_device_fb.dev),
+	INIT_CKEN("UARTCLK", FFUART, 14745600, 1, &pxa_device_ffuart.dev),
+	INIT_CKEN("UARTCLK", BTUART, 14745600, 1, &pxa_device_btuart.dev),
+	INIT_CKEN("UARTCLK", STUART, 14745600, 1, &pxa_device_stuart.dev),
+	INIT_CKEN("UARTCLK", BTUART, 14745600, 1, &pxa_device_btuart.dev),
+	INIT_CKEN("UDCCLK", USB, 47923000, 5, &pxa_device_udc.dev),
+	INIT_CKEN("MMCCLK", MMC, 19169000, 0, &pxa_device_mci.dev),
+	INIT_CKEN("I2CCLK", I2C, 31949000, 0, &pxa_device_i2c.dev),
+	/*
+	INIT_CKEN("PWMCLK",  PWM0, 3686400,  0, NULL),
+	INIT_CKEN("PWMCLK",  PWM0, 3686400,  0, NULL),
+	INIT_CKEN("SSPCLK",  SSP,  3686400,  0, NULL),
+	INIT_CKEN("I2SCLK",  I2S,  14745600, 0, NULL),
+	INIT_CKEN("NSSPCLK", NSSP, 3686400,  0, NULL),
+	INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL),
+	*/
+};
+
 #ifdef CONFIG_PM
 
 #define SAVE(x)		sleep_save[SLEEP_SAVE_##x] = x
@@ -215,6 +251,8 @@
 	int ret = 0;
 
 	if (cpu_is_pxa21x() || cpu_is_pxa25x()) {
+		clks_register(pxa25x_clks, ARRAY_SIZE(pxa25x_clks));
+
 		if ((ret = pxa_init_dma(16)))
 			return ret;
 #ifdef CONFIG_PM
diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c
index 3710981..433644e 100644
--- a/arch/arm/mach-pxa/pxa27x.c
+++ b/arch/arm/mach-pxa/pxa27x.c
@@ -27,6 +27,7 @@
 
 #include "generic.h"
 #include "devices.h"
+#include "clock.h"
 
 /* Crystal clock: 13MHz */
 #define BASE_CLK	13000000
@@ -120,6 +121,48 @@
 	return (K / 10000);
 }
 
+static unsigned long clk_pxa27x_lcd_getrate(struct clk *clk)
+{
+	return pxa27x_get_lcdclk_frequency_10khz() * 10000;
+}
+
+static const struct clkops clk_pxa27x_lcd_ops = {
+	.enable		= clk_cken_enable,
+	.disable	= clk_cken_disable,
+	.getrate	= clk_pxa27x_lcd_getrate,
+};
+
+static struct clk pxa27x_clks[] = {
+	INIT_CK("LCDCLK", LCD,    &clk_pxa27x_lcd_ops, &pxa_device_fb.dev),
+	INIT_CK("CAMCLK", CAMERA, &clk_pxa27x_lcd_ops, NULL),
+
+	INIT_CKEN("UARTCLK", STUART, 14857000, 1, &pxa_device_stuart.dev),
+	INIT_CKEN("UARTCLK", FFUART, 14857000, 1, &pxa_device_ffuart.dev),
+	INIT_CKEN("UARTCLK", BTUART, 14857000, 1, &pxa_device_btuart.dev),
+
+	INIT_CKEN("I2SCLK",  I2S,  14682000, 0, &pxa_device_i2s.dev),
+	INIT_CKEN("I2CCLK",  I2C,  32842000, 0, &pxa_device_i2c.dev),
+	INIT_CKEN("UDCCLK",  USB,  48000000, 5, &pxa_device_udc.dev),
+	INIT_CKEN("MMCCLK",  MMC,  19500000, 0, &pxa_device_mci.dev),
+	INIT_CKEN("FICPCLK", FICP, 48000000, 0, &pxa_device_ficp.dev),
+
+	INIT_CKEN("USBCLK", USB,    48000000, 0, &pxa27x_device_ohci.dev),
+	INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0, &pxa27x_device_i2c_power.dev),
+	INIT_CKEN("KBDCLK", KEYPAD, 32768, 0, NULL),
+
+	/*
+	INIT_CKEN("PWMCLK",  PWM0, 13000000, 0, NULL),
+	INIT_CKEN("SSPCLK",  SSP1, 13000000, 0, NULL),
+	INIT_CKEN("SSPCLK",  SSP2, 13000000, 0, NULL),
+	INIT_CKEN("SSPCLK",  SSP3, 13000000, 0, NULL),
+	INIT_CKEN("MSLCLK",  MSL,  48000000, 0, NULL),
+	INIT_CKEN("USIMCLK", USIM, 48000000, 0, NULL),
+	INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0, NULL),
+	INIT_CKEN("IMCLK",   IM,   0, 0, NULL),
+	INIT_CKEN("MEMCLK",  MEMC, 0, 0, NULL),
+	*/
+};
+
 #ifdef CONFIG_PM
 
 #define SAVE(x)		sleep_save[SLEEP_SAVE_##x] = x
@@ -343,6 +386,8 @@
 {
 	int ret = 0;
 	if (cpu_is_pxa27x()) {
+		clks_register(pxa27x_clks, ARRAY_SIZE(pxa27x_clks));
+
 		if ((ret = pxa_init_dma(32)))
 			return ret;
 #ifdef CONFIG_PM