diff options
author | Mikael Pettersson <mikpe@it.uu.se> | 2009-10-29 14:46:54 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2009-10-29 14:46:54 -0400 |
commit | 469d30448dad13600cdd246024f9db8e80614c45 (patch) | |
tree | a610e9dd4b40dd540376eba7aaaed2538298f828 /arch/arm/plat-iop | |
parent | a91549a8f27e63e0e537fe1c20d4845581de894f (diff) |
iop: clockevent support
This updates the IOP platform to expose the interrupting
timer 0 as a clockevent object. The timer interrupt handler
is changed to call the clockevent ->event_handler() instead
of timer_tick(), and ->set_next_event() and ->set_mode()
operations are added to allow the mode of the timer to be
updated (required for ONESHOT/NOHZ mode).
Timer 0 must now be properly initialised, which requires
a new write_tcr0() function from the mach-specific code.
The mode of timer 0 must be read at the start of ->set_mode(),
which requires a new read_tmr0() function from the mach-
specific code.
Initial setup of timer 0 is also rewritten to be more robust.
Tested on n2100, compile-tested for all plat-iop machines.
Signed-off-by: Mikael Pettersson <mikpe@it.uu.se>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'arch/arm/plat-iop')
-rw-r--r-- | arch/arm/plat-iop/time.c | 101 |
1 files changed, 90 insertions, 11 deletions
diff --git a/arch/arm/plat-iop/time.c b/arch/arm/plat-iop/time.c index 5506c9b45612..a550e96394ab 100644 --- a/arch/arm/plat-iop/time.c +++ b/arch/arm/plat-iop/time.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/timex.h> | 20 | #include <linux/timex.h> |
21 | #include <linux/io.h> | 21 | #include <linux/io.h> |
22 | #include <linux/clocksource.h> | 22 | #include <linux/clocksource.h> |
23 | #include <linux/clockchips.h> | ||
23 | #include <mach/hardware.h> | 24 | #include <mach/hardware.h> |
24 | #include <asm/irq.h> | 25 | #include <asm/irq.h> |
25 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
@@ -64,7 +65,81 @@ static void __init iop_clocksource_set_hz(struct clocksource *cs, unsigned int h | |||
64 | cs->name, cs->shift, cs->mult); | 65 | cs->name, cs->shift, cs->mult); |
65 | } | 66 | } |
66 | 67 | ||
68 | /* | ||
69 | * IOP clockevents (interrupting timer 0). | ||
70 | */ | ||
71 | static int iop_set_next_event(unsigned long delta, | ||
72 | struct clock_event_device *unused) | ||
73 | { | ||
74 | u32 tmr = IOP_TMR_PRIVILEGED | IOP_TMR_RATIO_1_1; | ||
75 | |||
76 | BUG_ON(delta == 0); | ||
77 | write_tmr0(tmr & ~(IOP_TMR_EN | IOP_TMR_RELOAD)); | ||
78 | write_tcr0(delta); | ||
79 | write_tmr0((tmr & ~IOP_TMR_RELOAD) | IOP_TMR_EN); | ||
80 | |||
81 | return 0; | ||
82 | } | ||
83 | |||
67 | static unsigned long ticks_per_jiffy; | 84 | static unsigned long ticks_per_jiffy; |
85 | |||
86 | static void iop_set_mode(enum clock_event_mode mode, | ||
87 | struct clock_event_device *unused) | ||
88 | { | ||
89 | u32 tmr = read_tmr0(); | ||
90 | |||
91 | switch (mode) { | ||
92 | case CLOCK_EVT_MODE_PERIODIC: | ||
93 | write_tmr0(tmr & ~IOP_TMR_EN); | ||
94 | write_tcr0(ticks_per_jiffy - 1); | ||
95 | tmr |= (IOP_TMR_RELOAD | IOP_TMR_EN); | ||
96 | break; | ||
97 | case CLOCK_EVT_MODE_ONESHOT: | ||
98 | /* ->set_next_event sets period and enables timer */ | ||
99 | tmr &= ~(IOP_TMR_RELOAD | IOP_TMR_EN); | ||
100 | break; | ||
101 | case CLOCK_EVT_MODE_RESUME: | ||
102 | tmr |= IOP_TMR_EN; | ||
103 | break; | ||
104 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
105 | case CLOCK_EVT_MODE_UNUSED: | ||
106 | default: | ||
107 | tmr &= ~IOP_TMR_EN; | ||
108 | break; | ||
109 | } | ||
110 | |||
111 | write_tmr0(tmr); | ||
112 | } | ||
113 | |||
114 | static struct clock_event_device iop_clockevent = { | ||
115 | .name = "iop_timer0", | ||
116 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
117 | .rating = 300, | ||
118 | .set_next_event = iop_set_next_event, | ||
119 | .set_mode = iop_set_mode, | ||
120 | }; | ||
121 | |||
122 | static void __init iop_clockevent_set_hz(struct clock_event_device *ce, unsigned int hz) | ||
123 | { | ||
124 | u64 temp; | ||
125 | u32 shift; | ||
126 | |||
127 | /* Find shift and mult values for hz. */ | ||
128 | shift = 32; | ||
129 | do { | ||
130 | temp = (u64) hz << shift; | ||
131 | do_div(temp, NSEC_PER_SEC); | ||
132 | if ((temp >> 32) == 0) | ||
133 | break; | ||
134 | } while (--shift != 0); | ||
135 | |||
136 | ce->shift = shift; | ||
137 | ce->mult = (u32) temp; | ||
138 | |||
139 | printk(KERN_INFO "clockevent: %s uses shift %u mult %#lx\n", | ||
140 | ce->name, ce->shift, ce->mult); | ||
141 | } | ||
142 | |||
68 | static unsigned long ticks_per_usec; | 143 | static unsigned long ticks_per_usec; |
69 | static unsigned long next_jiffy_time; | 144 | static unsigned long next_jiffy_time; |
70 | 145 | ||
@@ -95,14 +170,10 @@ unsigned long iop_gettimeoffset(void) | |||
95 | static irqreturn_t | 170 | static irqreturn_t |
96 | iop_timer_interrupt(int irq, void *dev_id) | 171 | iop_timer_interrupt(int irq, void *dev_id) |
97 | { | 172 | { |
98 | write_tisr(1); | 173 | struct clock_event_device *evt = dev_id; |
99 | |||
100 | while ((signed long)(next_jiffy_time - read_tcr1()) | ||
101 | >= ticks_per_jiffy) { | ||
102 | timer_tick(); | ||
103 | next_jiffy_time -= ticks_per_jiffy; | ||
104 | } | ||
105 | 174 | ||
175 | write_tisr(1); | ||
176 | evt->event_handler(evt); | ||
106 | return IRQ_HANDLED; | 177 | return IRQ_HANDLED; |
107 | } | 178 | } |
108 | 179 | ||
@@ -110,6 +181,7 @@ static struct irqaction iop_timer_irq = { | |||
110 | .name = "IOP Timer Tick", | 181 | .name = "IOP Timer Tick", |
111 | .handler = iop_timer_interrupt, | 182 | .handler = iop_timer_interrupt, |
112 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | 183 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, |
184 | .dev_id = &iop_clockevent, | ||
113 | }; | 185 | }; |
114 | 186 | ||
115 | static unsigned long iop_tick_rate; | 187 | static unsigned long iop_tick_rate; |
@@ -132,10 +204,19 @@ void __init iop_init_time(unsigned long tick_rate) | |||
132 | IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1; | 204 | IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1; |
133 | 205 | ||
134 | /* | 206 | /* |
135 | * We use timer 0 for our timer interrupt, and timer 1 as | 207 | * Set up interrupting clockevent timer 0. |
136 | * monotonic counter for tracking missed jiffies. | ||
137 | */ | 208 | */ |
209 | write_tmr0(timer_ctl & ~IOP_TMR_EN); | ||
210 | setup_irq(IRQ_IOP_TIMER0, &iop_timer_irq); | ||
211 | iop_clockevent_set_hz(&iop_clockevent, tick_rate); | ||
212 | iop_clockevent.max_delta_ns = | ||
213 | clockevent_delta2ns(0xfffffffe, &iop_clockevent); | ||
214 | iop_clockevent.min_delta_ns = | ||
215 | clockevent_delta2ns(0xf, &iop_clockevent); | ||
216 | iop_clockevent.cpumask = cpumask_of(0); | ||
217 | clockevents_register_device(&iop_clockevent); | ||
138 | write_trr0(ticks_per_jiffy - 1); | 218 | write_trr0(ticks_per_jiffy - 1); |
219 | write_tcr0(ticks_per_jiffy - 1); | ||
139 | write_tmr0(timer_ctl); | 220 | write_tmr0(timer_ctl); |
140 | 221 | ||
141 | /* | 222 | /* |
@@ -146,6 +227,4 @@ void __init iop_init_time(unsigned long tick_rate) | |||
146 | write_tmr1(timer_ctl); | 227 | write_tmr1(timer_ctl); |
147 | iop_clocksource_set_hz(&iop_clocksource, tick_rate); | 228 | iop_clocksource_set_hz(&iop_clocksource, tick_rate); |
148 | clocksource_register(&iop_clocksource); | 229 | clocksource_register(&iop_clocksource); |
149 | |||
150 | setup_irq(IRQ_IOP_TIMER0, &iop_timer_irq); | ||
151 | } | 230 | } |