Merge branch 'smp' into devel
This commit is contained in:
commit
c7f7ff179c
|
@ -844,7 +844,9 @@ source "kernel/time/Kconfig"
|
|||
config SMP
|
||||
bool "Symmetric Multi-Processing (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP)
|
||||
depends on GENERIC_CLOCKEVENTS
|
||||
select USE_GENERIC_SMP_HELPERS
|
||||
select HAVE_ARM_SCU if ARCH_REALVIEW
|
||||
help
|
||||
This enables support for systems with more than one CPU. If you have
|
||||
a system with only one CPU, like most personal computers, say N. If
|
||||
|
@ -862,6 +864,18 @@ config SMP
|
|||
|
||||
If you don't know what to do here, say N.
|
||||
|
||||
config HAVE_ARM_SCU
|
||||
bool
|
||||
depends on SMP
|
||||
help
|
||||
This option enables support for the ARM system coherency unit
|
||||
|
||||
config HAVE_ARM_TWD
|
||||
bool
|
||||
depends on SMP
|
||||
help
|
||||
This options enables support for the ARM timer and watchdog unit
|
||||
|
||||
choice
|
||||
prompt "Memory split"
|
||||
default VMSPLIT_3G
|
||||
|
@ -902,6 +916,7 @@ config LOCAL_TIMERS
|
|||
bool "Use local timer interrupts"
|
||||
depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP)
|
||||
default y
|
||||
select HAVE_ARM_TWD if ARCH_REALVIEW
|
||||
help
|
||||
Enable support for local timers on SMP platforms, rather then the
|
||||
legacy IPI broadcast method. Local timers allows the system
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
#ifndef __ASM_HARDWARE_TWD_H
|
||||
#define __ASM_HARDWARE_TWD_H
|
||||
|
||||
#define TWD_TIMER_LOAD 0x00
|
||||
#define TWD_TIMER_COUNTER 0x04
|
||||
#define TWD_TIMER_CONTROL 0x08
|
||||
#define TWD_TIMER_INTSTAT 0x0C
|
||||
|
||||
#define TWD_WDOG_LOAD 0x20
|
||||
#define TWD_WDOG_COUNTER 0x24
|
||||
#define TWD_WDOG_CONTROL 0x28
|
||||
#define TWD_WDOG_INTSTAT 0x2C
|
||||
#define TWD_WDOG_RESETSTAT 0x30
|
||||
#define TWD_WDOG_DISABLE 0x34
|
||||
|
||||
#define TWD_TIMER_CONTROL_ENABLE (1 << 0)
|
||||
#define TWD_TIMER_CONTROL_ONESHOT (0 << 1)
|
||||
#define TWD_TIMER_CONTROL_PERIODIC (1 << 1)
|
||||
#define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
#ifdef CONFIG_HAVE_ARM_TWD
|
||||
|
||||
#include "smp_twd.h"
|
||||
|
||||
#define local_timer_ack() twd_timer_ack()
|
||||
#define local_timer_stop() twd_timer_stop()
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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
|
|
@ -41,7 +41,7 @@ extern void show_ipi_list(struct seq_file *p);
|
|||
asmlinkage void do_IPI(struct pt_regs *regs);
|
||||
|
||||
/*
|
||||
* Setup the SMP cpu_possible_map
|
||||
* Setup the set of possible CPUs (via set_cpu_possible)
|
||||
*/
|
||||
extern void smp_init_cpus(void);
|
||||
|
||||
|
@ -55,11 +55,6 @@ extern void smp_store_cpu_info(unsigned int cpuid);
|
|||
*/
|
||||
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.
|
||||
* 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);
|
||||
#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
|
||||
*/
|
||||
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 */
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef __ASMARM_ARCH_SCU_H
|
||||
#define __ASMARM_ARCH_SCU_H
|
||||
|
||||
unsigned int scu_get_core_count(void __iomem *);
|
||||
void scu_enable(void __iomem *);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __ASMARM_SMP_TWD_H
|
||||
#define __ASMARM_SMP_TWD_H
|
||||
|
||||
struct clock_event_device;
|
||||
|
||||
extern void __iomem *twd_base;
|
||||
|
||||
void twd_timer_stop(void);
|
||||
int twd_timer_ack(void);
|
||||
void twd_timer_setup(struct clock_event_device *);
|
||||
|
||||
#endif
|
|
@ -22,6 +22,8 @@ obj-$(CONFIG_ARTHUR) += arthur.o
|
|||
obj-$(CONFIG_ISA_DMA) += dma-isa.o
|
||||
obj-$(CONFIG_PCI) += bios32.o isa.o
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o
|
||||
obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o
|
||||
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
|
||||
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <linux/smp.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/clockchips.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
@ -32,6 +34,7 @@
|
|||
#include <asm/processor.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/localtimer.h>
|
||||
|
||||
/*
|
||||
* as from 2.5, kernels no longer have an init_tasks structure
|
||||
|
@ -163,7 +166,7 @@ int __cpuexit __cpu_disable(void)
|
|||
* Take this CPU offline. Once we clear this, we can't return,
|
||||
* and we must not schedule until we're ready to give up the cpu.
|
||||
*/
|
||||
cpu_clear(cpu, cpu_online_map);
|
||||
set_cpu_online(cpu, false);
|
||||
|
||||
/*
|
||||
* OK - migrate IRQs away from this CPU
|
||||
|
@ -274,9 +277,9 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
|
|||
local_fiq_enable();
|
||||
|
||||
/*
|
||||
* Setup local timer for this CPU.
|
||||
* Setup the percpu timer for this CPU.
|
||||
*/
|
||||
local_timer_setup();
|
||||
percpu_timer_setup();
|
||||
|
||||
calibrate_delay();
|
||||
|
||||
|
@ -285,7 +288,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
|
|||
/*
|
||||
* OK, now it's safe to let the boot CPU continue
|
||||
*/
|
||||
cpu_set(cpu, cpu_online_map);
|
||||
set_cpu_online(cpu, true);
|
||||
|
||||
/*
|
||||
* OK, it's off to the idle thread for us
|
||||
|
@ -383,10 +386,16 @@ void show_local_irqs(struct seq_file *p)
|
|||
seq_putc(p, '\n');
|
||||
}
|
||||
|
||||
/*
|
||||
* Timer (local or broadcast) support
|
||||
*/
|
||||
static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent);
|
||||
|
||||
static void ipi_timer(void)
|
||||
{
|
||||
struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent);
|
||||
irq_enter();
|
||||
local_timer_interrupt();
|
||||
evt->event_handler(evt);
|
||||
irq_exit();
|
||||
}
|
||||
|
||||
|
@ -405,6 +414,42 @@ asmlinkage void __exception do_local_timer(struct pt_regs *regs)
|
|||
}
|
||||
#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);
|
||||
|
||||
/*
|
||||
|
@ -417,7 +462,7 @@ static void ipi_cpu_stop(unsigned int cpu)
|
|||
dump_stack();
|
||||
spin_unlock(&stop_lock);
|
||||
|
||||
cpu_clear(cpu, cpu_online_map);
|
||||
set_cpu_online(cpu, false);
|
||||
|
||||
local_fiq_disable();
|
||||
local_irq_disable();
|
||||
|
@ -501,11 +546,6 @@ void smp_send_reschedule(int cpu)
|
|||
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)
|
||||
{
|
||||
cpumask_t mask = cpu_online_map;
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* linux/arch/arm/kernel/smp_scu.c
|
||||
*
|
||||
* Copyright (C) 2002 ARM Ltd.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/smp_scu.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#define SCU_CTRL 0x00
|
||||
#define SCU_CONFIG 0x04
|
||||
#define SCU_CPU_STATUS 0x08
|
||||
#define SCU_INVALIDATE 0x0c
|
||||
#define SCU_FPGA_REVISION 0x10
|
||||
|
||||
/*
|
||||
* Get the number of CPU cores from the SCU configuration
|
||||
*/
|
||||
unsigned int __init scu_get_core_count(void __iomem *scu_base)
|
||||
{
|
||||
unsigned int ncores = __raw_readl(scu_base + SCU_CONFIG);
|
||||
return (ncores & 0x03) + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the SCU
|
||||
*/
|
||||
void __init scu_enable(void __iomem *scu_base)
|
||||
{
|
||||
u32 scu_ctrl;
|
||||
|
||||
scu_ctrl = __raw_readl(scu_base + SCU_CTRL);
|
||||
scu_ctrl |= 1;
|
||||
__raw_writel(scu_ctrl, scu_base + SCU_CTRL);
|
||||
|
||||
/*
|
||||
* Ensure that the data accessed by CPU0 before the SCU was
|
||||
* initialised is visible to the other CPUs.
|
||||
*/
|
||||
flush_cache_all();
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* linux/arch/arm/kernel/smp_twd.c
|
||||
*
|
||||
* Copyright (C) 2002 ARM Ltd.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/smp_twd.h>
|
||||
#include <asm/hardware/gic.h>
|
||||
|
||||
#define TWD_TIMER_LOAD 0x00
|
||||
#define TWD_TIMER_COUNTER 0x04
|
||||
#define TWD_TIMER_CONTROL 0x08
|
||||
#define TWD_TIMER_INTSTAT 0x0C
|
||||
|
||||
#define TWD_WDOG_LOAD 0x20
|
||||
#define TWD_WDOG_COUNTER 0x24
|
||||
#define TWD_WDOG_CONTROL 0x28
|
||||
#define TWD_WDOG_INTSTAT 0x2C
|
||||
#define TWD_WDOG_RESETSTAT 0x30
|
||||
#define TWD_WDOG_DISABLE 0x34
|
||||
|
||||
#define TWD_TIMER_CONTROL_ENABLE (1 << 0)
|
||||
#define TWD_TIMER_CONTROL_ONESHOT (0 << 1)
|
||||
#define TWD_TIMER_CONTROL_PERIODIC (1 << 1)
|
||||
#define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2)
|
||||
|
||||
/* set up by the platform code */
|
||||
void __iomem *twd_base;
|
||||
|
||||
static unsigned long twd_timer_rate;
|
||||
|
||||
static void twd_set_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *clk)
|
||||
{
|
||||
unsigned long ctrl;
|
||||
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
/* timer load already set up */
|
||||
ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
|
||||
| TWD_TIMER_CONTROL_PERIODIC;
|
||||
break;
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
/* period set, and timer enabled in 'next_event' hook */
|
||||
ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
|
||||
break;
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
default:
|
||||
ctrl = 0;
|
||||
}
|
||||
|
||||
__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
|
||||
}
|
||||
|
||||
static int twd_set_next_event(unsigned long evt,
|
||||
struct clock_event_device *unused)
|
||||
{
|
||||
unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
|
||||
|
||||
ctrl |= TWD_TIMER_CONTROL_ENABLE;
|
||||
|
||||
__raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
|
||||
__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* local_timer_ack: checks for a local timer interrupt.
|
||||
*
|
||||
* If a local timer interrupt has occurred, acknowledge and return 1.
|
||||
* Otherwise, return 0.
|
||||
*/
|
||||
int twd_timer_ack(void)
|
||||
{
|
||||
if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
|
||||
__raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __cpuinit twd_calibrate_rate(void)
|
||||
{
|
||||
unsigned long load, count;
|
||||
u64 waitjiffies;
|
||||
|
||||
/*
|
||||
* If this is the first time round, we need to work out how fast
|
||||
* the timer ticks
|
||||
*/
|
||||
if (twd_timer_rate == 0) {
|
||||
printk(KERN_INFO "Calibrating local timer... ");
|
||||
|
||||
/* Wait for a tick to start */
|
||||
waitjiffies = get_jiffies_64() + 1;
|
||||
|
||||
while (get_jiffies_64() < waitjiffies)
|
||||
udelay(10);
|
||||
|
||||
/* OK, now the tick has started, let's get the timer going */
|
||||
waitjiffies += 5;
|
||||
|
||||
/* enable, no interrupt or reload */
|
||||
__raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
|
||||
|
||||
/* maximum value */
|
||||
__raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
|
||||
|
||||
while (get_jiffies_64() < waitjiffies)
|
||||
udelay(10);
|
||||
|
||||
count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
|
||||
|
||||
twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
|
||||
|
||||
printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
|
||||
(twd_timer_rate / 100000) % 100);
|
||||
}
|
||||
|
||||
load = twd_timer_rate / HZ;
|
||||
|
||||
__raw_writel(load, twd_base + TWD_TIMER_LOAD);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the local clock events for a CPU.
|
||||
*/
|
||||
void __cpuinit twd_timer_setup(struct clock_event_device *clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
twd_calibrate_rate();
|
||||
|
||||
clk->name = "local_timer";
|
||||
clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
|
||||
clk->rating = 350;
|
||||
clk->set_mode = twd_set_mode;
|
||||
clk->set_next_event = twd_set_next_event;
|
||||
clk->shift = 20;
|
||||
clk->mult = div_sc(twd_timer_rate, NSEC_PER_SEC, clk->shift);
|
||||
clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk);
|
||||
clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
|
||||
|
||||
/* Make sure our local interrupt controller has this enabled */
|
||||
local_irq_save(flags);
|
||||
get_irq_chip(clk->irq)->unmask(clk->irq);
|
||||
local_irq_restore(flags);
|
||||
|
||||
clockevents_register_device(clk);
|
||||
}
|
||||
|
||||
/*
|
||||
* take a local timer down
|
||||
*/
|
||||
void __cpuexit twd_timer_stop(void)
|
||||
{
|
||||
__raw_writel(0, twd_base + TWD_TIMER_CONTROL);
|
||||
}
|
|
@ -7,5 +7,6 @@ obj-$(CONFIG_MACH_REALVIEW_EB) += realview_eb.o
|
|||
obj-$(CONFIG_MACH_REALVIEW_PB11MP) += realview_pb11mp.o
|
||||
obj-$(CONFIG_MACH_REALVIEW_PB1176) += realview_pb1176.o
|
||||
obj-$(CONFIG_MACH_REALVIEW_PBA8) += realview_pba8.o
|
||||
obj-$(CONFIG_SMP) += platsmp.o headsmp.o localtimer.o
|
||||
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
|
||||
|
|
|
@ -51,9 +51,6 @@ extern struct mmc_platform_data realview_mmc0_plat_data;
|
|||
extern struct mmc_platform_data realview_mmc1_plat_data;
|
||||
extern struct clcd_board clcd_plat_data;
|
||||
extern void __iomem *gic_cpu_base_addr;
|
||||
#ifdef CONFIG_LOCAL_TIMERS
|
||||
extern void __iomem *twd_base;
|
||||
#endif
|
||||
extern void __iomem *timer0_va_base;
|
||||
extern void __iomem *timer1_va_base;
|
||||
extern void __iomem *timer2_va_base;
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef __ASMARM_ARCH_SCU_H
|
||||
#define __ASMARM_ARCH_SCU_H
|
||||
|
||||
/*
|
||||
* SCU registers
|
||||
*/
|
||||
#define SCU_CTRL 0x00
|
||||
#define SCU_CONFIG 0x04
|
||||
#define SCU_CPU_STATUS 0x08
|
||||
#define SCU_INVALIDATE 0x0c
|
||||
#define SCU_FPGA_REVISION 0x10
|
||||
|
||||
#endif
|
|
@ -9,196 +9,18 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/hardware/arm_twd.h>
|
||||
#include <asm/hardware/gic.h>
|
||||
#include <mach/hardware.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
|
||||
|
||||
/* set up by the platform code */
|
||||
void __iomem *twd_base;
|
||||
|
||||
static unsigned long mpcore_timer_rate;
|
||||
|
||||
static void local_timer_set_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *clk)
|
||||
{
|
||||
unsigned long ctrl;
|
||||
|
||||
switch(mode) {
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
/* timer load already set up */
|
||||
ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
|
||||
| TWD_TIMER_CONTROL_PERIODIC;
|
||||
break;
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
/* period set, and timer enabled in 'next_event' hook */
|
||||
ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
|
||||
break;
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
default:
|
||||
ctrl = 0;
|
||||
}
|
||||
|
||||
__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
|
||||
}
|
||||
|
||||
static int local_timer_set_next_event(unsigned long evt,
|
||||
struct clock_event_device *unused)
|
||||
{
|
||||
unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
|
||||
|
||||
__raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
|
||||
__raw_writel(ctrl | TWD_TIMER_CONTROL_ENABLE, twd_base + TWD_TIMER_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* local_timer_ack: checks for a local timer interrupt.
|
||||
*
|
||||
* If a local timer interrupt has occurred, acknowledge and return 1.
|
||||
* Otherwise, return 0.
|
||||
*/
|
||||
int local_timer_ack(void)
|
||||
{
|
||||
if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
|
||||
__raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __cpuinit twd_calibrate_rate(void)
|
||||
{
|
||||
unsigned long load, count;
|
||||
u64 waitjiffies;
|
||||
|
||||
/*
|
||||
* If this is the first time round, we need to work out how fast
|
||||
* the timer ticks
|
||||
*/
|
||||
if (mpcore_timer_rate == 0) {
|
||||
printk("Calibrating local timer... ");
|
||||
|
||||
/* Wait for a tick to start */
|
||||
waitjiffies = get_jiffies_64() + 1;
|
||||
|
||||
while (get_jiffies_64() < waitjiffies)
|
||||
udelay(10);
|
||||
|
||||
/* OK, now the tick has started, let's get the timer going */
|
||||
waitjiffies += 5;
|
||||
|
||||
/* enable, no interrupt or reload */
|
||||
__raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
|
||||
|
||||
/* maximum value */
|
||||
__raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
|
||||
|
||||
while (get_jiffies_64() < waitjiffies)
|
||||
udelay(10);
|
||||
|
||||
count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
|
||||
|
||||
mpcore_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
|
||||
|
||||
printk("%lu.%02luMHz.\n", mpcore_timer_rate / 1000000,
|
||||
(mpcore_timer_rate / 100000) % 100);
|
||||
}
|
||||
|
||||
load = mpcore_timer_rate / HZ;
|
||||
|
||||
__raw_writel(load, twd_base + TWD_TIMER_LOAD);
|
||||
}
|
||||
#include <asm/smp_twd.h>
|
||||
#include <asm/localtimer.h>
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
twd_calibrate_rate();
|
||||
|
||||
clk->name = "local_timer";
|
||||
clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
|
||||
clk->rating = 350;
|
||||
clk->set_mode = local_timer_set_mode;
|
||||
clk->set_next_event = local_timer_set_next_event;
|
||||
clk->irq = IRQ_LOCALTIMER;
|
||||
clk->cpumask = cpumask_of(cpu);
|
||||
clk->shift = 20;
|
||||
clk->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, clk->shift);
|
||||
clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk);
|
||||
clk->min_delta_ns = clockevent_delta2ns(0xf, clk);
|
||||
|
||||
/* Make sure our local interrupt controller has this enabled */
|
||||
local_irq_save(flags);
|
||||
get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER);
|
||||
local_irq_restore(flags);
|
||||
|
||||
clockevents_register_device(clk);
|
||||
evt->irq = IRQ_LOCALTIMER;
|
||||
twd_timer_setup(evt);
|
||||
}
|
||||
|
||||
/*
|
||||
* take a local timer down
|
||||
*/
|
||||
void __cpuexit local_timer_stop(void)
|
||||
{
|
||||
__raw_writel(0, twd_base + TWD_TIMER_CONTROL);
|
||||
}
|
||||
|
||||
#else /* 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,10 +19,11 @@
|
|||
#include <asm/cacheflush.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/localtimer.h>
|
||||
|
||||
#include <mach/board-eb.h>
|
||||
#include <mach/board-pb11mp.h>
|
||||
#include <mach/scu.h>
|
||||
#include <asm/smp_scu.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
|
@ -44,31 +45,12 @@ static void __iomem *scu_base_addr(void)
|
|||
return (void __iomem *)0;
|
||||
}
|
||||
|
||||
static unsigned int __init get_core_count(void)
|
||||
static inline unsigned int get_core_count(void)
|
||||
{
|
||||
unsigned int ncores;
|
||||
void __iomem *scu_base = scu_base_addr();
|
||||
|
||||
if (scu_base) {
|
||||
ncores = __raw_readl(scu_base + SCU_CONFIG);
|
||||
ncores = (ncores & 0x03) + 1;
|
||||
} else
|
||||
ncores = 1;
|
||||
|
||||
return ncores;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the SCU
|
||||
*/
|
||||
static void scu_enable(void)
|
||||
{
|
||||
u32 scu_ctrl;
|
||||
void __iomem *scu_base = scu_base_addr();
|
||||
|
||||
scu_ctrl = __raw_readl(scu_base + SCU_CTRL);
|
||||
scu_ctrl |= 1;
|
||||
__raw_writel(scu_ctrl, scu_base + SCU_CTRL);
|
||||
if (scu_base)
|
||||
return scu_get_core_count(scu_base);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(boot_lock);
|
||||
|
@ -184,7 +166,7 @@ void __init smp_init_cpus(void)
|
|||
unsigned int i, ncores = get_core_count();
|
||||
|
||||
for (i = 0; i < ncores; i++)
|
||||
cpu_set(i, cpu_possible_map);
|
||||
set_cpu_possible(i, true);
|
||||
}
|
||||
|
||||
void __init smp_prepare_cpus(unsigned int max_cpus)
|
||||
|
@ -217,19 +199,12 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
|
|||
if (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
|
||||
* actually populated at the present time.
|
||||
*/
|
||||
for (i = 0; i < max_cpus; i++)
|
||||
cpu_set(i, cpu_present_map);
|
||||
set_cpu_present(i, true);
|
||||
|
||||
/*
|
||||
* Initialise the SCU if there are more than one CPU and let
|
||||
|
@ -239,7 +214,13 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
|
|||
* WFI
|
||||
*/
|
||||
if (max_cpus > 1) {
|
||||
scu_enable();
|
||||
/*
|
||||
* 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_base_addr());
|
||||
poke_milo();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <asm/hardware/gic.h>
|
||||
#include <asm/hardware/icst307.h>
|
||||
#include <asm/hardware/cache-l2x0.h>
|
||||
#include <asm/localtimer.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <asm/hardware/gic.h>
|
||||
#include <asm/hardware/icst307.h>
|
||||
#include <asm/hardware/cache-l2x0.h>
|
||||
#include <asm/localtimer.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/flash.h>
|
||||
|
|
Loading…
Reference in New Issue