[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:
committed by
Russell King
parent
ee348d5a1d
commit
bc28248ee2
@@ -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
|
||||||
|
51
arch/arm/include/asm/localtimer.h
Normal file
51
arch/arm/include/asm/localtimer.h
Normal 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
|
@@ -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 */
|
||||||
|
@@ -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;
|
||||||
|
@@ -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 */
|
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user