ARM: Realview/Versatile: separate out common SP804 timer code
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
f4b8b319bf
commit
e388771458
@ -257,6 +257,7 @@ config ARCH_REALVIEW
|
|||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||||
select PLAT_VERSATILE
|
select PLAT_VERSATILE
|
||||||
|
select ARM_TIMER_SP804
|
||||||
help
|
help
|
||||||
This enables support for ARM Ltd RealView boards.
|
This enables support for ARM Ltd RealView boards.
|
||||||
|
|
||||||
@ -271,6 +272,7 @@ config ARCH_VERSATILE
|
|||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||||
select PLAT_VERSATILE
|
select PLAT_VERSATILE
|
||||||
|
select ARM_TIMER_SP804
|
||||||
help
|
help
|
||||||
This enables support for ARM Ltd Versatile board.
|
This enables support for ARM Ltd Versatile board.
|
||||||
|
|
||||||
@ -944,6 +946,9 @@ config PLAT_PXA
|
|||||||
config PLAT_VERSATILE
|
config PLAT_VERSATILE
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config ARM_TIMER_SP804
|
||||||
|
bool
|
||||||
|
|
||||||
source arch/arm/mm/Kconfig
|
source arch/arm/mm/Kconfig
|
||||||
|
|
||||||
config IWMMXT
|
config IWMMXT
|
||||||
|
@ -25,8 +25,6 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/amba/bus.h>
|
#include <linux/amba/bus.h>
|
||||||
#include <linux/amba/clcd.h>
|
#include <linux/amba/clcd.h>
|
||||||
#include <linux/clocksource.h>
|
|
||||||
#include <linux/clockchips.h>
|
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/smsc911x.h>
|
#include <linux/smsc911x.h>
|
||||||
#include <linux/ata_platform.h>
|
#include <linux/ata_platform.h>
|
||||||
@ -51,6 +49,7 @@
|
|||||||
#include <mach/clkdev.h>
|
#include <mach/clkdev.h>
|
||||||
#include <mach/platform.h>
|
#include <mach/platform.h>
|
||||||
#include <mach/irqs.h>
|
#include <mach/irqs.h>
|
||||||
|
#include <plat/timer-sp.h>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
@ -645,133 +644,6 @@ void __iomem *timer1_va_base;
|
|||||||
void __iomem *timer2_va_base;
|
void __iomem *timer2_va_base;
|
||||||
void __iomem *timer3_va_base;
|
void __iomem *timer3_va_base;
|
||||||
|
|
||||||
/*
|
|
||||||
* How long is the timer interval?
|
|
||||||
*/
|
|
||||||
#define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10)
|
|
||||||
#if TIMER_INTERVAL >= 0x100000
|
|
||||||
#define TIMER_RELOAD (TIMER_INTERVAL >> 8)
|
|
||||||
#define TIMER_DIVISOR (TIMER_CTRL_DIV256)
|
|
||||||
#define TICKS2USECS(x) (256 * (x) / TICKS_PER_uSEC)
|
|
||||||
#elif TIMER_INTERVAL >= 0x10000
|
|
||||||
#define TIMER_RELOAD (TIMER_INTERVAL >> 4) /* Divide by 16 */
|
|
||||||
#define TIMER_DIVISOR (TIMER_CTRL_DIV16)
|
|
||||||
#define TICKS2USECS(x) (16 * (x) / TICKS_PER_uSEC)
|
|
||||||
#else
|
|
||||||
#define TIMER_RELOAD (TIMER_INTERVAL)
|
|
||||||
#define TIMER_DIVISOR (TIMER_CTRL_DIV1)
|
|
||||||
#define TICKS2USECS(x) ((x) / TICKS_PER_uSEC)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void timer_set_mode(enum clock_event_mode mode,
|
|
||||||
struct clock_event_device *clk)
|
|
||||||
{
|
|
||||||
unsigned long ctrl;
|
|
||||||
|
|
||||||
switch(mode) {
|
|
||||||
case CLOCK_EVT_MODE_PERIODIC:
|
|
||||||
writel(TIMER_RELOAD, timer0_va_base + TIMER_LOAD);
|
|
||||||
|
|
||||||
ctrl = TIMER_CTRL_PERIODIC;
|
|
||||||
ctrl |= TIMER_CTRL_32BIT | TIMER_CTRL_IE | TIMER_CTRL_ENABLE;
|
|
||||||
break;
|
|
||||||
case CLOCK_EVT_MODE_ONESHOT:
|
|
||||||
/* period set, and timer enabled in 'next_event' hook */
|
|
||||||
ctrl = TIMER_CTRL_ONESHOT;
|
|
||||||
ctrl |= TIMER_CTRL_32BIT | TIMER_CTRL_IE;
|
|
||||||
break;
|
|
||||||
case CLOCK_EVT_MODE_UNUSED:
|
|
||||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
|
||||||
default:
|
|
||||||
ctrl = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
writel(ctrl, timer0_va_base + TIMER_CTRL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int timer_set_next_event(unsigned long evt,
|
|
||||||
struct clock_event_device *unused)
|
|
||||||
{
|
|
||||||
unsigned long ctrl = readl(timer0_va_base + TIMER_CTRL);
|
|
||||||
|
|
||||||
writel(evt, timer0_va_base + TIMER_LOAD);
|
|
||||||
writel(ctrl | TIMER_CTRL_ENABLE, timer0_va_base + TIMER_CTRL);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct clock_event_device timer0_clockevent = {
|
|
||||||
.name = "timer0",
|
|
||||||
.shift = 32,
|
|
||||||
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
|
||||||
.set_mode = timer_set_mode,
|
|
||||||
.set_next_event = timer_set_next_event,
|
|
||||||
.rating = 300,
|
|
||||||
.cpumask = cpu_all_mask,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void __init realview_clockevents_init(unsigned int timer_irq)
|
|
||||||
{
|
|
||||||
timer0_clockevent.irq = timer_irq;
|
|
||||||
timer0_clockevent.mult =
|
|
||||||
div_sc(1000000, NSEC_PER_SEC, timer0_clockevent.shift);
|
|
||||||
timer0_clockevent.max_delta_ns =
|
|
||||||
clockevent_delta2ns(0xffffffff, &timer0_clockevent);
|
|
||||||
timer0_clockevent.min_delta_ns =
|
|
||||||
clockevent_delta2ns(0xf, &timer0_clockevent);
|
|
||||||
|
|
||||||
clockevents_register_device(&timer0_clockevent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IRQ handler for the timer
|
|
||||||
*/
|
|
||||||
static irqreturn_t realview_timer_interrupt(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct clock_event_device *evt = &timer0_clockevent;
|
|
||||||
|
|
||||||
/* clear the interrupt */
|
|
||||||
writel(1, timer0_va_base + TIMER_INTCLR);
|
|
||||||
|
|
||||||
evt->event_handler(evt);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct irqaction realview_timer_irq = {
|
|
||||||
.name = "RealView Timer Tick",
|
|
||||||
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
|
|
||||||
.handler = realview_timer_interrupt,
|
|
||||||
};
|
|
||||||
|
|
||||||
static cycle_t realview_get_cycles(struct clocksource *cs)
|
|
||||||
{
|
|
||||||
return ~readl(timer3_va_base + TIMER_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct clocksource clocksource_realview = {
|
|
||||||
.name = "timer3",
|
|
||||||
.rating = 200,
|
|
||||||
.read = realview_get_cycles,
|
|
||||||
.mask = CLOCKSOURCE_MASK(32),
|
|
||||||
.shift = 20,
|
|
||||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void __init realview_clocksource_init(void)
|
|
||||||
{
|
|
||||||
/* setup timer 0 as free-running clocksource */
|
|
||||||
writel(0, timer3_va_base + TIMER_CTRL);
|
|
||||||
writel(0xffffffff, timer3_va_base + TIMER_LOAD);
|
|
||||||
writel(0xffffffff, timer3_va_base + TIMER_VALUE);
|
|
||||||
writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
|
|
||||||
timer3_va_base + TIMER_CTRL);
|
|
||||||
|
|
||||||
clocksource_realview.mult =
|
|
||||||
clocksource_khz2mult(1000, clocksource_realview.shift);
|
|
||||||
clocksource_register(&clocksource_realview);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up the clock source and clock events devices
|
* Set up the clock source and clock events devices
|
||||||
*/
|
*/
|
||||||
@ -799,13 +671,8 @@ void __init realview_timer_init(unsigned int timer_irq)
|
|||||||
writel(0, timer2_va_base + TIMER_CTRL);
|
writel(0, timer2_va_base + TIMER_CTRL);
|
||||||
writel(0, timer3_va_base + TIMER_CTRL);
|
writel(0, timer3_va_base + TIMER_CTRL);
|
||||||
|
|
||||||
/*
|
sp804_clocksource_init(timer3_va_base);
|
||||||
* Make irqs happen for the system timer
|
sp804_clockevents_init(timer0_va_base, timer_irq);
|
||||||
*/
|
|
||||||
setup_irq(timer_irq, &realview_timer_irq);
|
|
||||||
|
|
||||||
realview_clocksource_init();
|
|
||||||
realview_clockevents_init(timer_irq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -28,8 +28,6 @@
|
|||||||
#include <linux/amba/clcd.h>
|
#include <linux/amba/clcd.h>
|
||||||
#include <linux/amba/pl061.h>
|
#include <linux/amba/pl061.h>
|
||||||
#include <linux/amba/mmci.h>
|
#include <linux/amba/mmci.h>
|
||||||
#include <linux/clocksource.h>
|
|
||||||
#include <linux/clockchips.h>
|
|
||||||
#include <linux/cnt32_to_63.h>
|
#include <linux/cnt32_to_63.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
|
||||||
@ -50,6 +48,7 @@
|
|||||||
#include <mach/clkdev.h>
|
#include <mach/clkdev.h>
|
||||||
#include <mach/hardware.h>
|
#include <mach/hardware.h>
|
||||||
#include <mach/platform.h>
|
#include <mach/platform.h>
|
||||||
|
#include <plat/timer-sp.h>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
@ -877,120 +876,6 @@ void __init versatile_init(void)
|
|||||||
#define TIMER1_VA_BASE (__io_address(VERSATILE_TIMER0_1_BASE) + 0x20)
|
#define TIMER1_VA_BASE (__io_address(VERSATILE_TIMER0_1_BASE) + 0x20)
|
||||||
#define TIMER2_VA_BASE __io_address(VERSATILE_TIMER2_3_BASE)
|
#define TIMER2_VA_BASE __io_address(VERSATILE_TIMER2_3_BASE)
|
||||||
#define TIMER3_VA_BASE (__io_address(VERSATILE_TIMER2_3_BASE) + 0x20)
|
#define TIMER3_VA_BASE (__io_address(VERSATILE_TIMER2_3_BASE) + 0x20)
|
||||||
#define VA_IC_BASE __io_address(VERSATILE_VIC_BASE)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* How long is the timer interval?
|
|
||||||
*/
|
|
||||||
#define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10)
|
|
||||||
#if TIMER_INTERVAL >= 0x100000
|
|
||||||
#define TIMER_RELOAD (TIMER_INTERVAL >> 8)
|
|
||||||
#define TIMER_DIVISOR (TIMER_CTRL_DIV256)
|
|
||||||
#define TICKS2USECS(x) (256 * (x) / TICKS_PER_uSEC)
|
|
||||||
#elif TIMER_INTERVAL >= 0x10000
|
|
||||||
#define TIMER_RELOAD (TIMER_INTERVAL >> 4) /* Divide by 16 */
|
|
||||||
#define TIMER_DIVISOR (TIMER_CTRL_DIV16)
|
|
||||||
#define TICKS2USECS(x) (16 * (x) / TICKS_PER_uSEC)
|
|
||||||
#else
|
|
||||||
#define TIMER_RELOAD (TIMER_INTERVAL)
|
|
||||||
#define TIMER_DIVISOR (TIMER_CTRL_DIV1)
|
|
||||||
#define TICKS2USECS(x) ((x) / TICKS_PER_uSEC)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void timer_set_mode(enum clock_event_mode mode,
|
|
||||||
struct clock_event_device *clk)
|
|
||||||
{
|
|
||||||
unsigned long ctrl;
|
|
||||||
|
|
||||||
switch(mode) {
|
|
||||||
case CLOCK_EVT_MODE_PERIODIC:
|
|
||||||
writel(TIMER_RELOAD, TIMER0_VA_BASE + TIMER_LOAD);
|
|
||||||
|
|
||||||
ctrl = TIMER_CTRL_PERIODIC;
|
|
||||||
ctrl |= TIMER_CTRL_32BIT | TIMER_CTRL_IE | TIMER_CTRL_ENABLE;
|
|
||||||
break;
|
|
||||||
case CLOCK_EVT_MODE_ONESHOT:
|
|
||||||
/* period set, and timer enabled in 'next_event' hook */
|
|
||||||
ctrl = TIMER_CTRL_ONESHOT;
|
|
||||||
ctrl |= TIMER_CTRL_32BIT | TIMER_CTRL_IE;
|
|
||||||
break;
|
|
||||||
case CLOCK_EVT_MODE_UNUSED:
|
|
||||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
|
||||||
default:
|
|
||||||
ctrl = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
writel(ctrl, TIMER0_VA_BASE + TIMER_CTRL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int timer_set_next_event(unsigned long evt,
|
|
||||||
struct clock_event_device *unused)
|
|
||||||
{
|
|
||||||
unsigned long ctrl = readl(TIMER0_VA_BASE + TIMER_CTRL);
|
|
||||||
|
|
||||||
writel(evt, TIMER0_VA_BASE + TIMER_LOAD);
|
|
||||||
writel(ctrl | TIMER_CTRL_ENABLE, TIMER0_VA_BASE + TIMER_CTRL);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct clock_event_device timer0_clockevent = {
|
|
||||||
.name = "timer0",
|
|
||||||
.shift = 32,
|
|
||||||
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
|
||||||
.set_mode = timer_set_mode,
|
|
||||||
.set_next_event = timer_set_next_event,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IRQ handler for the timer
|
|
||||||
*/
|
|
||||||
static irqreturn_t versatile_timer_interrupt(int irq, void *dev_id)
|
|
||||||
{
|
|
||||||
struct clock_event_device *evt = &timer0_clockevent;
|
|
||||||
|
|
||||||
writel(1, TIMER0_VA_BASE + TIMER_INTCLR);
|
|
||||||
|
|
||||||
evt->event_handler(evt);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct irqaction versatile_timer_irq = {
|
|
||||||
.name = "Versatile Timer Tick",
|
|
||||||
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
|
|
||||||
.handler = versatile_timer_interrupt,
|
|
||||||
};
|
|
||||||
|
|
||||||
static cycle_t versatile_get_cycles(struct clocksource *cs)
|
|
||||||
{
|
|
||||||
return ~readl(TIMER3_VA_BASE + TIMER_VALUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct clocksource clocksource_versatile = {
|
|
||||||
.name = "timer3",
|
|
||||||
.rating = 200,
|
|
||||||
.read = versatile_get_cycles,
|
|
||||||
.mask = CLOCKSOURCE_MASK(32),
|
|
||||||
.shift = 20,
|
|
||||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init versatile_clocksource_init(void)
|
|
||||||
{
|
|
||||||
/* setup timer3 as free-running clocksource */
|
|
||||||
writel(0, TIMER3_VA_BASE + TIMER_CTRL);
|
|
||||||
writel(0xffffffff, TIMER3_VA_BASE + TIMER_LOAD);
|
|
||||||
writel(0xffffffff, TIMER3_VA_BASE + TIMER_VALUE);
|
|
||||||
writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
|
|
||||||
TIMER3_VA_BASE + TIMER_CTRL);
|
|
||||||
|
|
||||||
clocksource_versatile.mult =
|
|
||||||
clocksource_khz2mult(1000, clocksource_versatile.shift);
|
|
||||||
clocksource_register(&clocksource_versatile);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up timer interrupt, and return the current time in seconds.
|
* Set up timer interrupt, and return the current time in seconds.
|
||||||
@ -1019,22 +904,8 @@ static void __init versatile_timer_init(void)
|
|||||||
writel(0, TIMER2_VA_BASE + TIMER_CTRL);
|
writel(0, TIMER2_VA_BASE + TIMER_CTRL);
|
||||||
writel(0, TIMER3_VA_BASE + TIMER_CTRL);
|
writel(0, TIMER3_VA_BASE + TIMER_CTRL);
|
||||||
|
|
||||||
/*
|
sp804_clocksource_init(TIMER3_VA_BASE);
|
||||||
* Make irqs happen for the system timer
|
sp804_clockevents_init(TIMER0_VA_BASE, IRQ_TIMERINT0_1);
|
||||||
*/
|
|
||||||
setup_irq(IRQ_TIMERINT0_1, &versatile_timer_irq);
|
|
||||||
|
|
||||||
versatile_clocksource_init();
|
|
||||||
|
|
||||||
timer0_clockevent.mult =
|
|
||||||
div_sc(1000000, NSEC_PER_SEC, timer0_clockevent.shift);
|
|
||||||
timer0_clockevent.max_delta_ns =
|
|
||||||
clockevent_delta2ns(0xffffffff, &timer0_clockevent);
|
|
||||||
timer0_clockevent.min_delta_ns =
|
|
||||||
clockevent_delta2ns(0xf, &timer0_clockevent);
|
|
||||||
|
|
||||||
timer0_clockevent.cpumask = cpumask_of(0);
|
|
||||||
clockevents_register_device(&timer0_clockevent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sys_timer versatile_timer = {
|
struct sys_timer versatile_timer = {
|
||||||
|
@ -1 +1,2 @@
|
|||||||
obj-y := clock.o
|
obj-y := clock.o
|
||||||
|
obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o
|
||||||
|
2
arch/arm/plat-versatile/include/plat/timer-sp.h
Normal file
2
arch/arm/plat-versatile/include/plat/timer-sp.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
void sp804_clocksource_init(void __iomem *);
|
||||||
|
void sp804_clockevents_init(void __iomem *, unsigned int);
|
168
arch/arm/plat-versatile/timer-sp.c
Normal file
168
arch/arm/plat-versatile/timer-sp.c
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* linux/arch/arm/plat-versatile/timer-sp.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 1999 - 2003 ARM Limited
|
||||||
|
* Copyright (C) 2000 Deep Blue Solutions Ltd
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#include <linux/clocksource.h>
|
||||||
|
#include <linux/clockchips.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
|
#include <asm/hardware/arm_timer.h>
|
||||||
|
|
||||||
|
#include <mach/platform.h>
|
||||||
|
|
||||||
|
#include <plat/timer-sp.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* How long is the timer interval?
|
||||||
|
*/
|
||||||
|
#define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10)
|
||||||
|
#if TIMER_INTERVAL >= 0x100000
|
||||||
|
#define TIMER_RELOAD (TIMER_INTERVAL >> 8)
|
||||||
|
#define TIMER_DIVISOR (TIMER_CTRL_DIV256)
|
||||||
|
#elif TIMER_INTERVAL >= 0x10000
|
||||||
|
#define TIMER_RELOAD (TIMER_INTERVAL >> 4) /* Divide by 16 */
|
||||||
|
#define TIMER_DIVISOR (TIMER_CTRL_DIV16)
|
||||||
|
#else
|
||||||
|
#define TIMER_RELOAD (TIMER_INTERVAL)
|
||||||
|
#define TIMER_DIVISOR (TIMER_CTRL_DIV1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static void __iomem *clksrc_base;
|
||||||
|
|
||||||
|
static cycle_t sp804_read(struct clocksource *cs)
|
||||||
|
{
|
||||||
|
return ~readl(clksrc_base + TIMER_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct clocksource clocksource_sp804 = {
|
||||||
|
.name = "timer3",
|
||||||
|
.rating = 200,
|
||||||
|
.read = sp804_read,
|
||||||
|
.mask = CLOCKSOURCE_MASK(32),
|
||||||
|
.shift = 20,
|
||||||
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
void __init sp804_clocksource_init(void __iomem *base)
|
||||||
|
{
|
||||||
|
struct clocksource *cs = &clocksource_sp804;
|
||||||
|
|
||||||
|
clksrc_base = base;
|
||||||
|
|
||||||
|
/* setup timer 0 as free-running clocksource */
|
||||||
|
writel(0, clksrc_base + TIMER_CTRL);
|
||||||
|
writel(0xffffffff, clksrc_base + TIMER_LOAD);
|
||||||
|
writel(0xffffffff, clksrc_base + TIMER_VALUE);
|
||||||
|
writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
|
||||||
|
clksrc_base + TIMER_CTRL);
|
||||||
|
|
||||||
|
cs->mult = clocksource_khz2mult(1000, cs->shift);
|
||||||
|
clocksource_register(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void __iomem *clkevt_base;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQ handler for the timer
|
||||||
|
*/
|
||||||
|
static irqreturn_t sp804_timer_interrupt(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct clock_event_device *evt = dev_id;
|
||||||
|
|
||||||
|
/* clear the interrupt */
|
||||||
|
writel(1, clkevt_base + TIMER_INTCLR);
|
||||||
|
|
||||||
|
evt->event_handler(evt);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sp804_set_mode(enum clock_event_mode mode,
|
||||||
|
struct clock_event_device *evt)
|
||||||
|
{
|
||||||
|
unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE;
|
||||||
|
|
||||||
|
writel(ctrl, clkevt_base + TIMER_CTRL);
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case CLOCK_EVT_MODE_PERIODIC:
|
||||||
|
writel(TIMER_RELOAD, clkevt_base + TIMER_LOAD);
|
||||||
|
ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CLOCK_EVT_MODE_ONESHOT:
|
||||||
|
/* period set, and timer enabled in 'next_event' hook */
|
||||||
|
ctrl |= TIMER_CTRL_ONESHOT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CLOCK_EVT_MODE_UNUSED:
|
||||||
|
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(ctrl, clkevt_base + TIMER_CTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sp804_set_next_event(unsigned long next,
|
||||||
|
struct clock_event_device *evt)
|
||||||
|
{
|
||||||
|
unsigned long ctrl = readl(clkevt_base + TIMER_CTRL);
|
||||||
|
|
||||||
|
writel(next, clkevt_base + TIMER_LOAD);
|
||||||
|
writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct clock_event_device sp804_clockevent = {
|
||||||
|
.name = "timer0",
|
||||||
|
.shift = 32,
|
||||||
|
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
|
||||||
|
.set_mode = sp804_set_mode,
|
||||||
|
.set_next_event = sp804_set_next_event,
|
||||||
|
.rating = 300,
|
||||||
|
.cpumask = cpu_all_mask,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct irqaction sp804_timer_irq = {
|
||||||
|
.name = "timer",
|
||||||
|
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
|
||||||
|
.handler = sp804_timer_interrupt,
|
||||||
|
.dev_id = &sp804_clockevent,
|
||||||
|
};
|
||||||
|
|
||||||
|
void __init sp804_clockevents_init(void __iomem *base, unsigned int timer_irq)
|
||||||
|
{
|
||||||
|
struct clock_event_device *evt = &sp804_clockevent;
|
||||||
|
|
||||||
|
clkevt_base = base;
|
||||||
|
|
||||||
|
evt->irq = timer_irq;
|
||||||
|
evt->mult = div_sc(1000000, NSEC_PER_SEC, evt->shift);
|
||||||
|
evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt);
|
||||||
|
evt->min_delta_ns = clockevent_delta2ns(0xf, evt);
|
||||||
|
|
||||||
|
setup_irq(timer_irq, &sp804_timer_irq);
|
||||||
|
clockevents_register_device(evt);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user