[ARM] smp: move core localtimer support out of platform specific files

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
Russell King
2009-05-17 18:58:34 +01:00
committed by Russell King
parent ee348d5a1d
commit bc28248ee2
6 changed files with 121 additions and 111 deletions

View File

@@ -859,6 +859,7 @@ source "kernel/time/Kconfig"
config SMP config SMP
bool "Symmetric Multi-Processing (EXPERIMENTAL)" bool "Symmetric Multi-Processing (EXPERIMENTAL)"
depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP) depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP)
depends on GENERIC_CLOCKEVENTS
select USE_GENERIC_SMP_HELPERS select USE_GENERIC_SMP_HELPERS
help help
This enables support for systems with more than one CPU. If you have This enables support for systems with more than one CPU. If you have

View File

@@ -0,0 +1,51 @@
/*
* arch/arm/include/asm/localtimer.h
*
* Copyright (C) 2004-2005 ARM Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __ASM_ARM_LOCALTIMER_H
#define __ASM_ARM_LOCALTIMER_H
struct clock_event_device;
/*
* Setup a per-cpu timer, whether it be a local timer or dummy broadcast
*/
void percpu_timer_setup(void);
/*
* Called from assembly, this is the local timer IRQ handler
*/
asmlinkage void do_local_timer(struct pt_regs *);
#ifdef CONFIG_LOCAL_TIMERS
/*
* Platform provides this to acknowledge a local timer IRQ.
* Returns true if the local timer IRQ is to be processed.
*/
int local_timer_ack(void);
/*
* Stop a local timer interrupt.
*/
void local_timer_stop(void);
/*
* Setup a local timer interrupt for a CPU.
*/
void local_timer_setup(struct clock_event_device *);
#else
static inline void local_timer_stop(void)
{
}
#endif
#endif

View File

@@ -55,11 +55,6 @@ extern void smp_store_cpu_info(unsigned int cpuid);
*/ */
extern void smp_cross_call(const struct cpumask *mask); extern void smp_cross_call(const struct cpumask *mask);
/*
* Broadcast a clock event to other CPUs.
*/
extern void smp_timer_broadcast(const struct cpumask *mask);
/* /*
* Boot a secondary CPU, and assign it the specified idle task. * Boot a secondary CPU, and assign it the specified idle task.
* This also gives us the initial stack to use for this CPU. * This also gives us the initial stack to use for this CPU.
@@ -100,44 +95,9 @@ extern void arch_send_call_function_single_ipi(int cpu);
extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
#define arch_send_call_function_ipi_mask arch_send_call_function_ipi_mask #define arch_send_call_function_ipi_mask arch_send_call_function_ipi_mask
/*
* Local timer interrupt handling function (can be IPI'ed).
*/
extern void local_timer_interrupt(void);
#ifdef CONFIG_LOCAL_TIMERS
/*
* Stop a local timer interrupt.
*/
extern void local_timer_stop(void);
/*
* Platform provides this to acknowledge a local timer IRQ
*/
extern int local_timer_ack(void);
#else
static inline void local_timer_stop(void)
{
}
#endif
/*
* Setup a local timer interrupt for a CPU.
*/
extern void local_timer_setup(void);
/* /*
* show local interrupt info * show local interrupt info
*/ */
extern void show_local_irqs(struct seq_file *); extern void show_local_irqs(struct seq_file *);
/*
* Called from assembly, this is the local timer IRQ handler
*/
asmlinkage void do_local_timer(struct pt_regs *);
#endif /* ifndef __ASM_ARM_SMP_H */ #endif /* ifndef __ASM_ARM_SMP_H */

View File

@@ -22,6 +22,8 @@
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/percpu.h>
#include <linux/clockchips.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
@@ -32,6 +34,7 @@
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/localtimer.h>
/* /*
* as from 2.5, kernels no longer have an init_tasks structure * as from 2.5, kernels no longer have an init_tasks structure
@@ -274,9 +277,9 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
local_fiq_enable(); local_fiq_enable();
/* /*
* Setup local timer for this CPU. * Setup the percpu timer for this CPU.
*/ */
local_timer_setup(); percpu_timer_setup();
calibrate_delay(); calibrate_delay();
@@ -383,10 +386,16 @@ void show_local_irqs(struct seq_file *p)
seq_putc(p, '\n'); seq_putc(p, '\n');
} }
/*
* Timer (local or broadcast) support
*/
static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent);
static void ipi_timer(void) static void ipi_timer(void)
{ {
struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent);
irq_enter(); irq_enter();
local_timer_interrupt(); evt->event_handler(evt);
irq_exit(); irq_exit();
} }
@@ -405,6 +414,42 @@ asmlinkage void __exception do_local_timer(struct pt_regs *regs)
} }
#endif #endif
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
static void smp_timer_broadcast(const struct cpumask *mask)
{
send_ipi_message(mask, IPI_TIMER);
}
static void broadcast_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
}
static void local_timer_setup(struct clock_event_device *evt)
{
evt->name = "dummy_timer";
evt->features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_DUMMY;
evt->rating = 400;
evt->mult = 1;
evt->set_mode = broadcast_timer_set_mode;
evt->broadcast = smp_timer_broadcast;
clockevents_register_device(evt);
}
#endif
void __cpuinit percpu_timer_setup(void)
{
unsigned int cpu = smp_processor_id();
struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu);
evt->cpumask = cpumask_of(cpu);
local_timer_setup(evt);
}
static DEFINE_SPINLOCK(stop_lock); static DEFINE_SPINLOCK(stop_lock);
/* /*
@@ -501,11 +546,6 @@ void smp_send_reschedule(int cpu)
send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE); send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE);
} }
void smp_timer_broadcast(const struct cpumask *mask)
{
send_ipi_message(mask, IPI_TIMER);
}
void smp_send_stop(void) void smp_send_stop(void)
{ {
cpumask_t mask = cpu_online_map; cpumask_t mask = cpu_online_map;

View File

@@ -11,10 +11,8 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/percpu.h>
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/io.h> #include <linux/io.h>
@@ -24,18 +22,6 @@
#include <mach/hardware.h> #include <mach/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
static DEFINE_PER_CPU(struct clock_event_device, local_clockevent);
/*
* Used on SMP for either the local timer or IPI_TIMER
*/
void local_timer_interrupt(void)
{
struct clock_event_device *clk = &__get_cpu_var(local_clockevent);
clk->event_handler(clk);
}
#ifdef CONFIG_LOCAL_TIMERS #ifdef CONFIG_LOCAL_TIMERS
/* set up by the platform code */ /* set up by the platform code */
@@ -44,7 +30,7 @@ void __iomem *twd_base;
static unsigned long mpcore_timer_rate; static unsigned long mpcore_timer_rate;
static void local_timer_set_mode(enum clock_event_mode mode, static void local_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *clk) struct clock_event_device *evt)
{ {
unsigned long ctrl; unsigned long ctrl;
@@ -140,32 +126,29 @@ static void __cpuinit twd_calibrate_rate(void)
/* /*
* Setup the local clock events for a CPU. * Setup the local clock events for a CPU.
*/ */
void __cpuinit local_timer_setup(void) void __cpuinit local_timer_setup(struct clock_event_device *evt)
{ {
unsigned int cpu = smp_processor_id();
struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
unsigned long flags; unsigned long flags;
twd_calibrate_rate(); twd_calibrate_rate();
clk->name = "local_timer"; evt->name = "local_timer";
clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
clk->rating = 350; evt->rating = 350;
clk->set_mode = local_timer_set_mode; evt->set_mode = local_timer_set_mode;
clk->set_next_event = local_timer_set_next_event; evt->set_next_event = local_timer_set_next_event;
clk->irq = IRQ_LOCALTIMER; evt->irq = IRQ_LOCALTIMER;
clk->cpumask = cpumask_of(cpu); evt->shift = 20;
clk->shift = 20; evt->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, evt->shift);
clk->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, clk->shift); evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt);
clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); evt->min_delta_ns = clockevent_delta2ns(0xf, evt);
clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
/* Make sure our local interrupt controller has this enabled */ /* Make sure our local interrupt controller has this enabled */
local_irq_save(flags); local_irq_save(flags);
get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER); get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER);
local_irq_restore(flags); local_irq_restore(flags);
clockevents_register_device(clk); clockevents_register_device(evt);
} }
/* /*
@@ -176,29 +159,4 @@ void __cpuexit local_timer_stop(void)
__raw_writel(0, twd_base + TWD_TIMER_CONTROL); __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
} }
#else /* CONFIG_LOCAL_TIMERS */ #endif /* CONFIG_LOCAL_TIMERS */
static void dummy_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *clk)
{
}
void __cpuinit local_timer_setup(void)
{
unsigned int cpu = smp_processor_id();
struct clock_event_device *clk = &per_cpu(local_clockevent, cpu);
clk->name = "dummy_timer";
clk->features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_DUMMY;
clk->rating = 400;
clk->mult = 1;
clk->set_mode = dummy_timer_set_mode;
clk->broadcast = smp_timer_broadcast;
clk->cpumask = cpumask_of(cpu);
clockevents_register_device(clk);
}
#endif /* !CONFIG_LOCAL_TIMERS */

View File

@@ -19,6 +19,7 @@
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <mach/hardware.h> #include <mach/hardware.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/localtimer.h>
#include <mach/board-eb.h> #include <mach/board-eb.h>
#include <mach/board-pb11mp.h> #include <mach/board-pb11mp.h>
@@ -217,13 +218,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
if (max_cpus > ncores) if (max_cpus > ncores)
max_cpus = ncores; max_cpus = ncores;
#if defined(CONFIG_LOCAL_TIMERS) || defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)
/*
* Enable the local timer or broadcast device for the boot CPU.
*/
local_timer_setup();
#endif
/* /*
* Initialise the present map, which describes the set of CPUs * Initialise the present map, which describes the set of CPUs
* actually populated at the present time. * actually populated at the present time.
@@ -239,6 +233,12 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
* WFI * WFI
*/ */
if (max_cpus > 1) { if (max_cpus > 1) {
/*
* Enable the local timer or broadcast device for the
* boot CPU, but only if we have more than one CPU.
*/
percpu_timer_setup();
scu_enable(); scu_enable();
poke_milo(); poke_milo();
} }