diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
commit | dcec8a13bf565e47942a1751a9cec21bec5648fe (patch) | |
tree | 548b69625b18cc2e88c3e68d0923be546c9ebb03 /arch/ppc/kernel | |
parent | 2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (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')
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, ¤t->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(¤t->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 *)®s->nip, ptr, 4); + ptr = mem2hex((char *)®s->msr, ptr, 4); + ptr = mem2hex((char *)®s->ccr, ptr, 4); + ptr = mem2hex((char *)®s->link, ptr, 4); + ptr = mem2hex((char *)®s->ctr, ptr, 4); + ptr = mem2hex((char *)®s->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 *)®s->nip, 4); + ptr = hex2mem(ptr, (char *)®s->msr, 4); + ptr = hex2mem(ptr, (char *)®s->ccr, 4); + ptr = hex2mem(ptr, (char *)®s->link, 4); + ptr = hex2mem(ptr, (char *)®s->ctr, 4); + ptr = hex2mem(ptr, (char *)®s->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, ¤t->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, ¤t->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 *)¤t_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 *)¤t->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); +} |