mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			244 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
--- 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);
 | 
						|
 }
 |