diff -urNp xx-ref/Documentation/Configure.help xx/Documentation/Configure.help --- xx-ref/Documentation/Configure.help Thu Oct 10 05:52:42 2002 +++ xx/Documentation/Configure.help Thu Oct 10 05:52:49 2002 @@ -254,6 +254,14 @@ CONFIG_X86_NUMAQ You will need a new lynxer.elf file to flash your firmware with - send email to Martin.Bligh@us.ibm.com +IBM x440 Summit support +CONFIG_X86_CYCLONE + This option enables support for the IBM x440 and related multi-CEC + systems based on the Summit chipset. This options allows you to pass + "cyclone" as a boot option to make use of a performance counter on + the Cyclone chipset for calculating do_gettimeofday, greatly + improving performance when compared to the PIT based method. + IO-APIC support on uniprocessors CONFIG_X86_UP_IOAPIC An IO-APIC (I/O Advanced Programmable Interrupt Controller) is an diff -urNp xx-ref/arch/i386/config.in xx/arch/i386/config.in --- xx-ref/arch/i386/config.in Thu Oct 10 05:52:44 2002 +++ xx/arch/i386/config.in Thu Oct 10 05:52:49 2002 @@ -244,8 +244,9 @@ else fi fi +bool 'IBM x440 Gettimeofday Cyclone Timer support' CONFIG_X86_CYCLONE bool 'Unsynced TSC support' CONFIG_X86_TSC_DISABLE -if [ "$CONFIG_X86_TSC_DISABLE" != "y" -a "$CONFIG_X86_HAS_TSC" = "y" ]; then +if [ "$CONFIG_X86_TSC_DISABLE" != "y" -a "$CONFIG_X86_HAS_TSC" = "y" -a "$CONFIG_X86_CYCLONE" != "y" ]; then define_bool CONFIG_X86_TSC y fi diff -urNp xx-ref/arch/i386/kernel/setup.c xx/arch/i386/kernel/setup.c --- xx-ref/arch/i386/kernel/setup.c Thu Oct 10 05:52:41 2002 +++ xx/arch/i386/kernel/setup.c Thu Oct 10 05:52:49 2002 @@ -1176,7 +1176,7 @@ __setup("cachesize=", cachesize_setup); #ifndef CONFIG_X86_TSC -static int tsc_disable __initdata = 0; +int tsc_disable __initdata = 0; static int __init notsc_setup(char *str) { diff -urNp xx-ref/arch/i386/kernel/time.c xx/arch/i386/kernel/time.c --- xx-ref/arch/i386/kernel/time.c Tue Oct 8 22:26:03 2002 +++ xx/arch/i386/kernel/time.c Thu Oct 10 05:52:49 2002 @@ -121,6 +121,173 @@ EXPORT_SYMBOL(i8253_lock); extern spinlock_t i8259A_lock; +#ifdef CONFIG_X86_CYCLONE + +#define CYCLONE_CBAR_ADDR 0xFEB00CD0 +#define CYCLONE_PMCC_OFFSET 0x51A0 +#define CYCLONE_MPMC_OFFSET 0x51D0 +#define CYCLONE_MPCS_OFFSET 0x51A8 +#define CYCLONE_TIMER_FREQ 100000000 + +const int use_cyclone = 0; +extern int tsc_disable; +/*XXX - should autodetect*/ +static int __init cyclone_setup(char *str) +{ + tsc_disable = 1; + use_cyclone = 1; + return 1; +} + +static u32* volatile cyclone_timer; /* Cyclone MPMC0 register */ +static u32 last_cyclone_timer; + +static inline void mark_timeoffset_cyclone(void) +{ + int count; + spin_lock(&i8253_lock); + /* quickly read the cyclone timer */ + if(cyclone_timer) + last_cyclone_timer = cyclone_timer[0]; + + /* calculate delay_at_last_interrupt */ + outb_p(0x00, 0x43); /* latch the count ASAP */ + + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + spin_unlock(&i8253_lock); + + count = ((LATCH-1) - count) * TICK_SIZE; + delay_at_last_interrupt = (count + LATCH/2) / LATCH; +} + +static unsigned long do_gettimeoffset_cyclone(void) +{ + u32 offset; + + if(!cyclone_timer) + return delay_at_last_interrupt; + + /* Read the cyclone timer */ + offset = cyclone_timer[0]; + + /* .. relative to previous jiffy */ + offset = offset - last_cyclone_timer; + + /* convert cyclone ticks to microseconds */ + /* XXX slow, can we speed this up? */ + offset = offset/(CYCLONE_TIMER_FREQ/1000000); + + /* our adjusted time offset in microseconds */ + return delay_at_last_interrupt + offset; +} + +static void __init init_cyclone_clock(void) +{ + u32* reg; + u32 base; /* saved cyclone base address */ + u32 pageaddr; /* page that contains cyclone_timer register */ + u32 offset; /* offset from pageaddr to cyclone_timer register */ + int i; + + printk(KERN_INFO "Summit chipset: Starting Cyclone Clock.\n"); + + /* find base address */ + pageaddr = (CYCLONE_CBAR_ADDR)&PAGE_MASK; + offset = (CYCLONE_CBAR_ADDR)&(~PAGE_MASK); + set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr); + reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset); + if(!reg){ + printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n"); + use_cyclone = 0; + return; + } + base = *reg; + if(!base){ + printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n"); + use_cyclone = 0; + return; + } + + /* setup PMCC */ + pageaddr = (base + CYCLONE_PMCC_OFFSET)&PAGE_MASK; + offset = (base + CYCLONE_PMCC_OFFSET)&(~PAGE_MASK); + set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr); + reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset); + if(!reg){ + printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n"); + use_cyclone = 0; + return; + } + reg[0] = 0x00000001; + + /* setup MPCS */ + pageaddr = (base + CYCLONE_MPCS_OFFSET)&PAGE_MASK; + offset = (base + CYCLONE_MPCS_OFFSET)&(~PAGE_MASK); + set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr); + reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset); + if(!reg){ + printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n"); + use_cyclone = 0; + return; + } + reg[0] = 0x00000001; + + /* map in cyclone_timer */ + pageaddr = (base + CYCLONE_MPMC_OFFSET)&PAGE_MASK; + offset = (base + CYCLONE_MPMC_OFFSET)&(~PAGE_MASK); + set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr); + cyclone_timer = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset); + if(!cyclone_timer){ + printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n"); + use_cyclone = 0; + return; + } + + /*quick test to make sure its ticking*/ + for(i=0; i<3; i++){ + u32 old = cyclone_timer[0]; + int stall = 100; + while(stall--) barrier(); + if(cyclone_timer[0] == old){ + printk(KERN_ERR "Summit chipset: Clock not ticking.\n"); + cyclone_timer = 0; + use_cyclone = 0; + return; + } + } + /* Everything looks good, so set do_gettimeoffset */ + do_gettimeoffset = do_gettimeoffset_cyclone; +} + +void __cyclone_delay(unsigned long loops) +{ + unsigned long bclock, now; + if(!cyclone_timer) + return; + bclock = cyclone_timer[0]; + do { + rep_nop(); + now = cyclone_timer[0]; + } while ((now-bclock) < loops); +} + +#else /*CONFIG_X86_CYCLONE*/ + +int __init cyclone_setup(char *str) +{ + printk(KERN_ERR "cyclone: Kernel not compiled with CONFIG_SUMMIT, cannot use the cyclone-timer.\n"); + return 1; +} +#define use_cyclone 0 +static void mark_timeoffset_cyclone(void) {} +static unsigned long do_gettimeoffset_cyclone(void) {return 0;} +static void init_cyclone_clock(void) {} +void __cyclone_delay(unsigned long loops) {} + +#endif /*CONFIG_X86_CYCLONE*/ +__setup("cyclone", cyclone_setup); + #ifndef CONFIG_X86_TSC /* This function must be called with interrupts disabled @@ -508,7 +675,8 @@ static void timer_interrupt(int irq, voi count = ((LATCH-1) - count) * TICK_SIZE; delay_at_last_interrupt = (count + LATCH/2) / LATCH; - } + } else if (use_cyclone) + mark_timeoffset_cyclone(); do_timer_interrupt(irq, NULL, regs); @@ -675,15 +843,22 @@ void __init time_init(void) unsigned long tsc_quotient = calibrate_tsc(); if (tsc_quotient) { fast_gettimeoffset_quotient = tsc_quotient; - use_tsc = 1; - /* - * We could be more selective here I suspect - * and just enable this for the next intel chips ? + /* XXX: This is messy + * However, we want to allow for the cyclone timer + * to work w/ or w/o the TSCs being avaliable + * -johnstul@us.ibm.com */ - x86_udelay_tsc = 1; + if(!use_cyclone){ + /* + * We could be more selective here I suspect + * and just enable this for the next intel chips ? + */ + use_tsc = 1; + x86_udelay_tsc = 1; #ifndef do_gettimeoffset - do_gettimeoffset = do_fast_gettimeoffset; + do_gettimeoffset = do_fast_gettimeoffset; #endif + } /* report CPU clock rate in Hz. * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) = @@ -699,6 +874,9 @@ void __init time_init(void) } } + if (!use_tsc && use_cyclone) + init_cyclone_clock(); + #ifdef CONFIG_VISWS printk("Starting Cobalt Timer system clock\n"); diff -urNp xx-ref/arch/i386/lib/delay.c xx/arch/i386/lib/delay.c --- xx-ref/arch/i386/lib/delay.c Tue Jan 22 18:52:53 2002 +++ xx/arch/i386/lib/delay.c Thu Oct 10 05:52:59 2002 @@ -57,10 +57,18 @@ static void __loop_delay(unsigned long l :"0" (loops)); } +#ifdef CONFIG_X86_CYCLONE +extern __cyclone_delay(unsigned long loops); +extern int use_cyclone; +#endif void __delay(unsigned long loops) { if (x86_udelay_tsc) __rdtsc_delay(loops); +#ifdef CONFIG_X86_CYCLONE + else if (use_cyclone) + __cyclone_delay(loops); +#endif else __loop_delay(loops); } diff -urNp xx-ref/include/asm-i386/fixmap.h xx/include/asm-i386/fixmap.h --- xx-ref/include/asm-i386/fixmap.h Thu Oct 10 05:52:33 2002 +++ xx/include/asm-i386/fixmap.h Thu Oct 10 05:52:49 2002 @@ -64,6 +64,9 @@ enum fixed_addresses { #ifndef CONFIG_X86_F00F_WORKS_OK FIX_F00F, #endif +#ifdef CONFIG_X86_CYCLONE + FIX_CYCLONE_TIMER, /*cyclone timer register*/ +#endif #ifdef CONFIG_HIGHMEM FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,