[ARM] smp: allow re-use of realview localtimer TWD support
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
a8cbcd92bd
commit
f32f4ce257
|
@ -885,6 +885,12 @@ config HAVE_ARM_SCU
|
|||
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
|
||||
|
@ -925,6 +931,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
|
|
@ -24,6 +24,16 @@ 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.
|
||||
|
@ -35,6 +45,8 @@ int local_timer_ack(void);
|
|||
*/
|
||||
void local_timer_stop(void);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Setup a local timer interrupt for a CPU.
|
||||
*/
|
||||
|
|
12
arch/arm/include/asm/smp_twd.h
Normal file
12
arch/arm/include/asm/smp_twd.h
Normal file
|
@ -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
|
|
@ -23,6 +23,7 @@ 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
|
||||
|
|
173
arch/arm/kernel/smp_twd.c
Normal file
173
arch/arm/kernel/smp_twd.c
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* 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);
|
||||
|
||||
__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 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("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;
|
||||
|
|
|
@ -9,154 +9,18 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/jiffies.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>
|
||||
|
||||
#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 *evt)
|
||||
{
|
||||
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(struct clock_event_device *evt)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
twd_calibrate_rate();
|
||||
|
||||
evt->name = "local_timer";
|
||||
evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
|
||||
evt->rating = 350;
|
||||
evt->set_mode = local_timer_set_mode;
|
||||
evt->set_next_event = local_timer_set_next_event;
|
||||
evt->irq = IRQ_LOCALTIMER;
|
||||
evt->shift = 20;
|
||||
evt->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, evt->shift);
|
||||
evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt);
|
||||
evt->min_delta_ns = clockevent_delta2ns(0xf, evt);
|
||||
|
||||
/* 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(evt);
|
||||
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);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_LOCAL_TIMERS */
|
||||
|
|
|
@ -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 a new issue