-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ARM] smp: allow re-use of realview localtimer TWD support
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
Showing
11 changed files
with
213 additions
and
165 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.