From 70adc3f32adc2fb90b0107c020678588e4cf9f51 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Aug 2012 20:26:42 +0200 Subject: [PATCH 1/4] ARM: ks8695: merge the timer header into the timer driver This is broadcasted in the entire kernel for no good reason, since it's only used by the timer driver. Merge it into the driver. Tested-by: Greg Ungerer Signed-off-by: Linus Walleij --- .../arm/mach-ks8695/include/mach/regs-timer.h | 40 ------------------- arch/arm/mach-ks8695/time.c | 21 +++++++++- 2 files changed, 20 insertions(+), 41 deletions(-) delete mode 100644 arch/arm/mach-ks8695/include/mach/regs-timer.h diff --git a/arch/arm/mach-ks8695/include/mach/regs-timer.h b/arch/arm/mach-ks8695/include/mach/regs-timer.h deleted file mode 100644 index e620cda99d2d..000000000000 --- a/arch/arm/mach-ks8695/include/mach/regs-timer.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * arch/arm/mach-ks8695/include/mach/regs-timer.h - * - * Copyright (C) 2006 Ben Dooks - * Copyright (C) 2006 Simtec Electronics - * - * KS8695 - Timer registers and bit definitions. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef KS8695_TIMER_H -#define KS8695_TIMER_H - -#define KS8695_TMR_OFFSET (0xF0000 + 0xE400) -#define KS8695_TMR_VA (KS8695_IO_VA + KS8695_TMR_OFFSET) -#define KS8695_TMR_PA (KS8695_IO_PA + KS8695_TMR_OFFSET) - - -/* - * Timer registers - */ -#define KS8695_TMCON (0x00) /* Timer Control Register */ -#define KS8695_T1TC (0x04) /* Timer 1 Timeout Count Register */ -#define KS8695_T0TC (0x08) /* Timer 0 Timeout Count Register */ -#define KS8695_T1PD (0x0C) /* Timer 1 Pulse Count Register */ -#define KS8695_T0PD (0x10) /* Timer 0 Pulse Count Register */ - - -/* Timer Control Register */ -#define TMCON_T1EN (1 << 1) /* Timer 1 Enable */ -#define TMCON_T0EN (1 << 0) /* Timer 0 Enable */ - -/* Timer0 Timeout Counter Register */ -#define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */ - - -#endif diff --git a/arch/arm/mach-ks8695/time.c b/arch/arm/mach-ks8695/time.c index ec783a3070ae..6427b7c2c9fa 100644 --- a/arch/arm/mach-ks8695/time.c +++ b/arch/arm/mach-ks8695/time.c @@ -29,11 +29,30 @@ #include #include -#include #include #include "generic.h" +#define KS8695_TMR_OFFSET (0xF0000 + 0xE400) +#define KS8695_TMR_VA (KS8695_IO_VA + KS8695_TMR_OFFSET) +#define KS8695_TMR_PA (KS8695_IO_PA + KS8695_TMR_OFFSET) + +/* + * Timer registers + */ +#define KS8695_TMCON (0x00) /* Timer Control Register */ +#define KS8695_T1TC (0x04) /* Timer 1 Timeout Count Register */ +#define KS8695_T0TC (0x08) /* Timer 0 Timeout Count Register */ +#define KS8695_T1PD (0x0C) /* Timer 1 Pulse Count Register */ +#define KS8695_T0PD (0x10) /* Timer 0 Pulse Count Register */ + +/* Timer Control Register */ +#define TMCON_T1EN (1 << 1) /* Timer 1 Enable */ +#define TMCON_T0EN (1 << 0) /* Timer 0 Enable */ + +/* Timer0 Timeout Counter Register */ +#define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */ + /* * Returns number of ms since last clock interrupt. Note that interrupts * will have been disabled by do_gettimeoffset() From 487748ca3eea6f951185b4c5d0b0c02eea4d053b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Aug 2012 20:27:02 +0200 Subject: [PATCH 2/4] ARM: ks8695: use [readl|writel]_relaxed() I have no clue why __raw* macros are used here, but I strongly suspect there is no good reason at all for this, so removing another bad example. Tested-by: Greg Ungerer Acked-by: Arnd Bergmann Signed-off-by: Linus Walleij --- arch/arm/mach-ks8695/time.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-ks8695/time.c b/arch/arm/mach-ks8695/time.c index 6427b7c2c9fa..ae9daccca4c1 100644 --- a/arch/arm/mach-ks8695/time.c +++ b/arch/arm/mach-ks8695/time.c @@ -67,11 +67,11 @@ static unsigned long ks8695_gettimeoffset (void) * interrupt. We solve this by ensuring that the counter has not * reloaded between our two reads. */ - elapsed = __raw_readl(KS8695_TMR_VA + KS8695_T1TC) + __raw_readl(KS8695_TMR_VA + KS8695_T1PD); + elapsed = readl_relaxed(KS8695_TMR_VA + KS8695_T1TC) + readl_relaxed(KS8695_TMR_VA + KS8695_T1PD); do { tick2 = elapsed; - intpending = __raw_readl(KS8695_IRQ_VA + KS8695_INTST) & (1 << KS8695_IRQ_TIMER1); - elapsed = __raw_readl(KS8695_TMR_VA + KS8695_T1TC) + __raw_readl(KS8695_TMR_VA + KS8695_T1PD); + intpending = readl_relaxed(KS8695_IRQ_VA + KS8695_INTST) & (1 << KS8695_IRQ_TIMER1); + elapsed = readl_relaxed(KS8695_TMR_VA + KS8695_T1TC) + readl_relaxed(KS8695_TMR_VA + KS8695_T1PD); } while (elapsed > tick2); /* Convert to number of ticks expired (not remaining) */ @@ -106,14 +106,14 @@ static void ks8695_timer_setup(void) unsigned long tmcon; /* disable timer1 */ - tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon & ~TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON); + tmcon = readl_relaxed(KS8695_TMR_VA + KS8695_TMCON); + writel_relaxed(tmcon & ~TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmout / 2, KS8695_TMR_VA + KS8695_T1TC); - __raw_writel(tmout / 2, KS8695_TMR_VA + KS8695_T1PD); + writel_relaxed(tmout / 2, KS8695_TMR_VA + KS8695_T1TC); + writel_relaxed(tmout / 2, KS8695_TMR_VA + KS8695_T1PD); /* re-enable timer1 */ - __raw_writel(tmcon | TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON); + writel_relaxed(tmcon | TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON); } static void __init ks8695_timer_init (void) @@ -138,12 +138,12 @@ void ks8695_restart(char mode, const char *cmd) soft_restart(0); /* disable timer0 */ - reg = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(reg & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); + reg = readl_relaxed(KS8695_TMR_VA + KS8695_TMCON); + writel_relaxed(reg & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); /* enable watchdog mode */ - __raw_writel((10 << 8) | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC); + writel_relaxed((10 << 8) | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC); /* re-enable timer0 */ - __raw_writel(reg | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); + writel_relaxed(reg | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); } From d7dda9875b84eb6c2828592b17aa173ac17bf75d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Aug 2012 20:27:12 +0200 Subject: [PATCH 3/4] ARM: ks8695: delete resume hook from timer This system does not support suspend/resume so let's skip this hook altogether. Tested-by: Greg Ungerer Signed-off-by: Linus Walleij --- arch/arm/mach-ks8695/time.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-ks8695/time.c b/arch/arm/mach-ks8695/time.c index ae9daccca4c1..6974c2355601 100644 --- a/arch/arm/mach-ks8695/time.c +++ b/arch/arm/mach-ks8695/time.c @@ -127,7 +127,6 @@ static void __init ks8695_timer_init (void) struct sys_timer ks8695_timer = { .init = ks8695_timer_init, .offset = ks8695_gettimeoffset, - .resume = ks8695_timer_setup, }; void ks8695_restart(char mode, const char *cmd) From c7e783d6adc7798307e7063e11f4127117446d5a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 29 Aug 2012 20:27:22 +0200 Subject: [PATCH 4/4] ARM: ks8695: convert to generic time and clocksource Old platforms using ancient gettimeoffset() and other arcane APIs are standing in the way of cleaning up the ARM kernel. The gettimeoffset() was also broken: it would try to read out the timer counter value, while this would not work (the counter statically returns the initially programmed value) so the implementation would anyway fall back to a homebrew version of jiffie calculation. This is an attempt at blind-coding a generic time and clocksource driver for the platform by way of a datasheet and looking at the old code. Tested-by: Greg Ungerer Signed-off-by: Linus Walleij --- arch/arm/Kconfig | 3 +- arch/arm/mach-ks8695/time.c | 99 ++++++++++++++++++++++++------------- 2 files changed, 66 insertions(+), 36 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 6d6e18fee9fe..e9ce0387f3d7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -652,8 +652,9 @@ config ARCH_KS8695 bool "Micrel/Kendin KS8695" select CPU_ARM922T select ARCH_REQUIRE_GPIOLIB - select ARCH_USES_GETTIMEOFFSET select NEED_MACH_MEMORY_H + select CLKSRC_MMIO + select GENERIC_CLOCKEVENTS help Support for Micrel/Kendin KS8695 "Centaur" (ARM922T) based System-on-Chip devices. diff --git a/arch/arm/mach-ks8695/time.c b/arch/arm/mach-ks8695/time.c index 6974c2355601..46c84bc7792c 100644 --- a/arch/arm/mach-ks8695/time.c +++ b/arch/arm/mach-ks8695/time.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -53,44 +54,69 @@ /* Timer0 Timeout Counter Register */ #define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */ -/* - * Returns number of ms since last clock interrupt. Note that interrupts - * will have been disabled by do_gettimeoffset() - */ -static unsigned long ks8695_gettimeoffset (void) +static void ks8695_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) { - unsigned long elapsed, tick2, intpending; + u32 tmcon; - /* - * Get the current number of ticks. Note that there is a race - * condition between us reading the timer and checking for an - * interrupt. We solve this by ensuring that the counter has not - * reloaded between our two reads. - */ - elapsed = readl_relaxed(KS8695_TMR_VA + KS8695_T1TC) + readl_relaxed(KS8695_TMR_VA + KS8695_T1PD); - do { - tick2 = elapsed; - intpending = readl_relaxed(KS8695_IRQ_VA + KS8695_INTST) & (1 << KS8695_IRQ_TIMER1); - elapsed = readl_relaxed(KS8695_TMR_VA + KS8695_T1TC) + readl_relaxed(KS8695_TMR_VA + KS8695_T1PD); - } while (elapsed > tick2); + if (mode == CLOCK_EVT_FEAT_PERIODIC) { + u32 rate = DIV_ROUND_CLOSEST(KS8695_CLOCK_RATE, HZ); + u32 half = DIV_ROUND_CLOSEST(rate, 2); - /* Convert to number of ticks expired (not remaining) */ - elapsed = (CLOCK_TICK_RATE / HZ) - elapsed; + /* Disable timer 1 */ + tmcon = readl_relaxed(KS8695_TMR_VA + KS8695_TMCON); + tmcon &= ~TMCON_T1EN; + writel_relaxed(tmcon, KS8695_TMR_VA + KS8695_TMCON); - /* Is interrupt pending? If so, then timer has been reloaded already. */ - if (intpending) - elapsed += (CLOCK_TICK_RATE / HZ); + /* Both registers need to count down */ + writel_relaxed(half, KS8695_TMR_VA + KS8695_T1TC); + writel_relaxed(half, KS8695_TMR_VA + KS8695_T1PD); - /* Convert ticks to usecs */ - return (unsigned long)(elapsed * (tick_nsec / 1000)) / LATCH; + /* Re-enable timer1 */ + tmcon |= TMCON_T1EN; + writel_relaxed(tmcon, KS8695_TMR_VA + KS8695_TMCON); + } } +static int ks8695_set_next_event(unsigned long cycles, + struct clock_event_device *evt) + +{ + u32 half = DIV_ROUND_CLOSEST(cycles, 2); + u32 tmcon; + + /* Disable timer 1 */ + tmcon = readl_relaxed(KS8695_TMR_VA + KS8695_TMCON); + tmcon &= ~TMCON_T1EN; + writel_relaxed(tmcon, KS8695_TMR_VA + KS8695_TMCON); + + /* Both registers need to count down */ + writel_relaxed(half, KS8695_TMR_VA + KS8695_T1TC); + writel_relaxed(half, KS8695_TMR_VA + KS8695_T1PD); + + /* Re-enable timer1 */ + tmcon |= TMCON_T1EN; + writel_relaxed(tmcon, KS8695_TMR_VA + KS8695_TMCON); + + return 0; +} + +static struct clock_event_device clockevent_ks8695 = { + .name = "ks8695_t1tc", + .rating = 300, /* Reasonably fast and accurate clock event */ + .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, + .set_next_event = ks8695_set_next_event, + .set_mode = ks8695_set_mode, +}; + /* * IRQ handler for the timer. */ static irqreturn_t ks8695_timer_interrupt(int irq, void *dev_id) { - timer_tick(); + struct clock_event_device *evt = &clockevent_ks8695; + + evt->event_handler(evt); return IRQ_HANDLED; } @@ -102,18 +128,22 @@ static struct irqaction ks8695_timer_irq = { static void ks8695_timer_setup(void) { - unsigned long tmout = CLOCK_TICK_RATE / HZ; unsigned long tmcon; - /* disable timer1 */ + /* Disable timer 0 and 1 */ tmcon = readl_relaxed(KS8695_TMR_VA + KS8695_TMCON); - writel_relaxed(tmcon & ~TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON); + tmcon &= ~TMCON_T0EN; + tmcon &= ~TMCON_T1EN; + writel_relaxed(tmcon, KS8695_TMR_VA + KS8695_TMCON); - writel_relaxed(tmout / 2, KS8695_TMR_VA + KS8695_T1TC); - writel_relaxed(tmout / 2, KS8695_TMR_VA + KS8695_T1PD); - - /* re-enable timer1 */ - writel_relaxed(tmcon | TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON); + /* + * Use timer 1 to fire IRQs on the timeline, minimum 2 cycles + * (one on each counter) maximum 2*2^32, but the API will only + * accept up to a 32bit full word (0xFFFFFFFFU). + */ + clockevents_config_and_register(&clockevent_ks8695, + KS8695_CLOCK_RATE, 2, + 0xFFFFFFFFU); } static void __init ks8695_timer_init (void) @@ -126,7 +156,6 @@ static void __init ks8695_timer_init (void) struct sys_timer ks8695_timer = { .init = ks8695_timer_init, - .offset = ks8695_gettimeoffset, }; void ks8695_restart(char mode, const char *cmd)