linux/arch/arm/mach-mmp/time.c

240 lines
5.2 KiB
C
Raw Normal View History

2009-01-20 06:15:18 +00:00
/*
* linux/arch/arm/mach-mmp/time.c
*
* Support for clocksource and clockevents
*
* Copyright (C) 2008 Marvell International Ltd.
* All rights reserved.
*
* 2008-04-11: Jason Chagas <Jason.chagas@marvell.com>
* 2008-10-08: Bin Yang <bin.yang@marvell.com>
*
* The timers module actually includes three timers, each timer with up to
2009-01-20 06:15:18 +00:00
* three match comparators. Timer #0 is used here in free-running mode as
* the clock source, and match comparator #1 used as clock event device.
*
* 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/interrupt.h>
#include <linux/clockchips.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
#include <asm/mach/time.h>
2009-01-20 06:15:18 +00:00
#include "addr-map.h"
#include "regs-timers.h"
#include "regs-apbc.h"
#include "irqs.h"
#include "cputype.h"
2009-01-20 06:15:18 +00:00
#include "clock.h"
#ifdef CONFIG_CPU_MMP2
#define MMP_CLOCK_FREQ 6500000
#else
#define MMP_CLOCK_FREQ 3250000
#endif
2009-01-20 06:15:18 +00:00
#define TIMERS_VIRT_BASE TIMERS1_VIRT_BASE
#define MAX_DELTA (0xfffffffe)
#define MIN_DELTA (16)
static void __iomem *mmp_timer_base = TIMERS_VIRT_BASE;
2009-01-20 06:15:18 +00:00
/*
* FIXME: the timer needs some delay to stablize the counter capture
*/
static inline uint32_t timer_read(void)
{
int delay = 100;
__raw_writel(1, mmp_timer_base + TMR_CVWR(1));
2009-01-20 06:15:18 +00:00
while (delay--)
cpu_relax();
return __raw_readl(mmp_timer_base + TMR_CVWR(1));
2009-01-20 06:15:18 +00:00
}
static u64 notrace mmp_read_sched_clock(void)
2009-01-20 06:15:18 +00:00
{
ARM: 7205/2: sched_clock: allow sched_clock to be selected at runtime sched_clock() is yet another blocker on the road to the single image. This patch implements an idea by Russell King: http://www.spinics.net/lists/linux-omap/msg49561.html Instead of asking the platform to implement both sched_clock() itself and the rollover callback, simply register a read() function, and let the ARM code care about sched_clock() itself, the conversion to ns and the rollover. sched_clock() uses this read() function as an indirection to the platform code. If the platform doesn't provide a read(), the code falls back to the jiffy counter (just like the default sched_clock). This allow some simplifications and possibly some footprint gain when multiple platforms are compiled in. Among the drawbacks, the removal of the *_fixed_sched_clock optimization which could negatively impact some platforms (sa1100, tegra, versatile and omap). Tested on 11MPCore, OMAP4 and Tegra. Cc: Imre Kaloz <kaloz@openwrt.org> Cc: Eric Miao <eric.y.miao@gmail.com> Cc: Colin Cross <ccross@android.com> Cc: Erik Gilling <konkers@android.com> Cc: Olof Johansson <olof@lixom.net> Cc: Sascha Hauer <kernel@pengutronix.de> Cc: Alessandro Rubini <rubini@unipv.it> Cc: STEricsson <STEricsson_nomadik_linux@list.st.com> Cc: Lennert Buytenhek <kernel@wantstofly.org> Cc: Ben Dooks <ben-linux@fluff.org> Tested-by: Jamie Iles <jamie@jamieiles.com> Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Kyungmin Park <kyungmin.park@samsung.com> Acked-by: Linus Walleij <linus.walleij@linaro.org> Acked-by: Nicolas Pitre <nico@linaro.org> Acked-by: Krzysztof Halasa <khc@pm.waw.pl> Acked-by: Kukjin Kim <kgene.kim@samsung.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2011-12-15 11:19:23 +00:00
return timer_read();
2009-01-20 06:15:18 +00:00
}
static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *c = dev_id;
/*
* Clear pending interrupt status.
*/
__raw_writel(0x01, mmp_timer_base + TMR_ICR(0));
/*
* Disable timer 0.
*/
__raw_writel(0x02, mmp_timer_base + TMR_CER);
2009-01-20 06:15:18 +00:00
c->event_handler(c);
2009-01-20 06:15:18 +00:00
return IRQ_HANDLED;
}
static int timer_set_next_event(unsigned long delta,
struct clock_event_device *dev)
{
unsigned long flags;
2009-01-20 06:15:18 +00:00
local_irq_save(flags);
/*
* Disable timer 0.
*/
__raw_writel(0x02, mmp_timer_base + TMR_CER);
/*
* Clear and enable timer match 0 interrupt.
*/
__raw_writel(0x01, mmp_timer_base + TMR_ICR(0));
__raw_writel(0x01, mmp_timer_base + TMR_IER(0));
2009-01-20 06:15:18 +00:00
/*
* Setup new clockevent timer value.
*/
__raw_writel(delta - 1, mmp_timer_base + TMR_TN_MM(0, 0));
/*
* Enable timer 0.
*/
__raw_writel(0x03, mmp_timer_base + TMR_CER);
2009-01-20 06:15:18 +00:00
local_irq_restore(flags);
2009-01-20 06:15:18 +00:00
return 0;
}
static int timer_set_shutdown(struct clock_event_device *evt)
2009-01-20 06:15:18 +00:00
{
unsigned long flags;
local_irq_save(flags);
/* disable the matching interrupt */
__raw_writel(0x00, mmp_timer_base + TMR_IER(0));
2009-01-20 06:15:18 +00:00
local_irq_restore(flags);
return 0;
2009-01-20 06:15:18 +00:00
}
static struct clock_event_device ckevt = {
.name = "clockevent",
.features = CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
.set_next_event = timer_set_next_event,
.set_state_shutdown = timer_set_shutdown,
.set_state_oneshot = timer_set_shutdown,
2009-01-20 06:15:18 +00:00
};
static cycle_t clksrc_read(struct clocksource *cs)
2009-01-20 06:15:18 +00:00
{
return timer_read();
}
static struct clocksource cksrc = {
.name = "clocksource",
.rating = 200,
.read = clksrc_read,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static void __init timer_config(void)
{
uint32_t ccr = __raw_readl(mmp_timer_base + TMR_CCR);
2009-01-20 06:15:18 +00:00
__raw_writel(0x0, mmp_timer_base + TMR_CER); /* disable */
2009-01-20 06:15:18 +00:00
ccr &= (cpu_is_mmp2()) ? (TMR_CCR_CS_0(0) | TMR_CCR_CS_1(0)) :
(TMR_CCR_CS_0(3) | TMR_CCR_CS_1(3));
__raw_writel(ccr, mmp_timer_base + TMR_CCR);
2009-01-20 06:15:18 +00:00
/* set timer 0 to periodic mode, and timer 1 to free-running mode */
__raw_writel(0x2, mmp_timer_base + TMR_CMR);
2009-01-20 06:15:18 +00:00
__raw_writel(0x1, mmp_timer_base + TMR_PLCR(0)); /* periodic */
__raw_writel(0x7, mmp_timer_base + TMR_ICR(0)); /* clear status */
__raw_writel(0x0, mmp_timer_base + TMR_IER(0));
2009-01-20 06:15:18 +00:00
__raw_writel(0x0, mmp_timer_base + TMR_PLCR(1)); /* free-running */
__raw_writel(0x7, mmp_timer_base + TMR_ICR(1)); /* clear status */
__raw_writel(0x0, mmp_timer_base + TMR_IER(1));
/* enable timer 1 counter */
__raw_writel(0x2, mmp_timer_base + TMR_CER);
2009-01-20 06:15:18 +00:00
}
static struct irqaction timer_irq = {
.name = "timer",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
2009-01-20 06:15:18 +00:00
.handler = timer_interrupt,
.dev_id = &ckevt,
};
void __init timer_init(int irq)
{
timer_config();
This cleanup series gets rid of <mach/timex.h> for platforms not using ARCH_MULTIPLATFORM. (For multi-platform code it's already unused since 387798b (ARM: initial multiplatform support).) To make this work some code out of arch/arm needed to be adapted. The respective changes got acks by their maintainers to be taken via armsoc (with Andrew Morton substituting for Alessandro Zummo as rtc maintainer). Compared to the previous pull request there was another patch added that fixes a (non-critical) regression on ixp4xx. Olof Johansson asked to not squash this fix into the original commit to save him from the need to reverify the series. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABCAAGBQJTA8PeAAoJEOLc3ZEyZpvW5GEP/iz+HIx8Rkf3exUOorZB0Kef dPC1Bmc4SThffhgmmXTjSapTzjfnuC8xq8dni008L7QR0LFJsC/rw8bR9GqYhTDs EP7Sk0vDJcTUw9dvlIG0jpBioxdsPY7isU8K85tr2a+dzi4JA2h8iv6gE7bsOf1c b18hIWp2ZZdeYQX3OcuzPeVfvXuMaayBJChq0akoW7zqxG2nHG9j1vCSOhBtrgpc xCTuEqPoaDOdXjdhyda//3SKkIYh1eMf+RWMgW38vz2uHEI3AsMn/EWe6pNRKzRt JdVC6LWFl5tl1Dz73NoGFQO+ztTBb2pTrmqggc+Hi4iTekJSmJSFU51D/E0hdJFj KmWDWPLiUdAItjPuRz/HyeZxoIZQjg9PJ8MkjwVNAz4f4Vmw2xNnAV1Eur3k9JyV fo55eaBvy2KIGzBB+/ksMUvs4HzMJ7Z/dVPzZYRF8VxlYFJXExT0O42oeJ8KsfH7 dJ1bjk+3VIWPLH3DHyyiIfBL1oxe4MemqrAREFnN2QxYHyCipXLwH35uNZXAqvcU jverroWnCdrpOn9KI+vpnp/kuE7Qc1IH/AwAZngPj2xhaFapiH6h1JK/xWcWjijR AKv1DhFJMqSp9fvclr/ZAb7o35V/LG0rpCs+oZumCCARwpxkbo8xXgG0CfPsYFrG KwLWPz5zwySGwvDZ2wub =g6PN -----END PGP SIGNATURE----- Merge tag 'dropmachtimexh-v2' of git://git.pengutronix.de/git/ukl/linux into next/cleanup This cleanup series gets rid of <mach/timex.h> for platforms not using ARCH_MULTIPLATFORM. (For multi-platform code it's already unused since 387798b (ARM: initial multiplatform support).) To make this work some code out of arch/arm needed to be adapted. The respective changes got acks by their maintainers to be taken via armsoc (with Andrew Morton substituting for Alessandro Zummo as rtc maintainer). Compared to the previous pull request there was another patch added that fixes a (non-critical) regression on ixp4xx. Olof Johansson asked to not squash this fix into the original commit to save him from the need to reverify the series. * tag 'dropmachtimexh-v2' of git://git.pengutronix.de/git/ukl/linux: ARM: ixp4xx: fix timer latch calculation ARM: drop <mach/timex.h> for !ARCH_MULTIPLATFORM, too ARM: rpc: stop using <mach/timex.h> ARM: ixp4xx: stop using <mach/timex.h> input: ixp4xx-beeper: don't use symbols from <mach/timex.h> ARM: at91: don't use <mach/timex.h> ARM: ep93xx: stop using mach/timex.h ARM: mmp: stop using mach/timex.h ARM: netx: stop using mach/timex.h ARM: sa1100: stop using mach/timex.h clocksource: sirf/marco+prima2: drop usage of CLOCK_TICK_RATE rtc: pxa: drop unused #define TIMER_FREQ rtc: at91sam9: include <mach/hardware.h> explicitly ARM/serial: at91: switch atmel serial to use gpiolib Signed-off-by: Olof Johansson <olof@lixom.net>
2014-02-19 06:19:33 +00:00
sched_clock_register(mmp_read_sched_clock, 32, MMP_CLOCK_FREQ);
2009-01-20 06:15:18 +00:00
ckevt.cpumask = cpumask_of(0);
setup_irq(irq, &timer_irq);
clocksource_register_hz(&cksrc, MMP_CLOCK_FREQ);
clockevents_config_and_register(&ckevt, MMP_CLOCK_FREQ,
MIN_DELTA, MAX_DELTA);
2009-01-20 06:15:18 +00:00
}
#ifdef CONFIG_OF
static const struct of_device_id mmp_timer_dt_ids[] = {
{ .compatible = "mrvl,mmp-timer", },
{}
};
void __init mmp_dt_init_timer(void)
{
struct device_node *np;
int irq, ret;
np = of_find_matching_node(NULL, mmp_timer_dt_ids);
if (!np) {
ret = -ENODEV;
goto out;
}
irq = irq_of_parse_and_map(np, 0);
if (!irq) {
ret = -EINVAL;
goto out;
}
mmp_timer_base = of_iomap(np, 0);
if (!mmp_timer_base) {
ret = -ENOMEM;
goto out;
}
timer_init(irq);
return;
out:
pr_err("Failed to get timer from device tree with error:%d\n", ret);
}
#endif