Skip to content

Commit

Permalink
sparc32, leon: Remove separate "ticker" timer for SMP
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
andreas-gaisler authored and davem330 committed Jun 19, 2013
1 parent 117a0c5 commit 1ffbc51
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 38 deletions.
2 changes: 1 addition & 1 deletion arch/sparc/include/asm/leon.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions arch/sparc/include/asm/leon_amba.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
54 changes: 17 additions & 37 deletions arch/sparc/kernel/leon_kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -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;
Expand All @@ -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");
Expand Down

0 comments on commit 1ffbc51

Please sign in to comment.