diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-10-09 22:37:56 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-10-09 22:37:56 +0000 |
commit | 08b0be66ff8bc4be230c2f4bc017d30d73228a7f (patch) | |
tree | d3df37be2932bc0dee4ad190a01950f955df0e85 /arch/mips/mips-boards/generic | |
parent | 4ba6edaa94f0b57323bfc4570c085e9da5853b91 (diff) |
More Atlas chainsawing.
Diffstat (limited to 'arch/mips/mips-boards/generic')
-rw-r--r-- | arch/mips/mips-boards/generic/Makefile | 15 | ||||
-rw-r--r-- | arch/mips/mips-boards/generic/memory.c | 217 | ||||
-rw-r--r-- | arch/mips/mips-boards/generic/mipsIRQ.S | 95 | ||||
-rw-r--r-- | arch/mips/mips-boards/generic/pci.c | 61 | ||||
-rw-r--r-- | arch/mips/mips-boards/generic/time.c | 231 |
5 files changed, 388 insertions, 231 deletions
diff --git a/arch/mips/mips-boards/generic/Makefile b/arch/mips/mips-boards/generic/Makefile index 377190c96..c76f6fd46 100644 --- a/arch/mips/mips-boards/generic/Makefile +++ b/arch/mips/mips-boards/generic/Makefile @@ -1,6 +1,3 @@ -# $Id: Makefile,v 1.1 2000/07/07 10:21:35 carstenl Exp $ -# -# Makefile # # Carsten Langgaard, carstenl@mips.com # Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. @@ -35,15 +32,11 @@ .S.o: $(CC) $(CFLAGS) -c $< -o $*.o -OBJS = mipsIRQ.o pci.o reset.o display.o init.o memory.o printf.o cmdline.o time.o - -all: mipsboards.a +all: mipsboards.o -mipsboards.a: $(OBJS) - $(AR) rcs mipsboards.a $(OBJS) - sync +O_TARGET := mipsboards.o -dep: - $(CPP) -M *.c > .depend +O_OBJS := mipsIRQ.o pci.o reset.o display.o init.o memory.o printf.o \ + cmdline.o time.o include $(TOPDIR)/Rules.make diff --git a/arch/mips/mips-boards/generic/memory.c b/arch/mips/mips-boards/generic/memory.c index b8e442f70..c352ab790 100644 --- a/arch/mips/mips-boards/generic/memory.c +++ b/arch/mips/mips-boards/generic/memory.c @@ -26,6 +26,7 @@ #include <linux/config.h> #include <linux/init.h> #include <linux/mm.h> +#include <linux/bootmem.h> #include <asm/bootinfo.h> #include <asm/page.h> @@ -37,7 +38,7 @@ enum yamon_memtypes { yamon_dontuse, yamon_prom, - yamon_free, + yamon_free, }; struct prom_pmemblock mdesc[PROM_MAX_PMEMBLOCKS]; @@ -47,7 +48,7 @@ struct prom_pmemblock mdesc[PROM_MAX_PMEMBLOCKS]; #ifdef DEBUG static char *mtypes[3] = { - "Dont use memory", + "Dont use memory", "YAMON PROM memory", "Free memmory", }; @@ -55,30 +56,28 @@ static char *mtypes[3] = { struct prom_pmemblock * __init prom_getmdesc(void) { - char *memsize_str; + char *memsize_str; unsigned int memsize; - + memsize_str = prom_getenv("memsize"); if (!memsize_str) { - prom_printf("memsize not set in boot prom, set to default (32Mb)\n"); + prom_printf("memsize not set in boot prom, set to default (32Mb)\n"); memsize = 0x02000000; - } - else - { + } else { #ifdef DEBUG - prom_printf("prom_memsize = %s\n", memsize_str); + prom_printf("prom_memsize = %s\n", memsize_str); #endif - memsize = simple_strtol(memsize_str, NULL, 0); + memsize = simple_strtol(memsize_str, NULL, 0); } - memset(mdesc, 0, sizeof(mdesc)); + memset(mdesc, 0, sizeof(mdesc)); mdesc[0].type = yamon_dontuse; - mdesc[0].base = 0x00000000; + mdesc[0].base = 0x00000000; mdesc[0].size = 0x00001000; mdesc[1].type = yamon_prom; - mdesc[1].base = 0x00001000; + mdesc[1].base = 0x00001000; mdesc[1].size = 0x000ef000; #if (CONFIG_MIPS_MALTA) @@ -90,157 +89,167 @@ struct prom_pmemblock * __init prom_getmdesc(void) * devices. */ mdesc[2].type = yamon_dontuse; - mdesc[2].base = 0x000f0000; + mdesc[2].base = 0x000f0000; mdesc[2].size = 0x00010000; #else mdesc[2].type = yamon_prom; - mdesc[2].base = 0x000f0000; + mdesc[2].base = 0x000f0000; mdesc[2].size = 0x00010000; #endif - mdesc[3].type = yamon_free; mdesc[3].base = 0x00100000; mdesc[3].size = memsize - mdesc[3].base; - return &mdesc[0]; + return &mdesc[0]; } -static struct prom_pmemblock prom_pblocks[PROM_MAX_PMEMBLOCKS]; - -struct prom_pmemblock * __init prom_getpblock_array(void) +int __init page_is_ram(unsigned long pagenr) { - return &prom_pblocks[0]; + if ((pagenr << PAGE_SHIFT) < mdesc[3].base + mdesc[3].size) + return 1; + + return 0; } +static struct prom_pmemblock pblocks[PROM_MAX_PMEMBLOCKS]; + static int __init prom_memtype_classify (unsigned int type) { - switch (type) { - case yamon_free: - return MEMTYPE_FREE; - case yamon_prom: - return MEMTYPE_PROM; - default: - return MEMTYPE_DONTUSE; + switch (type) { + case yamon_free: + return MEMTYPE_FREE; + case yamon_prom: + return MEMTYPE_PROM; + default: + return MEMTYPE_DONTUSE; } } -static void __init prom_setup_memupper(void) +static inline unsigned long find_max_low_pfn(void) { struct prom_pmemblock *p, *highest; + unsigned long pfn; - for(p = prom_getpblock_array(), highest = 0; p->size != 0; p++) { - if(p->base == 0xdeadbeef) - prom_printf("WHEEE, bogus pmemblock\n"); - if(!highest || p->base > highest->base) + p = pblocks; + highest = 0; + while (p->size != 0) { + if (!highest || p->base > highest->base) highest = p; - } - mips_memory_upper = highest->base + highest->size; + p++; + } + + pfn = (highest->base + highest->size) >> PAGE_SHIFT; #ifdef DEBUG - prom_printf("prom_setup_memupper: mips_memory_upper = %08lx\n", - mips_memory_upper); + prom_printf("find_max_low_pfn: 0x%lx pfns.\n", pfn); #endif + return pfn; +} + +static inline struct prom_pmemblock *find_largest_memblock(void) +{ + struct prom_pmemblock *p, *largest; + + p = pblocks; + largest = 0; + while (p->size != 0) { + if (!largest || p->size > largest->size) + largest = p; + p++; + } + + return largest; } void __init prom_meminit(void) { - struct prom_pmemblock *p; + struct prom_pmemblock *largest, *p; + unsigned long bootmap_size; int totram; int i = 0; #ifdef DEBUG - p = prom_getmdesc(); prom_printf("YAMON MEMORY DESCRIPTOR dump:\n"); - while(p->size) { + p = prom_getmdesc(); + while (p->size) { prom_printf("[%d,%p]: base<%08lx> size<%08lx> type<%s>\n", i, p, p->base, p->size, memtypes[p->type]); p++; i++; } #endif - p = prom_getmdesc(); totram = 0; i = 0; - while(p->size) { - prom_pblocks[i].type = prom_memtype_classify (p->type); - prom_pblocks[i].base = p->base | 0x80000000; - prom_pblocks[i].size = p->size; - switch (prom_pblocks[i].type) { - case MEMTYPE_FREE: - totram += prom_pblocks[i].size; + p = prom_getmdesc(); + while (p->size) { + pblocks[i].type = prom_memtype_classify (p->type); + pblocks[i].base = p->base | 0x80000000; + pblocks[i].size = p->size; + switch (pblocks[i].type) { + case MEMTYPE_FREE: + totram += pblocks[i].size; #ifdef DEBUG - prom_printf("free_chunk[%d]: base=%08lx size=%d\n", - i, prom_pblocks[i].base, - prom_pblocks[i].size); + prom_printf("free_chunk[%d]: base=%08lx size=%d\n", + i, pblocks[i].base, + pblocks[i].size); #endif - i++; - break; - case MEMTYPE_PROM: + i++; + break; + case MEMTYPE_PROM: #ifdef DEBUG - prom_printf("prom_chunk[%d]: base=%08lx size=%d\n", - i, prom_pblocks[i].base, - prom_pblocks[i].size); + prom_printf("prom_chunk[%d]: base=%08lx size=%d\n", + i, pblocks[i].base, + pblocks[i].size); #endif - i++; - break; - default: - break; - } - p++; + i++; + break; + default: + break; + } + p++; + } + pblocks[i].base = 0xdeadbeef; + pblocks[i].size = 0; /* indicates last elem. of array */ + + max_low_pfn = find_max_low_pfn(); + largest = find_largest_memblock(); + bootmap_size = init_bootmem(largest->base >> PAGE_SHIFT, max_low_pfn); + + for (i = 0; pblocks[i].size; i++) + if (pblocks[i].type == MEMTYPE_FREE) + free_bootmem(pblocks[i].base, pblocks[i].size); + + /* This test is simpleminded. It will fail if the bootmem bitmap + falls into multiple adjacent PROM memory areas. */ + if (bootmap_size > largest->size) { + prom_printf("CRITIAL: overwriting PROM data.\n"); + BUG(); } - prom_pblocks[i].base = 0xdeadbeef; - prom_pblocks[i].size = 0; /* indicates last elem. of array */ - printk("PROMLIB: Total free ram %d bytes (%dK,%dMB)\n", - totram, (totram/1024), (totram/1024/1024)); - /* Setup upper physical memory bound. */ - prom_setup_memupper(); -} + /* Reserve the memory bootmap itself */ + reserve_bootmem(largest->base, bootmap_size); -/* Called from mem_init() to fixup the mem_map page settings. */ -void __init prom_fixup_mem_map(unsigned long start, unsigned long end) -{ - struct prom_pmemblock *p; - int i, nents; - - /* Determine number of pblockarray entries. */ - p = prom_getpblock_array(); - for(i = 0; p[i].size; i++) - ; - nents = i; -restart: - while(start < end) { - for(i = 0; i < nents; i++) { - if((p[i].type == MEMTYPE_FREE) && - (start >= (p[i].base)) && - (start < (p[i].base + p[i].size))) { - start = p[i].base + p[i].size; - start &= PAGE_MASK; - goto restart; - } - } - set_bit(PG_reserved, &mem_map[MAP_NR(start)].flags); - start += PAGE_SIZE; - } + printk("PROMLIB: Total free ram %d bytes (%dK,%dMB)\n", + totram, (totram/1024), (totram/1024/1024)); } void prom_free_prom_memory (void) { struct prom_pmemblock *p; + unsigned long freed = 0; unsigned long addr; - unsigned long num_pages = 0; - for (p = prom_getpblock_array(); p->size != 0; p++) { + for (p = pblocks; p->size != 0; p++) { if (p->type != MEMTYPE_PROM) continue; - for (addr = p->base; addr < p->base + p->size; - addr += PAGE_SIZE) { - mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); - atomic_set(&mem_map[MAP_NR(addr)].count, 1); + addr = p->base; + while (addr < p->base + p->size) { + ClearPageReserved(virt_to_page(addr)); + set_page_count(virt_to_page(addr), 1); free_page(addr); - num_pages++; + addr += PAGE_SIZE; + freed++; } } - printk("Freeing prom memory: %ldk freed\n", - (num_pages << (PAGE_SHIFT - 10))); + printk("Freeing prom memory: %ldkk freed\n", freed >> 10); } diff --git a/arch/mips/mips-boards/generic/mipsIRQ.S b/arch/mips/mips-boards/generic/mipsIRQ.S index 110f67fad..c2b9d891f 100644 --- a/arch/mips/mips-boards/generic/mipsIRQ.S +++ b/arch/mips/mips-boards/generic/mipsIRQ.S @@ -1,9 +1,6 @@ -/* $Id: mipsIRQ.S,v 1.2 2000/08/25 06:53:54 carstenl Exp $ - * - * mipsIRQ.S - * +/* * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * Copyright (C) 1999, 2000 MIPS Technologies, Inc. All rights reserved. * * ######################################################################## * @@ -25,51 +22,48 @@ * Interrupt exception dispatch code. * */ - #include <linux/config.h> - + #include <asm/asm.h> #include <asm/mipsregs.h> #include <asm/regdef.h> #include <asm/stackframe.h> - /* A lot of complication here is taken away because: - * - * 1) We handle one interrupt and return, sitting in a loop - * and moving across all the pending IRQ bits in the cause - * register is _NOT_ the answer, the common case is one - * pending IRQ so optimize in that direction. - * - * 2) We need not check against bits in the status register - * IRQ mask, that would make this routine slow as hell. - * - * 3) Linux only thinks in terms of all IRQs on or all IRQs - * off, nothing in between like BSD spl() brain-damage. - * - * Furthermore, the IRQs on the MIPS board look basically (barring - * software IRQs which we don't use at all and all external interrupt - * sources are combined together on hardware interrupt 0 (MIPS - * IRQ 2)) like: - * - * MIPS IRQ Source - * -------- ------ - * 0 Software (ignored) - * 1 Software (ignored) - * 2 Combined hardware interrupt (hw0) - * 3 Hardware (ignored) - * 4 Hardware (ignored) - * 5 Hardware (ignored) - * 6 Hardware (ignored) - * 7 R4k timer (what we use) - * - * We handle the IRQ according to _our_ priority which is: - * - * Highest ---- R4k Timer - * Lowest ---- Combined hardware interrupt - * - * then we just return, if multiple IRQs are pending then - * we will just take another exception, big deal. - */ +/* A lot of complication here is taken away because: + * + * 1) We handle one interrupt and return, sitting in a loop and moving across + * all the pending IRQ bits in the cause register is _NOT_ the answer, the + * common case is one pending IRQ so optimize in that direction. + * + * 2) We need not check against bits in the status register IRQ mask, that + * would make this routine slow as hell. + * + * 3) Linux only thinks in terms of all IRQs on or all IRQs off, nothing in + * between like BSD spl() brain-damage. + * + * Furthermore, the IRQs on the MIPS board look basically (barring software + * IRQs which we don't use at all and all external interrupt sources are + * combined together on hardware interrupt 0 (MIPS IRQ 2)) like: + * + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 Combined hardware interrupt (hw0) + * 3 Hardware (ignored) + * 4 Hardware (ignored) + * 5 Hardware (ignored) + * 6 Hardware (ignored) + * 7 R4k timer (what we use) + * + * We handle the IRQ according to _our_ priority which is: + * + * Highest ---- R4k Timer + * Lowest ---- Combined hardware interrupt + * + * then we just return, if multiple IRQs are pending then we will just take + * another exception, big deal. + */ .text .set noreorder @@ -90,10 +84,10 @@ /* Wheee, a timer interrupt. */ move a0, sp jal mips_timer_interrupt - nop # delay slot + nop j ret_from_irq - nop # delay slot + nop 1: beq a0, zero, 1f @@ -113,15 +107,16 @@ nop # delay slot 1: - /* Here by mistake? This is possible, what can happen - * is that by the time we take the exception the IRQ - * pin goes low, so just leave if this is the case. + /* + * Here by mistake? This is possible, what can happen is that by the + * time we take the exception the IRQ pin goes low, so just leave if + * this is the case. */ move a1,s0 PRINT("Got interrupt: c0_cause = %08x\n") mfc0 a1, CP0_EPC PRINT("c0_epc = %08x\n") - + j ret_from_irq nop END(mipsIRQ) diff --git a/arch/mips/mips-boards/generic/pci.c b/arch/mips/mips-boards/generic/pci.c index 0909ca2ae..4ee5a2b9b 100644 --- a/arch/mips/mips-boards/generic/pci.c +++ b/arch/mips/mips-boards/generic/pci.c @@ -52,15 +52,15 @@ mips_pcibios_config_access(unsigned char access_type, struct pci_dev *dev, return -1; /* Because of a bug in the galileo (for slot 31). */ /* Clear cause register bits */ - GT_WRITE( GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT | - GT_INTRCAUSE_TARABORT0_BIT) ); + GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT | + GT_INTRCAUSE_TARABORT0_BIT)); /* Setup address */ - GT_WRITE( GT_PCI0_CFGADDR_OFS, - (bus << GT_PCI0_CFGADDR_BUSNUM_SHF) | - (dev_fn << GT_PCI0_CFGADDR_FUNCTNUM_SHF) | - ((where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF) | - GT_PCI0_CFGADDR_CONFIGEN_BIT ); + GT_WRITE(GT_PCI0_CFGADDR_OFS, + (bus << GT_PCI0_CFGADDR_BUSNUM_SHF) | + (dev_fn << GT_PCI0_CFGADDR_FUNCTNUM_SHF) | + ((where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF) | + GT_PCI0_CFGADDR_CONFIGEN_BIT); if (access_type == PCI_ACCESS_WRITE) { GT_WRITE(GT_PCI0_CFGDATA_OFS, *data); @@ -268,4 +268,51 @@ void __init pcibios_init(void) #endif } +int __init +pcibios_enable_device(struct pci_dev *dev) +{ + /* Not needed, since we enable all devices at startup. */ + return 0; +} + +void __init +pcibios_align_resource(void *data, struct resource *res, unsigned long size) +{ +} + +char * __init +pcibios_setup(char *str) +{ + /* Nothing to do for now. */ + + return str; +} + +struct pci_fixup pcibios_fixups[] = { + { 0 } +}; + +void __init +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + unsigned long where, size; + u32 reg; + + where = PCI_BASE_ADDRESS_0 + (resource * 4); + size = res->end - res->start; + pci_read_config_dword(dev, where, ®); + reg = (reg & size) | (((u32)(res->start - root->start)) & ~size); + pci_write_config_dword(dev, where, reg); +} + +/* + * Called after each bus is probed, but before its children + * are examined. + */ +void __init pcibios_fixup_bus(struct pci_bus *b) +{ + pci_read_bridge_bases(b); +} + #endif /* CONFIG_PCI */ diff --git a/arch/mips/mips-boards/generic/time.c b/arch/mips/mips-boards/generic/time.c index 84592fc2f..d5fdc5f6a 100644 --- a/arch/mips/mips-boards/generic/time.c +++ b/arch/mips/mips-boards/generic/time.c @@ -27,6 +27,7 @@ #include <linux/init.h> #include <linux/kernel_stat.h> #include <linux/sched.h> +#include <linux/spinlock.h> #include <asm/mipsregs.h> #include <asm/ptrace.h> @@ -37,11 +38,13 @@ #include <asm/mips-boards/generic.h> #include <asm/mips-boards/prom.h> +extern volatile unsigned long wall_jiffies; static long last_rtc_update = 0; unsigned long missed_heart_beats = 0; 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; #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) @@ -131,77 +134,79 @@ void mips_timer_interrupt(struct pt_regs *regs) { int irq = 7; - if(r4k_offset != 0) { - do { - kstat.irqs[0][irq]++; - do_timer(regs); - - /* Historical comment/code: - * RTC time of day s updated approx. every 11 - * minutes. Because of how the numbers work out - * we need to make absolutely sure we do this update - * within 500ms before the * next second starts, - * thus the following code. - */ - if ((time_status & STA_UNSYNC) == 0 - && xtime.tv_sec > last_rtc_update + 660 - && xtime.tv_usec >= 500000 - (tick >> 1) - && xtime.tv_usec <= 500000 + (tick >> 1)) - if (set_rtc_mmss(xtime.tv_sec) == 0) + if (r4k_offset == 0) + goto null; + + do { + kstat.irqs[0][irq]++; + do_timer(regs); + + /* Historical comment/code: + * RTC time of day s updated approx. every 11 + * minutes. Because of how the numbers work out + * we need to make absolutely sure we do this update + * within 500ms before the * next second starts, + * thus the following code. + */ + read_lock(&xtime_lock); + if ((time_status & STA_UNSYNC) == 0 + && xtime.tv_sec > last_rtc_update + 660 + && xtime.tv_usec >= 500000 - (tick >> 1) + && xtime.tv_usec <= 500000 + (tick >> 1)) + if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; - else + else /* do it again in 60 s */ - last_rtc_update = xtime.tv_sec - 600; + last_rtc_update = xtime.tv_sec - 600; + read_unlock(&xtime_lock); - if ((timer_tick_count++ % HZ) == 0) - { - mips_display_message(&display_string[display_count++]); - if (display_count == MAX_DISPLAY_COUNT) - display_count = 0; - } + if ((timer_tick_count++ % HZ) == 0) { + mips_display_message(&display_string[display_count++]); + if (display_count == MAX_DISPLAY_COUNT) + display_count = 0; + } - r4k_cur += r4k_offset; - ack_r4ktimer(r4k_cur); + r4k_cur += r4k_offset; + ack_r4ktimer(r4k_cur); - } while (((unsigned long)read_32bit_cp0_register(CP0_COUNT) - - r4k_cur) < 0x7fffffff); - } else ack_r4ktimer(0); + } while (((unsigned long)read_32bit_cp0_register(CP0_COUNT) + - r4k_cur) < 0x7fffffff); + + return; + +null: + ack_r4ktimer(0); } -static unsigned long cal_r4koff(void) +/* + * Figure out the r4k offset, the amount to increment the compare + * register for each time tick. + * Use the RTC to calculate offset. + */ +static unsigned long __init cal_r4koff(void) { - unsigned long count; - unsigned long flags; + unsigned long count; + unsigned int flags; - /* - * Figure out the r4k offset, the amount to increment the compare - * register for each time tick. - * Use the RTC to calculate offset. - */ - /* Disable Interrupts */ - save_and_cli(flags); + __save_and_cli(flags); /* Start counter exactly on falling edge of update flag */ - while (CMOS_READ(RTC_REG_A) & RTC_UIP) - ; - while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)) - ; + while (CMOS_READ(RTC_REG_A) & RTC_UIP); + while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); /* Start r4k counter. */ write_32bit_cp0_register(CP0_COUNT, 0); /* Read counter exactly on falling edge of update flag */ - while (CMOS_READ(RTC_REG_A) & RTC_UIP) - ; - while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)) - ; + while (CMOS_READ(RTC_REG_A) & RTC_UIP); + while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); count = read_32bit_cp0_register(CP0_COUNT); /* restore interrupts */ - restore_flags(flags); + __restore_flags(flags); - return (count / HZ); + return (count / HZ); } static unsigned long __init get_mips_time(void) @@ -241,7 +246,7 @@ static unsigned long __init get_mips_time(void) void __init time_init(void) { - unsigned int est_freq; + unsigned int est_freq, flags; /* Set Data mode - binary. */ CMOS_WRITE(CMOS_READ(RTC_CONTROL) | RTC_DM_BINARY, RTC_CONTROL); @@ -261,25 +266,133 @@ void __init time_init(void) set_cp0_status(ST0_IM, ALLINTS); /* Read time from the RTC chipset. */ + write_lock_irqsave (&xtime_lock, flags); xtime.tv_sec = get_mips_time(); xtime.tv_usec = 0; + write_unlock_irqrestore(&xtime_lock, flags); +} + +/* This is for machines which generate the exact clock. */ +#define USECS_PER_JIFFY (1000000/HZ) + +/* Cycle counter value at the previous timer interrupt.. */ + +static unsigned int timerhi = 0, timerlo = 0; + +/* + * FIXME: Does playing with the RP bit in c0_status interfere with this code? + */ +static unsigned long do_fast_gettimeoffset(void) +{ + u32 count; + unsigned long res, tmp; + + /* Last jiffy when do_fast_gettimeoffset() was called. */ + static unsigned long last_jiffies=0; + unsigned long quotient; + + /* + * Cached "1/(clocks per usec)*2^32" value. + * It has to be recalculated once each jiffy. + */ + static unsigned long cached_quotient=0; + + tmp = jiffies; + + quotient = cached_quotient; + + if (tmp && last_jiffies != tmp) { + last_jiffies = tmp; + __asm__(".set\tnoreorder\n\t" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "lwu\t%0,%2\n\t" + "dsll32\t$1,%1,0\n\t" + "or\t$1,$1,%0\n\t" + "ddivu\t$0,$1,%3\n\t" + "mflo\t$1\n\t" + "dsll32\t%0,%4,0\n\t" + "nop\n\t" + "ddivu\t$0,%0,$1\n\t" + "mflo\t%0\n\t" + ".set\tmips0\n\t" + ".set\tat\n\t" + ".set\treorder" + :"=&r" (quotient) + :"r" (timerhi), + "m" (timerlo), + "r" (tmp), + "r" (USECS_PER_JIFFY) + :"$1"); + cached_quotient = quotient; + } + + /* Get last timer tick in absolute kernel time */ + count = read_32bit_cp0_register(CP0_COUNT); + + /* .. relative to previous jiffy (32 bits is enough) */ + count -= timerlo; + + __asm__("multu\t%1,%2\n\t" + "mfhi\t%0" + :"=r" (res) + :"r" (count), + "r" (quotient)); + + /* + * Due to possible jiffies inconsistencies, we need to check + * the result so that we'll get a timer that is monotonic. + */ + if (res >= USECS_PER_JIFFY) + res = USECS_PER_JIFFY-1; + + return res; } void do_gettimeofday(struct timeval *tv) { - unsigned long flags; + unsigned int flags; - save_and_cli(flags); + read_lock_irqsave (&xtime_lock, flags); *tv = xtime; - restore_flags(flags); + tv->tv_usec += do_fast_gettimeoffset(); + + /* + * xtime is atomically updated in timer_bh. jiffies - wall_jiffies + * is nonzero if the timer bottom half hasnt executed yet. + */ + if (jiffies - wall_jiffies) + tv->tv_usec += USECS_PER_JIFFY; + + read_unlock_irqrestore (&xtime_lock, flags); + + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } } void do_settimeofday(struct timeval *tv) { - cli(); + write_lock_irq (&xtime_lock); + + /* This is revolting. We need to set the xtime.tv_usec correctly. + * However, the value in this location is is value at the last tick. + * Discover what correction gettimeofday would have done, and then + * undo it! + */ + tv->tv_usec -= do_fast_gettimeoffset(); + + if (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + xtime = *tv; - time_state = TIME_BAD; - time_maxerror = MAXPHASE; - time_esterror = MAXPHASE; - sti(); + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + + write_unlock_irq (&xtime_lock); } |