summaryrefslogtreecommitdiffstats
path: root/arch/ppc/kernel
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-05-07 02:55:41 +0000
commitdcec8a13bf565e47942a1751a9cec21bec5648fe (patch)
tree548b69625b18cc2e88c3e68d0923be546c9ebb03 /arch/ppc/kernel
parent2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff)
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash. o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'arch/ppc/kernel')
-rw-r--r--arch/ppc/kernel/Makefile34
-rw-r--r--arch/ppc/kernel/align.c16
-rw-r--r--arch/ppc/kernel/chrp_pci.c22
-rw-r--r--arch/ppc/kernel/chrp_setup.c103
-rw-r--r--arch/ppc/kernel/chrp_time.c9
-rw-r--r--arch/ppc/kernel/head.S818
-rw-r--r--arch/ppc/kernel/idle.c205
-rw-r--r--arch/ppc/kernel/irq.c334
-rw-r--r--arch/ppc/kernel/mbx_pci.c254
-rw-r--r--arch/ppc/kernel/mbx_setup.c169
-rw-r--r--arch/ppc/kernel/misc.S59
-rw-r--r--arch/ppc/kernel/mk_defs.c4
-rw-r--r--arch/ppc/kernel/pci.c116
-rw-r--r--arch/ppc/kernel/pmac_pci.c65
-rw-r--r--arch/ppc/kernel/pmac_setup.c160
-rw-r--r--arch/ppc/kernel/pmac_support.c26
-rw-r--r--arch/ppc/kernel/pmac_time.c46
-rw-r--r--arch/ppc/kernel/ppc-stub.c705
-rw-r--r--arch/ppc/kernel/ppc_htab.c15
-rw-r--r--arch/ppc/kernel/ppc_ksyms.c22
-rw-r--r--arch/ppc/kernel/prep_pci.c23
-rw-r--r--arch/ppc/kernel/prep_setup.c18
-rw-r--r--arch/ppc/kernel/prep_time.c2
-rw-r--r--arch/ppc/kernel/process.c110
-rw-r--r--arch/ppc/kernel/prom.c353
-rw-r--r--arch/ppc/kernel/ptrace.c11
-rw-r--r--arch/ppc/kernel/residual.c2
-rw-r--r--arch/ppc/kernel/setup.c395
-rw-r--r--arch/ppc/kernel/signal.c14
-rw-r--r--arch/ppc/kernel/smp.c189
-rw-r--r--arch/ppc/kernel/softemu8xx.c96
-rw-r--r--arch/ppc/kernel/time.c196
-rw-r--r--arch/ppc/kernel/time.h7
-rw-r--r--arch/ppc/kernel/traps.c93
34 files changed, 3802 insertions, 889 deletions
diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile
index 37776109d..3561fd26a 100644
--- a/arch/ppc/kernel/Makefile
+++ b/arch/ppc/kernel/Makefile
@@ -11,14 +11,32 @@
$(CC) $(CFLAGS) -D__ASSEMBLY__ -c $< -o $*.o
O_TARGET := kernel.o
-O_OBJS := misc.o traps.o process.o signal.o syscalls.o \
- align.o ptrace.o irq.o openpic.o bitops.o ppc_htab.o idle.o \
- time.o prep_time.o pmac_time.o chrp_time.o \
- setup.o prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \
- pci.o prep_pci.o pmac_pci.o chrp_pci.o \
- residual.o prom.o
OX_OBJS := ppc_ksyms.o
+
+O_OBJS := traps.o irq.o idle.o time.o process.o signal.o syscalls.o misc.o \
+ bitops.o ppc_htab.o setup.o ptrace.o align.o
+
+ifdef CONFIG_PCI
+O_OBJS += pci.o
+endif
+ifdef CONFIG_KGDB
+O_OBJS += ppc-stub.o
+endif
+
+ifeq ($(CONFIG_MBX),y)
+O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o
+else
+ifeq ($(CONFIG_APUS),y)
+O_OBJS += prom.o openpic.o
+else
+O_OBJS += prep_time.o pmac_time.o chrp_time.o \
+ prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \
+ prep_pci.o pmac_pci.o chrp_pci.o \
+ residual.o prom.o openpic.o
+endif
+endif
+
ifdef SMP
O_OBJS += smp.o
endif
@@ -38,10 +56,10 @@ ppc_defs.h: mk_defs.c ppc_defs.head \
rm mk_defs.s
find_name : find_name.c
- $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -o find_name find_name.c
+ $(HOSTCC) -o find_name find_name.c
checks: checks.c
- $(HOSTCC) -DKERNELBASE=$(KERNELBASE) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
+ $(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
./checks
include $(TOPDIR)/Rules.make
diff --git a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c
index 39cb04f77..70284674b 100644
--- a/arch/ppc/kernel/align.c
+++ b/arch/ppc/kernel/align.c
@@ -194,9 +194,13 @@ fix_alignment(struct pt_regs *regs)
return -EFAULT; /* bad address */
}
+#ifdef __SMP__
+ if ((flags & F) && (regs->msr & MSR_FP) )
+ smp_giveup_fpu(current);
+#else
if ((flags & F) && last_task_used_math == current)
giveup_fpu();
-
+#endif
if (flags & M)
return 0; /* too hard for now */
@@ -255,12 +259,22 @@ fix_alignment(struct pt_regs *regs)
* the kernel with -msoft-float so it doesn't use the
* fp regs for copying 8-byte objects. */
case LD+F+S:
+#ifdef __SMP__
+ if (regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
giveup_fpu();
+#endif
cvt_fd(&data.f, &current->tss.fpr[reg]);
/* current->tss.fpr[reg] = data.f; */
break;
case ST+F+S:
+#ifdef __SMP__
+ if (regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
giveup_fpu();
+#endif
cvt_df(&current->tss.fpr[reg], &data.f);
/* data.f = current->tss.fpr[reg]; */
break;
diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c
index 16b379254..6036efc28 100644
--- a/arch/ppc/kernel/chrp_pci.c
+++ b/arch/ppc/kernel/chrp_pci.c
@@ -4,13 +4,13 @@
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/bios32.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/openpic.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/hydra.h>
#include <asm/prom.h>
@@ -22,8 +22,12 @@
volatile struct Hydra *Hydra = NULL;
-
#if 1
+/*
+ * The VLSI Golden Gate II has only 512K of PCI configuration space, so we
+ * limit the bus number to 3 bits
+ */
+
int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
unsigned char offset, unsigned char *val)
{
@@ -32,11 +36,6 @@ int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
return PCIBIOS_DEVICE_NOT_FOUND;
}
*val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset));
- if (offset == PCI_INTERRUPT_LINE) {
- /* PCI interrupts are controlled by the OpenPIC */
- if (*val)
- *val = openpic_to_irq(*val);
- }
return PCIBIOS_SUCCESSFUL;
}
@@ -228,10 +227,11 @@ __initfunc(int w83c553f_init(void))
unsigned char t8;
unsigned short t16;
unsigned int t32;
- if (pcibios_find_device(PCI_VENDOR_ID_WINBOND,
- PCI_DEVICE_ID_WINBOND_83C553, 0, &bus, &dev)
- == PCIBIOS_SUCCESSFUL) {
- dev++;
+ struct pci_dev *pdev;
+ if ((pdev = pci_find_device(PCI_VENDOR_ID_WINBOND,
+ PCI_DEVICE_ID_WINBOND_83C553, NULL))) {
+ bus = pdev->bus->number;
+ dev = pdev->devfn + 1;
chrp_pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32);
if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) {
#if 0
diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c
index 6c5d2afa5..91ca5d595 100644
--- a/arch/ppc/kernel/chrp_setup.c
+++ b/arch/ppc/kernel/chrp_setup.c
@@ -60,21 +60,6 @@ extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
extern int rd_image_start; /* starting block # of image */
#endif
-
-int chrp_ide_irq = 0;
-
-void chrp_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
-{
- ide_ioreg_t port = base;
- int i = 8;
-
- while (i--)
- *p++ = port++;
- *p++ = base + 0x206;
- if (irq != NULL)
- *irq = chrp_ide_irq;
-}
-
static const char *gg2_memtypes[4] = {
"FPM", "SDRAM", "EDO", "BEDO"
};
@@ -89,6 +74,34 @@ static const char *gg2_cachemodes[4] = {
"Disabled", "Write-Through", "Copy-Back", "Transparent Mode"
};
+#if 0
+#ifdef CONFIG_BLK_DEV_IDE
+int chrp_ide_ports_known;
+ide_ioreg_t chrp_ide_regbase[MAX_HWIFS];
+ide_ioreg_t chrp_idedma_regbase; /* one for both channels */
+unsigned int chrp_ide_irq;
+
+void chrp_ide_probe(void)
+{
+}
+
+void chrp_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
+{
+ int i;
+
+ *p = 0;
+ if (base == 0)
+ return;
+ for (i = 0; i < 8; ++i)
+ *p++ = base + i * 0x10;
+ *p = base + 0x160;
+ if (irq != NULL) {
+ *irq = chrp_ide_irq;
+ }
+}
+#endif /* CONFIG_BLK_DEV_IDE */
+#endif
+
int
chrp_get_cpuinfo(char *buffer)
{
@@ -139,12 +152,63 @@ chrp_get_cpuinfo(char *buffer)
}
/* L2 cache */
t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL));
- len += sprintf(buffer+len, "l2\t\t: %s %s (%s)\n",
+ len += sprintf(buffer+len, "board l2\t: %s %s (%s)\n",
gg2_cachesizes[(t>>7) & 3], gg2_cachetypes[(t>>2) & 3],
gg2_cachemodes[t & 3]);
return len;
}
+ /*
+ * Fixes for the National Semiconductor PC78308VUL SuperI/O
+ *
+ * Some versions of Open Firmware incorrectly initialize the IRQ settings
+ * for keyboard and mouse
+ */
+
+__initfunc(static inline void sio_write(u8 val, u8 index))
+{
+ outb(index, 0x15c);
+ outb(val, 0x15d);
+}
+
+__initfunc(static inline u8 sio_read(u8 index))
+{
+ outb(index, 0x15c);
+ return inb(0x15d);
+}
+
+__initfunc(static void sio_init(void))
+{
+ u8 irq, type;
+
+ /* select logical device 0 (KBC/Keyboard) */
+ sio_write(0, 0x07);
+ irq = sio_read(0x70);
+ type = sio_read(0x71);
+ printk("sio: Keyboard irq %d, type %d: ", irq, type);
+ if (irq == 1 && type == 3)
+ printk("OK\n");
+ else {
+ printk("remapping to irq 1, type 3\n");
+ sio_write(1, 0x70);
+ sio_write(3, 0x71);
+ }
+
+ /* select logical device 1 (KBC/Mouse) */
+ sio_write(1, 0x07);
+ irq = sio_read(0x70);
+ type = sio_read(0x71);
+ printk("sio: Mouse irq %d, type %d: ", irq, type);
+ if (irq == 12 && type == 3)
+ printk("OK\n");
+ else {
+ printk("remapping to irq 12, type 3\n");
+ sio_write(12, 0x70);
+ sio_write(3, 0x71);
+ }
+}
+
+
__initfunc(void
chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
{
@@ -191,7 +255,12 @@ chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
hydra_init(); /* Mac I/O */
w83c553f_init(); /* PCI-ISA bridge and IDE */
-#ifdef CONFIG_ABSTRACT_CONSOLE
+ /*
+ * Fix the Super I/O configuration
+ */
+ sio_init();
+
+#ifdef CONFIG_FB
/* Frame buffer device based console */
conswitchp = &fb_con;
#endif
diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c
index 73dcf9844..7109c0e5d 100644
--- a/arch/ppc/kernel/chrp_time.c
+++ b/arch/ppc/kernel/chrp_time.c
@@ -38,7 +38,7 @@ void chrp_time_init(void)
rtcs = find_compatible_devices("rtc", "pnpPNP,b00");
if (rtcs == NULL || rtcs->addrs == NULL)
return;
- base = ((int *)rtcs->addrs)[2];
+ base = rtcs->addrs[0].address;
nvram_as1 = 0;
nvram_as0 = base;
nvram_data = base + 1;
@@ -69,7 +69,7 @@ int chrp_set_rtc_time(unsigned long nowtime)
unsigned char save_control, save_freq_select;
struct rtc_time tm;
- to_tm(nowtime + 10*60*60, &tm); /* XXX for now */
+ to_tm(nowtime, &tm);
save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */
@@ -146,7 +146,7 @@ unsigned long chrp_get_rtc_time(void)
}
if ((year += 1900) < 1970)
year += 100;
- return mktime(year, mon, day, hour, min, sec) - 10*60*60 /* XXX for now */;
+ return mktime(year, mon, day, hour, min, sec);
}
@@ -155,6 +155,9 @@ void chrp_calibrate_decr(void)
struct device_node *cpu;
int freq, *fp, divisor;
+ if (via_calibrate_decr())
+ return;
+
/*
* The cpu node should have a timebase-frequency property
* to tell us the rate at which the decrementer counts.
diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S
index 2924febc5..d8540e68d 100644
--- a/arch/ppc/kernel/head.S
+++ b/arch/ppc/kernel/head.S
@@ -9,6 +9,8 @@
* Low-level exception handlers and MMU support
* rewritten by Paul Mackerras.
* Copyright (C) 1996 Paul Mackerras.
+ * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net).
+ * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
*
* This file contains the low-level support and setup for the
* PowerPC platform, including trap and interrupt dispatch.
@@ -29,12 +31,14 @@
#include <linux/sys.h>
#include <linux/errno.h>
#include <linux/config.h>
+#ifdef CONFIG_8xx
+#include <asm/mmu.h>
+#include <asm/pgtable.h>
+#include <asm/cache.h>
+#endif
#ifdef CONFIG_APUS
-/* At CYBERBASEp we'll find the following sum:
- * -KERNELBASE+CyberStormMemoryBase
- */
-#define CYBERBASEp (0xfff00000)
+#include <asm/amigappc.h>
#endif
/* optimization for 603 to load the tlb directly from the linux table */
@@ -78,6 +82,8 @@ LG_CACHE_LINE_SIZE = 5
isync
/* This instruction is not implemented on the PPC 603 or 601 */
+#ifndef CONFIG_8xx
+/* This instruction is not implemented on the PPC 603 or 601 */
#define tlbia \
li r4,128; \
mtctr r4; \
@@ -85,6 +91,7 @@ LG_CACHE_LINE_SIZE = 5
0: tlbie r4; \
addi r4,r4,0x1000; \
bdnz 0b
+#endif
#define LOAD_BAT(n, offset, reg, RA, RB) \
lwz RA,offset+0(reg); \
@@ -96,6 +103,20 @@ LG_CACHE_LINE_SIZE = 5
mtspr DBAT##n##U,RA; \
mtspr DBAT##n##L,RB
+#ifndef CONFIG_APUS
+#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h
+#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h
+#else
+#define tophys(rd,rs,rt) \
+ lis rt,CYBERBASEp@h; \
+ lwz rt,0(rt); \
+ add rd,rs,rt
+#define tovirt(rd,rs,rt) \
+ lis rt,CYBERBASEp@h; \
+ lwz rt,0(rt); \
+ sub rd,rs,rt
+#endif
+
.text
.globl _stext
_stext:
@@ -130,12 +151,44 @@ _start:
*
* This just gets a minimal mmu environment setup so we can call
* start_here() to do the real work.
- * -- Cort
+ * -- Cort
+ *
+ * MPC8xx
+ * This port was done on an MBX board with an 860. Right now I only
+ * support an ELF compressed (zImage) boot from EPPC-Bug because the
+ * code there loads up some registers before calling us:
+ * r3: ptr to board info data
+ * r4: initrd_start or if no initrd then 0
+ * r5: initrd_end - unused if r4 is 0
+ * r6: Start of command line string
+ * r7: End of command line string
+ *
+ * I decided to use conditional compilation instead of checking PVR and
+ * adding more processor specific branches around code I don't need.
+ * Since this is an embedded processor, I also appreciate any memory
+ * savings I can get.
+ *
+ * The MPC8xx does not have any BATs, but it supports large page sizes.
+ * We first initialize the MMU to support 8M byte pages, then load one
+ * entry into each of the instruction and data TLBs to map the first
+ * 8M 1:1. I also mapped an additional I/O space 1:1 so we can get to
+ * the "internal" processor registers before MMU_init is called.
+ *
+ * The TLB code currently contains a major hack. Since I use the condition
+ * code register, I have to save and restore it. I am out of registers, so
+ * I just store it in memory location 0 (the TLB handlers are not reentrant).
+ * To avoid making any decisions, I need to use the "segment" valid bit
+ * in the first level table, but that would require many changes to the
+ * Linux page directory/table functions that I don't want to do right now.
+ *
+ * I used to use SPRG2 for a temporary register in the TLB handler, but it
+ * has since been put to other uses. I now use a hack to save a register
+ * and the CCR at memory location 0.....Someday I'll fix this.....
+ * -- Dan
*/
.globl __start
__start:
-
/*
* We have to do any OF calls before we map ourselves to KERNELBASE,
* because OF may have I/O devices mapped in in that area
@@ -145,12 +198,16 @@ __start:
mr r30,r4
mr r29,r5
mr r28,r6
- mr r29,r7
+ mr r27,r7
+#ifndef CONFIG_8xx
+#ifndef CONFIG_APUS
bl prom_init
+#endif
/*
* Use the first pair of BAT registers to map the 1st 16MB
- * of RAM to KERNELBASE.
+ * of RAM to KERNELBASE. From this point on we can't safely
+ * call OF any more.
*/
mfspr r9,PVR
rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */
@@ -173,17 +230,134 @@ __start:
addis r8,r8,KERNELBASE@h
addi r8,r8,2
#endif
- mtspr DBAT0U,r11
+5: mtspr DBAT0U,r11
mtspr DBAT0L,r8
-5: mtspr IBAT0U,r11
+ mtspr IBAT0U,r11
mtspr IBAT0L,r8
isync
+
+/*
+ * We need to run with _start at physical address 0.
+ * On CHRP, we are loaded at 0x10000 since OF on CHRP uses
+ * the exception vectors at 0 (and therefore this copy
+ * overwrites OF's exception vectors with our own).
+ * If the MMU is already turned on, we copy stuff to KERNELBASE,
+ * otherwise we copy it to 0.
+ */
+ bl reloc_offset
+ mr r26,r3
+ addis r4,r3,KERNELBASE@h /* current address of _start */
+ cmpwi 0,r4,0 /* are we already running at 0? */
+ beq 2f /* assume it's OK if so */
+ li r3,0
+ mfmsr r0
+ andi. r0,r0,MSR_DR /* MMU enabled? */
+ beq 7f
+ lis r3,KERNELBASE@h /* if so, are we */
+ cmpw 0,r4,r3 /* already running at KERNELBASE? */
+ beq 2f
+ rlwinm r4,r4,0,8,31 /* translate source address */
+ add r4,r4,r3 /* to region mapped with BATs */
+7: addis r9,r26,klimit@ha /* fetch klimit */
+ lwz r25,klimit@l(r9)
+ addis r25,r25,-KERNELBASE@h
+ li r6,0 /* Destination */
+#ifdef CONFIG_APUS
+ lis r9,0x6170
+ ori r9,r9,0x7573
+ cmpw 0,r9,r31
+ bne 8f
+ lis r6,0xfff0 /* Copy to 0xfff00000 on APUS */
+8:
+#endif
+ li r5,0x4000 /* # bytes of memory to copy */
+ bl copy_and_flush /* copy the first 0x4000 bytes */
+#ifdef CONFIG_APUS
+ cmpw 0,r9,r31 /* That's all we need on APUS. */
+ beq 2f
+#endif
+ addi r0,r3,4f@l /* jump to the address of 4f */
+ mtctr r0 /* in copy and do the rest. */
+ bctr /* jump to the copy */
+4: mr r5,r25
+ bl copy_and_flush /* copy the rest */
+2:
/*
* we now have the 1st 16M of ram mapped with the bats.
* prep needs the mmu to be turned on here, but pmac already has it on.
* this shouldn't bother the pmac since it just gets turned on again
* as we jump to our code at KERNELBASE. -- Cort
*/
+
+#else /* CONFIG_8xx */
+ tlbia /* Invalidate all TLB entries */
+ li r8, 0
+ mtspr MI_CTR, r8 /* Set instruction control to zero */
+ lis r8, MD_RESETVAL@h
+ mtspr MD_CTR, r8 /* Set data TLB control */
+
+ /* Now map the lower 8 Meg into the TLBs. For this quick hack,
+ * we can load the instruction and data TLB registers with the
+ * same values.
+ */
+ lis r8, KERNELBASE@h /* Create vaddr for TLB */
+ ori r8, r8, MI_EVALID /* Mark it valid */
+ mtspr MI_EPN, r8
+ mtspr MD_EPN, r8
+ li r8, MI_PS8MEG /* Set 8M byte page */
+ ori r8, r8, MI_SVALID /* Make it valid */
+ mtspr MI_TWC, r8
+ mtspr MD_TWC, r8
+ li r8, MI_BOOTINIT /* Create RPN for address 0 */
+ mtspr MI_RPN, r8 /* Store TLB entry */
+ mtspr MD_RPN, r8
+ lis r8, MI_Kp@h /* Set the protection mode */
+ mtspr MI_AP, r8
+ mtspr MD_AP, r8
+#ifdef CONFIG_MBX
+ /* Map another 8 MByte at 0xfa000000 to get the processor
+ * internal registers (among other things).
+ */
+ lis r8, 0xfa000000@h /* Create vaddr for TLB */
+ ori r8, r8, MD_EVALID /* Mark it valid */
+ mtspr MD_EPN, r8
+ li r8, MD_PS8MEG /* Set 8M byte page */
+ ori r8, r8, MD_SVALID /* Make it valid */
+ mtspr MD_TWC, r8
+ lis r8, 0xfa000000@h /* Create paddr for TLB */
+ ori r8, r8, MI_BOOTINIT
+ mtspr MD_RPN, r8
+#endif
+
+ /* Since the cache is enabled according to the information we
+ * just loaded into the TLB, invalidate and enable the caches here.
+ * We should probably check/set other modes....later.
+ */
+ lis r8, IDC_INVALL@h
+ mtspr IC_CST, r8
+ mtspr DC_CST, r8
+ lis r8, IDC_ENABLE@h
+ mtspr IC_CST, r8
+#ifdef notdef
+ mtspr DC_CST, r8
+#else
+ /* I still have a bug somewhere because the Ethernet driver
+ * does not want to work with copyback enabled. For now,
+ * at least enable write through.
+ */
+#if 0
+ lis r8, DC_SFWT@h
+ mtspr DC_CST, r8
+ lis r8, IDC_ENABLE@h
+ mtspr DC_CST, r8
+#endif
+#endif
+
+/* We now have the lower 8 Meg mapped into TLB entries, and the caches
+ * ready to work.
+ */
+#endif /* CONFIG_8xx */
+
mfmsr r0
ori r0,r0,MSR_DR|MSR_IR
mtspr SRR1,r0
@@ -202,48 +376,6 @@ __start:
#define STACK_UNDERHEAD 64
/*
- * Macros for storing registers into and loading registers from
- * exception frames.
- */
-#define SAVE_GPR(n, base) stw n,GPR0+4*(n)(base)
-#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base)
-#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base)
-#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base)
-#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base)
-#define REST_GPR(n, base) lwz n,GPR0+4*(n)(base)
-#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base)
-#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base)
-#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base)
-#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base)
-
-#define SAVE_FPR(n, base) stfd n,TSS_FPR0+8*(n)(base)
-#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base)
-#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base)
-#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base)
-#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base)
-#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base)
-#define REST_FPR(n, base) lfd n,TSS_FPR0+8*(n)(base)
-#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base)
-#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base)
-#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base)
-#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base)
-#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base)
-
-#ifndef CONFIG_APUS
-#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h
-#define tovirt(rd,rs,rt) addis rd,rs,KERNELBASE@h
-#else
-#define tophys(rd,rs,rt) \
- lis rt,CYBERBASEp@h; \
- lwz rt,0(rt); \
- add rd,rs,rt
-#define tovirt(rd,rs,rt) \
- lis rt,CYBERBASEp@h; \
- lwz rt,0(rt); \
- sub rd,rs,rt
-#endif
-
-/*
* Exception entry code. This code runs with address translation
* turned off, i.e. using physical addresses.
* We assume sprg3 has the physical address of the current
@@ -303,11 +435,15 @@ label: \
/* Machine check */
STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
-/* Data access exception */
+/* Data access exception.
+ * This is "never generated" by the MPC8xx. We jump to it for other
+ * translation errors.
+ */
. = 0x300
DataAccess:
EXCEPTION_PROLOG
mfspr r20,DSISR
+#ifndef CONFIG_8xx
andis. r0,r20,0xa470 /* weird error? */
bne 1f /* if not, try to put a PTE */
mfspr r3,DAR /* into the hash table */
@@ -315,6 +451,7 @@ DataAccess:
rlwimi r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */
mfspr r5,SPRG3 /* phys addr of TSS */
bl hash_page
+#endif
1: stw r20,_DSISR(r21)
mr r5,r20
mfspr r4,DAR
@@ -326,10 +463,14 @@ DataAccess:
.long do_page_fault
.long int_return
-/* Instruction access exception */
+/* Instruction access exception.
+ * This is "never generated" by the MPC8xx. We jump to it for other
+ * translation errors.
+ */
. = 0x400
InstructionAccess:
EXCEPTION_PROLOG
+#ifndef CONFIG_8xx
andis. r0,r23,0x4000 /* no pte found? */
beq 1f /* if so, try to put a PTE */
mr r3,r22 /* into the hash table */
@@ -337,6 +478,7 @@ InstructionAccess:
mr r20,r23 /* SRR1 has reason bits */
mfspr r5,SPRG3 /* phys addr of TSS */
bl hash_page
+#endif
1: addi r3,r1,STACK_FRAME_OVERHEAD
mr r4,r22
mr r5,r23
@@ -347,7 +489,38 @@ InstructionAccess:
.long int_return
/* External interrupt */
- STD_EXCEPTION(0x500, HardwareInterrupt, do_IRQ)
+ . = 0x500;
+HardwareInterrupt:
+ EXCEPTION_PROLOG;
+#ifdef CONFIG_APUS
+ mfmsr 20
+ xori r20,r20,MSR_DR
+ sync
+ mtmsr r20
+ sync
+
+ lis r3,APUS_IPL_EMU@h
+
+ li r20,(IPLEMU_SETRESET|IPLEMU_DISABLEINT)
+ stb r20,APUS_IPL_EMU@l(r3)
+ sync
+
+ lbz r3,APUS_IPL_EMU@l(r3)
+
+ mfmsr r20
+ xori r20,r20,MSR_DR
+ sync
+ mtmsr r20
+ sync
+
+ stw r3,(_CCR+4)(r21);
+#endif
+ addi r3,r1,STACK_FRAME_OVERHEAD;
+ li r20,MSR_KERNEL;
+ bl transfer_to_handler;
+ .long do_IRQ;
+ .long int_return
+
/* Alignment exception */
. = 0x600
@@ -375,6 +548,7 @@ ProgramCheck:
.long ProgramCheckException
.long int_return
+#ifndef CONFIG_8xx
/* Floating-point unavailable */
. = 0x800
FPUnavailable:
@@ -384,6 +558,11 @@ FPUnavailable:
bl transfer_to_handler /* if from kernel, take a trap */
.long KernelFP
.long int_return
+#else
+/* No FPU on MPC8xx. This exception is not supposed to happen.
+*/
+ STD_EXCEPTION(0x800, FPUnavailable, UnknownException)
+#endif
STD_EXCEPTION(0x900, Decrementer, timer_interrupt)
STD_EXCEPTION(0xa00, Trap_0a, UnknownException)
@@ -406,6 +585,7 @@ SystemCall:
STD_EXCEPTION(0xe00, Trap_0e, UnknownException)
STD_EXCEPTION(0xf00, Trap_0f, UnknownException)
+#ifndef CONFIG_8xx
/*
* Handle TLB miss for instruction on 603/603e.
* Note: we get an alternate set of r0 - r3 to use automatically.
@@ -502,7 +682,14 @@ InstructionAddressInvalid:
sync /* Some chip revs have problems here... */
mtmsr r0
b InstructionAccess
+#else
+/* On the MPC8xx, this is a software emulation interrupt. It occurs
+ * for all unimplemented and illegal instructions.
+ */
+ STD_EXCEPTION(0x1000, SoftEmu, SoftwareEmulation)
+#endif
+#ifndef CONFIG_8xx
/*
* Handle TLB miss for DATA Load operation on 603/603e
*/
@@ -598,12 +785,78 @@ DataAddressInvalid:
sync /* Some chip revs have problems here... */
mtmsr r0
b DataAccess
+#else
+/*
+ * For the MPC8xx, this is a software tablewalk to load the instruction
+ * TLB. It is modelled after the example in the Motorola manual. The task
+ * switch loads the M_TWB register with the pointer to the first level table.
+ * If we discover there is no second level table (the value is zero), the
+ * plan was to load that into the TLB, which causes another fault into the
+ * TLB Error interrupt where we can handle such problems. However, that did
+ * not work, so if we discover there is no second level table, we restore
+ * registers and branch to the error exception. We have to use the MD_xxx
+ * registers for the tablewalk because the equivalent MI_xxx registers
+ * only perform the attribute functions.
+ */
+InstructionTLBMiss:
+ mtspr M_TW, r20 /* Save a couple of working registers */
+ mfcr r20
+ stw r20, 0(r0)
+ stw r21, 4(r0)
+ mfspr r20, SRR0 /* Get effective address of fault */
+ mtspr MD_EPN, r20 /* Have to use MD_EPN for walk, MI_EPN can't */
+ mfspr r20, M_TWB /* Get level 1 table entry address */
+ lwz r21, 0(r20) /* Get the level 1 entry */
+ rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */
+ beq 2f /* If zero, don't try to find a pte */
+
+ /* We have a pte table, so load the MI_TWC with the attributes
+ * for this page, which has only bit 31 set.
+ */
+ tophys(r21,r21,0)
+ ori r21,r21,1 /* Set valid bit */
+ mtspr MI_TWC, r21 /* Set page attributes */
+ mtspr MD_TWC, r21 /* Load pte table base address */
+ mfspr r21, MD_TWC /* ....and get the pte address */
+ lwz r21, 0(r21) /* Get the pte */
+
+ /* Set four subpage valid bits (24, 25, 26, and 27).
+ * Since we currently run MI_CTR.PPCS = 0, the manual says,
+ * "If the page size is larger than 4k byte, then all the
+ * 4 bits should have the same value."
+ * I don't really know what to do if the page size is 4k Bytes,
+ * but I know setting them all to 0 does not work, and setting them
+ * all to 1 does, so that is the way it is right now.
+ * BTW, these four bits map to the software only bits in the
+ * linux page table. I used to turn them all of, but now just
+ * set them all for the hardware.
+ li r20, 0x00f0
+ andc r20, r21, r20
+ ori r20, r20, 0x0080
+ */
+ ori r20, r21, 0x00f0
+ mtspr MI_RPN, r20 /* Update TLB entry */
+
+ mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ rfi
+
+2: mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ b InstructionAccess
+#endif /* CONFIG_8xx */
+
/*
* Handle TLB miss for DATA Store on 603/603e
*/
. = 0x1200
DataStoreTLBMiss:
+#ifndef CONFIG_8xx
#ifdef NO_RELOAD_HTAB
/*
* r0: stored ctr
@@ -671,27 +924,164 @@ DataStoreTLBMiss:
ori r3,r3,0x40 /* Set secondary hash */
b 00b /* Try lookup again */
#endif /* NO_RELOAD_HTAB */
-
+#else /* CONFIG_8xx */
+ mtspr M_TW, r20 /* Save a couple of working registers */
+ mfcr r20
+ stw r20, 0(r0)
+ stw r21, 4(r0)
+ mfspr r20, M_TWB /* Get level 1 table entry address */
+ lwz r21, 0(r20) /* Get the level 1 entry */
+ rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */
+ beq 2f /* If zero, don't try to find a pte */
+
+ /* We have a pte table, so load fetch the pte from the table.
+ */
+ tophys(r21, r21, 0)
+ ori r21, r21, 1 /* Set valid bit in physical L2 page */
+ mtspr MD_TWC, r21 /* Load pte table base address */
+ mfspr r21, MD_TWC /* ....and get the pte address */
+ lwz r21, 0(r21) /* Get the pte */
+
+ /* Set four subpage valid bits (24, 25, 26, and 27).
+ * Since we currently run MD_CTR.PPCS = 0, the manual says,
+ * "If the page size is larger than 4k byte, then all the
+ * 4 bits should have the same value."
+ * I don't really know what to do if the page size is 4k Bytes,
+ * but I know setting them all to 0 does not work, and setting them
+ * all to 1 does, so that is the way it is right now.
+ * BTW, these four bits map to the software only bits in the
+ * linux page table. I used to turn them all of, but now just
+ * set them all for the hardware.
+ li r20, 0x00f0
+ andc r20, r21, r20
+ ori r20, r20, 0x0080
+ */
+ ori r20, r21, 0x00f0
+ mtspr MD_RPN, r20 /* Update TLB entry */
+
+ mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ rfi
+
+2: mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ b DataAccess
+#endif /* CONFIG_8xx */
+
+#ifndef CONFIG_8xx
/* Instruction address breakpoint exception (on 603/604) */
STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint)
+#else
+
+/* This is an instruction TLB error on the MPC8xx. This could be due
+ * to many reasons, such as executing guarded memory or illegal instruction
+ * addresses. There is nothing to do but handle a big time error fault.
+ */
+ . = 0x1300
+InstructionTLBError:
+ b InstructionAccess
+#endif
/* System management exception (603?) */
+#ifndef CONFIG_8xx
STD_EXCEPTION(0x1400, Trap_14, UnknownException)
+#else
+
+/* This is the data TLB error on the MPC8xx. This could be due to
+ * many reasons, including a dirty update to a pte. We can catch that
+ * one here, but anything else is an error. First, we track down the
+ * Linux pte. If it is valid, write access is allowed, but the
+ * page dirty bit is not set, we will set it and reload the TLB. For
+ * any other case, we bail out to a higher level function that can
+ * handle it.
+ */
+ . = 0x1400
+DataTLBError:
+ mtspr M_TW, r20 /* Save a couple of working registers */
+ mfcr r20
+ stw r20, 0(r0)
+ stw r21, 4(r0)
+
+ /* First, make sure this was a store operation.
+ */
+ mfspr r20, DSISR
+ andis. r21, r20, 0x0200 /* If set, indicates store op */
+ beq 2f
+
+ mfspr r20, M_TWB /* Get level 1 table entry address */
+ lwz r21, 0(r20) /* Get the level 1 entry */
+ rlwinm. r20, r21,0,0,20 /* Extract page descriptor page address */
+ beq 2f /* If zero, bail */
+
+ /* We have a pte table, so fetch the pte from the table.
+ */
+ tophys(r21, r21, 0)
+ ori r21, r21, 1 /* Set valid bit in physical L2 page */
+ mtspr MD_TWC, r21 /* Load pte table base address */
+ mfspr r21, MD_TWC /* ....and get the pte address */
+ lwz r21, 0(r21) /* Get the pte */
+
+ andi. r20, r21, _PAGE_RW /* Is it writeable? */
+ beq 2f /* Bail out if not */
+
+ ori r21, r21, _PAGE_DIRTY /* Update changed bit */
+ mfspr r20, MD_TWC /* Get pte address again */
+ stw r21, 0(r20) /* and update pte in table */
+
+ /* Set four subpage valid bits (24, 25, 26, and 27).
+ * Since we currently run MD_CTR.PPCS = 0, the manual says,
+ * "If the page size is larger than 4k byte, then all the
+ * 4 bits should have the same value."
+ * I don't really know what to do if the page size is 4k Bytes,
+ * but I know setting them all to 0 does not work, and setting them
+ * all to 1 does, so that is the way it is right now.
+ * BTW, these four bits map to the software only bits in the
+ * linux page table. I used to turn them all of, but now just
+ * set them all for the hardware.
+ li r20, 0x00f0
+ andc r20, r21, r20
+ ori r20, r20, 0x0080
+ */
+ ori r20, r21, 0x00f0
+
+ mtspr MD_RPN, r20 /* Update TLB entry */
+
+ mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ rfi
+2:
+ mfspr r20, M_TW /* Restore registers */
+ lwz r21, 0(r0)
+ mtcr r21
+ lwz r21, 4(r0)
+ b DataAccess
+#endif /* CONFIG_8xx */
STD_EXCEPTION(0x1500, Trap_15, UnknownException)
STD_EXCEPTION(0x1600, Trap_16, UnknownException)
- STD_EXCEPTION(0x1700, Trap_17, UnknownException)
+ STD_EXCEPTION(0x1700, Trap_17, TAUException)
STD_EXCEPTION(0x1800, Trap_18, UnknownException)
STD_EXCEPTION(0x1900, Trap_19, UnknownException)
STD_EXCEPTION(0x1a00, Trap_1a, UnknownException)
STD_EXCEPTION(0x1b00, Trap_1b, UnknownException)
+/* On the MPC8xx, these next four traps are used for development
+ * support of breakpoints and such. Someday I will get around to
+ * using them.
+ */
STD_EXCEPTION(0x1c00, Trap_1c, UnknownException)
STD_EXCEPTION(0x1d00, Trap_1d, UnknownException)
STD_EXCEPTION(0x1e00, Trap_1e, UnknownException)
STD_EXCEPTION(0x1f00, Trap_1f, UnknownException)
-/* Run mode exception */
+#ifndef CONFIG_8xx
+ /* Run mode exception */
STD_EXCEPTION(0x2000, RunMode, RunModeException)
STD_EXCEPTION(0x2100, Trap_21, UnknownException)
@@ -711,6 +1101,9 @@ DataStoreTLBMiss:
STD_EXCEPTION(0x2f00, Trap_2f, UnknownException)
. = 0x3000
+#else
+ . = 0x2000
+#endif
/*
* This code finishes saving the registers to the exception frame
@@ -720,6 +1113,8 @@ DataStoreTLBMiss:
.globl transfer_to_handler
transfer_to_handler:
stw r22,_NIP(r21)
+ lis r22,MSR_POW@h
+ andc r23,r23,r22
stw r23,_MSR(r21)
SAVE_GPR(7, r21)
SAVE_4GPRS(8, r21)
@@ -768,6 +1163,7 @@ stack_ovf:
SYNC
rfi
+#ifndef CONFIG_8xx
/*
* Continuation of the floating-point unavailable handler.
*/
@@ -790,9 +1186,18 @@ load_up_fpu:
ori r5,r5,MSR_FP
SYNC
mtmsr r5 /* enable use of fpu now */
+#ifndef __SMP__
SYNC
cmpi 0,r4,0
beq 1f
+#else
+/*
+ * All the saving of last_task_used_math is handled
+ * by a switch_to() call to smp_giveup_fpu() in SMP so
+ * last_task_used_math is not used. -- Cort
+ */
+ b 1f
+#endif
add r4,r4,r6
addi r4,r4,TSS /* want TSS of last_task_used_math */
SAVE_32FPRS(0, r4)
@@ -810,9 +1215,11 @@ load_up_fpu:
lfd fr0,TSS_FPSCR-4(r5)
mtfsf 0xff,fr0
REST_32FPRS(0, r5)
+#ifndef __SMP__
subi r4,r5,TSS
sub r4,r4,r6
stw r4,last_task_used_math@l(r3)
+#endif /* __SMP__ */
/* restore registers and return */
lwz r3,_CCR(r21)
lwz r4,_LINK(r21)
@@ -859,16 +1266,6 @@ Hash_msk = (((1 << Hash_bits) - 1) * 64)
.globl hash_page
hash_page:
-#ifdef __SMP__
- lis r6,hash_table_lock@h
- ori r6,r6,hash_table_lock@l
- tophys(r6,r6,r2)
-1011: lwarx r0,0,r6
- stwcx. r6,0,r6
- bne- 1011b
- cmpi 0,r0,0
- bne 1011b
-#endif /* __SMP__ */
/* Get PTE (linux-style) and check access */
lwz r5,PG_TABLES(r5)
tophys(r5,r5,r2) /* convert to phys addr */
@@ -1018,7 +1415,6 @@ found_slot:
lwz r3,0(r2)
addi r3,r3,1
stw r3,0(r2)
- SYNC
/* Return from the exception */
lwz r3,_CCR(r21)
@@ -1027,19 +1423,14 @@ found_slot:
mtcrf 0xff,r3
mtlr r4
mtctr r5
-#ifdef __SMP__
- lis r5,hash_table_lock@h
- ori r5,r5,hash_table_lock@l
- tophys(r5,r5,r6)
- li r6,0
- stw r6,0(r5)
-#endif /* __SMP__ */
REST_GPR(0, r21)
REST_2GPRS(1, r21)
REST_4GPRS(3, r21)
/* we haven't used xer */
+ SYNC
mtspr SRR1,r23
mtspr SRR0,r22
+ SYNC
REST_GPR(20, r21)
REST_2GPRS(22, r21)
lwz r21,GPR21(r21)
@@ -1047,16 +1438,37 @@ found_slot:
rfi
hash_page_out:
-#ifdef __SMP__
- lis r5,hash_table_lock@h
- ori r5,r5,hash_table_lock@l
- tophys(r5,r5,r6)
- li r6,0
- stw r6,0(r5)
-#endif /* __SMP__ */
blr
next_slot:
.long 0
+#endif /* CONFIG_8xx */
+
+#ifndef CONFIG_APUS
+/*
+ * Copy routine used to copy the kernel to start at physical address 0
+ * and flush and invalidate the caches as needed.
+ * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset
+ * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5.
+ */
+copy_and_flush:
+ addi r5,r5,-4
+ addi r6,r6,-4
+4: li r0,8
+ mtctr r0
+3: addi r6,r6,4 /* copy a cache line */
+ lwzx r0,r6,r4
+ stwx r0,r6,r3
+ bdnz 3b
+ dcbst r6,r3 /* write it to memory */
+ sync
+ icbi r6,r3 /* flush the icache line */
+ cmplw 0,r6,r5
+ blt 4b
+ isync
+ addi r5,r5,4
+ addi r6,r6,4
+ blr
+#endif
#ifdef CONFIG_APUS
/* On APUS the first 0x4000 bytes of the kernel will be mapped
@@ -1072,6 +1484,7 @@ next_slot:
*/
start_here:
+#ifndef CONFIG_8xx
/*
* Enable caches and 604-specific features if necessary.
*/
@@ -1108,6 +1521,7 @@ start_here:
ori r11,r11,HID0_BTCD
5: mtspr HID0,r11 /* superscalar exec & br history tbl */
4:
+#endif /* CONFIG_8xx */
/* ptr to current */
lis r2,init_task_union@h
ori r2,r2,init_task_union@l
@@ -1140,6 +1554,9 @@ start_here:
mr r6,r28
mr r7,r27
bl identify_machine
+#ifdef CONFIG_MBX
+ bl set_mbx_memory
+#endif
bl MMU_init
/*
@@ -1147,8 +1564,19 @@ start_here:
* for SDR1 (hash table pointer) and the segment registers
* and change to using our exception vectors.
*/
+#ifndef CONFIG_8xx
lis r6,_SDR1@ha
lwz r6,_SDR1@l(r6)
+#else
+ /* The right way to do this would be to track it down through
+ * init's TSS like the context switch code does, but this is
+ * easier......until someone changes init's static structures.
+ */
+ lis r6, swapper_pg_dir@h
+ tophys(r6,r6,0)
+ ori r6, r6, swapper_pg_dir@l
+ mtspr M_TWB, r6
+#endif
lis r4,2f@h
ori r4,r4,2f@l
tophys(r4,r4,r3)
@@ -1158,8 +1586,10 @@ start_here:
rfi
/* Load up the kernel context */
2:
+
SYNC /* Force all PTE updates to finish */
tlbia /* Clear all TLB entries */
+#ifndef CONFIG_8xx
mtspr SDR1,r6
li r0,16 /* load up segment register values */
mtctr r0 /* for context 0 */
@@ -1179,7 +1609,8 @@ start_here:
LOAD_BAT(1,16,r3,r4,r5)
LOAD_BAT(2,32,r3,r4,r5)
LOAD_BAT(3,48,r3,r4,r5)
-
+#endif /* CONFIG_8xx */
+
/* Set up for using our exception vectors */
/* ptr to phys current tss */
tophys(r4,r2,r4)
@@ -1188,36 +1619,6 @@ start_here:
li r3,0
mtspr SPRG2,r3 /* 0 => r1 has kernel sp */
-/* On CHRP copy exception vectors down to 0 */
- lis r5,_stext@ha
- addi r5,r5,_stext@l
- addis r5,r5,-KERNELBASE@h
- cmpwi 0,r5,0
- beq 77f /* vectors are already at 0 */
- li r3,0x1000
- mtctr r3
- li r4,-4
- addi r5,r5,-4
-74: lwzu r0,4(r5)
- stwu r0,4(r4)
- bdnz 74b
- /* need to flush/invalidate caches too */
- li r3,0x4000/CACHE_LINE_SIZE
- li r4,0
- mtctr r3
-73: dcbst 0,r4
- addi r4,r4,CACHE_LINE_SIZE
- bdnz 73b
- sync
- li r4,0
- mtctr r3
-72: icbi 0,r4
- addi r4,r4,CACHE_LINE_SIZE
- bdnz 72b
- sync
- isync
-77:
-
/* Now turn on the MMU for real! */
li r4,MSR_KERNEL
lis r3,start_kernel@h
@@ -1227,29 +1628,6 @@ start_here:
rfi /* enable MMU and jump to start_kernel */
- .globl reset_SDR1
-reset_SDR1:
- lis r6,_SDR1@ha
- lwz r6,_SDR1@l(r6)
- mfmsr r5
- li r4,0
- ori r4,r4,MSR_EE|MSR_IR|MSR_DR
- andc r3,r5,r4
- lis r4,2f@h
- ori r4,r4,2f@l
- tophys(r4,r4,r5)
- mtspr SRR0,r4
- mtspr SRR1,r3
- rfi
-2: /* load new SDR1 */
- tlbia
- mtspr SDR1,r6
- /* turn the mmu back on */
- mflr r3
- mtspr SRR0,r3
- mtspr SRR1,r5
- rfi
-
/*
* FP unavailable trap from kernel - print a message, but let
* the task use FP in the kernel until it returns to user mode.
@@ -1272,10 +1650,19 @@ KernelFP:
* and save its floating-point registers in its thread_struct.
* Enables the FPU for use in the kernel on return.
*/
+/* smp_giveup_fpu() takes an arg to tell it where to save the fpu
+ * regs since last_task_used_math can't be trusted (many many race
+ * conditions). -- Cort
+ */
+ .globl smp_giveup_fpu
+smp_giveup_fpu:
+ mr r4,r3
+ b 12f
.globl giveup_fpu
giveup_fpu:
lis r3,last_task_used_math@ha
lwz r4,last_task_used_math@l(r3)
+12:
mfmsr r5
ori r5,r5,MSR_FP
SYNC
@@ -1284,8 +1671,10 @@ giveup_fpu:
cmpi 0,r4,0
beqlr- /* if no previous owner, done */
addi r4,r4,TSS /* want TSS of last_task_used_math */
+#ifndef __SMP__
li r5,0
stw r5,last_task_used_math@l(r3)
+#endif /* __SMP__ */
SAVE_32FPRS(0, r4)
mffs fr0
stfd fr0,TSS_FPSCR-4(r4)
@@ -1445,6 +1834,18 @@ syscall_ret_2:
*
* The code which creates the new task context is in 'copy_thread'
* in arch/ppc/kernel/process.c
+ *
+ * The MPC8xx has something that currently happens "automagically."
+ * Unshared user space address translations are subject to ASID (context)
+ * match. During each task switch, the ASID is incremented. We can
+ * guarantee (I hope :-) that no entries currently match this ASID
+ * because every task will cause at least a TLB entry to be loaded for
+ * the first instruction and data access, plus the kernel running will
+ * have displaced several more TLBs. The MMU contains 32 entries for
+ * each TLB, and there are 16 contexts, so we just need to make sure
+ * two pages get replaced for every context switch, which currently
+ * happens. There are other TLB management techniques that I will
+ * eventually implement, but this is the easiest for now. -- Dan
*/
_GLOBAL(_switch)
stwu r1,-INT_FRAME_SIZE-STACK_UNDERHEAD(r1)
@@ -1476,6 +1877,7 @@ _GLOBAL(_switch)
SYNC
lwz r1,KSP(r4) /* Load new stack pointer */
addi r2,r4,-TSS /* Update current */
+#ifndef CONFIG_8xx
/* Set up segment registers for new task */
rlwinm r5,r5,4,8,27 /* VSID = context << 4 */
addis r5,r5,0x6000 /* Set Ks, Ku bits */
@@ -1486,9 +1888,32 @@ _GLOBAL(_switch)
addi r5,r5,1 /* next VSID */
addis r3,r3,0x1000 /* address of next segment */
bdnz 3b
+#else
+/* On the MPC8xx, we place the physical address of the new task
+ * page directory loaded into the MMU base register, and set the
+ * ASID compare register with the new "context".
+ */
+ mtspr M_CASID, r5 /* Update context */
+ lwz r5,MM-TSS(r4) /* Get virtual address of mm */
+ lwz r5,PGD(r5) /* get new->mm->pgd */
+ tophys(r5, r5, 0) /* convert to phys addr */
+ mtspr M_TWB, r5 /* Update MMU base address */
+#endif
SYNC
/* FALL THROUGH into int_return */
+#ifdef __SMP__
+ /* drop scheduler_lock since we weren't called by schedule() */
+ lwz r5,TSS_SMP_FORK_RET(r4)
+ cmpi 0,r5,0
+ beq+ int_return
+ li r3,0
+ lis r5,scheduler_lock@ha
+ stw r3,TSS_SMP_FORK_RET(r4)
+ stw r3,scheduler_lock@l+4(r5) /* owner_pc */
+ stw r3,scheduler_lock@l+8(r5) /* owner_cpu */
+ stw r3,scheduler_lock@l(r5) /* lock */
+#endif /* __SMP__ */
/*
* Trap exit.
@@ -1566,6 +1991,18 @@ int_return:
SYNC
rfi
+#if 0/*def __SMP__*/
+ .globl ret_from_smpfork
+ret_from_smpfork:
+ /* drop scheduler_lock since schedule() called us */
+ lis r4,scheduler_lock@ha
+ li r5,0
+ stw r5,scheduler_lock@l+4(r4) /* owner_pc */
+ stw r5,scheduler_lock@l+8(r4) /* owner_cpu */
+ stw r5,scheduler_lock@l(r4) /* lock */
+ b int_return
+#endif /* __SMP__ */
+
/*
* Fake an interrupt from kernel mode.
* This is used when enable_irq loses an interrupt.
@@ -1686,6 +2123,7 @@ _GLOBAL(flush_page_to_ram)
* Flush entries from the hash table with VSIDs in the range
* given.
*/
+#ifndef CONFIG_8xx
_GLOBAL(flush_hash_segments)
#ifdef NO_RELOAD_HTAB
/*
@@ -1700,15 +2138,6 @@ _GLOBAL(flush_hash_segments)
rlwnm. r0,r9,r0,0,0
bne 99f
#endif /* NO_RELOAD_HTAB */
-#ifdef __SMP__
- lis r6,hash_table_lock@h
- ori r6,r6,hash_table_lock@l
-1011: lwarx r0,0,r6
- stwcx. r6,0,r6
- bne- 1011b
- cmpi 0,r0,0
- bne 1011b
-#endif /* __SMP__ */
rlwinm r3,r3,7,1,24 /* put VSID lower limit in position */
oris r3,r3,0x8000 /* set V bit */
rlwinm r4,r4,7,1,24 /* put VSID upper limit in position */
@@ -1730,12 +2159,6 @@ _GLOBAL(flush_hash_segments)
stw r0,0(r5) /* invalidate entry */
2: bdnz 1b /* continue with loop */
sync
-#ifdef __SMP__
- lis r5,hash_table_lock@h
- ori r5,r5,hash_table_lock@l
- li r6,0
- stw r6,0(r5)
-#endif /* __SMP__ */
99: tlbia
isync
blr
@@ -1753,15 +2176,6 @@ _GLOBAL(flush_hash_page)
rlwnm. r0,r9,r0,0,0
bne 99f
#endif /* NO_RELOAD_HTAB */
-#ifdef __SMP__
- lis r6,hash_table_lock@h
- ori r6,r6,hash_table_lock@l
-1011: lwarx r0,0,r6
- stwcx. r6,0,r6
- bne- 1011b
- cmpi 0,r0,0
- bne 1011b
-#endif /* __SMP__ */
rlwinm r3,r3,11,1,20 /* put context into vsid */
rlwimi r3,r4,11,21,24 /* put top 4 bits of va into vsid */
oris r3,r3,0x8000 /* set V (valid) bit */
@@ -1794,22 +2208,17 @@ _GLOBAL(flush_hash_page)
3: li r0,0
stw r0,0(r7) /* invalidate entry */
4: sync
-#ifdef __SMP__
- lis r5,hash_table_lock@h
- ori r5,r5,hash_table_lock@l
- li r6,0
- stw r6,0(r5)
-#endif /* __SMP__ */
99: tlbie r4 /* in hw tlb too */
isync
blr
-
+#endif /* CONFIG_8xx */
/*
* This routine is just here to keep GCC happy - sigh...
*/
_GLOBAL(__main)
blr
+#ifndef CONFIG_8xx
/*
* On CHRP, the Run-Time Abstraction Services (RTAS) have to be
* called with the MMU off.
@@ -1819,9 +2228,9 @@ enter_rtas:
stwu r1,-16(r1)
mflr r0
stw r0,20(r1)
- addis r3,r3,-KERNELBASE@h
lis r4,rtas_data@ha
lwz r4,rtas_data@l(r4)
+ addis r4,r4,-KERNELBASE@h
lis r6,1f@ha /* physical return address for rtas */
addi r6,r6,1f@l
addis r6,r6,-KERNELBASE@h
@@ -1829,14 +2238,15 @@ enter_rtas:
addis r7,r7,-KERNELBASE@h
lis r8,rtas_entry@ha
lwz r8,rtas_entry@l(r8)
+ addis r5,r8,-KERNELBASE@h
mfmsr r9
stw r9,8(r1)
- li r0,0
ori r0,r0,MSR_EE|MSR_SE|MSR_BE
andc r0,r9,r0
andi. r9,r9,MSR_ME|MSR_RI
sync /* disable interrupts so SRR0/1 */
mtmsr r0 /* don't get trashed */
+ li r6,0
mtlr r6
mtspr SPRG2,r7
mtspr SRR0,r8
@@ -1850,23 +2260,26 @@ enter_rtas:
mtspr SRR0,r8
mtspr SRR1,r9
rfi /* return to caller */
+#endif /* CONFIG_8xx */
+#ifdef CONFIG_8xx
+/* This is called during an exec when new page tables are created.
+ * It maps to the SET_PAGE_DIR macro. I guess I should make it an
+ * inline function.
+ */
+_GLOBAL(set_page_dir)
+ addis r3,r3,-KERNELBASE@h /* convert to phys addr */
+ mtspr M_TWB, r3 /* Update MMU base address */
+ blr
+#endif
- .globl amhere
-amhere: .long 0
-
+
#ifdef __SMP__
/*
* Secondary processor begins executing here.
*/
.globl secondary_entry
secondary_entry:
- lis r0,amhere@h
- ori r0,r0,amhere@l
- addis r0,r0,-KERNELBASE@h
- stw r0,0(r0)
- sync
- isync
/* just like __start() with a few changes -- Cort */
mfspr r9,PVR
rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */
@@ -1938,16 +2351,6 @@ secondary_entry:
ori r11,r11,HID0_BTCD
5: mtspr HID0,r11 /* superscalar exec & br history tbl */
4:
- /* get ptr to current */
- lis r2,current_set@h
- ori r2,r2,current_set@l
- /* assume we're second processor for now */
- lwz r2,4(r2)
- /* stack */
- addi r1,r2,TASK_UNION_SIZE
- li r0,0
- stwu r0,-STACK_FRAME_OVERHEAD(r1)
-
/*
* init_MMU on the first processor has setup the variables
* for us - all we need to do is load them -- Cort
@@ -1969,6 +2372,18 @@ secondary_entry:
rfi
/* Load up the kernel context */
2:
+ /* get ptr to current */
+ lis r2,current_set@h
+ ori r2,r2,current_set@l
+ /* assume we're second processor for now */
+ tophys(r2,r2,r10)
+ lwz r2,4(r2)
+ /* stack */
+ addi r1,r2,TASK_UNION_SIZE
+ li r0,0
+ tophys(r3,r1,r10)
+ stwu r0,-STACK_FRAME_OVERHEAD(r3)
+
SYNC /* Force all PTE updates to finish */
tlbia /* Clear all TLB entries */
mtspr SDR1,r6
@@ -2025,6 +2440,31 @@ secondary_entry:
/* should never return */
.long 0
#endif /* __SMP__ */
+
+#ifdef CONFIG_MBX
+/* Jump into the system reset for the MBX rom.
+ * We first disable the MMU, and then jump to the ROM reset address.
+ *
+ * This does not work, don't bother trying. There is no place in
+ * the ROM we can jump to cause a reset. We will have to program
+ * a watchdog of some type that we don't service to cause a processor
+ * reset.
+ */
+ .globl MBX_gorom
+MBX_gorom:
+ li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
+ lis r4,2f@h
+ addis r4,r4,-KERNELBASE@h
+ ori r4,r4,2f@l
+ mtspr SRR0,r4
+ mtspr SRR1,r3
+ rfi
+2:
+ lis r4, 0xfe000000@h
+ addi r4, r4, 0xfe000000@l
+ mtlr r4
+ blr
+#endif
/*
* We put a few things here that have to be page-aligned.
diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c
index f47d6f3d6..21c6966e0 100644
--- a/arch/ppc/kernel/idle.c
+++ b/arch/ppc/kernel/idle.c
@@ -1,5 +1,5 @@
/*
- * $Id: idle.c,v 1.13 1998/01/06 06:44:55 cort Exp $
+ * $Id: idle.c,v 1.35 1998/04/07 20:24:23 cort Exp $
*
* Idle daemon for PowerPC. Idle daemon will handle any action
* that needs to be taken when the system becomes idle.
@@ -13,6 +13,7 @@
*/
#define __KERNEL_SYSCALLS__
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -31,41 +32,44 @@
#include <asm/smp_lock.h>
#include <asm/processor.h>
#include <asm/mmu.h>
+#include <asm/cache.h>
+#ifdef CONFIG_PMAC
+#include <asm/mediabay.h>
+#endif
-int zero_paged(void *unused);
-void inline power_save(void);
+void zero_paged(void);
+void power_save(void);
void inline htab_reclaim(void);
+unsigned long htab_reclaim_on = 0;
+unsigned long zero_paged_on = 0;
+
int idled(void *unused)
{
int ret = -EPERM;
- /*
- * want one per cpu since it would be nice to have all
- * processors who aren't doing anything
- * zero-ing pages since this daemon is lock-free
- * -- Cort
- */
- /* kernel_thread(zero_paged, NULL, 0); */
-
-#ifdef __SMP__
-printk("SMP %d: in idle. current = %s/%d\n",
- current->processor,current->comm,current->pid);
-#endif /* __SMP__ */
for (;;)
{
+ __sti();
+
/* endless loop with no priority at all */
current->priority = -100;
current->counter = -100;
+
+ check_pgt_cache();
- /* endless idle loop with no priority at all */
- /* htab_reclaim(); */
- schedule();
+ if ( !need_resched && zero_paged_on ) zero_paged();
+ if ( !need_resched && htab_reclaim_on ) htab_reclaim();
+
+ /*
+ * Only processor 1 may sleep now since processor 2 would
+ * never wake up. Need to add timer code for processor 2
+ * then it can sleep. -- Cort
+ */
#ifndef __SMP__
- /* can't do this on smp since second processor
- will never wake up -- Cort */
- /* power_save(); */
-#endif /* __SMP__ */
+ if ( !need_resched ) power_save();
+#endif /* __SMP__ */
+ schedule();
}
ret = 0;
return ret;
@@ -76,13 +80,16 @@ printk("SMP %d: in idle. current = %s/%d\n",
* Mark 'zombie' pte's in the hash table as invalid.
* This improves performance for the hash table reload code
* a bit since we don't consider unused pages as valid.
- * I haven't done any rigorous performance analysis yet
- * so it's still experimental and turned off here.
* -- Cort
*/
+PTE *reclaim_ptr = 0;
void inline htab_reclaim(void)
{
+#ifndef CONFIG_8xx
+#if 0
PTE *ptr, *start;
+ static int dir = 1;
+#endif
struct task_struct *p;
unsigned long valid = 0;
extern PTE *Hash, *Hash_end;
@@ -91,28 +98,33 @@ void inline htab_reclaim(void)
/* if we don't have a htab */
if ( Hash_size == 0 )
return;
- /*lock_dcache();*/
-
+ lock_dcache(1);
+
+#if 0
/* find a random place in the htab to start each time */
- start = &Hash[jiffies%(Hash_size/sizeof(ptr))];
- for ( ptr = start; ptr < Hash_end ; ptr++)
+ start = &Hash[jiffies%(Hash_size/sizeof(PTE))];
+ /* go a different direction each time */
+ dir *= -1;
+ for ( ptr = start;
+ !need_resched && (ptr != Hash_end) && (ptr != Hash);
+ ptr += dir)
{
- if ( ptr == start )
- return;
- if ( ptr == Hash_end )
- ptr = Hash;
- valid = 0;
- if (!ptr->v)
+#else
+ if ( !reclaim_ptr ) reclaim_ptr = Hash;
+ while ( !need_resched )
+ {
+ reclaim_ptr++;
+ if ( reclaim_ptr == Hash_end ) reclaim_ptr = Hash;
+#endif
+ if (!reclaim_ptr->v)
continue;
+ valid = 0;
for_each_task(p)
{
if ( need_resched )
- {
- /*unlock_dcache();*/
- return;
- }
+ goto out;
/* if this vsid/context is in use */
- if ( (ptr->vsid >> 4) == p->mm->context )
+ if ( (reclaim_ptr->vsid >> 4) == p->mm->context )
{
valid = 1;
break;
@@ -121,19 +133,28 @@ void inline htab_reclaim(void)
if ( valid )
continue;
/* this pte isn't used */
- ptr->v = 0;
+ reclaim_ptr->v = 0;
}
- /*unlock_dcache();*/
+out:
+ if ( need_resched ) printk("need_resched: %x\n", need_resched);
+ unlock_dcache();
+#endif /* CONFIG_8xx */
}
-
+
/*
* Syscall entry into the idle task. -- Cort
*/
asmlinkage int sys_idle(void)
{
+ extern int media_bay_task(void *);
if(current->pid != 0)
return -EPERM;
+#ifdef CONFIG_PMAC
+ if (media_bay_present)
+ kernel_thread(media_bay_task, NULL, 0);
+#endif
+
idled(NULL);
return 0; /* should never execute this but it makes gcc happy -- Cort */
}
@@ -157,10 +178,8 @@ unsigned long zero_list = 0; /* head linked list of pre-zero'd pages */
unsigned long bytecount = 0; /* pointer into the currently being zero'd page */
unsigned long zerocount = 0; /* # currently pre-zero'd pages */
unsigned long zerototal = 0; /* # pages zero'd over time -- for ooh's and ahhh's */
-unsigned long pageptr = 0; /* current page being zero'd */
unsigned long zeropage_hits = 0;/* # zero'd pages request that we've done */
unsigned long zeropage_calls = 0;/* # zero'd pages request that've been made */
-static struct wait_queue * page_zerod_wait = NULL;
#define PAGE_THRESHOLD 96 /* how many pages to keep pre-zero'd */
/*
@@ -189,7 +208,6 @@ unsigned long get_prezerod_page(void)
*/
atomic_inc((atomic_t *)&zeropage_hits);
atomic_dec((atomic_t *)&zerocount);
- wake_up(&page_zerod_wait);
need_resched = 1;
/* zero out the pointer to next in the page */
@@ -201,35 +219,18 @@ unsigned long get_prezerod_page(void)
/*
* Experimental stuff to zero out pages in the idle task
- * to speed up get_free_pages() -- Cort
- * Zero's out pages until we need to resched or
- * we've reached the limit of zero'd pages.
+ * to speed up get_free_pages(). Zero's out pages until
+ * we've reached the limit of zero'd pages. We handle
+ * reschedule()'s in here so when we return we know we've
+ * zero'd all we need to for now.
*/
-int zero_paged(void *unused)
+void zero_paged(void)
{
- extern pte_t *get_pte( struct mm_struct *mm, unsigned long address );
- pgd_t *dir;
- pmd_t *pmd;
+ unsigned long pageptr = 0; /* current page being zero'd */
pte_t *pte;
-
- sprintf(current->comm, "zero_paged (idle)");
- /* current->blocked = ~0UL; */
-#ifdef __SMP__
- printk("Started zero_paged (cpu %d)\n", hard_smp_processor_id());
-#else
- printk("Started zero_paged\n");
-#endif /* __SMP__ */
-
- __sti();
- while ( 1 )
+ while ( zerocount <= PAGE_THRESHOLD )
{
- /* don't want to be pre-empted by swapper or power_save */
- current->priority = -98;
- current->counter = -98;
- /* we don't want to run until we have something to do */
- while ( zerocount >= PAGE_THRESHOLD )
- sleep_on(&page_zerod_wait);
/*
* Mark a page as reserved so we can mess with it
* If we're interrupted we keep this page and our place in it
@@ -237,7 +238,7 @@ int zero_paged(void *unused)
*/
pageptr = __get_free_pages(GFP_ATOMIC, 0);
if ( !pageptr )
- goto retry;
+ return;
if ( need_resched )
schedule();
@@ -245,20 +246,15 @@ int zero_paged(void *unused)
/*
* Make the page no cache so we don't blow our cache with 0's
*/
- dir = pgd_offset( init_task.mm, pageptr );
- if (dir)
+ pte = find_pte(init_task.mm, pageptr);
+ if ( !pte )
{
- pmd = pmd_offset(dir, pageptr & PAGE_MASK);
- if (pmd && pmd_present(*pmd))
- {
- pte = pte_offset(pmd, pageptr & PAGE_MASK);
- if (pte && pte_present(*pte))
- {
- pte_uncache(*pte);
- flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr);
- }
- }
+ printk("pte NULL in zero_paged()\n");
+ return;
}
+
+ pte_uncache(*pte);
+ flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr);
/*
* Important here to not take time away from real processes.
@@ -308,35 +304,34 @@ int zero_paged(void *unused)
*/
atomic_inc((atomic_t *)&zerocount);
atomic_inc((atomic_t *)&zerototal);
-retry:
- schedule();
}
}
-void inline power_save(void)
+int powersave_mode = HID0_DOZE;
+
+void power_save(void)
{
unsigned long msr, hid0;
- /* no powersaving modes on the 601 */
- if( (_get_PVR()>>16) == 1 )
+ /* only sleep on the 603-family/750 processors */
+ switch (_get_PVR() >> 16) {
+ case 3: /* 603 */
+ case 6: /* 603e */
+ case 7: /* 603ev */
+ case 8: /* 750 */
+ break;
+ default:
return;
+ }
- __sti();
- asm volatile(
- /* clear powersaving modes and set nap mode */
- "mfspr %3,1008 \n\t"
- "andc %3,%3,%4 \n\t"
- "or %3,%3,%5 \n\t"
- "mtspr 1008,%3 \n\t"
- /* enter the mode */
- "mfmsr %0 \n\t"
- "oris %0,%0,%2 \n\t"
- "sync \n\t"
- "mtmsr %0 \n\t"
- "isync \n\t"
- : "=&r" (msr)
- : "0" (msr), "i" (MSR_POW>>16),
- "r" (hid0),
- "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP),
- "r" (HID0_NAP));
+ save_flags(msr);
+ cli();
+ if (!need_resched) {
+ asm("mfspr %0,1008" : "=r" (hid0) :);
+ hid0 &= ~(HID0_NAP | HID0_SLEEP | HID0_DOZE);
+ hid0 |= powersave_mode | HID0_DPM;
+ asm("mtspr 1008,%0" : : "r" (hid0));
+ msr |= MSR_POW;
+ }
+ restore_flags(msr);
}
diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c
index 4616d59a2..462805a65 100644
--- a/arch/ppc/kernel/irq.c
+++ b/arch/ppc/kernel/irq.c
@@ -14,6 +14,14 @@
* instead of just grabbing them. Thus setups with different IRQ numbers
* shouldn't result in any weird surprises, and installing new handlers
* should be easier.
+ *
+ * The MPC8xx has an interrupt mask in the SIU. If a bit is set, the
+ * interrupt is _enabled_. As expected, IRQ0 is bit 0 in the 32-bit
+ * mask register (of which only 16 are defined), hence the weird shifting
+ * and compliment of the cached_irq_mask. I want to be able to stuff
+ * this right into the SIU SMASK register.
+ * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx
+ * to reduce code space and undefined function references.
*/
@@ -30,30 +38,41 @@
#include <linux/malloc.h>
#include <linux/openpic.h>
#include <linux/pci.h>
-#include <linux/bios32.h>
#include <linux/openpic.h>
#include <asm/hydra.h>
#include <asm/system.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/gg2.h>
+#include <asm/cache.h>
+#ifdef CONFIG_8xx
+#include <asm/8xx_immap.h>
+#include <asm/mbx.h>
+#endif
#undef SHOW_IRQ
unsigned lost_interrupts = 0;
unsigned int local_irq_count[NR_CPUS];
-static struct irqaction irq_action[NR_IRQS];
+static struct irqaction *irq_action[NR_IRQS];
static int spurious_interrupts = 0;
+#ifndef CONFIG_8xx
+static unsigned int cached_irq_mask = 0xffffffff;
+#else
static unsigned int cached_irq_mask = 0xffffffff;
+#endif
static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { }
-spinlock_t irq_controller_lock;
+/*spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED;*/
#ifdef __SMP__
atomic_t __ppc_bh_counter = ATOMIC_INIT(0);
#else
int __ppc_bh_counter = 0;
#endif
+static volatile unsigned char *gg2_int_ack_special;
+extern volatile unsigned long ipi_count;
#define cached_21 (((char *)(&cached_irq_mask))[3])
#define cached_A1 (((char *)(&cached_irq_mask))[2])
@@ -61,9 +80,19 @@ int __ppc_bh_counter = 0;
/*
* These are set to the appropriate functions by init_IRQ()
*/
+#ifndef CONFIG_8xx
void (*mask_and_ack_irq)(int irq_nr);
void (*mask_irq)(unsigned int irq_nr);
void (*unmask_irq)(unsigned int irq_nr);
+#else /* CONFIG_8xx */
+/* init_IRQ() happens too late for the MBX because we initialize the
+ * CPM early and it calls request_irq() before we have these function
+ * pointers initialized.
+ */
+#define mask_and_ack_irq(irq) mbx_mask_irq(irq)
+#define mask_irq(irq) mbx_mask_irq(irq)
+#define unmask_irq(irq) mbx_unmask_irq(irq)
+#endif /* CONFIG_8xx */
/* prep */
@@ -79,9 +108,46 @@ extern unsigned long route_pci_interrupts(void);
#define PMAC_IRQ_MASK (~ld_le32(IRQ_ENABLE))
+
+/* nasty hack for shared irq's since we need to do kmalloc calls but
+ * can't very very early in the boot when we need to do a request irq.
+ * this needs to be removed.
+ * -- Cort
+ */
+static char cache_bitmask = 0;
+static struct irqaction malloc_cache[4];
+extern int mem_init_done;
+
+void *irq_kmalloc(size_t size, int pri)
+{
+ unsigned int i;
+ if ( mem_init_done )
+ return kmalloc(size,pri);
+ for ( i = 0; i <= 3 ; i++ )
+ if ( ! ( cache_bitmask & (1<<i) ) )
+ {
+ cache_bitmask |= (1<<i);
+ return (void *)(&malloc_cache[i]);
+ }
+ return 0;
+}
+
+void irq_kfree(void *ptr)
+{
+ unsigned int i;
+ for ( i = 0 ; i <= 3 ; i++ )
+ if ( ptr == &malloc_cache[i] )
+ {
+ cache_bitmask &= ~(1<<i);
+ return;
+ }
+ kfree(ptr);
+}
+
+#ifndef CONFIG_8xx
void i8259_mask_and_ack_irq(int irq_nr)
{
- spin_lock(&irq_controller_lock);
+ /* spin_lock(&irq_controller_lock);*/
cached_irq_mask |= 1 << irq_nr;
if (irq_nr > 7) {
inb(0xA1); /* DUMMY */
@@ -96,20 +162,22 @@ void i8259_mask_and_ack_irq(int irq_nr)
outb(0x60|irq_nr,0x20); /* specific eoi */
}
- spin_unlock(&irq_controller_lock);
+ /* spin_unlock(&irq_controller_lock);*/
}
void pmac_mask_and_ack_irq(int irq_nr)
{
unsigned long bit = 1UL << irq_nr;
- spin_lock(&irq_controller_lock);
+ /* spin_lock(&irq_controller_lock);*/
cached_irq_mask |= bit;
lost_interrupts &= ~bit;
out_le32(IRQ_ACK, bit);
out_le32(IRQ_ENABLE, ~cached_irq_mask);
out_le32(IRQ_ACK, bit);
- spin_unlock(&irq_controller_lock);
+ /* spin_unlock(&irq_controller_lock);*/
+ /*if ( irq_controller_lock.lock )
+ panic("irq controller lock still held in mask and ack\n");*/
}
void chrp_mask_and_ack_irq(int irq_nr)
@@ -188,44 +256,96 @@ static void chrp_unmask_irq(unsigned int irq_nr)
else
openpic_enable_irq(irq_to_openpic(irq_nr));
}
+#else /* CONFIG_8xx */
+static void mbx_mask_irq(unsigned int irq_nr)
+{
+ cached_irq_mask &= ~(1 << (31-irq_nr));
+ ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask =
+ cached_irq_mask;
+}
+
+static void mbx_unmask_irq(unsigned int irq_nr)
+{
+ cached_irq_mask |= (1 << (31-irq_nr));
+ ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask =
+ cached_irq_mask;
+}
+#endif /* CONFIG_8xx */
void disable_irq(unsigned int irq_nr)
{
- unsigned long flags;
+ /*unsigned long flags;*/
- spin_lock_irqsave(&irq_controller_lock, flags);
+ /* spin_lock_irqsave(&irq_controller_lock, flags);*/
mask_irq(irq_nr);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
+ /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/
synchronize_irq();
}
void enable_irq(unsigned int irq_nr)
{
- unsigned long flags;
+ /*unsigned long flags;*/
- spin_lock_irqsave(&irq_controller_lock, flags);
+ /* spin_lock_irqsave(&irq_controller_lock, flags);*/
unmask_irq(irq_nr);
- spin_unlock_irqrestore(&irq_controller_lock, flags);
+ /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/
}
int get_irq_list(char *buf)
{
- int i, len = 0;
+ int i, len = 0, j;
struct irqaction * action;
+ len += sprintf(buf+len, " ");
+ for (j=0; j<smp_num_cpus; j++)
+ len += sprintf(buf+len, "CPU%d ",j);
+ *(char *)(buf+len++) = '\n';
+
for (i = 0 ; i < NR_IRQS ; i++) {
- action = irq_action + i;
+ action = irq_action[i];
if (!action || !action->handler)
continue;
- len += sprintf(buf+len, "%2d: %10u %s",
- i, kstat.interrupts[i], action->name);
+ len += sprintf(buf+len, "%3d: ", i);
+#ifdef __SMP__
+ for (j = 0; j < smp_num_cpus; j++)
+ len += sprintf(buf+len, "%10u ",
+ kstat.irqs[cpu_logical_map(j)][i]);
+#else
+ len += sprintf(buf+len, "%10u ", kstat_irqs(i));
+#endif /* __SMP__ */
+ switch( _machine )
+ {
+ case _MACH_prep:
+ len += sprintf(buf+len, " 82c59 ");
+ break;
+ case _MACH_Pmac:
+ len += sprintf(buf+len, " PMAC-PIC ");
+ break;
+ case _MACH_chrp:
+ if ( is_8259_irq(i) )
+ len += sprintf(buf+len, " 82c59 ");
+ else
+ len += sprintf(buf+len, " OpenPIC ");
+ break;
+ }
+
+ len += sprintf(buf+len, " %s",action->name);
for (action=action->next; action; action = action->next) {
len += sprintf(buf+len, ", %s", action->name);
}
len += sprintf(buf+len, "\n");
}
- len += sprintf(buf+len, "99: %10u spurious or short\n",
- spurious_interrupts);
+#ifdef __SMP__
+ /* should this be per processor send/receive? */
+ len += sprintf(buf+len, "IPI: %10lu\n", ipi_count);
+ for ( i = 0 ; i <= smp_num_cpus-1; i++ )
+ len += sprintf(buf+len," ");
+ len += sprintf(buf+len, " interprocessor messages received\n");
+#endif
+ len += sprintf(buf+len, "BAD: %10u",spurious_interrupts);
+ for ( i = 0 ; i <= smp_num_cpus-1; i++ )
+ len += sprintf(buf+len," ");
+ len += sprintf(buf+len, " spurious or short\n");
return len;
}
@@ -394,20 +514,31 @@ asmlinkage void do_IRQ(struct pt_regs *regs)
int status;
int openpic_eoi_done = 0;
+ /* save the HID0 in case dcache was off - see idle.c
+ * this hack should leave for a better solution -- Cort */
+ unsigned dcache_locked;
+
+ dcache_locked = unlock_dcache();
+ hardirq_enter(cpu);
+#ifndef CONFIG_8xx
#ifdef __SMP__
if ( cpu != 0 )
- panic("cpu %d received interrupt", cpu);
-#endif /* __SMP__ */
-
- hardirq_enter(cpu);
+ {
+ if ( !lost_interrupts )
+ {
+ extern smp_message_recv(void);
+ goto out;
+
+ ipi_count++;
+ smp_message_recv();
+ goto out;
+ }
+ /* could be here due to a do_fake_interrupt call but we don't
+ mess with the controller from the second cpu -- Cort */
+ goto out;
+ }
+#endif /* __SMP__ */
- /*
- * I'll put this ugly mess of code into a function
- * such as get_pending_irq() or some such clear thing
- * so we don't have a switch in the irq code and
- * the chrp code is merged a bit with the prep.
- * -- Cort
- */
switch ( _machine )
{
case _MACH_Pmac:
@@ -425,7 +556,7 @@ asmlinkage void do_IRQ(struct pt_regs *regs)
*
* This should go in the above mask/ack code soon. -- Cort
*/
- irq = *(volatile unsigned char *)GG2_INT_ACK_SPECIAL;
+ irq = *gg2_int_ack_special;
/*
* Acknowledge as soon as possible to allow i8259
* interrupt nesting
@@ -456,7 +587,6 @@ asmlinkage void do_IRQ(struct pt_regs *regs)
}
break;
case _MACH_prep:
-#if 1
outb(0x0C, 0x20);
irq = inb(0x20) & 7;
if (irq == 2)
@@ -470,23 +600,6 @@ retry_cascade:
irq = (irq&7) + 8;
}
bits = 1UL << irq;
-#else
- /*
- * get the isr from the intr controller since
- * the bit in the irr has been cleared
- */
- outb(0x0a, 0x20);
- bits = inb(0x20)&0xff;
- /* handle cascade */
- if ( bits & 4 )
- {
- bits &= ~4UL;
- outb(0x0a, 0xA0);
- bits |= inb(0xA0)<<8;
- }
- /* ignore masked irqs */
- bits &= ~cached_irq_mask;
-#endif
break;
}
@@ -494,43 +607,62 @@ retry_cascade:
printk("Bogus interrupt from PC = %lx\n", regs->nip);
goto out;
}
+
+#else /* CONFIG_8xx */
+ /* For MPC8xx, read the SIVEC register and shift the bits down
+ * to get the irq number.
+ */
+ bits = ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_sivec;
+ irq = bits >> 26;
+#endif /* CONFIG_8xx */
mask_and_ack_irq(irq);
status = 0;
- action = irq_action + irq;
- kstat.interrupts[irq]++;
- if (action->handler) {
+ action = irq_action[irq];
+ kstat.irqs[cpu][irq]++;
+ if ( action && action->handler) {
if (!(action->flags & SA_INTERRUPT))
__sti();
- status |= action->flags;
- action->handler(irq, action->dev_id, regs);
- /*if (status & SA_SAMPLE_RANDOM)
- add_interrupt_randomness(irq);*/
- __cli(); /* in case the handler turned them on */
- spin_lock(&irq_controller_lock);
+ do {
+ status |= action->flags;
+ action->handler(irq, action->dev_id, regs);
+ /*if (status & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);*/
+ action = action->next;
+ } while ( action );
+ __cli();
+ /* spin_lock(&irq_controller_lock);*/
unmask_irq(irq);
- spin_unlock(&irq_controller_lock);
+ /* spin_unlock(&irq_controller_lock);*/
} else {
+#ifndef CONFIG_8xx
if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */
+#endif
spurious_interrupts++;
disable_irq( irq );
}
/* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */
+#ifndef CONFIG_8xx
if ( is_prep && (irq > 7) )
goto retry_cascade;
/* do_bottom_half is called if necessary from int_return in head.S */
out:
if (_machine == _MACH_chrp && !openpic_eoi_done)
openpic_eoi(0);
+#endif /* CONFIG_8xx */
hardirq_exit(cpu);
+
+ /* restore the HID0 in case dcache was off - see idle.c
+ * this hack should leave for a better solution -- Cort */
+ lock_dcache(dcache_locked);
}
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags, const char * devname, void *dev_id)
{
- struct irqaction * action;
+ struct irqaction *old, **p, *action;
unsigned long flags;
#ifdef SHOW_IRQ
@@ -540,49 +672,58 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *)
if (irq >= NR_IRQS)
return -EINVAL;
- action = irq + irq_action;
- if (action->handler)
- return -EBUSY;
+
if (!handler)
- return -EINVAL;
+ {
+ /* Free */
+ for (p = irq + irq_action; (action = *p) != NULL; p = &action->next)
+ {
+ /* Found it - now free it */
+ save_flags(flags);
+ cli();
+ *p = action->next;
+ restore_flags(flags);
+ irq_kfree(action);
+ return 0;
+ }
+ return -ENOENT;
+ }
+
+ action = (struct irqaction *)
+ irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if (!action)
+ return -ENOMEM;
save_flags(flags);
cli();
+
action->handler = handler;
action->flags = irqflags;
action->mask = 0;
action->name = devname;
action->dev_id = dev_id;
+ action->next = NULL;
enable_irq(irq);
- restore_flags(flags);
+ p = irq_action + irq;
+
+ if ((old = *p) != NULL) {
+ /* Can't share interrupts unless both agree to */
+ if (!(old->flags & action->flags & SA_SHIRQ))
+ return -EBUSY;
+ /* add new interrupt at end of irq queue */
+ do {
+ p = &old->next;
+ old = *p;
+ } while (old);
+ }
+ *p = action;
+
+ restore_flags(flags);
return 0;
}
void free_irq(unsigned int irq, void *dev_id)
{
- struct irqaction * action = irq + irq_action;
- unsigned long flags;
-
-#ifdef SHOW_IRQ
- printk("free_irq(): irq %d dev_id %04x\n", irq, dev_id);
-#endif /* SHOW_IRQ */
-
- if (irq >= NR_IRQS) {
- printk("Trying to free IRQ%d\n",irq);
- return;
- }
- if (!action->handler) {
- printk("Trying to free free IRQ%d\n",irq);
- return;
- }
- disable_irq(irq);
- save_flags(flags);
- cli();
- action->handler = NULL;
- action->flags = 0;
- action->mask = 0;
- action->name = NULL;
- action->dev_id = NULL;
- restore_flags(flags);
+ request_irq(irq, NULL, 0, NULL, dev_id);
}
unsigned long probe_irq_on (void)
@@ -595,6 +736,7 @@ int probe_irq_off (unsigned long irqs)
return 0;
}
+#ifndef CONFIG_8xx
__initfunc(static void i8259_init(void))
{
/* init master interrupt controller */
@@ -616,11 +758,17 @@ __initfunc(static void i8259_init(void))
panic("Could not allocate cascade IRQ!");
enable_irq(2); /* Enable cascade interrupt */
}
+#endif /* CONFIG_8xx */
+/* On MBX8xx, the interrupt control (SIEL) was set by EPPC-bug. External
+ * interrupts can be either edge or level triggered, but there is no
+ * reason for us to change the EPPC-bug values (it would not work if we did).
+ */
__initfunc(void init_IRQ(void))
{
extern void xmon_irq(int, void *, struct pt_regs *);
+#ifndef CONFIG_8xx
switch (_machine)
{
case _MACH_Pmac:
@@ -637,7 +785,8 @@ __initfunc(void init_IRQ(void))
mask_and_ack_irq = chrp_mask_and_ack_irq;
mask_irq = chrp_mask_irq;
unmask_irq = chrp_unmask_irq;
- ioremap(GG2_INT_ACK_SPECIAL, 1);
+ gg2_int_ack_special = (volatile unsigned char *)
+ ioremap(GG2_INT_ACK_SPECIAL, 1);
openpic_init();
i8259_init();
#ifdef CONFIG_XMON
@@ -653,7 +802,7 @@ __initfunc(void init_IRQ(void))
i8259_init();
route_pci_interrupts();
/*
- * According to the Carolina spec from ibm irq's 0,1,2, and 8
+ * According to the Carolina spec from ibm irqs 0,1,2, and 8
* must be edge triggered. Also, the pci intrs must be level
* triggered and _only_ isa intrs can be level sensitive
* which are 3-7,9-12,14-15. 13 is special - it can be level.
@@ -686,5 +835,6 @@ __initfunc(void init_IRQ(void))
}
break;
- }
+ }
+#endif /* CONFIG_8xx */
}
diff --git a/arch/ppc/kernel/mbx_pci.c b/arch/ppc/kernel/mbx_pci.c
new file mode 100644
index 000000000..30b7b1184
--- /dev/null
+++ b/arch/ppc/kernel/mbx_pci.c
@@ -0,0 +1,254 @@
+/*
+ * MBX pci routines.
+ * The MBX uses the QSpan PCI bridge. The config address register
+ * is located 0x500 from the base of the bridge control/status registers.
+ * The data register is located at 0x504.
+ * This is a two step operation. First, the address register is written,
+ * then the data register is read/written as required.
+ * I don't know what to do about interrupts (yet).
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/mbx.h>
+
+
+/*
+ * This blows......The MBX uses the Tundra QSpan PCI bridge. When
+ * reading the configuration space, if something does not respond
+ * the bus times out and we get a machine check interrupt. So, the
+ * good ol' exception tables come to mind to trap it and return some
+ * value.
+ *
+ * On an error we just return a -1, since that is what the caller wants
+ * returned if nothing is present. I copied this from __get_user_asm,
+ * with the only difference of returning -1 instead of EFAULT.
+ * There is an associated hack in the machine check trap code.
+ *
+ * The QSPAN is also a big endian device, that is it makes the PCI
+ * look big endian to us. This presents a problem for the Linux PCI
+ * functions, which assume little endian. For example, we see the
+ * first 32-bit word like this:
+ * ------------------------
+ * | Device ID | Vendor ID |
+ * ------------------------
+ * If we read/write as a double word, that's OK. But in our world,
+ * when read as a word, device ID is at location 0, not location 2 as
+ * the little endian PCI would believe. We have to switch bits in
+ * the PCI addresses given to us to get the data to/from the correct
+ * byte lanes.
+ *
+ * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5.
+ * It always forces the MS bit to zero. Therefore, dev_fn values
+ * greater than 128 are returned as "no device found" errors.
+ *
+ * The QSPAN can only perform long word (32-bit) configuration cycles.
+ * The "offset" must have the two LS bits set to zero. Read operations
+ * require we read the entire word and then sort out what should be
+ * returned. Write operations other than long word require that we
+ * read the long word, update the proper word or byte, then write the
+ * entire long word back.
+ *
+ * PCI Bridge hack. We assume (correctly) that bus 0 is the primary
+ * PCI bus from the QSPAN. If we are called with a bus number other
+ * than zero, we create a Type 1 configuration access that a downstream
+ * PCI bridge will interpret.
+ */
+
+#define __get_mbx_pci_config(x, addr, op) \
+ __asm__ __volatile__( \
+ "1: "op" %0,0(%1)\n" \
+ " eieio\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: li %0,-1\n" \
+ " b 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 2\n" \
+ " .long 1b,3b\n" \
+ ".text" \
+ : "=r"(x) : "r"(addr))
+
+#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500))
+#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504))
+
+#define mk_config_addr(bus, dev, offset) \
+ (((bus)<<16) | ((dev)<<8) | (offset & 0xfc))
+
+#define mk_config_type1(bus, dev, offset) \
+ mk_config_addr(bus, dev, offset) | 1;
+
+int mbx_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned char *val)
+{
+ uint temp;
+ u_char *cp;
+
+ if ((bus > 7) || (dev_fn > 127)) {
+ *val = 0xff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ __get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz");
+
+ offset ^= 0x03;
+ cp = ((u_char *)&temp) + (offset & 0x03);
+ *val = *cp;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned short *val)
+{
+ uint temp;
+ ushort *sp;
+
+ if ((bus > 7) || (dev_fn > 127)) {
+ *val = 0xffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ __get_mbx_pci_config(temp, QS_CONFIG_DATA, "lwz");
+ offset ^= 0x02;
+
+ sp = ((ushort *)&temp) + ((offset >> 1) & 1);
+ *val = *sp;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned int *val)
+{
+ if ((bus > 7) || (dev_fn > 127)) {
+ *val = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ __get_mbx_pci_config(*val, QS_CONFIG_DATA, "lwz");
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned char val)
+{
+ uint temp;
+ u_char *cp;
+
+ if ((bus > 7) || (dev_fn > 127))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
+
+ offset ^= 0x03;
+ cp = ((u_char *)&temp) + (offset & 0x03);
+ *cp = val;
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ *QS_CONFIG_DATA = temp;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned short val)
+{
+ uint temp;
+ ushort *sp;
+
+ if ((bus > 7) || (dev_fn > 127))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ mbx_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
+
+ offset ^= 0x02;
+ sp = ((ushort *)&temp) + ((offset >> 1) & 1);
+ *sp = val;
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ *QS_CONFIG_DATA = temp;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned int val)
+{
+ if ((bus > 7) || (dev_fn > 127))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ *(unsigned int *)QS_CONFIG_DATA = val;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int mbx_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
+ unsigned short index, unsigned char *bus_ptr,
+ unsigned char *dev_fn_ptr)
+{
+ int num, devfn;
+ unsigned int x, vendev;
+
+ if (vendor == 0xffff)
+ return PCIBIOS_BAD_VENDOR_ID;
+ vendev = (dev_id << 16) + vendor;
+ num = 0;
+ for (devfn = 0; devfn < 32; devfn++) {
+ mbx_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x);
+ if (x == vendev) {
+ if (index == num) {
+ *bus_ptr = 0;
+ *dev_fn_ptr = devfn<<3;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++num;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+int mbx_pcibios_find_class(unsigned int class_code, unsigned short index,
+ unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
+{
+ int devnr, x, num;
+
+ num = 0;
+ for (devnr = 0; devnr < 32; devnr++) {
+ mbx_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x);
+ if ((x>>8) == class_code) {
+ if (index == num) {
+ *bus_ptr = 0;
+ *dev_fn_ptr = devnr<<3;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++num;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c
new file mode 100644
index 000000000..c028bb12d
--- /dev/null
+++ b/arch/ppc/kernel/mbx_setup.c
@@ -0,0 +1,169 @@
+/*
+ * linux/arch/ppc/kernel/setup.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Adapted from 'alpha' version by Gary Thomas
+ * Modified by Cort Dougan (cort@cs.nmt.edu)
+ * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net)
+ */
+
+/*
+ * bootup setup stuff..
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/major.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/blk.h>
+#include <linux/ioport.h>
+
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <asm/residual.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/ide.h>
+#include <asm/mbx.h>
+
+extern unsigned long loops_per_sec;
+
+unsigned long empty_zero_page[1024];
+
+#ifdef CONFIG_BLK_DEV_RAM
+extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
+extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
+extern int rd_image_start; /* starting block # of image */
+#endif
+
+extern char saved_command_line[256];
+
+extern unsigned long find_available_memory(void);
+extern void mbx_cpm_reset(uint);
+
+
+void mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
+{
+
+ *p = 0;
+ *irq = 0;
+
+ if (base != 0) /* Only map the first ATA flash drive */
+ return;
+#ifdef ATA_FLASH
+ base = (unsigned long) ioremap(PCMCIA_MEM_ADDR, 0x200);
+ for (i = 0; i < 8; ++i)
+ *p++ = base++;
+ *p = ++base; /* Does not matter */
+ if (irq)
+ *irq = 13;
+#endif
+}
+
+int
+mbx_get_cpuinfo(char *buffer)
+{
+ int pvr = _get_PVR();
+ int len;
+ char *model;
+ bd_t *bp;
+ extern RESIDUAL res;
+
+ /* I know the MPC860 is 0x50. I don't have the book handy
+ * to check the others.
+ */
+ if ((pvr>>16) == 0x50)
+ model = "MPC860";
+ else
+ model = "unknown";
+
+#ifdef __SMP__
+#define CD(X) (cpu_data[n].X)
+#else
+#define CD(X) (X)
+#define CPUN 0
+#endif
+ bp = (bd_t *)&res;
+
+ len = sprintf(buffer,"processor\t: %d\n"
+ "cpu\t\t: %s\n"
+ "revision\t: %d.%d\n"
+ "clock\t\t: %d MHz\n"
+ "bus clock\t: %d MHz\n",
+ CPUN,
+ model,
+ MAJOR(pvr), MINOR(pvr),
+ bp->bi_intfreq / 1000000,
+ bp->bi_busfreq / 1000000
+ );
+
+ return len;
+}
+
+__initfunc(void
+mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
+{
+ int cpm_page;
+
+ cpm_page = *memory_start_p;
+ *memory_start_p += PAGE_SIZE;
+
+ /* Reset the Communication Processor Module.
+ */
+ mbx_cpm_reset(cpm_page);
+
+#ifdef notdef
+ ROOT_DEV = to_kdev_t(0x0301); /* hda1 */
+#endif
+
+#ifdef CONFIG_BLK_DEV_RAM
+#if 0
+ ROOT_DEV = to_kdev_t(0x0200); /* floppy */
+ rd_prompt = 1;
+ rd_doload = 1;
+ rd_image_start = 0;
+#endif
+ /* initrd_start and size are setup by boot/head.S and kernel/head.S */
+ if ( initrd_start )
+ {
+ if (initrd_end > *memory_end_p)
+ {
+ printk("initrd extends beyond end of memory "
+ "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+ initrd_end,*memory_end_p);
+ initrd_start = 0;
+ }
+ }
+#endif
+
+#ifdef notdef
+ request_region(0x20,0x20,"pic1");
+ request_region(0xa0,0x20,"pic2");
+ request_region(0x00,0x20,"dma1");
+ request_region(0x40,0x20,"timer");
+ request_region(0x80,0x10,"dma page reg");
+ request_region(0xc0,0x20,"dma2");
+#endif
+}
+
+void
+abort(void)
+{
+#ifdef CONFIG_XMON
+ extern void xmon(void *);
+ xmon(0);
+#endif
+ machine_restart(NULL);
+}
diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S
index 2c866eed8..6607e00bd 100644
--- a/arch/ppc/kernel/misc.S
+++ b/arch/ppc/kernel/misc.S
@@ -12,6 +12,7 @@
*
*/
+#include <linux/config.h>
#include <linux/sys.h>
#include <asm/unistd.h>
#include <asm/errno.h>
@@ -19,6 +20,7 @@
#include "ppc_asm.tmpl"
#include "ppc_defs.h"
+#ifndef CONFIG_8xx
/* This instruction is not implemented on the PPC 601 or 603 */
#define tlbia \
li r4,128; \
@@ -27,7 +29,7 @@
0: tlbie r4; \
addi r4,r4,0x1000; \
bdnz 0b
-
+#endif
.text
/*
@@ -323,6 +325,18 @@ _GLOBAL(_get_SP)
mr r3,r1 /* Close enough */
blr
+_GLOBAL(_get_THRM1)
+ mfspr r3,THRM1
+ blr
+
+_GLOBAL(_set_THRM1)
+ mtspr THRM1,r3
+ blr
+
+_GLOBAL(_get_L2CR)
+ mfspr r3,L2CR
+ blr
+
_GLOBAL(_get_PVR)
mfspr r3,PVR
blr
@@ -348,33 +362,6 @@ cvt_df:
stfs 0,0(r4)
blr
-
-_GLOBAL(lock_dcache)
- mfspr r3,PVR /* nop on 601 */
- rlwinm r3,r3,16,16,31
- cmpwi 0,r3,1
- beqlr-
- mfspr r3,HID0
- ori r3,r3,HID0_DLOCK
- mtspr HID0,r3
- sync
- isync
- blr
-
-_GLOBAL(unlock_dcache)
- mfspr r3,PVR /* nop on 601 */
- rlwinm r3,r3,16,16,31
- cmpwi 0,r3,1
- beqlr-
- mfspr r3,HID0
- li r4,HID0_DLOCK
- andc r3,r3,r4
- mtspr HID0,r3
- sync
- isync
- blr
-
-
/*
* Create a kernel thread
* __kernel_thread(flags, fn, arg)
@@ -386,6 +373,16 @@ _GLOBAL(__kernel_thread)
bnelr /* return if parent */
mtlr r4 /* fn addr in lr */
mr r3,r5 /* load arg and call fn */
+#if 0/*def __SMP__*/
+ /* drop scheduler_lock since schedule() called us */
+ lis r4,scheduler_lock@ha
+ li r5,0
+ stw r5,scheduler_lock@l+4(r4) /* owner_pc */
+ stw r5,scheduler_lock@l+8(r4) /* owner_cpu */
+ stw r5,scheduler_lock@l(r4)
+ sync
+ isync
+#endif /* __SMP__ */
blrl
li r0,__NR_exit /* exit after child exits */
li r3,0
@@ -413,7 +410,9 @@ SYSCALL(execve)
SYSCALL(open)
SYSCALL(close)
SYSCALL(waitpid)
+SYSCALL(fork)
SYSCALL(delete_module)
+SYSCALL(_exit)
/* Why isn't this a) automatic, b) written in 'C'? */
@@ -593,5 +592,7 @@ sys_call_table:
.long sys_setresgid
.long sys_getresgid /* 170 */
.long sys_prctl
- .space (NR_syscalls-171)*4
+ .long sys_xstat
+ .long sys_xmknod
+ .space (NR_syscalls-173)*4
diff --git a/arch/ppc/kernel/mk_defs.c b/arch/ppc/kernel/mk_defs.c
index 8db1763db..9de056504 100644
--- a/arch/ppc/kernel/mk_defs.c
+++ b/arch/ppc/kernel/mk_defs.c
@@ -19,6 +19,7 @@
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
+#include <asm/io.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
@@ -29,9 +30,11 @@
void
main(void)
{
+ DEFINE(KERNELBASE, KERNELBASE);
DEFINE(STATE, offsetof(struct task_struct, state));
DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task));
DEFINE(COUNTER, offsetof(struct task_struct, counter));
+ DEFINE(PROCESSOR, offsetof(struct task_struct, processor));
DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending));
DEFINE(TSS, offsetof(struct task_struct, tss));
DEFINE(MM, offsetof(struct task_struct, mm));
@@ -45,6 +48,7 @@ main(void)
DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags));
DEFINE(TSS_FPR0, offsetof(struct thread_struct, fpr[0]));
DEFINE(TSS_FPSCR, offsetof(struct thread_struct, fpscr));
+ DEFINE(TSS_SMP_FORK_RET, offsetof(struct thread_struct, smp_fork_ret));
/* Interrupt register frame */
DEFINE(TASK_UNION_SIZE, sizeof(union task_union));
DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD);
diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c
index 7e39487d8..186165a46 100644
--- a/arch/ppc/kernel/pci.c
+++ b/arch/ppc/kernel/pci.c
@@ -1,24 +1,27 @@
/*
- * $Id: pci.c,v 1.18 1997/10/29 03:35:07 cort Exp $
+ * $Id: pci.c,v 1.24 1998/02/19 21:29:49 cort Exp $
* Common pmac/prep/chrp pci routines. -- Cort
*/
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/bios32.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/pci.h>
+#include <linux/openpic.h>
#include <asm/processor.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
+#include <asm/irq.h>
-#if !defined(CONFIG_MACH_SPECIFIC)
+#if !defined(CONFIG_MACH_SPECIFIC) || defined(CONFIG_PMAC)
unsigned long isa_io_base;
+#endif /* CONFIG_MACH_SPECIFIC || CONFIG_PMAC */
+#if !defined(CONFIG_MACH_SPECIFIC)
unsigned long isa_mem_base;
unsigned long pci_dram_offset;
#endif /* CONFIG_MACH_SPECIFIC */
@@ -121,49 +124,6 @@ int pcibios_present(void)
return 1;
}
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
- unsigned short index, unsigned char *bus,
- unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->vendor == vendor && dev->device == device_id) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-/*
- * Given the class, find the n'th instance of that device
- * in the system.
- */
-int pcibios_find_class (unsigned int class_code, unsigned short index,
- unsigned char *bus, unsigned char *devfn)
-{
- unsigned int curr = 0;
- struct pci_dev *dev;
-
- for (dev = pci_devices; dev; dev = dev->next) {
- if (dev->class == class_code) {
- if (curr == index) {
- *devfn = dev->devfn;
- *bus = dev->bus->number;
- return PCIBIOS_SUCCESSFUL;
- }
- ++curr;
- }
- }
- return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-
__initfunc(unsigned long
pcibios_init(unsigned long mem_start,unsigned long mem_end))
{
@@ -203,6 +163,70 @@ __initfunc(void
__initfunc(unsigned long
pcibios_fixup(unsigned long mem_start, unsigned long mem_end))
+
{
+ extern route_pci_interrupts(void);
+ struct pci_dev *dev;
+ extern struct bridge_data **bridges;
+ extern unsigned char *Motherboard_map;
+ extern unsigned char *Motherboard_routes;
+
+ /*
+ * FIXME: This is broken: We should not assign IRQ's to IRQless
+ * devices (look at PCI_INTERRUPT_PIN) and we also should
+ * honor the existence of multi-function devices where
+ * different functions have different interrupt pins. [mj]
+ */
+ switch (_machine )
+ {
+ case _MACH_prep:
+ route_pci_interrupts();
+ for(dev=pci_devices; dev; dev=dev->next)
+ {
+ unsigned char d = PCI_SLOT(dev->devfn);
+ dev->irq = Motherboard_routes[Motherboard_map[d]];
+ }
+ break;
+ case _MACH_chrp:
+ /* PCI interrupts are controlled by the OpenPIC */
+ for(dev=pci_devices; dev; dev=dev->next)
+ if (dev->irq)
+ dev->irq = openpic_to_irq(dev->irq);
+ break;
+ case _MACH_Pmac:
+ for(dev=pci_devices; dev; dev=dev->next)
+ {
+ /*
+ * Open Firmware often doesn't initialize the,
+ * PCI_INTERRUPT_LINE config register properly, so we
+ * should find the device node and se if it has an
+ * AAPL,interrupts property.
+ */
+ struct bridge_data *bp = bridges[dev->bus->number];
+ struct device_node *node;
+ unsigned int *reg;
+ unsigned char pin;
+
+ if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) ||
+ !pin)
+ continue; /* No interrupt generated -> no fixup */
+ for (node = bp->node->child; node != 0;
+ node = node->sibling) {
+ reg = (unsigned int *) get_property(node, "reg", 0);
+ if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev->devfn)
+ continue;
+ /* this is the node, see if it has interrupts */
+ if (node->n_intrs > 0)
+ dev->irq = node->intrs[0].line;
+ break;
+ }
+ }
+ break;
+ }
return mem_start;
}
+
+__initfunc(char *pcibios_setup(char *str))
+{
+ return str;
+}
diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c
index d0144a567..a71aede12 100644
--- a/arch/ppc/kernel/pmac_pci.c
+++ b/arch/ppc/kernel/pmac_pci.c
@@ -12,27 +12,18 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/bios32.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/init.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
-struct bridge_data {
- volatile unsigned int *cfg_addr;
- volatile unsigned char *cfg_data;
- void *io_base;
- int bus_number;
- int max_bus;
- struct bridge_data *next;
- struct device_node *node;
-};
-
-static struct bridge_data **bridges, *bridge_list;
+struct bridge_data **bridges, *bridge_list;
static int max_bus;
static void add_bridges(struct device_node *dev, unsigned long *mem_ptr);
@@ -112,9 +103,11 @@ static void add_bridges(struct device_node *dev, unsigned long *mem_ptr)
int *bus_range;
int len;
struct bridge_data *bp;
+ struct reg_property *addr;
for (; dev != NULL; dev = dev->next) {
- if (dev->n_addrs < 1) {
+ addr = (struct reg_property *) get_property(dev, "reg", &len);
+ if (addr == NULL || len < sizeof(*addr)) {
printk(KERN_WARNING "Can't use %s: no address\n",
dev->full_name);
continue;
@@ -130,15 +123,18 @@ static void add_bridges(struct device_node *dev, unsigned long *mem_ptr)
else
printk(KERN_INFO "PCI buses %d..%d", bus_range[0],
bus_range[1]);
- printk(" controlled by %s at %x\n",
- dev->name, dev->addrs[0].address);
+ printk(" controlled by %s at %x\n", dev->name, addr->address);
bp = (struct bridge_data *) *mem_ptr;
*mem_ptr += sizeof(struct bridge_data);
bp->cfg_addr = (volatile unsigned int *)
- ioremap(dev->addrs[0].address + 0x800000, 0x1000);
+ ioremap(addr->address + 0x800000, 0x1000);
bp->cfg_data = (volatile unsigned char *)
- ioremap(dev->addrs[0].address + 0xc00000, 0x1000);
- bp->io_base = (void *) ioremap(dev->addrs[0].address, 0x10000);
+ ioremap(addr->address + 0xc00000, 0x1000);
+ bp->io_base = (void *) ioremap(addr->address, 0x10000);
+#ifdef CONFIG_PMAC
+ if (isa_io_base == 0)
+ isa_io_base = (unsigned long) bp->io_base;
+#endif
bp->bus_number = bus_range[0];
bp->max_bus = bus_range[1];
bp->next = bridge_list;
@@ -180,7 +176,7 @@ int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr,
}
int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned char *val)
+ unsigned char offset, unsigned char *val)
{
struct bridge_data *bp;
@@ -198,32 +194,11 @@ int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
}
udelay(2);
*val = in_8(bp->cfg_data + (offset & 3));
-
- if (offset == PCI_INTERRUPT_LINE) {
- /*
- * Open Firmware often doesn't initialize this
- * register properly, so we find the node and see
- * if it has an AAPL,interrupts property.
- */
- struct device_node *node;
- unsigned int *reg;
-
- for (node = bp->node->child; node != 0; node = node->sibling) {
- reg = (unsigned int *) get_property(node, "reg", 0);
- if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev_fn)
- continue;
- /* this is the node, see if it has interrupts */
- if (node->n_intrs > 0)
- *val = node->intrs[0];
- break;
- }
- }
-
return PCIBIOS_SUCCESSFUL;
}
int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned short *val)
+ unsigned char offset, unsigned short *val)
{
struct bridge_data *bp;
@@ -245,7 +220,7 @@ int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
}
int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned int *val)
+ unsigned char offset, unsigned int *val)
{
struct bridge_data *bp;
@@ -267,7 +242,7 @@ int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
}
int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned char val)
+ unsigned char offset, unsigned char val)
{
struct bridge_data *bp;
@@ -288,7 +263,7 @@ int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
}
int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned short val)
+ unsigned char offset, unsigned short val)
{
struct bridge_data *bp;
@@ -309,7 +284,7 @@ int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
}
int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, unsigned int val)
+ unsigned char offset, unsigned int val)
{
struct bridge_data *bp;
diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c
index 4e37becd5..afcd4fd8b 100644
--- a/arch/ppc/kernel/pmac_setup.c
+++ b/arch/ppc/kernel/pmac_setup.c
@@ -47,23 +47,18 @@
#include <asm/ide.h>
#include <asm/pci-bridge.h>
#include <asm/adb.h>
+#include <asm/mediabay.h>
+#include <asm/ohare.h>
+#include <asm/mediabay.h>
#include "time.h"
-/*
- * A magic address and value to put into it on machines with the
- * "ohare" I/O controller. This makes the IDE CD work on Starmaxes.
- * Contributed by Harry Eaton.
- */
-#define OMAGICPLACE ((volatile unsigned *) 0xf3000038)
-#define OMAGICCONT 0xbeff7a
-
extern int root_mountflags;
unsigned char drive_info;
#define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */
-static void gc_init(const char *, int);
+static void ohare_init(void);
void
pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)
@@ -91,27 +86,44 @@ pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)
loops_per_sec = 50000000;
}
+ /* this area has the CPU identification register
+ and some registers used by smp boards */
+ ioremap(0xf8000000, 0x1000);
+
*memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
- gc_init("gc", 0);
- gc_init("ohare", 1);
-#ifdef CONFIG_ABSTRACT_CONSOLE
+ ohare_init();
+
+#ifdef CONFIG_FB
/* Frame buffer device based console */
conswitchp = &fb_con;
#endif
}
-static void gc_init(const char *name, int isohare)
+static volatile u32 *feature_addr;
+
+static void ohare_init(void)
{
struct device_node *np;
- for (np = find_devices(name); np != NULL; np = np->next) {
- if (np->n_addrs > 0)
- ioremap(np->addrs[0].address, np->addrs[0].size);
- if (isohare) {
- printk(KERN_INFO "Twiddling the magic ohare bits\n");
- out_le32(OMAGICPLACE, OMAGICCONT);
- }
+ np = find_devices("ohare");
+ if (np == 0)
+ return;
+ if (np->next != 0)
+ printk(KERN_WARNING "only using the first ohare\n");
+ if (np->n_addrs == 0) {
+ printk(KERN_ERR "No addresses for %s\n", np->full_name);
+ return;
+ }
+ feature_addr = (volatile u32 *)
+ ioremap(np->addrs[0].address + OHARE_FEATURE_REG, 4);
+
+ if (find_devices("via-pmu") == 0) {
+ printk(KERN_INFO "Twiddling the magic ohare bits\n");
+ out_le32(feature_addr, STARMAX_FEATURES);
+ } else {
+ out_le32(feature_addr, in_le32(feature_addr) | PBOOK_FEATURES);
+ printk(KERN_DEBUG "feature reg = %x\n", in_le32(feature_addr));
}
}
@@ -125,10 +137,15 @@ kdev_t boot_dev;
unsigned long
powermac_init(unsigned long mem_start, unsigned long mem_end)
{
- pmac_nvram_init();
+#ifdef CONFIG_KGDB
+ extern void zs_kgdb_hook(int tty_num);
+ zs_kgdb_hook(0);
+#endif
adb_init();
+ pmac_nvram_init();
if (_machine == _MACH_Pmac) {
pmac_read_rtc_time();
+ media_bay_init();
}
#ifdef CONFIG_PMAC_CONSOLE
pmac_find_display();
@@ -175,7 +192,7 @@ note_scsi_host(struct device_node *node, void *host)
#include "../../../drivers/scsi/sd.h"
#include "../../../drivers/scsi/hosts.h"
-int sd_find_target(void *host, int tgt)
+kdev_t sd_find_target(void *host, int tgt)
{
Scsi_Disk *dp;
int i;
@@ -190,7 +207,7 @@ int sd_find_target(void *host, int tgt)
void find_boot_device(void)
{
- int dev;
+ kdev_t dev;
if (kdev_t_to_nr(ROOT_DEV) != 0)
return;
@@ -201,7 +218,7 @@ void find_boot_device(void)
dev = sd_find_target(boot_host, boot_target);
if (dev == 0)
return;
- boot_dev = to_kdev_t(dev + boot_part);
+ boot_dev = MKDEV(MAJOR(dev), MINOR(dev) + boot_part);
#endif
/* XXX should cope with booting from IDE also */
}
@@ -221,39 +238,92 @@ void note_bootable_part(kdev_t dev, int part)
}
}
+#ifdef CONFIG_BLK_DEV_IDE
+int pmac_ide_ports_known;
+ide_ioreg_t pmac_ide_regbase[MAX_HWIFS];
+int pmac_ide_irq[MAX_HWIFS];
+
void pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
{
- struct device_node *np;
int i;
- static struct device_node *atas;
- static int atas_valid;
*p = 0;
- *irq = 0;
- if (!atas_valid) {
- atas = find_devices("ATA");
- atas_valid = 1;
- }
- for (i = (int)base, np = atas; i > 0 && np != NULL; --i, np = np->next)
- ;
- if (np == NULL)
+ if (base == 0)
return;
- if (np->n_addrs == 0) {
- printk("ide: no addresses for device %s\n", np->full_name);
+ if (base == mb_cd_base && !check_media_bay(MB_CD)) {
+ mb_cd_index = -1;
return;
}
- if (np->n_intrs == 0) {
- printk("ide: no intrs for device %s, using 13\n",
- np->full_name);
- *irq = 13;
- } else {
- *irq = np->intrs[0];
- }
- base = (unsigned long) ioremap(np->addrs[0].address, 0x200);
for (i = 0; i < 8; ++i)
*p++ = base + i * 0x10;
*p = base + 0x160;
+ if (irq != NULL) {
+ *irq = 0;
+ for (i = 0; i < MAX_HWIFS; ++i) {
+ if (base == pmac_ide_regbase[i]) {
+ *irq = pmac_ide_irq[i];
+ break;
+ }
+ }
+ }
+}
+
+void pmac_ide_probe(void)
+{
+ struct device_node *np;
+ int i;
+ struct device_node *atas;
+ struct device_node *p, **pp, *removables, **rp;
+
+ pp = &atas;
+ rp = &removables;
+ p = find_devices("ATA");
+ if (p == NULL)
+ p = find_devices("IDE");
+ /* Move removable devices such as the media-bay CDROM
+ on the PB3400 to the end of the list. */
+ for (; p != NULL; p = p->next) {
+ if (p->parent && p->parent->name
+ && strcasecmp(p->parent->name, "media-bay") == 0) {
+ *rp = p;
+ rp = &p->next;
+ } else {
+ *pp = p;
+ pp = &p->next;
+ }
+ }
+ *rp = NULL;
+ *pp = removables;
+
+ for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) {
+ if (np->n_addrs == 0) {
+ printk(KERN_WARNING "ide: no address for device %s\n",
+ np->full_name);
+ continue;
+ }
+ pmac_ide_regbase[i] = (unsigned long)
+ ioremap(np->addrs[0].address, 0x200);
+ if (np->n_intrs == 0) {
+ printk("ide: no intrs for device %s, using 13\n",
+ np->full_name);
+ pmac_ide_irq[i] = 13;
+ } else {
+ pmac_ide_irq[i] = np->intrs[0].line;
+ }
+
+ if (np->parent && np->parent->name
+ && strcasecmp(np->parent->name, "media-bay") == 0) {
+ mb_cd_index = i;
+ mb_cd_base = pmac_ide_regbase[i];
+ mb_cd_irq = pmac_ide_irq[i];
+ }
+
+ ++i;
+ }
+
+ pmac_ide_ports_known = 1;
}
+#endif /* CONFIG_BLK_DEV_IDE */
int
pmac_get_cpuinfo(char *buffer)
diff --git a/arch/ppc/kernel/pmac_support.c b/arch/ppc/kernel/pmac_support.c
index 17226f8ff..4ceaa0dc9 100644
--- a/arch/ppc/kernel/pmac_support.c
+++ b/arch/ppc/kernel/pmac_support.c
@@ -7,8 +7,11 @@
#include <linux/nvram.h>
#include <asm/ptrace.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/prom.h>
+#include <asm/adb.h>
+#include <asm/pmu.h>
/*
* Read and write the non-volatile RAM on PowerMacs and CHRP machines.
@@ -32,8 +35,7 @@ void pmac_nvram_init(void)
}
nvram_naddrs = dp->n_addrs;
if (_machine == _MACH_chrp && nvram_naddrs == 1) {
- /* XXX for now */
- nvram_data = ioremap(0xf70e0000, NVRAM_SIZE);
+ nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size);
nvram_mult = 1;
} else if (nvram_naddrs == 1) {
nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size);
@@ -41,6 +43,8 @@ void pmac_nvram_init(void)
} else if (nvram_naddrs == 2) {
nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size);
nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size);
+ } else if (nvram_naddrs == 0 && adb_hardware == ADB_VIAPMU) {
+ nvram_naddrs = -1;
} else {
printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n",
nvram_naddrs);
@@ -49,7 +53,16 @@ void pmac_nvram_init(void)
unsigned char nvram_read_byte(int addr)
{
+ struct adb_request req;
+
switch (nvram_naddrs) {
+ case -1:
+ if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM,
+ (addr >> 8) & 0xff, addr & 0xff))
+ break;
+ while (!req.complete)
+ pmu_poll();
+ return req.reply[1];
case 1:
return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult];
case 2:
@@ -62,7 +75,16 @@ unsigned char nvram_read_byte(int addr)
void nvram_write_byte(unsigned char val, int addr)
{
+ struct adb_request req;
+
switch (nvram_naddrs) {
+ case -1:
+ if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM,
+ (addr >> 8) & 0xff, addr & 0xff, val))
+ break;
+ while (!req.complete)
+ pmu_poll();
+ break;
case 1:
nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val;
break;
diff --git a/arch/ppc/kernel/pmac_time.c b/arch/ppc/kernel/pmac_time.c
index 1a31ffa9a..3c976506e 100644
--- a/arch/ppc/kernel/pmac_time.c
+++ b/arch/ppc/kernel/pmac_time.c
@@ -15,8 +15,11 @@
#include <linux/mm.h>
#include <asm/adb.h>
#include <asm/cuda.h>
+#include <asm/pmu.h>
#include <asm/prom.h>
#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
#include "time.h"
@@ -44,7 +47,11 @@
/* Bits in IFR and IER */
#define T1_INT 0x40 /* Timer 1 interrupt */
-static int via_calibrate_decr(void)
+/*
+ * Calibrate the decrementer register using VIA timer 1.
+ * This is used both on powermacs and CHRP machines.
+ */
+int via_calibrate_decr(void)
{
struct device_node *vias;
volatile unsigned char *via;
@@ -54,9 +61,12 @@ static int via_calibrate_decr(void)
vias = find_devices("via-cuda");
if (vias == 0)
vias = find_devices("via-pmu");
+ if (vias == 0)
+ vias = find_devices("via");
if (vias == 0 || vias->n_addrs == 0)
return 0;
- via = (volatile unsigned char *) vias->addrs[0].address;
+ via = (volatile unsigned char *)
+ ioremap(vias->addrs[0].address, vias->addrs[0].size);
/* set timer 1 for continuous interrupts */
out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
@@ -123,14 +133,30 @@ pmac_get_rtc_time(void)
struct adb_request req;
/* Get the time from the RTC */
- cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME);
- while (!req.complete)
- cuda_poll();
- if (req.reply_len != 7)
- printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
- req.reply_len);
- return (req.reply[3] << 24) + (req.reply[4] << 16)
- + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET;
+ switch (adb_hardware) {
+ case ADB_VIACUDA:
+ if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0)
+ return 0;
+ while (!req.complete)
+ cuda_poll();
+ if (req.reply_len != 7)
+ printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
+ req.reply_len);
+ return (req.reply[3] << 24) + (req.reply[4] << 16)
+ + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET;
+ case ADB_VIAPMU:
+ if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)
+ return 0;
+ while (!req.complete)
+ pmu_poll();
+ if (req.reply_len != 5)
+ printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
+ req.reply_len);
+ return (req.reply[1] << 24) + (req.reply[2] << 16)
+ + (req.reply[3] << 8) + req.reply[4] - RTC_OFFSET;
+ default:
+ return 0;
+ }
}
int pmac_set_rtc_time(unsigned long nowtime)
diff --git a/arch/ppc/kernel/ppc-stub.c b/arch/ppc/kernel/ppc-stub.c
new file mode 100644
index 000000000..b7eab0fa1
--- /dev/null
+++ b/arch/ppc/kernel/ppc-stub.c
@@ -0,0 +1,705 @@
+/* $Id: ppc-stub.c,v 1.2 1998/04/11 17:29:03 geert Exp $
+ * ppc-stub.c: KGDB support for the Linux kernel.
+ *
+ * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC
+ * some stuff borrowed from Paul Mackerras' xmon
+ * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu)
+ *
+ * Modifications to run under Linux
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ * This file originally came from the gdb sources, and the
+ * copyright notices have been retained below.
+ */
+
+/****************************************************************************
+
+ THIS SOFTWARE IS NOT COPYRIGHTED
+
+ HP offers the following for use in the public domain. HP makes no
+ warranty with regard to the software or its performance and the
+ user accepts the software "AS IS" with all faults.
+
+ HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
+ TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+****************************************************************************/
+
+/****************************************************************************
+ * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
+ *
+ * Module name: remcom.c $
+ * Revision: 1.34 $
+ * Date: 91/03/09 12:29:49 $
+ * Contributor: Lake Stevens Instrument Division$
+ *
+ * Description: low level support for gdb debugger. $
+ *
+ * Considerations: only works on target hardware $
+ *
+ * Written by: Glenn Engel $
+ * ModuleState: Experimental $
+ *
+ * NOTES: See Below $
+ *
+ * Modified for SPARC by Stu Grossman, Cygnus Support.
+ *
+ * This code has been extensively tested on the Fujitsu SPARClite demo board.
+ *
+ * To enable debugger support, two things need to happen. One, a
+ * call to set_debug_traps() is necessary in order to allow any breakpoints
+ * or error conditions to be properly intercepted and reported to gdb.
+ * Two, a breakpoint needs to be generated to begin communication. This
+ * is most easily accomplished by a call to breakpoint(). Breakpoint()
+ * simulates a breakpoint by executing a trap #1.
+ *
+ *************
+ *
+ * The following gdb commands are supported:
+ *
+ * command function Return value
+ *
+ * g return the value of the CPU registers hex data or ENN
+ * G set the value of the CPU registers OK or ENN
+ * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz
+ *
+ * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
+ * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
+ *
+ * c Resume at current address SNN ( signal NN)
+ * cAA..AA Continue at address AA..AA SNN
+ *
+ * s Step one instruction SNN
+ * sAA..AA Step one instruction from AA..AA SNN
+ *
+ * k kill
+ *
+ * ? What was the last sigval ? SNN (signal NN)
+ *
+ * bBB..BB Set baud rate to BB..BB OK or BNN, then sets
+ * baud rate
+ *
+ * All commands and responses are sent with a packet which includes a
+ * checksum. A packet consists of
+ *
+ * $<packet info>#<checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <checksum> :: <two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * When a packet is received, it is first acknowledged with either '+' or '-'.
+ * '+' indicates a successful transfer. '-' indicates a failed transfer.
+ *
+ * Example:
+ *
+ * Host: Reply:
+ * $m0,10#2a +$00010203040506070809101112131415#42
+ *
+ ****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/system.h>
+#include <asm/signal.h>
+#include <asm/system.h>
+#include <asm/kgdb.h>
+#include <asm/pgtable.h>
+#include <asm/ptrace.h>
+
+void breakinst(void);
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound buffers
+ * at least NUMREGBYTES*2 are needed for register packets
+ */
+#define BUFMAX 2048
+static char remcomInBuffer[BUFMAX];
+static char remcomOutBuffer[BUFMAX];
+
+static int initialized = 0;
+static int kgdb_active = 0;
+static u_int fault_jmp_buf[100];
+static int kdebug;
+
+static const char hexchars[]="0123456789abcdef";
+
+/* Place where we save old trap entries for restoration - sparc*/
+/* struct tt_entry kgdb_savettable[256]; */
+/* typedef void (*trapfunc_t)(void); */
+
+#if 0
+/* Install an exception handler for kgdb */
+static void exceptionHandler(int tnum, unsigned int *tfunc)
+{
+ /* We are dorking with a live trap table, all irqs off */
+}
+#endif
+
+int
+kgdb_setjmp(long *buf)
+{
+ asm ("mflr 0; stw 0,0(%0);"
+ "stw 1,4(%0); stw 2,8(%0);"
+ "mfcr 0; stw 0,12(%0);"
+ "stmw 13,16(%0)"
+ : : "r" (buf));
+ /* XXX should save fp regs as well */
+ return 0;
+}
+void
+kgdb_longjmp(long *buf, int val)
+{
+ if (val == 0)
+ val = 1;
+ asm ("lmw 13,16(%0);"
+ "lwz 0,12(%0); mtcrf 0x38,0;"
+ "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);"
+ "mtlr 0; mr 3,%1"
+ : : "r" (buf), "r" (val));
+}
+/* Convert ch from a hex digit to an int */
+static int
+hex(unsigned char ch)
+{
+ if (ch >= 'a' && ch <= 'f')
+ return ch-'a'+10;
+ if (ch >= '0' && ch <= '9')
+ return ch-'0';
+ if (ch >= 'A' && ch <= 'F')
+ return ch-'A'+10;
+ return -1;
+}
+
+/* Convert the memory pointed to by mem into hex, placing result in buf.
+ * Return a pointer to the last char put in buf (null), in case of mem fault,
+ * return 0.
+ */
+static unsigned char *
+mem2hex(char *mem, char *buf, int count)
+{
+ unsigned char ch;
+
+ if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
+ debugger_fault_handler = kgdb_fault_handler;
+ while (count-- > 0) {
+ ch = *mem++;
+ *buf++ = hexchars[ch >> 4];
+ *buf++ = hexchars[ch & 0xf];
+ }
+ } else {
+ /* error condition */
+ }
+ debugger_fault_handler = 0;
+ *buf = 0;
+ return buf;
+}
+
+/* convert the hex array pointed to by buf into binary to be placed in mem
+ * return a pointer to the character AFTER the last byte written.
+*/
+static char *
+hex2mem(char *buf, char *mem, int count)
+{
+ int i;
+ unsigned char ch;
+
+ if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
+ debugger_fault_handler = kgdb_fault_handler;
+ for (i=0; i<count; i++) {
+ ch = hex(*buf++) << 4;
+ ch |= hex(*buf++);
+ *mem++ = ch;
+ }
+ flush_icache_range((int)mem, (int)mem+count);
+ } else {
+ /* error condition */
+ }
+ debugger_fault_handler = 0;
+ return mem;
+}
+
+/*
+ * While we find nice hex chars, build an int.
+ * Return number of chars processed.
+ */
+static int
+hexToInt(char **ptr, int *intValue)
+{
+ int numChars = 0;
+ int hexValue;
+
+ *intValue = 0;
+
+ if (kgdb_setjmp((long*)fault_jmp_buf) == 0) {
+ debugger_fault_handler = kgdb_fault_handler;
+ while (**ptr) {
+ hexValue = hex(**ptr);
+ if (hexValue < 0)
+ break;
+
+ *intValue = (*intValue << 4) | hexValue;
+ numChars ++;
+
+ (*ptr)++;
+ }
+ } else {
+ /* error condition */
+ }
+ debugger_fault_handler = 0;
+
+ return (numChars);
+}
+
+/* scan for the sequence $<data>#<checksum> */
+static void
+getpacket(char *buffer)
+{
+ unsigned char checksum;
+ unsigned char xmitcsum;
+ int i;
+ int count;
+ unsigned char ch;
+
+ do {
+ /* wait around for the start character, ignore all other
+ * characters */
+ while ((ch = (getDebugChar() & 0x7f)) != '$') ;
+
+ checksum = 0;
+ xmitcsum = -1;
+
+ count = 0;
+
+ /* now, read until a # or end of buffer is found */
+ while (count < BUFMAX) {
+ ch = getDebugChar() & 0x7f;
+ if (ch == '#')
+ break;
+ checksum = checksum + ch;
+ buffer[count] = ch;
+ count = count + 1;
+ }
+
+ if (count >= BUFMAX)
+ continue;
+
+ buffer[count] = 0;
+
+ if (ch == '#') {
+ xmitcsum = hex(getDebugChar() & 0x7f) << 4;
+ xmitcsum |= hex(getDebugChar() & 0x7f);
+ if (checksum != xmitcsum)
+ putDebugChar('-'); /* failed checksum */
+ else {
+ putDebugChar('+'); /* successful transfer */
+ /* if a sequence char is present, reply the ID */
+ if (buffer[2] == ':') {
+ putDebugChar(buffer[0]);
+ putDebugChar(buffer[1]);
+ /* remove sequence chars from buffer */
+ count = strlen(buffer);
+ for (i=3; i <= count; i++)
+ buffer[i-3] = buffer[i];
+ }
+ }
+ }
+ } while (checksum != xmitcsum);
+}
+
+/* send the packet in buffer. */
+static void putpacket(unsigned char *buffer)
+{
+ unsigned char checksum;
+ int count;
+ unsigned char ch, recv;
+
+ /* $<packet info>#<checksum>. */
+ do {
+ putDebugChar('$');
+ checksum = 0;
+ count = 0;
+
+ while ((ch = buffer[count])) {
+ putDebugChar(ch);
+ checksum += ch;
+ count += 1;
+ }
+
+ putDebugChar('#');
+ putDebugChar(hexchars[checksum >> 4]);
+ putDebugChar(hexchars[checksum & 0xf]);
+ recv = getDebugChar();
+ } while ((recv & 0x7f) != '+');
+}
+
+static void kgdb_flush_cache_all(void)
+{
+ flush_instruction_cache();
+}
+
+static inline int get_msr()
+{
+ int msr;
+ asm volatile("mfmsr %0" : "=r" (msr):);
+ return msr;
+}
+
+static inline void set_msr(int msr)
+{
+ asm volatile("mfmsr %0" : : "r" (msr));
+}
+
+/* Set up exception handlers for tracing and breakpoints
+ * [could be called kgdb_init()]
+ */
+void set_debug_traps(void)
+{
+#if 0
+ unsigned char c;
+
+ save_and_cli(flags);
+
+ /* In case GDB is started before us, ack any packets (presumably
+ * "$?#xx") sitting there.
+ *
+ * I've found this code causes more problems than it solves,
+ * so that's why it's commented out. GDB seems to work fine
+ * now starting either before or after the kernel -bwb
+ */
+
+ while((c = getDebugChar()) != '$');
+ while((c = getDebugChar()) != '#');
+ c = getDebugChar(); /* eat first csum byte */
+ c = getDebugChar(); /* eat second csum byte */
+ putDebugChar('+'); /* ack it */
+#endif
+ debugger = kgdb;
+ debugger_bpt = kgdb_bpt;
+ debugger_sstep = kgdb_sstep;
+ debugger_iabr_match = kgdb_iabr_match;
+ debugger_dabr_match = kgdb_dabr_match;
+
+ kgdb_interruptible(1);
+ initialized = 1;
+}
+
+static void kgdb_fault_handler(struct pt_regs *regs)
+{
+ kgdb_longjmp((long*)fault_jmp_buf, 1);
+}
+
+int kgdb_bpt(struct pt_regs *regs)
+{
+ handle_exception(regs);
+ return 1;
+}
+
+int kgdb_sstep(struct pt_regs *regs)
+{
+ handle_exception(regs);
+ return 1;
+}
+
+void kgdb(struct pt_regs *regs)
+{
+ handle_exception(regs);
+}
+
+int kgdb_iabr_match(struct pt_regs *regs)
+{
+ printk("kgdb doesn't support iabr, what?!?\n");
+ handle_exception(regs);
+ return 1;
+}
+
+int kgdb_dabr_match(struct pt_regs *regs)
+{
+ printk("kgdb doesn't support dabr, what?!?\n");
+ handle_exception(regs);
+ return 1;
+}
+
+/* Convert the SPARC hardware trap type code to a unix signal number. */
+/*
+ * This table contains the mapping between PowerPC hardware trap types, and
+ * signals, which are primarily what GDB understands.
+ */
+static struct hard_trap_info
+{
+ unsigned int tt; /* Trap type code for powerpc */
+ unsigned char signo; /* Signal that we map this trap into */
+} hard_trap_info[] = {
+ { 0x200, SIGSEGV }, /* machine check */
+ { 0x300, SIGSEGV }, /* address error (store) */
+ { 0x400, SIGBUS }, /* instruction bus error */
+ { 0x500, SIGINT }, /* interrupt */
+ { 0x600, SIGBUS }, /* alingment */
+ { 0x700, SIGILL }, /* reserved instruction or sumpin' */
+ { 0x800, SIGFPE }, /* fpu unavail */
+ { 0x900, SIGALRM }, /* decrementer */
+ { 0xa00, SIGILL }, /* reserved */
+ { 0xb00, SIGILL }, /* reserved */
+ { 0xc00, SIGCHLD }, /* syscall */
+ { 0xd00, SIGINT }, /* watch */
+ { 0xe00, SIGFPE }, /* fp assist */
+ { 0, 0} /* Must be last */
+};
+
+static int computeSignal(unsigned int tt)
+{
+ struct hard_trap_info *ht;
+
+ for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
+ if (ht->tt == tt)
+ return ht->signo;
+
+ return SIGHUP; /* default for things we don't know about */
+}
+
+/*
+ * This function does all command processing for interfacing to gdb.
+ */
+static void
+handle_exception (struct pt_regs *regs)
+{
+ int sigval;
+ int addr;
+ int length;
+ char *ptr;
+ unsigned int msr;
+
+ if (debugger_fault_handler) {
+ debugger_fault_handler(regs);
+ panic("kgdb longjump failed!\n");
+ }
+ if (kgdb_active) {
+ printk("interrupt while in kgdb, returning\n");
+ return;
+ }
+ kgdb_active = 1;
+
+ printk("kgdb: entering handle_exception; trap [0x%x]\n",
+ (unsigned int)regs->trap);
+
+ kgdb_interruptible(0);
+ lock_kernel();
+ msr = get_msr();
+ set_msr(msr & ~MSR_EE); /* disable interrupts */
+
+ if (regs->nip == (unsigned long)breakinst) {
+ /* Skip over breakpoint trap insn */
+ regs->nip += 4;
+ }
+
+ /* reply to host that an exception has occurred */
+ sigval = computeSignal(regs->trap);
+ ptr = remcomOutBuffer;
+
+ *ptr++ = 'S';
+ *ptr++ = hexchars[sigval >> 4];
+ *ptr++ = hexchars[sigval & 0xf];
+
+ *ptr++ = 0;
+
+ putpacket(remcomOutBuffer);
+
+ /* XXX We may want to add some features dealing with poking the
+ * XXX page tables, ... (look at sparc-stub.c for more info)
+ * XXX also required hacking to the gdb sources directly...
+ */
+
+ while (1) {
+ remcomOutBuffer[0] = 0;
+
+ getpacket(remcomInBuffer);
+ switch (remcomInBuffer[0]) {
+ case '?': /* report most recent signal */
+ remcomOutBuffer[0] = 'S';
+ remcomOutBuffer[1] = hexchars[sigval >> 4];
+ remcomOutBuffer[2] = hexchars[sigval & 0xf];
+ remcomOutBuffer[3] = 0;
+ break;
+#if 0
+ case 'q': /* this screws up gdb for some reason...*/
+ {
+ extern long _start, sdata, __bss_start;
+
+ ptr = &remcomInBuffer[1];
+ if (strncmp(ptr, "Offsets", 7) != 0)
+ break;
+
+ ptr = remcomOutBuffer;
+ sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x",
+ &_start, &sdata, &__bss_start);
+ break;
+ }
+#endif
+ case 'd':
+ /* toggle debug flag */
+ kdebug ^= 1;
+ break;
+
+ case 'g': /* return the value of the CPU registers.
+ * some of them are non-PowerPC names :(
+ * they are stored in gdb like:
+ * struct {
+ * u32 gpr[32];
+ * f64 fpr[32];
+ * u32 pc, ps, cnd, lr; (ps=msr)
+ * u32 cnt, xer, mq;
+ * }
+ */
+ {
+ int i;
+ ptr = remcomOutBuffer;
+ /* General Purpose Regs */
+ ptr = mem2hex((char *)regs, ptr, 32 * 4);
+ /* Floating Point Regs - FIXME */
+ /*ptr = mem2hex((char *), ptr, 32 * 8);*/
+ for(i=0; i<(32*8*2); i++) { /* 2chars/byte */
+ ptr[i] = '0';
+ }
+ ptr += 32*8*2;
+ /* pc, msr, cr, lr, ctr, xer, (mq is unused) */
+ ptr = mem2hex((char *)&regs->nip, ptr, 4);
+ ptr = mem2hex((char *)&regs->msr, ptr, 4);
+ ptr = mem2hex((char *)&regs->ccr, ptr, 4);
+ ptr = mem2hex((char *)&regs->link, ptr, 4);
+ ptr = mem2hex((char *)&regs->ctr, ptr, 4);
+ ptr = mem2hex((char *)&regs->xer, ptr, 4);
+ }
+ break;
+
+ case 'G': /* set the value of the CPU registers */
+ {
+ ptr = &remcomInBuffer[1];
+
+ /*
+ * If the stack pointer has moved, you should pray.
+ * (cause only god can help you).
+ */
+
+ /* General Purpose Regs */
+ hex2mem(ptr, (char *)regs, 32 * 4);
+
+ /* Floating Point Regs - FIXME?? */
+ /*ptr = hex2mem(ptr, ??, 32 * 8);*/
+ ptr += 32*8*2;
+
+ /* pc, msr, cr, lr, ctr, xer, (mq is unused) */
+ ptr = hex2mem(ptr, (char *)&regs->nip, 4);
+ ptr = hex2mem(ptr, (char *)&regs->msr, 4);
+ ptr = hex2mem(ptr, (char *)&regs->ccr, 4);
+ ptr = hex2mem(ptr, (char *)&regs->link, 4);
+ ptr = hex2mem(ptr, (char *)&regs->ctr, 4);
+ ptr = hex2mem(ptr, (char *)&regs->xer, 4);
+
+ strcpy(remcomOutBuffer,"OK");
+ }
+ break;
+ case 'H':
+ /* dont do anything, yet, just acknowledge */
+ hexToInt(&ptr, &addr);
+ strcpy(remcomOutBuffer,"OK");
+ break;
+
+ case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
+ /* Try to read %x,%x. */
+
+ ptr = &remcomInBuffer[1];
+
+ if (hexToInt(&ptr, &addr)
+ && *ptr++ == ','
+ && hexToInt(&ptr, &length)) {
+ if (mem2hex((char *)addr, remcomOutBuffer,length))
+ break;
+ strcpy (remcomOutBuffer, "E03");
+ } else {
+ strcpy(remcomOutBuffer,"E01");
+ }
+ break;
+
+ case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+ /* Try to read '%x,%x:'. */
+
+ ptr = &remcomInBuffer[1];
+
+ if (hexToInt(&ptr, &addr)
+ && *ptr++ == ','
+ && hexToInt(&ptr, &length)
+ && *ptr++ == ':') {
+ if (hex2mem(ptr, (char *)addr, length)) {
+ strcpy(remcomOutBuffer, "OK");
+ } else {
+ strcpy(remcomOutBuffer, "E03");
+ }
+ } else {
+ strcpy(remcomOutBuffer, "E02");
+ }
+ break;
+
+
+ case 'k': /* kill the program, actually just continue */
+ case 'c': /* cAA..AA Continue; address AA..AA optional */
+ /* try to read optional parameter, pc unchanged if no parm */
+
+ ptr = &remcomInBuffer[1];
+ if (hexToInt(&ptr, &addr)) {
+ regs->nip = addr;
+ }
+
+/* Need to flush the instruction cache here, as we may have deposited a
+ * breakpoint, and the icache probably has no way of knowing that a data ref to
+ * some location may have changed something that is in the instruction cache.
+ */
+ kgdb_flush_cache_all();
+ set_msr(msr);
+ kgdb_interruptible(1);
+ unlock_kernel();
+ kgdb_active = 0;
+ return;
+
+ case 's':
+ kgdb_flush_cache_all();
+ regs->msr |= MSR_SE;
+ set_msr(msr | MSR_SE);
+ unlock_kernel();
+ kgdb_active = 0;
+ return;
+
+ case 'r': /* Reset (if user process..exit ???)*/
+ panic("kgdb reset.");
+ break;
+ } /* switch */
+ if (remcomOutBuffer[0] && kdebug) {
+ printk("remcomInBuffer: %s\n", remcomInBuffer);
+ printk("remcomOutBuffer: %s\n", remcomOutBuffer);
+ }
+ /* reply to the request */
+ putpacket(remcomOutBuffer);
+ } /* while(1) */
+}
+
+/* This function will generate a breakpoint exception. It is used at the
+ beginning of a program to sync up with a debugger and can be used
+ otherwise as a quick means to stop program execution and "break" into
+ the debugger. */
+
+void
+breakpoint(void)
+{
+ if (!initialized) {
+ printk("breakpoint() called b4 kgdb init\n");
+ return;
+ }
+
+ asm(" .globl breakinst
+ breakinst: trap
+ ");
+}
diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c
index 386bfe3ac..e1803a269 100644
--- a/arch/ppc/kernel/ppc_htab.c
+++ b/arch/ppc/kernel/ppc_htab.c
@@ -1,5 +1,5 @@
/*
- * $Id: ppc_htab.c,v 1.16 1997/11/17 18:25:04 cort Exp $
+ * $Id: ppc_htab.c,v 1.17 1998/03/14 07:52:49 cort Exp $
*
* PowerPC hash table management proc entry. Will show information
* about the current hash table and will allow changes to it.
@@ -12,6 +12,7 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
@@ -88,6 +89,7 @@ struct inode_operations proc_ppc_htab_inode_operations = {
#define PMC1 953
#define PMC2 954
+#ifndef CONFIG_8xx
char *pmc1_lookup(unsigned long mmcr0)
{
switch ( mmcr0 & (0x7f<<7) )
@@ -123,7 +125,7 @@ char *pmc2_lookup(unsigned long mmcr0)
return "unknown";
}
}
-
+#endif /* CONFIG_8xx */
/*
* print some useful info about the hash table. This function
@@ -133,6 +135,7 @@ char *pmc2_lookup(unsigned long mmcr0)
static ssize_t ppc_htab_read(struct file * file, char * buf,
size_t count, loff_t *ppos)
{
+#ifndef CONFIG_8xx
unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0;
int n = 0, valid;
unsigned int kptes = 0, overflow = 0, uptes = 0, zombie_ptes = 0;
@@ -249,6 +252,9 @@ return_string:
copy_to_user(buf, buffer + *ppos, n);
*ppos += n;
return n;
+#else /* CONFIG_8xx */
+ return 0;
+#endif /* CONFIG_8xx */
}
/*
@@ -257,6 +263,7 @@ return_string:
static ssize_t ppc_htab_write(struct file * file, const char * buffer,
size_t count, loff_t *ppos)
{
+#ifndef CONFIG_8xx
unsigned long tmp;
if ( current->uid != 0 )
return -EACCES;
@@ -493,6 +500,9 @@ static ssize_t ppc_htab_write(struct file * file, const char * buffer,
reset_SDR1();
#endif
return count;
+#else /* CONFIG_8xx */
+ return 0;
+#endif /* CONFIG_8xx */
}
@@ -512,4 +522,3 @@ ppc_htab_lseek(struct file * file, loff_t offset, int orig)
return(-EINVAL);
}
}
-
diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c
index 6ec6258ab..29df507d3 100644
--- a/arch/ppc/kernel/ppc_ksyms.c
+++ b/arch/ppc/kernel/ppc_ksyms.c
@@ -4,9 +4,7 @@
#include <linux/elfcore.h>
#include <linux/sched.h>
#include <linux/string.h>
-#include <linux/bios32.h>
#include <linux/interrupt.h>
-#include <linux/pci.h>
#include <asm/semaphore.h>
#include <asm/processor.h>
@@ -18,9 +16,11 @@
#include <asm/pgtable.h>
#include <asm/adb.h>
#include <asm/cuda.h>
+#include <asm/pmu.h>
#include <asm/prom.h>
#include <asm/system.h>
#include <asm/pci-bridge.h>
+#include <asm/irq.h>
extern void transfer_to_handler(void);
extern void int_return(void);
@@ -49,9 +49,13 @@ EXPORT_SYMBOL(sys_sigreturn);
EXPORT_SYMBOL(lost_interrupts);
EXPORT_SYMBOL(do_lost_interrupts);
EXPORT_SYMBOL(__ppc_bh_counter);
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
-#if !defined(CONFIG_MACH_SPECIFIC)
+#if !defined(CONFIG_MACH_SPECIFIC) || defined(CONFIG_PMAC)
EXPORT_SYMBOL(isa_io_base);
+#endif
+#if !defined(CONFIG_MACH_SPECIFIC)
EXPORT_SYMBOL(pci_dram_offset);
#endif
@@ -114,11 +118,15 @@ EXPORT_SYMBOL(outw);
EXPORT_SYMBOL(outl);
EXPORT_SYMBOL(outsl);*/
+EXPORT_SYMBOL(_insb);
+EXPORT_SYMBOL(_outsb);
EXPORT_SYMBOL(_insw);
EXPORT_SYMBOL(_outsw);
EXPORT_SYMBOL(_insl);
EXPORT_SYMBOL(_outsl);
EXPORT_SYMBOL(ioremap);
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(iounmap);
EXPORT_SYMBOL(start_thread);
@@ -140,6 +148,10 @@ EXPORT_SYMBOL(adb_autopoll);
EXPORT_SYMBOL(adb_register);
EXPORT_SYMBOL(cuda_request);
EXPORT_SYMBOL(cuda_send_request);
+EXPORT_SYMBOL(cuda_poll);
+EXPORT_SYMBOL(pmu_request);
+EXPORT_SYMBOL(pmu_send_request);
+EXPORT_SYMBOL(pmu_poll);
EXPORT_SYMBOL(abort);
EXPORT_SYMBOL(find_devices);
EXPORT_SYMBOL(find_type_devices);
@@ -148,7 +160,3 @@ EXPORT_SYMBOL(get_property);
EXPORT_SYMBOL(pci_io_base);
EXPORT_SYMBOL(pci_device_loc);
EXPORT_SYMBOL(note_scsi_host);
-
-#if CONFIG_PCI
-EXPORT_SYMBOL(pci_devices);
-#endif
diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c
index 193ded4df..999406200 100644
--- a/arch/ppc/kernel/prep_pci.c
+++ b/arch/ppc/kernel/prep_pci.c
@@ -1,5 +1,5 @@
/*
- * $Id: prep_pci.c,v 1.12 1997/10/29 03:35:08 cort Exp $
+ * $Id: prep_pci.c,v 1.16 1998/02/23 02:47:32 davem Exp $
* PReP pci functions.
* Originally by Gary Thomas
* rewritten and updated by Cort Dougan (cort@cs.nmt.edu)
@@ -8,7 +8,6 @@
*/
#include <linux/types.h>
-#include <linux/bios32.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -271,6 +270,12 @@ static char Nobis_pci_IRQ_routes[] = {
#define CAROLINA_IRQ_EDGE_MASK_LO 0x00 /* IRQ's 0-7 */
#define CAROLINA_IRQ_EDGE_MASK_HI 0xA4 /* IRQ's 8-15 [10,13,15] */
+/*
+ * FIXME: This code incorrectly assumes there's only bus #0, breaking all
+ * PCI-to-PCI bridges. Also multi-function devices are not supported
+ * at all. [mj]
+ */
+
int
prep_pcibios_read_config_dword (unsigned char bus,
unsigned char dev, unsigned char offset, unsigned int *val)
@@ -319,16 +324,6 @@ prep_pcibios_read_config_byte (unsigned char bus,
unsigned char _val;
volatile unsigned char *ptr;
dev >>= 3;
- /* Note: the configuration registers don't always have this right! */
- if (offset == PCI_INTERRUPT_LINE)
- {
- *val = Motherboard_routes[Motherboard_map[dev]];
-/*printk("dev %d map %d route %d on board %d\n",
- dev,Motherboard_map[dev],
- Motherboard_routes[Motherboard_map[dev]],
- *(unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)));*/
- return PCIBIOS_SUCCESSFUL;
- }
if ((bus != 0) || (dev > MAX_DEVNR))
{
*(unsigned long *)val = (unsigned long) 0xFFFFFFFF;
@@ -406,7 +401,7 @@ __initfunc(unsigned long route_pci_interrupts(void))
int i;
if ( _prep_type == _PREP_Motorola)
- {
+ {
switch (inb(0x800) & 0xF0)
{
case 0x10: /* MVME16xx */
@@ -430,7 +425,6 @@ __initfunc(unsigned long route_pci_interrupts(void))
break;
case 0x40: /* PowerStack */
default: /* Can't hurt, can it? */
-
Motherboard_map_name = "Blackhawk (Powerstack)";
Motherboard_map = Blackhawk_pci_IRQ_map;
Motherboard_routes = Blackhawk_pci_IRQ_routes;
@@ -474,3 +468,4 @@ __initfunc(unsigned long route_pci_interrupts(void))
*ibc_pcicon |= 0x20;
return 0;
}
+
diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c
index 0aee7cff4..5234435ca 100644
--- a/arch/ppc/kernel/prep_setup.c
+++ b/arch/ppc/kernel/prep_setup.c
@@ -80,8 +80,7 @@ prep_get_cpuinfo(char *buffer)
{
extern char *Motherboard_map_name;
extern RESIDUAL res;
- int i;
- int len;
+ int len, i;
#ifdef __SMP__
#define CD(X) (cpu_data[n].X)
@@ -93,7 +92,7 @@ prep_get_cpuinfo(char *buffer)
if ( res.ResidualLength == 0 )
return len;
-
+
/* print info about SIMMs */
len += sprintf(buffer+len,"simms\t\t: ");
for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ )
@@ -106,6 +105,7 @@ prep_get_cpuinfo(char *buffer)
}
len += sprintf(buffer+len,"\n");
+#if 0
/* TLB */
len += sprintf(buffer+len,"tlb\t\t:");
switch(res.VitalProductData.TLBAttrib)
@@ -123,7 +123,6 @@ prep_get_cpuinfo(char *buffer)
len += sprintf(buffer+len," not present\n");
break;
}
-
/* L1 */
len += sprintf(buffer+len,"l1\t\t: ");
switch(res.VitalProductData.CacheAttrib)
@@ -144,6 +143,7 @@ prep_get_cpuinfo(char *buffer)
len += sprintf(buffer+len,"not present\n");
break;
}
+#endif
/* L2 */
if ( (inb(IBM_EQUIP_PRESENT) & 1) == 0) /* l2 present */
@@ -201,7 +201,11 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
}
}
#endif
-
+ /* make the serial port the console */
+ /* strcat(cmd_line,"console=ttyS0,9600n8"); */
+ /* use the normal console but send output to the serial port, too */
+ /*strcat(cmd_line,"console=tty0 console=ttyS0,9600n8");*/
+ sprintf(cmd_line,"%s console=tty0 console=ttyS0,9600n8", cmd_line);
printk("Boot arguments: %s\n", cmd_line);
#ifdef CONFIG_CS4232
@@ -256,9 +260,5 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p))
#ifdef CONFIG_VGA_CONSOLE
conswitchp = &vga_con;
#endif
-#ifdef CONFIG_FB
- /* Frame buffer device based console */
- conswitchp = &fb_con;
-#endif
#endif
}
diff --git a/arch/ppc/kernel/prep_time.c b/arch/ppc/kernel/prep_time.c
index 4c3a91f91..3536eab43 100644
--- a/arch/ppc/kernel/prep_time.c
+++ b/arch/ppc/kernel/prep_time.c
@@ -220,7 +220,7 @@ static inline void timer_interrupt(int irq, void *dev, struct pt_regs * regs)
#ifdef CONFIG_HEARTBEAT
/* use hard disk LED as a heartbeat instead -- much more useful
for debugging -- Cort */
- switch(kstat.interrupts[0] % 101)
+ switch(kstat_irqs(0) % 101)
{
/* act like an actual heart beat -- ie thump-thump-pause... */
case 0:
diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c
index 7ffaf58c0..1c993bdc1 100644
--- a/arch/ppc/kernel/process.c
+++ b/arch/ppc/kernel/process.c
@@ -1,4 +1,3 @@
-
/*
* linux/arch/ppc/kernel/process.c
*
@@ -77,8 +76,13 @@ struct task_struct *current_set[NR_CPUS] = {&init_task, };
int
dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs)
{
+#ifdef __SMP__
+ if ( regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
if (last_task_used_math == current)
giveup_fpu();
+#endif
memcpy(fpregs, &current->tss.fpr[0], sizeof(*fpregs));
return 1;
}
@@ -98,7 +102,7 @@ int check_stack(struct task_struct *tsk)
printk("tss.magic bad: %08x\n", tsk->tss.magic);
}
#endif
-
+
if ( !tsk )
printk("check_stack(): tsk bad tsk %p\n",tsk);
@@ -157,17 +161,21 @@ switch_to(struct task_struct *prev, struct task_struct *new)
#endif
#ifdef SHOW_TASK_SWITCHES
- printk("%s/%d -> %s/%d cpu %d\n",
+ printk("%s/%d -> %s/%d NIP %08lx cpu %d sfr %d lock %x\n",
prev->comm,prev->pid,
- new->comm,new->pid,new->processor);
+ new->comm,new->pid,new->tss.regs->nip,new->processor,
+ new->tss.smp_fork_ret,scheduler_lock.lock);
#endif
#ifdef __SMP__
- /* bad news if last_task_used_math changes processors right now -- Cort */
- if ( (last_task_used_math == new) &&
- (new->processor != new->last_processor) )
- panic("last_task_used_math switched processors");
+ /* avoid complexity of lazy save/restore of fpu
+ * by just saving it every time we switch out -- Cort
+ */
+ if ( prev->tss.regs->msr & MSR_FP )
+ smp_giveup_fpu(prev);
+
/* be noisy about processor changes for debugging -- Cort */
- if ( new->last_processor != new->processor )
+ if ( (new->last_processor != NO_PROC_ID) &&
+ (new->last_processor != new->processor) )
printk("switch_to(): changing cpu's %d -> %d %s/%d\n",
new->last_processor,new->processor,
new->comm,new->pid);
@@ -181,11 +189,6 @@ switch_to(struct task_struct *prev, struct task_struct *new)
_enable_interrupts(s);
}
-asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs)
-{
- return 0;
-}
-
void show_regs(struct pt_regs * regs)
{
int i;
@@ -257,12 +260,11 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
((unsigned long)p + sizeof(union task_union)
- STACK_FRAME_OVERHEAD)) - 2;
*childregs = *regs;
-
if ((childregs->msr & MSR_PR) == 0)
childregs->gpr[2] = (unsigned long) p; /* `current' in new task */
childregs->gpr[3] = 0; /* Result from fork() */
p->tss.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD;
- p->tss.regs = childregs;
+ p->tss.regs = childregs;
if (usp >= (unsigned long) regs) {
/* Stack is in kernel space - must adjust */
childregs->gpr[1] = (unsigned long)(childregs + 1);
@@ -271,18 +273,28 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
childregs->gpr[1] = usp;
}
p->tss.last_syscall = -1;
-
+
/*
* copy fpu info - assume lazy fpu switch now always
* -- Cort
*/
+#ifdef __SMP__
+ if ( regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
if ( last_task_used_math == current )
giveup_fpu();
+#endif
memcpy(&p->tss.fpr, &current->tss.fpr, sizeof(p->tss.fpr));
p->tss.fpscr = current->tss.fpscr;
childregs->msr &= ~MSR_FP;
+#ifdef __SMP__
+ if ( (p->pid != 0) || !(clone_flags & CLONE_PID) )
+ p->tss.smp_fork_ret = 1;
+ p->last_processor = NO_PROC_ID;
+#endif /* __SMP__ */
return 0;
}
@@ -337,20 +349,48 @@ void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp)
shove_aux_table(sp);
}
+asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
+ struct pt_regs *regs)
+{
+ unsigned long clone_flags = p1;
+ int res;
+ lock_kernel();
+ res = do_fork(clone_flags, regs->gpr[1], regs);
+ /*
+ * only parent returns here, child returns to either
+ * syscall_ret_1() or kernel_thread()
+ * -- Cort
+ */
+#ifdef __SMP__
+ /* When we clone the idle task we keep the same pid but
+ * the return value of 0 for both causes problems.
+ * -- Cort
+ */
+ if ((current->pid == 0) && (current == &init_task))
+ res = 1;
+#endif /* __SMP__ */
+ unlock_kernel();
+ return res;
+}
asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6,
struct pt_regs *regs)
{
- int ret;
+ int res;
lock_kernel();
- ret = do_fork(SIGCHLD, regs->gpr[1], regs);
-#if 0/*def __SMP__*/
- if ( ret ) /* drop scheduler lock in child */
- scheduler_lock.lock = 0L;
-#endif /* __SMP__ */
+ res = do_fork(SIGCHLD, regs->gpr[1], regs);
+ /* only parent returns here */
+#ifdef __SMP__
+ /* When we clone the idle task we keep the same pid but
+ * the return value of 0 for both causes problems.
+ * -- Cort
+ */
+ if ((current->pid == 0) && (current == &init_task))
+ res = 1;
+#endif /* __SMP__ */
unlock_kernel();
- return ret;
+ return res;
}
asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
@@ -374,28 +414,6 @@ out:
return error;
}
-asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6,
- struct pt_regs *regs)
-{
- unsigned long clone_flags = p1;
- int res;
-
- lock_kernel();
- res = do_fork(clone_flags, regs->gpr[1], regs);
-#ifdef __SMP__
- /* When we clone the idle task we keep the same pid but
- * the return value of 0 for both causes problems.
- * -- Cort
- */
- if ((current->pid == 0) && (current == &init_task))
- res = 1;
- if ( 0 /*res*/ ) /* drop scheduler lock in child */
- scheduler_lock.lock = 0L;
-#endif /* __SMP__ */
- unlock_kernel();
- return res;
-}
-
void
print_backtrace(unsigned long *sp)
{
diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c
index 121ffea73..0e656caa1 100644
--- a/arch/ppc/kernel/prom.c
+++ b/arch/ppc/kernel/prom.c
@@ -16,6 +16,8 @@
#include <asm/prom.h>
#include <asm/page.h>
#include <asm/processor.h>
+#include <asm/irq.h>
+#include <asm/io.h>
/*
* Properties whose value is longer than this get excluded from our
@@ -50,6 +52,19 @@ struct pci_range {
unsigned size_lo;
};
+struct isa_reg_property {
+ unsigned space;
+ unsigned address;
+ unsigned size;
+};
+
+typedef unsigned long interpret_func(struct device_node *, unsigned long);
+static interpret_func interpret_pci_props;
+static interpret_func interpret_dbdma_props;
+static interpret_func interpret_isa_props;
+static interpret_func interpret_macio_props;
+static interpret_func interpret_root_props;
+
char *prom_display_paths[FB_MAX] __initdata = { 0, };
unsigned int prom_num_displays = 0;
@@ -60,19 +75,21 @@ extern char *klimit;
char *bootpath = 0;
char *bootdevice = 0;
-unsigned int rtas_data = 0;
-unsigned int rtas_entry = 0;
+unsigned int rtas_data = 0; /* virtual pointer */
+unsigned int rtas_entry = 0; /* physical pointer */
+unsigned int rtas_size = 0;
+char chunk[PAGE_SIZE*64];
static struct device_node *allnodes = 0;
static void *call_prom(const char *service, int nargs, int nret, ...);
-static void prom_print(const char *msg);
+ void prom_print(const char *msg);
static void prom_exit(void);
static unsigned long copy_device_tree(unsigned long, unsigned long);
static unsigned long inspect_node(phandle, struct device_node *, unsigned long,
unsigned long, struct device_node ***);
static unsigned long finish_node(struct device_node *, unsigned long,
- unsigned long);
+ interpret_func *);
static unsigned long check_display(unsigned long);
static int prom_next_node(phandle *);
@@ -119,6 +136,18 @@ prom_exit()
;
}
+void
+prom_enter(void)
+{
+ struct prom_args args;
+ unsigned long offset = reloc_offset();
+
+ args.service = RELOC("enter");
+ args.nargs = 0;
+ args.nret = 0;
+ RELOC(prom)(&args);
+}
+
static void *
call_prom(const char *service, int nargs, int nret, ...)
{
@@ -140,7 +169,7 @@ call_prom(const char *service, int nargs, int nret, ...)
return prom_args.args[nargs];
}
-static void
+void
prom_print(const char *msg)
{
const char *p, *q;
@@ -160,6 +189,11 @@ prom_print(const char *msg)
}
}
+
+#ifdef CONFIG_ALL_PPC
+unsigned char OF_type[16], OF_model[16];
+#endif
+
/*
* We enter here early on, when the Open Firmware prom is still
* handling exceptions and the MMU hash table for us.
@@ -169,11 +203,14 @@ prom_init(int r3, int r4, prom_entry pp)
{
unsigned long mem;
ihandle prom_rtas;
- unsigned int rtas_size;
unsigned long offset = reloc_offset();
int l;
char *p, *d;
+ /* check if we're prep, return if we are */
+ if ( *(unsigned long *)(0) == 0xdeadc0de )
+ return;
+
/* First get a handle for the stdout device */
RELOC(prom) = pp;
RELOC(prom_chosen) = call_prom(RELOC("finddevice"), 1, 1,
@@ -209,27 +246,57 @@ prom_init(int r3, int r4, prom_entry pp)
prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas"));
if (prom_rtas != (void *) -1) {
- rtas_size = 0;
+ RELOC(rtas_size) = 0;
call_prom(RELOC("getprop"), 4, 1, prom_rtas,
- RELOC("rtas-size"), &rtas_size, sizeof(rtas_size));
+ RELOC("rtas-size"), &RELOC(rtas_size), sizeof(rtas_size));
prom_print(RELOC("instantiating rtas..."));
- if (rtas_size == 0) {
+ if (RELOC(rtas_size) == 0) {
RELOC(rtas_data) = 0;
} else {
mem = (mem + 4095) & -4096; /* round to page bdry */
RELOC(rtas_data) = mem - KERNELBASE;
- mem += rtas_size;
+ mem += RELOC(rtas_size);
+ }
+ prom_rtas = call_prom(RELOC("open"), 1, 1, RELOC("/rtas"));
+ RELOC(rtas_data) = ((ulong)chunk+4095)&-4096;
+ {
+ int i, nargs;
+ struct prom_args prom_args;
+ nargs = 3;
+ prom_args.service = RELOC("call-method");
+ prom_args.nargs = nargs;
+ prom_args.nret = 2;
+ prom_args.args[0] = RELOC("instantiate-rtas");
+ prom_args.args[1] = prom_rtas;
+ prom_args.args[2] = ((void *)RELOC(rtas_data)-KERNELBASE);
+ RELOC(prom)(&prom_args);
+ if (prom_args.args[nargs] != 0)
+ i = 0;
+ else
+ i = (int)prom_args.args[nargs+1];
+ RELOC(rtas_entry) = i;
}
- RELOC(rtas_entry) = (unsigned int)
- call_prom(RELOC("instantiate-rtas"), 1, 1,
- RELOC(rtas_data));
- if (RELOC(rtas_entry) == -1)
+ if ((RELOC(rtas_entry) == -1) || (RELOC(rtas_entry) == 0))
prom_print(RELOC(" failed\n"));
else
prom_print(RELOC(" done\n"));
}
RELOC(klimit) = (char *) (mem - offset);
+#ifdef CONFIG_ALL_PPC
+ {
+
+ ihandle prom_root;
+
+ RELOC(prom_root) = call_prom(RELOC("finddevice"), 1, 1, RELOC("/"));
+ call_prom(RELOC("getprop"), 4, 1, RELOC(prom_root),
+ RELOC("device_type"), RELOC(OF_type),
+ (void *) 16);
+ call_prom(RELOC("getprop"), 4, 1, RELOC(prom_root),
+ RELOC("model"), RELOC(OF_model),
+ (void *) 16);
+ }
+#endif
}
/*
@@ -397,12 +464,18 @@ inspect_node(phandle node, struct device_node *dad,
return mem_start;
}
+/*
+ * finish_device_tree is called once things are running normally
+ * (i.e. with text and data mapped to the address they were linked at).
+ * It traverses the device tree and fills in the name, type,
+ * {n_}addrs and {n_}intrs fields of each node.
+ */
void
finish_device_tree(void)
{
unsigned long mem = (unsigned long) klimit;
- mem = finish_node(allnodes, mem, 0UL);
+ mem = finish_node(allnodes, mem, NULL);
printk(KERN_INFO "device tree used %lu bytes\n",
mem - (unsigned long) allnodes);
klimit = (char *) mem;
@@ -410,23 +483,53 @@ finish_device_tree(void)
static unsigned long
finish_node(struct device_node *np, unsigned long mem_start,
- unsigned long base_address)
+ interpret_func *ifunc)
{
- struct reg_property *rp;
- struct pci_reg_property *pci_addrs;
- struct address_range *adr;
struct device_node *child;
- int i, l;
np->name = get_property(np, "name", 0);
np->type = get_property(np, "device_type", 0);
- /* get all the device addresses and interrupts */
- adr = (struct address_range *) mem_start;
+ /* get the device addresses and interrupts */
+ if (ifunc != NULL)
+ mem_start = ifunc(np, mem_start);
+
+ if (!strcmp(np->name, "device-tree"))
+ ifunc = interpret_root_props;
+ else if (np->type == 0)
+ ifunc = NULL;
+ else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci"))
+ ifunc = interpret_pci_props;
+ else if (!strcmp(np->type, "dbdma")
+ || (ifunc == interpret_dbdma_props
+ && (!strcmp(np->type, "escc")
+ || !strcmp(np->type, "media-bay"))))
+ ifunc = interpret_dbdma_props;
+ else if (!strcmp(np->type, "mac-io"))
+ ifunc = interpret_macio_props;
+ else if (!strcmp(np->type, "isa"))
+ ifunc = interpret_isa_props;
+ else
+ ifunc = NULL;
+
+ for (child = np->child; child != NULL; child = child->sibling)
+ mem_start = finish_node(child, mem_start, ifunc);
+
+ return mem_start;
+}
+
+static unsigned long
+interpret_pci_props(struct device_node *np, unsigned long mem_start)
+{
+ struct address_range *adr;
+ struct pci_reg_property *pci_addrs;
+ int i, l, *ip;
+
pci_addrs = (struct pci_reg_property *)
get_property(np, "assigned-addresses", &l);
- i = 0;
- if (pci_addrs != 0) {
+ if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
while ((l -= sizeof(struct pci_reg_property)) >= 0) {
/* XXX assumes PCI addresses mapped 1-1 to physical */
adr[i].space = pci_addrs[i].addr.a_hi;
@@ -434,36 +537,194 @@ finish_node(struct device_node *np, unsigned long mem_start,
adr[i].size = pci_addrs[i].size_lo;
++i;
}
- } else {
- rp = (struct reg_property *) get_property(np, "reg", &l);
- if (rp != 0) {
- while ((l -= sizeof(struct reg_property)) >= 0) {
- adr[i].space = 0;
- adr[i].address = rp[i].address + base_address;
- adr[i].size = rp[i].size;
- ++i;
- }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / sizeof(int);
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = 0;
+ }
+ }
+
+ return mem_start;
+}
+
+static unsigned long
+interpret_dbdma_props(struct device_node *np, unsigned long mem_start)
+{
+ struct reg_property *rp;
+ struct address_range *adr;
+ unsigned long base_address;
+ int i, l, *ip;
+ struct device_node *db;
+
+ base_address = 0;
+ for (db = np->parent; db != NULL; db = db->parent) {
+ if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) {
+ base_address = db->addrs[0].address;
+ break;
}
}
- if (i > 0) {
+
+ rp = (struct reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = 0;
+ adr[i].address = rp[i].address + base_address;
+ adr[i].size = rp[i].size;
+ ++i;
+ }
np->addrs = adr;
np->n_addrs = i;
mem_start += i * sizeof(struct address_range);
}
- np->intrs = (int *) get_property(np, "AAPL,interrupts", &l);
- if (np->intrs == 0)
- np->intrs = (int *) get_property(np, "interrupts", &l);
- if (np->intrs != 0)
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
np->n_intrs = l / sizeof(int);
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = 0;
+ }
+ }
- if (np->type != 0 && np->n_addrs > 0
- && (strcmp(np->type, "dbdma") == 0
- || strcmp(np->type, "mac-io") == 0))
- base_address = np->addrs[0].address;
+ return mem_start;
+}
- for (child = np->child; child != NULL; child = child->sibling)
- mem_start = finish_node(child, mem_start, base_address);
+static unsigned long
+interpret_macio_props(struct device_node *np, unsigned long mem_start)
+{
+ struct reg_property *rp;
+ struct address_range *adr;
+ unsigned long base_address;
+ int i, l, *ip;
+ struct device_node *db;
+
+ base_address = 0;
+ for (db = np->parent; db != NULL; db = db->parent) {
+ if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) {
+ base_address = db->addrs[0].address;
+ break;
+ }
+ }
+
+ rp = (struct reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = 0;
+ adr[i].address = rp[i].address + base_address;
+ adr[i].size = rp[i].size;
+ ++i;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / (2 * sizeof(int));
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = openpic_to_irq(*ip++);
+ np->intrs[i].sense = *ip++;
+ }
+ }
+
+ return mem_start;
+}
+
+static unsigned long
+interpret_isa_props(struct device_node *np, unsigned long mem_start)
+{
+ struct isa_reg_property *rp;
+ struct address_range *adr;
+ int i, l, *ip;
+
+ rp = (struct isa_reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct isa_reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = rp[i].space;
+ adr[i].address = rp[i].address
+ + (adr[i].space? 0: _ISA_MEM_BASE);
+ adr[i].size = rp[i].size;
+ ++i;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / (2 * sizeof(int));
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = *ip++;
+ }
+ }
+
+ return mem_start;
+}
+
+static unsigned long
+interpret_root_props(struct device_node *np, unsigned long mem_start)
+{
+ struct reg_property *rp;
+ struct address_range *adr;
+ int i, l, *ip;
+
+ rp = (struct reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = 0;
+ adr[i].address = rp[i].address;
+ adr[i].size = rp[i].size;
+ ++i;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / sizeof(int);
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = 0;
+ }
+ }
return mem_start;
}
@@ -648,14 +909,14 @@ call_rtas(const char *service, int nargs, int nret,
printk(KERN_ERR "No RTAS service called %s\n", service);
return -1;
}
- u.words[0] = *tokp;
+ u.words[0] = __pa(*tokp);
u.words[1] = nargs;
u.words[2] = nret;
va_start(list, outputs);
for (i = 0; i < nargs; ++i)
u.words[i+3] = va_arg(list, unsigned long);
va_end(list);
- enter_rtas(&u);
+ enter_rtas((void *)__pa(&u));
if (nret > 1 && outputs != NULL)
for (i = 0; i < nret-1; ++i)
outputs[i] = u.words[i+nargs+4];
diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c
index 4f6068c6d..ce2f35058 100644
--- a/arch/ppc/kernel/ptrace.c
+++ b/arch/ppc/kernel/ptrace.c
@@ -390,8 +390,14 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
tmp = get_reg(child, addr);
}
else if (addr >= PT_FPR0 && addr <= PT_FPSCR) {
+#ifdef __SMP__
+ if (child->tss.regs->msr & MSR_FP )
+ smp_giveup_fpu(child);
+#else
+ /* only current can be last task to use math on SMP */
if (last_task_used_math == child)
giveup_fpu();
+#endif
tmp = ((long *)child->tss.fpr)[addr - PT_FPR0];
}
else
@@ -423,8 +429,13 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
goto out;
}
if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) {
+#ifndef __SMP__
+ if (child->tss.regs->msr & MSR_FP )
+ smp_giveup_fpu(child);
+#else
if (last_task_used_math == child)
giveup_fpu();
+#endif
((long *)child->tss.fpr)[addr - PT_FPR0] = data;
ret = 0;
goto out;
diff --git a/arch/ppc/kernel/residual.c b/arch/ppc/kernel/residual.c
index fdf62e921..b5516d0e5 100644
--- a/arch/ppc/kernel/residual.c
+++ b/arch/ppc/kernel/residual.c
@@ -1,5 +1,5 @@
/*
- * $Id: residual.c,v 1.5 1997/10/30 21:25:19 cort Exp $
+ * $Id: residual.c,v 1.7 1998/03/08 05:49:20 davem Exp $
*
* Code to deal with the PReP residual data.
*
diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c
index 86407e273..bdf05af76 100644
--- a/arch/ppc/kernel/setup.c
+++ b/arch/ppc/kernel/setup.c
@@ -1,5 +1,5 @@
/*
- * $Id: setup.c,v 1.48 1998/01/01 10:04:44 paulus Exp $
+ * $Id: setup.c,v 1.68 1998/04/07 08:20:33 geert Exp $
* Common prep/pmac/chrp boot and setup code.
*/
@@ -13,10 +13,26 @@
#include <asm/adb.h>
#include <asm/cuda.h>
+#include <asm/pmu.h>
#include <asm/residual.h>
#include <asm/io.h>
#include <asm/ide.h>
#include <asm/prom.h>
+#include <asm/processor.h>
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+#endif
+/* ifdef APUS specific stuff until the merge is completed. -jskov */
+#ifdef CONFIG_APUS
+#include <asm/bootinfo.h>
+#include <asm/setup.h>
+#include <asm/amigappc.h>
+extern unsigned long m68k_machtype;
+extern void amiga_reset (void);
+extern struct mem_info m68k_ramdisk;
+extern int m68k_parse_bootinfo(const struct bi_record *);
+extern char _end[];
+#endif
extern char cmd_line[512];
char saved_command_line[256];
@@ -26,13 +42,13 @@ unsigned char aux_device_present;
unsigned long ISA_DMA_THRESHOLD;
unsigned long DMA_MODE_READ, DMA_MODE_WRITE;
int _machine;
+/* if we have openfirmware */
+unsigned long have_of;
#endif /* ! CONFIG_MACH_SPECIFIC */
/* copy of the residual data */
RESIDUAL res;
int _prep_type;
-/* if we have openfirmware */
-unsigned long have_of;
/*
* Perhaps we can put the pmac screen_info[] here
@@ -40,6 +56,7 @@ unsigned long have_of;
* Until we get multiple-console support in here
* that is. -- Cort
*/
+#ifndef CONFIG_MBX
#if !defined(CONFIG_PMAC_CONSOLE)
struct screen_info screen_info = {
0, 25, /* orig-x, orig-y */
@@ -65,29 +82,66 @@ void pmac_find_display(void)
}
#endif
+#else /* CONFIG_MBX */
+
+/* We need this to satisfy some external references until we can
+ * strip the kernel down.
+ */
+struct screen_info screen_info = {
+ 0, 25, /* orig-x, orig-y */
+ { 0, 0 }, /* unused */
+ 0, /* orig-video-page */
+ 0, /* orig-video-mode */
+ 80, /* orig-video-cols */
+ 0,0,0, /* ega_ax, ega_bx, ega_cx */
+ 25, /* orig-video-lines */
+ 0, /* orig-video-isVGA */
+ 16 /* orig-video-points */
+};
+#endif /* CONFIG_MBX */
+
/* cmd is ignored for now... */
void machine_restart(char *cmd)
{
struct adb_request req;
unsigned long flags;
unsigned long i = 10000;
-#if 0
+#if 0
int err;
#endif
switch(_machine)
{
case _MACH_Pmac:
- cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM);
- for (;;)
- cuda_poll();
+ switch (adb_hardware) {
+ case ADB_VIACUDA:
+ cuda_request(&req, NULL, 2, CUDA_PACKET,
+ CUDA_RESET_SYSTEM);
+ for (;;)
+ cuda_poll();
+ break;
+ case ADB_VIAPMU:
+ pmu_request(&req, NULL, 1, PMU_RESET);
+ for (;;)
+ pmu_poll();
+ break;
+ default:
+ }
break;
+
case _MACH_chrp:
#if 0 /* RTAS doesn't seem to work on Longtrail.
For now, do it the same way as the PReP. */
- err = call_rtas("system-reboot", 0, 1, NULL);
+ /*err = call_rtas("system-reboot", 0, 1, NULL);
printk("RTAS system-reboot returned %d\n", err);
- for (;;);
+ for (;;);*/
+
+ {
+ extern unsigned int rtas_entry, rtas_data, rtas_size;
+ unsigned long status, value;
+ printk("rtas_entry: %08x rtas_data: %08x rtas_size: %08x\n",
+ rtas_entry,rtas_data,rtas_size);
+ }
#endif
case _MACH_prep:
_disable_interrupts();
@@ -104,6 +158,23 @@ void machine_restart(char *cmd)
while ( i != 0 ) i++;
panic("restart failed\n");
break;
+ case _MACH_apus:
+ cli();
+ /* APUS:FIXME: Reset the system. Apparently there's
+ * more magic to it than this!?!?
+ */
+#if 0
+ APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET);
+ APUS_WRITE(APUS_REG_RESET,
+ REGRESET_PPCRESET|REGRESET_M68KRESET|
+ REGRESET_AMIGARESET|REGRESET_AUXRESET|
+ REGRESET_SCSIRESET);
+#endif
+ printk("\n**************************************\n");
+ printk("*** You can make a hard reset now! ***\n");
+ printk("**************************************\n");
+ for(;;);
+ break;
}
}
@@ -116,9 +187,23 @@ void machine_power_off(void)
switch (_machine) {
case _MACH_Pmac:
- cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN);
- for (;;)
- cuda_poll();
+ switch (adb_hardware) {
+ case ADB_VIACUDA:
+ cuda_request(&req, NULL, 2, CUDA_PACKET,
+ CUDA_POWERDOWN);
+ for (;;)
+ cuda_poll();
+ break;
+ case ADB_VIAPMU:
+ pmu_request(&req, NULL, 5, PMU_SHUTDOWN,
+ 'M', 'A', 'T', 'T');
+ for (;;)
+ pmu_poll();
+ break;
+ default:
+ }
+ break;
+
case _MACH_chrp:
#if 0 /* RTAS doesn't seem to work on Longtrail.
For now, do it the same way as the PReP. */
@@ -126,9 +211,19 @@ void machine_power_off(void)
printk("RTAS system-reboot returned %d\n", err);
for (;;);
#endif
+
case _MACH_prep:
machine_restart(NULL);
+#ifdef CONFIG_APUS
+ case _MACH_apus:
+#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
+ apm_set_power_state(APM_STATE_OFF);
+ for (;;);
+#endif
+#endif
}
+ for (;;)
+ ;
}
void machine_halt(void)
@@ -141,18 +236,90 @@ void machine_halt(void)
machine_power_off(); /* for now */
#endif
}
- else /* prep or chrp */
+ else /* prep, chrp or apus */
machine_restart(NULL);
}
+#ifdef CONFIG_BLK_DEV_IDE
void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)
{
- if ( _machine == _MACH_Pmac )
+ switch (_machine) {
+ case _MACH_Pmac:
pmac_ide_init_hwif_ports(p,base,irq);
- else /* prep or chrp */
+ break;
+ case _MACH_chrp:
+ chrp_ide_init_hwif_ports(p,base,irq);
+ break;
+ case _MACH_prep:
prep_ide_init_hwif_ports(p,base,irq);
+ break;
+ }
+}
+#endif
+
+unsigned long cpu_temp(void)
+{
+ unsigned long i, temp, thrm1, dir;
+ int sanity;
+ /*
+ * setup thrm3 - need to give TAU at least 20us
+ * to do the compare so assume a 300MHz clock.
+ * We need 300*20 ticks then.
+ * -- Cort
+ */
+ asm("mtspr 1020, %1\n\t"
+ "mtspr 1021, %1\n\t"
+ "mtspr 1022, %0\n\t"::
+ "r" ( ((300*20)<<18) | THRM3_E), "r" (0) );
+
+#if 0
+ for ( i = 127 ; i >= 0 ; i-- )
+ {
+ asm("mtspr 1020, %0\n\t"::
+ "r" (THRM1_TID|THRM1_V|(i<<2)) );
+ /* check value */
+ while ( !( thrm1 & THRM1_TIV) )
+ asm("mfspr %0, 1020 \n\t": "=r" (thrm1) );
+ if ( thrm1 & THRM1_TIN )
+ {
+ printk("tin set: %x tiv %x\n", thrm1,thrm1&THRM1_TIV);
+ goto out;
+ }
+
+ }
+#endif
+#if 0
+ i = 32; /* increment */
+ dir = 1; /* direction we're checking 0=up 1=down */
+ temp = 64; /* threshold checking against */
+ while ( i )
+ {
+ _set_THRM1((1<<29) | THRM1_V | (temp<<2) );
+ printk("checking %d in dir %d thrm set to %x/%x\n", temp,dir,
+ ( (1<<29) | THRM1_V | (temp<<2)),_get_THRM1());
+ /* check value */
+ sanity = 0x0fffffff;
+ while ( (!( thrm1 & THRM1_TIV)) && (sanity--) )
+ thrm1 = _get_THRM1();
+ /*asm("mfspr %0, 1020 \n\t": "=r" (thrm1) );*/
+ if ( ! sanity || sanity==0xffffffff ) printk("no sanity\n");
+ /* temp is not in that direction */
+ if ( !(thrm1 & THRM1_TIN) )
+ {
+ printk("not in that dir thrm1 %x\n",thrm1);
+ if ( dir == 0 ) dir = 1;
+ else dir = 0;
+ }
+ if ( dir ) temp -= i;
+ else temp += i;
+ i /= 2;
+ }
+ asm("mtspr 1020, %0\n\t"
+ "mtspr 1022, %0\n\t" ::"r" (0) );
+#endif
+ return temp;
}
int get_cpuinfo(char *buffer)
@@ -160,9 +327,11 @@ int get_cpuinfo(char *buffer)
extern int pmac_get_cpuinfo(char *);
extern int chrp_get_cpuinfo(char *);
extern int prep_get_cpuinfo(char *);
+ extern int apus_get_cpuinfo(char *);
unsigned long len = 0;
unsigned long bogosum = 0;
unsigned long i;
+ unsigned long cr;
#ifdef __SMP__
extern unsigned long cpu_present_map;
extern struct cpuinfo_PPC cpu_data[NR_CPUS];
@@ -202,7 +371,18 @@ int get_cpuinfo(char *buffer)
len += sprintf(len+buffer, "603ev\n");
break;
case 8:
- len += sprintf(len+buffer, "750 (Arthur)\n");
+ len += sprintf(len+buffer,"750\n");
+ cr = _get_L2CR();
+ len += sprintf(len+buffer,"L2CR\t\t: %lx\n",cr);
+ if ( cr & (0x1<<1)) cr = 256;
+ else if ( cr & (0x2<<1)) cr = 512;
+ else if ( cr & (0x3<<1)) cr = 1024;
+ else cr = 0;
+ len += sprintf(len+buffer,"on-chip l2\t: "
+ "%ld KB (%s)\n",
+ cr,(_get_L2CR()&1) ? "on" : "off");
+ len += sprintf(len+buffer,"temperature \t: %lu C\n",
+ cpu_temp());
break;
case 9:
len += sprintf(len+buffer, "604e\n");
@@ -216,6 +396,7 @@ int get_cpuinfo(char *buffer)
break;
}
+
/*
* Assume here that all clock rates are the same in a
* smp system. -- Cort
@@ -290,6 +471,11 @@ int get_cpuinfo(char *buffer)
case _MACH_chrp:
len += chrp_get_cpuinfo(buffer+len);
break;
+#ifdef CONFIG_APUS
+ case _MACH_apus:
+ len += apus_get_cpuinfo(buffer+len);
+ break;
+#endif
}
return len;
}
@@ -302,43 +488,63 @@ __initfunc(unsigned long
identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7))
{
- extern unsigned long initrd_start, initrd_end;
extern setup_pci_ptrs(void);
- unsigned long boot_sdr1;
- ihandle prom_root;
- unsigned char type[16], model[16];
-
- asm("mfspr %0,25\n\t" :"=r" (boot_sdr1));
+#ifndef CONFIG_MBX8xx
- /*
- * if we have a sdr1 then we have openfirmware
- * and can ask it what machine we are (chrp/pmac/prep).
- * otherwise we're definitely prep. -- Cort
- */
- if ( !boot_sdr1 )
+#ifdef CONFIG_APUS
+ if ( r3 == 0x61707573 )
{
- /* we know for certain we're prep if no OF */
+ /* Parse bootinfo. The bootinfo is located right after
+ the kernel bss */
+ m68k_parse_bootinfo((const struct bi_record *)&_end);
+
have_of = 0;
- /* make a copy of residual data */
- if ( r3 )
- memcpy((void *)&res,(void *)(r3+KERNELBASE),
- sizeof(RESIDUAL));
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ /* Take care of initrd if we have one. Use data from
+ bootinfo to avoid the need to initialize PPC
+ registers when kernel is booted via a PPC reset. */
+ if ( m68k_ramdisk.addr ) {
+ initrd_start = (unsigned long) __va(m68k_ramdisk.addr);
+ initrd_end = (unsigned long)
+ __va(m68k_ramdisk.size + m68k_ramdisk.addr);
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+ return 0;
+ }
+#endif
+
#ifndef CONFIG_MACH_SPECIFIC
+ /* prep boot loader tells us if we're prep or not */
+ if ( *(unsigned long *)(KERNELBASE) == (0xdeadc0de) )
+ {
_machine = _MACH_prep;
-#endif /* CONFIG_MACH_SPECIFIC */
+ have_of = 0;
+ } else
+ {
+ /* need to ask OF if we're chrp or pmac */
+ extern unsigned char OF_type[16], OF_model[16];
+ prom_print(OF_type);
+ prom_print(OF_model);
+ if ( !strncmp("chrp", OF_type,4) )
+ {
+ _machine = _MACH_chrp;
+ }
+ else
+ {
+ /*if ( !strncmp("Power Macintosh", type,15) )*/
+ _machine = _MACH_Pmac;
+ }
+ _machine = _MACH_Pmac;
+
}
- else
+#endif /* CONFIG_MACH_SPECIFIC */
+
+ if ( have_of )
{
- /*
- * init prom here, then ask the openfirmware
- * what machine we are (prep/chrp/pmac). We don't use
- * OF on prep just yet. -- Cort
- */
-#ifndef CONFIG_PREP /* don't use OF on prep yet */
- have_of = 1;
/* prom_init has already been called from __start */
finish_device_tree();
-
/*
* If we were booted via quik, r3 points to the physical
* address of the command-line parameters.
@@ -356,7 +562,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
} else {
struct device_node *chosen;
char *p;
-
+
#ifdef CONFIG_BLK_DEV_INITRD
if (r3 - KERNELBASE < 0x800000
&& r4 != 0 && r4 != 0xdeadbeef) {
@@ -365,7 +571,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
}
#endif
- chosen = find_path_device("/chosen");
+ chosen = find_devices("chosen");
if (chosen != NULL) {
p = get_property(chosen, "bootargs", NULL);
if (p != NULL)
@@ -373,47 +579,18 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
}
}
cmd_line[sizeof(cmd_line) - 1] = 0;
-#endif /* CONFIG_PREP */
-
-#ifndef CONFIG_MACH_SPECIFIC
-#if 0
- prom_root = call_prom("finddevice", 1, 1, "/");
- call_prom("getprop", 4, 1, prom_root, "device_type", &type,
- (void *) sizeof(type));
- call_prom("getprop", 4, 1, prom_root, "model", &type,
- (void *) sizeof(model));
- if ( !strncmp("chrp", type,4) )
- {
- _machine = _MACH_chrp;
- }
- else
- {
- /*if ( !strncmp("Power Macintosh", type,15) )*/
- _machine = _MACH_Pmac;
- }
-#else
-
-#ifdef CONFIG_CHRP
- _machine = _MACH_chrp;
-#endif /* CONFIG_CHRP */
-#ifdef CONFIG_PMAC
- _machine = _MACH_Pmac;
-#endif /* CONFIG_PMAC */
-#ifdef CONFIG_PREP
- _machine = _MACH_Prep;
-#endif /* CONFIG_PREP */
-#endif /* #if */
-#endif /* CONFIG_MACH_SPECIFIC */
}
+#ifdef CONFIG_PCI
/* so that pmac/chrp can use pci to find its console -- Cort */
setup_pci_ptrs();
-
+#endif
+
switch (_machine)
{
case _MACH_Pmac:
#if !defined(CONFIG_MACH_SPECIFIC)
- isa_io_base = PMAC_ISA_IO_BASE;
+ /* isa_io_base gets set in pmac_find_bridges */
isa_mem_base = PMAC_ISA_MEM_BASE;
pci_dram_offset = PMAC_PCI_DRAM_OFFSET;
ISA_DMA_THRESHOLD = ~0L;
@@ -422,6 +599,10 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
#endif /* ! CONFIG_MACH_SPECIFIC */
break;
case _MACH_prep:
+ /* make a copy of residual data */
+ if ( r3 )
+ memcpy((void *)&res,(void *)(r3+KERNELBASE),
+ sizeof(RESIDUAL));
#if !defined(CONFIG_MACH_SPECIFIC)
isa_io_base = PREP_ISA_IO_BASE;
isa_mem_base = PREP_ISA_MEM_BASE;
@@ -434,13 +615,12 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
if ( res.ResidualLength != 0 )
{
if ( !strncmp(res.VitalProductData.PrintableModel,"IBM",3) )
- _prep_type = 0x00;
+ _prep_type = _PREP_IBM;
else
- _prep_type = 0x01;
+ _prep_type = _PREP_Motorola;
}
else /* assume motorola if no residual (netboot?) */
_prep_type = _PREP_Motorola;
-
#ifdef CONFIG_BLK_DEV_RAM
/* take care of initrd if we have one */
if ( r4 )
@@ -457,7 +637,14 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
}
break;
case _MACH_chrp:
- /* LongTrail */
+#ifdef CONFIG_BLK_DEV_RAM
+ /* take care of initrd if we have one */
+ if ( r3 )
+ {
+ initrd_start = r3 + KERNELBASE;
+ initrd_end = r3+ r4 + KERNELBASE;
+ }
+#endif /* CONFIG_BLK_DEV_RAM */
#if !defined(CONFIG_MACH_SPECIFIC)
isa_io_base = CHRP_ISA_IO_BASE;
isa_mem_base = CHRP_ISA_MEM_BASE;
@@ -470,13 +657,33 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5,
default:
printk("Unknown machine type in identify_machine!\n");
}
- return 0;
-}
+#else /* CONFIG_MBX8xx */
+ extern setup_pci_ptrs(void);
-__initfunc(unsigned long
-bios32_init(unsigned long memory_start, unsigned long memory_end))
-{
- return memory_start;
+ if ( r3 )
+ memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(bd_t) );
+
+ setup_pci_ptrs();
+
+#ifdef CONFIG_BLK_DEV_RAM
+ /* take care of initrd if we have one */
+ if ( r4 )
+ {
+ initrd_start = r4 + KERNELBASE;
+ initrd_end = r5 + KERNELBASE;
+ }
+#endif /* CONFIG_BLK_DEV_RAM */
+ /* take care of cmd line */
+ if ( r6 )
+ {
+
+ *(char *)(r7+KERNELBASE) = 0;
+ strcpy(cmd_line, (char *)(r6+KERNELBASE));
+ }
+
+#endif /* CONFIG_MBX */
+
+ return 0;
}
__initfunc(void setup_arch(char **cmdline_p,
@@ -485,12 +692,18 @@ __initfunc(void setup_arch(char **cmdline_p,
extern void pmac_setup_arch(unsigned long *, unsigned long *);
extern void chrp_setup_arch(unsigned long *, unsigned long *);
extern void prep_setup_arch(unsigned long *, unsigned long *);
+ extern void apus_setup_arch(char **, unsigned long *, unsigned long *);
extern int panic_timeout;
extern char _etext[], _edata[];
extern char *klimit;
extern unsigned long find_available_memory(void);
extern unsigned long *end_of_DRAM;
+#ifdef CONFIG_XMON
+ extern void xmon_map_scc(void);
+ xmon_map_scc();
+#endif /* CONFIG_XMON */
+
/* reboot on panic */
panic_timeout = 180;
@@ -516,6 +729,12 @@ __initfunc(void setup_arch(char **cmdline_p,
case _MACH_chrp:
chrp_setup_arch(memory_start_p, memory_end_p);
break;
+#ifdef CONFIG_APUS
+ case _MACH_apus:
+ m68k_machtype = MACH_AMIGA;
+ apus_setup_arch(cmdline_p,memory_start_p,memory_end_p);
+ break;
+#endif
default:
printk("Unknown machine %d in setup_arch()\n", _machine);
}
diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c
index ae6d3c5aa..1c977bb51 100644
--- a/arch/ppc/kernel/signal.c
+++ b/arch/ppc/kernel/signal.c
@@ -201,8 +201,13 @@ int sys_sigreturn(struct pt_regs *regs)
if (sc == (struct sigcontext_struct *)(sigctx.regs)) {
/* Last stacked signal - restore registers */
sr = (struct sigregs *) sigctx.regs;
+#ifdef __SMP__
+ if ( regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
if (last_task_used_math == current)
giveup_fpu();
+#endif
if (copy_from_user(saved_regs, &sr->gp_regs,
sizeof(sr->gp_regs)))
goto badframe;
@@ -249,8 +254,13 @@ setup_frame(struct pt_regs *regs, struct sigregs *frame,
if (verify_area(VERIFY_WRITE, frame, sizeof(*frame)))
goto badframe;
- if (last_task_used_math == current)
- giveup_fpu();
+#ifdef __SMP__
+ if ( regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
+ if (last_task_used_math == current)
+ giveup_fpu();
+#endif
if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE)
|| __copy_to_user(&frame->fp_regs, current->tss.fpr,
ELF_NFPREG * sizeof(double))
diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c
index 33d3a23b4..217e695f8 100644
--- a/arch/ppc/kernel/smp.c
+++ b/arch/ppc/kernel/smp.c
@@ -1,5 +1,5 @@
/*
- * $Id: smp.c,v 1.8 1998/01/06 06:44:57 cort Exp $
+ * $Id: smp.c,v 1.22 1998/04/10 01:53:34 cort Exp $
*
* Smp support for ppc.
*
@@ -30,22 +30,27 @@
#include <asm/init.h>
#include <asm/io.h>
+#include "time.h"
+
int smp_threads_ready = 0;
volatile int smp_commenced = 0;
int smp_num_cpus = 1;
unsigned long cpu_present_map = 0;
volatile int cpu_number_map[NR_CPUS];
volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
+volatile int __cpu_logical_map[NR_CPUS];
static unsigned char boot_cpu_id = 0;
struct cpuinfo_PPC cpu_data[NR_CPUS];
-struct klock_info klock_info = { KLOCK_CLEAR, 0 };
+struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 };
volatile unsigned char active_kernel_processor = NO_PROC_ID; /* Processor holding kernel spinlock */
-volatile unsigned long hash_table_lock;
+volatile unsigned long ipi_count;
+
+unsigned int prof_multiplier[NR_CPUS];
+unsigned int prof_counter[NR_CPUS];
int start_secondary(void *);
-extern void init_IRQ(void);
extern int cpu_idle(void *unused);
void smp_boot_cpus(void)
@@ -56,49 +61,75 @@ void smp_boot_cpus(void)
struct task_struct *p;
printk("Entering SMP Mode...\n");
+
+ for (i = 0; i < NR_CPUS; i++) {
+ cpu_number_map[i] = -1;
+ prof_counter[i] = 1;
+ prof_multiplier[i] = 1;
+ }
+
cpu_present_map = 0;
for(i=0; i < NR_CPUS; i++)
- cpu_number_map[i] = -1;
+ __cpu_logical_map[i] = -1;
smp_store_cpu_info(boot_cpu_id);
active_kernel_processor = boot_cpu_id;
current->processor = boot_cpu_id;
-
cpu_present_map |= 1;
+ cpu_number_map[boot_cpu_id] = 0;
+ __cpu_logical_map[0] = boot_cpu_id;
+
+ if ( _machine != _MACH_Pmac )
+ {
+ printk("SMP not supported on this machine.\n");
+ return;
+ }
+
/* assume a 2nd processor for now */
cpu_present_map |= (1 << 1);
smp_num_cpus = 2;
- cpu_number_map[boot_cpu_id] = 0;
/* create a process for second processor */
kernel_thread(start_secondary, NULL, CLONE_PID);
- cpu_number_map[1] = 1;
p = task[1];
if ( !p )
panic("No idle task for secondary processor\n");
p->processor = 1;
current_set[1] = p;
+ /* need to flush here since secondary bat's aren't setup */
+ dcbf((volatile unsigned long *)&current_set[1]);
+
/* setup entry point of secondary processor */
*(volatile unsigned long *)(0xf2800000)
= (unsigned long)secondary_entry-KERNELBASE;
- /* interrupt secondary to begin executing code */
eieio();
- *(volatile unsigned long *)(0xf80000c0) = 0;
+ /* interrupt secondary to begin executing code */
+ *(volatile unsigned long *)(0xf80000c0) = 0L;
eieio();
/* wait to see if the secondary made a callin (is actually up) */
- for ( timeout = 0; timeout < 1500 ; timeout++ )
+ for ( timeout = 0; timeout < 15000 ; timeout++ )
+ {
+ if(cpu_callin_map[1])
+ break;
udelay(100);
+ }
if(cpu_callin_map[1]) {
cpu_number_map[1] = 1;
+ __cpu_logical_map[i] = 1;
printk("Processor 1 found.\n");
+
+#if 0 /* this sync's the decr's */
+ set_dec(decrementer_count);
+#endif
+ /* interrupt secondary to sync the time bases */
+ smp_message_pass(1,0xf0f0, 0, 0);
+ /* interrupt secondary to begin executing code */
+ /**(volatile unsigned long *)(0xf80000c0) = 0L;
+ eieio();*/
} else {
smp_num_cpus--;
printk("Processor %d is stuck.\n", 1);
}
-{
- extern unsigned long amhere;
- printk("amhere: %x\n", amhere);
-}
}
void smp_commence(void)
@@ -128,16 +159,15 @@ int start_secondary(void *unused)
void smp_callin(void)
{
printk("SMP %d: smp_callin()\n",current->processor);
- /*calibrate_delay();*/
smp_store_cpu_info(1);
-
- /* assume we're just the secondary processor for now */
- cpu_callin_map[1] = 1;
- dcbf(&cpu_callin_map[1]);
+ set_dec(decrementer_count);
current->mm->mmap->vm_page_prot = PAGE_SHARED;
current->mm->mmap->vm_start = PAGE_OFFSET;
current->mm->mmap->vm_end = init_task.mm->mmap->vm_end;
+
+ /* assume we're just the secondary processor for now */
+ cpu_callin_map[1] = 1;
while(!smp_commenced)
barrier();
@@ -149,21 +179,120 @@ void smp_setup(char *str, int *ints)
printk("SMP %d: smp_setup()\n",current->processor);
}
-void smp_message_pass(int target, int msg, unsigned long data, int wait)
+void smp_local_timer_interrupt(struct pt_regs * regs)
{
- printk("SMP %d: sending smp message\n",current->processor);
-#if 0
- if ( smp_processor_id() == 0 )
+ int cpu = smp_processor_id();
+ extern void update_one_process(struct task_struct *,unsigned long,
+ unsigned long,unsigned long,int);
+
+ if (!--prof_counter[cpu]) {
+ int user=0,system=0;
+ struct task_struct * p = current;
+
+ /*
+ * After doing the above, we need to make like
+ * a normal interrupt - otherwise timer interrupts
+ * ignore the global interrupt lock, which is the
+ * WrongThing (tm) to do.
+ */
+
+ if (user_mode(regs))
+ user=1;
+ else
+ system=1;
+
+ if (p->pid) {
+ update_one_process(p, 1, user, system, cpu);
+
+ p->counter -= 1;
+ if (p->counter < 0) {
+ p->counter = 0;
+ need_resched = 1;
+ }
+ if (p->priority < DEF_PRIORITY) {
+ kstat.cpu_nice += user;
+ kstat.per_cpu_nice[cpu] += user;
+ } else {
+ kstat.cpu_user += user;
+ kstat.per_cpu_user[cpu] += user;
+ }
+
+ kstat.cpu_system += system;
+ kstat.per_cpu_system[cpu] += system;
+
+ }
+ prof_counter[cpu]=prof_multiplier[cpu];
+ }
+}
+
+/*
+ * Dirty hack to get smp message passing working.
+ * Right now it only works for stop cpu's but will be setup
+ * later for more general message passing.
+ *
+ * As it is now, if we're sending two message as the same time
+ * we have race conditions. I avoided doing locks here since
+ * all that works right now is the stop cpu message.
+ *
+ * -- Cort
+ */
+int smp_message[NR_CPUS];
+void smp_message_recv(void)
+{
+ int msg = smp_message[smp_processor_id()];
+
+ printk("SMP %d: smp_message_recv() msg %x\n", smp_processor_id(),msg);
+
+ /* make sure msg is for us */
+ if ( msg == -1 ) return;
+printk("recv after msg check\n");
+ switch( msg )
{
- /* interrupt secondary */
- *(volatile unsigned long *)(0xf80000c0) = 0;
+ case MSG_STOP_CPU:
+ __cli();
+ while (1) ;
+ break;
+ case 0xf0f0: /* syncing time bases - just return */
+ break;
+ default:
+ printk("SMP %d: smp_message_recv(): unknown msg %d\n",
+ smp_processor_id(), msg);
+ break;
}
- else
+ /* reset message */
+ smp_message[smp_processor_id()] = -1;
+}
+
+spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED;
+void smp_message_pass(int target, int msg, unsigned long data, int wait)
+{
+ printk("SMP %d: sending smp message\n", current->processor);
+
+ spin_lock(&mesg_pass_lock);
+ if ( _machine != _MACH_Pmac )
+ return;
+
+#define OTHER (~smp_processor_id() & 1)
+
+ switch( target )
{
- /* interrupt primary */
- *(volatile unsigned long *)(0xf3019000);
+ case MSG_ALL:
+ smp_message[smp_processor_id()] = msg;
+ /* fall through */
+ case MSG_ALL_BUT_SELF:
+ smp_message[OTHER] = msg;
+ break;
+ default:
+ smp_message[target] = msg;
+ break;
}
-#endif
+ /* interrupt secondary processor */
+ /**(volatile unsigned long *)(0xf80000c0) = 0xffffffff;
+ eieio();*/
+ *(volatile unsigned long *)(0xf80000c0) = 0;
+ /* interrupt primary */
+ /**(volatile unsigned long *)(0xf3019000);*/
+ spin_unlock(&mesg_pass_lock);
}
int setup_profiling_timer(unsigned int multiplier)
diff --git a/arch/ppc/kernel/softemu8xx.c b/arch/ppc/kernel/softemu8xx.c
new file mode 100644
index 000000000..dedf24806
--- /dev/null
+++ b/arch/ppc/kernel/softemu8xx.c
@@ -0,0 +1,96 @@
+/*
+ * Software emulation of some PPC instructions for the 8xx core.
+ *
+ * Copyright (C) 1998 Dan Malek (dmalek@jlc.net)
+ *
+ * Software floating emuation for the MPC8xx processor. I did this mostly
+ * because it was easier than trying to get the libraries compiled for
+ * software floating point. The goal is still to get the libraries done,
+ * but I lost patience and needed some hacks to at least get init and
+ * shells running. The first problem is the setjmp/longjmp that save
+ * and restore the floating point registers.
+ *
+ * For this emulation, our working registers are found on the register
+ * save area.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+
+/* Eventually we may need a look-up table, but this works for now.
+*/
+#define LFD 50
+#define LFDU 51
+#define STFD 54
+#define STFDU 55
+
+/*
+ * We return 0 on success, 1 on unimplemented instruction, and EFAULT
+ * if a load/store faulted.
+ */
+int
+Soft_emulate_8xx(struct pt_regs *regs)
+{
+ uint inst, instword;
+ uint flreg, idxreg, disp;
+ uint retval;
+ uint *ea, *ip;
+
+ retval = 0;
+
+ instword = *((uint *)regs->nip);
+ inst = instword >> 26;
+
+ flreg = (instword >> 21) & 0x1f;
+ idxreg = (instword >> 16) & 0x1f;
+ disp = instword & 0xffff;
+
+ ea = (uint *)(regs->gpr[idxreg] + disp);
+ ip = (uint *)&current->tss.fpr[flreg];
+
+ if (inst == LFD) {
+ if (copy_from_user(ip, ea, sizeof(double)))
+ retval = EFAULT;
+ }
+ else if (inst == LFDU) {
+
+ if (copy_from_user(ip, ea, sizeof(double)))
+ retval = EFAULT;
+ else
+ regs->gpr[idxreg] = (uint)ea;
+ }
+ else if (inst == STFD) {
+
+ if (copy_to_user(ea, ip, sizeof(double)))
+ retval = EFAULT;
+ }
+ else if (inst == STFDU) {
+
+ if (copy_to_user(ea, ip, sizeof(double)))
+ retval = EFAULT;
+ else
+ regs->gpr[idxreg] = (uint)ea;
+ }
+ else {
+ retval = 1;
+ }
+
+ if (retval == 0)
+ regs->nip += 4;
+ return(retval);
+}
diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c
index 319db15de..5a1063d4e 100644
--- a/arch/ppc/kernel/time.c
+++ b/arch/ppc/kernel/time.c
@@ -1,11 +1,25 @@
/*
- * $Id: time.c,v 1.17 1997/12/28 22:47:21 paulus Exp $
+ * $Id: time.c,v 1.28 1998/04/07 18:49:49 cort Exp $
* Common time routines among all ppc machines.
*
* Written by Cort Dougan (cort@cs.nmt.edu) to merge
* Paul Mackerras' version and mine for PReP and Pmac.
+ * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net).
+ *
+ * Since the MPC8xx has a programmable interrupt timer, I decided to
+ * use that rather than the decrementer. Two reasons: 1.) the clock
+ * frequency is low, causing 2.) a long wait in the timer interrupt
+ * while ((d = get_dec()) == dval)
+ * loop. The MPC8xx can be driven from a variety of input clocks,
+ * so a number of assumptions have been made here because the kernel
+ * parameter HZ is a constant. We assume (correctly, today :-) that
+ * the MPC8xx on the MBX board is driven from a 32.768 kHz crystal.
+ * This is then divided by 4, providing a 8192 Hz clock into the PIT.
+ * Since it is not possible to get a nice 100 Hz clock out of this, without
+ * creating a software PLL, I have set HZ to 128. -- Dan
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -22,12 +36,21 @@
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/nvram.h>
+#include <asm/cache.h>
+#ifdef CONFIG_MBX
+#include <asm/mbx.h>
+#endif
+#ifdef CONFIG_8xx
+#include <asm/8xx_immap.h>
+#endif
#include "time.h"
/* this is set to the appropriate pmac/prep/chrp func in init_IRQ() */
int (*set_rtc_time)(unsigned long);
+void smp_local_timer_interrupt(struct pt_regs *);
+
/* keep track of when we need to update the rtc */
unsigned long last_rtc_update = 0;
@@ -48,6 +71,14 @@ unsigned count_period_den; /* count_period_num / count_period_den us */
void timer_interrupt(struct pt_regs * regs)
{
int dval, d;
+ unsigned long cpu = smp_processor_id();
+ /* save the HID0 in case dcache was off - see idle.c
+ * this hack should leave for a better solution -- Cort */
+ unsigned dcache_locked = unlock_dcache();
+
+if ( smp_processor_id() ) printk("SMP 1: timer intr\n");
+ hardirq_enter(cpu);
+
while ((dval = get_dec()) < 0) {
/*
* Wait for the decrementer to change, then jump
@@ -57,17 +88,49 @@ void timer_interrupt(struct pt_regs * regs)
while ((d = get_dec()) == dval)
;
set_dec(d + decrementer_count);
- do_timer(regs);
- /*
- * update the rtc when needed
- */
- if ( xtime.tv_sec > last_rtc_update + 660 )
- if (set_rtc_time(xtime.tv_sec) == 0)
- last_rtc_update = xtime.tv_sec;
- else
- last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ if ( !smp_processor_id() )
+ {
+ do_timer(regs);
+ /*
+ * update the rtc when needed
+ */
+ if ( xtime.tv_sec > last_rtc_update + 660 )
+ if (set_rtc_time(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+ }
}
+#ifdef __SMP__
+ smp_local_timer_interrupt(regs);
+#endif
+
+ hardirq_exit(cpu);
+ /* restore the HID0 in case dcache was off - see idle.c
+ * this hack should leave for a better solution -- Cort */
+ lock_dcache(dcache_locked);
+}
+
+#ifdef CONFIG_MBX
+/* A place holder for time base interrupts, if they are ever enabled.
+*/
+void timebase_interrupt(int irq, void * dev, struct pt_regs * regs)
+{
+}
+
+/* The RTC on the MPC8xx is an internal register.
+ * We want to protect this during power down, so we need to unlock,
+ * modify, and re-lock.
+ */
+static int
+mbx_set_rtc_time(unsigned long time)
+{
+ ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY;
+ ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc = time;
+ ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY;
+ return(0);
}
+#endif /* CONFIG_MBX */
/*
* This version of gettimeofday has microsecond resolution.
@@ -108,6 +171,7 @@ void do_settimeofday(struct timeval *tv)
void
time_init(void)
{
+#ifndef CONFIG_MBX
if ((_get_PVR() >> 16) == 1) {
/* 601 processor: dec counts down by 128 every 128ns */
decrementer_count = DECREMENTER_COUNT_601;
@@ -119,9 +183,10 @@ time_init(void)
case _MACH_Pmac:
/* can't call pmac_get_rtc_time() yet,
because via-cuda isn't initialized yet. */
- if ((_get_PVR() >> 16) != 1)
+ if ( (_get_PVR() >> 16) != 1 && (!smp_processor_id()) )
pmac_calibrate_decr();
- set_rtc_time = pmac_set_rtc_time;
+ if ( !smp_processor_id() )
+ set_rtc_time = pmac_set_rtc_time;
break;
case _MACH_chrp:
chrp_time_init();
@@ -135,18 +200,63 @@ time_init(void)
prep_calibrate_decr();
set_rtc_time = prep_set_rtc_time;
break;
+/* ifdef APUS specific stuff until the merge is completed. -jskov */
+#ifdef CONFIG_APUS
+ case _MACH_apus:
+ {
+ xtime.tv_sec = apus_get_rtc_time();
+ apus_calibrate_decr();
+ set_rtc_time = apus_set_rtc_time;
+ break;
}
+#endif
+ }
+ xtime.tv_usec = 0;
+ set_dec(decrementer_count);
+#else
+ mbx_calibrate_decr();
+ set_rtc_time = mbx_set_rtc_time;
+
+ /* First, unlock all of the registers we are going to modify.
+ * To protect them from corruption during power down, registers
+ * that are maintained by keep alive power are "locked". To
+ * modify these registers we have to write the key value to
+ * the key location associated with the register.
+ */
+ ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY;
+ ((immap_t *)MBX_IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY;
+
+
+ /* Disable the RTC one second and alarm interrupts.
+ */
+ ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtcsc &=
+ ~(RTCSC_SIE | RTCSC_ALE);
+
+ /* Enabling the decrementer also enables the timebase interrupts
+ * (or from the other point of view, to get decrementer interrupts
+ * we have to enable the timebase). The decrementer interrupt
+ * is wired into the vector table, nothing to do here for that.
+ */
+ ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_tbscr =
+ ((mk_int_int_mask(DEC_INTERRUPT) << 8) |
+ (TBSCR_TBF | TBSCR_TBE));
+ if (request_irq(DEC_INTERRUPT, timebase_interrupt, 0, "tbint", NULL) != 0)
+ panic("Could not allocate timer IRQ!");
+
+ /* Get time from the RTC.
+ */
+ xtime.tv_sec = ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc;
xtime.tv_usec = 0;
- /*
- * mark the rtc/on-chip timer as in sync
+#endif /* CONFIG_MBX */
+
+ /* mark the rtc/on-chip timer as in sync
* so we don't update right away
*/
last_rtc_update = xtime.tv_sec;
-
- set_dec(decrementer_count);
}
+#ifndef CONFIG_MBX
/*
* Uses the on-board timer to calibrate the on-chip decrementer register
* for prep systems. On the pmac the OF tells us what the frequency is
@@ -158,6 +268,27 @@ volatile int *done_ptr = &calibrate_done;
void prep_calibrate_decr(void)
{
unsigned long flags;
+
+ /* the Powerstack II's have trouble with the timer so
+ * we use a default value -- Cort
+ */
+ if ( (_prep_type == _PREP_Motorola) &&
+ ((inb(0x800) & 0xF0) & 0x40) )
+ {
+ unsigned long freq, divisor;
+ static unsigned long t2 = 0;
+
+ t2 = 998700000/60;
+ freq = t2 * 60; /* try to make freq/1e6 an integer */
+ divisor = 60;
+ printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n",
+ freq, divisor,t2>>20);
+ decrementer_count = freq / HZ / divisor;
+ count_period_num = divisor;
+ count_period_den = freq / 1000000;
+ return;
+ }
+
save_flags(flags);
@@ -181,7 +312,7 @@ void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs)
{
unsigned long freq, divisor;
static unsigned long t1 = 0, t2 = 0;
-
+
if ( !t1 )
t1 = get_dec();
else if (!t2)
@@ -189,11 +320,6 @@ void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs)
t2 = get_dec();
t2 = t1-t2; /* decr's in 1/HZ */
t2 = t2*HZ; /* # decrs in 1s - thus in Hz */
-if ( (t2>>20) > 100 )
-{
- printk("Decrementer frequency too high: %luMHz. Using 15MHz.\n",t2>>20);
- t2 = 998700000/60;
-}
freq = t2 * 60; /* try to make freq/1e6 an integer */
divisor = 60;
printk("time_init: decrementer frequency = %lu/%lu (%luMHz)\n",
@@ -205,6 +331,32 @@ if ( (t2>>20) > 100 )
}
}
+#else /* CONFIG_MBX */
+
+/* The decrementer counts at the system (internal) clock frequency divided by
+ * sixteen, or external oscillator divided by four. Currently, we only
+ * support the MBX, which is system clock divided by sixteen.
+ */
+void mbx_calibrate_decr(void)
+{
+ int freq, fp, divisor;
+
+ if ((((immap_t *)MBX_IMAP_ADDR)->im_clkrst.car_sccr & 0x02000000) == 0)
+ printk("WARNING: Wrong decrementer source clock.\n");
+
+ /* The manual says the frequency is in Hz, but it is really
+ * as MHz. The value 'fp' is the number of decrementer ticks
+ * per second.
+ */
+ /*fp = (mbx_board_info.bi_intfreq * 1000000) / 16;*/
+ freq = fp*60; /* try to make freq/1e6 an integer */
+ divisor = 60;
+ printk("time_init: decrementer frequency = %d/%d\n", freq, divisor);
+ decrementer_count = freq / HZ / divisor;
+ count_period_num = divisor;
+ count_period_den = freq / 1000000;
+}
+#endif /* CONFIG_MBX */
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
diff --git a/arch/ppc/kernel/time.h b/arch/ppc/kernel/time.h
index 538ac7bb1..64a07250c 100644
--- a/arch/ppc/kernel/time.h
+++ b/arch/ppc/kernel/time.h
@@ -1,5 +1,5 @@
/*
- * $Id: time.h,v 1.7 1997/12/28 22:47:24 paulus Exp $
+ * $Id: time.h,v 1.10 1998/04/01 07:46:03 geert Exp $
* Common time prototypes and such for all ppc machines.
*
* Written by Cort Dougan (cort@cs.nmt.edu) to merge
@@ -12,6 +12,7 @@
void prep_calibrate_decr_handler(int, void *,struct pt_regs *);
void prep_calibrate_decr(void);
void pmac_calibrate_decr(void);
+extern void apus_calibrate_decr(void);
extern unsigned decrementer_count;
extern unsigned count_period_num;
extern unsigned count_period_den;
@@ -24,12 +25,16 @@ extern unsigned long last_rtc_update;
unsigned long prep_get_rtc_time(void);
unsigned long pmac_get_rtc_time(void);
unsigned long chrp_get_rtc_time(void);
+unsigned long apus_get_rtc_time(void);
int prep_set_rtc_time(unsigned long nowtime);
int pmac_set_rtc_time(unsigned long nowtime);
int chrp_set_rtc_time(unsigned long nowtime);
+int apus_set_rtc_time(unsigned long nowtime);
void pmac_read_rtc_time(void);
void chrp_calibrate_decr(void);
void chrp_time_init(void);
+int via_calibrate_decr(void);
+void mbx_calibrate_decr(void);
/* Accessor functions for the decrementer register. */
static __inline__ unsigned int get_dec(void)
diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c
index 7199dd5c1..c5c90b527 100644
--- a/arch/ppc/kernel/traps.c
+++ b/arch/ppc/kernel/traps.c
@@ -39,13 +39,31 @@ extern int fix_alignment(struct pt_regs *);
extern void bad_page_fault(struct pt_regs *, unsigned long);
#ifdef CONFIG_XMON
+extern void xmon(struct pt_regs *regs);
extern int xmon_bpt(struct pt_regs *regs);
extern int xmon_sstep(struct pt_regs *regs);
-extern void xmon(struct pt_regs *regs);
extern int xmon_iabr_match(struct pt_regs *regs);
+extern int xmon_dabr_match(struct pt_regs *regs);
extern void (*xmon_fault_handler)(struct pt_regs *regs);
#endif
+#ifdef CONFIG_XMON
+void (*debugger)(struct pt_regs *regs) = xmon;
+int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt;
+int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep;
+int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match;
+int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match;
+void (*debugger_fault_handler)(struct pt_regs *regs);
+#else
+#ifdef CONFIG_KGDB
+void (*debugger)(struct pt_regs *regs);
+int (*debugger_bpt)(struct pt_regs *regs);
+int (*debugger_sstep)(struct pt_regs *regs);
+int (*debugger_iabr_match)(struct pt_regs *regs);
+int (*debugger_dabr_match)(struct pt_regs *regs);
+void (*debugger_fault_handler)(struct pt_regs *regs);
+#endif
+#endif
/*
* Trap & Exception support
*/
@@ -61,8 +79,8 @@ _exception(int signr, struct pt_regs *regs)
if (!user_mode(regs))
{
show_regs(regs);
-#ifdef CONFIG_XMON
- xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ debugger(regs);
#endif
print_backtrace((unsigned long *)regs->gpr[1]);
panic("Exception in kernel pc %lx signal %d",regs->nip,signr);
@@ -75,9 +93,9 @@ MachineCheckException(struct pt_regs *regs)
{
if ( !user_mode(regs) )
{
-#ifdef CONFIG_XMON
- if (xmon_fault_handler) {
- xmon_fault_handler(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_fault_handler) {
+ debugger_fault_handler(regs);
return;
}
#endif
@@ -103,8 +121,8 @@ MachineCheckException(struct pt_regs *regs)
printk("Unknown values in msr\n");
}
show_regs(regs);
-#ifdef CONFIG_XMON
- xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ debugger(regs);
#endif
print_backtrace((unsigned long *)regs->gpr[1]);
panic("machine check");
@@ -123,8 +141,8 @@ UnknownException(struct pt_regs *regs)
void
InstructionBreakpoint(struct pt_regs *regs)
{
-#ifdef CONFIG_XMON
- if (xmon_iabr_match(regs))
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_iabr_match(regs))
return;
#endif
_exception(SIGTRAP, regs);
@@ -144,8 +162,8 @@ ProgramCheckException(struct pt_regs *regs)
_exception(SIGFPE, regs);
} else if (regs->msr & 0x20000) {
/* trap exception */
-#ifdef CONFIG_XMON
- if (xmon_bpt(regs))
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_bpt(regs))
return;
#endif
_exception(SIGTRAP, regs);
@@ -158,8 +176,8 @@ void
SingleStepException(struct pt_regs *regs)
{
regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */
-#ifdef CONFIG_XMON
- if (xmon_sstep(regs))
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ if (debugger_sstep(regs))
return;
#endif
_exception(SIGTRAP, regs);
@@ -170,8 +188,13 @@ AlignmentException(struct pt_regs *regs)
{
int fixed;
+#ifdef __SMP__
+ if (regs->msr & MSR_FP )
+ smp_giveup_fpu(current);
+#else
if (last_task_used_math == current)
giveup_fpu();
+#endif
fixed = fix_alignment(regs);
if (fixed == 1) {
regs->nip += 4; /* skip over emulated instruction */
@@ -190,8 +213,8 @@ StackOverflow(struct pt_regs *regs)
{
printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n",
current, regs->gpr[1]);
-#ifdef CONFIG_XMON
- xmon(regs);
+#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
+ debugger(regs);
#endif
show_regs(regs);
print_backtrace((unsigned long *)regs->gpr[1]);
@@ -205,3 +228,41 @@ trace_syscall(struct pt_regs *regs)
current, current->pid, regs->nip, regs->link, regs->gpr[0],
regs->ccr&0x10000000?"Error=":"", regs->gpr[3]);
}
+
+#ifdef CONFIG_8xx
+
+void
+SoftwareEmulation(struct pt_regs *regs)
+{
+ int errcode;
+ extern int Soft_emulate_8xx (struct pt_regs *regs);
+
+ if (user_mode(regs)) {
+#if 0
+ printk("(user mode)\n");
+ _exception(SIGTRAP, regs);
+#else
+ if (errcode = Soft_emulate_8xx(regs)) {
+printk("Software Emulation 0x%x: 0x%x ",
+ regs->nip, *((uint *)regs->nip));
+print_8xx_pte(current->mm, regs->nip);
+ if (errcode == EFAULT)
+ _exception(SIGBUS, regs);
+ else
+ _exception(SIGILL, regs);
+ }
+#endif
+ }
+ else {
+ printk("(kernel mode)\n");
+ panic("Kernel Mode Software Emulation");
+ }
+}
+#endif
+
+void
+TAUException(struct pt_regs *regs)
+{
+ printk("TAU trap at PC: %lx, SR: %lx, vector=%lx\n",
+ regs->nip, regs->msr, regs->trap);
+}