Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6] / arch / mips / kernel / cevt-r4k.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2007 MIPS Technologies, Inc.
7  * Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org>
8  */
9 #include <linux/clockchips.h>
10 #include <linux/interrupt.h>
11 #include <linux/percpu.h>
12
13 #include <asm/smtc_ipi.h>
14 #include <asm/time.h>
15
16 static int mips_next_event(unsigned long delta,
17                            struct clock_event_device *evt)
18 {
19         unsigned int cnt;
20         int res;
21
22 #ifdef CONFIG_MIPS_MT_SMTC
23         {
24         unsigned long flags, vpflags;
25         local_irq_save(flags);
26         vpflags = dvpe();
27 #endif
28         cnt = read_c0_count();
29         cnt += delta;
30         write_c0_compare(cnt);
31         res = ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
32 #ifdef CONFIG_MIPS_MT_SMTC
33         evpe(vpflags);
34         local_irq_restore(flags);
35         }
36 #endif
37         return res;
38 }
39
40 static void mips_set_mode(enum clock_event_mode mode,
41                           struct clock_event_device *evt)
42 {
43         /* Nothing to do ...  */
44 }
45
46 static DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
47 static int cp0_timer_irq_installed;
48
49 /*
50  * Timer ack for an R4k-compatible timer of a known frequency.
51  */
52 static void c0_timer_ack(void)
53 {
54         write_c0_compare(read_c0_compare());
55 }
56
57 /*
58  * Possibly handle a performance counter interrupt.
59  * Return true if the timer interrupt should not be checked
60  */
61 static inline int handle_perf_irq(int r2)
62 {
63         /*
64          * The performance counter overflow interrupt may be shared with the
65          * timer interrupt (cp0_perfcount_irq < 0). If it is and a
66          * performance counter has overflowed (perf_irq() == IRQ_HANDLED)
67          * and we can't reliably determine if a counter interrupt has also
68          * happened (!r2) then don't check for a timer interrupt.
69          */
70         return (cp0_perfcount_irq < 0) &&
71                 perf_irq() == IRQ_HANDLED &&
72                 !r2;
73 }
74
75 static irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
76 {
77         const int r2 = cpu_has_mips_r2;
78         struct clock_event_device *cd;
79         int cpu = smp_processor_id();
80
81         /*
82          * Suckage alert:
83          * Before R2 of the architecture there was no way to see if a
84          * performance counter interrupt was pending, so we have to run
85          * the performance counter interrupt handler anyway.
86          */
87         if (handle_perf_irq(r2))
88                 goto out;
89
90         /*
91          * The same applies to performance counter interrupts.  But with the
92          * above we now know that the reason we got here must be a timer
93          * interrupt.  Being the paranoiacs we are we check anyway.
94          */
95         if (!r2 || (read_c0_cause() & (1 << 30))) {
96                 c0_timer_ack();
97 #ifdef CONFIG_MIPS_MT_SMTC
98                 if (cpu_data[cpu].vpe_id)
99                         goto out;
100                 cpu = 0;
101 #endif
102                 cd = &per_cpu(mips_clockevent_device, cpu);
103                 cd->event_handler(cd);
104         }
105
106 out:
107         return IRQ_HANDLED;
108 }
109
110 static struct irqaction c0_compare_irqaction = {
111         .handler = c0_compare_interrupt,
112 #ifdef CONFIG_MIPS_MT_SMTC
113         .flags = IRQF_DISABLED,
114 #else
115         .flags = IRQF_DISABLED | IRQF_PERCPU,
116 #endif
117         .name = "timer",
118 };
119
120 #ifdef CONFIG_MIPS_MT_SMTC
121 DEFINE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
122
123 static void smtc_set_mode(enum clock_event_mode mode,
124                           struct clock_event_device *evt)
125 {
126 }
127
128 static void mips_broadcast(cpumask_t mask)
129 {
130         unsigned int cpu;
131
132         for_each_cpu_mask(cpu, mask)
133                 smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
134 }
135
136 static void setup_smtc_dummy_clockevent_device(void)
137 {
138         //uint64_t mips_freq = mips_hpt_^frequency;
139         unsigned int cpu = smp_processor_id();
140         struct clock_event_device *cd;
141
142         cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
143
144         cd->name                = "SMTC";
145         cd->features            = CLOCK_EVT_FEAT_DUMMY;
146
147         /* Calculate the min / max delta */
148         cd->mult        = 0; //div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
149         cd->shift               = 0; //32;
150         cd->max_delta_ns        = 0; //clockevent_delta2ns(0x7fffffff, cd);
151         cd->min_delta_ns        = 0; //clockevent_delta2ns(0x30, cd);
152
153         cd->rating              = 200;
154         cd->irq                 = 17; //-1;
155 //      if (cpu)
156 //              cd->cpumask     = CPU_MASK_ALL; // cpumask_of_cpu(cpu);
157 //      else
158                 cd->cpumask     = cpumask_of_cpu(cpu);
159
160         cd->set_mode            = smtc_set_mode;
161
162         cd->broadcast           = mips_broadcast;
163
164         clockevents_register_device(cd);
165 }
166 #endif
167
168 static void mips_event_handler(struct clock_event_device *dev)
169 {
170 }
171
172 /*
173  * FIXME: This doesn't hold for the relocated E9000 compare interrupt.
174  */
175 static int c0_compare_int_pending(void)
176 {
177         return (read_c0_cause() >> cp0_compare_irq) & 0x100;
178 }
179
180 static int c0_compare_int_usable(void)
181 {
182         const unsigned int delta = 0x300000;
183         unsigned int cnt;
184
185         /*
186          * IP7 already pending?  Try to clear it by acking the timer.
187          */
188         if (c0_compare_int_pending()) {
189                 write_c0_compare(read_c0_compare());
190                 irq_disable_hazard();
191                 if (c0_compare_int_pending())
192                         return 0;
193         }
194
195         cnt = read_c0_count();
196         cnt += delta;
197         write_c0_compare(cnt);
198
199         while ((long)(read_c0_count() - cnt) <= 0)
200                 ;       /* Wait for expiry  */
201
202         if (!c0_compare_int_pending())
203                 return 0;
204
205         write_c0_compare(read_c0_compare());
206         irq_disable_hazard();
207         if (c0_compare_int_pending())
208                 return 0;
209
210         /*
211          * Feels like a real count / compare timer.
212          */
213         return 1;
214 }
215
216 void __cpuinit mips_clockevent_init(void)
217 {
218         uint64_t mips_freq = mips_hpt_frequency;
219         unsigned int cpu = smp_processor_id();
220         struct clock_event_device *cd;
221         unsigned int irq = MIPS_CPU_IRQ_BASE + 7;
222
223         if (!cpu_has_counter)
224                 return;
225
226 #ifdef CONFIG_MIPS_MT_SMTC
227         setup_smtc_dummy_clockevent_device();
228
229         /*
230          * On SMTC we only register VPE0's compare interrupt as clockevent
231          * device.
232          */
233         if (cpu)
234                 return;
235 #endif
236
237         if (!c0_compare_int_usable())
238                 return;
239
240         cd = &per_cpu(mips_clockevent_device, cpu);
241
242         cd->name                = "MIPS";
243         cd->features            = CLOCK_EVT_FEAT_ONESHOT;
244
245         /* Calculate the min / max delta */
246         cd->mult        = div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
247         cd->shift               = 32;
248         cd->max_delta_ns        = clockevent_delta2ns(0x7fffffff, cd);
249         cd->min_delta_ns        = clockevent_delta2ns(0x300, cd);
250
251         cd->rating              = 300;
252         cd->irq                 = irq;
253 #ifdef CONFIG_MIPS_MT_SMTC
254         cd->cpumask             = CPU_MASK_ALL;
255 #else
256         cd->cpumask             = cpumask_of_cpu(cpu);
257 #endif
258         cd->set_next_event      = mips_next_event;
259         cd->set_mode            = mips_set_mode;
260         cd->event_handler       = mips_event_handler;
261
262         clockevents_register_device(cd);
263
264         if (!cp0_timer_irq_installed) {
265 #ifdef CONFIG_MIPS_MT_SMTC
266 #define CPUCTR_IMASKBIT (0x100 << cp0_compare_irq)
267                 setup_irq_smtc(irq, &c0_compare_irqaction, CPUCTR_IMASKBIT);
268 #else
269                 setup_irq(irq, &c0_compare_irqaction);
270 #endif /* CONFIG_MIPS_MT_SMTC */
271                 cp0_timer_irq_installed = 1;
272         }
273 }