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