summaryrefslogtreecommitdiffstats
path: root/arch/mips/mips-boards/generic
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-10-09 22:37:56 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-10-09 22:37:56 +0000
commit08b0be66ff8bc4be230c2f4bc017d30d73228a7f (patch)
treed3df37be2932bc0dee4ad190a01950f955df0e85 /arch/mips/mips-boards/generic
parent4ba6edaa94f0b57323bfc4570c085e9da5853b91 (diff)
More Atlas chainsawing.
Diffstat (limited to 'arch/mips/mips-boards/generic')
-rw-r--r--arch/mips/mips-boards/generic/Makefile15
-rw-r--r--arch/mips/mips-boards/generic/memory.c217
-rw-r--r--arch/mips/mips-boards/generic/mipsIRQ.S95
-rw-r--r--arch/mips/mips-boards/generic/pci.c61
-rw-r--r--arch/mips/mips-boards/generic/time.c231
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 = (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);
}