gemini: add 3.18 support
[openwrt.git] / target / linux / gemini / patches-3.18 / 160-gemini-timers.patch
diff --git a/target/linux/gemini/patches-3.18/160-gemini-timers.patch b/target/linux/gemini/patches-3.18/160-gemini-timers.patch
new file mode 100644 (file)
index 0000000..0625135
--- /dev/null
@@ -0,0 +1,243 @@
+--- a/arch/arm/mach-gemini/time.c      2015-03-25 23:06:03.188317455 +0200
++++ b/arch/arm/mach-gemini/time.c      2015-03-25 23:06:24.417315486 +0200
+@@ -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 @@
+       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 struct irqaction gemini_timer_irq = {
+-      .name           = "Gemini Timer Tick",
+-      .flags          = IRQF_TIMER,
+-      .handler        = gemini_timer_interrupt,
++      .name           = "gemini timer 1",
++      .flags          = IRQF_DISABLED | IRQF_TIMER,
++      .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 @@
+       }
+       /*
+-       * 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);
+ }