aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/time.c
diff options
context:
space:
mode:
authorPavel Pisa <ppisa@pikron.com>2007-05-13 12:37:33 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-07-12 06:11:39 -0400
commit89bba43514d06478eb27e3fa9099a8ae7dee5589 (patch)
tree54437db2d06a9f9f95869ff97fc2f8a6c5165cec /arch/arm/mach-imx/time.c
parentb3e6a508ed920698d367e5993ed056d70364d91f (diff)
[ARM] 4374/3: i.MX/MX1 clock event source
Support clock event source based on i.MX general purpose timer in free running timer mode. Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz> Acked-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-imx/time.c')
-rw-r--r--arch/arm/mach-imx/time.c121
1 files changed, 107 insertions, 14 deletions
diff --git a/arch/arm/mach-imx/time.c b/arch/arm/mach-imx/time.c
index 6960a9d04217..010f6fa984a6 100644
--- a/arch/arm/mach-imx/time.c
+++ b/arch/arm/mach-imx/time.c
@@ -3,6 +3,7 @@
3 * 3 *
4 * Copyright (C) 2000-2001 Deep Blue Solutions 4 * Copyright (C) 2000-2001 Deep Blue Solutions
5 * Copyright (C) 2002 Shane Nay (shane@minirl.com) 5 * Copyright (C) 2002 Shane Nay (shane@minirl.com)
6 * Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com)
6 * 7 *
7 * This program is free software; you can redistribute it and/or modify 8 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as 9 * it under the terms of the GNU General Public License version 2 as
@@ -15,6 +16,7 @@
15#include <linux/irq.h> 16#include <linux/irq.h>
16#include <linux/time.h> 17#include <linux/time.h>
17#include <linux/clocksource.h> 18#include <linux/clocksource.h>
19#include <linux/clockchips.h>
18 20
19#include <asm/hardware.h> 21#include <asm/hardware.h>
20#include <asm/io.h> 22#include <asm/io.h>
@@ -25,7 +27,8 @@
25/* Use timer 1 as system timer */ 27/* Use timer 1 as system timer */
26#define TIMER_BASE IMX_TIM1_BASE 28#define TIMER_BASE IMX_TIM1_BASE
27 29
28static unsigned long evt_diff; 30static struct clock_event_device clockevent_imx;
31static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_UNUSED;
29 32
30/* 33/*
31 * IRQ handler for the timer 34 * IRQ handler for the timer
@@ -33,25 +36,20 @@ static unsigned long evt_diff;
33static irqreturn_t 36static irqreturn_t
34imx_timer_interrupt(int irq, void *dev_id) 37imx_timer_interrupt(int irq, void *dev_id)
35{ 38{
39 struct clock_event_device *evt = &clockevent_imx;
36 uint32_t tstat; 40 uint32_t tstat;
41 irqreturn_t ret = IRQ_NONE;
37 42
38 /* clear the interrupt */ 43 /* clear the interrupt */
39 tstat = IMX_TSTAT(TIMER_BASE); 44 tstat = IMX_TSTAT(TIMER_BASE);
40 IMX_TSTAT(TIMER_BASE) = 0; 45 IMX_TSTAT(TIMER_BASE) = 0;
41 46
42 if (tstat & TSTAT_COMP) { 47 if (tstat & TSTAT_COMP) {
43 do { 48 evt->event_handler(evt);
44 49 ret = IRQ_HANDLED;
45 write_seqlock(&xtime_lock);
46 timer_tick();
47 write_sequnlock(&xtime_lock);
48 IMX_TCMP(TIMER_BASE) += evt_diff;
49
50 } while (unlikely((int32_t)(IMX_TCMP(TIMER_BASE)
51 - IMX_TCN(TIMER_BASE)) < 0));
52 } 50 }
53 51
54 return IRQ_HANDLED; 52 return ret;
55} 53}
56 54
57static struct irqaction imx_timer_irq = { 55static struct irqaction imx_timer_irq = {
@@ -70,10 +68,8 @@ static void __init imx_timer_hardware_init(void)
70 */ 68 */
71 IMX_TCTL(TIMER_BASE) = 0; 69 IMX_TCTL(TIMER_BASE) = 0;
72 IMX_TPRER(TIMER_BASE) = 0; 70 IMX_TPRER(TIMER_BASE) = 0;
73 IMX_TCMP(TIMER_BASE) = LATCH - 1;
74 71
75 IMX_TCTL(TIMER_BASE) = TCTL_FRR | TCTL_CLK_PCLK1 | TCTL_IRQEN | TCTL_TEN; 72 IMX_TCTL(TIMER_BASE) = TCTL_FRR | TCTL_CLK_PCLK1 | TCTL_TEN;
76 evt_diff = LATCH;
77} 73}
78 74
79cycle_t imx_get_cycles(void) 75cycle_t imx_get_cycles(void)
@@ -99,11 +95,108 @@ static int __init imx_clocksource_init(void)
99 return 0; 95 return 0;
100} 96}
101 97
98static int imx_set_next_event(unsigned long evt,
99 struct clock_event_device *unused)
100{
101 unsigned long tcmp;
102
103 tcmp = IMX_TCN(TIMER_BASE) + evt;
104 IMX_TCMP(TIMER_BASE) = tcmp;
105
106 return (int32_t)(tcmp - IMX_TCN(TIMER_BASE)) < 0 ? -ETIME : 0;
107}
108
109#ifdef DEBUG
110static const char *clock_event_mode_label[]={
111 [CLOCK_EVT_MODE_PERIODIC] = "CLOCK_EVT_MODE_PERIODIC",
112 [CLOCK_EVT_MODE_ONESHOT] = "CLOCK_EVT_MODE_ONESHOT",
113 [CLOCK_EVT_MODE_SHUTDOWN] = "CLOCK_EVT_MODE_SHUTDOWN",
114 [CLOCK_EVT_MODE_UNUSED] = "CLOCK_EVT_MODE_UNUSED"
115};
116#endif /*DEBUG*/
117
118static void imx_set_mode(enum clock_event_mode mode, struct clock_event_device *evt)
119{
120 unsigned long flags;
121
122 /*
123 * The timer interrupt generation is disabled at least
124 * for enough time to call imx_set_next_event()
125 */
126 local_irq_save(flags);
127 /* Disable interrupt in GPT module */
128 IMX_TCTL(TIMER_BASE) &= ~TCTL_IRQEN;
129 if (mode != clockevent_mode) {
130 /* Set event time into far-far future */
131 IMX_TCMP(TIMER_BASE) = IMX_TCN(TIMER_BASE) - 3;
132 /* Clear pending interrupt */
133 IMX_TSTAT(TIMER_BASE) &= ~TSTAT_COMP;
134 }
135
136#ifdef DEBUG
137 printk(KERN_INFO "imx_set_mode: changing mode from %s to %s\n",
138 clock_event_mode_label[clockevent_mode], clock_event_mode_label[mode]);
139#endif /*DEBUG*/
140
141 /* Remember timer mode */
142 clockevent_mode = mode;
143 local_irq_restore(flags);
144
145 switch (mode) {
146 case CLOCK_EVT_MODE_PERIODIC:
147 printk(KERN_ERR "imx_set_mode: Periodic mode is not supported for i.MX\n");
148 break;
149 case CLOCK_EVT_MODE_ONESHOT:
150 /*
151 * Do not put overhead of interrupt enable/disable into
152 * imx_set_next_event(), the core has about 4 minutes
153 * to call imx_set_next_event() or shutdown clock after
154 * mode switching
155 */
156 local_irq_save(flags);
157 IMX_TCTL(TIMER_BASE) |= TCTL_IRQEN;
158 local_irq_restore(flags);
159 break;
160 case CLOCK_EVT_MODE_SHUTDOWN:
161 case CLOCK_EVT_MODE_UNUSED:
162 /* Left event sources disabled, no more interrupts appears */
163 break;
164 }
165}
166
167static struct clock_event_device clockevent_imx = {
168 .name = "imx_timer1",
169 .features = CLOCK_EVT_FEAT_ONESHOT,
170 .shift = 32,
171 .set_mode = imx_set_mode,
172 .set_next_event = imx_set_next_event,
173 .rating = 200,
174};
175
176static int __init imx_clockevent_init(void)
177{
178 clockevent_imx.mult = div_sc(imx_get_perclk1(), NSEC_PER_SEC,
179 clockevent_imx.shift);
180 clockevent_imx.max_delta_ns =
181 clockevent_delta2ns(0xfffffffe, &clockevent_imx);
182 clockevent_imx.min_delta_ns =
183 clockevent_delta2ns(0xf, &clockevent_imx);
184
185 clockevent_imx.cpumask = cpumask_of_cpu(0);
186
187 clockevents_register_device(&clockevent_imx);
188
189 return 0;
190}
191
192
102static void __init imx_timer_init(void) 193static void __init imx_timer_init(void)
103{ 194{
104 imx_timer_hardware_init(); 195 imx_timer_hardware_init();
105 imx_clocksource_init(); 196 imx_clocksource_init();
106 197
198 imx_clockevent_init();
199
107 /* 200 /*
108 * Make irqs happen for the system timer 201 * Make irqs happen for the system timer
109 */ 202 */