aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorJonas Aaberg <jonas.aberg@stericsson.com>2011-04-15 07:53:20 +0200
committerJonas ABERG <jonas.aberg@stericsson.com>2011-04-19 18:10:25 +0200
commit78c6544aa035c6698989aab91512cda135efe21f (patch)
treed7fa4eaec03cfbb77fafeed6be3471101b54b4b3 /arch
parentc97227d29d3287d95bab3f2ea03ee83c8f438fcf (diff)
ARM: ux500: timer-mtu: Add oneshot and broadcast support
The mtu timer can now be programmed as oneshot clockevent and as broadcast clock. ST-Ericsson Linux next: Not tested, ask SSM for ER ST-Ericsson ID: ER332789 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> Change-Id: I2b391503dc4ed94364dabcdd720a649af6c5f864 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21037 Reviewed-by: Mattias WALLIN <mattias.wallin@stericsson.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-ux500/timer-mtu.c178
1 files changed, 107 insertions, 71 deletions
diff --git a/arch/arm/mach-ux500/timer-mtu.c b/arch/arm/mach-ux500/timer-mtu.c
index dd083af05fd..3cdf3ca3c20 100644
--- a/arch/arm/mach-ux500/timer-mtu.c
+++ b/arch/arm/mach-ux500/timer-mtu.c
@@ -36,14 +36,18 @@
#define MTU_BGLR(x) (0x10 + 0x10 * (x) + 0x0c) /* At next overflow */
/* bits for the control register */
-#define MTU_CRn_ENA 0x80
-#define MTU_CRn_PERIODIC 0x40 /* if 0 = free-running */
-#define MTU_CRn_PRESCALE_MASK 0x0c
-#define MTU_CRn_PRESCALE_1 0x00
-#define MTU_CRn_PRESCALE_16 0x04
-#define MTU_CRn_PRESCALE_256 0x08
-#define MTU_CRn_32BITS 0x02
-#define MTU_CRn_ONESHOT 0x01 /* if 0 = wraps reloading from BGLR*/
+#define MTU_CRn_ENA (0x01 << 7)
+#define MTU_CRn_DIS (0x00 << 7)
+#define MTU_CRn_PERIODIC (0x01 << 6) /* if 0 = free-running */
+#define MTU_CRn_FREERUNNING (0x00 << 6)
+#define MTU_CRn_PRESCALE_MASK (0x03 << 2)
+#define MTU_CRn_PRESCALE_1 (0x00 << 2)
+#define MTU_CRn_PRESCALE_16 (0x01 << 2)
+#define MTU_CRn_PRESCALE_256 (0x02 << 2)
+#define MTU_CRn_32BITS (0x01 << 1)
+
+/* if 0 = wraps reloading from BGLR*/
+#define MTU_CRn_ONESHOT (0x01 << 0)
/* Other registers are usual amba/primecell registers, currently not used */
#define MTU_ITCR 0xff0
@@ -59,9 +63,9 @@
#define MTU_PCELL2 0xff8
#define MTU_PCELL3 0xffC
-static u32 u8500_count; /* accumulated count */
-static u32 u8500_cycle; /* write-once */
+static u32 u8500_cycle; /* write-once */
static __iomem void *mtu0_base;
+static bool mtu_periodic = true;
/*
* U8500 sched_clock implementation. It has a resolution of
@@ -141,10 +145,26 @@ static cycle_t u8500_read_timer_dummy(struct clocksource *cs)
{
return 0;
}
+static void mtu_clockevent_reset(void);
+static void mtu_clocksource_reset(void)
+{
+ writel(MTU_CRn_DIS, mtu0_base + MTU_CR(1));
-void mtu_timer_reset(void);
+ /* ClockSource: configure load and background-load, and fire it up */
+ writel(u8500_cycle, mtu0_base + MTU_LR(1));
+ writel(u8500_cycle, mtu0_base + MTU_BGLR(1));
-static void u8500_clocksource_resume(struct clocksource *cs)
+ writel(MTU_CRn_PRESCALE_1 | MTU_CRn_32BITS | MTU_CRn_ENA |
+ MTU_CRn_FREERUNNING, mtu0_base + MTU_CR(1));
+}
+
+void mtu_timer_reset(void)
+{
+ mtu_clocksource_reset();
+ mtu_clockevent_reset();
+}
+
+static void u8500_mtu_clocksource_resume(struct clocksource *cs)
{
mtu_timer_reset();
}
@@ -154,9 +174,9 @@ static struct clocksource u8500_clksrc = {
.rating = 120,
.read = u8500_read_timer_dummy,
.shift = 20,
- .mask = CLOCKSOURCE_MASK(32),
+ .mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
- .resume = u8500_clocksource_resume,
+ .resume = u8500_mtu_clocksource_resume,
};
#ifdef ARCH_HAS_READ_CURRENT_TIMER
@@ -179,98 +199,111 @@ int read_current_timer(unsigned long *timer_val)
*timer_val = u8500_read_timer(&u8500_clksrc);
return 0;
}
-
#endif
/*
* Clockevent device: currently only periodic mode is supported
*/
-static void u8500_clkevt_mode(enum clock_event_mode mode,
- struct clock_event_device *dev)
+
+static void mtu_clockevent_reset(void)
{
- unsigned long flags;
+ if (mtu_periodic) {
+
+ /* Timer: configure load and background-load, and fire it up */
+ writel(u8500_cycle, mtu0_base + MTU_LR(0));
+ writel(u8500_cycle, mtu0_base + MTU_BGLR(0));
+ writel(MTU_CRn_PERIODIC | MTU_CRn_PRESCALE_1 |
+ MTU_CRn_32BITS | MTU_CRn_ENA,
+ mtu0_base + MTU_CR(0));
+ writel(1 << 0, mtu0_base + MTU_IMSC);
+ }
+}
+
+static void u8500_mtu_clkevt_mode(enum clock_event_mode mode,
+ struct clock_event_device *dev)
+{
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
- /* enable interrupts -- and count current value? */
- raw_local_irq_save(flags);
- writel(1, mtu0_base + MTU_IMSC);
- raw_local_irq_restore(flags);
+ mtu_periodic = true;
+ mtu_clockevent_reset();
break;
case CLOCK_EVT_MODE_ONESHOT:
- BUG(); /* Not yet supported */
- /* FALLTHROUGH */
+ mtu_periodic = false;
+ break;
case CLOCK_EVT_MODE_SHUTDOWN:
case CLOCK_EVT_MODE_UNUSED:
- /* disable irq */
- raw_local_irq_save(flags);
+ writel(MTU_CRn_DIS, mtu0_base + MTU_CR(0));
writel(0, mtu0_base + MTU_IMSC);
- raw_local_irq_restore(flags);
break;
case CLOCK_EVT_MODE_RESUME:
break;
}
}
-static struct clock_event_device u8500_clkevt = {
- .name = "mtu_0",
- .features = CLOCK_EVT_FEAT_PERIODIC,
- .shift = 32,
- .rating = 100,
- .set_mode = u8500_clkevt_mode,
- .irq = IRQ_MTU0,
-};
+static int u8500_mtu_clkevt_next(unsigned long evt, struct clock_event_device *ev)
+{
+ writel(1 << 0, mtu0_base + MTU_IMSC);
+ writel(evt, mtu0_base + MTU_LR(0));
+ /* Load highest value, enable device, enable interrupts */
+ writel(MTU_CRn_ONESHOT | MTU_CRn_PRESCALE_1 |
+ MTU_CRn_32BITS | MTU_CRn_ENA,
+ mtu0_base + MTU_CR(0));
+ return 0;
+}
/*
* IRQ Handler for the timer 0 of the MTU block. The irq is not shared
* as we are the only users of mtu0 by now.
*/
-static irqreturn_t u8500_timer_interrupt(int irq, void *dev_id)
+static irqreturn_t u8500_timer_interrupt(int irq, void *dev)
{
+ struct clock_event_device *clkevt = dev;
/* ack: "interrupt clear register" */
writel(1 << 0, mtu0_base + MTU_ICR);
- u8500_clkevt.event_handler(&u8500_clkevt);
+ clkevt->event_handler(clkevt);
return IRQ_HANDLED;
}
/*
+ * Added here as asm/smp.h is removed in v2.6.34 and
+ * this funcitons is needed for current PM setup.
+ */
+#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
+void smp_timer_broadcast(const struct cpumask *mask);
+#endif
+
+static struct clock_event_device u8500_mtu_clkevt = {
+ .name = "mtu_0",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .shift = 32,
+ /* Must be of higher rating the timer-rtt at boot */
+ .rating = 100,
+ .set_mode = u8500_mtu_clkevt_mode,
+ .set_next_event = u8500_mtu_clkevt_next,
+ .irq = IRQ_MTU0,
+#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
+ .broadcast = smp_timer_broadcast,
+#endif
+ .cpumask = cpu_all_mask,
+};
+
+/*
* Set up timer interrupt, and return the current time in seconds.
*/
static struct irqaction u8500_timer_irq = {
.name = "MTU Timer Tick",
.flags = IRQF_DISABLED | IRQF_TIMER,
.handler = u8500_timer_interrupt,
+ .dev_id = &u8500_mtu_clkevt,
};
-void mtu_timer_reset(void)
-{
- u32 cr;
-
- writel(0, mtu0_base + MTU_CR(0)); /* off */
- writel(0, mtu0_base + MTU_CR(1)); /* off */
-
- /* Timer: configure load and background-load, and fire it up */
- writel(u8500_cycle, mtu0_base + MTU_LR(0));
- writel(u8500_cycle, mtu0_base + MTU_BGLR(0));
- cr = MTU_CRn_PERIODIC | (MTU_CRn_PRESCALE_1 << 2) | MTU_CRn_32BITS;
- writel(cr, mtu0_base + MTU_CR(0));
- writel(cr | MTU_CRn_ENA, mtu0_base + MTU_CR(0));
-
- /* CS: configure load and background-load, and fire it up */
- writel(u8500_cycle, mtu0_base + MTU_LR(1));
- writel(u8500_cycle, mtu0_base + MTU_BGLR(1));
- cr = (MTU_CRn_PRESCALE_1 << 2) | MTU_CRn_32BITS;
- writel(cr, mtu0_base + MTU_CR(1));
- writel(cr | MTU_CRn_ENA, mtu0_base + MTU_CR(1));
-}
-
void __init mtu_timer_init(void)
{
unsigned long rate;
struct clk *clk0;
- int bits;
clk0 = clk_get_sys("mtu0", NULL);
BUG_ON(IS_ERR(clk0));
@@ -287,29 +320,32 @@ void __init mtu_timer_init(void)
/* Save global pointer to mtu, used by functions above */
if (cpu_is_u5500()) {
- mtu0_base = __io_address(U5500_MTU0_BASE);
- } else if (cpu_is_u8500ed()) {
- mtu0_base = __io_address(U8500_MTU0_BASE_ED);
+ mtu0_base = ioremap(U5500_MTU0_BASE, SZ_4K);
} else if (cpu_is_u8500()) {
- mtu0_base = __io_address(U8500_MTU0_BASE);
- } else
+ mtu0_base = ioremap(U8500_MTU0_BASE, SZ_4K);
+ } else {
ux500_unknown_soc();
+ }
- /* Init the timer and register clocksource */
- mtu_timer_reset();
+ /* Restart clock source */
+ mtu_clocksource_reset();
/* Now the scheduling clock is ready */
u8500_clksrc.read = u8500_read_timer;
u8500_clksrc.mult = clocksource_hz2mult(rate, u8500_clksrc.shift);
- bits = 8*sizeof(u8500_count);
clocksource_register(&u8500_clksrc);
/* Register irq and clockevents */
+ u8500_mtu_clkevt.mult = div_sc(rate, NSEC_PER_SEC,
+ u8500_mtu_clkevt.shift);
+ u8500_mtu_clkevt.max_delta_ns = clockevent_delta2ns(0xffffffff,
+ &u8500_mtu_clkevt);
+ u8500_mtu_clkevt.min_delta_ns = clockevent_delta2ns(0xff,
+ &u8500_mtu_clkevt);
+
setup_irq(IRQ_MTU0, &u8500_timer_irq);
- u8500_clkevt.mult = div_sc(rate, NSEC_PER_SEC, u8500_clkevt.shift);
- u8500_clkevt.cpumask = cpumask_of(0);
- clockevents_register_device(&u8500_clkevt);
+ clockevents_register_device(&u8500_mtu_clkevt);
#ifdef ARCH_HAS_READ_CURRENT_TIMER
set_delay_fn(mtu_timer_delay_loop);
#endif