sparc32, leon: Remove separate "ticker" timer for SMP
authorAndreas Larsson <andreas@gaisler.com>
Mon, 10 Jun 2013 06:53:28 +0000 (08:53 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 19 Jun 2013 09:10:29 +0000 (02:10 -0700)
This reduces the need from two timers to one timer.

Moreover, without this patch, when the "ticker" timer triggers timer_cs_read via
tick_periodic it reads the value of the usual timer it can get an wrapped timer
value without timer_cs_internal_counter having been updated leading to the clock
going backwards. This effectively hangs one cpu that gets stuck in
update_wall_time with an offset slightly smaller than 0xffffffffffffffff.

Signed-off-by: Andreas Larsson <andreas@gaisler.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
arch/sparc/include/asm/leon.h
arch/sparc/include/asm/leon_amba.h
arch/sparc/kernel/leon_kernel.c

index 15a7169..b836e92 100644 (file)
@@ -135,7 +135,7 @@ static inline int sparc_leon3_cpuid(void)
 
 #ifdef CONFIG_SMP
 # define LEON3_IRQ_IPI_DEFAULT         13
-# define LEON3_IRQ_TICKER              (leon3_ticker_irq)
+# define LEON3_IRQ_TICKER              (leon3_gptimer_irq)
 # define LEON3_IRQ_CROSS_CALL          15
 #endif
 
index f3034ed..24ec48c 100644 (file)
@@ -47,6 +47,7 @@ struct amba_prom_registers {
 #define LEON3_GPTIMER_LD 4
 #define LEON3_GPTIMER_IRQEN 8
 #define LEON3_GPTIMER_SEPIRQ 8
+#define LEON3_GPTIMER_TIMERS 0x7
 
 #define LEON23_REG_TIMER_CONTROL_EN    0x00000001 /* 1 = enable counting */
 /* 0 = hold scalar and counter */
index 7c0231d..b7c6897 100644 (file)
@@ -38,7 +38,6 @@ static DEFINE_SPINLOCK(leon_irq_lock);
 
 unsigned long leon3_gptimer_irq; /* interrupt controller irq number */
 unsigned long leon3_gptimer_idx; /* Timer Index (0..6) within Timer Core */
-int leon3_ticker_irq; /* Timer ticker IRQ */
 unsigned int sparc_leon_eirq;
 #define LEON_IMASK(cpu) (&leon3_irqctrl_regs->mask[cpu])
 #define LEON_IACK (&leon3_irqctrl_regs->iclear)
@@ -278,6 +277,9 @@ irqreturn_t leon_percpu_timer_ce_interrupt(int irq, void *unused)
 
        leon_clear_profile_irq(cpu);
 
+       if (cpu == boot_cpu_id)
+               timer_interrupt(irq, NULL);
+
        ce = &per_cpu(sparc32_clockevent, cpu);
 
        irq_enter();
@@ -299,6 +301,7 @@ void __init leon_init_timers(void)
        int icsel;
        int ampopts;
        int err;
+       u32 config;
 
        sparc_config.get_cycles_offset = leon_cycles_offset;
        sparc_config.cs_period = 1000000 / HZ;
@@ -377,23 +380,6 @@ void __init leon_init_timers(void)
        LEON3_BYPASS_STORE_PA(
                        &leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl, 0);
 
-#ifdef CONFIG_SMP
-       leon3_ticker_irq = leon3_gptimer_irq + 1 + leon3_gptimer_idx;
-
-       if (!(LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->config) &
-             (1<<LEON3_GPTIMER_SEPIRQ))) {
-               printk(KERN_ERR "timer not configured with separate irqs\n");
-               BUG();
-       }
-
-       LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].val,
-                               0);
-       LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].rld,
-                               (((1000000/HZ) - 1)));
-       LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl,
-                               0);
-#endif
-
        /*
         * The IRQ controller may (if implemented) consist of multiple
         * IRQ controllers, each mapped on a 4Kb boundary.
@@ -416,13 +402,6 @@ void __init leon_init_timers(void)
        if (eirq != 0)
                leon_eirq_setup(eirq);
 
-       irq = _leon_build_device_irq(NULL, leon3_gptimer_irq+leon3_gptimer_idx);
-       err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL);
-       if (err) {
-               printk(KERN_ERR "unable to attach timer IRQ%d\n", irq);
-               prom_halt();
-       }
-
 #ifdef CONFIG_SMP
        {
                unsigned long flags;
@@ -439,30 +418,31 @@ void __init leon_init_timers(void)
        }
 #endif
 
-       LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl,
-                             LEON3_GPTIMER_EN |
-                             LEON3_GPTIMER_RL |
-                             LEON3_GPTIMER_LD |
-                             LEON3_GPTIMER_IRQEN);
+       config = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->config);
+       if (config & (1 << LEON3_GPTIMER_SEPIRQ))
+               leon3_gptimer_irq += leon3_gptimer_idx;
+       else if ((config & LEON3_GPTIMER_TIMERS) > 1)
+               pr_warn("GPTIMER uses shared irqs, using other timers of the same core will fail.\n");
 
 #ifdef CONFIG_SMP
        /* Install per-cpu IRQ handler for broadcasted ticker */
-       irq = leon_build_device_irq(leon3_ticker_irq, handle_percpu_irq,
+       irq = leon_build_device_irq(leon3_gptimer_irq, handle_percpu_irq,
                                    "per-cpu", 0);
        err = request_irq(irq, leon_percpu_timer_ce_interrupt,
-                         IRQF_PERCPU | IRQF_TIMER, "ticker",
-                         NULL);
+                         IRQF_PERCPU | IRQF_TIMER, "timer", NULL);
+#else
+       irq = _leon_build_device_irq(NULL, leon3_gptimer_irq);
+       err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL);
+#endif
        if (err) {
-               printk(KERN_ERR "unable to attach ticker IRQ%d\n", irq);
+               pr_err("Unable to attach timer IRQ%d\n", irq);
                prom_halt();
        }
-
-       LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx+1].ctrl,
+       LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].ctrl,
                              LEON3_GPTIMER_EN |
                              LEON3_GPTIMER_RL |
                              LEON3_GPTIMER_LD |
                              LEON3_GPTIMER_IRQEN);
-#endif
        return;
 bad:
        printk(KERN_ERR "No Timer/irqctrl found\n");