sh: clockevent/clocksource/hrtimers/nohz TMU support.
This adds basic support for clockevents and clocksources, presently only implemented for TMU-based systems (which are the majority of SH-3 and SH-4 systems). The old NO_IDLE_HZ implementation is also dropped completely, the only users of this were on TMU-based systems anyways. More work needs to be done to generalize the TMU handling, in that the current implementation is rather tied to the notion of TMU0 and TMU1 utilization. Additionally, as more SH timers switch over to this scheme, we'll be able to gut most of the remaining system timer infrastructure that existed before. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
parent
1ce7ddd5f4
commit
57be2b484a
5 changed files with 192 additions and 247 deletions
|
@ -52,6 +52,9 @@ config GENERIC_IOMAP
|
|||
config GENERIC_TIME
|
||||
def_bool n
|
||||
|
||||
config GENERIC_CLOCKEVENTS
|
||||
def_bool n
|
||||
|
||||
config SYS_SUPPORTS_APM_EMULATION
|
||||
bool
|
||||
|
||||
|
@ -436,11 +439,11 @@ endmenu
|
|||
|
||||
menu "Timer and clock configuration"
|
||||
|
||||
if !GENERIC_TIME
|
||||
|
||||
config SH_TMU
|
||||
bool "TMU timer support"
|
||||
depends on CPU_SH3 || CPU_SH4
|
||||
select GENERIC_TIME
|
||||
select GENERIC_CLOCKEVENTS
|
||||
default y
|
||||
help
|
||||
This enables the use of the TMU as the system timer.
|
||||
|
@ -459,8 +462,6 @@ config SH_MTU2
|
|||
help
|
||||
This enables the use of the MTU2 as the system timer.
|
||||
|
||||
endif
|
||||
|
||||
config SH_TIMER_IRQ
|
||||
int
|
||||
default "28" if CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785
|
||||
|
@ -468,24 +469,6 @@ config SH_TIMER_IRQ
|
|||
default "140" if CPU_SUBTYPE_SH7206
|
||||
default "16"
|
||||
|
||||
config NO_IDLE_HZ
|
||||
bool "Dynamic tick timer"
|
||||
help
|
||||
Select this option if you want to disable continuous timer ticks
|
||||
and have them programmed to occur as required. This option saves
|
||||
power as the system can remain in idle state for longer.
|
||||
|
||||
By default dynamic tick is disabled during the boot, and can be
|
||||
manually enabled with:
|
||||
|
||||
echo 1 > /sys/devices/system/timer/timer0/dyn_tick
|
||||
|
||||
Alternatively, if you want dynamic tick automatically enabled
|
||||
during boot, pass "dyntick=enable" via the kernel command string.
|
||||
|
||||
Please note that dynamic tick may affect the accuracy of
|
||||
timekeeping on some platforms depending on the implementation.
|
||||
|
||||
config SH_PCLK_FREQ
|
||||
int "Peripheral clock frequency (in Hz)"
|
||||
default "27000000" if CPU_SUBTYPE_SH73180 || CPU_SUBTYPE_SH7343
|
||||
|
@ -509,6 +492,8 @@ config SH_CLK_MD
|
|||
help
|
||||
MD2 - MD0 pin setting.
|
||||
|
||||
source "kernel/time/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "CPU Frequency scaling"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/kallsyms.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/tick.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/pgalloc.h>
|
||||
|
@ -60,8 +61,10 @@ void cpu_idle(void)
|
|||
if (!idle)
|
||||
idle = default_idle;
|
||||
|
||||
tick_nohz_stop_sched_tick();
|
||||
while (!need_resched())
|
||||
idle();
|
||||
tick_nohz_restart_sched_tick();
|
||||
|
||||
preempt_enable_no_resched();
|
||||
schedule();
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
|
||||
* Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
|
||||
* Copyright (C) 2002 - 2006 Paul Mundt
|
||||
* Copyright (C) 2002 - 2007 Paul Mundt
|
||||
* Copyright (C) 2002 M. R. Brown <mrbrown@linux-sh.org>
|
||||
*
|
||||
* Some code taken from i386 version.
|
||||
|
@ -15,6 +15,7 @@
|
|||
#include <linux/profile.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <asm/clock.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/timer.h>
|
||||
|
@ -38,6 +39,14 @@ static int null_rtc_set_time(const time_t secs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Null high precision timer functions for systems lacking one.
|
||||
*/
|
||||
static cycle_t null_hpt_read(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time;
|
||||
int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time;
|
||||
|
||||
|
@ -101,6 +110,7 @@ int do_settimeofday(struct timespec *tv)
|
|||
EXPORT_SYMBOL(do_settimeofday);
|
||||
#endif /* !CONFIG_GENERIC_TIME */
|
||||
|
||||
#ifndef CONFIG_GENERIC_CLOCKEVENTS
|
||||
/* last time the RTC clock got updated */
|
||||
static long last_rtc_update;
|
||||
|
||||
|
@ -138,6 +148,7 @@ void handle_timer_tick(void)
|
|||
last_rtc_update = xtime.tv_sec - 600;
|
||||
}
|
||||
}
|
||||
#endif /* !CONFIG_GENERIC_CLOCKEVENTS */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int timer_suspend(struct sys_device *dev, pm_message_t state)
|
||||
|
@ -168,108 +179,6 @@ static struct sysdev_class timer_sysclass = {
|
|||
.resume = timer_resume,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_NO_IDLE_HZ
|
||||
static int timer_dyn_tick_enable(void)
|
||||
{
|
||||
struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick;
|
||||
unsigned long flags;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (dyn_tick) {
|
||||
spin_lock_irqsave(&dyn_tick->lock, flags);
|
||||
ret = 0;
|
||||
if (!(dyn_tick->state & DYN_TICK_ENABLED)) {
|
||||
ret = dyn_tick->enable();
|
||||
|
||||
if (ret == 0)
|
||||
dyn_tick->state |= DYN_TICK_ENABLED;
|
||||
}
|
||||
spin_unlock_irqrestore(&dyn_tick->lock, flags);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int timer_dyn_tick_disable(void)
|
||||
{
|
||||
struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick;
|
||||
unsigned long flags;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (dyn_tick) {
|
||||
spin_lock_irqsave(&dyn_tick->lock, flags);
|
||||
ret = 0;
|
||||
if (dyn_tick->state & DYN_TICK_ENABLED) {
|
||||
ret = dyn_tick->disable();
|
||||
|
||||
if (ret == 0)
|
||||
dyn_tick->state &= ~DYN_TICK_ENABLED;
|
||||
}
|
||||
spin_unlock_irqrestore(&dyn_tick->lock, flags);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reprogram the system timer for at least the calculated time interval.
|
||||
* This function should be called from the idle thread with IRQs disabled,
|
||||
* immediately before sleeping.
|
||||
*/
|
||||
void timer_dyn_reprogram(void)
|
||||
{
|
||||
struct dyn_tick_timer *dyn_tick = sys_timer->dyn_tick;
|
||||
unsigned long next, seq, flags;
|
||||
|
||||
if (!dyn_tick)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&dyn_tick->lock, flags);
|
||||
if (dyn_tick->state & DYN_TICK_ENABLED) {
|
||||
next = next_timer_interrupt();
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
dyn_tick->reprogram(next - jiffies);
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
}
|
||||
spin_unlock_irqrestore(&dyn_tick->lock, flags);
|
||||
}
|
||||
|
||||
static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%i\n",
|
||||
(sys_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1);
|
||||
}
|
||||
|
||||
static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned int enable = simple_strtoul(buf, NULL, 2);
|
||||
|
||||
if (enable)
|
||||
timer_dyn_tick_enable();
|
||||
else
|
||||
timer_dyn_tick_disable();
|
||||
|
||||
return count;
|
||||
}
|
||||
static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick);
|
||||
|
||||
/*
|
||||
* dyntick=enable|disable
|
||||
*/
|
||||
static char dyntick_str[4] __initdata = "";
|
||||
|
||||
static int __init dyntick_setup(char *str)
|
||||
{
|
||||
if (str)
|
||||
strlcpy(dyntick_str, str, sizeof(dyntick_str));
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("dyntick=", dyntick_setup);
|
||||
#endif
|
||||
|
||||
static int __init timer_init_sysfs(void)
|
||||
{
|
||||
int ret = sysdev_class_register(&timer_sysclass);
|
||||
|
@ -277,27 +186,51 @@ static int __init timer_init_sysfs(void)
|
|||
return ret;
|
||||
|
||||
sys_timer->dev.cls = &timer_sysclass;
|
||||
ret = sysdev_register(&sys_timer->dev);
|
||||
|
||||
#ifdef CONFIG_NO_IDLE_HZ
|
||||
if (ret == 0 && sys_timer->dyn_tick) {
|
||||
ret = sysdev_create_file(&sys_timer->dev, &attr_dyn_tick);
|
||||
|
||||
/*
|
||||
* Turn on dynamic tick after calibrate delay
|
||||
* for correct bogomips
|
||||
*/
|
||||
if (ret == 0 && dyntick_str[0] == 'e')
|
||||
ret = timer_dyn_tick_enable();
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
return sysdev_register(&sys_timer->dev);
|
||||
}
|
||||
device_initcall(timer_init_sysfs);
|
||||
|
||||
void (*board_time_init)(void);
|
||||
|
||||
/*
|
||||
* Shamelessly based on the MIPS and Sparc64 work.
|
||||
*/
|
||||
static unsigned long timer_ticks_per_nsec_quotient __read_mostly;
|
||||
unsigned long sh_hpt_frequency = 0;
|
||||
|
||||
#define NSEC_PER_CYC_SHIFT 10
|
||||
|
||||
struct clocksource clocksource_sh = {
|
||||
.name = "SuperH",
|
||||
.rating = 200,
|
||||
.mask = CLOCKSOURCE_MASK(32),
|
||||
.read = null_hpt_read,
|
||||
.shift = 16,
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
};
|
||||
|
||||
static void __init init_sh_clocksource(void)
|
||||
{
|
||||
if (!sh_hpt_frequency || clocksource_sh.read == null_hpt_read)
|
||||
return;
|
||||
|
||||
clocksource_sh.mult = clocksource_hz2mult(sh_hpt_frequency,
|
||||
clocksource_sh.shift);
|
||||
|
||||
timer_ticks_per_nsec_quotient =
|
||||
clocksource_hz2mult(sh_hpt_frequency, NSEC_PER_CYC_SHIFT);
|
||||
|
||||
clocksource_register(&clocksource_sh);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GENERIC_TIME
|
||||
unsigned long long sched_clock(void)
|
||||
{
|
||||
unsigned long long ticks = clocksource_sh.read();
|
||||
return (ticks * timer_ticks_per_nsec_quotient) >> NSEC_PER_CYC_SHIFT;
|
||||
}
|
||||
#endif
|
||||
|
||||
void __init time_init(void)
|
||||
{
|
||||
if (board_time_init)
|
||||
|
@ -316,10 +249,15 @@ void __init time_init(void)
|
|||
sys_timer = get_sys_timer();
|
||||
printk(KERN_INFO "Using %s for system timer\n", sys_timer->name);
|
||||
|
||||
#ifdef CONFIG_NO_IDLE_HZ
|
||||
if (sys_timer->dyn_tick)
|
||||
spin_lock_init(&sys_timer->dyn_tick->lock);
|
||||
#endif
|
||||
if (sys_timer->ops->read)
|
||||
clocksource_sh.read = sys_timer->ops->read;
|
||||
|
||||
init_sh_clocksource();
|
||||
|
||||
if (sh_hpt_frequency)
|
||||
printk("Using %lu.%03lu MHz high precision timer.\n",
|
||||
((sh_hpt_frequency + 500) / 1000) / 1000,
|
||||
((sh_hpt_frequency + 500) / 1000) % 1000);
|
||||
|
||||
#if defined(CONFIG_SH_KGDB)
|
||||
/*
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* arch/sh/kernel/timers/timer-tmu.c - TMU Timer Support
|
||||
*
|
||||
* Copyright (C) 2005 Paul Mundt
|
||||
* Copyright (C) 2005 - 2007 Paul Mundt
|
||||
*
|
||||
* TMU handling code hacked out of arch/sh/kernel/time.c
|
||||
*
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/seqlock.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <asm/timer.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/io.h>
|
||||
|
@ -25,56 +26,75 @@
|
|||
#include <asm/clock.h>
|
||||
|
||||
#define TMU_TOCR_INIT 0x00
|
||||
#define TMU0_TCR_INIT 0x0020
|
||||
#define TMU_TSTR_INIT 1
|
||||
#define TMU_TCR_INIT 0x0020
|
||||
|
||||
#define TMU0_TCR_CALIB 0x0000
|
||||
|
||||
static unsigned long tmu_timer_get_offset(void)
|
||||
static int tmu_timer_start(void)
|
||||
{
|
||||
int count;
|
||||
static int count_p = 0x7fffffff; /* for the first call after boot */
|
||||
static unsigned long jiffies_p = 0;
|
||||
|
||||
/*
|
||||
* cache volatile jiffies temporarily; we have IRQs turned off.
|
||||
*/
|
||||
unsigned long jiffies_t;
|
||||
|
||||
/* timer count may underflow right here */
|
||||
count = ctrl_inl(TMU0_TCNT); /* read the latched count */
|
||||
|
||||
jiffies_t = jiffies;
|
||||
|
||||
/*
|
||||
* avoiding timer inconsistencies (they are rare, but they happen)...
|
||||
* there is one kind of problem that must be avoided here:
|
||||
* 1. the timer counter underflows
|
||||
*/
|
||||
|
||||
if (jiffies_t == jiffies_p) {
|
||||
if (count > count_p) {
|
||||
/* the nutcase */
|
||||
if (ctrl_inw(TMU0_TCR) & 0x100) { /* Check UNF bit */
|
||||
count -= LATCH;
|
||||
} else {
|
||||
printk("%s (): hardware timer problem?\n",
|
||||
__FUNCTION__);
|
||||
}
|
||||
}
|
||||
} else
|
||||
jiffies_p = jiffies_t;
|
||||
|
||||
count_p = count;
|
||||
|
||||
count = ((LATCH-1) - count) * TICK_SIZE;
|
||||
count = (count + LATCH/2) / LATCH;
|
||||
|
||||
return count;
|
||||
ctrl_outb(ctrl_inb(TMU_TSTR) | 0x3, TMU_TSTR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tmu0_timer_set_interval(unsigned long interval, unsigned int reload)
|
||||
{
|
||||
ctrl_outl(interval, TMU0_TCNT);
|
||||
|
||||
/*
|
||||
* TCNT reloads from TCOR on underflow, clear it if we don't
|
||||
* intend to auto-reload
|
||||
*/
|
||||
if (reload)
|
||||
ctrl_outl(interval, TMU0_TCOR);
|
||||
else
|
||||
ctrl_outl(0, TMU0_TCOR);
|
||||
|
||||
tmu_timer_start();
|
||||
}
|
||||
|
||||
static int tmu_timer_stop(void)
|
||||
{
|
||||
ctrl_outb(ctrl_inb(TMU_TSTR) & ~0x3, TMU_TSTR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cycle_t tmu_timer_read(void)
|
||||
{
|
||||
return ~ctrl_inl(TMU1_TCNT);
|
||||
}
|
||||
|
||||
static int tmu_set_next_event(unsigned long cycles,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
tmu0_timer_set_interval(cycles, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tmu_set_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
ctrl_outl(ctrl_inl(TMU0_TCNT), TMU0_TCOR);
|
||||
break;
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
ctrl_outl(0, TMU0_TCOR);
|
||||
break;
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct clock_event_device tmu0_clockevent = {
|
||||
.name = "tmu0",
|
||||
.shift = 32,
|
||||
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
||||
.set_mode = tmu_set_mode,
|
||||
.set_next_event = tmu_set_next_event,
|
||||
};
|
||||
|
||||
static irqreturn_t tmu_timer_interrupt(int irq, void *dummy)
|
||||
{
|
||||
struct clock_event_device *evt = &tmu0_clockevent;
|
||||
unsigned long timer_status;
|
||||
|
||||
/* Clear UNF bit */
|
||||
|
@ -82,72 +102,76 @@ static irqreturn_t tmu_timer_interrupt(int irq, void *dummy)
|
|||
timer_status &= ~0x100;
|
||||
ctrl_outw(timer_status, TMU0_TCR);
|
||||
|
||||
/*
|
||||
* Here we are in the timer irq handler. We just have irqs locally
|
||||
* disabled but we don't know if the timer_bh is running on the other
|
||||
* CPU. We need to avoid to SMP race with it. NOTE: we don' t need
|
||||
* the irq version of write_lock because as just said we have irq
|
||||
* locally disabled. -arca
|
||||
*/
|
||||
write_seqlock(&xtime_lock);
|
||||
handle_timer_tick();
|
||||
write_sequnlock(&xtime_lock);
|
||||
evt->event_handler(evt);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct irqaction tmu_irq = {
|
||||
.name = "timer",
|
||||
static struct irqaction tmu0_irq = {
|
||||
.name = "periodic timer",
|
||||
.handler = tmu_timer_interrupt,
|
||||
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
|
||||
.mask = CPU_MASK_NONE,
|
||||
};
|
||||
|
||||
static void tmu_clk_init(struct clk *clk)
|
||||
static void tmu0_clk_init(struct clk *clk)
|
||||
{
|
||||
u8 divisor = TMU0_TCR_INIT & 0x7;
|
||||
ctrl_outw(TMU0_TCR_INIT, TMU0_TCR);
|
||||
u8 divisor = TMU_TCR_INIT & 0x7;
|
||||
ctrl_outw(TMU_TCR_INIT, TMU0_TCR);
|
||||
clk->rate = clk->parent->rate / (4 << (divisor << 1));
|
||||
}
|
||||
|
||||
static void tmu_clk_recalc(struct clk *clk)
|
||||
static void tmu0_clk_recalc(struct clk *clk)
|
||||
{
|
||||
u8 divisor = ctrl_inw(TMU0_TCR) & 0x7;
|
||||
clk->rate = clk->parent->rate / (4 << (divisor << 1));
|
||||
}
|
||||
|
||||
static struct clk_ops tmu_clk_ops = {
|
||||
.init = tmu_clk_init,
|
||||
.recalc = tmu_clk_recalc,
|
||||
static struct clk_ops tmu0_clk_ops = {
|
||||
.init = tmu0_clk_init,
|
||||
.recalc = tmu0_clk_recalc,
|
||||
};
|
||||
|
||||
static struct clk tmu0_clk = {
|
||||
.name = "tmu0_clk",
|
||||
.ops = &tmu_clk_ops,
|
||||
.ops = &tmu0_clk_ops,
|
||||
};
|
||||
|
||||
static int tmu_timer_start(void)
|
||||
static void tmu1_clk_init(struct clk *clk)
|
||||
{
|
||||
ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
|
||||
return 0;
|
||||
u8 divisor = TMU_TCR_INIT & 0x7;
|
||||
ctrl_outw(divisor, TMU1_TCR);
|
||||
clk->rate = clk->parent->rate / (4 << (divisor << 1));
|
||||
}
|
||||
|
||||
static int tmu_timer_stop(void)
|
||||
static void tmu1_clk_recalc(struct clk *clk)
|
||||
{
|
||||
ctrl_outb(0, TMU_TSTR);
|
||||
return 0;
|
||||
u8 divisor = ctrl_inw(TMU1_TCR) & 0x7;
|
||||
clk->rate = clk->parent->rate / (4 << (divisor << 1));
|
||||
}
|
||||
|
||||
static struct clk_ops tmu1_clk_ops = {
|
||||
.init = tmu1_clk_init,
|
||||
.recalc = tmu1_clk_recalc,
|
||||
};
|
||||
|
||||
static struct clk tmu1_clk = {
|
||||
.name = "tmu1_clk",
|
||||
.ops = &tmu1_clk_ops,
|
||||
};
|
||||
|
||||
static int tmu_timer_init(void)
|
||||
{
|
||||
unsigned long interval;
|
||||
unsigned long frequency;
|
||||
|
||||
setup_irq(CONFIG_SH_TIMER_IRQ, &tmu_irq);
|
||||
setup_irq(CONFIG_SH_TIMER_IRQ, &tmu0_irq);
|
||||
|
||||
tmu0_clk.parent = clk_get(NULL, "module_clk");
|
||||
tmu1_clk.parent = clk_get(NULL, "module_clk");
|
||||
|
||||
/* Start TMU0 */
|
||||
tmu_timer_stop();
|
||||
|
||||
#if !defined(CONFIG_CPU_SUBTYPE_SH7300) && \
|
||||
!defined(CONFIG_CPU_SUBTYPE_SH7760) && \
|
||||
!defined(CONFIG_CPU_SUBTYPE_SH7785)
|
||||
|
@ -155,15 +179,29 @@ static int tmu_timer_init(void)
|
|||
#endif
|
||||
|
||||
clk_register(&tmu0_clk);
|
||||
clk_register(&tmu1_clk);
|
||||
clk_enable(&tmu0_clk);
|
||||
clk_enable(&tmu1_clk);
|
||||
|
||||
interval = (clk_get_rate(&tmu0_clk) + HZ / 2) / HZ;
|
||||
printk(KERN_INFO "Interval = %ld\n", interval);
|
||||
frequency = clk_get_rate(&tmu0_clk);
|
||||
interval = (frequency + HZ / 2) / HZ;
|
||||
|
||||
ctrl_outl(interval, TMU0_TCOR);
|
||||
ctrl_outl(interval, TMU0_TCNT);
|
||||
sh_hpt_frequency = clk_get_rate(&tmu1_clk);
|
||||
ctrl_outl(~0, TMU1_TCNT);
|
||||
ctrl_outl(~0, TMU1_TCOR);
|
||||
|
||||
tmu_timer_start();
|
||||
tmu0_timer_set_interval(interval, 1);
|
||||
|
||||
tmu0_clockevent.mult = div_sc(frequency, NSEC_PER_SEC,
|
||||
tmu0_clockevent.shift);
|
||||
tmu0_clockevent.max_delta_ns =
|
||||
clockevent_delta2ns(-1, &tmu0_clockevent);
|
||||
tmu0_clockevent.min_delta_ns =
|
||||
clockevent_delta2ns(1, &tmu0_clockevent);
|
||||
|
||||
tmu0_clockevent.cpumask = cpumask_of_cpu(0);
|
||||
|
||||
clockevents_register_device(&tmu0_clockevent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -172,9 +210,7 @@ struct sys_timer_ops tmu_timer_ops = {
|
|||
.init = tmu_timer_init,
|
||||
.start = tmu_timer_start,
|
||||
.stop = tmu_timer_stop,
|
||||
#ifndef CONFIG_GENERIC_TIME
|
||||
.get_offset = tmu_timer_get_offset,
|
||||
#endif
|
||||
.read = tmu_timer_read,
|
||||
};
|
||||
|
||||
struct sys_timer tmu_timer = {
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
#define __ASM_SH_TIMER_H
|
||||
|
||||
#include <linux/sysdev.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <asm/cpu/timer.h>
|
||||
|
||||
struct sys_timer_ops {
|
||||
int (*init)(void);
|
||||
int (*start)(void);
|
||||
int (*stop)(void);
|
||||
cycle_t (*read)(void);
|
||||
#ifndef CONFIG_GENERIC_TIME
|
||||
unsigned long (*get_offset)(void);
|
||||
#endif
|
||||
|
@ -18,29 +20,8 @@ struct sys_timer {
|
|||
|
||||
struct sys_device dev;
|
||||
struct sys_timer_ops *ops;
|
||||
|
||||
#ifdef CONFIG_NO_IDLE_HZ
|
||||
struct dyn_tick_timer *dyn_tick;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_NO_IDLE_HZ
|
||||
#define DYN_TICK_ENABLED (1 << 1)
|
||||
|
||||
struct dyn_tick_timer {
|
||||
spinlock_t lock;
|
||||
unsigned int state; /* Current state */
|
||||
int (*enable)(void); /* Enables dynamic tick */
|
||||
int (*disable)(void); /* Disables dynamic tick */
|
||||
void (*reprogram)(unsigned long); /* Reprograms the timer */
|
||||
int (*handler)(int, void *);
|
||||
};
|
||||
|
||||
void timer_dyn_reprogram(void);
|
||||
#else
|
||||
#define timer_dyn_reprogram() do { } while (0)
|
||||
#endif
|
||||
|
||||
#define TICK_SIZE (tick_nsec / 1000)
|
||||
|
||||
extern struct sys_timer tmu_timer, cmt_timer, mtu2_timer;
|
||||
|
@ -58,5 +39,7 @@ struct sys_timer *get_sys_timer(void);
|
|||
|
||||
/* arch/sh/kernel/time.c */
|
||||
void handle_timer_tick(void);
|
||||
extern unsigned long sh_hpt_frequency;
|
||||
extern struct clocksource clocksource_sh;
|
||||
|
||||
#endif /* __ASM_SH_TIMER_H */
|
||||
|
|
Loading…
Reference in a new issue