X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Farm%2Fmach-s3c2410%2Fclock.c;h=e13fb6778890d6559f2a57e532cf16f4cf13dd38;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=a12ade71b25aa453901ca7be709d1f73081ffed7;hpb=a2c21200f1c81b08cb55e417b68150bba439b646;p=linux-2.6.git diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c index a12ade71b..e13fb6778 100644 --- a/arch/arm/mach-s3c2410/clock.c +++ b/arch/arm/mach-s3c2410/clock.c @@ -1,9 +1,9 @@ /* linux/arch/arm/mach-s3c2410/clock.c * - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks * - * S3C2410 Clock control support + * S3C24XX Core clock control support * * Based on, and code from linux/arch/arm/mach-versatile/clock.c ** @@ -32,63 +32,75 @@ #include #include #include - +#include +#include #include #include +#include +#include +#include #include -#include #include #include -#include #include +#include #include "clock.h" +#include "cpu.h" +/* clock information */ static LIST_HEAD(clocks); -static DECLARE_MUTEX(clocks_sem); +DEFINE_MUTEX(clocks_mutex); -/* old functions */ +/* enable and disable calls for use with the clk struct */ -void s3c2410_clk_enable(unsigned int clocks, unsigned int enable) +static int clk_null_enable(struct clk *clk, int enable) { - unsigned long clkcon; - unsigned long flags; - - local_irq_save(flags); - - clkcon = __raw_readl(S3C2410_CLKCON); - clkcon &= ~clocks; - - if (enable) - clkcon |= clocks; - - __raw_writel(clkcon, S3C2410_CLKCON); - - local_irq_restore(flags); + return 0; } - /* Clock API calls */ struct clk *clk_get(struct device *dev, const char *id) { struct clk *p; struct clk *clk = ERR_PTR(-ENOENT); + int idno; + + if (dev == NULL || dev->bus != &platform_bus_type) + idno = -1; + else + idno = to_platform_device(dev)->id; + + mutex_lock(&clocks_mutex); - down(&clocks_sem); list_for_each_entry(p, &clocks, list) { - if (strcmp(id, p->name) == 0 && + if (p->id == idno && + strcmp(id, p->name) == 0 && try_module_get(p->owner)) { clk = p; break; } } - up(&clocks_sem); + /* check for the case where a device was supplied, but the + * clock that was being searched for is not device specific */ + + if (IS_ERR(clk)) { + list_for_each_entry(p, &clocks, list) { + if (p->id == -1 && strcmp(id, p->name) == 0 && + try_module_get(p->owner)) { + clk = p; + break; + } + } + } + + mutex_unlock(&clocks_mutex); return clk; } @@ -99,46 +111,72 @@ void clk_put(struct clk *clk) int clk_enable(struct clk *clk) { - if (clk->ctrlbit != 0) - s3c2410_clk_enable(clk->ctrlbit, 1); + if (IS_ERR(clk) || clk == NULL) + return -EINVAL; + + clk_enable(clk->parent); + + mutex_lock(&clocks_mutex); + + if ((clk->usage++) == 0) + (clk->enable)(clk, 1); + mutex_unlock(&clocks_mutex); return 0; } void clk_disable(struct clk *clk) { - s3c2410_clk_enable(clk->ctrlbit, 0); -} - + if (IS_ERR(clk) || clk == NULL) + return; -int clk_use(struct clk *clk) -{ - atomic_inc(&clk->used); - return 0; -} + mutex_lock(&clocks_mutex); + if ((--clk->usage) == 0) + (clk->enable)(clk, 0); -void clk_unuse(struct clk *clk) -{ - atomic_dec(&clk->used); + mutex_unlock(&clocks_mutex); + clk_disable(clk->parent); } + unsigned long clk_get_rate(struct clk *clk) { + if (IS_ERR(clk)) + return 0; + + if (clk->rate != 0) + return clk->rate; + + if (clk->get_rate != NULL) + return (clk->get_rate)(clk); + if (clk->parent != NULL) - return clk->parent->rate; + return clk_get_rate(clk->parent); return clk->rate; } long clk_round_rate(struct clk *clk, unsigned long rate) { + if (!IS_ERR(clk) && clk->round_rate) + return (clk->round_rate)(clk, rate); + return rate; } int clk_set_rate(struct clk *clk, unsigned long rate) { - return -EINVAL; + int ret; + + if (IS_ERR(clk)) + return -EINVAL; + + mutex_lock(&clocks_mutex); + ret = (clk->set_rate)(clk, rate); + mutex_unlock(&clocks_mutex); + + return ret; } struct clk *clk_get_parent(struct clk *clk) @@ -146,165 +184,266 @@ struct clk *clk_get_parent(struct clk *clk) return clk->parent; } +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int ret = 0; + + if (IS_ERR(clk)) + return -EINVAL; + + mutex_lock(&clocks_mutex); + + if (clk->set_parent) + ret = (clk->set_parent)(clk, parent); + + mutex_unlock(&clocks_mutex); + + return ret; +} + EXPORT_SYMBOL(clk_get); EXPORT_SYMBOL(clk_put); EXPORT_SYMBOL(clk_enable); EXPORT_SYMBOL(clk_disable); -EXPORT_SYMBOL(clk_use); -EXPORT_SYMBOL(clk_unuse); EXPORT_SYMBOL(clk_get_rate); EXPORT_SYMBOL(clk_round_rate); EXPORT_SYMBOL(clk_set_rate); EXPORT_SYMBOL(clk_get_parent); +EXPORT_SYMBOL(clk_set_parent); /* base clocks */ -static struct clk clk_f = { - .name = "fclk", - .rate = 0, - .parent = NULL, - .ctrlbit = 0 +struct clk clk_xtal = { + .name = "xtal", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, +}; + +struct clk clk_mpll = { + .name = "mpll", + .id = -1, +}; + +struct clk clk_upll = { + .name = "upll", + .id = -1, + .parent = NULL, + .ctrlbit = 0, +}; + +struct clk clk_f = { + .name = "fclk", + .id = -1, + .rate = 0, + .parent = &clk_mpll, + .ctrlbit = 0, +}; + +struct clk clk_h = { + .name = "hclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, }; -static struct clk clk_h = { - .name = "hclk", - .rate = 0, - .parent = NULL, - .ctrlbit = 0 +struct clk clk_p = { + .name = "pclk", + .id = -1, + .rate = 0, + .parent = NULL, + .ctrlbit = 0, }; -static struct clk clk_p = { - .name = "pclk", - .rate = 0, - .parent = NULL, - .ctrlbit = 0 +struct clk clk_usb_bus = { + .name = "usb-bus", + .id = -1, + .rate = 0, + .parent = &clk_upll, }; -/* clock definitions */ - -static struct clk init_clocks[] = { - { .name = "nand", - .parent = &clk_h, - .ctrlbit = S3C2410_CLKCON_NAND - }, - { .name = "lcd", - .parent = &clk_h, - .ctrlbit = S3C2410_CLKCON_LCDC - }, - { .name = "usb-host", - .parent = &clk_h, - .ctrlbit = S3C2410_CLKCON_USBH - }, - { .name = "usb-device", - .parent = &clk_h, - .ctrlbit = S3C2410_CLKCON_USBD - }, - { .name = "timers", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_PWMT - }, - { .name = "sdi", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_SDI - }, - { .name = "uart0", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_UART0 - }, - { .name = "uart1", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_UART1 - }, - { .name = "uart2", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_UART2 - }, - { .name = "gpio", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_GPIO - }, - { .name = "rtc", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_RTC - }, - { .name = "adc", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_ADC - }, - { .name = "i2c", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_IIC - }, - { .name = "iis", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_IIS - }, - { .name = "spi", - .parent = &clk_p, - .ctrlbit = S3C2410_CLKCON_SPI - }, - { .name = "watchdog", - .parent = &clk_p, - .ctrlbit = 0 +/* clocks that could be registered by external code */ + +static int s3c24xx_dclk_enable(struct clk *clk, int enable) +{ + unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON); + + if (enable) + dclkcon |= clk->ctrlbit; + else + dclkcon &= ~clk->ctrlbit; + + __raw_writel(dclkcon, S3C24XX_DCLKCON); + + return 0; +} + +static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) +{ + unsigned long dclkcon; + unsigned int uclk; + + if (parent == &clk_upll) + uclk = 1; + else if (parent == &clk_p) + uclk = 0; + else + return -EINVAL; + + clk->parent = parent; + + dclkcon = __raw_readl(S3C24XX_DCLKCON); + + if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { + if (uclk) + dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; + else + dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; + } else { + if (uclk) + dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; + else + dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; } + + __raw_writel(dclkcon, S3C24XX_DCLKCON); + + return 0; +} + + +static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) +{ + unsigned long mask; + unsigned long source; + + /* calculate the MISCCR setting for the clock */ + + if (parent == &clk_xtal) + source = S3C2410_MISCCR_CLK0_MPLL; + else if (parent == &clk_upll) + source = S3C2410_MISCCR_CLK0_UPLL; + else if (parent == &clk_f) + source = S3C2410_MISCCR_CLK0_FCLK; + else if (parent == &clk_h) + source = S3C2410_MISCCR_CLK0_HCLK; + else if (parent == &clk_p) + source = S3C2410_MISCCR_CLK0_PCLK; + else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) + source = S3C2410_MISCCR_CLK0_DCLK0; + else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) + source = S3C2410_MISCCR_CLK0_DCLK0; + else + return -EINVAL; + + clk->parent = parent; + + if (clk == &s3c24xx_dclk0) + mask = S3C2410_MISCCR_CLK0_MASK; + else { + source <<= 4; + mask = S3C2410_MISCCR_CLK1_MASK; + } + + s3c2410_modify_misccr(mask, source); + return 0; +} + +/* external clock definitions */ + +struct clk s3c24xx_dclk0 = { + .name = "dclk0", + .id = -1, + .ctrlbit = S3C2410_DCLKCON_DCLK0EN, + .enable = s3c24xx_dclk_enable, + .set_parent = s3c24xx_dclk_setparent, +}; + +struct clk s3c24xx_dclk1 = { + .name = "dclk1", + .id = -1, + .ctrlbit = S3C2410_DCLKCON_DCLK0EN, + .enable = s3c24xx_dclk_enable, + .set_parent = s3c24xx_dclk_setparent, +}; + +struct clk s3c24xx_clkout0 = { + .name = "clkout0", + .id = -1, + .set_parent = s3c24xx_clkout_setparent, +}; + +struct clk s3c24xx_clkout1 = { + .name = "clkout1", + .id = -1, + .set_parent = s3c24xx_clkout_setparent, +}; + +struct clk s3c24xx_uclk = { + .name = "uclk", + .id = -1, }; /* initialise the clock system */ -int s3c2410_register_clock(struct clk *clk) +int s3c24xx_register_clock(struct clk *clk) { clk->owner = THIS_MODULE; - atomic_set(&clk->used, 0); + + if (clk->enable == NULL) + clk->enable = clk_null_enable; /* add to the list of available clocks */ - down(&clocks_sem); + mutex_lock(&clocks_mutex); list_add(&clk->list, &clocks); - up(&clocks_sem); + mutex_unlock(&clocks_mutex); return 0; } /* initalise all the clocks */ -static int __init s3c2410_init_clocks(void) +int __init s3c24xx_setup_clocks(unsigned long xtal, + unsigned long fclk, + unsigned long hclk, + unsigned long pclk) { - struct clk *clkp = init_clocks; - int ptr; - int ret; - - printk(KERN_INFO "S3C2410 Clock control, (c) 2004 Simtec Electronics\n"); + printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n"); /* initialise the main system clocks */ - clk_h.rate = s3c2410_hclk; - clk_p.rate = s3c2410_pclk; - clk_f.rate = s3c2410_fclk; + clk_xtal.rate = xtal; + clk_upll.rate = s3c2410_get_pll(__raw_readl(S3C2410_UPLLCON), xtal); + + clk_mpll.rate = fclk; + clk_h.rate = hclk; + clk_p.rate = pclk; + clk_f.rate = fclk; - /* set the enabled clocks to a minimal (known) state */ - __raw_writel(S3C2410_CLKCON_PWMT | S3C2410_CLKCON_UART0 | S3C2410_CLKCON_UART1 | S3C2410_CLKCON_UART2 | S3C2410_CLKCON_GPIO | S3C2410_CLKCON_RTC, S3C2410_CLKCON); + /* assume uart clocks are correctly setup */ /* register our clocks */ - if (s3c2410_register_clock(&clk_f) < 0) + if (s3c24xx_register_clock(&clk_xtal) < 0) + printk(KERN_ERR "failed to register master xtal\n"); + + if (s3c24xx_register_clock(&clk_mpll) < 0) + printk(KERN_ERR "failed to register mpll clock\n"); + + if (s3c24xx_register_clock(&clk_upll) < 0) + printk(KERN_ERR "failed to register upll clock\n"); + + if (s3c24xx_register_clock(&clk_f) < 0) printk(KERN_ERR "failed to register cpu fclk\n"); - if (s3c2410_register_clock(&clk_h) < 0) + if (s3c24xx_register_clock(&clk_h) < 0) printk(KERN_ERR "failed to register cpu hclk\n"); - if (s3c2410_register_clock(&clk_p) < 0) + if (s3c24xx_register_clock(&clk_p) < 0) printk(KERN_ERR "failed to register cpu pclk\n"); - for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { - ret = s3c2410_register_clock(clkp); - if (ret < 0) { - printk(KERN_ERR "Failed to register clock %s (%d)\n", - clkp->name, ret); - } - } - return 0; } - -arch_initcall(s3c2410_init_clocks); -