1#include <linux/types.h> 2#include <linux/init.h> 3#include <linux/kernel_stat.h> 4#include <linux/sched.h> 5#include <linux/spinlock.h> 6#include <linux/interrupt.h> 7#include <linux/mc146818rtc.h> 8#include <linux/timex.h> 9 10#include <asm/mipsregs.h> 11#include <asm/ptrace.h> 12#include <asm/hardirq.h> 13#include <asm/div64.h> 14#include <asm/cpu.h> 15#include <asm/time.h> 16#include <asm/irq.h> 17#include <asm/mc146818-time.h> 18#include <asm/msc01_ic.h> 19#include <asm/smp.h> 20 21#include <asm/mips-boards/generic.h> 22#include <asm/mips-boards/prom.h> 23#include <asm/mips-boards/simint.h> 24 25 26unsigned long cpu_khz; 27 28irqreturn_t sim_timer_interrupt(int irq, void *dev_id) 29{ 30#ifdef CONFIG_SMP 31 int cpu = smp_processor_id(); 32 33 /* 34 * CPU 0 handles the global timer interrupt job 35 * resets count/compare registers to trigger next timer int. 36 */ 37#ifndef CONFIG_MIPS_MT_SMTC 38 if (cpu == 0) { 39 timer_interrupt(irq, dev_id); 40 } 41 else { 42 /* Everyone else needs to reset the timer int here as 43 ll_local_timer_interrupt doesn't */ 44 write_c0_compare (read_c0_count() + ( mips_hpt_frequency/HZ)); 45 } 46#else /* SMTC */ 47 /* 48 * In SMTC system, one Count/Compare set exists per VPE. 49 * Which TC within a VPE gets the interrupt is essentially 50 * random - we only know that it shouldn't be one with 51 * IXMT set. Whichever TC gets the interrupt needs to 52 * send special interprocessor interrupts to the other 53 * TCs to make sure that they schedule, etc. 54 * 55 * That code is specific to the SMTC kernel, not to 56 * the simulation platform, so it's invoked from 57 * the general MIPS timer_interrupt routine. 58 * 59 * We have a problem in that the interrupt vector code 60 * had to turn off the timer IM bit to avoid redundant 61 * entries, but we may never get to mips_cpu_irq_end 62 * to turn it back on again if the scheduler gets 63 * involved. So we clear the pending timer here, 64 * and re-enable the mask... 65 */ 66 67 int vpflags = dvpe(); 68 write_c0_compare (read_c0_count() - 1); 69 clear_c0_cause(0x100 << cp0_compare_irq); 70 set_c0_status(0x100 << cp0_compare_irq); 71 irq_enable_hazard(); 72 evpe(vpflags); 73 74 if(cpu_data[cpu].vpe_id == 0) timer_interrupt(irq, dev_id); 75 else write_c0_compare (read_c0_count() + ( mips_hpt_frequency/HZ)); 76 smtc_timer_broadcast(cpu_data[cpu].vpe_id); 77 78#endif /* CONFIG_MIPS_MT_SMTC */ 79 80 /* 81 * every CPU should do profiling and process accounting 82 */ 83 local_timer_interrupt (irq, dev_id); 84 return IRQ_HANDLED; 85#else 86 return timer_interrupt (irq, dev_id); 87#endif 88} 89 90 91 92/* 93 * Estimate CPU frequency. Sets mips_hpt_frequency as a side-effect 94 */ 95static unsigned int __init estimate_cpu_frequency(void) 96{ 97 unsigned int prid = read_c0_prid() & 0xffff00; 98 unsigned int count; 99 100 /* 101 * hardwire the board frequency to 12MHz. 102 */ 103 104 if ((prid == (PRID_COMP_MIPS | PRID_IMP_20KC)) || 105 (prid == (PRID_COMP_MIPS | PRID_IMP_25KF))) 106 count = 12000000; 107 else 108 count = 6000000; 109 110 mips_hpt_frequency = count; 111 112 if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) && 113 (prid != (PRID_COMP_MIPS | PRID_IMP_25KF))) 114 count *= 2; 115 116 count += 5000; /* round */ 117 count -= count%10000; 118 119 return count; 120} 121 122void __init sim_time_init(void) 123{ 124 unsigned int est_freq, flags; 125 126 local_irq_save(flags); 127 128 129 /* Set Data mode - binary. */ 130 CMOS_WRITE(CMOS_READ(RTC_CONTROL) | RTC_DM_BINARY, RTC_CONTROL); 131 132 133 est_freq = estimate_cpu_frequency (); 134 135 printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, 136 (est_freq%1000000)*100/1000000); 137 138 cpu_khz = est_freq / 1000; 139 140 local_irq_restore(flags); 141} 142 143static int mips_cpu_timer_irq; 144 145static void mips_timer_dispatch(void) 146{ 147 do_IRQ(mips_cpu_timer_irq); 148} 149 150 151void __init plat_timer_setup(struct irqaction *irq) 152{ 153 if (cpu_has_veic) { 154 set_vi_handler(MSC01E_INT_CPUCTR, mips_timer_dispatch); 155 mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR; 156 } 157 else { 158 if (cpu_has_vint) 159 set_vi_handler(cp0_compare_irq, mips_timer_dispatch); 160 mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq; 161 } 162 163 /* we are using the cpu counter for timer interrupts */ 164 irq->handler = sim_timer_interrupt; 165 setup_irq(mips_cpu_timer_irq, irq); 166 167#ifdef CONFIG_SMP 168 /* irq_desc(riptor) is a global resource, when the interrupt overlaps 169 on seperate cpu's the first one tries to handle the second interrupt. 170 The effect is that the int remains disabled on the second cpu. 171 Mark the interrupt with IRQ_PER_CPU to avoid any confusion */ 172 irq_desc[mips_cpu_timer_irq].flags |= IRQ_PER_CPU; 173 set_irq_handler(mips_cpu_timer_irq, handle_percpu_irq); 174#endif 175} 176