[ARM] pxa: introduce clk support for PXA SoC clocks
authorRussell King <rmk@dyn-67.arm.linux.org.uk>
Mon, 20 Aug 2007 09:18:02 +0000 (10:18 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Fri, 12 Oct 2007 20:14:55 +0000 (21:14 +0100)
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mach-pxa/clock.c
arch/arm/mach-pxa/clock.h [new file with mode: 0644]
arch/arm/mach-pxa/pxa25x.c
arch/arm/mach-pxa/pxa27x.c

index 34a31ca..83ef5ec 100644 (file)
@@ -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 @@ struct clk *clk_get(struct device *dev, const char *id)
 
        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 @@ EXPORT_SYMBOL(clk_get);
 
 void clk_put(struct clk *clk)
 {
-       module_put(clk->owner);
 }
 EXPORT_SYMBOL(clk_put);
 
@@ -56,8 +52,12 @@ int clk_enable(struct clk *clk)
 
        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 @@ void clk_disable(struct clk *clk)
 
        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)
+
+void clk_cken_enable(struct clk *clk)
 {
-       mutex_lock(&clocks_mutex);
-       list_add(&clk->node, &clocks);
-       mutex_unlock(&clocks_mutex);
-       return 0;
+       CKEN |= 1 << clk->cken;
 }
-EXPORT_SYMBOL(clk_register);
 
-void clk_unregister(struct clk *clk)
+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 (file)
index 0000000..bc6b77e
--- /dev/null
@@ -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);
index bcf3f0a..62a7701 100644 (file)
@@ -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 @@ unsigned int pxa25x_get_memclk_frequency_10khz(void)
        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 @@ static int __init pxa25x_init(void)
        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
index 3710981..433644e 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "generic.h"
 #include "devices.h"
+#include "clock.h"
 
 /* Crystal clock: 13MHz */
 #define BASE_CLK       13000000
@@ -120,6 +121,48 @@ unsigned int pxa27x_get_lcdclk_frequency_10khz(void)
        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 @@ static int __init pxa27x_init(void)
 {
        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