--- zzzz-none-000/linux-2.4.17/arch/mips/au1000/common/time.c 2001-09-09 17:43:01.000000000 +0000 +++ sangam-fb-322/linux-2.4.17/arch/mips/au1000/common/time.c 2004-11-24 13:22:38.000000000 +0000 @@ -1,4 +1,5 @@ /* + * * Copyright (C) 2001 MontaVista Software, ppopov@mvista.com * Copied and modified Carsten Langgaard's time.c * @@ -23,7 +24,10 @@ * ######################################################################## * * Setting up the clock on the MIPS boards. + * */ + +#include #include #include #include @@ -32,28 +36,38 @@ #include #include +#include +#include #include #include #include #include +extern void startup_match20_interrupt(void); + extern volatile unsigned long wall_jiffies; unsigned long missed_heart_beats = 0; -unsigned long uart_baud_base; static unsigned long r4k_offset; /* Amount to increment compare reg each time */ static unsigned long r4k_cur; /* What counter should be at next timer irq */ extern rwlock_t xtime_lock; +unsigned int mips_counter_frequency = 0; + +/* Cycle counter value at the previous timer interrupt.. */ +static unsigned int timerhi = 0, timerlo = 0; -#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) +#ifdef CONFIG_PM +#define MATCH20_INC 328 +extern void startup_match20_interrupt(void); +static unsigned long last_pc0, last_match20; +#endif static inline void ack_r4ktimer(unsigned long newval) { write_32bit_cp0_register(CP0_COMPARE, newval); } - /* * There are a lot of conceptually broken versions of the MIPS timer interrupt * handler floating around. This one is rather different, but the algorithm @@ -62,12 +76,27 @@ unsigned long wtimer; void mips_timer_interrupt(struct pt_regs *regs) { - int irq = 7; + int irq = 63; + unsigned long count; + int cpu = smp_processor_id(); + + irq_enter(cpu, irq); + kstat.irqs[cpu][irq]++; + +#ifdef CONFIG_PM + printk(KERN_ERR "Unexpected CP0 interrupt\n"); + regs->cp0_status &= ~IE_IRQ5; /* disable CP0 interrupt */ + return; +#endif if (r4k_offset == 0) goto null; do { + count = read_32bit_cp0_register(CP0_COUNT); + timerhi += (count < timerlo); /* Wrap around */ + timerlo = count; + kstat.irqs[0][irq]++; do_timer(regs); r4k_cur += r4k_offset; @@ -76,12 +105,61 @@ } while (((unsigned long)read_32bit_cp0_register(CP0_COUNT) - r4k_cur) < 0x7fffffff); + irq_exit(cpu, irq); + + if (softirq_pending(cpu)) + do_softirq(); return; null: ack_r4ktimer(0); } +#ifdef CONFIG_PM +void counter0_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long pc0; + int time_elapsed; + static int jiffie_drift = 0; + + kstat.irqs[0][irq]++; + if (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20) { + /* should never happen! */ + printk(KERN_WARNING "counter 0 w status eror\n"); + return; + } + + pc0 = inl(SYS_TOYREAD); + if (pc0 < last_match20) { + /* counter overflowed */ + time_elapsed = (0xffffffff - last_match20) + pc0; + } + else { + time_elapsed = pc0 - last_match20; + } + + while (time_elapsed > 0) { + do_timer(regs); + time_elapsed -= MATCH20_INC; + last_match20 += MATCH20_INC; + jiffie_drift++; + } + + last_pc0 = pc0; + outl(last_match20 + MATCH20_INC, SYS_TOYMATCH2); + au_sync(); + + /* our counter ticks at 10.009765625 ms/tick, we we're running + * almost 10uS too slow per tick. + */ + + if (jiffie_drift >= 999) { + jiffie_drift -= 999; + do_timer(regs); /* increment jiffies by one */ + } +} +#endif + /* * Figure out the r4k offset, the amount to increment the compare * register for each time tick. @@ -90,49 +168,48 @@ unsigned long cal_r4koff(void) { unsigned long count; - unsigned long cpu_pll; unsigned long cpu_speed; unsigned long start, end; unsigned long counter; - int i; int trim_divide = 16; + unsigned long flags; - counter = inl(PC_COUNTER_CNTRL); - outl(counter | PC_CNTRL_EN1, PC_COUNTER_CNTRL); + save_and_cli(flags); - while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_T1S); - outl(trim_divide-1, PC1_TRIM); /* RTC now ticks at 32.768/16 kHz */ - while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_T1S); - - while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_C1S); - outl (0, PC1_COUNTER_WRITE); - while (inl(PC_COUNTER_CNTRL) & PC_CNTRL_C1S); + counter = inl(SYS_COUNTER_CNTRL); + outl(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL); - start = inl(PC1_COUNTER_READ); + while (inl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); + outl(trim_divide-1, SYS_RTCTRIM); /* RTC now ticks at 32.768/16 kHz */ + while (inl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); + + while (inl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); + outl (0, SYS_TOYWRITE); + while (inl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); + + start = inl(SYS_RTCREAD); start += 2; /* wait for the beginning of a new tick */ - while (inl(PC1_COUNTER_READ) < start); + while (inl(SYS_RTCREAD) < start); /* Start r4k counter. */ write_32bit_cp0_register(CP0_COUNT, 0); end = start + (32768 / trim_divide)/2; /* wait 0.5 seconds */ - while (end > inl(PC1_COUNTER_READ)); + while (end > inl(SYS_RTCREAD)); count = read_32bit_cp0_register(CP0_COUNT); cpu_speed = count * 2; - uart_baud_base = (((cpu_speed) / 4) / 16); + mips_counter_frequency = count; + set_au1000_uart_baud_base(((cpu_speed) / 4) / 16); + restore_flags(flags); return (cpu_speed / HZ); } -static unsigned long __init get_mips_time(void) -{ - return inl(PC0_COUNTER_READ); -} void __init time_init(void) { - unsigned int est_freq, flags; + unsigned int est_freq; printk("calculating r4koff... "); r4k_offset = cal_r4koff(); @@ -144,25 +221,51 @@ est_freq -= est_freq%10000; printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, (est_freq%1000000)*100/1000000); + set_au1000_speed(est_freq); + set_au1000_lcd_clock(); // program the LCD clock r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset); write_32bit_cp0_register(CP0_COMPARE, r4k_cur); - set_cp0_status(ALLINTS); - /* Read time from the RTC chipset. */ - write_lock_irqsave (&xtime_lock, flags); - xtime.tv_sec = get_mips_time(); + /* no RTC on the pb1000 */ + xtime.tv_sec = 0; xtime.tv_usec = 0; - write_unlock_irqrestore(&xtime_lock, flags); + +#ifdef CONFIG_PM + /* + * setup counter 0, since it keeps ticking after a + * 'wait' instruction has been executed. The CP0 timer and + * counter 1 do NOT continue running after 'wait' + * + * It's too early to call request_irq() here, so we handle + * counter 0 interrupt as a special irq and it doesn't show + * up under /proc/interrupts. + */ + while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S); + writel(0, SYS_TOYWRITE); + while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S); + + writel(readl(SYS_WAKEMSK) | (1<<8), SYS_WAKEMSK); + writel(~0, SYS_WAKESRC); + au_sync(); + while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20); + + /* setup match20 to interrupt once every 10ms */ + last_pc0 = last_match20 = readl(SYS_TOYREAD); + writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2); + au_sync(); + while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20); + startup_match20_interrupt(); +#endif + + //set_cp0_status(ALLINTS); + au_sync(); } /* This is for machines which generate the exact clock. */ #define USECS_PER_JIFFY (1000000/HZ) -#define USECS_PER_JIFFY_FRAC ((1000000ULL << 32) / HZ & 0xffffffff) - -/* Cycle counter value at the previous timer interrupt.. */ +#define USECS_PER_JIFFY_FRAC (0x100000000*1000000/HZ&0xffffffff) -static unsigned int timerhi = 0, timerlo = 0; static unsigned long div64_32(unsigned long v1, unsigned long v2, unsigned long v3) @@ -173,11 +276,28 @@ } -/* - * FIXME: Does playing with the RP bit in c0_status interfere with this code? - */ static unsigned long do_fast_gettimeoffset(void) { +#ifdef CONFIG_PM + unsigned long pc0; + unsigned long offset; + + pc0 = readl(SYS_TOYREAD); + if (pc0 < last_pc0) { + offset = 0xffffffff - last_pc0 + pc0; + printk("offset over: %x\n", (unsigned)offset); + } + else { + offset = (unsigned long)(((pc0 - last_pc0) * 305) / 10); + } + if ((pc0-last_pc0) > 2*MATCH20_INC) { + printk("huge offset %x, last_pc0 %x last_match20 %x pc0 %x\n", + (unsigned)offset, (unsigned)last_pc0, + (unsigned)last_match20, (unsigned)pc0); + } + au_sync(); + return offset; +#else u32 count; unsigned long res, tmp; unsigned long r0; @@ -225,6 +345,7 @@ res = USECS_PER_JIFFY-1; return res; +#endif } void do_gettimeofday(struct timeval *tv) @@ -274,13 +395,3 @@ write_unlock_irq (&xtime_lock); } - -/* - * The UART baud base is not known at compile time ... if - * we want to be able to use the same code on different - * speed CPUs. - */ -unsigned long get_au1000_uart_baud() -{ - return uart_baud_base; -}