diff options
author | Andy Green <andy@openmoko.com> | 2008-11-21 12:37:48 +0000 |
---|---|---|
committer | Andy Green <andy@openmoko.com> | 2008-11-21 12:37:48 +0000 |
commit | a7e6827348e9e85466c9ab6488769bf862e9fd0b (patch) | |
tree | c61f9126a906a80bc4b0e4e259f8c39b904179de /arch | |
parent | 8ddc1622f93675a10de39e1f38a230e953bf2a04 (diff) |
MERGE-via-balaji-tracking-s3c64xx-clock-fixes
Signed-off-by: Andy Green <andy@openmoko.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-s3c2412/clock.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-s3c2443/clock.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c6400/include/mach/pwm-clock.h | 11 | ||||
-rw-r--r-- | arch/arm/plat-s3c/include/plat/clock.h | 5 | ||||
-rw-r--r-- | arch/arm/plat-s3c/pwm-clock.c | 110 | ||||
-rw-r--r-- | arch/arm/plat-s3c/time.c | 66 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/include/mach/pwm-clock.h | 11 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/s3c2410-clock.c | 1 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/clock.c | 2 |
9 files changed, 146 insertions, 63 deletions
diff --git a/arch/arm/mach-s3c2412/clock.c b/arch/arm/mach-s3c2412/clock.c index 3ce15e082e7..a037df5e1c2 100644 --- a/arch/arm/mach-s3c2412/clock.c +++ b/arch/arm/mach-s3c2412/clock.c @@ -767,5 +767,6 @@ int __init s3c2412_baseclk_add(void) s3c2412_clkcon_enable(clkp, 0); } + s3c_pwmclk_init(); return 0; } diff --git a/arch/arm/mach-s3c2443/clock.c b/arch/arm/mach-s3c2443/clock.c index 363f3960878..fdd4ec335a7 100644 --- a/arch/arm/mach-s3c2443/clock.c +++ b/arch/arm/mach-s3c2443/clock.c @@ -1107,4 +1107,6 @@ void __init s3c2443_init_clocks(int xtal) (clkp->enable)(clkp, 0); } + + s3c_pwmclk_init(); } diff --git a/arch/arm/mach-s3c6400/include/mach/pwm-clock.h b/arch/arm/mach-s3c6400/include/mach/pwm-clock.h index fe156c4e46e..b25bedee0d5 100644 --- a/arch/arm/mach-s3c6400/include/mach/pwm-clock.h +++ b/arch/arm/mach-s3c6400/include/mach/pwm-clock.h @@ -42,4 +42,15 @@ static inline unsigned int pwm_tdiv_has_div1(void) return 1; } +/** + * pwm_tdiv_div_bits() - calculate TCFG1 divisor value. + * @div: The divisor to calculate the bit information for. + * + * Turn a divisor into the necessary bit field for TCFG1. + */ +static inline unsigned long pwm_tdiv_div_bits(unsigned int div) +{ + return ilog2(div); +} + #define S3C_TCFG1_MUX_TCLK S3C64XX_TCFG1_MUX_TCLK diff --git a/arch/arm/plat-s3c/include/plat/clock.h b/arch/arm/plat-s3c/include/plat/clock.h index ea1f3ffa971..a10622eed43 100644 --- a/arch/arm/plat-s3c/include/plat/clock.h +++ b/arch/arm/plat-s3c/include/plat/clock.h @@ -81,3 +81,8 @@ extern void s3c2443_setup_clocks(void); /* S3C64XX specific functions and clocks */ extern int s3c64xx_sclk_ctrl(struct clk *clk, int enable); + +/* Init for pwm clock code */ + +extern void s3c_pwmclk_init(void); + diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c index b6e0edf0f39..a318215ab53 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/errno.h> +#include <linux/log2.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> @@ -72,11 +73,13 @@ * tclk -------------------------/ */ -static unsigned long clk_pwm_scaler_getrate(struct clk *clk) +static struct clk clk_timer_scaler[]; + +static unsigned long clk_pwm_scaler_get_rate(struct clk *clk) { unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); - if (clk->id == 1) { + if (clk == &clk_timer_scaler[1]) { tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; } else { @@ -86,18 +89,61 @@ static unsigned long clk_pwm_scaler_getrate(struct clk *clk) return clk_get_rate(clk->parent) / (tcfg0 + 1); } -/* TODO - add set rate calls. */ +static unsigned long clk_pwm_scaler_round_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long divisor = parent_rate / rate; + + if (divisor > 256) + divisor = 256; + else if (divisor < 2) + divisor = 2; + + return parent_rate / divisor; +} + +static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long round = clk_pwm_scaler_round_rate(clk, rate); + unsigned long tcfg0; + unsigned long divisor; + unsigned long flags; + + divisor = clk_get_rate(clk->parent) / round; + divisor--; + + local_irq_save(flags); + tcfg0 = __raw_readl(S3C2410_TCFG0); + + if (clk == &clk_timer_scaler[1]) { + tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT; + } else { + tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; + tcfg0 |= divisor; + } + + __raw_writel(tcfg0, S3C2410_TCFG0); + local_irq_restore(flags); + + return 0; +} static struct clk clk_timer_scaler[] = { [0] = { .name = "pwm-scaler0", .id = -1, - .get_rate = clk_pwm_scaler_getrate, + .get_rate = clk_pwm_scaler_get_rate, + .set_rate = clk_pwm_scaler_set_rate, + .round_rate = clk_pwm_scaler_round_rate, }, [1] = { .name = "pwm-scaler1", .id = -1, - .get_rate = clk_pwm_scaler_getrate, + .get_rate = clk_pwm_scaler_get_rate, + .set_rate = clk_pwm_scaler_set_rate, + .round_rate = clk_pwm_scaler_round_rate, }, }; @@ -130,7 +176,7 @@ static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); tcfg1 &= S3C2410_TCFG1_MUX_MASK; - if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) + if (pwm_cfg_src_is_tclk(tcfg1)) divisor = to_tdiv(clk)->divisor; else divisor = tcfg_to_divisor(tcfg1); @@ -163,29 +209,7 @@ static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) { - unsigned long bits; - - switch (divclk->divisor) { - case 1: - BUG_ON(!pwm_tdiv_has_div1()); - bits = S3C64XX_TCFG1_MUX_DIV1; - break; - case 2: - bits = S3C2410_TCFG1_MUX_DIV2; - break; - case 4: - bits = S3C2410_TCFG1_MUX_DIV4; - break; - case 8: - bits = S3C2410_TCFG1_MUX_DIV8; - break; - case 16: - default: - bits = S3C2410_TCFG1_MUX_DIV16; - break; - } - - return bits; + return pwm_tdiv_div_bits(divclk->divisor); } static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) @@ -383,7 +407,16 @@ static __init int clk_pwm_tin_register(struct clk *pwm) return clk_set_parent(pwm, parent); } -static __init int s3c24xx_pwmclk_init(void) +/** + * s3c_pwmclk_init() - initialise pwm clocks + * + * Initialise and register the clocks which provide the inputs for the + * pwm timer blocks. + * + * Note, this call is required by the time core, so must be called after + * the base clocks are added and before any of the initcalls are run. + */ +__init void s3c_pwmclk_init(void) { struct clk *clk_timers; unsigned int clk; @@ -392,7 +425,7 @@ static __init int s3c24xx_pwmclk_init(void) clk_timers = clk_get(NULL, "timers"); if (IS_ERR(clk_timers)) { printk(KERN_ERR "%s: no parent clock\n", __func__); - return -EINVAL; + return; } for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { @@ -400,7 +433,7 @@ static __init int s3c24xx_pwmclk_init(void) ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); if (ret < 0) { printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); - goto err; + return; } } @@ -408,7 +441,7 @@ static __init int s3c24xx_pwmclk_init(void) ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); if (ret < 0) { printk(KERN_ERR "error adding pww tclk%d\n", clk); - goto err; + return; } } @@ -416,7 +449,7 @@ static __init int s3c24xx_pwmclk_init(void) ret = clk_pwm_tdiv_register(clk); if (ret < 0) { printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); - goto err; + return; } } @@ -424,14 +457,7 @@ static __init int s3c24xx_pwmclk_init(void) ret = clk_pwm_tin_register(&clk_tin[clk]); if (ret < 0) { printk(KERN_ERR "error adding pwm%d tin clock\n", clk); - goto err; + return; } } - - return 0; - - err: - return ret; } - -arch_initcall(s3c24xx_pwmclk_init); diff --git a/arch/arm/plat-s3c/time.c b/arch/arm/plat-s3c/time.c index a581ff7ba66..3b27b29da47 100644 --- a/arch/arm/plat-s3c/time.c +++ b/arch/arm/plat-s3c/time.c @@ -26,6 +26,7 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/platform_device.h> #include <asm/system.h> #include <asm/leds.h> @@ -147,6 +148,10 @@ static struct irqaction s3c2410_timer_irq = { machine_is_anubis() || \ machine_is_osiris()) +static struct clk *tin; +static struct clk *tdiv; +static struct clk *timerclk; + /* * Set up timer interrupt, and return the current time in seconds. * @@ -162,12 +167,6 @@ static void s3c2410_timer_setup (void) tcnt = TICK_MAX; /* default value for tcnt */ - /* read the current timer configuration bits */ - - tcon = __raw_readl(S3C2410_TCON); - tcfg1 = __raw_readl(S3C2410_TCFG1); - tcfg0 = __raw_readl(S3C2410_TCFG0); - /* configure the system for whichever machine is in use */ if (use_tclk1_12()) { @@ -175,11 +174,13 @@ static void s3c2410_timer_setup (void) timer_usec_ticks = timer_mask_usec_ticks(1, 12000000); tcnt = 12000000 / HZ; + tcfg1 = __raw_readl(S3C2410_TCFG1); tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; tcfg1 |= S3C2410_TCFG1_MUX4_TCLK1; + __raw_writel(tcfg1, S3C2410_TCFG1); } else { unsigned long pclk; - struct clk *clk; + struct clk *tscaler; /* for the h1940 (and others), we use the pclk from the core * to generate the timer values. since values around 50 to @@ -190,29 +191,25 @@ static void s3c2410_timer_setup (void) * (8.45 ticks per usec) */ - /* this is used as default if no other timer can be found */ - - clk = clk_get(NULL, "timers"); - if (IS_ERR(clk)) - panic("failed to get clock for system timer"); - - clk_enable(clk); - - pclk = clk_get_rate(clk); + pclk = clk_get_rate(timerclk); /* configure clock tick */ timer_usec_ticks = timer_mask_usec_ticks(6, pclk); - tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK; - tcfg1 |= S3C2410_TCFG1_MUX4_DIV2; + tscaler = clk_get_parent(tdiv); - tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; - tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT; + clk_set_rate(tscaler, pclk / 3); + clk_set_rate(tdiv, pclk / 6); + clk_set_parent(tin, tdiv); - tcnt = (pclk / 6) / HZ; + tcnt = clk_get_rate(tin) / HZ; } + tcon = __raw_readl(S3C2410_TCON); + tcfg0 = __raw_readl(S3C2410_TCFG0); + tcfg1 = __raw_readl(S3C2410_TCFG1); + /* timers reload after counting zero, so reduce the count by 1 */ tcnt--; @@ -248,8 +245,35 @@ static void s3c2410_timer_setup (void) __raw_writel(tcon, S3C2410_TCON); } +static void __init s3c2410_timer_resources(void) +{ + struct platform_device tmpdev; + + tmpdev.dev.bus = &platform_bus_type; + tmpdev.id = 4; + + timerclk = clk_get(NULL, "timers"); + if (IS_ERR(timerclk)) + panic("failed to get clock for system timer"); + + clk_enable(timerclk); + + if (!use_tclk1_12()) { + tin = clk_get(&tmpdev.dev, "pwm-tin"); + if (IS_ERR(tin)) + panic("failed to get pwm-tin clock for system timer"); + + tdiv = clk_get(&tmpdev.dev, "pwm-tdiv"); + if (IS_ERR(tdiv)) + panic("failed to get pwm-tdiv clock for system timer"); + } + + clk_enable(tin); +} + static void __init s3c2410_timer_init(void) { + s3c2410_timer_resources(); s3c2410_timer_setup(); setup_irq(IRQ_TIMER4, &s3c2410_timer_irq); } diff --git a/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h b/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h index 3194ed329ea..a087de21bc2 100644 --- a/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h +++ b/arch/arm/plat-s3c24xx/include/mach/pwm-clock.h @@ -41,4 +41,15 @@ static inline unsigned int pwm_tdiv_has_div1(void) return 0; } +/** + * pwm_tdiv_div_bits() - calculate TCFG1 divisor value. + * @div: The divisor to calculate the bit information for. + * + * Turn a divisor into the necessary bit field for TCFG1. + */ +static inline unsigned long pwm_tdiv_div_bits(unsigned int div) +{ + return ilog2(div) - 1; +} + #define S3C_TCFG1_MUX_TCLK S3C2410_TCFG1_MUX_TCLK diff --git a/arch/arm/plat-s3c24xx/s3c2410-clock.c b/arch/arm/plat-s3c24xx/s3c2410-clock.c index 4e07943c1e2..b61bdb79373 100644 --- a/arch/arm/plat-s3c24xx/s3c2410-clock.c +++ b/arch/arm/plat-s3c24xx/s3c2410-clock.c @@ -272,5 +272,6 @@ int __init s3c2410_baseclk_add(void) (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on", (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on"); + s3c_pwmclk_init(); return 0; } diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c index 523da0cb55c..5a1e97e1f8f 100644 --- a/arch/arm/plat-s3c64xx/clock.c +++ b/arch/arm/plat-s3c64xx/clock.c @@ -277,4 +277,6 @@ void s3c64xx_register_clocks(void) (clkp->enable)(clkp, 0); } + + s3c_pwmclk_init(); } |