Commit c7f7ff17 authored by Russell King's avatar Russell King Committed by Russell King

Merge branch 'smp' into devel

parents a22f277b e03cdade
...@@ -844,7 +844,9 @@ source "kernel/time/Kconfig" ...@@ -844,7 +844,9 @@ 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
select HAVE_ARM_SCU if ARCH_REALVIEW
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
a system with only one CPU, like most personal computers, say N. If a system with only one CPU, like most personal computers, say N. If
...@@ -862,6 +864,18 @@ config SMP ...@@ -862,6 +864,18 @@ config SMP
If you don't know what to do here, say N. 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 choice
prompt "Memory split" prompt "Memory split"
default VMSPLIT_3G default VMSPLIT_3G
...@@ -902,6 +916,7 @@ config LOCAL_TIMERS ...@@ -902,6 +916,7 @@ config LOCAL_TIMERS
bool "Use local timer interrupts" bool "Use local timer interrupts"
depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP) depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || REALVIEW_EB_A9MP)
default y default y
select HAVE_ARM_TWD if ARCH_REALVIEW
help help
Enable support for local timers on SMP platforms, rather then the Enable support for local timers on SMP platforms, rather then the
legacy IPI broadcast method. Local timers allows the system legacy IPI broadcast method. Local timers allows the system
......
#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
/*
* 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); ...@@ -41,7 +41,7 @@ extern void show_ipi_list(struct seq_file *p);
asmlinkage void do_IPI(struct pt_regs *regs); 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); extern void smp_init_cpus(void);
...@@ -55,11 +55,6 @@ extern void smp_store_cpu_info(unsigned int cpuid); ...@@ -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); ...@@ -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 */
#ifndef __ASMARM_ARCH_SCU_H
#define __ASMARM_ARCH_SCU_H
unsigned int scu_get_core_count(void __iomem *);
void scu_enable(void __iomem *);
#endif
#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 ...@@ -22,6 +22,8 @@ obj-$(CONFIG_ARTHUR) += arthur.o
obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_ISA_DMA) += dma-isa.o
obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_PCI) += bios32.o isa.o
obj-$(CONFIG_SMP) += smp.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_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o
......
...@@ -22,6 +22,8 @@ ...@@ -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 @@ ...@@ -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
...@@ -163,7 +166,7 @@ int __cpuexit __cpu_disable(void) ...@@ -163,7 +166,7 @@ int __cpuexit __cpu_disable(void)
* Take this CPU offline. Once we clear this, we can't return, * 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. * 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 * OK - migrate IRQs away from this CPU
...@@ -274,9 +277,9 @@ asmlinkage void __cpuinit secondary_start_kernel(void) ...@@ -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();
...@@ -285,7 +288,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void) ...@@ -285,7 +288,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
/* /*
* OK, now it's safe to let the boot CPU continue * 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 * OK, it's off to the idle thread for us
...@@ -383,10 +386,16 @@ void show_local_irqs(struct seq_file *p) ...@@ -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) ...@@ -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);
/* /*
...@@ -417,7 +462,7 @@ static void ipi_cpu_stop(unsigned int cpu) ...@@ -417,7 +462,7 @@ static void ipi_cpu_stop(unsigned int cpu)
dump_stack(); dump_stack();
spin_unlock(&stop_lock); spin_unlock(&stop_lock);
cpu_clear(cpu, cpu_online_map); set_cpu_online(cpu, false);
local_fiq_disable(); local_fiq_disable();
local_irq_disable(); local_irq_disable();
...@@ -501,11 +546,6 @@ void smp_send_reschedule(int cpu) ...@@ -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;
......
/*
* 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();
}
/*
* 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 ...@@ -7,5 +7,6 @@ obj-$(CONFIG_MACH_REALVIEW_EB) += realview_eb.o
obj-$(CONFIG_MACH_REALVIEW_PB11MP) += realview_pb11mp.o obj-$(CONFIG_MACH_REALVIEW_PB11MP) += realview_pb11mp.o
obj-$(CONFIG_MACH_REALVIEW_PB1176) += realview_pb1176.o obj-$(CONFIG_MACH_REALVIEW_PB1176) += realview_pb1176.o
obj-$(CONFIG_MACH_REALVIEW_PBA8) += realview_pba8.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_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o
...@@ -51,9 +51,6 @@ extern struct mmc_platform_data realview_mmc0_plat_data; ...@@ -51,9 +51,6 @@ extern struct mmc_platform_data realview_mmc0_plat_data;
extern struct mmc_platform_data realview_mmc1_plat_data; extern struct mmc_platform_data realview_mmc1_plat_data;
extern struct clcd_board clcd_plat_data; extern struct clcd_board clcd_plat_data;
extern void __iomem *gic_cpu_base_addr; 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 *timer0_va_base;
extern void __iomem *timer1_va_base; extern void __iomem *timer1_va_base;
extern void __iomem *timer2_va_base; extern void __iomem *timer2_va_base;
......
#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 @@ ...@@ -9,196 +9,18 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/jiffies.h>
#include <linux/percpu.h>
#include <linux/clockchips.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> #include <asm/irq.h>
#include <asm/smp_twd.h>
static DEFINE_PER_CPU(struct clock_event_device, local_clockevent); #include <asm/localtimer.h>
/*
* 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);
}
/* /*
* 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;
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);
}
/*
* take a local timer down
*/
void __cpuexit local_timer_stop(void)
{ {
__raw_writel(0, twd_base + TWD_TIMER_CONTROL); evt->irq = IRQ_LOCALTIMER;
twd_timer_setup(evt);
} }
#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 @@ ...@@ -19,10 +19,11 @@
#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>
#include <mach/scu.h> #include <asm/smp_scu.h>
#include "core.h" #include "core.h"
...@@ -44,31 +45,12 @@ static void __iomem *scu_base_addr(void) ...@@ -44,31 +45,12 @@ static void __iomem *scu_base_addr(void)
return (void __iomem *)0; 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(); void __iomem *scu_base = scu_base_addr();
if (scu_base)
if (scu_base) { return scu_get_core_count(scu_base);
ncores = __raw_readl(scu_base + SCU_CONFIG); return 1;
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);
} }
static DEFINE_SPINLOCK(boot_lock); static DEFINE_SPINLOCK(boot_lock);
...@@ -184,7 +166,7 @@ void __init smp_init_cpus(void) ...@@ -184,7 +166,7 @@ void __init smp_init_cpus(void)
unsigned int i, ncores = get_core_count(); unsigned int i, ncores = get_core_count();
for (i = 0; i < ncores; i++) 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) void __init smp_prepare_cpus(unsigned int max_cpus)
...@@ -217,19 +199,12 @@ 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) 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.
*/ */
for (i = 0; i < max_cpus; i++) 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 * 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) ...@@ -239,7 +214,13 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
* WFI * WFI
*/ */
if (max_cpus > 1) { 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(); poke_milo();
} }
} }
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <asm/hardware/gic.h> #include <asm/hardware/gic.h>
#include <asm/hardware/icst307.h> #include <asm/hardware/icst307.h>
#include <asm/hardware/cache-l2x0.h> #include <asm/hardware/cache-l2x0.h>
#include <asm/localtimer.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <asm/hardware/gic.h> #include <asm/hardware/gic.h>
#include <asm/hardware/icst307.h> #include <asm/hardware/icst307.h>
#include <asm/hardware/cache-l2x0.h> #include <asm/hardware/cache-l2x0.h>
#include <asm/localtimer.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/flash.h> #include <asm/mach/flash.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment