|
@@ -54,3 +54,136 @@ static bool arch_timer_use_virtual = true;
|
|
|
|
|
|
#define ARCH_TIMER_REG_CTRL 0
|
|
|
#define ARCH_TIMER_REG_FREQ 1
|
|
|
+#define ARCH_TIMER_REG_TVAL 2
|
|
|
+
|
|
|
+#define ARCH_TIMER_PHYS_ACCESS 0
|
|
|
+#define ARCH_TIMER_VIRT_ACCESS 1
|
|
|
+
|
|
|
+/*
|
|
|
+ * These register accessors are marked inline so the compiler can
|
|
|
+ * nicely work out which register we want, and chuck away the rest of
|
|
|
+ * the code. At least it does so with a recent GCC (4.6.3).
|
|
|
+ */
|
|
|
+static inline void arch_timer_reg_write(const int access, const int reg, u32 val)
|
|
|
+{
|
|
|
+ if (access == ARCH_TIMER_PHYS_ACCESS) {
|
|
|
+ switch (reg) {
|
|
|
+ case ARCH_TIMER_REG_CTRL:
|
|
|
+ asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
|
|
|
+ break;
|
|
|
+ case ARCH_TIMER_REG_TVAL:
|
|
|
+ asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (access == ARCH_TIMER_VIRT_ACCESS) {
|
|
|
+ switch (reg) {
|
|
|
+ case ARCH_TIMER_REG_CTRL:
|
|
|
+ asm volatile("mcr p15, 0, %0, c14, c3, 1" : : "r" (val));
|
|
|
+ break;
|
|
|
+ case ARCH_TIMER_REG_TVAL:
|
|
|
+ asm volatile("mcr p15, 0, %0, c14, c3, 0" : : "r" (val));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ isb();
|
|
|
+}
|
|
|
+
|
|
|
+static inline u32 arch_timer_reg_read(const int access, const int reg)
|
|
|
+{
|
|
|
+ u32 val = 0;
|
|
|
+
|
|
|
+ if (access == ARCH_TIMER_PHYS_ACCESS) {
|
|
|
+ switch (reg) {
|
|
|
+ case ARCH_TIMER_REG_CTRL:
|
|
|
+ asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
|
|
|
+ break;
|
|
|
+ case ARCH_TIMER_REG_TVAL:
|
|
|
+ asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
|
|
|
+ break;
|
|
|
+ case ARCH_TIMER_REG_FREQ:
|
|
|
+ asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (access == ARCH_TIMER_VIRT_ACCESS) {
|
|
|
+ switch (reg) {
|
|
|
+ case ARCH_TIMER_REG_CTRL:
|
|
|
+ asm volatile("mrc p15, 0, %0, c14, c3, 1" : "=r" (val));
|
|
|
+ break;
|
|
|
+ case ARCH_TIMER_REG_TVAL:
|
|
|
+ asm volatile("mrc p15, 0, %0, c14, c3, 0" : "=r" (val));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return val;
|
|
|
+}
|
|
|
+
|
|
|
+static inline cycle_t arch_timer_counter_read(const int access)
|
|
|
+{
|
|
|
+ cycle_t cval = 0;
|
|
|
+
|
|
|
+ if (access == ARCH_TIMER_PHYS_ACCESS)
|
|
|
+ asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval));
|
|
|
+
|
|
|
+ if (access == ARCH_TIMER_VIRT_ACCESS)
|
|
|
+ asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cval));
|
|
|
+
|
|
|
+ return cval;
|
|
|
+}
|
|
|
+
|
|
|
+static inline cycle_t arch_counter_get_cntpct(void)
|
|
|
+{
|
|
|
+ return arch_timer_counter_read(ARCH_TIMER_PHYS_ACCESS);
|
|
|
+}
|
|
|
+
|
|
|
+static inline cycle_t arch_counter_get_cntvct(void)
|
|
|
+{
|
|
|
+ return arch_timer_counter_read(ARCH_TIMER_VIRT_ACCESS);
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t inline timer_handler(const int access,
|
|
|
+ struct clock_event_device *evt)
|
|
|
+{
|
|
|
+ unsigned long ctrl;
|
|
|
+ ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL);
|
|
|
+ if (ctrl & ARCH_TIMER_CTRL_IT_STAT) {
|
|
|
+ ctrl |= ARCH_TIMER_CTRL_IT_MASK;
|
|
|
+ arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl);
|
|
|
+ evt->event_handler(evt);
|
|
|
+ return IRQ_HANDLED;
|
|
|
+ }
|
|
|
+
|
|
|
+ return IRQ_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t arch_timer_handler_virt(int irq, void *dev_id)
|
|
|
+{
|
|
|
+ struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
|
|
|
+
|
|
|
+ return timer_handler(ARCH_TIMER_VIRT_ACCESS, evt);
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t arch_timer_handler_phys(int irq, void *dev_id)
|
|
|
+{
|
|
|
+ struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
|
|
|
+
|
|
|
+ return timer_handler(ARCH_TIMER_PHYS_ACCESS, evt);
|
|
|
+}
|
|
|
+
|
|
|
+static inline void timer_set_mode(const int access, int mode)
|
|
|
+{
|
|
|
+ unsigned long ctrl;
|
|
|
+ switch (mode) {
|
|
|
+ case CLOCK_EVT_MODE_UNUSED:
|
|
|
+ case CLOCK_EVT_MODE_SHUTDOWN:
|
|
|
+ ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL);
|
|
|
+ ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
|
|
|
+ arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|