From e24154896e2f60b0c20bda492d662ad77ffa5c32 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 13 Jun 2013 21:22:44 +0200 Subject: clocksource: samsung_pwm_timer: Do not request PWM mem region PWM registers are shared between clocksource and PWM drivers and so can not be claimed for exclusive use. Signed-off-by: Tomasz Figa Reviewed-by: Sylwester Nawrocki Tested-by: Heiko Stuebner Tested-by: Mark Brown Tested-by: Sylwester Nawrocki Acked-by: Arnd Bergmann --- drivers/clocksource/samsung_pwm_timer.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/clocksource/samsung_pwm_timer.c') diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index 584b5472eea3..3fa5b07fa50d 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -404,7 +404,6 @@ void __init samsung_pwm_clocksource_init(void __iomem *base, static void __init samsung_pwm_alloc(struct device_node *np, const struct samsung_pwm_variant *variant) { - struct resource res; struct property *prop; const __be32 *cur; u32 val; @@ -423,17 +422,9 @@ static void __init samsung_pwm_alloc(struct device_node *np, pwm.variant.output_mask |= 1 << val; } - of_address_to_resource(np, 0, &res); - if (!request_mem_region(res.start, - resource_size(&res), "samsung-pwm")) { - pr_err("%s: failed to request IO mem region\n", __func__); - return; - } - - pwm.base = ioremap(res.start, resource_size(&res)); + pwm.base = of_iomap(np, 0); if (!pwm.base) { pr_err("%s: failed to map PWM registers\n", __func__); - release_mem_region(res.start, resource_size(&res)); return; } -- cgit v1.2.3 From ceea124103c6f00561491b45133be5ccefc8ea1d Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Mon, 17 Jun 2013 02:10:24 +0200 Subject: clocksource: samsung_pwm_timer: Correct definition of AUTORELOAD bit PWM channel 4 has its autoreload bit located at different position. This patch fixes the driver to account for that. This fixes a problem with the clocksource hanging after it overflows because it is not reloaded any more. Signed-off-by: Tomasz Figa Reviewed-by: Sylwester Nawrocki Tested-by: Heiko Stuebner Tested-by: Mark Brown Tested-by: Sylwester Nawrocki Acked-by: Arnd Bergmann --- drivers/clocksource/samsung_pwm_timer.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/clocksource/samsung_pwm_timer.c') diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index 3fa5b07fa50d..5d0049f07af4 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -44,10 +44,21 @@ #define TCFG1_SHIFT(x) ((x) * 4) #define TCFG1_MUX_MASK 0xf +/* + * Each channel occupies 4 bits in TCON register, but there is a gap of 4 + * bits (one channel) after channel 0, so channels have different numbering + * when accessing TCON register. + * + * In addition, the location of autoreload bit for channel 4 (TCON channel 5) + * in its set of bits is 2 as opposed to 3 for other channels. + */ #define TCON_START(chan) (1 << (4 * (chan) + 0)) #define TCON_MANUALUPDATE(chan) (1 << (4 * (chan) + 1)) #define TCON_INVERT(chan) (1 << (4 * (chan) + 2)) -#define TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3)) +#define _TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3)) +#define _TCON_AUTORELOAD4(chan) (1 << (4 * (chan) + 2)) +#define TCON_AUTORELOAD(chan) \ + ((chan < 5) ? _TCON_AUTORELOAD(chan) : _TCON_AUTORELOAD4(chan)) DEFINE_SPINLOCK(samsung_pwm_lock); EXPORT_SYMBOL(samsung_pwm_lock); -- cgit v1.2.3 From 61d7e2056eb6147824a8689c6e6ab62396e26321 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Mon, 17 Jun 2013 00:07:03 +0200 Subject: clocksource: samsung_pwm_timer: Cache clocksource register address Instead of calculating register every time the timer should be read, we can just do it one time at initialization and store the address in driver data. Signed-off-by: Tomasz Figa Reviewed-by: Sylwester Nawrocki Tested-by: Heiko Stuebner Tested-by: Mark Brown Tested-by: Sylwester Nawrocki Acked-by: Arnd Bergmann --- drivers/clocksource/samsung_pwm_timer.c | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) (limited to 'drivers/clocksource/samsung_pwm_timer.c') diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index 5d0049f07af4..0c005f4fa296 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -65,6 +65,7 @@ EXPORT_SYMBOL(samsung_pwm_lock); struct samsung_pwm_clocksource { void __iomem *base; + void __iomem *source_reg; unsigned int irq[SAMSUNG_PWM_NUM]; struct samsung_pwm_variant variant; @@ -297,23 +298,6 @@ static void __init samsung_clockevent_init(void) } } -static void __iomem *samsung_timer_reg(void) -{ - switch (pwm.source_id) { - case 0: - case 1: - case 2: - case 3: - return pwm.base + pwm.source_id * 0x0c + 0x14; - - case 4: - return pwm.base + 0x40; - - default: - BUG(); - } -} - /* * Override the global weak sched_clock symbol with this * local implementation which uses the clocksource to get some @@ -323,17 +307,11 @@ static void __iomem *samsung_timer_reg(void) */ static u32 notrace samsung_read_sched_clock(void) { - void __iomem *reg = samsung_timer_reg(); - - if (!reg) - return 0; - - return ~__raw_readl(reg); + return ~__raw_readl(pwm.source_reg); } static void __init samsung_clocksource_init(void) { - void __iomem *reg = samsung_timer_reg(); unsigned long pclk; unsigned long clock_rate; int ret; @@ -348,10 +326,15 @@ static void __init samsung_clocksource_init(void) samsung_time_setup(pwm.source_id, pwm.tcnt_max); samsung_time_start(pwm.source_id, true); + if (pwm.source_id == 4) + pwm.source_reg = pwm.base + 0x40; + else + pwm.source_reg = pwm.base + pwm.source_id * 0x0c + 0x14; + setup_sched_clock(samsung_read_sched_clock, pwm.variant.bits, clock_rate); - ret = clocksource_mmio_init(reg, "samsung_clocksource_timer", + ret = clocksource_mmio_init(pwm.source_reg, "samsung_clocksource_timer", clock_rate, 250, pwm.variant.bits, clocksource_mmio_readl_down); if (ret) -- cgit v1.2.3 From 6792e636d5bfc1b26c25e7ed056b358e1144c6df Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Mon, 17 Jun 2013 00:13:06 +0200 Subject: clocksource: samsung_pwm_timer: Do not use clocksource_mmio In case of Samsung PWM timer, clocksource MMIO can not be used, because custom suspend/resume callbacks are required. Signed-off-by: Tomasz Figa Reviewed-by: Daniel Lezcano Reviewed-by: Sylwester Nawrocki Tested-by: Heiko Stuebner Tested-by: Mark Brown Tested-by: Sylwester Nawrocki Acked-by: Arnd Bergmann --- drivers/clocksource/samsung_pwm_timer.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/clocksource/samsung_pwm_timer.c') diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index 0c005f4fa296..b3112dc293ba 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -298,6 +298,18 @@ static void __init samsung_clockevent_init(void) } } +static cycle_t samsung_clocksource_read(struct clocksource *c) +{ + return ~readl_relaxed(pwm.source_reg); +} + +static struct clocksource samsung_clocksource = { + .name = "samsung_clocksource_timer", + .rating = 250, + .read = samsung_clocksource_read, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + /* * Override the global weak sched_clock symbol with this * local implementation which uses the clocksource to get some @@ -307,7 +319,7 @@ static void __init samsung_clockevent_init(void) */ static u32 notrace samsung_read_sched_clock(void) { - return ~__raw_readl(pwm.source_reg); + return samsung_clocksource_read(NULL); } static void __init samsung_clocksource_init(void) @@ -334,9 +346,8 @@ static void __init samsung_clocksource_init(void) setup_sched_clock(samsung_read_sched_clock, pwm.variant.bits, clock_rate); - ret = clocksource_mmio_init(pwm.source_reg, "samsung_clocksource_timer", - clock_rate, 250, pwm.variant.bits, - clocksource_mmio_readl_down); + samsung_clocksource.mask = CLOCKSOURCE_MASK(pwm.variant.bits); + ret = clocksource_register_hz(&samsung_clocksource, clock_rate); if (ret) panic("samsung_clocksource_timer: can't register clocksource\n"); } -- cgit v1.2.3 From 0b96258b420208ebaacc0ef4b21b67dba262badf Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Mon, 17 Jun 2013 01:11:31 +0200 Subject: clocksource: samsung_pwm_timer: Handle suspend/resume correctly Current suspend/resume handling of the driver was broken, because: - periodic timer was being enabled in CLOCK_EVT_MODE_RESUME mode, which does not seem to be correct behavior looking at other platforms, - PWM divisors need to be restored, but they were not, - clockevent interrupt mask needs to be restored, but it was not, - clocksource was being restored in clockevent resume callback. This patch fixes issues mentioned above, making suspend/resume handling in the driver correct. Signed-off-by: Tomasz Figa Reviewed-by: Sylwester Nawrocki Tested-by: Heiko Stuebner Tested-by: Mark Brown Tested-by: Sylwester Nawrocki Acked-by: Arnd Bergmann Acked-by: Daniel Lezcano --- drivers/clocksource/samsung_pwm_timer.c | 42 ++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 14 deletions(-) (limited to 'drivers/clocksource/samsung_pwm_timer.c') diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index b3112dc293ba..ac60f8b8a5f7 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -207,17 +207,6 @@ static int samsung_set_next_event(unsigned long cycles, return 0; } -static void samsung_timer_resume(void) -{ - /* event timer restart */ - samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1); - samsung_time_start(pwm.event_id, true); - - /* source timer restart */ - samsung_time_setup(pwm.source_id, pwm.tcnt_max); - samsung_time_start(pwm.source_id, true); -} - static void samsung_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { @@ -234,20 +223,29 @@ static void samsung_set_mode(enum clock_event_mode mode, case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: - break; - case CLOCK_EVT_MODE_RESUME: - samsung_timer_resume(); break; } } +static void samsung_clockevent_resume(struct clock_event_device *cev) +{ + samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div); + samsung_timer_set_divisor(pwm.event_id, pwm.tdiv); + + if (pwm.variant.has_tint_cstat) { + u32 mask = (1 << pwm.event_id); + writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT); + } +} + static struct clock_event_device time_event_device = { .name = "samsung_event_timer", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .rating = 200, .set_next_event = samsung_set_next_event, .set_mode = samsung_set_mode, + .resume = samsung_clockevent_resume, }; static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id) @@ -298,6 +296,20 @@ static void __init samsung_clockevent_init(void) } } +static void samsung_clocksource_suspend(struct clocksource *cs) +{ + samsung_time_stop(pwm.source_id); +} + +static void samsung_clocksource_resume(struct clocksource *cs) +{ + samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div); + samsung_timer_set_divisor(pwm.source_id, pwm.tdiv); + + samsung_time_setup(pwm.source_id, pwm.tcnt_max); + samsung_time_start(pwm.source_id, true); +} + static cycle_t samsung_clocksource_read(struct clocksource *c) { return ~readl_relaxed(pwm.source_reg); @@ -307,6 +319,8 @@ static struct clocksource samsung_clocksource = { .name = "samsung_clocksource_timer", .rating = 250, .read = samsung_clocksource_read, + .suspend = samsung_clocksource_suspend, + .resume = samsung_clocksource_resume, .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; -- cgit v1.2.3 From a1fa6f503aad8da91c4cc8dd0e71d2789d78d3f6 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Mon, 26 Aug 2013 19:08:58 +0200 Subject: clocksource: samsung_pwm_timer: Get clock from device tree When booting with device tree static clkdev aliases should not be used. This patch modifies the samsung_pwm_timer driver to use DT-based clock lookup when booting with device tree. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Sylwester Nawrocki Signed-off-by: Mike Turquette --- drivers/clocksource/samsung_pwm_timer.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/clocksource/samsung_pwm_timer.c') diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index 584b5472eea3..32950c3ed374 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -349,10 +349,6 @@ static void __init samsung_clocksource_init(void) static void __init samsung_timer_resources(void) { - pwm.timerclk = clk_get(NULL, "timers"); - if (IS_ERR(pwm.timerclk)) - panic("failed to get timers clock for timer"); - clk_prepare_enable(pwm.timerclk); pwm.tcnt_max = (1UL << pwm.variant.bits) - 1; @@ -397,6 +393,10 @@ void __init samsung_pwm_clocksource_init(void __iomem *base, memcpy(&pwm.variant, variant, sizeof(pwm.variant)); memcpy(pwm.irq, irqs, SAMSUNG_PWM_NUM * sizeof(*irqs)); + pwm.timerclk = clk_get(NULL, "timers"); + if (IS_ERR(pwm.timerclk)) + panic("failed to get timers clock for timer"); + _samsung_pwm_clocksource_init(); } @@ -437,6 +437,10 @@ static void __init samsung_pwm_alloc(struct device_node *np, return; } + pwm.timerclk = of_clk_get_by_name(np, "timers"); + if (IS_ERR(pwm.timerclk)) + panic("failed to get timers clock for timer"); + _samsung_pwm_clocksource_init(); } -- cgit v1.2.3