Commit 21872250 authored by John Stultz's avatar John Stultz Committed by David S. Miller

[PATCH] linux-2.5.41_cyclone-timer_B2

        In order to demonstrate how new time-sources are added to my
timer-changes patch. Here is my current version of my cyclone-timer
patch for 2.5.41. This uses the infrastructure set up in the
timer-changes_A4 patch set to add the cyclone counter (found on IBM
Summit Based hardware) as a time-source.

        The current code is not enabled as it also depends on James
Cleverdon's 2.5 summit patch, however it illustrates how cleanly new
time-sources can be added.
parent f59a48cf
...@@ -65,6 +65,14 @@ CONFIG_X86_NUMAQ ...@@ -65,6 +65,14 @@ CONFIG_X86_NUMAQ
You will need a new lynxer.elf file to flash your firmware with - send You will need a new lynxer.elf file to flash your firmware with - send
email to Martin.Bligh@us.ibm.com email to Martin.Bligh@us.ibm.com
CONFIG_X86_CYCLONE
This patch used the Cyclone performance counter on IBM x440, x360,
and other Summit based systems for calculating gettimeofday. This
fixes problems resulting from TSC skew found in multi-CEC system.
If you are suffering from time skew using a multi-CEC system, say YES.
Otherwise it is safe to say NO.
CONFIG_X86_UP_IOAPIC CONFIG_X86_UP_IOAPIC
An IO-APIC (I/O Advanced Programmable Interrupt Controller) is an An IO-APIC (I/O Advanced Programmable Interrupt Controller) is an
SMP-capable replacement for PC-style interrupt controllers. Most SMP-capable replacement for PC-style interrupt controllers. Most
......
...@@ -6,5 +6,6 @@ obj-y := timer.o ...@@ -6,5 +6,6 @@ obj-y := timer.o
obj-y += timer_tsc.o obj-y += timer_tsc.o
obj-y += timer_pit.o obj-y += timer_pit.o
obj-$(CONFIG_X86_CYCLONE) += timer_cyclone.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
/* Cyclone-timer:
* This code implements timer_ops for the cyclone counter found
* on IBM x440, x360, and other Summit based systems.
*
* Copyright (C) 2002 IBM, John Stultz (johnstul@us.ibm.com)
*/
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/timex.h>
#include <asm/timer.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/fixmap.h>
extern spinlock_t i8253_lock;
/* Number of usecs that the last interrupt was delayed */
static int delay_at_last_interrupt;
#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
int use_cyclone = 0;
static u32* volatile cyclone_timer; /* Cyclone MPMC0 register */
static u32 last_cyclone_timer;
static void mark_offset_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 get_offset_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 int init_cyclone(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;
/*make sure we're on a summit box*/
/*XXX need to use proper summit hooks! such as xapic -john*/
if(!use_cyclone) return 0;
printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\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");
return 0;
}
base = *reg;
if(!base){
printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
return 0;
}
/* 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");
return 0;
}
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");
return 0;
}
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");
return 0;
}
/*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: Counter not counting! DISABLED\n");
cyclone_timer = 0;
return 0;
}
}
/* Everything looks good! */
return 1;
}
#if 0 /* XXX future work */
static void delay_cyclone(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);
}
#endif
/************************************************************/
/* cyclone timer_opts struct */
struct timer_opts timer_cyclone = {
init: init_cyclone,
mark_offset: mark_offset_cyclone,
get_offset: get_offset_cyclone
};
...@@ -65,6 +65,9 @@ enum fixed_addresses { ...@@ -65,6 +65,9 @@ enum fixed_addresses {
#ifdef CONFIG_X86_F00F_BUG #ifdef CONFIG_X86_F00F_BUG
FIX_F00F_IDT, /* Virtual mapping for IDT */ FIX_F00F_IDT, /* Virtual mapping for IDT */
#endif #endif
#ifdef CONFIG_X86_CYCLONE
FIX_CYCLONE_TIMER, /*cyclone timer register*/
#endif
#ifdef CONFIG_HIGHMEM #ifdef CONFIG_HIGHMEM
FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment