Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cevt-r4k.c
Go to the documentation of this file.
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 <[email protected]>
8  */
9 #include <linux/clockchips.h>
10 #include <linux/interrupt.h>
11 #include <linux/percpu.h>
12 #include <linux/smp.h>
13 #include <linux/irq.h>
14 
15 #include <asm/smtc_ipi.h>
16 #include <asm/time.h>
17 #include <asm/cevt-r4k.h>
18 #include <asm/gic.h>
19 
20 /*
21  * The SMTC Kernel for the 34K, 1004K, et. al. replaces several
22  * of these routines with SMTC-specific variants.
23  */
24 
25 #ifndef CONFIG_MIPS_MT_SMTC
26 
27 static int mips_next_event(unsigned long delta,
28  struct clock_event_device *evt)
29 {
30  unsigned int cnt;
31  int res;
32 
33  cnt = read_c0_count();
34  cnt += delta;
35  write_c0_compare(cnt);
36  res = ((int)(read_c0_count() - cnt) >= 0) ? -ETIME : 0;
37  return res;
38 }
39 
40 #endif /* CONFIG_MIPS_MT_SMTC */
41 
42 void mips_set_clock_mode(enum clock_event_mode mode,
43  struct clock_event_device *evt)
44 {
45  /* Nothing to do ... */
46 }
47 
48 DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
50 
51 #ifndef CONFIG_MIPS_MT_SMTC
52 
54 {
55  const int r2 = cpu_has_mips_r2;
56  struct clock_event_device *cd;
57  int cpu = smp_processor_id();
58 
59  /*
60  * Suckage alert:
61  * Before R2 of the architecture there was no way to see if a
62  * performance counter interrupt was pending, so we have to run
63  * the performance counter interrupt handler anyway.
64  */
65  if (handle_perf_irq(r2))
66  goto out;
67 
68  /*
69  * The same applies to performance counter interrupts. But with the
70  * above we now know that the reason we got here must be a timer
71  * interrupt. Being the paranoiacs we are we check anyway.
72  */
73  if (!r2 || (read_c0_cause() & (1 << 30))) {
74  /* Clear Count/Compare Interrupt */
76  cd = &per_cpu(mips_clockevent_device, cpu);
77  cd->event_handler(cd);
78  }
79 
80 out:
81  return IRQ_HANDLED;
82 }
83 
84 #endif /* Not CONFIG_MIPS_MT_SMTC */
85 
87  .handler = c0_compare_interrupt,
88  .flags = IRQF_PERCPU | IRQF_TIMER,
89  .name = "timer",
90 };
91 
92 
93 void mips_event_handler(struct clock_event_device *dev)
94 {
95 }
96 
97 /*
98  * FIXME: This doesn't hold for the relocated E9000 compare interrupt.
99  */
100 static int c0_compare_int_pending(void)
101 {
102 #ifdef CONFIG_IRQ_GIC
103  if (cpu_has_veic)
104  return gic_get_timer_pending();
105 #endif
106  return (read_c0_cause() >> cp0_compare_irq_shift) & (1ul << CAUSEB_IP);
107 }
108 
109 /*
110  * Compare interrupt can be routed and latched outside the core,
111  * so wait up to worst case number of cycle counter ticks for timer interrupt
112  * changes to propagate to the cause register.
113  */
114 #define COMPARE_INT_SEEN_TICKS 50
115 
117 {
118  unsigned int delta;
119  unsigned int cnt;
120 
121  /*
122  * IP7 already pending? Try to clear it by acking the timer.
123  */
124  if (c0_compare_int_pending()) {
125  cnt = read_c0_count();
126  write_c0_compare(cnt);
127  back_to_back_c0_hazard();
128  while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
129  if (!c0_compare_int_pending())
130  break;
131  if (c0_compare_int_pending())
132  return 0;
133  }
134 
135  for (delta = 0x10; delta <= 0x400000; delta <<= 1) {
136  cnt = read_c0_count();
137  cnt += delta;
138  write_c0_compare(cnt);
139  back_to_back_c0_hazard();
140  if ((int)(read_c0_count() - cnt) < 0)
141  break;
142  /* increase delta if the timer was already expired */
143  }
144 
145  while ((int)(read_c0_count() - cnt) <= 0)
146  ; /* Wait for expiry */
147 
148  while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
149  if (c0_compare_int_pending())
150  break;
151  if (!c0_compare_int_pending())
152  return 0;
153  cnt = read_c0_count();
154  write_c0_compare(cnt);
155  back_to_back_c0_hazard();
156  while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
157  if (!c0_compare_int_pending())
158  break;
159  if (c0_compare_int_pending())
160  return 0;
161 
162  /*
163  * Feels like a real count / compare timer.
164  */
165  return 1;
166 }
167 
168 #ifndef CONFIG_MIPS_MT_SMTC
169 
171 {
172  unsigned int cpu = smp_processor_id();
173  struct clock_event_device *cd;
174  unsigned int irq;
175 
177  return -ENXIO;
178 
179  if (!c0_compare_int_usable())
180  return -ENXIO;
181 
182  /*
183  * With vectored interrupts things are getting platform specific.
184  * get_c0_compare_int is a hook to allow a platform to return the
185  * interrupt number of it's liking.
186  */
188  if (get_c0_compare_int)
189  irq = get_c0_compare_int();
190 
191  cd = &per_cpu(mips_clockevent_device, cpu);
192 
193  cd->name = "MIPS";
194  cd->features = CLOCK_EVT_FEAT_ONESHOT;
195 
196  clockevent_set_clock(cd, mips_hpt_frequency);
197 
198  /* Calculate the min / max delta */
199  cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
200  cd->min_delta_ns = clockevent_delta2ns(0x300, cd);
201 
202  cd->rating = 300;
203  cd->irq = irq;
204  cd->cpumask = cpumask_of(cpu);
205  cd->set_next_event = mips_next_event;
206  cd->set_mode = mips_set_clock_mode;
207  cd->event_handler = mips_event_handler;
208 
210 
211  if (cp0_timer_irq_installed)
212  return 0;
213 
214  cp0_timer_irq_installed = 1;
215 
216  setup_irq(irq, &c0_compare_irqaction);
217 
218  return 0;
219 }
220 
221 #endif /* Not CONFIG_MIPS_MT_SMTC */