Skip to content

Commit

Permalink
[ARM] smp: allow re-use of realview localtimer TWD support
Browse files Browse the repository at this point in the history
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Russell King authored and Russell King committed May 17, 2009
1 parent a8cbcd9 commit f32f4ce
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 165 deletions.
7 changes: 7 additions & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
21 changes: 0 additions & 21 deletions arch/arm/include/asm/hardware/arm_twd.h

This file was deleted.

12 changes: 12 additions & 0 deletions arch/arm/include/asm/localtimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -35,6 +45,8 @@ int local_timer_ack(void);
*/
void local_timer_stop(void);

#endif

/*
* Setup a local timer interrupt for a CPU.
*/
Expand Down
12 changes: 12 additions & 0 deletions arch/arm/include/asm/smp_twd.h
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions arch/arm/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
173 changes: 173 additions & 0 deletions arch/arm/kernel/smp_twd.c
Original file line number Diff line number Diff line change
@@ -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);
}
3 changes: 2 additions & 1 deletion arch/arm/mach-realview/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 0 additions & 3 deletions arch/arm/mach-realview/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit f32f4ce

Please sign in to comment.