From 93b458858f6d40650513f63feef0b350eff4845f Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:55 +0100 Subject: [ARM] S3C: Move pwm-clock.c to arch/arm/plat-s3c Move pwm-clock.c to arch/arm/plat-s3c to be shared with the S3C64XX implementations. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/pwm-clock.c | 437 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 arch/arm/plat-s3c/pwm-clock.c (limited to 'arch/arm/plat-s3c/pwm-clock.c') diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c new file mode 100644 index 000000000000..3fad68a1e6bc --- /dev/null +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -0,0 +1,437 @@ +/* linux/arch/arm/plat-s3c24xx/pwm-clock.c + * + * Copyright (c) 2007 Simtec Electronics + * Copyright (c) 2007, 2008 Ben Dooks + * Ben Dooks + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +/* Each of the timers 0 through 5 go through the following + * clock tree, with the inputs depending on the timers. + * + * pclk ---- [ prescaler 0 ] -+---> timer 0 + * +---> timer 1 + * + * pclk ---- [ prescaler 1 ] -+---> timer 2 + * +---> timer 3 + * \---> timer 4 + * + * Which are fed into the timers as so: + * + * prescaled 0 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 0 + * tclk 0 ------------------------------/ + * + * prescaled 0 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 1 + * tclk 0 ------------------------------/ + * + * + * prescaled 1 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 2 + * tclk 1 ------------------------------/ + * + * prescaled 1 ---- [ div 2,4,8,16 ] ---\ + * [mux] -> timer 3 + * tclk 1 ------------------------------/ + * + * prescaled 1 ---- [ div 2,4,8, 16 ] --\ + * [mux] -> timer 4 + * tclk 1 ------------------------------/ + * + * Since the mux and the divider are tied together in the + * same register space, it is impossible to set the parent + * and the rate at the same time. To avoid this, we add an + * intermediate 'prescaled-and-divided' clock to select + * as the parent for the timer input clock called tdiv. + * + * prescaled clk --> pwm-tdiv ---\ + * [ mux ] --> timer X + * tclk -------------------------/ +*/ + +static unsigned long clk_pwm_scaler_getrate(struct clk *clk) +{ + unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); + + if (clk->id == 1) { + tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; + tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; + } else { + tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK; + } + + return clk_get_rate(clk->parent) / (tcfg0 + 1); +} + +/* TODO - add set rate calls. */ + +static struct clk clk_timer_scaler[] = { + [0] = { + .name = "pwm-scaler0", + .id = -1, + .get_rate = clk_pwm_scaler_getrate, + }, + [1] = { + .name = "pwm-scaler1", + .id = -1, + .get_rate = clk_pwm_scaler_getrate, + }, +}; + +static struct clk clk_timer_tclk[] = { + [0] = { + .name = "pwm-tclk0", + .id = -1, + }, + [1] = { + .name = "pwm-tclk1", + .id = -1, + }, +}; + +struct pwm_tdiv_clk { + struct clk clk; + unsigned int divisor; +}; + +static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) +{ + return container_of(clk, struct pwm_tdiv_clk, clk); +} + +static inline unsigned long tcfg_to_divisor(unsigned long tcfg1) +{ + return 1 << (1 + tcfg1); +} + +static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned int divisor; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) + divisor = to_tdiv(clk)->divisor; + else + divisor = tcfg_to_divisor(tcfg1); + + return clk_get_rate(clk->parent) / divisor; +} + +static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, + unsigned long rate) +{ + unsigned long parent_rate; + unsigned long divisor; + + parent_rate = clk_get_rate(clk->parent); + divisor = parent_rate / rate; + + if (divisor <= 2) + divisor = 2; + else if (divisor <= 4) + divisor = 4; + else if (divisor <= 8) + divisor = 8; + else + divisor = 16; + + return parent_rate / divisor; +} + +static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) +{ + unsigned long bits; + + switch (divclk->divisor) { + 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; +} + +static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned long bits = clk_pwm_tdiv_bits(divclk); + unsigned long flags; + unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id); + + local_irq_save(flags); + + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); + tcfg1 |= bits << shift; + __raw_writel(tcfg1, S3C2410_TCFG1); + + local_irq_restore(flags); +} + +static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) +{ + struct pwm_tdiv_clk *divclk = to_tdiv(clk); + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned long parent_rate = clk_get_rate(clk->parent); + unsigned long divisor; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + rate = clk_round_rate(clk, rate); + divisor = parent_rate / rate; + + if (divisor > 16) + return -EINVAL; + + divclk->divisor = divisor; + + /* Update the current MUX settings if we are currently + * selected as the clock source for this clock. */ + + if (tcfg1 != S3C2410_TCFG1_MUX_TCLK) + clk_pwm_tdiv_update(divclk); + + return 0; +} + +static struct pwm_tdiv_clk clk_timer_tdiv[] = { + [0] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[0], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, + [1] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[0], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + } + }, + [2] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[1], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, + [3] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[1], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, + [4] = { + .clk = { + .name = "pwm-tdiv", + .parent = &clk_timer_scaler[1], + .get_rate = clk_pwm_tdiv_get_rate, + .set_rate = clk_pwm_tdiv_set_rate, + .round_rate = clk_pwm_tdiv_round_rate, + }, + }, +}; + +static int __init clk_pwm_tdiv_register(unsigned int id) +{ + struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id]; + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + + tcfg1 >>= S3C2410_TCFG1_SHIFT(id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + divclk->clk.id = id; + divclk->divisor = tcfg_to_divisor(tcfg1); + + return s3c24xx_register_clock(&divclk->clk); +} + +static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id) +{ + return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0]; +} + +static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id) +{ + return &clk_timer_tdiv[id].clk; +} + +static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) +{ + unsigned int id = clk->id; + unsigned long tcfg1; + unsigned long flags; + unsigned long bits; + unsigned long shift = S3C2410_TCFG1_SHIFT(id); + + if (parent == s3c24xx_pwmclk_tclk(id)) + bits = S3C2410_TCFG1_MUX_TCLK << shift; + else if (parent == s3c24xx_pwmclk_tdiv(id)) + bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift; + else + return -EINVAL; + + clk->parent = parent; + + local_irq_save(flags); + + tcfg1 = __raw_readl(S3C2410_TCFG1); + tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); + __raw_writel(tcfg1 | bits, S3C2410_TCFG1); + + local_irq_restore(flags); + + return 0; +} + +static struct clk clk_tin[] = { + [0] = { + .name = "pwm-tin", + .id = 0, + .set_parent = clk_pwm_tin_set_parent, + }, + [1] = { + .name = "pwm-tin", + .id = 1, + .set_parent = clk_pwm_tin_set_parent, + }, + [2] = { + .name = "pwm-tin", + .id = 2, + .set_parent = clk_pwm_tin_set_parent, + }, + [3] = { + .name = "pwm-tin", + .id = 3, + .set_parent = clk_pwm_tin_set_parent, + }, + [4] = { + .name = "pwm-tin", + .id = 4, + .set_parent = clk_pwm_tin_set_parent, + }, +}; + +static __init int clk_pwm_tin_register(struct clk *pwm) +{ + unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); + unsigned int id = pwm->id; + + struct clk *parent; + int ret; + + ret = s3c24xx_register_clock(pwm); + if (ret < 0) + return ret; + + tcfg1 >>= S3C2410_TCFG1_SHIFT(id); + tcfg1 &= S3C2410_TCFG1_MUX_MASK; + + if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) + parent = s3c24xx_pwmclk_tclk(id); + else + parent = s3c24xx_pwmclk_tdiv(id); + + return clk_set_parent(pwm, parent); +} + +static __init int s3c24xx_pwmclk_init(void) +{ + struct clk *clk_timers; + unsigned int clk; + int ret; + + clk_timers = clk_get(NULL, "timers"); + if (IS_ERR(clk_timers)) { + printk(KERN_ERR "%s: no parent clock\n", __func__); + return -EINVAL; + } + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { + clk_timer_scaler[clk].parent = clk_timers; + ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); + if (ret < 0) { + printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); + goto err; + } + } + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) { + ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); + if (ret < 0) { + printk(KERN_ERR "error adding pww tclk%d\n", clk); + goto err; + } + } + + for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { + ret = clk_pwm_tdiv_register(clk); + if (ret < 0) { + printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); + goto err; + } + } + + for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) { + 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 0; + + err: + return ret; +} + +arch_initcall(s3c24xx_pwmclk_init); -- cgit v1.2.2 From 82fd8e681d60a195ce6e9fc783d0ebe7a81b1ead Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 21 Nov 2008 10:36:04 +0000 Subject: [ARM] S3C: Add set_rate/round_rate methods for pwm-scaler clock Add the set_rate and round_rate methods for the pwm-scaler clock for use with the time code. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/pwm-clock.c | 51 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) (limited to 'arch/arm/plat-s3c/pwm-clock.c') diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c index 3fad68a1e6bc..b0fcbc1fb64b 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -73,7 +73,7 @@ * tclk -------------------------/ */ -static unsigned long clk_pwm_scaler_getrate(struct clk *clk) +static unsigned long clk_pwm_scaler_get_rate(struct clk *clk) { unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); @@ -87,18 +87,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->id == 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, }, }; -- cgit v1.2.2 From 7d2dbcf9faad074c52a941d01fc21eea3c95ca33 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 21 Nov 2008 10:36:06 +0000 Subject: [ARM] S3C: Fix scaler1 clock rate information The pwm-scaler0 and pwm-scaler1 clocks have their .id field set to -1 as they are not referenced to any specific device. However, parts of the pwm-clock code used the .id field to identify which scaler clock was being used. Fix the problem by comparing against the pointer to the clock to identify the scalers. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/pwm-clock.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'arch/arm/plat-s3c/pwm-clock.c') diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c index b0fcbc1fb64b..e07d82891a92 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -73,11 +73,13 @@ * tclk -------------------------/ */ +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 { @@ -114,7 +116,7 @@ static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate) local_irq_save(flags); tcfg0 = __raw_readl(S3C2410_TCFG0); - if (clk->id == 1) { + if (clk == &clk_timer_scaler[1]) { tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT; } else { -- cgit v1.2.2 From e550ae741663e4708dcdad3fc392db156189c77c Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Oct 2008 14:06:56 +0100 Subject: [ARM] S3C: Fix PWM build on S3C6400 The pwm-clock.c needs which is included via other means on S3C24XX systems, so ensure it is explicitly included. Remove the includes of regs-clock.h and regs-gpio.h as these are not needed by the build. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/pwm-clock.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'arch/arm/plat-s3c/pwm-clock.c') diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c index e07d82891a92..5242fb0afcca 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -19,11 +19,9 @@ #include #include +#include #include -#include -#include - #include #include -- cgit v1.2.2 From b09bcdd4c2f52b54115895c4d62ad82918f71431 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 21 Nov 2008 10:36:03 +0000 Subject: [ARM] S3C64XX: Update TCFG for new timer divider settings. The S3C64XX series has a new TCFG divider setting to allow the clock directly through, which means that we need to update the pwm-clock code to cope with this. Add containing the specific code to deal with the TCFG divider settings and provide any other per-arch data that the pwm-clock driver needs to function. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/pwm-clock.c | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) (limited to 'arch/arm/plat-s3c/pwm-clock.c') diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c index 5242fb0afcca..988c0cd7ade2 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include /* Each of the timers 0 through 5 go through the following * clock tree, with the inputs depending on the timers. @@ -166,11 +168,6 @@ static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) return container_of(clk, struct pwm_tdiv_clk, clk); } -static inline unsigned long tcfg_to_divisor(unsigned long tcfg1) -{ - return 1 << (1 + tcfg1); -} - static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) { unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); @@ -179,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); @@ -196,7 +193,9 @@ static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, parent_rate = clk_get_rate(clk->parent); divisor = parent_rate / rate; - if (divisor <= 2) + if (divisor <= 1 && pwm_tdiv_has_div1()) + divisor = 1; + else if (divisor <= 2) divisor = 2; else if (divisor <= 4) divisor = 4; @@ -210,25 +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 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) @@ -269,7 +250,7 @@ static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) /* Update the current MUX settings if we are currently * selected as the clock source for this clock. */ - if (tcfg1 != S3C2410_TCFG1_MUX_TCLK) + if (!pwm_cfg_src_is_tclk(tcfg1)) clk_pwm_tdiv_update(divclk); return 0; @@ -356,7 +337,7 @@ static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) unsigned long shift = S3C2410_TCFG1_SHIFT(id); if (parent == s3c24xx_pwmclk_tclk(id)) - bits = S3C2410_TCFG1_MUX_TCLK << shift; + bits = S3C_TCFG1_MUX_TCLK << shift; else if (parent == s3c24xx_pwmclk_tdiv(id)) bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift; else @@ -418,7 +399,7 @@ static __init int clk_pwm_tin_register(struct clk *pwm) tcfg1 >>= S3C2410_TCFG1_SHIFT(id); tcfg1 &= S3C2410_TCFG1_MUX_MASK; - if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) + if (pwm_cfg_src_is_tclk(tcfg1)) parent = s3c24xx_pwmclk_tclk(id); else parent = s3c24xx_pwmclk_tdiv(id); -- cgit v1.2.2 From 9d325f23416d1525401d43442bafca2bb254ab74 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 21 Nov 2008 10:36:05 +0000 Subject: [ARM] S3C: Update time initialisation to fix S3C64XX time problems The S3C64XX timer is running at the wrong rate due to the assumptions made in the timer initialisation about the way the pwm dividers work. This means that time on the S3C64XX runs twice as fast as it should. Fix the problem by moving to using the clk framework to setup the pwm timer clock muxes, as the pwm-clock code has all the necessary knowledge of how the timer clock inputs are routed. Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/pwm-clock.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'arch/arm/plat-s3c/pwm-clock.c') diff --git a/arch/arm/plat-s3c/pwm-clock.c b/arch/arm/plat-s3c/pwm-clock.c index 988c0cd7ade2..a318215ab535 100644 --- a/arch/arm/plat-s3c/pwm-clock.c +++ b/arch/arm/plat-s3c/pwm-clock.c @@ -407,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; @@ -416,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++) { @@ -424,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; } } @@ -432,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; } } @@ -440,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; } } @@ -448,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); -- cgit v1.2.2