diff options
Diffstat (limited to 'arch/arm/plat-iop/time.c')
-rw-r--r-- | arch/arm/plat-iop/time.c | 176 |
1 files changed, 141 insertions, 35 deletions
diff --git a/arch/arm/plat-iop/time.c b/arch/arm/plat-iop/time.c index 8da95d57c21f..6c8a02ad98e3 100644 --- a/arch/arm/plat-iop/time.c +++ b/arch/arm/plat-iop/time.c | |||
@@ -19,6 +19,8 @@ | |||
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
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> | ||
23 | #include <linux/clockchips.h> | ||
22 | #include <mach/hardware.h> | 24 | #include <mach/hardware.h> |
23 | #include <asm/irq.h> | 25 | #include <asm/irq.h> |
24 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
@@ -26,45 +28,136 @@ | |||
26 | #include <asm/mach/time.h> | 28 | #include <asm/mach/time.h> |
27 | #include <mach/time.h> | 29 | #include <mach/time.h> |
28 | 30 | ||
31 | /* | ||
32 | * IOP clocksource (free-running timer 1). | ||
33 | */ | ||
34 | static cycle_t iop_clocksource_read(struct clocksource *unused) | ||
35 | { | ||
36 | return 0xffffffffu - read_tcr1(); | ||
37 | } | ||
38 | |||
39 | static struct clocksource iop_clocksource = { | ||
40 | .name = "iop_timer1", | ||
41 | .rating = 300, | ||
42 | .read = iop_clocksource_read, | ||
43 | .mask = CLOCKSOURCE_MASK(32), | ||
44 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
45 | }; | ||
46 | |||
47 | static void __init iop_clocksource_set_hz(struct clocksource *cs, unsigned int hz) | ||
48 | { | ||
49 | u64 temp; | ||
50 | u32 shift; | ||
51 | |||
52 | /* Find shift and mult values for hz. */ | ||
53 | shift = 32; | ||
54 | do { | ||
55 | temp = (u64) NSEC_PER_SEC << shift; | ||
56 | do_div(temp, hz); | ||
57 | if ((temp >> 32) == 0) | ||
58 | break; | ||
59 | } while (--shift != 0); | ||
60 | |||
61 | cs->shift = shift; | ||
62 | cs->mult = (u32) temp; | ||
63 | |||
64 | printk(KERN_INFO "clocksource: %s uses shift %u mult %#x\n", | ||
65 | cs->name, cs->shift, cs->mult); | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * IOP sched_clock() implementation via its clocksource. | ||
70 | */ | ||
71 | unsigned long long sched_clock(void) | ||
72 | { | ||
73 | cycle_t cyc = iop_clocksource_read(NULL); | ||
74 | struct clocksource *cs = &iop_clocksource; | ||
75 | |||
76 | return clocksource_cyc2ns(cyc, cs->mult, cs->shift); | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * IOP clockevents (interrupting timer 0). | ||
81 | */ | ||
82 | static int iop_set_next_event(unsigned long delta, | ||
83 | struct clock_event_device *unused) | ||
84 | { | ||
85 | u32 tmr = IOP_TMR_PRIVILEGED | IOP_TMR_RATIO_1_1; | ||
86 | |||
87 | BUG_ON(delta == 0); | ||
88 | write_tmr0(tmr & ~(IOP_TMR_EN | IOP_TMR_RELOAD)); | ||
89 | write_tcr0(delta); | ||
90 | write_tmr0((tmr & ~IOP_TMR_RELOAD) | IOP_TMR_EN); | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
29 | static unsigned long ticks_per_jiffy; | 95 | static unsigned long ticks_per_jiffy; |
30 | static unsigned long ticks_per_usec; | ||
31 | static unsigned long next_jiffy_time; | ||
32 | 96 | ||
33 | unsigned long iop_gettimeoffset(void) | 97 | static void iop_set_mode(enum clock_event_mode mode, |
98 | struct clock_event_device *unused) | ||
34 | { | 99 | { |
35 | unsigned long offset, temp; | 100 | u32 tmr = read_tmr0(); |
101 | |||
102 | switch (mode) { | ||
103 | case CLOCK_EVT_MODE_PERIODIC: | ||
104 | write_tmr0(tmr & ~IOP_TMR_EN); | ||
105 | write_tcr0(ticks_per_jiffy - 1); | ||
106 | tmr |= (IOP_TMR_RELOAD | IOP_TMR_EN); | ||
107 | break; | ||
108 | case CLOCK_EVT_MODE_ONESHOT: | ||
109 | /* ->set_next_event sets period and enables timer */ | ||
110 | tmr &= ~(IOP_TMR_RELOAD | IOP_TMR_EN); | ||
111 | break; | ||
112 | case CLOCK_EVT_MODE_RESUME: | ||
113 | tmr |= IOP_TMR_EN; | ||
114 | break; | ||
115 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
116 | case CLOCK_EVT_MODE_UNUSED: | ||
117 | default: | ||
118 | tmr &= ~IOP_TMR_EN; | ||
119 | break; | ||
120 | } | ||
36 | 121 | ||
37 | /* enable cp6, if necessary, to avoid taking the overhead of an | 122 | write_tmr0(tmr); |
38 | * undefined instruction trap | 123 | } |
39 | */ | 124 | |
40 | asm volatile ( | 125 | static struct clock_event_device iop_clockevent = { |
41 | "mrc p15, 0, %0, c15, c1, 0\n\t" | 126 | .name = "iop_timer0", |
42 | "tst %0, #(1 << 6)\n\t" | 127 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, |
43 | "orreq %0, %0, #(1 << 6)\n\t" | 128 | .rating = 300, |
44 | "mcreq p15, 0, %0, c15, c1, 0\n\t" | 129 | .set_next_event = iop_set_next_event, |
45 | #ifdef CONFIG_CPU_XSCALE | 130 | .set_mode = iop_set_mode, |
46 | "mrceq p15, 0, %0, c15, c1, 0\n\t" | 131 | }; |
47 | "moveq %0, %0\n\t" | 132 | |
48 | "subeq pc, pc, #4\n\t" | 133 | static void __init iop_clockevent_set_hz(struct clock_event_device *ce, unsigned int hz) |
49 | #endif | 134 | { |
50 | : "=r"(temp) : : "cc"); | 135 | u64 temp; |
51 | 136 | u32 shift; | |
52 | offset = next_jiffy_time - read_tcr1(); | 137 | |
53 | 138 | /* Find shift and mult values for hz. */ | |
54 | return offset / ticks_per_usec; | 139 | shift = 32; |
140 | do { | ||
141 | temp = (u64) hz << shift; | ||
142 | do_div(temp, NSEC_PER_SEC); | ||
143 | if ((temp >> 32) == 0) | ||
144 | break; | ||
145 | } while (--shift != 0); | ||
146 | |||
147 | ce->shift = shift; | ||
148 | ce->mult = (u32) temp; | ||
149 | |||
150 | printk(KERN_INFO "clockevent: %s uses shift %u mult %#lx\n", | ||
151 | ce->name, ce->shift, ce->mult); | ||
55 | } | 152 | } |
56 | 153 | ||
57 | static irqreturn_t | 154 | static irqreturn_t |
58 | iop_timer_interrupt(int irq, void *dev_id) | 155 | iop_timer_interrupt(int irq, void *dev_id) |
59 | { | 156 | { |
60 | write_tisr(1); | 157 | struct clock_event_device *evt = dev_id; |
61 | |||
62 | while ((signed long)(next_jiffy_time - read_tcr1()) | ||
63 | >= ticks_per_jiffy) { | ||
64 | timer_tick(); | ||
65 | next_jiffy_time -= ticks_per_jiffy; | ||
66 | } | ||
67 | 158 | ||
159 | write_tisr(1); | ||
160 | evt->event_handler(evt); | ||
68 | return IRQ_HANDLED; | 161 | return IRQ_HANDLED; |
69 | } | 162 | } |
70 | 163 | ||
@@ -72,6 +165,7 @@ static struct irqaction iop_timer_irq = { | |||
72 | .name = "IOP Timer Tick", | 165 | .name = "IOP Timer Tick", |
73 | .handler = iop_timer_interrupt, | 166 | .handler = iop_timer_interrupt, |
74 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | 167 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, |
168 | .dev_id = &iop_clockevent, | ||
75 | }; | 169 | }; |
76 | 170 | ||
77 | static unsigned long iop_tick_rate; | 171 | static unsigned long iop_tick_rate; |
@@ -86,21 +180,33 @@ void __init iop_init_time(unsigned long tick_rate) | |||
86 | u32 timer_ctl; | 180 | u32 timer_ctl; |
87 | 181 | ||
88 | ticks_per_jiffy = DIV_ROUND_CLOSEST(tick_rate, HZ); | 182 | ticks_per_jiffy = DIV_ROUND_CLOSEST(tick_rate, HZ); |
89 | ticks_per_usec = tick_rate / 1000000; | ||
90 | next_jiffy_time = 0xffffffff; | ||
91 | iop_tick_rate = tick_rate; | 183 | iop_tick_rate = tick_rate; |
92 | 184 | ||
93 | timer_ctl = IOP_TMR_EN | IOP_TMR_PRIVILEGED | | 185 | timer_ctl = IOP_TMR_EN | IOP_TMR_PRIVILEGED | |
94 | IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1; | 186 | IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1; |
95 | 187 | ||
96 | /* | 188 | /* |
97 | * We use timer 0 for our timer interrupt, and timer 1 as | 189 | * Set up interrupting clockevent timer 0. |
98 | * monotonic counter for tracking missed jiffies. | ||
99 | */ | 190 | */ |
191 | write_tmr0(timer_ctl & ~IOP_TMR_EN); | ||
192 | setup_irq(IRQ_IOP_TIMER0, &iop_timer_irq); | ||
193 | iop_clockevent_set_hz(&iop_clockevent, tick_rate); | ||
194 | iop_clockevent.max_delta_ns = | ||
195 | clockevent_delta2ns(0xfffffffe, &iop_clockevent); | ||
196 | iop_clockevent.min_delta_ns = | ||
197 | clockevent_delta2ns(0xf, &iop_clockevent); | ||
198 | iop_clockevent.cpumask = cpumask_of(0); | ||
199 | clockevents_register_device(&iop_clockevent); | ||
100 | write_trr0(ticks_per_jiffy - 1); | 200 | write_trr0(ticks_per_jiffy - 1); |
201 | write_tcr0(ticks_per_jiffy - 1); | ||
101 | write_tmr0(timer_ctl); | 202 | write_tmr0(timer_ctl); |
203 | |||
204 | /* | ||
205 | * Set up free-running clocksource timer 1. | ||
206 | */ | ||
102 | write_trr1(0xffffffff); | 207 | write_trr1(0xffffffff); |
208 | write_tcr1(0xffffffff); | ||
103 | write_tmr1(timer_ctl); | 209 | write_tmr1(timer_ctl); |
104 | 210 | iop_clocksource_set_hz(&iop_clocksource, tick_rate); | |
105 | setup_irq(IRQ_IOP_TIMER0, &iop_timer_irq); | 211 | clocksource_register(&iop_clocksource); |
106 | } | 212 | } |