diff options
Diffstat (limited to 'target/linux/gemini/patches-4.1/160-gemini-timers.patch')
-rw-r--r-- | target/linux/gemini/patches-4.1/160-gemini-timers.patch | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/target/linux/gemini/patches-4.1/160-gemini-timers.patch b/target/linux/gemini/patches-4.1/160-gemini-timers.patch new file mode 100644 index 0000000..d7c8f86 --- /dev/null +++ b/target/linux/gemini/patches-4.1/160-gemini-timers.patch @@ -0,0 +1,242 @@ +--- a/arch/arm/mach-gemini/time.c ++++ b/arch/arm/mach-gemini/time.c +@@ -15,15 +15,18 @@ + #include <asm/mach/time.h> + #include <linux/clockchips.h> + #include <linux/clocksource.h> ++#include <linux/sched_clock.h> + + /* + * Register definitions for the timers + */ +-#define TIMER_COUNT(BASE_ADDR) (BASE_ADDR + 0x00) +-#define TIMER_LOAD(BASE_ADDR) (BASE_ADDR + 0x04) +-#define TIMER_MATCH1(BASE_ADDR) (BASE_ADDR + 0x08) +-#define TIMER_MATCH2(BASE_ADDR) (BASE_ADDR + 0x0C) +-#define TIMER_CR(BASE_ADDR) (BASE_ADDR + 0x30) ++#define TIMER_COUNT(BASE_ADDR) (IO_ADDRESS(BASE_ADDR) + 0x00) ++#define TIMER_LOAD(BASE_ADDR) (IO_ADDRESS(BASE_ADDR) + 0x04) ++#define TIMER_MATCH1(BASE_ADDR) (IO_ADDRESS(BASE_ADDR) + 0x08) ++#define TIMER_MATCH2(BASE_ADDR) (IO_ADDRESS(BASE_ADDR) + 0x0C) ++#define TIMER_CR(BASE_ADDR) (IO_ADDRESS(BASE_ADDR) + 0x30) ++#define TIMER_INTR_STATE(BASE_ADDR) (IO_ADDRESS(BASE_ADDR) + 0x34) ++#define TIMER_INTR_MASK(BASE_ADDR) (IO_ADDRESS(BASE_ADDR) + 0x38) + + #define TIMER_1_CR_ENABLE (1 << 0) + #define TIMER_1_CR_CLOCK (1 << 1) +@@ -34,27 +37,38 @@ + #define TIMER_3_CR_ENABLE (1 << 6) + #define TIMER_3_CR_CLOCK (1 << 7) + #define TIMER_3_CR_INT (1 << 8) ++#define TIMER_1_CR_UPDOWN (1 << 9) ++#define TIMER_2_CR_UPDOWN (1 << 10) ++#define TIMER_3_CR_UPDOWN (1 << 11) ++ ++#define TIMER_1_INT_MATCH1 (1 << 0) ++#define TIMER_1_INT_MATCH2 (1 << 1) ++#define TIMER_1_INT_OVERFLOW (1 << 2) ++#define TIMER_2_INT_MATCH1 (1 << 3) ++#define TIMER_2_INT_MATCH2 (1 << 4) ++#define TIMER_2_INT_OVERFLOW (1 << 5) ++#define TIMER_3_INT_MATCH1 (1 << 6) ++#define TIMER_3_INT_MATCH2 (1 << 7) ++#define TIMER_3_INT_OVERFLOW (1 << 8) ++#define TIMER_INT_ALL_MASK 0x1ff + + static unsigned int tick_rate; + ++static u64 notrace gemini_read_sched_clock(void) ++{ ++ return readl(TIMER_COUNT(GEMINI_TIMER3_BASE)); ++} ++ + static int gemini_timer_set_next_event(unsigned long cycles, + struct clock_event_device *evt) + { + u32 cr; + +- cr = readl(TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); +- +- /* This may be overdoing it, feel free to test without this */ +- cr &= ~TIMER_2_CR_ENABLE; +- cr &= ~TIMER_2_CR_INT; +- writel(cr, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); +- +- /* Set next event */ +- writel(cycles, TIMER_COUNT(IO_ADDRESS(GEMINI_TIMER2_BASE))); +- writel(cycles, TIMER_LOAD(IO_ADDRESS(GEMINI_TIMER2_BASE))); +- cr |= TIMER_2_CR_ENABLE; +- cr |= TIMER_2_CR_INT; +- writel(cr, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); ++ /* Setup the match register */ ++ cr = readl(TIMER_COUNT(GEMINI_TIMER1_BASE)); ++ writel(cr + cycles, TIMER_MATCH1(GEMINI_TIMER1_BASE)); ++ if (readl(TIMER_COUNT(GEMINI_TIMER1_BASE)) - cr > cycles) ++ return -ETIME; + + return 0; + } +@@ -66,48 +80,68 @@ static void gemini_timer_set_mode(enum c + u32 cr; + + switch (mode) { +- case CLOCK_EVT_MODE_PERIODIC: +- /* Start the timer */ +- writel(period, +- TIMER_COUNT(IO_ADDRESS(GEMINI_TIMER2_BASE))); +- writel(period, +- TIMER_LOAD(IO_ADDRESS(GEMINI_TIMER2_BASE))); +- cr = readl(TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); +- cr |= TIMER_2_CR_ENABLE; +- cr |= TIMER_2_CR_INT; +- writel(cr, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); ++ case CLOCK_EVT_MODE_PERIODIC: ++ /* Stop timer and interrupt. */ ++ cr = readl(TIMER_CR(GEMINI_TIMER_BASE)); ++ cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT); ++ writel(cr, TIMER_CR(GEMINI_TIMER_BASE)); ++ ++ /* Setup timer to fire at 1/HZ intervals. */ ++ cr = 0xffffffff - (period - 1); ++ writel(cr, TIMER_COUNT(GEMINI_TIMER1_BASE)); ++ writel(cr, TIMER_LOAD(GEMINI_TIMER1_BASE)); ++ ++ /* enable interrupt on overflaw */ ++ cr = readl(TIMER_INTR_MASK(GEMINI_TIMER_BASE)); ++ cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2); ++ cr |= TIMER_1_INT_OVERFLOW; ++ writel(cr, TIMER_INTR_MASK(GEMINI_TIMER_BASE)); ++ ++ /* start the timer */ ++ cr = readl(TIMER_CR(GEMINI_TIMER_BASE)); ++ cr |= TIMER_1_CR_ENABLE | TIMER_1_CR_INT; ++ writel(cr, TIMER_CR(GEMINI_TIMER_BASE)); + break; ++ + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_UNUSED: +- case CLOCK_EVT_MODE_SHUTDOWN: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ /* Stop timer and interrupt. */ ++ cr = readl(TIMER_CR(GEMINI_TIMER_BASE)); ++ cr &= ~(TIMER_1_CR_ENABLE | TIMER_1_CR_INT); ++ writel(cr, TIMER_CR(GEMINI_TIMER_BASE)); ++ ++ /* Setup counter start from 0 */ ++ writel(0, TIMER_COUNT(GEMINI_TIMER1_BASE)); ++ writel(0, TIMER_LOAD(GEMINI_TIMER1_BASE)); ++ ++ /* enable interrupt */ ++ cr = readl(TIMER_INTR_MASK(GEMINI_TIMER_BASE)); ++ cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2); ++ cr |= TIMER_1_INT_MATCH1; ++ writel(cr, TIMER_INTR_MASK(GEMINI_TIMER_BASE)); ++ ++ /* start the timer */ ++ cr = readl(TIMER_CR(GEMINI_TIMER_BASE)); ++ cr |= TIMER_1_CR_ENABLE; ++ writel(cr, TIMER_CR(GEMINI_TIMER_BASE)); ++ break; ++ + case CLOCK_EVT_MODE_RESUME: +- /* +- * Disable also for oneshot: the set_next() call will +- * arm the timer instead. +- */ +- cr = readl(TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); +- cr &= ~TIMER_2_CR_ENABLE; +- cr &= ~TIMER_2_CR_INT; +- writel(cr, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); + break; +- default: +- break; + } + } + +-/* Use TIMER2 as clock event */ + static struct clock_event_device gemini_clockevent = { +- .name = "TIMER2", +- .rating = 300, /* Reasonably fast and accurate clock event */ +- .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, +- .set_next_event = gemini_timer_set_next_event, +- .set_mode = gemini_timer_set_mode, ++ .name = "gemini_timer_1", ++ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, ++ .shift = 32, ++ .rating = 300, ++ .set_next_event = gemini_timer_set_next_event, ++ .set_mode = gemini_timer_set_mode, + }; + +-/* +- * IRQ handler for the timer +- */ +-static irqreturn_t gemini_timer_interrupt(int irq, void *dev_id) ++static irqreturn_t gemini_timer_intr(int irq, void *dev_id) + { + struct clock_event_device *evt = &gemini_clockevent; + +@@ -116,14 +150,11 @@ static irqreturn_t gemini_timer_interrup + } + + static struct irqaction gemini_timer_irq = { +- .name = "Gemini Timer Tick", ++ .name = "gemini timer 1", + .flags = IRQF_TIMER, +- .handler = gemini_timer_interrupt, ++ .handler = gemini_timer_intr, + }; + +-/* +- * Set up timer interrupt, and return the current time in seconds. +- */ + void __init gemini_timer_init(void) + { + u32 reg_v; +@@ -151,20 +182,35 @@ void __init gemini_timer_init(void) + } + + /* +- * Make irqs happen for the system timer ++ * Reset the interrupt mask and status + */ +- setup_irq(IRQ_TIMER2, &gemini_timer_irq); ++ writel(TIMER_INT_ALL_MASK, TIMER_INTR_MASK(GEMINI_TIMER_BASE)); ++ writel(0, TIMER_INTR_STATE(GEMINI_TIMER_BASE)); ++ writel(TIMER_1_CR_UPDOWN | TIMER_3_CR_ENABLE | TIMER_3_CR_UPDOWN, ++ TIMER_CR(GEMINI_TIMER_BASE)); + +- /* Enable and use TIMER1 as clock source */ +- writel(0xffffffff, TIMER_COUNT(IO_ADDRESS(GEMINI_TIMER1_BASE))); +- writel(0xffffffff, TIMER_LOAD(IO_ADDRESS(GEMINI_TIMER1_BASE))); +- writel(TIMER_1_CR_ENABLE, TIMER_CR(IO_ADDRESS(GEMINI_TIMER_BASE))); +- if (clocksource_mmio_init(TIMER_COUNT(IO_ADDRESS(GEMINI_TIMER1_BASE)), +- "TIMER1", tick_rate, 300, 32, +- clocksource_mmio_readl_up)) +- pr_err("timer: failed to initialize gemini clock source\n"); ++ /* ++ * Setup free-running clocksource timer (interrupts ++ * disabled.) ++ */ ++ writel(0, TIMER_COUNT(GEMINI_TIMER3_BASE)); ++ writel(0, TIMER_LOAD(GEMINI_TIMER3_BASE)); ++ writel(0, TIMER_MATCH1(GEMINI_TIMER3_BASE)); ++ writel(0, TIMER_MATCH2(GEMINI_TIMER3_BASE)); ++ clocksource_mmio_init(TIMER_COUNT(GEMINI_TIMER3_BASE), ++ "gemini_clocksource", tick_rate, ++ 300, 32, clocksource_mmio_readl_up); ++ sched_clock_register(gemini_read_sched_clock, 32, tick_rate); + +- /* Configure and register the clockevent */ ++ /* ++ * Setup clockevent timer (interrupt-driven.) ++ */ ++ writel(0, TIMER_COUNT(GEMINI_TIMER1_BASE)); ++ writel(0, TIMER_LOAD(GEMINI_TIMER1_BASE)); ++ writel(0, TIMER_MATCH1(GEMINI_TIMER1_BASE)); ++ writel(0, TIMER_MATCH2(GEMINI_TIMER1_BASE)); ++ setup_irq(IRQ_TIMER1, &gemini_timer_irq); ++ gemini_clockevent.cpumask = cpumask_of(0); + clockevents_config_and_register(&gemini_clockevent, tick_rate, + 1, 0xffffffff); + } |