diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
commit | db7d4daea91e105e3859cf461d7e53b9b77454b2 (patch) | |
tree | 9bb65b95440af09e8aca63abe56970dd3360cc57 /arch/ppc/kernel | |
parent | 9c1c01ead627bdda9211c9abd5b758d6c687d8ac (diff) |
Merge with Linux 2.2.8.
Diffstat (limited to 'arch/ppc/kernel')
46 files changed, 5419 insertions, 2630 deletions
diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index d047c2086..5c8b44a90 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -27,7 +27,7 @@ O_OBJS += totalmp.o endif ifeq ($(CONFIG_MBX),y) -O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o +O_OBJS += mbx_setup.o mbx_pci.o softemu8xx.o i8259.o ppc8xx_pic.o else ifeq ($(CONFIG_APUS),y) O_OBJS += apus_setup.o prom.o openpic.o @@ -36,7 +36,8 @@ ifneq ($(CONFIG_MBX),y) O_OBJS += prep_time.o pmac_time.o chrp_time.o \ pmac_setup.o pmac_support.o \ prep_pci.o pmac_pci.o chrp_pci.o \ - residual.o prom.o openpic.o feature.o + residual.o prom.o openpic.o feature.o \ + prep_nvram.o open_pic.o i8259.o pmac_pic.o indirect_pci.o OX_OBJS += chrp_setup.o prep_setup.o endif endif diff --git a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c index 83ee7756d..cf5fcffd3 100644 --- a/arch/ppc/kernel/align.c +++ b/arch/ppc/kernel/align.c @@ -194,13 +194,8 @@ 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 & F) && (regs->msr & MSR_FP)) + giveup_fpu(current); if (flags & M) return 0; /* too hard for now */ @@ -254,27 +249,16 @@ fix_alignment(struct pt_regs *regs) data.d = current->tss.fpr[reg]; break; /* these require some floating point conversions... */ - /* note that giveup_fpu enables the FPU for the kernel */ /* we'd like to use the assignment, but we have to compile * 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 + enable_kernel_fp(); cvt_fd(&data.f, ¤t->tss.fpr[reg], ¤t->tss.fpscr); /* 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 + enable_kernel_fp(); cvt_df(¤t->tss.fpr[reg], &data.f, ¤t->tss.fpscr); /* data.f = current->tss.fpr[reg]; */ break; diff --git a/arch/ppc/kernel/apus_setup.c b/arch/ppc/kernel/apus_setup.c index c3a81ad3e..55e57fc5b 100644 --- a/arch/ppc/kernel/apus_setup.c +++ b/arch/ppc/kernel/apus_setup.c @@ -14,12 +14,50 @@ #include <linux/sched.h> #include <linux/kd.h> #include <linux/init.h> +#include <linux/hdreg.h> + +/* Get the IDE stuff from the 68k file */ +#define ide_init_hwif_ports m68k_ide_init_hwif_ports +#define ide_default_irq m68k_ide_default_irq +#define ide_default_io_base m68k_ide_default_io_base +#define ide_check_region m68k_ide_check_region +#define ide_request_region m68k_ide_request_region +#define ide_release_region m68k_ide_release_region +#define ide_fix_driveid m68k_ide_fix_driveid +#include <asm-m68k/ide.h> +#undef ide_init_hwif_ports +#define ide_default_irq +#define ide_default_io_base +#define ide_check_region +#define ide_request_region +#define ide_release_region +#define ide_fix_driveid + #include <asm/setup.h> #include <asm/amigahw.h> #include <asm/amigappc.h> #include <asm/pgtable.h> #include <asm/io.h> +#include <asm/machdep.h> +#include <asm/ide.h> + +#include "time.h" +#include "local_irq.h" + +unsigned long apus_get_rtc_time(void); +int apus_set_rtc_time(unsigned long nowtime); + +/* APUS defs */ +extern int parse_bootinfo(const struct bi_record *); +extern char _end[]; +#ifdef CONFIG_APUS +struct mem_info ramdisk; +unsigned long isa_io_base; +unsigned long isa_mem_base; +unsigned long pci_dram_offset; +#endif +/* END APUS defs */ unsigned long m68k_machtype; char debug_device[6] = ""; @@ -72,6 +110,8 @@ __initfunc(void apus_setup_arch(unsigned long * memory_start_p, int i; char *p, *q; + m68k_machtype = MACH_AMIGA; + /* Parse the command line for arch-specific options. * For the m68k, this is currently only "debug=xxx" to enable printing * certain kernel messages to some machine-specific device. */ @@ -409,3 +449,194 @@ void cache_clear(__u32 addr, int length) "isync \n\t" : : "r" (addr)); } + +void +apus_restart(char *cmd) +{ + cli(); + + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK2); + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK3); + APUS_WRITE(APUS_REG_LOCK, + REGLOCK_BLACKMAGICK2|REGLOCK_BLACKMAGICK3); + APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); + APUS_WRITE(APUS_REG_RESET, REGRESET_AMIGARESET); + for(;;); +} + +void +apus_power_off(void) +{ + for (;;); +} + +void +apus_halt(void) +{ + apus_restart(NULL); +} + +void +apus_do_IRQ(struct pt_regs *regs, + int cpu, + int isfake) +{ + int old_level, new_level; + + /* I don't think we need SMP code here - Corey */ + + old_level = ~(regs->mq) & IPLEMU_IPLMASK; + new_level = (~(regs->mq) >> 3) & IPLEMU_IPLMASK; + if (new_level != 0) + { + APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); + APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET + | (~(new_level) & IPLEMU_IPLMASK))); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); + + process_int (VEC_SPUR+new_level, regs); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_DISABLEINT); + APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); + APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET + | (~(old_level) & IPLEMU_IPLMASK))); + } + APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +void +apus_ide_insw(ide_ioreg_t port, void *buf, int ns) +{ + ide_insw(port, buf, ns); +} + +void +apus_ide_outsw(ide_ioreg_t port, void *buf, int ns) +{ + ide_outsw(port, buf, ns); +} + +int +apus_ide_default_irq(ide_ioreg_t base) +{ + m68k_ide_default_irq(base); +} + +ide_ioreg_t +apus_ide_default_io_base(int index) +{ + m68k_ide_default_io_base(index); +} + +int +apus_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return m68k_ide_check_region(from, extent); +} + +void +apus_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + m68k_ide_request_region(from, extent, name); +} + +void +apus_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + m68k_ide_release_region(from, extent); +} + +void +apus_ide_fix_driveid(struct hd_driveid *id) +{ + m68k_ide_fix_driveid(id); +} + +__initfunc(void +apus_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)) +{ + m68k_ide_init_hwif_ports(p, base, irq); +} +#endif + +__initfunc(void +apus_local_init_IRQ(void)) +{ + ppc_md.mask_irq = amiga_disable_irq; + ppc_md.unmask_irq = amiga_enable_irq; + apus_init_IRQ(); +} + +__initfunc(void +apus_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + /* Parse bootinfo. The bootinfo is located right after + the kernel bss */ + parse_bootinfo((const struct bi_record *)&_end); +#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 ( ramdisk.addr ) { + initrd_start = (unsigned long) __va(ramdisk.addr); + initrd_end = (unsigned long) + __va(ramdisk.size + ramdisk.addr); + } + /* Make sure code below is not executed. */ + r4 = 0; + r6 = 0; +#endif /* CONFIG_BLK_DEV_INITRD */ + + ISA_DMA_THRESHOLD = 0x00ffffff; + + ppc_md.setup_arch = apus_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = apus_get_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = apus_init_IRQ; + ppc_md.do_IRQ = apus_do_IRQ; + ppc_md.get_irq_source = NULL; + ppc_md.init = NULL; + + ppc_md.restart = apus_restart; + ppc_md.power_off = apus_power_off; + ppc_md.halt = apus_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = apus_set_rtc_time; + ppc_md.get_rtc_time = apus_get_rtc_time; + ppc_md.calibrate_decr = apus_calibrate_decr; + + /* These should not be used for the APUS yet, since it uses + the M68K keyboard now. */ + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; + ppc_md.kbd_sysrq_xlate = NULL; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.insw = apus_ide_insw; + ppc_ide_md.outsw = apus_ide_outsw; + ppc_ide_md.default_irq = apus_ide_default_irq; + ppc_ide_md.default_io_base = apus_ide_default_io_base; + ppc_ide_md.check_region = apus_ide_check_region; + ppc_ide_md.request_region = apus_ide_request_region; + ppc_ide_md.release_region = apus_ide_release_region; + ppc_ide_md.fix_driveid = apus_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = apus_ide_init_hwif_ports; + + ppc_ide_md.io_base = _IO_BASE; +#endif +} diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c index 98a3e182e..fa4dda10b 100644 --- a/arch/ppc/kernel/chrp_pci.c +++ b/arch/ppc/kernel/chrp_pci.c @@ -15,10 +15,14 @@ #include <asm/hydra.h> #include <asm/prom.h> #include <asm/gg2.h> +#include <asm/ide.h> +#include <asm/machdep.h> + +#include "pci.h" /* LongTrail */ #define pci_config_addr(bus, dev, offset) \ - (GG2_PCI_CONFIG_BASE | ((bus)<<16) | ((dev)<<8) | (offset)) +(GG2_PCI_CONFIG_BASE | ((bus)<<16) | ((dev)<<8) | (offset)) volatile struct Hydra *Hydra = NULL; @@ -30,144 +34,136 @@ volatile struct Hydra *Hydra = NULL; int gg2_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { - if (bus > 7) { - *val = 0xff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - *val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset)); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) { + *val = 0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + *val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset)); + return PCIBIOS_SUCCESSFUL; } int gg2_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short *val) { - if (bus > 7) { - *val = 0xffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - *val = in_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset)); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) { + *val = 0xffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + *val = in_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset)); + return PCIBIOS_SUCCESSFUL; } int gg2_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int *val) { - if (bus > 7) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - *val = in_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset)); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + *val = in_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset)); + return PCIBIOS_SUCCESSFUL; } int gg2_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char val) { - if (bus > 7) - return PCIBIOS_DEVICE_NOT_FOUND; - out_8((unsigned char *)pci_config_addr(bus, dev_fn, offset), val); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + out_8((unsigned char *)pci_config_addr(bus, dev_fn, offset), val); + return PCIBIOS_SUCCESSFUL; } int gg2_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short val) { - if (bus > 7) - return PCIBIOS_DEVICE_NOT_FOUND; - out_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset), val); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset), val); + return PCIBIOS_SUCCESSFUL; } int gg2_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int val) { - if (bus > 7) - return PCIBIOS_DEVICE_NOT_FOUND; - out_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset), val); - return PCIBIOS_SUCCESSFUL; + if (bus > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset), val); + return PCIBIOS_SUCCESSFUL; } -extern volatile unsigned int *pci_config_address; -extern volatile unsigned char *pci_config_data; - -#define DEV_FN_MAX (31<<3) +#define python_config_address(bus) (unsigned *)((0xfef00000+0xf8000)-(bus*0x100000)) +#define python_config_data(bus) ((0xfef00000+0xf8010)-(bus*0x100000)) +#define PYTHON_CFA(b, d, o) (0x80 | ((b<<6) << 8) | ((d) << 16) \ + | (((o) & ~3) << 24)) +unsigned int python_busnr = 1; -int raven_pcibios_read_config_byte(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned char *val) +int python_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - *val = in_8(pci_config_data+(offset&3)); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) { + *val = 0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + *val = in_8((unsigned char *)python_config_data(bus) + (offset&3)); + return PCIBIOS_SUCCESSFUL; } -int raven_pcibios_read_config_word(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned short *val) +int python_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - if (offset&1)return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - *val = in_le16((volatile unsigned short *) - (pci_config_data+(offset&3))); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) { + *val = 0xffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + *val = in_le16((unsigned short *)(python_config_data(bus) + (offset&3))); + return PCIBIOS_SUCCESSFUL; } -int raven_pcibios_read_config_dword(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned int *val) + +int python_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - if (offset&3)return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24)); - *val = in_le32((volatile unsigned int *)(pci_config_data)); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + *val = in_le32((unsigned *)python_config_data(bus)); + return PCIBIOS_SUCCESSFUL; } -int raven_pcibios_write_config_byte(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned char val) +int python_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - out_8(pci_config_data+(offset&3),val); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + out_8((volatile unsigned char *)python_config_data(bus) + (offset&3), val); + return PCIBIOS_SUCCESSFUL; } -int raven_pcibios_write_config_word(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned short val) +int python_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - if (offset&1)return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - out_le16((volatile unsigned short *)(pci_config_data+(offset&3)),val); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + out_le16((volatile unsigned short *)python_config_data(bus) + (offset&3), + val); + return PCIBIOS_SUCCESSFUL; } -int raven_pcibios_write_config_dword(unsigned char bus, - unsigned char dev_fn, - unsigned char offset, - unsigned int val) +int python_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) { - if (dev_fn >= DEV_FN_MAX) return PCIBIOS_DEVICE_NOT_FOUND; - if (offset&3)return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci_config_address, - 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24)); - out_le32((volatile unsigned int *)pci_config_data,val); - return PCIBIOS_SUCCESSFUL; + if (bus > python_busnr) + return PCIBIOS_DEVICE_NOT_FOUND; + out_be32( python_config_address( bus ), PYTHON_CFA(bus,dev_fn,offset)); + out_le32((unsigned *)python_config_data(bus) + (offset&3), val); + return PCIBIOS_SUCCESSFUL; } /* @@ -191,7 +187,8 @@ static u_char hydra_openpic_initsenses[] __initdata = { /* all others are 1 (= default) */ }; -__initfunc(int hydra_init(void)) +int __init +hydra_init(void) { struct device_node *np; @@ -216,75 +213,95 @@ __initfunc(int hydra_init(void)) return 1; } - -extern int chrp_ide_irq; - -__initfunc(int w83c553f_init(void)) +void __init +chrp_pcibios_fixup(void) { - u_char bus, dev; -#if 0 - unsigned char t8; - unsigned short t16; -#endif - unsigned int t32; - 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; - pcibios_read_config_dword(bus, dev, PCI_VENDOR_ID, &t32); - if (t32 == (PCI_DEVICE_ID_WINBOND_82C105<<16) + PCI_VENDOR_ID_WINBOND) { -#if 0 - printk("Enabling SL82C105 IDE on W83C553F\n"); - /* - * FIXME: this doesn't help :-( - */ - - /* I/O mapping */ - pcibios_read_config_word(bus, dev, PCI_COMMAND, &t16); - t16 |= PCI_COMMAND_IO; - pcibios_write_config_word(bus, dev, PCI_COMMAND, t16); - - /* Standard IDE registers */ - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_0, - 0x000001f0 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_1, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_1, - 0x000003f4 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_2, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_2, - 0x00000170 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_3, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_3, - 0x00000374 | 1); + struct pci_dev *dev; + + /* some of IBM chrps have > 1 bus */ + if ( !strncmp("IBM", get_property(find_path_device("/"), + "name", NULL),3) ) + { + pci_scan_peer_bridge(1); + pci_scan_peer_bridge(2); + } + + /* 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 ); + /* adjust the io_port for the NCR cards for busses other than 0 -- Cort */ + if ( (dev->bus->number > 0) && (dev->vendor == PCI_VENDOR_ID_NCR) ) + dev->base_address[0] += (dev->bus->number*0x08000000); + /* these need to be absolute addrs for OF and Matrox FB -- Cort */ + if ( dev->vendor == PCI_VENDOR_ID_MATROX ) + { + if ( dev->base_address[0] < isa_mem_base ) + dev->base_address[0] += isa_mem_base; + if ( dev->base_address[1] < isa_mem_base ) + dev->base_address[1] += isa_mem_base; + } + /* the F50 identifies the amd as a trident */ + if ( (dev->vendor == PCI_VENDOR_ID_TRIDENT) && + (dev->class == PCI_CLASS_NETWORK_ETHERNET) ) + { + dev->vendor = PCI_VENDOR_ID_AMD; + pcibios_write_config_word(dev->bus->number, dev->devfn, + PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); + } + } +} - /* IDE Bus Master Control */ - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_4, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_4, - 0x1000 | 1); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5, - 0xffffffff); - pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_5, &t32); - pcibios_write_config_dword(bus, dev, PCI_BASE_ADDRESS_5, - 0x1010 | 1); +decl_config_access_method(grackle); +decl_config_access_method(indirect); - /* IDE Interrupt */ - pcibios_read_config_byte(bus, dev, PCI_INTERRUPT_LINE, &t8); - chrp_ide_irq = t8; -#endif - return 1; - } - } - return 0; +void __init +chrp_setup_pci_ptrs(void) +{ + struct device_node *py; + + if ( !strncmp("MOT", + get_property(find_path_device("/"), "model", NULL),3) ) + { + pci_dram_offset = 0; + isa_mem_base = 0xf7000000; + isa_io_base = 0xfe000000; + set_config_access_method(grackle); + } + else + { + if ( (py = find_compatible_devices( "pci", "IBM,python" )) ) + { + /* find out how many pythons */ + while ( (py = py->next) ) python_busnr++; + set_config_access_method(python); + /* + * We base these values on the machine type but should + * try to read them from the python controller itself. + * -- Cort + */ + if ( !strncmp("IBM,7025-F50", get_property(find_path_device("/"), "name", NULL),12) ) + { + pci_dram_offset = 0x80000000; + isa_mem_base = 0xa0000000; + isa_io_base = 0x88000000; + } else if ( !strncmp("IBM,7043-260", + get_property(find_path_device("/"), "name", NULL),12) ) + { + pci_dram_offset = 0x80000000; + isa_mem_base = 0xc0000000; + isa_io_base = 0xf8000000; + } + } + else + { + pci_dram_offset = 0; + isa_mem_base = 0xf7000000; + isa_io_base = 0xf8000000; + set_config_access_method(gg2); + } + } + + ppc_md.pcibios_fixup = chrp_pcibios_fixup; } diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 5b373c876..2c652f049 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -31,6 +31,7 @@ #include <linux/ioport.h> #include <linux/console.h> #include <linux/pci.h> +#include <linux/openpic.h> #include <asm/mmu.h> #include <asm/processor.h> @@ -40,9 +41,50 @@ #include <asm/prom.h> #include <asm/gg2.h> #include <asm/pci-bridge.h> - -extern void hydra_init(void); -extern void w83c553f_init(void); +#include <asm/dma.h> +#include <asm/machdep.h> +#include <asm/irq.h> +#include <asm/adb.h> +#include <asm/hydra.h> + +#include "time.h" +#include "local_irq.h" +#include "i8259.h" +#include "open_pic.h" + +/* Fixme - need to move these into their own .c and .h file */ +extern void i8259_mask_and_ack_irq(unsigned int irq_nr); +extern void i8259_set_irq_mask(unsigned int irq_nr); +extern void i8259_mask_irq(unsigned int irq_nr); +extern void i8259_unmask_irq(unsigned int irq_nr); +extern void i8259_init(void); + +/* Fixme - remove this when it is fixed. - Corey */ +extern volatile unsigned char *chrp_int_ack_special; + +unsigned long chrp_get_rtc_time(void); +int chrp_set_rtc_time(unsigned long nowtime); +void chrp_calibrate_decr(void); +void chrp_time_init(void); + +void chrp_setup_pci_ptrs(void); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; +extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int mackbd_getkeycode(unsigned int scancode); +extern int mackbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char mackbd_unexpected_up(unsigned char keycode); +extern void mackbd_leds(unsigned char leds); +extern void mackbd_init_hw(void); +extern unsigned char mackbd_sysrq_xlate[128]; /* for the mac fs */ kdev_t boot_dev; @@ -53,7 +95,6 @@ extern int probingmem; extern unsigned long loops_per_sec; unsigned long empty_zero_page[1024]; -extern unsigned char aux_device_present; #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ @@ -62,17 +103,17 @@ extern int rd_image_start; /* starting block # of image */ #endif static const char *gg2_memtypes[4] = { - "FPM", "SDRAM", "EDO", "BEDO" + "FPM", "SDRAM", "EDO", "BEDO" }; static const char *gg2_cachesizes[4] = { - "256 KB", "512 KB", "1 MB", "Reserved" + "256 KB", "512 KB", "1 MB", "Reserved" }; static const char *gg2_cachetypes[4] = { - "Asynchronous", "Reserved", "Flow-Through Synchronous", - "Pipelined Synchronous" + "Asynchronous", "Reserved", "Flow-Through Synchronous", + "Pipelined Synchronous" }; static const char *gg2_cachemodes[4] = { - "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" + "Disabled", "Write-Through", "Copy-Back", "Transparent Mode" }; int @@ -85,53 +126,60 @@ chrp_get_cpuinfo(char *buffer) root = find_path_device("/"); if (root) - model = get_property(root, "model", NULL); + model = get_property(root, "model", NULL); len = sprintf(buffer,"machine\t\t: CHRP %s\n", model); - /* VLSI VAS96011/12 `Golden Gate 2' */ - /* Memory banks */ - sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_CTRL)) - >>31) & 1; - for (i = 0; i < (sdramen ? 4 : 6); i++) { - t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_DRAM_BANK0+ - i*4)); - if (!(t & 1)) - continue; - switch ((t>>8) & 0x1f) { - case 0x1f: - model = "4 MB"; - break; - case 0x1e: - model = "8 MB"; - break; - case 0x1c: - model = "16 MB"; - break; - case 0x18: - model = "32 MB"; - break; - case 0x10: - model = "64 MB"; - break; - case 0x00: - model = "128 MB"; - break; - default: - model = "Reserved"; - break; - } - len += sprintf(buffer+len, "memory bank %d\t: %s %s\n", i, model, - gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); + /* longtrail (goldengate) stuff */ + if ( !strncmp( model, "IBM,LongTrail", 9 ) ) + { + /* VLSI VAS96011/12 `Golden Gate 2' */ + /* Memory banks */ + sdramen = (in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+ + GG2_PCI_DRAM_CTRL)) + >>31) & 1; + for (i = 0; i < (sdramen ? 4 : 6); i++) { + t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+ + GG2_PCI_DRAM_BANK0+ + i*4)); + if (!(t & 1)) + continue; + switch ((t>>8) & 0x1f) { + case 0x1f: + model = "4 MB"; + break; + case 0x1e: + model = "8 MB"; + break; + case 0x1c: + model = "16 MB"; + break; + case 0x18: + model = "32 MB"; + break; + case 0x10: + model = "64 MB"; + break; + case 0x00: + model = "128 MB"; + break; + default: + model = "Reserved"; + break; + } + len += sprintf(buffer+len, "memory bank %d\t: %s %s\n", i, model, + gg2_memtypes[sdramen ? 1 : ((t>>1) & 3)]); + } + /* L2 cache */ + t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL)); + 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]); } - /* L2 cache */ - t = in_le32((unsigned *)(GG2_PCI_CONFIG_BASE+GG2_PCI_CC_CTRL)); - 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 @@ -140,57 +188,55 @@ chrp_get_cpuinfo(char *buffer) __initfunc(static inline void sio_write(u8 val, u8 index)) { - outb(index, 0x15c); - outb(val, 0x15d); + outb(index, 0x15c); + outb(val, 0x15d); } __initfunc(static inline u8 sio_read(u8 index)) { - outb(index, 0x15c); - return inb(0x15d); + outb(index, 0x15c); + return inb(0x15d); } __initfunc(static void sio_fixup_irq(const char *name, u8 device, u8 level, u8 type)) { - u8 level0, type0, active; - - /* select logical device */ - sio_write(device, 0x07); - active = sio_read(0x30); - level0 = sio_read(0x70); - type0 = sio_read(0x71); - printk("sio: %s irq level %d, type %d, %sactive: ", name, level0, type0, - !active ? "in" : ""); - if (level0 == level && type0 == type && active) - printk("OK\n"); - else { - printk("remapping to level %d, type %d, active\n", level, type); - sio_write(0x01, 0x30); - sio_write(level, 0x70); - sio_write(type, 0x71); - } + u8 level0, type0, active; + + /* select logical device */ + sio_write(device, 0x07); + active = sio_read(0x30); + level0 = sio_read(0x70); + type0 = sio_read(0x71); + printk("sio: %s irq level %d, type %d, %sactive: ", name, level0, type0, + !active ? "in" : ""); + if (level0 == level && type0 == type && active) + printk("OK\n"); + else { + printk("remapping to level %d, type %d, active\n", level, type); + sio_write(0x01, 0x30); + sio_write(level, 0x70); + sio_write(type, 0x71); + } } __initfunc(static void sio_init(void)) { - /* logical device 0 (KBC/Keyboard) */ - sio_fixup_irq("keyboard", 0, 1, 2); - /* select logical device 1 (KBC/Mouse) */ - sio_fixup_irq("mouse", 1, 12, 2); + /* logical device 0 (KBC/Keyboard) */ + sio_fixup_irq("keyboard", 0, 1, 2); + /* select logical device 1 (KBC/Mouse) */ + sio_fixup_irq("mouse", 1, 12, 2); } __initfunc(void -chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) + chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) { extern char cmd_line[]; /* init to some ~sane value until calibrate_delay() runs */ loops_per_sec = 50000000; - - aux_device_present = 0xaa; #ifdef CONFIG_BLK_DEV_INITRD /* this is fine for chrp */ @@ -219,8 +265,15 @@ chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) * -- Geert */ hydra_init(); /* Mac I/O */ - w83c553f_init(); /* PCI-ISA bridge and IDE */ + /* Some IBM machines don't have the hydra -- Cort */ + if ( !OpenPIC ) + { + OpenPIC = (struct OpenPIC *)*(unsigned long *)get_property( + find_path_device("/"), "platform-open-pic", NULL); + OpenPIC = ioremap((unsigned long)OpenPIC, sizeof(struct OpenPIC)); + } + /* * Fix the Super I/O configuration */ @@ -232,27 +285,210 @@ chrp_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) if ( !strncmp("MOT", get_property(find_path_device("/"), "model", NULL),3) ) *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p); + /* + * The f50 has a lot of IO space - we need to map some in that + * isn't covered by the BAT mappings in MMU_init() -- Cort + */ + if ( !strncmp("F5", get_property(find_path_device("/"), + "ibm,model-class", NULL),2) ) + { +#if 0 + /* + * This ugly hack allows us to force ioremap() to + * create a 1-to-1 mapping for us, even though + * the address is < ioremap_base. This is necessary + * since we want our PCI IO space to have contiguous + * virtual addresses and I think it's worse to have + * calls to map_page() here. + * -- Cort + */ + unsigned long hold = ioremap_base; + ioremap_base = 0; + __ioremap(0x90000000, 0x10000000, _PAGE_NO_CACHE); + ioremap_base = hold; +#endif + } } -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +void +chrp_restart(char *cmd) +{ +#if 0 + extern unsigned int rtas_entry, rtas_data, rtas_size; + printk("RTAS system-reboot returned %d\n", + call_rtas("system-reboot", 0, 1, NULL)); + printk("rtas_entry: %08lx rtas_data: %08lx rtas_size: %08lx\n", + rtas_entry,rtas_data,rtas_size); + for (;;); +#else + printk("System Halted\n"); + while(1); +#endif +} -unsigned int chrp_ide_irq = 0; -int chrp_ide_ports_known = 0; -ide_ioreg_t chrp_ide_regbase[MAX_HWIFS]; -ide_ioreg_t chrp_idedma_regbase; +void +chrp_power_off(void) +{ + /* RTAS doesn't seem to work on Longtrail. + For now, do it the same way as the PReP. */ +#if 0 + extern unsigned int rtas_entry, rtas_data, rtas_size; + printk("RTAS power-off returned %d\n", + call_rtas("power-off", 2, 1, NULL, 0, 0)); + printk("rtas_entry: %08lx rtas_data: %08lx rtas_size: %08lx\n", + rtas_entry,rtas_data,rtas_size); + for (;;); +#else + chrp_restart(NULL); +#endif +} -void chrp_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) +void +chrp_halt(void) { - ide_ioreg_t port = base; - int i = 8; + chrp_restart(NULL); +} - while (i--) - *p++ = port++; - *p++ = port; - if (irq != NULL) - *irq = chrp_ide_irq; +u_int +chrp_irq_cannonicalize(u_int irq) +{ + if (irq == 2) + { + return 9; + } + else + { + return irq; + } +} + +void +chrp_do_IRQ(struct pt_regs *regs, + int cpu, + int isfake) +{ + int irq; + unsigned long bits = 0; + int openpic_eoi_done = 0; + +#ifdef __SMP__ + { + unsigned int loops = 1000000; + while (test_bit(0, &global_irq_lock)) { + if (smp_processor_id() == global_irq_holder) { + printk("uh oh, interrupt while we hold global irq lock!\n"); +#ifdef CONFIG_XMON + xmon(0); +#endif + break; + } + if (loops-- == 0) { + printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder); +#ifdef CONFIG_XMON + xmon(0); +#endif + } + } + } +#endif /* __SMP__ */ + + irq = openpic_irq(0); + if (irq == IRQ_8259_CASCADE) + { + /* + * This magic address generates a PCI IACK cycle. + * + * This should go in the above mask/ack code soon. -- Cort + */ + if ( chrp_int_ack_special ) + irq = *chrp_int_ack_special; + else + irq = i8259_irq(0); + /* + * Acknowledge as soon as possible to allow i8259 + * interrupt nesting */ + openpic_eoi(0); + openpic_eoi_done = 1; + } + if (irq == OPENPIC_VEC_SPURIOUS) + { + /* + * Spurious interrupts should never be + * acknowledged + */ + ppc_spurious_interrupts++; + openpic_eoi_done = 1; + goto out; + } + bits = 1UL << irq; + + if (irq < 0) + { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + ppc_spurious_interrupts++; + } + else + { + ppc_irq_dispatch_handler( regs, irq ); + } +out: + if (!openpic_eoi_done) + openpic_eoi(0); } +__initfunc(void + chrp_init_IRQ(void)) +{ + struct device_node *np; + int i; + + if ( !(np = find_devices("pci") ) ) + printk("Cannot find pci to get ack address\n"); + else + { + chrp_int_ack_special = (volatile unsigned char *) + (*(unsigned long *)get_property(np, + "8259-interrupt-acknowledge", NULL)); + } + for ( i = 16 ; i < NR_IRQS ; i++ ) + irq_desc[i].ctl = &open_pic; + /* openpic knows that it's at irq 16 offset + * so we don't need to set it in the pic structure + * -- Cort + */ + openpic_init(1); + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].ctl = &i8259_pic; + i8259_init(); +#ifdef CONFIG_XMON + request_irq(openpic_to_irq(HYDRA_INT_ADB_NMI), + xmon_irq, 0, "NMI", 0); +#endif /* CONFIG_XMON */ +#ifdef __SMP__ + request_irq(openpic_to_irq(OPENPIC_VEC_IPI), + openpic_ipi_action, 0, "IPI0", 0); +#endif /* __SMP__ */ +} + +__initfunc(void + chrp_init2(void)) +{ + adb_init(); + + /* Should this be here? - Corey */ + pmac_nvram_init(); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +unsigned int chrp_ide_irq = 0; +int chrp_ide_ports_known = 0; +ide_ioreg_t chrp_ide_regbase[MAX_HWIFS]; +ide_ioreg_t chrp_idedma_regbase; + void chrp_ide_probe(void) { struct pci_dev *pdev = pci_find_device(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, NULL); @@ -270,9 +506,167 @@ void chrp_ide_probe(void) { } } +void +chrp_ide_insw(ide_ioreg_t port, void *buf, int ns) +{ + ide_insw(port+_IO_BASE, buf, ns); +} + +void +chrp_ide_outsw(ide_ioreg_t port, void *buf, int ns) +{ + ide_outsw(port+_IO_BASE, buf, ns); +} + +int +chrp_ide_default_irq(ide_ioreg_t base) +{ + if (chrp_ide_ports_known == 0) + chrp_ide_probe(); + return chrp_ide_irq; +} + +ide_ioreg_t +chrp_ide_default_io_base(int index) +{ + if (chrp_ide_ports_known == 0) + chrp_ide_probe(); + return chrp_ide_regbase[index]; +} + +int +chrp_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +void +chrp_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); +} + +void +chrp_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); +} + +void +chrp_ide_fix_driveid(struct hd_driveid *id) +{ + ppc_generic_ide_fix_driveid(id); +} + +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++ = port; + if (irq != NULL) + *irq = chrp_ide_irq; +} + EXPORT_SYMBOL(chrp_ide_irq); EXPORT_SYMBOL(chrp_ide_ports_known); EXPORT_SYMBOL(chrp_ide_regbase); EXPORT_SYMBOL(chrp_ide_probe); #endif + +__initfunc(void + chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + chrp_setup_pci_ptrs(); +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r3 ) + { + initrd_start = r3 + KERNELBASE; + initrd_end = r3 + r4 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* pci_dram_offset/isa_io_base/isa_mem_base set by setup_pci_ptrs() */ + ISA_DMA_THRESHOLD = ~0L; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = chrp_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = chrp_get_cpuinfo; + ppc_md.irq_cannonicalize = chrp_irq_cannonicalize; + ppc_md.init_IRQ = chrp_init_IRQ; + ppc_md.do_IRQ = chrp_do_IRQ; + + ppc_md.init = chrp_init2; + + ppc_md.restart = chrp_restart; + ppc_md.power_off = chrp_power_off; + ppc_md.halt = chrp_halt; + + ppc_md.time_init = chrp_time_init; + ppc_md.set_rtc_time = chrp_set_rtc_time; + ppc_md.get_rtc_time = chrp_get_rtc_time; + ppc_md.calibrate_decr = chrp_calibrate_decr; + +#ifdef CONFIG_VT +#ifdef CONFIG_MAC_KEYBOAD + if ( adb_hardware == ADB_NONE ) + { + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = pckbd_sysrq_xlate; +#endif + } + else + { + ppc_md.kbd_setkeycode = mackbd_setkeycode; + ppc_md.kbd_getkeycode = mackbd_getkeycode; + ppc_md.kbd_translate = mackbd_translate; + ppc_md.kbd_unexpected_up = mackbd_unexpected_up; + ppc_md.kbd_leds = mackbd_leds; + ppc_md.kbd_init_hw = mackbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = mackbd_sysrq_xlate; +#endif + } +#else + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = pckbd_sysrq_xlate; +#endif +#endif +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.insw = chrp_ide_insw; + ppc_ide_md.outsw = chrp_ide_outsw; + ppc_ide_md.default_irq = chrp_ide_default_irq; + ppc_ide_md.default_io_base = chrp_ide_default_io_base; + ppc_ide_md.check_region = chrp_ide_check_region; + ppc_ide_md.request_region = chrp_ide_request_region; + ppc_ide_md.release_region = chrp_ide_release_region; + ppc_ide_md.fix_driveid = chrp_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = chrp_ide_init_hwif_ports; + + ppc_ide_md.io_base = _IO_BASE; +#endif +} diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c index 7a2ea7b26..c374c9bd1 100644 --- a/arch/ppc/kernel/chrp_time.c +++ b/arch/ppc/kernel/chrp_time.c @@ -154,7 +154,8 @@ unsigned long chrp_get_rtc_time(void) __initfunc(void chrp_calibrate_decr(void)) { struct device_node *cpu; - int freq, *fp, divisor; + int *fp, divisor; + unsigned long freq; if (via_calibrate_decr()) return; @@ -170,10 +171,9 @@ __initfunc(void chrp_calibrate_decr(void)) if (fp != 0) freq = *fp; } - freq *= 60; /* try to make freq/1e6 an integer */ divisor = 60; - printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); + printk("time_init: decrementer frequency = %lu/%d\n", freq, divisor); decrementer_count = freq / HZ / divisor; count_period_num = divisor; count_period_den = freq / 1000000; diff --git a/arch/ppc/kernel/feature.c b/arch/ppc/kernel/feature.c index 48d8bcb39..25ee3424b 100644 --- a/arch/ppc/kernel/feature.c +++ b/arch/ppc/kernel/feature.c @@ -41,7 +41,6 @@ static u32 feature_bits_pbook[] = { OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ 0, /* FEATURE_BMac_reset */ 0, /* FEATURE_BMac_IO_enable */ - 0, /* FEATURE_Modem_PowerOn -> guess...*/ 0 /* FEATURE_Modem_Reset -> guess...*/ }; @@ -64,8 +63,7 @@ static u32 feature_bits_heathrow[] = { OH_BAY_FLOPPY_ENABLE, /* FEATURE_Mediabay_floppy_enable */ 0x80000000, /* FEATURE_BMac_reset */ 0x60000000, /* FEATURE_BMac_IO_enable */ - 0x02000000, /* FEATURE_Modem_PowerOn -> guess...*/ - 0x07000000 /* FEATURE_Modem_Reset -> guess...*/ + 0x02000000 /* FEATURE_Modem_Reset -> guess...*/ }; /* definition of a feature controller object */ diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index d7b791387..b9ecc2dcc 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -1,7 +1,7 @@ /* * arch/ppc/kernel/head.S * - * $Id: head.S,v 1.114 1998/12/28 10:28:45 paulus Exp $ + * $Id: head.S,v 1.130 1999/05/09 19:16:43 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -44,8 +44,13 @@ /* optimization for 603 to load the tlb directly from the linux table */ #define NO_RELOAD_HTAB 1 +#ifndef CONFIG_8xx CACHE_LINE_SIZE = 32 LG_CACHE_LINE_SIZE = 5 +#else +CACHE_LINE_SIZE = 16 +LG_CACHE_LINE_SIZE = 4 +#endif #define TOPHYS(x) (x - KERNELBASE) @@ -81,13 +86,12 @@ LG_CACHE_LINE_SIZE = 5 sync; \ 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; \ - lis r4,0xC000; \ + lis r4,KERNELBASE@h; \ 0: tlbie r4; \ addi r4,r4,0x1000; \ bdnz 0b @@ -212,6 +216,7 @@ __start: mr r29,r5 mr r28,r6 mr r27,r7 + li r24,0 /* cpu # */ #ifndef CONFIG_8xx bl prom_init .globl __secondary_start @@ -236,15 +241,33 @@ __secondary_start: mtspr IBAT1L,r10 b 5f 4: -#ifndef CONFIG_APUS - ori r11,r11,0x1fe /* set up BAT registers for 604 */ - li r8,2 /* R/W access */ -#else +#ifdef CONFIG_APUS + ori r11,r11,BL_8M<<2|0x2 /* set up an 8MB mapping */ ori r11,r11,0xfe /* set up an 8MB mapping */ lis r8,CYBERBASEp@h lwz r8,0(r8) addis r8,r8,KERNELBASE@h addi r8,r8,2 +#else + ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */ + li r8,2 /* R/W access */ + /* + * allow secondary cpus to get at all of ram in early bootup + * since their init_task may be up there -- Cort + */ + oris r18,r8,0x10000000@h + oris r21,r11,(KERNELBASE+0x10000000)@h + mtspr DBAT1L,r18 /* N.B. 6xx (not 601) have valid */ + mtspr DBAT1U,r21 /* bit in upper BAT register */ + mtspr IBAT1L,r18 + mtspr IBAT1U,r21 + + oris r18,r8,0x20000000@h + oris r21,r11,(KERNELBASE+0x20000000)@h + mtspr DBAT2L,r18 /* N.B. 6xx (not 601) have valid */ + mtspr DBAT2U,r21 /* bit in upper BAT register */ + mtspr IBAT2L,r28 + mtspr IBAT2U,r21 #endif mtspr DBAT0L,r8 /* N.B. 6xx (not 601) have valid */ mtspr DBAT0U,r11 /* bit in upper BAT register */ @@ -327,20 +350,28 @@ __secondary_start: lis r8, MI_Kp@h /* Set the protection mode */ mtspr MI_AP, r8 mtspr MD_AP, r8 -#ifdef CONFIG_MBX + +/* We will get these from a configuration file as soon as I verify + * the extraneous bits don't cause problems in the TLB. + */ +#if defined(CONFIG_MBX) || defined(CONFIG_RPXLITE) +#define BOOT_IMMR 0xfa000000 +#endif +#ifdef CONFIG_BSEIP +#define BOOT_IMMR 0xff000000 +#endif /* Map another 8 MByte at 0xfa000000 to get the processor * internal registers (among other things). */ - lis r8, 0xfa000000@h /* Create vaddr for TLB */ + lis r8, BOOT_IMMR@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 */ + lis r8, BOOT_IMMR@h /* Create paddr for TLB */ ori r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */ 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. @@ -354,9 +385,8 @@ __secondary_start: #if 0 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. + /* For a debug option, I left this here to easily enable + * the write through cache mode */ lis r8, DC_SFWT@h mtspr DC_CST, r8 @@ -385,7 +415,7 @@ turn_on_mmu: * this, we leave this much untouched space on the stack on exception * entry. */ -#define STACK_UNDERHEAD 64 +#define STACK_UNDERHEAD 0 /* * Exception entry code. This code runs with address translation @@ -442,7 +472,11 @@ label: \ .long int_return /* System reset */ +#ifdef CONFIG_SMP /* MVME/MTX start the secondary here */ + STD_EXCEPTION(0x100, Reset, __secondary_start_psurge) +#else STD_EXCEPTION(0x100, Reset, UnknownException) +#endif /* Machine check */ STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) @@ -1148,6 +1182,8 @@ transfer_to_handler: mflr r23 andi. r24,r23,0x3f00 /* get vector offset */ stw r24,TRAP(r21) + li r22,RESULT + stwcx. r22,r22,r21 /* to clear the reservation */ li r22,0 stw r22,RESULT(r21) mtspr SPRG2,r22 /* r1 is now kernel sp */ @@ -1155,7 +1191,7 @@ transfer_to_handler: cmplw 0,r1,r2 cmplw 1,r1,r24 crand 1,1,4 - bgt stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ + bgt- stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ lwz r24,0(r23) /* virtual address of handler */ lwz r23,4(r23) /* where to go when done */ mtspr SRR0,r24 @@ -1204,13 +1240,10 @@ Hash_base = 0x180000 Hash_bits = 12 /* e.g. 256kB hash table */ Hash_msk = (((1 << Hash_bits) - 1) * 64) - .globl hash_table_lock -hash_table_lock: -.long 0 - .globl hash_page hash_page: #ifdef __SMP__ + eieio lis r2,hash_table_lock@h ori r2,r2,hash_table_lock@l tophys(r2,r2,r6) @@ -1226,7 +1259,7 @@ hash_page: 12: cmpw r6,r0 bdnzf 2,10b tw 31,31,31 -11: +11: eieio #endif /* Get PTE (linux-style) and check access */ lwz r5,PG_TABLES(r5) @@ -1234,13 +1267,25 @@ hash_page: rlwimi r5,r3,12,20,29 /* insert top 10 bits of address */ lwz r5,0(r5) /* get pmd entry */ rlwinm. r5,r5,0,0,19 /* extract address of pte page */ +#ifdef __SMP__ beq- hash_page_out /* return if no mapping */ +#else + /* XXX it seems like the 601 will give a machine fault on the + rfi if its alignment is wrong (bottom 4 bits of address are + 8 or 0xc) and we have had a not-taken conditional branch + to the address following the rfi. */ + beqlr- +#endif tophys(r2,r5,r2) rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r6,0(r2) /* get linux-style pte */ ori r4,r4,1 /* set _PAGE_PRESENT bit in access */ andc. r0,r4,r6 /* check access & ~permission */ +#ifdef __SMP__ bne- hash_page_out /* return if access not permitted */ +#else + bnelr- +#endif ori r6,r6,0x100 /* set _PAGE_ACCESSED in pte */ rlwinm r5,r4,5,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ @@ -1257,7 +1302,9 @@ hash_page: /* Construct the high word of the PPC-style PTE */ mfsrin r5,r3 /* get segment reg for segment */ rlwinm r5,r5,7,1,24 /* put VSID in 0x7fffff80 bits */ +#ifndef __SMP__ /* do this later for SMP */ oris r5,r5,0x8000 /* set V (valid) bit */ +#endif rlwimi r5,r3,10,26,31 /* put in API (abbrev page index) */ /* Get the address of the primary PTE group in the hash table */ @@ -1274,9 +1321,6 @@ hash_page_patch_A: li r2,8 /* PTEs/group */ bne 10f /* no PTE: go look for an empty slot */ tlbie r3 /* invalidate TLB entry */ -#ifdef __SMP__ - tlbsync -#endif /* Search the primary PTEG for a PTE whose 1st word matches r5 */ mtctr r2 @@ -1345,18 +1389,43 @@ hash_page_patch_C: addi r4,r4,1 stw r4,0(r2) +#ifndef __SMP__ /* Store PTE in PTEG */ found_empty: stw r5,0(r3) found_slot: stw r6,4(r3) - SYNC + sync + +#else /* __SMP__ */ /* - * These nop's seem to be necessary to avoid getting a machine - * check on the rfi on 601 processors. + * Between the tlbie above and updating the hash table entry below, + * another CPU could read the hash table entry and put it in its TLB. + * There are 3 cases: + * 1. using an empty slot + * 2. updating an earlier entry to change permissions (i.e. enable write) + * 3. taking over the PTE for an unrelated address + * + * In each case it doesn't really matter if the other CPUs have the old + * PTE in their TLB. So we don't need to bother with another tlbie here, + * which is convenient as we've overwritten the register that had the + * address. :-) The tlbie above is mainly to make sure that this CPU comes + * and gets the new PTE from the hash table. + * + * We do however have to make sure that the PTE is never in an invalid + * state with the V bit set. */ - nop - nop +found_empty: +found_slot: + stw r5,0(r3) /* clear V (valid) bit in PTE */ + sync + tlbsync + sync + stw r6,4(r3) /* put in correct RPN, WIMG, PP bits */ + sync + oris r5,r5,0x8000 + stw r5,0(r3) /* finally set V bit in PTE */ +#endif /* __SMP__ */ /* * Update the hash table miss count. We only want misses here @@ -1380,6 +1449,7 @@ found_slot: tophys(r2,r2,r6) li r0,0 stw r0,hash_table_lock@l(r2) + eieio #endif /* Return from the exception */ @@ -1398,17 +1468,22 @@ found_slot: REST_GPR(20, r21) REST_2GPRS(22, r21) lwz r21,GPR21(r21) - SYNC rfi -hash_page_out: #ifdef __SMP__ +hash_page_out: lis r2,hash_table_lock@ha tophys(r2,r2,r6) li r0,0 stw r0,hash_table_lock@l(r2) -#endif + eieio blr + + .globl hash_table_lock +hash_table_lock: + .long 0 +#endif + next_slot: .long 0 @@ -1420,27 +1495,25 @@ load_up_fpu: * On SMP we know the fpu is free, since we give it up every * switch. -- Cort */ + mfmsr r5 + ori r5,r5,MSR_FP + SYNC + mtmsr r5 /* enable use of fpu now */ + SYNC +/* + * For SMP, we don't do lazy FPU switching because it just gets too + * horrendously complex, especially when a task switches from one CPU + * to another. Instead we call giveup_fpu in switch_to. + */ +#ifndef __SMP__ #ifndef CONFIG_APUS lis r6,-KERNELBASE@h #else lis r6,CYBERBASEp@h lwz r6,0(r6) #endif - addis r3,r6,last_task_used_math@ha lwz r4,last_task_used_math@l(r3) - mfmsr r5 - ori r5,r5,MSR_FP - SYNC - mtmsr r5 /* enable use of fpu now */ -/* - * 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 - */ -#ifndef __SMP__ - SYNC cmpi 0,r4,0 beq 1f add r4,r4,r6 @@ -1454,15 +1527,17 @@ load_up_fpu: li r20,MSR_FP|MSR_FE0|MSR_FE1 andc r4,r4,r20 /* disable FP for previous task */ stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: #endif /* __SMP__ */ -1: ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 /* enable use of FP after return */ + /* enable use of FP after return */ + ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 mfspr r5,SPRG3 /* current task's TSS (phys) */ lfd fr0,TSS_FPSCR-4(r5) mtfsf 0xff,fr0 REST_32FPRS(0, r5) +#ifndef __SMP__ subi r4,r5,TSS sub r4,r4,r6 -#ifndef __SMP__ stw r4,last_task_used_math@l(r3) #endif /* __SMP__ */ /* restore registers and return */ @@ -1499,48 +1574,44 @@ KernelFP: .align 4 /* - * Disable FP for the task which had the FPU previously, - * and save its floating-point registers in its thread_struct. + * giveup_fpu(tsk) + * Disable FP for the task given as the argument, + * and save the 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 mtmsr r5 /* enable use of fpu now */ SYNC - cmpi 0,r4,0 + cmpi 0,r3,0 beqlr- /* if no previous owner, done */ - addi r4,r4,TSS /* want TSS of last_task_used_math */ + addi r3,r3,TSS /* want TSS of task */ + lwz r5,PT_REGS(r3) + cmpi 0,r5,0 + SAVE_32FPRS(0, r3) + mffs fr0 + stfd fr0,TSS_FPSCR-4(r3) + beq 1f + lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r3,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r3 /* disable FP for previous task */ + stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: #ifndef __SMP__ li r5,0 - stw r5,last_task_used_math@l(r3) + lis r4,last_task_used_math@ha + stw r5,last_task_used_math@l(r4) #endif /* __SMP__ */ - SAVE_32FPRS(0, r4) - mffs fr0 - stfd fr0,TSS_FPSCR-4(r4) - lwz r5,PT_REGS(r4) - lwz r3,_MSR-STACK_FRAME_OVERHEAD(r5) - li r4,MSR_FP|MSR_FE0|MSR_FE1 - andc r3,r3,r4 /* disable FP for previous task */ - stw r3,_MSR-STACK_FRAME_OVERHEAD(r5) + blr + #else /* CONFIG_8xx */ .globl giveup_fpu giveup_fpu: -#endif /* CONFIG_8xx */ blr +#endif /* CONFIG_8xx */ /* * This code is jumped to from the startup code to copy @@ -1600,10 +1671,38 @@ copy_and_flush: . = 0x4000 #endif +#ifdef CONFIG_SMP + .globl __secondary_start_psurge +__secondary_start_psurge: + li r24,1 /* cpu # */ + b __secondary_start + + .globl __secondary_hold +__secondary_hold: + /* tell the master we're here */ + lis r5,0x4@h + ori r5,r5,0x4@l + stw r3,0(r5) + dcbf 0,r5 +100: + lis r5,0 + dcbi 0,r5 + lwz r4,0(r5) + /* wait until we're told to start */ + cmp 0,r4,r3 + bne 100b + /* our cpu # was at addr 0 - go */ + lis r5,__secondary_start@h + ori r5,r5,__secondary_start@l + tophys(r5,r5,r4) + mtlr r5 + mr r24,r3 /* cpu # */ + blr +#endif /* CONFIG_SMP */ + /* * This is where the main kernel code starts. */ - start_here: #ifndef CONFIG_8xx /* @@ -1650,9 +1749,9 @@ start_here: /* get current */ lis r2,current_set@h ori r2,r2,current_set@l - addi r2,r2,4 + slwi r24,r24,2 /* cpu # to current_set[cpu#] */ + add r2,r2,r24 lwz r2,0(r2) - b 10f 99: #endif /* __SMP__ */ @@ -1677,12 +1776,10 @@ start_here: #ifdef __SMP__ 10: #endif /* __SMP__ */ - /* stack */ addi r1,r2,TASK_UNION_SIZE li r0,0 stwu r0,-STACK_FRAME_OVERHEAD(r1) - /* * Decide what sort of machine this is and initialize the MMU. */ @@ -1693,7 +1790,6 @@ start_here: mr r7,r27 bl identify_machine bl MMU_init - /* * Go back to running unmapped so we can load up new values * for SDR1 (hash table pointer) and the segment registers @@ -1725,8 +1821,10 @@ start_here: 2: SYNC /* Force all PTE updates to finish */ tlbia /* Clear all TLB entries */ + sync /* wait for tlbia/tlbie to finish */ #ifdef __SMP__ - tlbsync + tlbsync /* ... on all CPUs */ + sync #endif #ifndef CONFIG_8xx mtspr SDR1,r6 @@ -1947,8 +2045,9 @@ _GLOBAL(_switch) stw r0,GPR0(r1) lwz r0,0(r1) stw r0,GPR1(r1) - SAVE_10GPRS(2, r1) - SAVE_10GPRS(12, r1) + /* r3-r13 are caller saved -- Cort */ + SAVE_GPR(2, r1) + SAVE_8GPRS(14, r1) SAVE_10GPRS(22, r1) mflr r20 /* Return to switch caller */ mfmsr r22 @@ -1971,46 +2070,71 @@ _GLOBAL(_switch) mtspr SPRG3,r0 /* Update current TSS phys addr */ SYNC lwz r1,KSP(r4) /* Load new stack pointer */ + /* save the old current 'last' for return value */ + mr r3,r2 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 */ - li r0,8 /* TASK_SIZE / SEGMENT_SIZE */ + li r0,12 /* TASK_SIZE / SEGMENT_SIZE */ mtctr r0 - li r3,0 -3: mtsrin r5,r3 + li r9,0 +3: mtsrin r5,r9 addi r5,r5,1 /* next VSID */ - addis r3,r3,0x1000 /* address of next segment */ + addis r9,r9,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". */ - lwz r3,MM-TSS(r4) /* Get virtual address of mm */ - lwz r3,PGD(r3) /* get new->mm->pgd */ - addis r3,r3,-KERNELBASE@h /* convert to phys addr */ - mtspr M_TWB, r3 /* Update MMU base address */ + lwz r9,MM-TSS(r4) /* Get virtual address of mm */ + lwz r9,PGD(r9) /* get new->mm->pgd */ + addis r9,r9,-KERNELBASE@h /* convert to phys addr */ + mtspr M_TWB, r9 /* Update MMU base address */ mtspr M_CASID, r5 /* Update context */ tlbia #endif SYNC - -/* FALL THROUGH into int_return */ -#ifdef __SMP__ - /* call schedule_tail if this is the first time for a child process */ - lwz r5,TSS_SMP_FORK_RET(r4) - cmpi 0,r5,0 - beq+ int_return - li r3,0 - stw r3,TSS_SMP_FORK_RET(r4) - bl schedule_tail -#endif /* __SMP__ */ +2: lwz r9,_MSR(r1) /* Returning to user mode? */ + andi. r9,r9,MSR_PR + beq+ 10f /* if not, don't adjust kernel stack */ +8: addi r4,r1,INT_FRAME_SIZE+STACK_UNDERHEAD /* size of frame */ + stw r4,TSS+KSP(r2) /* save kernel stack pointer */ + tophys(r9,r1,r9) + mtspr SPRG2,r9 /* phys exception stack pointer */ +10: lwz r2,_CTR(r1) + lwz r0,_LINK(r1) + mtctr r2 + mtlr r0 + lwz r2,_XER(r1) + lwz r0,_CCR(r1) + mtspr XER,r2 + mtcrf 0xFF,r0 + /* r3-r13 are destroyed -- Cort */ + REST_GPR(14, r1) + REST_8GPRS(15, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + lwz r2,_NIP(r1) /* Restore environment */ + lwz r0,_MSR(r1) + mtspr SRR0,r2 + mtspr SRR1,r0 + lwz r0,GPR0(r1) + lwz r2,GPR2(r1) + lwz r1,GPR1(r1) + SYNC + rfi /* * Trap exit. */ +#ifdef __SMP__ + .globl ret_from_smpfork +ret_from_smpfork: + bl schedule_tail +#endif .globl ret_from_syscall ret_from_syscall: .globl int_return @@ -2025,8 +2149,8 @@ int_return: lwz r5,_MSR(r1) and. r5,r5,r4 beq 2f -3: lis r4,n_lost_interrupts@ha - lwz r4,n_lost_interrupts@l(r4) +3: lis r4,ppc_n_lost_interrupts@ha + lwz r4,ppc_n_lost_interrupts@l(r4) cmpi 0,r4,0 beq+ 1f addi r3,r1,STACK_FRAME_OVERHEAD @@ -2118,7 +2242,7 @@ _GLOBAL(fake_interrupt) _GLOBAL(set_context) rlwinm r3,r3,4,8,27 /* VSID = context << 4 */ addis r3,r3,0x6000 /* Set Ks, Ku bits */ - li r0,8 /* TASK_SIZE / SEGMENT_SIZE */ + li r0,12 /* TASK_SIZE / SEGMENT_SIZE */ mtctr r0 li r4,0 3: mtsrin r3,r4 @@ -2177,6 +2301,27 @@ _GLOBAL(flush_icache_range) blr /* + * Like above, but only do the D-cache. This is used by the 8xx + * to push the cache so the CPM doesn't get stale data. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + */ +_GLOBAL(flush_dcache_range) + li r5,CACHE_LINE_SIZE-1 + andc r3,r3,r5 + subf r4,r3,r4 + add r4,r4,r5 + srwi. r4,r4,LG_CACHE_LINE_SIZE + beqlr + mtctr r4 + +1: dcbst 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + sync /* wait for dcbst's to get to ram */ + blr + +/* * Flush a particular page from the DATA cache * Note: this is necessary because the instruction cache does *not* * snoop from the data cache. @@ -2207,25 +2352,35 @@ _GLOBAL(flush_page_to_ram) blr /* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + li r0,4096/CACHE_LINE_SIZE + mtctr r0 +1: dcbz 0,r3 + addi r3,r3,CACHE_LINE_SIZE + bdnz 1b + blr + +/* * Flush entries from the hash table with VSIDs in the range * given. */ #ifndef CONFIG_8xx _GLOBAL(flush_hash_segments) + lis r5,Hash@ha + lwz r5,Hash@l(r5) /* base of hash table */ #ifdef NO_RELOAD_HTAB -/* - * Bitmask of PVR numbers of 603-like chips, - * for which we don't use the hash table at all. - */ -#define PVR_603_LIKE 0x13000000 /* bits 3, 6, 7 set */ - - mfspr r0,PVR - rlwinm r0,r0,16,27,31 - lis r9,PVR_603_LIKE@h - rlwnm. r0,r9,r0,0,0 - beq+ 99f + cmpwi 0,r5,0 + bne+ 99f tlbia - isync + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr 99: #endif /* NO_RELOAD_HTAB */ @@ -2247,14 +2402,13 @@ _GLOBAL(flush_hash_segments) bne- 10b stwcx. r8,0,r9 bne- 10b + eieio #endif 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 */ oris r4,r4,0x8000 ori r4,r4,0x7f - lis r5,Hash@ha - lwz r5,Hash@l(r5) /* base of hash table */ lis r6,Hash_size@ha lwz r6,Hash_size@l(r6) /* size in bytes */ srwi r6,r6,3 /* # PTEs */ @@ -2270,11 +2424,11 @@ _GLOBAL(flush_hash_segments) 2: bdnz 1b /* continue with loop */ sync tlbia - isync + sync #ifdef __SMP__ tlbsync + sync lis r3,hash_table_lock@ha - li r0,0 stw r0,hash_table_lock@l(r3) mtmsr r10 SYNC @@ -2287,14 +2441,17 @@ _GLOBAL(flush_hash_segments) * flush_hash_page(unsigned context, unsigned long va) */ _GLOBAL(flush_hash_page) + lis r6,Hash@ha + lwz r6,Hash@l(r6) /* hash table base */ #ifdef NO_RELOAD_HTAB - mfspr r0,PVR - rlwinm r0,r0,16,27,31 - lis r9,PVR_603_LIKE@h - rlwnm. r0,r9,r0,0,0 - beq+ 99f + cmpwi 0,r6,0 /* hash table in use? */ + bne+ 99f tlbie r4 /* in hw tlb too */ - isync + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr 99: #endif /* NO_RELOAD_HTAB */ @@ -2311,11 +2468,12 @@ _GLOBAL(flush_hash_page) ori r9,r9,hash_table_lock@l lwz r8,PROCESSOR(r2) oris r8,r8,9 -10: lwarx r6,0,r9 - cmpi 0,r6,0 +10: lwarx r7,0,r9 + cmpi 0,r7,0 bne- 10b stwcx. r8,0,r9 bne- 10b + eieio #endif rlwinm r3,r3,11,1,20 /* put context into vsid */ rlwimi r3,r4,11,21,24 /* put top 4 bits of va into vsid */ @@ -2328,8 +2486,6 @@ _GLOBAL(flush_hash_page) lwz r5,Hash_mask@l(r5) /* hash mask */ slwi r5,r5,6 /* << 6 */ and r7,r7,r5 - lis r6,Hash@ha - lwz r6,Hash@l(r6) /* hash table base */ add r6,r6,r7 /* address of primary PTEG */ li r8,8 mtctr r8 @@ -2350,9 +2506,10 @@ _GLOBAL(flush_hash_page) stw r0,0(r7) /* invalidate entry */ 4: sync tlbie r4 /* in hw tlb too */ - isync + sync #ifdef __SMP__ tlbsync + sync lis r3,hash_table_lock@h li r0,0 stw r0,hash_table_lock@l(r3) @@ -2418,30 +2575,27 @@ enter_rtas: rfi /* return to caller */ #endif /* CONFIG_8xx */ -#ifdef CONFIG_MBX -/* Jump into the system reset for the MBX rom. +#ifdef CONFIG_8xx +/* Jump into the system reset for the 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. + * r3 is the board info structure, r4 is the location for starting. + * I use this for building a small kernel that can load other kernels, + * rather than trying to write or rely on a rom monitor that can tftp load. */ - .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 + .globl m8xx_gorom +m8xx_gorom: + li r5,MSR_KERNEL & ~(MSR_IR|MSR_DR) + lis r6,2f@h + addis r6,r6,-KERNELBASE@h + ori r6,r6,2f@l + mtspr SRR0,r6 + mtspr SRR1,r5 + rfi 2: - lis r4, 0xfe000000@h - addi r4, r4, 0xfe000000@l - mtlr r4 - blr -#endif /* CONFIG_MBX */ + mtlr r4 + blr +#endif /* CONFIG_8xx */ /* * We put a few things here that have to be page-aligned. diff --git a/arch/ppc/kernel/i8259.c b/arch/ppc/kernel/i8259.c new file mode 100644 index 000000000..4ec6d3e11 --- /dev/null +++ b/arch/ppc/kernel/i8259.c @@ -0,0 +1,130 @@ + +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <asm/io.h> +#include "i8259.h" + +unsigned char cached_8259[2] = { 0xff, 0xff }; +#define cached_A1 (cached_8259[0]) +#define cached_21 (cached_8259[1]) + +int i8259_irq(int cpu) +{ + int irq; + + /* + * Perform an interrupt acknowledge cycle on controller 1 + */ + outb(0x0C, 0x20); + irq = inb(0x20) & 7; + if (irq == 2) + { + /* + * Interrupt is cascaded so perform interrupt + * acknowledge on controller 2 + */ + outb(0x0C, 0xA0); + irq = (inb(0xA0) & 7) + 8; + } + else if (irq==7) + { + /* + * This may be a spurious interrupt + * + * Read the interrupt status register. If the most + * significant bit is not set then there is no valid + * interrupt + */ + outb(0x0b, 0x20); + if(~inb(0x20)&0x80) + return -1; + } + return irq; +} + +static void i8259_mask_and_ack_irq(unsigned int irq_nr) +{ + if ( irq_nr >= i8259_pic.irq_offset ) + irq_nr -= i8259_pic.irq_offset; + + if (irq_nr > 7) { + cached_A1 |= 1 << (irq_nr-8); + inb(0xA1); /* DUMMY */ + outb(cached_A1,0xA1); + outb(0x20,0xA0); /* Non-specific EOI */ + outb(0x20,0x20); /* Non-specific EOI to cascade */ + } else { + cached_21 |= 1 << irq_nr; + inb(0x21); /* DUMMY */ + outb(cached_21,0x21); + outb(0x20,0x20); /* Non-specific EOI */ + } +} + +static void i8259_set_irq_mask(int irq_nr) +{ + outb(cached_A1,0xA1); + outb(cached_21,0x21); +} + +static void i8259_mask_irq(unsigned int irq_nr) +{ + if ( irq_nr >= i8259_pic.irq_offset ) + irq_nr -= i8259_pic.irq_offset; + if ( irq_nr < 8 ) + cached_21 |= 1 << irq_nr; + else + cached_A1 |= 1 << (irq_nr-8); + i8259_set_irq_mask(irq_nr); +} + +static void i8259_unmask_irq(unsigned int irq_nr) +{ + + if ( irq_nr >= i8259_pic.irq_offset ) + irq_nr -= i8259_pic.irq_offset; + if ( irq_nr < 8 ) + cached_21 &= ~(1 << irq_nr); + else + cached_A1 &= ~(1 << (irq_nr-8)); + i8259_set_irq_mask(irq_nr); +} + +struct hw_interrupt_type i8259_pic = { + " i8259 ", + NULL, + NULL, + NULL, + i8259_unmask_irq, + i8259_mask_irq, + i8259_mask_and_ack_irq, + 0 +}; + +static void +no_action(int cpl, void *dev_id, struct pt_regs *regs) +{ +} + +void __init i8259_init(void) +{ + /* init master interrupt controller */ + outb(0x11, 0x20); /* Start init sequence */ + outb(0x00, 0x21); /* Vector base */ + outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0x21); /* Select 8086 mode */ + outb(0xFF, 0x21); /* Mask all */ + /* init slave interrupt controller */ + outb(0x11, 0xA0); /* Start init sequence */ + outb(0x08, 0xA1); /* Vector base */ + outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0xA1); /* Select 8086 mode */ + outb(0xFF, 0xA1); /* Mask all */ + outb(cached_A1, 0xA1); + outb(cached_21, 0x21); + request_irq( i8259_pic.irq_offset + 2, no_action, SA_INTERRUPT, + "82c59 secondary cascade", NULL ); + enable_irq(i8259_pic.irq_offset + 2); /* Enable cascade interrupt */ +} diff --git a/arch/ppc/kernel/i8259.h b/arch/ppc/kernel/i8259.h new file mode 100644 index 000000000..a1d6df0a1 --- /dev/null +++ b/arch/ppc/kernel/i8259.h @@ -0,0 +1,12 @@ + +#ifndef _PPC_KERNEL_i8259_H +#define _PPC_KERNEL_i8259_H + +#include "local_irq.h" + +extern struct hw_interrupt_type i8259_pic; + +void i8259_init(void); +int i8259_irq(int); + +#endif /* _PPC_KERNEL_i8259_H */ diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index af163699b..c68d1e63e 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -1,5 +1,5 @@ /* - * $Id: idle.c,v 1.57 1998/12/28 10:28:46 paulus Exp $ + * $Id: idle.c,v 1.61 1999/03/18 04:15:45 cort Exp $ * * Idle daemon for PowerPC. Idle daemon will handle any action * that needs to be taken when the system becomes idle. @@ -39,6 +39,12 @@ unsigned long htab_reclaim_on = 0; unsigned long zero_paged_on = 0; unsigned long powersave_nap = 0; +unsigned long *zero_cache; /* head linked list of pre-zero'd pages */ +unsigned long zero_sz; /* # currently pre-zero'd pages */ +unsigned long zeropage_hits; /* # zero'd pages request that we've done */ +unsigned long zeropage_calls; /* # zero'd pages request that've been made */ +unsigned long zerototal; /* # pages zero'd over time */ + int idled(void *unused) { /* endless loop with no priority at all */ @@ -108,8 +114,6 @@ void inline htab_reclaim(void) /* if we don't have a htab */ if ( Hash_size == 0 ) return; - lock_dcache(1); - #if 0 /* find a random place in the htab to start each time */ start = &Hash[jiffies%(Hash_size/sizeof(PTE))]; @@ -147,7 +151,6 @@ void inline htab_reclaim(void) } out: if ( current->need_resched ) printk("need_resched: %lx\n", current->need_resched); - unlock_dcache(); #endif /* CONFIG_8xx */ } @@ -159,7 +162,7 @@ unsigned long get_zero_page_fast(void) { unsigned long page = 0; - atomic_inc((atomic_t *)&quicklists.zeropage_calls); + atomic_inc((atomic_t *)&zero_cache_calls); if ( zero_quicklist ) { /* atomically remove this page from the list */ @@ -177,10 +180,10 @@ unsigned long get_zero_page_fast(void) #endif /* __SMP__ */ /* we can update zerocount after the fact since it is not * used for anything but control of a loop which doesn't - * matter since it won't affect anything if it zero's one + * matter since it won't affect anything if it zeros one * less page -- Cort */ - atomic_inc((atomic_t *)&quicklists.zeropage_hits); + atomic_inc((atomic_t *)&zero_cache_hits); atomic_dec((atomic_t *)&zero_cache_sz); /* zero out the pointer to next in the page */ @@ -222,7 +225,6 @@ void zero_paged(void) /* * Make the page no cache so we don't blow our cache with 0's - * We should just turn off the cache instead. -- Cort */ pte = find_pte(init_task.mm, pageptr); if ( !pte ) @@ -254,8 +256,8 @@ void zero_paged(void) * So we update the list atomically without locking it. * -- Cort */ + /* turn cache on for this page */ - pte_cache(*pte); flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); /* atomically add this page to the list */ @@ -280,7 +282,7 @@ void zero_paged(void) * reads it. -- Cort */ atomic_inc((atomic_t *)&zero_cache_sz); - atomic_inc((atomic_t *)&quicklists.zerototal); + atomic_inc((atomic_t *)&zero_cache_total); } } @@ -301,11 +303,17 @@ void power_save(void) hid0 &= ~(HID0_NAP | HID0_SLEEP | HID0_DOZE); hid0 |= (powersave_nap? HID0_NAP: HID0_DOZE) | HID0_DPM; asm("mtspr 1008,%0" : : "r" (hid0)); - msr |= MSR_POW; + + /* set the POW bit in the MSR, and enable interrupts + * so we wake up sometime! */ + _nmask_and_or_msr(0, MSR_POW | MSR_EE); + + /* Disable interrupts again so restore_flags will + * work. */ + _nmask_and_or_msr(MSR_EE, 0); } restore_flags(msr); default: return; } - } diff --git a/arch/ppc/kernel/indirect_pci.c b/arch/ppc/kernel/indirect_pci.c new file mode 100644 index 000000000..641d77a52 --- /dev/null +++ b/arch/ppc/kernel/indirect_pci.c @@ -0,0 +1,121 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (C) 1998 Gabriel Paubert. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/pci.h> +#include <asm/io.h> +#include <asm/system.h> + +unsigned int * pci_config_address; +unsigned char * pci_config_data; + +int indirect_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + unsigned flags; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + *val= in_8(pci_config_data + (offset&3)); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + unsigned flags; + + if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + *val= in_le16((unsigned short *)(pci_config_data + (offset&3))); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + unsigned flags; + + if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + *val= in_le32((unsigned *)pci_config_data); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + unsigned flags; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + out_8(pci_config_data + (offset&3), val); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + unsigned flags; + + if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + out_le16((unsigned short *)(pci_config_data + (offset&3)), val); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + unsigned flags; + + if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; + + save_flags(flags); cli(); + + out_be32(pci_config_address, + ((offset&0xfc)<<24) | (dev_fn<<16) | (bus<<8) | 0x80); + + out_le32((unsigned *)pci_config_data, val); + + restore_flags(flags); + return PCIBIOS_SUCCESSFUL; +} diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index f4a7c7143..461e51aa1 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -1,5 +1,5 @@ /* - * $Id: irq.c,v 1.91 1998/12/28 10:28:47 paulus Exp $ + * $Id: irq.c,v 1.105 1999/03/25 19:51:51 cort Exp $ * * arch/ppc/kernel/irq.c * @@ -42,6 +42,7 @@ #include <linux/malloc.h> #include <linux/openpic.h> #include <linux/pci.h> +#include <linux/delay.h> #include <asm/bitops.h> #include <asm/hydra.h> @@ -56,101 +57,46 @@ #include <asm/amigaints.h> #include <asm/amigahw.h> #include <asm/amigappc.h> -#ifdef CONFIG_8xx -#include <asm/8xx_immap.h> -#include <asm/mbx.h> -#endif +#include <asm/ptrace.h> + +#include "local_irq.h" -extern void process_int(unsigned long vec, struct pt_regs *fp); -extern void apus_init_IRQ(void); -extern void amiga_disable_irq(unsigned int irq); -extern void amiga_enable_irq(unsigned int irq); -static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } -static volatile unsigned char *chrp_int_ack_special; extern volatile unsigned long ipi_count; -static void pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base); +void enable_irq(unsigned int irq_nr); +void disable_irq(unsigned int irq_nr); + +/* Fixme - Need to figure out a way to get rid of this - Corey */ +volatile unsigned char *chrp_int_ack_special; #ifdef CONFIG_APUS /* Rename a few functions. Requires the CONFIG_APUS protection. */ #define request_irq nop_ppc_request_irq #define free_irq nop_ppc_free_irq #define get_irq_list nop_get_irq_list -#endif -#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 */ - #define VEC_SPUR (24) -#undef SHOW_IRQ -#undef SHOW_GATWICK_IRQS -#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) -#define cached_21 (((char *)(cached_irq_mask))[3]) -#define cached_A1 (((char *)(cached_irq_mask))[2]) -#define PREP_IRQ_MASK (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21 - -unsigned int local_bh_count[NR_CPUS]; -unsigned int local_irq_count[NR_CPUS]; -int max_irqs; -int max_real_irqs; -static struct irqaction *irq_action[NR_IRQS]; -static int spurious_interrupts = 0; -static unsigned int cached_irq_mask[NR_MASK_WORDS]; -unsigned int lost_interrupts[NR_MASK_WORDS]; -atomic_t n_lost_interrupts; - -/* pmac */ -struct pmac_irq_hw { - unsigned int flag; - unsigned int enable; - unsigned int ack; - unsigned int level; -}; +#endif -/* XXX these addresses should be obtained from the device tree */ -volatile struct pmac_irq_hw *pmac_irq_hw[4] = { - (struct pmac_irq_hw *) 0xf3000020, - (struct pmac_irq_hw *) 0xf3000010, - (struct pmac_irq_hw *) 0xf4000020, - (struct pmac_irq_hw *) 0xf4000010, -}; +#define MAXCOUNT 10000000 -/* This is the interrupt used on the main controller for the secondary - controller. Happens on PowerBooks G3 Series (a second mac-io) - -- BenH - */ -static int second_irq = -999; +#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) -/* Returns the number of 0's to the left of the most significant 1 bit */ -static inline int cntlzw(int bits) -{ - int lz; +int ppc_spurious_interrupts = 0; - asm ("cntlzw %0,%1" : "=r" (lz) : "r" (bits)); - return lz; -} +unsigned int ppc_local_bh_count[NR_CPUS]; +unsigned int ppc_local_irq_count[NR_CPUS]; +struct irqaction *ppc_irq_action[NR_IRQS]; +unsigned int ppc_cached_irq_mask[NR_MASK_WORDS]; +unsigned int ppc_lost_interrupts[NR_MASK_WORDS]; +atomic_t ppc_n_lost_interrupts; -static inline void sync(void) -{ - asm volatile ("sync"); -} /* 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. + * can't 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]; +static struct irqaction malloc_cache[8]; extern int mem_init_done; void *irq_kmalloc(size_t size, int pri) @@ -179,168 +125,80 @@ void irq_kfree(void *ptr) kfree(ptr); } -#ifndef CONFIG_8xx -void i8259_mask_and_ack_irq(int irq_nr) -{ - /* spin_lock(&irq_controller_lock);*/ - cached_irq_mask[0] |= 1 << irq_nr; - if (irq_nr > 7) { - inb(0xA1); /* DUMMY */ - outb(cached_A1,0xA1); - outb(0x62,0x20); /* Specific EOI to cascade */ - /*outb(0x20,0xA0);*/ - outb(0x60|(irq_nr-8), 0xA0); /* specific eoi */ - } else { - inb(0x21); /* DUMMY */ - outb(cached_21,0x21); - /*outb(0x20,0x20);*/ - outb(0x60|irq_nr,0x20); /* specific eoi */ - - } - /* spin_unlock(&irq_controller_lock);*/ -} - -void __pmac pmac_mask_and_ack_irq(int irq_nr) -{ - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; - - if ((unsigned)irq_nr >= max_irqs) - return; - /*spin_lock(&irq_controller_lock);*/ - - clear_bit(irq_nr, cached_irq_mask); - if (test_and_clear_bit(irq_nr, lost_interrupts)) - atomic_dec(&n_lost_interrupts); - out_le32(&pmac_irq_hw[i]->ack, bit); - out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]); - out_le32(&pmac_irq_hw[i]->ack, bit); - /* make sure ack gets to controller before we enable interrupts */ - sync(); - - /*spin_unlock(&irq_controller_lock);*/ - /*if ( irq_controller_lock.lock ) - panic("irq controller lock still held in mask and ack\n");*/ -} +struct irqdesc irq_desc[NR_IRQS] = {{0, 0}, }; -void __openfirmware chrp_mask_and_ack_irq(int irq_nr) +int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) { - /* spinlocks are done by i8259_mask_and_ack() - Cort */ - if (is_8259_irq(irq_nr)) - i8259_mask_and_ack_irq(irq_nr); -} - + struct irqaction *old, **p, *action; + unsigned long flags; -static void i8259_set_irq_mask(int irq_nr) -{ - if (irq_nr > 7) { - outb(cached_A1,0xA1); - } else { - outb(cached_21,0x21); + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + { + /* Free */ + for (p = &irq_desc[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; } -} - -static void __pmac pmac_set_irq_mask(int irq_nr) -{ - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; - - if ((unsigned)irq_nr >= max_irqs) - return; - - /* enable unmasked interrupts */ - out_le32(&pmac_irq_hw[i]->enable, cached_irq_mask[i]); - - /* - * Unfortunately, setting the bit in the enable register - * when the device interrupt is already on *doesn't* set - * the bit in the flag register or request another interrupt. - */ - if ((bit & cached_irq_mask[i]) - && (ld_le32(&pmac_irq_hw[i]->level) & bit) - && !(ld_le32(&pmac_irq_hw[i]->flag) & bit)) { - if (!test_and_set_bit(irq_nr, lost_interrupts)) - atomic_inc(&n_lost_interrupts); + + 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); + + p = &irq_desc[irq].action; + + 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); } -} - -/* - * These have to be protected by the spinlock - * before being called. - */ -static void i8259_mask_irq(unsigned int irq_nr) -{ - cached_irq_mask[0] |= 1 << irq_nr; - i8259_set_irq_mask(irq_nr); -} - -static void i8259_unmask_irq(unsigned int irq_nr) -{ - cached_irq_mask[0] &= ~(1 << irq_nr); - i8259_set_irq_mask(irq_nr); -} - -static void __pmac pmac_mask_irq(unsigned int irq_nr) -{ - clear_bit(irq_nr, cached_irq_mask); - pmac_set_irq_mask(irq_nr); - sync(); -} - -static void __pmac pmac_unmask_irq(unsigned int irq_nr) -{ - set_bit(irq_nr, cached_irq_mask); - pmac_set_irq_mask(irq_nr); -} - -static void __openfirmware chrp_mask_irq(unsigned int irq_nr) -{ - if (is_8259_irq(irq_nr)) - i8259_mask_irq(irq_nr); - else - openpic_disable_irq(irq_to_openpic(irq_nr)); -} + *p = action; -static void __openfirmware chrp_unmask_irq(unsigned int irq_nr) -{ - if (is_8259_irq(irq_nr)) - i8259_unmask_irq(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[0] &= ~(1 << (31-irq_nr)); - ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = - cached_irq_mask[0]; + restore_flags(flags); + return 0; } -static void mbx_unmask_irq(unsigned int irq_nr) +void free_irq(unsigned int irq, void *dev_id) { - cached_irq_mask[0] |= (1 << (31-irq_nr)); - ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = - cached_irq_mask[0]; + request_irq(irq, NULL, 0, NULL, dev_id); } -#endif /* CONFIG_8xx */ void disable_irq(unsigned int irq_nr) { - /*unsigned long flags;*/ - - /* spin_lock_irqsave(&irq_controller_lock, flags);*/ mask_irq(irq_nr); - /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/ synchronize_irq(); } void enable_irq(unsigned int irq_nr) { - /*unsigned long flags;*/ - - /* spin_lock_irqsave(&irq_controller_lock, flags);*/ unmask_irq(irq_nr); - /* spin_unlock_irqrestore(&irq_controller_lock, flags);*/ } int get_irq_list(char *buf) @@ -354,8 +212,8 @@ int get_irq_list(char *buf) *(char *)(buf+len++) = '\n'; for (i = 0 ; i < NR_IRQS ; i++) { - action = irq_action[i]; - if ((!action || !action->handler) && (i != second_irq)) + action = irq_desc[i].action; + if ( !action || !action->handler ) continue; len += sprintf(buf+len, "%3d: ", i); #ifdef __SMP__ @@ -365,56 +223,83 @@ int get_irq_list(char *buf) #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: - if (i < 64) - len += sprintf(buf+len, " PMAC-PIC "); - else - len += sprintf(buf+len, " GATWICK "); - break; - case _MACH_chrp: - if ( is_8259_irq(i) ) - len += sprintf(buf+len, " 82c59 "); - else - len += sprintf(buf+len, " OpenPIC "); - break; - case _MACH_mbx: - len += sprintf(buf+len, " MPC8xx "); - break; + if ( irq_desc[i].ctl ) + len += sprintf(buf+len, " %s ", irq_desc[i].ctl->typename ); + len += sprintf(buf+len, " %s",action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ", %s", action->name); } - - if (i != second_irq) { - 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"); - } else - len += sprintf(buf+len, " Gatwick secondary IRQ controller\n"); + len += sprintf(buf+len, "\n"); } #ifdef __SMP__ /* should this be per processor send/receive? */ - len += sprintf(buf+len, "IPI: %10lu", ipi_count); - for ( i = 0 ; i <= smp_num_cpus-1; i++ ) - len += sprintf(buf+len," "); - len += sprintf(buf+len, " interprocessor messages received\n"); + len += sprintf(buf+len, "IPI: %10lu\n", ipi_count); #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"); + len += sprintf(buf+len, "BAD: %10u\n", ppc_spurious_interrupts); return len; } - /* - * Global interrupt locks for SMP. Allow interrupts to come in on any - * CPU, yet make cli/sti act globally to protect critical regions.. + * Eventually, this should take an array of interrupts and an array size + * so it can dispatch multiple interrupts. */ +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq) +{ + int status; + struct irqaction *action; + int cpu = smp_processor_id(); + + mask_and_ack_irq(irq); + status = 0; + action = irq_desc[irq].action; + kstat.irqs[cpu][irq]++; + if (action && action->handler) { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while ( action ); + __cli(); + unmask_irq(irq); + } else { + ppc_spurious_interrupts++; + disable_irq( irq ); + } +} + +asmlinkage void do_IRQ(struct pt_regs *regs, int isfake) +{ + int cpu = smp_processor_id(); + + hardirq_enter(cpu); + ppc_md.do_IRQ(regs, cpu, isfake); + hardirq_exit(cpu); +} + +unsigned long probe_irq_on (void) +{ + return 0; +} + +int probe_irq_off (unsigned long irqs) +{ + return 0; +} + +void __init init_IRQ(void) +{ + static int once = 0; + + if ( once ) + return; + else + once++; + + ppc_md.init_IRQ(); +} + #ifdef __SMP__ unsigned char global_irq_holder = NO_PROC_ID; unsigned volatile int global_irq_lock; @@ -431,9 +316,13 @@ static void show(char * str) printk("\n%s, CPU %d:\n", str, cpu); printk("irq: %d [%d %d]\n", - atomic_read(&global_irq_count), local_irq_count[0], local_irq_count[1]); + atomic_read(&global_irq_count), + ppc_local_irq_count[0], + ppc_local_irq_count[1]); printk("bh: %d [%d %d]\n", - atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]); + atomic_read(&global_bh_count), + ppc_local_bh_count[0], + ppc_local_bh_count[1]); stack = (unsigned long *) &str; for (i = 40; i ; i--) { unsigned long x = *++stack; @@ -443,7 +332,6 @@ static void show(char * str) } } -#define MAXCOUNT 100000000 static inline void wait_on_bh(void) { int count = MAXCOUNT; @@ -469,7 +357,8 @@ static inline void wait_on_irq(int cpu) * already executing in one.. */ if (!atomic_read(&global_irq_count)) { - if (local_bh_count[cpu] || !atomic_read(&global_bh_count)) + if (ppc_local_bh_count[cpu] + || !atomic_read(&global_bh_count)) break; } @@ -490,7 +379,8 @@ static inline void wait_on_irq(int cpu) continue; if (global_irq_lock) continue; - if (!local_bh_count[cpu] && atomic_read(&global_bh_count)) + if (!ppc_local_bh_count[cpu] + && atomic_read(&global_bh_count)) continue; if (!test_and_set_bit(0,&global_irq_lock)) break; @@ -512,7 +402,6 @@ void synchronize_bh(void) wait_on_bh(); } - /* * This is called when we want to synchronize with * interrupts. We may for example tell a device to @@ -581,7 +470,7 @@ void __global_cli(void) if (flags & (1 << 15)) { int cpu = smp_processor_id(); __cli(); - if (!local_irq_count[cpu]) + if (!ppc_local_irq_count[cpu]) get_irqlock(cpu); } } @@ -590,7 +479,7 @@ void __global_sti(void) { int cpu = smp_processor_id(); - if (!local_irq_count[cpu]) + if (!ppc_local_irq_count[cpu]) release_irqlock(cpu); __sti(); } @@ -614,7 +503,7 @@ unsigned long __global_save_flags(void) retval = 2 + local_enabled; /* check for global flags if we're not in an interrupt */ - if (!local_irq_count[smp_processor_id()]) { + if (!ppc_local_irq_count[smp_processor_id()]) { if (local_enabled) retval = 1; if (global_irq_holder == (unsigned char) smp_processor_id()) @@ -623,6 +512,31 @@ unsigned long __global_save_flags(void) return retval; } +int +tb(long vals[], + int max_size) +{ + register unsigned long *orig_sp __asm__ ("r1"); + register unsigned long lr __asm__ ("r3"); + unsigned long *sp; + int i; + + asm volatile ("mflr 3"); + vals[0] = lr; + sp = (unsigned long *) *orig_sp; + sp = (unsigned long *) *sp; + for (i=1; i<max_size; i++) { + if (sp == 0) { + break; + } + + vals[i] = *(sp+1); + sp = (unsigned long *) *sp; + } + + return i; +} + void __global_restore_flags(unsigned long flags) { switch (flags) { @@ -639,559 +553,21 @@ void __global_restore_flags(unsigned long flags) __sti(); break; default: - printk("global_restore_flags: %08lx (%08lx)\n", - flags, (&flags)[-1]); - } -} - -#endif /* __SMP__ */ - -asmlinkage void do_IRQ(struct pt_regs *regs, int isfake) -{ - int irq; - unsigned long bits; - struct irqaction *action; - int cpu = smp_processor_id(); - 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 ) - { - if (!isfake) - { - extern void smp_message_recv(void); -#ifdef CONFIG_XMON - static int xmon_2nd; - if (xmon_2nd) - xmon(regs); -#endif - 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; - } - - { - unsigned int loops = MAXCOUNT; - while (test_bit(0, &global_irq_lock)) { - if (smp_processor_id() == global_irq_holder) { - printk("uh oh, interrupt while we hold global irq lock!\n"); -#ifdef CONFIG_XMON - xmon(0); -#endif - break; - } - if (loops-- == 0) { - printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder); -#ifdef CONFIG_XMON - xmon(0); -#endif - } - } - } -#endif /* __SMP__ */ - - switch ( _machine ) - { - case _MACH_Pmac: - for (irq = max_real_irqs - 1; irq > 0; irq -= 32) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | lost_interrupts[i]; - if (bits == 0) - continue; - irq -= cntlzw(bits); - break; - } - - /* Here, we handle interrupts coming from Gatwick, - * normal interrupt code will take care of acking and - * masking the irq on Gatwick itself but we ack&mask - * the Gatwick main interrupt on Heathrow now. It's - * unmasked later, after interrupt handling. -- BenH - */ - if (irq == second_irq) { - mask_and_ack_irq(second_irq); - for (irq = max_irqs - 1; irq > max_real_irqs; irq -= 32) { - int i = irq >> 5; - bits = ld_le32(&pmac_irq_hw[i]->flag) - | lost_interrupts[i]; - if (bits == 0) - continue; - irq -= cntlzw(bits); - break; - } - /* If not found, on exit, irq is 63 (128-1-32-32). - * We set it to -1 and revalidate second controller - */ - if (irq < max_real_irqs) { - irq = -1; - unmask_irq(second_irq); - } -#ifdef SHOW_GATWICK_IRQS - printk("Gatwick irq %d (i:%d, bits:0x%08lx\n", irq, i, bits); -#endif - } - - break; - case _MACH_chrp: - irq = openpic_irq(0); - if (irq == IRQ_8259_CASCADE) - { - /* - * This magic address generates a PCI IACK cycle. - * - * This should go in the above mask/ack code soon. -- Cort - */ - irq = *chrp_int_ack_special; - /* - * Acknowledge as soon as possible to allow i8259 - * interrupt nesting - */ - openpic_eoi(0); - openpic_eoi_done = 1; - } - else if (irq >= OPENPIC_VEC_TIMER) - { - /* - * OpenPIC interrupts >64 will be used for other purposes - * like interprocessor interrupts and hardware errors - */ - if (irq == OPENPIC_VEC_SPURIOUS) { - /* - * Spurious interrupts should never be - * acknowledged - */ - spurious_interrupts++; - openpic_eoi_done = 1; - } else { - /* - * Here we should process IPI timer - * for now the interrupt is dismissed. - */ - } - goto out; - } - break; - case _MACH_prep: - outb(0x0C, 0x20); - irq = inb(0x20) & 7; - if (irq == 2) - { -retry_cascade: - outb(0x0C, 0xA0); - irq = inb(0xA0); - /* if no intr left */ - if ( !(irq & 128 ) ) - goto out; - irq = (irq&7) + 8; - } - bits = 1UL << irq; - break; -#ifdef CONFIG_APUS - case _MACH_apus: { - int old_level, new_level; - - old_level = ~(regs->mq) & IPLEMU_IPLMASK; - new_level = (~(regs->mq) >> 3) & IPLEMU_IPLMASK; - - if (new_level == 0) - { - goto apus_out; - } - - APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); - APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET - | (~(new_level) & IPLEMU_IPLMASK))); - APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); - - process_int (VEC_SPUR+new_level, regs); - - APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_DISABLEINT); - APUS_WRITE(APUS_IPL_EMU, IPLEMU_IPLMASK); - APUS_WRITE(APUS_IPL_EMU, (IPLEMU_SETRESET - | (~(old_level) & IPLEMU_IPLMASK))); - -apus_out: - hardirq_exit(cpu); - APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT); - goto out2; - } -#endif - } - - if (irq < 0) { - /* we get here with Gatwick but the 'bogus' isn't correct in that case -- Cort */ - if ( irq != second_irq ) - { - printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", - irq, regs->nip); - spurious_interrupts++; - } - goto out; - } - -#else /* CONFIG_8xx */ - /* For MPC8xx, read the SIVEC register and shift the bits down - * to get the irq number. - */ - bits = ((immap_t *)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.irqs[cpu][irq]++; - if (action && action->handler) { - if (!(action->flags & SA_INTERRUPT)) - __sti(); - do { - status |= action->flags; - action->handler(irq, action->dev_id, regs); - action = action->next; - } while ( action ); - __cli(); - unmask_irq(irq); - } else { -#ifndef CONFIG_8xx - if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */ -#endif - spurious_interrupts++; - disable_irq( irq ); - } - - /* This was a gatwick sub-interrupt, we re-enable them on Heathrow - now */ - if (_machine == _MACH_Pmac && irq >= max_real_irqs) - unmask_irq(second_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); - -#ifdef CONFIG_APUS -out2: -#endif - /* 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 *old, **p, *action; - unsigned long flags; - -#ifdef SHOW_IRQ - printk("request_irq(): irq %d handler %08x name %s dev_id %04x\n", - irq,(int)handler,devname,(int)dev_id); -#endif /* SHOW_IRQ */ - - if (irq >= NR_IRQS) - return -EINVAL; - - /* Cannot allocate second controller IRQ */ - if (irq == second_irq) - return -EBUSY; + unsigned long trace[5]; + int count; + int i; - if (!handler) - { - /* 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); - 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) -{ - request_irq(irq, NULL, 0, NULL, dev_id); -} - -unsigned long probe_irq_on (void) -{ - return 0; -} - -int probe_irq_off (unsigned long irqs) -{ - return 0; -} - -#ifndef CONFIG_8xx -__initfunc(static void i8259_init(void)) -{ - /* init master interrupt controller */ - outb(0x11, 0x20); /* Start init sequence */ - outb(0x00, 0x21); /* Vector base */ - outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ - outb(0x01, 0x21); /* Select 8086 mode */ - outb(0xFF, 0x21); /* Mask all */ - - /* init slave interrupt controller */ - outb(0x11, 0xA0); /* Start init sequence */ - outb(0x08, 0xA1); /* Vector base */ - outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ - outb(0x01, 0xA1); /* Select 8086 mode */ - outb(0xFF, 0xA1); /* Mask all */ - outb(cached_A1, 0xA1); - outb(cached_21, 0x21); - if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL) != 0) - 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 *); - int i; - struct device_node *irqctrler; - unsigned long addr; - struct device_node *np; - -#ifndef CONFIG_8xx - switch (_machine) - { - case _MACH_Pmac: - mask_and_ack_irq = pmac_mask_and_ack_irq; - mask_irq = pmac_mask_irq; - unmask_irq = pmac_unmask_irq; - - /* G3 powermacs have 64 interrupts, G3 Series PowerBook have 128, - others have 32 */ - max_irqs = max_real_irqs = 32; - irqctrler = find_devices("mac-io"); - if (irqctrler) - { - max_real_irqs = 64; - if (irqctrler->next) - max_irqs = 128; - else - max_irqs = 64; - } - - /* get addresses of first controller */ - if (irqctrler) { - if (irqctrler->n_addrs > 0) { - addr = (unsigned long) - ioremap(irqctrler->addrs[0].address, 0x40); - for (i = 0; i < 2; ++i) - pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) - (addr + (2 - i) * 0x10); - } - - /* get addresses of second controller */ - irqctrler = (irqctrler->next) ? irqctrler->next : NULL; - if (irqctrler && irqctrler->n_addrs > 0) { - addr = (unsigned long) - ioremap(irqctrler->addrs[0].address, 0x40); - for (i = 2; i < 4; ++i) - pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) - (addr + (4 - i) * 0x10); - } - } - - /* disable all interrupts in all controllers */ - for (i = 0; i * 32 < max_irqs; ++i) - out_le32(&pmac_irq_hw[i]->enable, 0); - - - /* get interrupt line of secondary interrupt controller */ - if (irqctrler) { - second_irq = irqctrler->intrs[0].line; - printk(KERN_INFO "irq: secondary controller on irq %d\n", - (int)second_irq); - if (device_is_compatible(irqctrler, "gatwick")) - pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs); - enable_irq(second_irq); - } - printk("System has %d possible interrupts\n", max_irqs); - if (max_irqs != max_real_irqs) - printk(KERN_DEBUG "%d interrupts on main controller\n", - max_real_irqs); - -#ifdef CONFIG_XMON - request_irq(20, xmon_irq, 0, "NMI", 0); -#endif /* CONFIG_XMON */ - break; - case _MACH_chrp: - mask_and_ack_irq = chrp_mask_and_ack_irq; - mask_irq = chrp_mask_irq; - unmask_irq = chrp_unmask_irq; - - if ( !(np = find_devices("pci") ) ) - printk("Cannot find pci to get ack address\n"); - else - { - chrp_int_ack_special = (volatile unsigned char *) - (*(unsigned long *)get_property(np, - "8259-interrupt-acknowledge", NULL)); - } - openpic_init(1); - i8259_init(); - cached_irq_mask[0] = cached_irq_mask[1] = ~0UL; -#ifdef CONFIG_XMON - request_irq(openpic_to_irq(HYDRA_INT_ADB_NMI), - xmon_irq, 0, "NMI", 0); -#endif /* CONFIG_XMON */ - break; - case _MACH_prep: - mask_and_ack_irq = i8259_mask_and_ack_irq; - mask_irq = i8259_mask_irq; - unmask_irq = i8259_unmask_irq; - cached_irq_mask[0] = ~0UL; - - i8259_init(); - /* - * 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. - * - * power on default is 0's in both regs - all edge. - * - * These edge/level control regs allow edge/level status - * to be decided on a irq basis instead of on a PIC basis. - * It's still pretty ugly. - * - Cort - */ - { - unsigned char irq_mode1 = 0, irq_mode2 = 0; - irq_mode1 = 0; /* to get rid of compiler warnings */ - /* - * On Carolina, irq 15 and 13 must be level (scsi/ide/net). - */ - if ( _prep_type == _PREP_IBM ) - irq_mode2 |= 0xa0; + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + count = tb(trace, 5); + printk("tb:"); + for(i=0; i<count; i++) { + printk(" %8.8lx", trace[i]); } - break; -#ifdef CONFIG_APUS - case _MACH_apus: - mask_irq = amiga_disable_irq; - unmask_irq = amiga_enable_irq; - apus_init_IRQ(); - break; -#endif + printk("\n"); } -#endif /* CONFIG_8xx */ -} - -/* This routine will fix some missing interrupt values in the device tree - * on the gatwick mac-io controller used by some PowerBooks - */ -static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) -{ - struct device_node *node; - static struct interrupt_info int_pool[4]; - - memset(int_pool, 0, sizeof(int_pool)); - node = gw->child; - while(node) - { - /* Fix SCC */ - if (strcasecmp(node->name, "escc") == 0) - if (node->child && node->child->n_intrs == 0) - { - node->child->n_intrs = 1; - node->child->intrs = &int_pool[0]; - int_pool[0].line = 15+irq_base; - printk(KERN_INFO "irq: fixed SCC on second controller (%d)\n", - int_pool[0].line); - } - /* Fix media-bay & left SWIM */ - if (strcasecmp(node->name, "media-bay") == 0) - { - struct device_node* ya_node; - - if (node->n_intrs == 0) - { - node->n_intrs = 1; - node->intrs = &int_pool[1]; - int_pool[1].line = 29+irq_base; - printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n", - int_pool[1].line); - } - ya_node = node->child; - while(ya_node) - { - if ((strcasecmp(ya_node->name, "floppy") == 0) && - ya_node->n_intrs == 0) - { - ya_node->n_intrs = 2; - ya_node->intrs = &int_pool[2]; - int_pool[2].line = 19+irq_base; - int_pool[3].line = 1+irq_base; - printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n", - int_pool[2].line, int_pool[3].line); - } - ya_node = ya_node->sibling; - } - } - node = node->sibling; } - } +#endif /* __SMP__ */ diff --git a/arch/ppc/kernel/local_irq.h b/arch/ppc/kernel/local_irq.h new file mode 100644 index 000000000..5149c291a --- /dev/null +++ b/arch/ppc/kernel/local_irq.h @@ -0,0 +1,45 @@ + +#ifndef _PPC_KERNEL_LOCAL_IRQ_H +#define _PPC_KERNEL_LOCAL_IRQ_H + +#include <linux/kernel_stat.h> +#include <linux/interrupt.h> + +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); + +/* Structure describing interrupts */ +struct hw_interrupt_type { + const char * typename; + void (*startup)(unsigned int irq); + void (*shutdown)(unsigned int irq); + void (*handle)(unsigned int irq, struct pt_regs * regs); + void (*enable)(unsigned int irq); + void (*disable)(unsigned int irq); + void (*mask_and_ack)(unsigned int irq); + int irq_offset; +}; + +#define mask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->disable) irq_desc[irq].ctl->disable(irq);}) +#define unmask_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->enable) irq_desc[irq].ctl->enable(irq);}) +#define mask_and_ack_irq(irq) ({if (irq_desc[irq].ctl && irq_desc[irq].ctl->mask_and_ack) irq_desc[irq].ctl->mask_and_ack(irq);}) + +struct irqdesc { + struct irqaction *action; + struct hw_interrupt_type *ctl; +}; + +extern struct irqdesc irq_desc[NR_IRQS]; + + +#define NR_MASK_WORDS ((NR_IRQS + 31) / 32) + +extern int ppc_spurious_interrupts; +extern int ppc_second_irq; +extern struct irqaction *ppc_irq_action[NR_IRQS]; +extern unsigned int ppc_local_bh_count[NR_CPUS]; +extern unsigned int ppc_local_irq_count[NR_CPUS]; +extern unsigned int ppc_cached_irq_mask[NR_MASK_WORDS]; +extern unsigned int ppc_lost_interrupts[NR_MASK_WORDS]; +extern atomic_t ppc_n_lost_interrupts; + +#endif /* _PPC_KERNEL_LOCAL_IRQ_H */ diff --git a/arch/ppc/kernel/mbx_pci.c b/arch/ppc/kernel/mbx_pci.c index 30b7b1184..5114c3cfb 100644 --- a/arch/ppc/kernel/mbx_pci.c +++ b/arch/ppc/kernel/mbx_pci.c @@ -252,3 +252,20 @@ int mbx_pcibios_find_class(unsigned int class_code, unsigned short index, } return PCIBIOS_DEVICE_NOT_FOUND; } + +__initfunc( +void +mbx_pcibios_fixup(void)) +{ + /* Nothing to do here? */ +} + +__initfunc( +void +mbx_setup_pci_ptrs(void)) +{ + set_config_access_method(mbx); + + ppc_md.pcibios_fixup = mbx_pcibios_fixup; +} + diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c index 90647dcd9..0f1eb3eb5 100644 --- a/arch/ppc/kernel/mbx_setup.c +++ b/arch/ppc/kernel/mbx_setup.c @@ -1,5 +1,5 @@ /* - * $Id: mbx_setup.c,v 1.5 1998/12/29 18:55:07 cort Exp $ + * $Id: mbx_setup.c,v 1.9 1999/04/28 11:54:09 davem Exp $ * * linux/arch/ppc/kernel/setup.c * @@ -39,6 +39,22 @@ #include <asm/pgtable.h> #include <asm/ide.h> #include <asm/mbx.h> +#include <asm/machdep.h> + +#include "time.h" +#include "local_irq.h" + +static int mbx_set_rtc_time(unsigned long time); +unsigned long mbx_get_rtc_time(void); +void mbx_calibrate_decr(void); + +extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int mackbd_getkeycode(unsigned int scancode); +extern int mackbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char mackbd_unexpected_up(unsigned char keycode); +extern void mackbd_leds(unsigned char leds); +extern void mackbd_init_hw(void); extern unsigned long loops_per_sec; @@ -55,35 +71,10 @@ extern char saved_command_line[256]; extern unsigned long find_available_memory(void); extern void m8xx_cpm_reset(uint); -/* this really does make things cleaner -- Cort */ -void __init powermac_init(void) -{ -} - void __init adbdev_init(void) { } -void __init mbx_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 = 0; -#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 -} - __initfunc(void mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) { @@ -145,3 +136,337 @@ abort(void) #endif machine_restart(NULL); } + +/* 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. + */ +__initfunc(void mbx_calibrate_decr(void)) +{ + bd_t *binfo = (bd_t *)&res; + 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 = (binfo->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; +} + +/* A place holder for time base interrupts, if they are ever enabled. +*/ +void timebase_interrupt(int irq, void * dev, struct pt_regs * regs) +{ + printk("timebase_interrupt()\n"); +} + +/* 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); +} + +initfunc(unsigned long +mbx_get_rtc_time(void) +{ + /* 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. + */ + return ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc; +} + +void +mbx_restart(char *cmd) +{ + extern void MBX_gorom(void); + + MBX_gorom(); +} + +void +mbx_power_off(void) +{ + mbx_restart(NULL); +} + +void +mbx_halt(void) +{ + mbx_restart(NULL) +} + + +int mbx_setup_residual(char *buffer) +{ + int len = 0; + bd_t *bp; + extern RESIDUAL *res; + + bp = (bd_t *)res; + + len += sprintf(len+buffer,"clock\t\t: %dMHz\n" + "bus clock\t: %dMHz\n", + bp->bi_intfreq /*/ 1000000*/, + bp->bi_busfreq /*/ 1000000*/); + + return len; +} + +void +mbx_do_IRQ(struct pt_regs *regs, + int cpu, + int isfake) +{ + int irq; + unsigned long bits = 0; + + /* For MPC8xx, read the SIVEC register and shift the bits down + * to get the irq number. */ + bits = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec; + irq = bits >> 26; + irq += ppc8xx_pic.irq_offset; + bits = 1UL << irq; + + if (irq < 0) { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + spurious_interrupts++; + } + else { + ppc_irq_dispatch_handler( regs, irq ); + } + +} + +static void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + int bits, irq; + + /* A bug in the QSpan chip causes it to give us 0xff always + * when doing a character read. So read 32 bits and shift. + * This doesn't seem to return useful values anyway, but + * read it to make sure things are acked. + * -- Cort + */ + irq = (inl(0x508) >> 24)&0xff; + if ( irq != 0xff ) printk("iack %d\n", irq); + + outb(0x0C, 0x20); + irq = inb(0x20) & 7; + if (irq == 2) + { + outb(0x0C, 0xA0); + irq = inb(0xA0); + irq = (irq&7) + 8; + } + bits = 1UL << irq; + irq += i8259_pic.irq_offset; + ppc_irq_dispatch_handler( regs, irq ); +} + + +/* 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 +mbx_init_IRQ(void)) +{ + int i; + + ppc8xx_pic.irq_offset = 16; + for ( i = 16 ; i < 32 ; i++ ) + irq_desc[i].ctl = &ppc8xx_pic; + unmask_irq(CPM_INTERRUPT); + + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].ctl = &i8259_pic; + i8259_init(); + request_irq(ISA_BRIDGE_INT, mbx_i8259_action, 0, "8259 cascade", NULL); + enable_irq(ISA_BRIDGE_INT); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +void +mbx_ide_insw(ide_ioreg_t port, void *buf, int ns) +{ + ide_insw(port+_IO_BASE), buf, ns); +} + +void +mbx_ide_outsw(ide_ioreg_t port, void *buf, int ns) +{ + ide_outsw(port+_IO_BASE, buf, ns); +} + +int +mbx_ide_default_irq(ide_ioreg_t base) +{ + return 14; +} + +ide_ioreg_t +mbx_ide_default_io_base(int index) +{ + return index; +} + +int +mbx_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return 0 +} + +void +mbx_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ +} + +void +mbx_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ +} + +void +mbx_ide_fix_driveid(struct hd_driveid *id) +{ + ppc_generic_ide_fix_driveid(id); +} + +void __init mbx_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 = 0; +#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 +} +#endif + +__initfunc(void +mbx_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + + if ( r3 ) + memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); + +#ifdef CONFIG_PCI + mbx_setup_pci_ptrs(); +#endif + +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + /* take care of cmd line */ + if ( r6 ) + { + + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + + ppc_md.setup_arch = mbx_setup_arch; + ppc_md.setup_residual = mbx_setup_residual; + ppc_md.get_cpuinfo = NULL; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = mbx_init_IRQ; + ppc_md.do_IRQ = mbx_do_IRQ; + ppc_md.init = NULL; + + ppc_md.restart = mbx_restart; + ppc_md.power_off = mbx_power_off; + ppc_md.halt = mbx_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = mbx_set_rtc_time; + ppc_md.get_rtc_time = mbx_get_rtc_time; + ppc_md.calibrate_decr = mbx_calibrate_decr; + + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = pckbd_sysrq_xlate; +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.insw = mbx_ide_insw; + ppc_ide_md.outsw = mbx_ide_outsw; + ppc_ide_md.default_irq = mbx_ide_default_irq; + ppc_ide_md.default_io_base = mbx_ide_default_io_base; + ppc_ide_md.check_region = mbx_ide_check_region; + ppc_ide_md.request_region = mbx_ide_request_region; + ppc_ide_md.release_region = mbx_ide_release_region; + ppc_ide_md.fix_driveid = mbx_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = mbx_ide_init_hwif_ports; + + ppc_ide_md.io_base = _IO_BASE; +#endif +} diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index e4589b1e0..cbeb4ffce 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -36,6 +36,7 @@ * Returns (address we're running at) - (address we were linked at) * for use before the text and data are mapped to KERNELBASE. */ + _GLOBAL(reloc_offset) mflr r0 bl 1f @@ -72,8 +73,8 @@ _GLOBAL(_enable_interrupts) beqlr /* nothing to do if state == 0 */ _GLOBAL(__sti) _GLOBAL(_hard_sti) - lis r4,n_lost_interrupts@ha - lwz r4,n_lost_interrupts@l(r4) + lis r4,ppc_n_lost_interrupts@ha + lwz r4,ppc_n_lost_interrupts@l(r4) mfmsr r3 /* Get current state */ ori r3,r3,MSR_EE /* Turn on 'EE' bit */ cmpi 0,r4,0 /* lost interrupts to process first? */ @@ -93,8 +94,8 @@ do_lost_interrupts: stw r0,20(r1) stw r3,8(r1) 1: bl fake_interrupt - lis r4,n_lost_interrupts@ha - lwz r4,n_lost_interrupts@l(r4) + lis r4,ppc_n_lost_interrupts@ha + lwz r4,ppc_n_lost_interrupts@l(r4) cmpi 0,r4,0 bne- 1b lwz r3,8(r1) @@ -105,11 +106,31 @@ do_lost_interrupts: addi r1,r1,16 blr + +/* + * complement mask on the msr then "or" some values on. + * _nmask_and_or_msr(nmask, value_to_or) + */ + _GLOBAL(_nmask_and_or_msr) + mfmsr r0 /* Get current msr */ + andc r0,r0,r3 /* And off the bits set in r3 (first parm) */ + or r0,r0,r4 /* Or on the bits in r4 (second parm) */ + sync /* Some chip revs have problems here... */ + mtmsr r0 /* Update machine state */ + blr /* Done */ + + /* * Flush MMU TLB */ _GLOBAL(_tlbia) + sync tlbia + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr /* @@ -117,11 +138,17 @@ _GLOBAL(_tlbia) */ _GLOBAL(_tlbie) tlbie r3 + sync +#ifdef __SMP__ + tlbsync + sync +#endif blr + /* * Atomic [test&set] exchange * - * void *xchg_u32(void *ptr, unsigned long val) + * unsigned long xchg_u32(void *ptr, unsigned long val) * Changes the memory location '*ptr' to be val and returns * the previous value stored there. */ @@ -133,6 +160,27 @@ _GLOBAL(xchg_u32) blr /* + * Try to acquire a spinlock. + * Only does the stwcx. if the load returned 0 - the Programming + * Environments Manual suggests not doing unnecessary stcwx.'s + * since they may inhibit forward progress by other CPUs in getting + * a lock. + */ +_GLOBAL(__spin_trylock) + mr r4,r3 + eieio /* prevent reordering of stores */ + li r5,-1 + lwarx r3,0,r4 /* fetch old value, establish reservation */ + cmpwi 0,r3,0 /* is it 0? */ + bnelr- /* return failure if not */ + stwcx. r5,0,r4 /* try to update with new value */ + bne- 1f /* if we failed */ + eieio /* prevent reordering of stores */ + blr +1: li r3,1 /* return non-zero for failure */ + blr + +/* * Atomic add/sub/inc/dec operations * * void atomic_add(int c, int *v) @@ -590,6 +638,16 @@ cvt_df: stfd 0,-4(r5) blr + .globl __clear_msr_me +__clear_msr_me: + mfmsr r0 /* Get current interrupt state */ + lis r3,0 + ori r3,r3,MSR_ME + andc r0,r0,r3 /* Clears bit in (r4) */ + sync /* Some chip revs have problems here */ + mtmsr r0 /* Update machine state */ + blr + /* * Fetch the current SR register * get_SR(int index) @@ -608,6 +666,8 @@ _GLOBAL(__kernel_thread) sc cmpi 0,r3,0 /* parent or child? */ bnelr /* return if parent */ + li r0,0 /* clear out p->tss.regs */ + stw r0,TSS+PT_REGS(r2) /* since we don't have user ctx */ mtlr r4 /* fn addr in lr */ mr r3,r5 /* load arg and call fn */ blrl @@ -836,4 +896,5 @@ sys_call_table: .long sys_sendfile .long sys_ni_syscall /* streams1 */ .long sys_ni_syscall /* streams2 */ + .long sys_vfork .space (NR_syscalls-183)*4 diff --git a/arch/ppc/kernel/mk_defs.c b/arch/ppc/kernel/mk_defs.c index 9b51a6cc1..b66ccffa8 100644 --- a/arch/ppc/kernel/mk_defs.c +++ b/arch/ppc/kernel/mk_defs.c @@ -29,7 +29,7 @@ int main(void) { - DEFINE(KERNELBASE, KERNELBASE); + /*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)); @@ -48,7 +48,6 @@ main(void) DEFINE(NEED_RESCHED, offsetof(struct task_struct, need_resched)); 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/open_pic.c b/arch/ppc/kernel/open_pic.c new file mode 100644 index 000000000..2ca879dd8 --- /dev/null +++ b/arch/ppc/kernel/open_pic.c @@ -0,0 +1,48 @@ +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/openpic.h> +#include <asm/irq.h> +#include "open_pic.h" +#include "i8259.h" + +#ifdef __SMP__ +void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + smp_message_recv(); +} +#endif /* __SMP__ */ + +void chrp_mask_and_ack_irq(unsigned int irq_nr) +{ + if (is_8259_irq(irq_nr)) + i8259_pic.mask_and_ack(irq_nr); +} + +static void chrp_mask_irq(unsigned int irq_nr) +{ + if (is_8259_irq(irq_nr)) + i8259_pic.disable(irq_nr); + else + openpic_disable_irq(irq_to_openpic(irq_nr)); +} + +static void chrp_unmask_irq(unsigned int irq_nr) +{ + if (is_8259_irq(irq_nr)) + i8259_pic.enable(irq_nr); + else + openpic_enable_irq(irq_to_openpic(irq_nr)); +} + +struct hw_interrupt_type open_pic = { + " OpenPIC ", + NULL, + NULL, + NULL, + chrp_unmask_irq, + chrp_mask_irq, + chrp_mask_and_ack_irq, + 0 +}; diff --git a/arch/ppc/kernel/open_pic.h b/arch/ppc/kernel/open_pic.h new file mode 100644 index 000000000..77b8a46d0 --- /dev/null +++ b/arch/ppc/kernel/open_pic.h @@ -0,0 +1,11 @@ + +#ifndef _PPC_KERNEL_OPEN_PIC_H +#define _PPC_KERNEL_OPEN_PIC_H + +#include "local_irq.h" + +extern struct hw_interrupt_type open_pic; + +void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs); + +#endif /* _PPC_KERNEL_OPEN_PIC_H */ diff --git a/arch/ppc/kernel/openpic.c b/arch/ppc/kernel/openpic.c index ec60ca5a6..f7893dd79 100644 --- a/arch/ppc/kernel/openpic.c +++ b/arch/ppc/kernel/openpic.c @@ -107,7 +107,7 @@ static inline void out_le32(volatile u_int *addr, u_int val) } #endif -static inline u_int openpic_read(volatile u_int *addr) +u_int openpic_read(volatile u_int *addr) { u_int val; @@ -176,8 +176,8 @@ static void openpic_safe_writefield(volatile u_int *addr, u_int mask, __initfunc(void openpic_init(int main_pic)) { u_int t, i; - u_int vendorid, devid, stepping, timerfreq; - const char *version, *vendor, *device; + u_int timerfreq; + const char *version; if (!OpenPIC) panic("No OpenPIC found"); @@ -190,6 +190,9 @@ __initfunc(void openpic_init(int main_pic)) case 2: version = "1.2"; break; + case 3: + version = "1.3"; + break; default: version = "?"; break; @@ -200,32 +203,6 @@ __initfunc(void openpic_init(int main_pic)) OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, NumProcessors, NumSources, OpenPIC); - - t = openpic_read(&OpenPIC->Global.Vendor_Identification); - vendorid = t & OPENPIC_VENDOR_ID_VENDOR_ID_MASK; - devid = (t & OPENPIC_VENDOR_ID_DEVICE_ID_MASK) >> - OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT; - stepping = (t & OPENPIC_VENDOR_ID_STEPPING_MASK) >> - OPENPIC_VENDOR_ID_STEPPING_SHIFT; - switch (vendorid) { - case OPENPIC_VENDOR_ID_APPLE: - vendor = "Apple"; - break; - default: - vendor = "Unknown"; - break; - } - switch (devid) { - case OPENPIC_DEVICE_ID_APPLE_HYDRA: - device = "Hydra"; - break; - default: - device = "Unknown"; - break; - } - printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", vendorid, - vendor, devid, device, stepping); - timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); printk("OpenPIC timer frequency is "); if (timerfreq) diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 1dfa3a6c8..ccef5f36a 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1,5 +1,5 @@ /* - * $Id: pci.c,v 1.43 1998/12/29 18:55:11 cort Exp $ + * $Id: pci.c,v 1.54 1999/03/18 04:16:04 cort Exp $ * Common pmac/prep/chrp pci routines. -- Cort */ @@ -21,94 +21,41 @@ #include <asm/irq.h> #include <asm/gg2.h> -unsigned long isa_io_base; -unsigned long isa_mem_base; -unsigned long pci_dram_offset; +#include "pci.h" -unsigned int * pci_config_address; -unsigned char * pci_config_data; - -/* - * It would be nice if we could create a include/asm/pci.h and have just - * function ptrs for all these in there, but that isn't the case. - * We have a function, pcibios_*() which calls the function ptr ptr_pcibios_*() - * which has been setup by pcibios_init(). This is all to avoid a check - * for pmac/prep every time we call one of these. It should also make the move - * to a include/asm/pcibios.h easier, we can drop the ptr_ on these functions - * and create pci.h - * -- Cort - */ -int (*ptr_pcibios_read_config_byte)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val); -int (*ptr_pcibios_read_config_word)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short *val); -int (*ptr_pcibios_read_config_dword)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val); -int (*ptr_pcibios_write_config_byte)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char val); -int (*ptr_pcibios_write_config_word)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned short val); -int (*ptr_pcibios_write_config_dword)(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int val); - -#define decl_config_access_method(name) \ -extern int name##_pcibios_read_config_byte(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned char *val); \ -extern int name##_pcibios_read_config_word(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned short *val); \ -extern int name##_pcibios_read_config_dword(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned int *val); \ -extern int name##_pcibios_write_config_byte(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned char val); \ -extern int name##_pcibios_write_config_word(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned short val); \ -extern int name##_pcibios_write_config_dword(unsigned char bus, \ - unsigned char dev_fn, unsigned char offset, unsigned int val) - -#define set_config_access_method(name) \ - ptr_pcibios_read_config_byte = name##_pcibios_read_config_byte; \ - ptr_pcibios_read_config_word = name##_pcibios_read_config_word; \ - ptr_pcibios_read_config_dword = name##_pcibios_read_config_dword; \ - ptr_pcibios_write_config_byte = name##_pcibios_write_config_byte; \ - ptr_pcibios_write_config_word = name##_pcibios_write_config_word; \ - ptr_pcibios_write_config_dword = name##_pcibios_write_config_dword - -decl_config_access_method(pmac); -decl_config_access_method(grackle); -decl_config_access_method(gg2); -decl_config_access_method(raven); -decl_config_access_method(prep); -decl_config_access_method(mbx); +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +unsigned long pci_dram_offset = 0; int pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { - return ptr_pcibios_read_config_byte(bus,dev_fn,offset,val); + return ppc_md.pcibios_read_config_byte(bus,dev_fn,offset,val); } int pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short *val) { - return ptr_pcibios_read_config_word(bus,dev_fn,offset,val); + return ppc_md.pcibios_read_config_word(bus,dev_fn,offset,val); } int pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int *val) { - return ptr_pcibios_read_config_dword(bus,dev_fn,offset,val); + return ppc_md.pcibios_read_config_dword(bus,dev_fn,offset,val); } int pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char val) { - return ptr_pcibios_write_config_byte(bus,dev_fn,offset,val); + return ppc_md.pcibios_write_config_byte(bus,dev_fn,offset,val); } int pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short val) { - return ptr_pcibios_write_config_word(bus,dev_fn,offset,val); + return ppc_md.pcibios_write_config_word(bus,dev_fn,offset,val); } int pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int val) { - return ptr_pcibios_write_config_dword(bus,dev_fn,offset,val); + return ppc_md.pcibios_write_config_dword(bus,dev_fn,offset,val); } int pcibios_present(void) @@ -116,167 +63,46 @@ int pcibios_present(void) return 1; } -__initfunc(void pcibios_init(void)) +void __init pcibios_init(void) { } -__initfunc(void - setup_pci_ptrs(void)) + +void __init pcibios_fixup(void) { -#ifndef CONFIG_MBX - PPC_DEVICE *hostbridge; - switch (_machine) { - case _MACH_prep: - hostbridge=residual_find_device(PROCESSORDEVICE, NULL, - BridgeController, PCIBridge, - -1, 0); - if (hostbridge && - hostbridge->DeviceId.Interface == PCIBridgeIndirect) { - PnP_TAG_PACKET * pkt; - set_config_access_method(raven); - pkt=PnP_find_large_vendor_packet( - res->DevicePnPHeap+hostbridge->AllocatedOffset, - 3, 0); - if(pkt) { -#define p pkt->L4_Pack.L4_Data.L4_PPCPack - pci_config_address= (unsigned *) - ld_le32((unsigned *) p.PPCData); - pci_config_data= (unsigned char *) - ld_le32((unsigned *) (p.PPCData+8)); - } else {/* default values */ - pci_config_address= (unsigned *) 0x80000cf8; - pci_config_data= (unsigned char *) 0x80000cfc; - } - } else { - set_config_access_method(prep); - } - break; - case _MACH_Pmac: - if (find_devices("pci") != 0) { - /* looks like a G3 powermac */ - set_config_access_method(grackle); - } else { - set_config_access_method(pmac); - } - break; - case _MACH_chrp: - if ( !strncmp("MOT", - get_property(find_path_device("/"), "model", NULL),3) ) - { - isa_io_base = 0xfe000000; - set_config_access_method(grackle); - } - else - { - isa_io_base = GG2_ISA_IO_BASE; - set_config_access_method(gg2); - } - break; - default: - printk("setup_pci_ptrs(): unknown machine type!\n"); - } -#else /* CONFIG_MBX */ - set_config_access_method(mbx); -#endif /* CONFIG_MBX */ -#undef set_config_access_method + ppc_md.pcibios_fixup(); } -__initfunc(void pcibios_fixup(void)) +void __init pcibios_fixup_bus(struct pci_bus *bus) { - extern unsigned long route_pci_interrupts(void); - struct pci_dev *dev; - extern struct bridge_data **bridges; - extern unsigned char *Motherboard_map; - extern unsigned char *Motherboard_routes; - unsigned char i; -#ifndef CONFIG_MBX - switch (_machine ) - { - case _MACH_prep: - route_pci_interrupts(); - for(dev=pci_devices; dev; dev=dev->next) - { - /* - * Use our old hard-coded kludge to figure out what - * irq this device uses. This is necessary on things - * without residual data. -- Cort - */ - unsigned char d = PCI_SLOT(dev->devfn); - dev->irq = Motherboard_routes[Motherboard_map[d]]; - for ( i = 0 ; i <= 5 ; i++ ) - { - if ( dev->base_address[i] > 0x10000000 ) - { - printk("Relocating PCI address %x -> %x\n", - dev->base_address[i], - (dev->base_address[i] & 0x00FFFFFF) - | 0x01000000); - dev->base_address[i] = - (dev->base_address[i] & 0x00FFFFFF) | 0x01000000; - pci_write_config_dword(dev, - PCI_BASE_ADDRESS_0+(i*0x4), - dev->base_address[i] ); - } - } -#if 0 - /* - * If we have residual data and if it knows about this - * device ask it what the irq is. - * -- Cort - */ - ppcd = residual_find_device_id( ~0L, dev->device, - -1,-1,-1, 0); -#endif - } - 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; - } -#else /* CONFIG_MBX */ - for(dev=pci_devices; dev; dev=dev->next) - { - } -#endif /* CONFIG_MBX */ } -__initfunc(void pcibios_fixup_bus(struct pci_bus *bus)) +char __init *pcibios_setup(char *str) { + return str; } -__initfunc(char *pcibios_setup(char *str)) +#ifndef CONFIG_MBX +/* Recursively searches any node that is of type PCI-PCI bridge. Without + * this, the old code would miss children of P2P bridges and hence not + * fix IRQ's for cards located behind P2P bridges. + * - Ranjit Deshpande, 01/20/99 + */ +void __init fix_intr(struct device_node *node, struct pci_dev *dev) { - return str; + unsigned int *reg, *class_code; + + for (; node != 0;node = node->sibling) { + class_code = (unsigned int *) get_property(node, "class-code", 0); + if((*class_code >> 8) == PCI_CLASS_BRIDGE_PCI) + fix_intr(node->child, dev); + 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; + } } +#endif diff --git a/arch/ppc/kernel/pci.h b/arch/ppc/kernel/pci.h new file mode 100644 index 000000000..231f1d952 --- /dev/null +++ b/arch/ppc/kernel/pci.h @@ -0,0 +1,36 @@ + +#ifndef __PPC_KERNEL_PCI_H__ +#define __PPC_KERNEL_PCI_H__ + +extern unsigned long isa_io_base; +extern unsigned long isa_mem_base; +extern unsigned long pci_dram_offset; + +extern unsigned int *pci_config_address; +extern unsigned char *pci_config_data; + +void fix_intr(struct device_node *node, struct pci_dev *dev); + +#define decl_config_access_method(name) \ +extern int name##_pcibios_read_config_byte(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned char *val); \ +extern int name##_pcibios_read_config_word(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned short *val); \ +extern int name##_pcibios_read_config_dword(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned int *val); \ +extern int name##_pcibios_write_config_byte(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned char val); \ +extern int name##_pcibios_write_config_word(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned short val); \ +extern int name##_pcibios_write_config_dword(unsigned char bus, \ + unsigned char dev_fn, unsigned char offset, unsigned int val) + +#define set_config_access_method(name) \ + ppc_md.pcibios_read_config_byte = name##_pcibios_read_config_byte; \ + ppc_md.pcibios_read_config_word = name##_pcibios_read_config_word; \ + ppc_md.pcibios_read_config_dword = name##_pcibios_read_config_dword; \ + ppc_md.pcibios_write_config_byte = name##_pcibios_write_config_byte; \ + ppc_md.pcibios_write_config_word = name##_pcibios_write_config_word; \ + ppc_md.pcibios_write_config_dword = name##_pcibios_write_config_dword + +#endif /* __PPC_KERNEL_PCI_H__ */ diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c index 7763059f7..089169e37 100644 --- a/arch/ppc/kernel/pmac_pci.c +++ b/arch/ppc/kernel/pmac_pci.c @@ -21,6 +21,9 @@ #include <asm/pgtable.h> #include <asm/prom.h> #include <asm/pci-bridge.h> +#include <asm/machdep.h> + +#include "pci.h" struct bridge_data **bridges, *bridge_list; static int max_bus; @@ -84,7 +87,12 @@ int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* Bus number once again taken into consideration. + * Change applied from 2.1.24. This makes devices located + * behind PCI-PCI bridges visible. + * -Ranjit Deshpande, 01/20/99 + */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); *val = in_8(bp->cfg_data + (offset & 3)); @@ -109,7 +117,8 @@ int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); *val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3))); @@ -134,7 +143,8 @@ int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + offset); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + offset + 1); } udelay(2); *val = in_le32((volatile unsigned int *)bp->cfg_data); @@ -156,7 +166,8 @@ int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); out_8(bp->cfg_data + (offset & 3), val); @@ -180,7 +191,8 @@ int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + (offset & ~3)); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val); @@ -204,7 +216,8 @@ int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + offset); } else { - out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + /* See pci_read_config_byte */ + out_le32(bp->cfg_addr, (bus << 16) + (dev_fn << 8) + (offset & ~3) + 1); } udelay(2); out_le32((volatile unsigned int *)bp->cfg_data, val); @@ -429,3 +442,47 @@ __initfunc(static void add_bridges(struct device_node *dev, unsigned long *mem_p } } +__initfunc( +void +pmac_pcibios_fixup(void)) +{ + struct pci_dev *dev; + + /* + * 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] + */ + 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]; + unsigned char pin; + + if (pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin) || + !pin) + continue; /* No interrupt generated -> no fixup */ + fix_intr(bp->node->child, dev); + } +} + +__initfunc( +void +pmac_setup_pci_ptrs(void)) +{ + if (find_devices("pci") != 0) { + /* looks like a G3 powermac */ + set_config_access_method(grackle); + } else { + set_config_access_method(pmac); + } + + ppc_md.pcibios_fixup = pmac_pcibios_fixup; +} + diff --git a/arch/ppc/kernel/pmac_pic.c b/arch/ppc/kernel/pmac_pic.c new file mode 100644 index 000000000..6a49f1405 --- /dev/null +++ b/arch/ppc/kernel/pmac_pic.c @@ -0,0 +1,362 @@ + +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <asm/prom.h> +#include "pmac_pic.h" + +/* pmac */struct pmac_irq_hw { + unsigned int flag; + unsigned int enable; + unsigned int ack; + unsigned int level; +}; + +/* XXX these addresses should be obtained from the device tree */ +static volatile struct pmac_irq_hw *pmac_irq_hw[4] = { + (struct pmac_irq_hw *) 0xf3000020, + (struct pmac_irq_hw *) 0xf3000010, + (struct pmac_irq_hw *) 0xf4000020, + (struct pmac_irq_hw *) 0xf4000010, +}; + +static int max_irqs; +static int max_real_irqs; + +#define MAXCOUNT 10000000 + +#define GATWICK_IRQ_POOL_SIZE 10 +static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE]; + +static void __pmac pmac_mask_and_ack_irq(unsigned int irq_nr) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + + if ((unsigned)irq_nr >= max_irqs) + return; + + clear_bit(irq_nr, ppc_cached_irq_mask); + if (test_and_clear_bit(irq_nr, ppc_lost_interrupts)) + atomic_dec(&ppc_n_lost_interrupts); + out_le32(&pmac_irq_hw[i]->ack, bit); + out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); + out_le32(&pmac_irq_hw[i]->ack, bit); + do { + /* make sure ack gets to controller before we enable + interrupts */ + mb(); + } while(in_le32(&pmac_irq_hw[i]->flag) & bit); +} + +static void __pmac pmac_set_irq_mask(unsigned int irq_nr) +{ + unsigned long bit = 1UL << (irq_nr & 0x1f); + int i = irq_nr >> 5; + + if ((unsigned)irq_nr >= max_irqs) + return; + + /* enable unmasked interrupts */ + out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); + + do { + /* make sure mask gets to controller before we + return to user */ + mb(); + } while((in_le32(&pmac_irq_hw[i]->enable) & bit) + != (ppc_cached_irq_mask[i] & bit)); + + /* + * Unfortunately, setting the bit in the enable register + * when the device interrupt is already on *doesn't* set + * the bit in the flag register or request another interrupt. + */ + if ((bit & ppc_cached_irq_mask[i]) + && (ld_le32(&pmac_irq_hw[i]->level) & bit) + && !(ld_le32(&pmac_irq_hw[i]->flag) & bit)) { + if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) + atomic_inc(&ppc_n_lost_interrupts); + } +} + +static void __pmac pmac_mask_irq(unsigned int irq_nr) +{ + clear_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr); + mb(); +} + +static void __pmac pmac_unmask_irq(unsigned int irq_nr) +{ + set_bit(irq_nr, ppc_cached_irq_mask); + pmac_set_irq_mask(irq_nr); +} + +struct hw_interrupt_type pmac_pic = { + " PMAC-PIC ", + NULL, + NULL, + NULL, + pmac_unmask_irq, + pmac_mask_irq, + pmac_mask_and_ack_irq, + 0 +}; + +struct hw_interrupt_type gatwick_pic = { + " GATWICK ", + NULL, + NULL, + NULL, + pmac_unmask_irq, + pmac_mask_irq, + pmac_mask_and_ack_irq, + 0 +}; + +static void gatwick_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + int irq, bits; + + for (irq = max_irqs - 1; irq > max_real_irqs; irq -= 32) { + int i = irq >> 5; + bits = ld_le32(&pmac_irq_hw[i]->flag) + | ppc_lost_interrupts[i]; + if (bits == 0) + continue; + irq -= cntlzw(bits); + break; + } + /* The previous version of this code allowed for this case, we + * don't. Put this here to check for it. + * -- Cort + */ + if ( irq_desc[irq].ctl != &gatwick_pic ) + printk("gatwick irq not from gatwick pic\n"); + else + ppc_irq_dispatch_handler( regs, irq ); +} + +void +pmac_do_IRQ(struct pt_regs *regs, + int cpu, + int isfake) +{ + int irq; + unsigned long bits = 0; + +#ifdef __SMP__ + /* IPI's are a hack on the powersurge -- Cort */ + if ( cpu != 0 ) + { + if (!isfake) + { +#ifdef CONFIG_XMON + static int xmon_2nd; + if (xmon_2nd) + xmon(regs); +#endif + 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; + } + + { + unsigned int loops = MAXCOUNT; + while (test_bit(0, &global_irq_lock)) { + if (smp_processor_id() == global_irq_holder) { + printk("uh oh, interrupt while we hold global irq lock!\n"); +#ifdef CONFIG_XMON + xmon(0); +#endif + break; + } + if (loops-- == 0) { + printk("do_IRQ waiting for irq lock (holder=%d)\n", global_irq_holder); +#ifdef CONFIG_XMON + xmon(0); +#endif + } + } + } +#endif /* __SMP__ */ + + for (irq = max_real_irqs - 1; irq > 0; irq -= 32) { + int i = irq >> 5; + bits = ld_le32(&pmac_irq_hw[i]->flag) + | ppc_lost_interrupts[i]; + if (bits == 0) + continue; + irq -= cntlzw(bits); + break; + } + + if (irq < 0) + { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + ppc_spurious_interrupts++; + } + else + { + ppc_irq_dispatch_handler( regs, irq ); + } +#ifdef CONFIG_SMP +out: +#endif /* CONFIG_SMP */ +} + +/* This routine will fix some missing interrupt values in the device tree + * on the gatwick mac-io controller used by some PowerBooks + */ +static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) +{ + struct device_node *node; + int count; + + memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool)); + node = gw->child; + count = 0; + while(node) + { + /* Fix SCC */ + if (strcasecmp(node->name, "escc") == 0) + if (node->child) { + if (node->child->n_intrs < 3) { + node->child->intrs = &gatwick_int_pool[count]; + count += 3; + } + node->child->n_intrs = 3; + node->child->intrs[0].line = 15+irq_base; + node->child->intrs[1].line = 4+irq_base; + node->child->intrs[2].line = 5+irq_base; + printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n", + node->child->intrs[0].line, + node->child->intrs[1].line, + node->child->intrs[2].line); + } + /* Fix media-bay & left SWIM */ + if (strcasecmp(node->name, "media-bay") == 0) { + struct device_node* ya_node; + + if (node->n_intrs == 0) + node->intrs = &gatwick_int_pool[count++]; + node->n_intrs = 1; + node->intrs[0].line = 29+irq_base; + printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n", + node->intrs[0].line); + + ya_node = node->child; + while(ya_node) + { + if (strcasecmp(ya_node->name, "floppy") == 0) { + if (ya_node->n_intrs < 2) { + ya_node->intrs = &gatwick_int_pool[count]; + count += 2; + } + ya_node->n_intrs = 2; + ya_node->intrs[0].line = 19+irq_base; + ya_node->intrs[1].line = 1+irq_base; + printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n", + ya_node->intrs[0].line, ya_node->intrs[1].line); + } + if (strcasecmp(ya_node->name, "ata4") == 0) { + if (ya_node->n_intrs < 2) { + ya_node->intrs = &gatwick_int_pool[count]; + count += 2; + } + ya_node->n_intrs = 2; + ya_node->intrs[0].line = 14+irq_base; + ya_node->intrs[1].line = 3+irq_base; + printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n", + ya_node->intrs[0].line, ya_node->intrs[1].line); + } + ya_node = ya_node->sibling; + } + } + node = node->sibling; + } + if (count > 10) { + printk("WARNING !! Gatwick interrupt pool overflow\n"); + printk(" GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE); + printk(" requested = %d\n", count); + } +} + +__initfunc(void +pmac_pic_init(void)) +{ + int i; + struct device_node *irqctrler; + unsigned long addr; + int second_irq = -999; + + + /* G3 powermacs have 64 interrupts, G3 Series PowerBook have 128, + others have 32 */ + max_irqs = max_real_irqs = 32; + irqctrler = find_devices("mac-io"); + if (irqctrler) + { + max_real_irqs = 64; + if (irqctrler->next) + max_irqs = 128; + else + max_irqs = 64; + } + for ( i = 0; i < max_real_irqs ; i++ ) + irq_desc[i].ctl = &pmac_pic; + + /* get addresses of first controller */ + if (irqctrler) { + if (irqctrler->n_addrs > 0) { + addr = (unsigned long) + ioremap(irqctrler->addrs[0].address, 0x40); + for (i = 0; i < 2; ++i) + pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) + (addr + (2 - i) * 0x10); + } + + /* get addresses of second controller */ + irqctrler = (irqctrler->next) ? irqctrler->next : NULL; + if (irqctrler && irqctrler->n_addrs > 0) { + addr = (unsigned long) + ioremap(irqctrler->addrs[0].address, 0x40); + for (i = 2; i < 4; ++i) + pmac_irq_hw[i] = (volatile struct pmac_irq_hw*) + (addr + (4 - i) * 0x10); + } + } + + /* disable all interrupts in all controllers */ + for (i = 0; i * 32 < max_irqs; ++i) + out_le32(&pmac_irq_hw[i]->enable, 0); + + /* get interrupt line of secondary interrupt controller */ + if (irqctrler) { + second_irq = irqctrler->intrs[0].line; + printk(KERN_INFO "irq: secondary controller on irq %d\n", + (int)second_irq); + if (device_is_compatible(irqctrler, "gatwick")) + pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs); + for ( i = max_real_irqs ; i < max_irqs ; i++ ) + irq_desc[i].ctl = &gatwick_pic; + request_irq( second_irq, gatwick_action, SA_INTERRUPT, + "gatwick cascade", 0 ); + } + printk("System has %d possible interrupts\n", max_irqs); + if (max_irqs != max_real_irqs) + printk(KERN_DEBUG "%d interrupts on main controller\n", + max_real_irqs); + +#ifdef CONFIG_XMON + request_irq(20, xmon_irq, 0, "NMI - XMON", 0); +#endif /* CONFIG_XMON */ +} diff --git a/arch/ppc/kernel/pmac_pic.h b/arch/ppc/kernel/pmac_pic.h new file mode 100644 index 000000000..335a9ef69 --- /dev/null +++ b/arch/ppc/kernel/pmac_pic.h @@ -0,0 +1,15 @@ +#ifndef _PPC_KERNEL_PMAC_PIC_H +#define _PPC_KERNEL_PMAC_PIC_H + +#include "local_irq.h" + +extern struct hw_interrupt_type pmac_pic; + +void pmac_pic_init(void); +void pmac_do_IRQ(struct pt_regs *regs, + int cpu, + int isfake); + + +#endif /* _PPC_KERNEL_PMAC_PIC_H */ + diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index 499e8b6a5..9f8638021 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -44,6 +44,7 @@ #include <asm/prom.h> #include <asm/system.h> #include <asm/pgtable.h> +#include <asm/bitops.h> #include <asm/io.h> #include <asm/pci-bridge.h> #include <asm/adb.h> @@ -52,10 +53,44 @@ #include <asm/ohare.h> #include <asm/mediabay.h> #include <asm/feature.h> +#include <asm/ide.h> +#include <asm/machdep.h> + #include "time.h" +#include "local_irq.h" +#include "pmac_pic.h" + +#undef SHOW_GATWICK_IRQS + +unsigned long pmac_get_rtc_time(void); +int pmac_set_rtc_time(unsigned long nowtime); +void pmac_read_rtc_time(void); +void pmac_calibrate_decr(void); +void pmac_setup_pci_ptrs(void); + +extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int mackbd_getkeycode(unsigned int scancode); +extern int mackbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char mackbd_unexpected_up(unsigned char keycode); +extern void mackbd_leds(unsigned char leds); +extern void mackbd_init_hw(void); +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char mackbd_sysrq_xlate[128]; +#endif /* CONFIG_MAGIC_SYSRQ */ +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); unsigned char drive_info; +int ppc_override_l2cr = 0; +int ppc_override_l2cr_value; + extern char saved_command_line[]; #define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ @@ -132,6 +167,16 @@ pmac_get_cpuinfo(char *buffer) } } + /* Checks "l2cr-value" property in the registry */ + np = find_devices("cpus"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + len += sprintf(buffer+len, "l2cr override\t: 0x%x\n", *l2cr); + } + } + return len; } @@ -210,6 +255,26 @@ pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)) *memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p); + /* Checks "l2cr-value" property in the registry */ + if ( (_get_PVR() >> 16) == 8) { + struct device_node *np = find_devices("cpus"); + if (np != 0) { + unsigned int *l2cr = (unsigned int *) + get_property(np, "l2cr-value", NULL); + if (l2cr != 0) { + ppc_override_l2cr = 1; + ppc_override_l2cr_value = *l2cr; + _set_L2CR(0); + _set_L2CR(ppc_override_l2cr_value); + } + } + } + + if (ppc_override_l2cr) + printk(KERN_INFO "L2CR overriden (0x%x), backside cache is %s\n", + ppc_override_l2cr_value, (ppc_override_l2cr_value & 0x80000000) + ? "enabled" : "disabled"); + feature_init(); #ifdef CONFIG_KGDB @@ -258,15 +323,12 @@ int boot_target; int boot_part; kdev_t boot_dev; -void __init powermac_init(void) +__initfunc(void +pmac_init2(void)) { - if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) ) - return; adb_init(); pmac_nvram_init(); - if (_machine == _MACH_Pmac) { - media_bay_init(); - } + media_bay_init(); } #ifdef CONFIG_SCSI @@ -371,3 +433,174 @@ void note_bootable_part(kdev_t dev, int part) } } +void +pmac_restart(char *cmd) +{ + struct adb_request req; + + switch (adb_hardware) { + case ADB_VIACUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_RESET_SYSTEM); + for (;;) + cuda_poll(); + break; + + case ADB_VIAPMU: + pmu_restart(); + break; + default: + } +} + +void +pmac_power_off(void) +{ + struct adb_request req; + + switch (adb_hardware) { + case ADB_VIACUDA: + cuda_request(&req, NULL, 2, CUDA_PACKET, + CUDA_POWERDOWN); + for (;;) + cuda_poll(); + break; + + case ADB_VIAPMU: + pmu_shutdown(); + break; + default: + } +} + +void +pmac_halt(void) +{ + pmac_power_off(); +} + + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +void +pmac_ide_insw(ide_ioreg_t port, void *buf, int ns) +{ + ide_insw(port, buf, ns); +} + +void +pmac_ide_outsw(ide_ioreg_t port, void *buf, int ns) +{ + ide_outsw(port, buf, ns); +} + +int +pmac_ide_default_irq(ide_ioreg_t base) +{ + return 0; +} + +ide_ioreg_t +pmac_ide_default_io_base(int index) +{ +#if defined(CONFIG_BLK_DEV_IDE_PMAC) + return pmac_ide_regbase[index]; +#else + return 0; +#endif +} + +int +pmac_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return 0; +} + +void +pmac_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ +} + +void +pmac_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ +} + +/* Convert the shorts/longs in hd_driveid from little to big endian; + * chars are endian independant, of course, but strings need to be flipped. + * (Despite what it says in drivers/block/ide.h, they come up as little + * endian...) + * + * Changes to linux/hdreg.h may require changes here. */ +void +pmac_ide_fix_driveid(struct hd_driveid *id) +{ + ppc_generic_ide_fix_driveid(id); +} + +/* This is declared in drivers/block/ide-pmac.c */ +void pmac_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq); +#endif + +__initfunc(void +pmac_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + pmac_setup_pci_ptrs(); + + /* 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; + DMA_MODE_READ = 1; + DMA_MODE_WRITE = 2; + + ppc_md.setup_arch = pmac_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = pmac_get_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = pmac_pic_init; + ppc_md.do_IRQ = pmac_do_IRQ; + ppc_md.init = pmac_init2; + + ppc_md.restart = pmac_restart; + ppc_md.power_off = pmac_power_off; + ppc_md.halt = pmac_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = pmac_set_rtc_time; + ppc_md.get_rtc_time = pmac_get_rtc_time; + ppc_md.calibrate_decr = pmac_calibrate_decr; + +#if defined(CONFIG_VT) && defined(CONFIG_MAC_KEYBOARD) + ppc_md.kbd_setkeycode = mackbd_setkeycode; + ppc_md.kbd_getkeycode = mackbd_getkeycode; + ppc_md.kbd_translate = mackbd_translate; + ppc_md.kbd_unexpected_up = mackbd_unexpected_up; + ppc_md.kbd_leds = mackbd_leds; + ppc_md.kbd_init_hw = mackbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = mackbd_sysrq_xlate; +#endif +#endif + +#if defined(CONFIG_BLK_DEV_IDE_PMAC) + ppc_ide_md.insw = pmac_ide_insw; + ppc_ide_md.outsw = pmac_ide_outsw; + ppc_ide_md.default_irq = pmac_ide_default_irq; + ppc_ide_md.default_io_base = pmac_ide_default_io_base; + ppc_ide_md.check_region = pmac_ide_check_region; + ppc_ide_md.request_region = pmac_ide_request_region; + ppc_ide_md.release_region = pmac_ide_release_region; + ppc_ide_md.fix_driveid = pmac_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = pmac_ide_init_hwif_ports; + + ppc_ide_md.io_base = 0; +#endif +} + diff --git a/arch/ppc/kernel/ppc8xx_pic.c b/arch/ppc/kernel/ppc8xx_pic.c new file mode 100644 index 000000000..87bc6d0f8 --- /dev/null +++ b/arch/ppc/kernel/ppc8xx_pic.c @@ -0,0 +1,49 @@ + +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <asm/irq.h> +#include <asm/8xx_immap.h> +#include <asm/mbx.h> +#include "ppc8xx_pic.h" + + +static void mbx_mask_irq(unsigned int irq_nr) +{ + if ( irq_nr == ISA_BRIDGE_INT ) return; + if ( irq_nr >= ppc8xx_pic.irq_offset ) + irq_nr -= ppc8xx_pic.irq_offset; + ppc_cached_irq_mask[0] &= ~(1 << (31-irq_nr)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = ppc_cached_irq_mask[0]; +} + +static void mbx_unmask_irq(unsigned int irq_nr) +{ + if ( irq_nr >= ppc8xx_pic.irq_offset ) + irq_nr -= ppc8xx_pic.irq_offset; + ppc_cached_irq_mask[0] |= (1 << (31-irq_nr)); + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = ppc_cached_irq_mask[0]; +} + +static void mbx_mask_and_ack(unsigned int irq_nr) +{ + /* this shouldn't be masked, we mask the 8259 if we need to -- Cort */ + if ( irq_nr != ISA_BRIDGE_INT ) + mbx_mask_irq(irq_nr); + if ( irq_nr >= ppc8xx_pic.irq_offset ) + irq_nr -= ppc8xx_pic.irq_offset; + /* clear the pending bits */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sipend = 1 << (31-irq_nr); +} + +struct hw_interrupt_type ppc8xx_pic = { + " 8xx SIU ", + NULL, + NULL, + NULL, + mbx_unmask_irq, + mbx_mask_irq, + mbx_mask_and_ack, + 0 +}; diff --git a/arch/ppc/kernel/ppc8xx_pic.h b/arch/ppc/kernel/ppc8xx_pic.h new file mode 100644 index 000000000..d6b424fec --- /dev/null +++ b/arch/ppc/kernel/ppc8xx_pic.h @@ -0,0 +1,9 @@ + +#ifndef _PPC_KERNEL_PPC8xx_H +#define _PPC_KERNEL_PPC8xx_H + +#include "local_irq.h" + +extern struct hw_interrupt_type ppc8xx_pic; + +#endif /* _PPC_KERNEL_PPC8xx_H */ diff --git a/arch/ppc/kernel/ppc_defs.h b/arch/ppc/kernel/ppc_defs.h deleted file mode 100644 index 83315a2ae..000000000 --- a/arch/ppc/kernel/ppc_defs.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * WARNING! This file is automatically generated - DO NOT EDIT! - */ -#define KERNELBASE -1073741824 -#define STATE 0 -#define NEXT_TASK 48 -#define COUNTER 24 -#define PROCESSOR 36 -#define SIGPENDING 8 -#define TSS 568 -#define MM 872 -#define TASK_STRUCT_SIZE 912 -#define KSP 0 -#define PG_TABLES 4 -#define PGD 8 -#define LAST_SYSCALL 20 -#define PT_REGS 12 -#define PF_TRACESYS 32 -#define TASK_FLAGS 4 -#define NEED_RESCHED 20 -#define TSS_FPR0 24 -#define TSS_FPSCR 284 -#define TSS_SMP_FORK_RET 288 -#define TASK_UNION_SIZE 8192 -#define STACK_FRAME_OVERHEAD 16 -#define INT_FRAME_SIZE 192 -#define GPR0 16 -#define GPR1 20 -#define GPR2 24 -#define GPR3 28 -#define GPR4 32 -#define GPR5 36 -#define GPR6 40 -#define GPR7 44 -#define GPR8 48 -#define GPR9 52 -#define GPR10 56 -#define GPR11 60 -#define GPR12 64 -#define GPR13 68 -#define GPR14 72 -#define GPR15 76 -#define GPR16 80 -#define GPR17 84 -#define GPR18 88 -#define GPR19 92 -#define GPR20 96 -#define GPR21 100 -#define GPR22 104 -#define GPR23 108 -#define GPR24 112 -#define GPR25 116 -#define GPR26 120 -#define GPR27 124 -#define GPR28 128 -#define GPR29 132 -#define GPR30 136 -#define GPR31 140 -#define _NIP 144 -#define _MSR 148 -#define _CTR 156 -#define _LINK 160 -#define _CCR 168 -#define _XER 164 -#define _DAR 180 -#define _DSISR 184 -#define ORIG_GPR3 152 -#define RESULT 188 -#define TRAP 176 diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 1b7753dd0..834bdf102 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -8,11 +8,12 @@ #include <linux/vt_kern.h> #include <linux/nvram.h> +#include <asm/page.h> #include <asm/semaphore.h> #include <asm/processor.h> #include <asm/uaccess.h> -#include <asm/ide.h> #include <asm/io.h> +#include <asm/ide.h> #include <asm/atomic.h> #include <asm/bitops.h> #include <asm/checksum.h> @@ -26,6 +27,8 @@ #include <asm/irq.h> #include <asm/feature.h> #include <asm/spinlock.h> +#include <asm/dma.h> +#include <asm/machdep.h> #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> @@ -39,13 +42,14 @@ extern void AlignmentException(struct pt_regs *regs); extern void ProgramCheckException(struct pt_regs *regs); extern void SingleStepException(struct pt_regs *regs); extern int sys_sigreturn(struct pt_regs *regs); -extern atomic_t n_lost_interrupts; +extern atomic_t ppc_n_lost_interrupts; extern void do_lost_interrupts(unsigned long); extern int do_signal(sigset_t *, struct pt_regs *); asmlinkage long long __ashrdi3(long long, int); asmlinkage int abs(int); +EXPORT_SYMBOL(clear_page); EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(syscall_trace); EXPORT_SYMBOL(transfer_to_handler); @@ -57,16 +61,21 @@ EXPORT_SYMBOL(AlignmentException); EXPORT_SYMBOL(ProgramCheckException); EXPORT_SYMBOL(SingleStepException); EXPORT_SYMBOL(sys_sigreturn); -EXPORT_SYMBOL(n_lost_interrupts); +EXPORT_SYMBOL(ppc_n_lost_interrupts); EXPORT_SYMBOL(do_lost_interrupts); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); -EXPORT_SYMBOL(local_irq_count); -EXPORT_SYMBOL(local_bh_count); +EXPORT_SYMBOL(ppc_local_irq_count); +EXPORT_SYMBOL(ppc_local_bh_count); EXPORT_SYMBOL(isa_io_base); EXPORT_SYMBOL(isa_mem_base); EXPORT_SYMBOL(pci_dram_offset); +EXPORT_SYMBOL(ISA_DMA_THRESHOLD); +EXPORT_SYMBOL(DMA_MODE_READ); +EXPORT_SYMBOL(DMA_MODE_WRITE); +EXPORT_SYMBOL(_prep_type); +EXPORT_SYMBOL(ucSystemType); EXPORT_SYMBOL(atomic_add); EXPORT_SYMBOL(atomic_sub); @@ -155,6 +164,7 @@ EXPORT_SYMBOL(_enable_interrupts); EXPORT_SYMBOL(flush_instruction_cache); EXPORT_SYMBOL(_get_PVR); EXPORT_SYMBOL(giveup_fpu); +EXPORT_SYMBOL(enable_kernel_fp); EXPORT_SYMBOL(flush_icache_range); EXPORT_SYMBOL(xchg_u32); #ifdef __SMP__ @@ -171,18 +181,14 @@ EXPORT_SYMBOL(_write_lock); EXPORT_SYMBOL(_write_unlock); #endif -#ifndef CONFIG_MACH_SPECIFIC EXPORT_SYMBOL(_machine); -#endif +EXPORT_SYMBOL(ppc_md); EXPORT_SYMBOL(adb_request); -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); #ifdef CONFIG_PMAC_PBOOK EXPORT_SYMBOL(sleep_notifier_list); @@ -194,7 +200,6 @@ EXPORT_SYMBOL(find_compatible_devices); EXPORT_SYMBOL(find_path_device); EXPORT_SYMBOL(find_phandle); EXPORT_SYMBOL(get_property); -EXPORT_SYMBOL(device_is_compatible); EXPORT_SYMBOL(pci_io_base); EXPORT_SYMBOL(pci_device_loc); EXPORT_SYMBOL(feature_set); @@ -209,9 +214,8 @@ EXPORT_SYMBOL(nvram_read_byte); EXPORT_SYMBOL(nvram_write_byte); #endif /* CONFIG_PMAC */ -#ifdef CONFIG_SOUND_MODULE EXPORT_SYMBOL(abs); -#endif +EXPORT_SYMBOL(device_is_compatible); /* The following are special because they're not called explicitly (the C compiler generates them). Fortunately, diff --git a/arch/ppc/kernel/prep_nvram.c b/arch/ppc/kernel/prep_nvram.c new file mode 100644 index 000000000..e69563c8a --- /dev/null +++ b/arch/ppc/kernel/prep_nvram.c @@ -0,0 +1,173 @@ +/* + * linux/arch/ppc/kernel/prep_nvram.c + * + * Copyright (C) 1998 Corey Minyard + * + */ +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/ioport.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/machdep.h> +#include <asm/prep_nvram.h> + +/* + * Allow for a maximum of 32K of PReP NvRAM data + */ +#define MAX_PREP_NVRAM 0x8000 +static char nvramData[MAX_PREP_NVRAM]; +static NVRAM_MAP *nvram=(NVRAM_MAP *)&nvramData[0]; + +#define PREP_NVRAM_AS0 0x74 +#define PREP_NVRAM_AS1 0x75 +#define PREP_NVRAM_DATA 0x77 + +unsigned char *rs_pcNvRAM; + +unsigned char prep_nvram_read_val(int addr) +{ + outb(addr, PREP_NVRAM_AS0); + outb(addr>>8, PREP_NVRAM_AS1); + return inb(PREP_NVRAM_DATA); +} + +void prep_nvram_write_val(int addr, + unsigned char val) +{ + outb(addr, PREP_NVRAM_AS0); + outb(addr>>8, PREP_NVRAM_AS1); + outb(val, PREP_NVRAM_DATA); +} + +/* + * Most Radstone boards have NvRAM memory mapped at offset 8M in ISA space + */ +unsigned char rs_nvram_read_val(int addr) +{ + return rs_pcNvRAM[addr]; +} + +void rs_nvram_write_val(int addr, + unsigned char val) +{ + rs_pcNvRAM[addr]=val; +} + +__initfunc(void init_prep_nvram(void)) +{ + unsigned char *nvp; + int i; + int nvramSize; + + /* + * I'm making the assumption that 32k will always cover the + * nvramsize. If this isn't the case please let me know and we can + * map the header, then get the size from the header, then map + * the whole size. -- Cort + */ + if ( _prep_type == _PREP_Radstone ) + rs_pcNvRAM = (unsigned char *)ioremap(_ISA_MEM_BASE+0x00800000, + 32<<10); + request_region(PREP_NVRAM_AS0, 0x8, "PReP NVRAM"); + /* + * The following could fail if the NvRAM were corrupt but + * we expect the boot firmware to have checked its checksum + * before boot + */ + nvp = (char *) &nvram->Header; + for (i=0; i<sizeof(HEADER); i++) + { + *nvp = ppc_md.nvram_read_val(i); + nvp++; + } + + /* + * The PReP NvRAM may be any size so read in the header to + * determine how much we must read in order to get the complete + * GE area + */ + nvramSize=(int)nvram->Header.GEAddress+nvram->Header.GELength; + if(nvramSize>MAX_PREP_NVRAM) + { + /* + * NvRAM is too large + */ + nvram->Header.GELength=0; + return; + } + + /* + * Read the remainder of the PReP NvRAM + */ + nvp = (char *) &nvram->GEArea[0]; + for (i=sizeof(HEADER); i<nvramSize; i++) + { + *nvp = ppc_md.nvram_read_val(i); + nvp++; + } +} + +__prep +char *prep_nvram_get_var(const char *name) +{ + char *cp; + int namelen; + + namelen = strlen(name); + cp = prep_nvram_first_var(); + while (cp != NULL) { + if ((strncmp(name, cp, namelen) == 0) + && (cp[namelen] == '=')) + { + return cp+namelen+1; + } + cp = prep_nvram_next_var(cp); + } + + return NULL; +} + +__prep +char *prep_nvram_first_var(void) +{ + if (nvram->Header.GELength == 0) { + return NULL; + } else { + return (((char *)nvram) + + ((unsigned int) nvram->Header.GEAddress)); + } +} + +__prep +char *prep_nvram_next_var(char *name) +{ + char *cp; + + + cp = name; + while (((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) + && (*cp != '\0')) + { + cp++; + } + + /* Skip over any null characters. */ + while (((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) + && (*cp == '\0')) + { + cp++; + } + + if ((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) { + return cp; + } else { + return NULL; + } +} + + + diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c index b48f7a1fc..dd7f1eee1 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.24 1998/12/10 02:39:51 cort Exp $ + * $Id: prep_pci.c,v 1.33 1999/05/09 20:15:54 cort Exp $ * PReP pci functions. * Originally by Gary Thomas * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) @@ -11,11 +11,19 @@ #include <linux/pci.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/openpic.h> #include <asm/byteorder.h> #include <asm/io.h> #include <asm/ptrace.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> +#include <asm/residual.h> #include <asm/processor.h> +#include <asm/irq.h> +#include <asm/machdep.h> + +#include "pci.h" #define MAX_DEVNR 22 @@ -27,6 +35,9 @@ unsigned char *Motherboard_map_name; /* How is the 82378 PIRQ mapping setup? */ unsigned char *Motherboard_routes; +/* Used for Motorola to store system config register */ +static unsigned long *ProcInfo; + /* Tables for known hardware */ /* Motorola PowerStackII - Utah */ @@ -34,38 +45,39 @@ static char Utah_pci_IRQ_map[23] __prepdata = { 0, /* Slot 0 - unused */ 0, /* Slot 1 - unused */ - 4, /* Slot 2 - SCSI - NCR825A */ + 5, /* Slot 2 - SCSI - NCR825A */ 0, /* Slot 3 - unused */ 1, /* Slot 4 - Ethernet - DEC2114x */ 0, /* Slot 5 - unused */ - 2, /* Slot 6 - PCI Card slot #1 */ - 3, /* Slot 7 - PCI Card slot #2 */ - 4, /* Slot 8 - PCI Card slot #3 */ - 4, /* Slot 9 - PCI Bridge */ + 3, /* Slot 6 - PCI Card slot #1 */ + 4, /* Slot 7 - PCI Card slot #2 */ + 5, /* Slot 8 - PCI Card slot #3 */ + 5, /* Slot 9 - PCI Bridge */ /* added here in case we ever support PCI bridges */ /* Secondary PCI bus cards are at slot-9,6 & slot-9,7 */ 0, /* Slot 10 - unused */ 0, /* Slot 11 - unused */ - 4, /* Slot 12 - SCSI - NCR825A */ + 5, /* Slot 12 - SCSI - NCR825A */ 0, /* Slot 13 - unused */ - 2, /* Slot 14 - enet */ + 3, /* Slot 14 - enet */ 0, /* Slot 15 - unused */ - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 2, /* Slot 16 - unused */ + 3, /* Slot 17 - unused */ + 5, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ }; static char Utah_pci_IRQ_routes[] __prepdata = { 0, /* Line 0 - Unused */ 9, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15, /* Line 4 */ + 10, /* Line 2 */ + 11, /* Line 3 */ + 14, /* Line 4 */ + 15, /* Line 5 */ }; /* Motorola PowerStackII - Omaha */ @@ -125,9 +137,9 @@ static char Blackhawk_pci_IRQ_map[19] __prepdata = 0, /* Slot 13 - unused */ 1, /* Slot 14 - Ethernet */ 0, /* Slot 15 - unused */ - 1, /* Slot P7 */ - 2, /* Slot P6 */ - 3, /* Slot P5 */ + 1, /* Slot P7 */ + 2, /* Slot P6 */ + 3, /* Slot P5 */ }; static char Blackhawk_pci_IRQ_routes[] __prepdata = @@ -139,6 +151,122 @@ static char Blackhawk_pci_IRQ_routes[] __prepdata = 15 /* Line 4 */ }; +/* Motorola Mesquite */ +static char Mesquite_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unxued */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 3, /* Slot 16 - PMC */ + 0, /* Slot 17 - unused */ + 0, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +/* Motorola Sitka */ +static char Sitka_pci_IRQ_map[21] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unxued */ + 0, /* Slot 11 - unused */ + 0, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PMC 1 */ + 12, /* Slot 17 - PMC 2 */ + 0, /* Slot 18 - unused */ + 0, /* Slot 19 - unused */ + 4, /* Slot 20 - NT P2P bridge */ +}; + +/* Motorola MTX */ +static char MTX_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PCI/PMC slot 1 */ + 10, /* Slot 17 - PCI/PMC slot 2 */ + 11, /* Slot 18 - PCI slot 3 */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +/* Motorola MTX Plus */ +/* Secondary bus interrupt routing is not supported yet */ +static char MTXplus_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - Ethernet 1 */ + 0, /* Slot 15 - unused */ + 9, /* Slot 16 - PCI slot 1P */ + 10, /* Slot 17 - PCI slot 2P */ + 11, /* Slot 18 - PCI slot 3P */ + 10, /* Slot 19 - Ethernet 2 */ + 0, /* Slot 20 - P2P Bridge */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + +static char Raven_pci_IRQ_routes[] __prepdata = +{ + 0, /* This is a dummy structure */ +}; + /* Motorola MVME16xx */ static char Genesis_pci_IRQ_map[16] __prepdata = { @@ -169,8 +297,35 @@ static char Genesis_pci_IRQ_routes[] __prepdata = 15 /* Line 4 */ }; +static char Genesis2_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - Ethernet */ + 0, /* Slot 11 - Universe PCI - VME Bridge */ + 3, /* Slot 12 - unused */ + 0, /* Slot 13 - unused */ + 2, /* Slot 14 - SCSI */ + 0, /* Slot 15 - graphics on 3600 */ + 9, /* Slot 16 - PMC */ + 12, /* Slot 17 - pci */ + 11, /* Slot 18 - pci */ + 10, /* Slot 19 - pci */ + 0, /* Slot 20 - pci */ + 0, /* Slot 21 - unused */ + 0, /* Slot 22 - unused */ +}; + /* Motorola Series-E */ -static char Comet_pci_IRQ_map[16] __prepdata = +static char Comet_pci_IRQ_map[23] __prepdata = { 0, /* Slot 0 - unused */ 0, /* Slot 1 - unused */ @@ -188,6 +343,13 @@ static char Comet_pci_IRQ_map[16] __prepdata = 0, /* Slot 13 - unused */ 1, /* Slot 14 - Ethernet */ 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI bridge */ + 0, + 0, + 0, }; static char Comet_pci_IRQ_routes[] __prepdata = @@ -199,6 +361,43 @@ static char Comet_pci_IRQ_routes[] __prepdata = 15 /* Line 4 */ }; +/* Motorola Series-EX */ +static char Comet2_pci_IRQ_map[23] __prepdata = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 3, /* Slot 2 - SCSI - NCR825A */ + 0, /* Slot 3 - unused */ + 1, /* Slot 4 - Ethernet - DEC2104X */ + 0, /* Slot 5 - unused */ + 1, /* Slot 6 - PCI slot 1 */ + 2, /* Slot 7 - PCI slot 2 */ + 3, /* Slot 8 - PCI slot 3 */ + 4, /* Slot 9 - PCI bridge */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI - NCR825A */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet - DEC2104X */ + 0, /* Slot 15 - unused */ + 1, /* Slot 16 - PCI slot 1 */ + 2, /* Slot 17 - PCI slot 2 */ + 3, /* Slot 18 - PCI slot 3 */ + 4, /* Slot 19 - PCI bridge */ + 0, + 0, + 0, +}; + +static char Comet2_pci_IRQ_routes[] __prepdata = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15, /* Line 4 */ +}; + /* * ibm 830 (and 850?). * This is actually based on the Carolina motherboard @@ -312,22 +511,40 @@ static char Nobis_pci_IRQ_routes[] __prepdata = { #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] */ +/* + * 8259 edge/level control definitions + */ +#define ISA8259_M_ELCR 0x4d0 +#define ISA8259_S_ELCR 0x4d1 + +#define ELCRS_INT15_LVL 0x80 +#define ELCRS_INT14_LVL 0x40 +#define ELCRS_INT12_LVL 0x10 +#define ELCRS_INT11_LVL 0x08 +#define ELCRS_INT10_LVL 0x04 +#define ELCRS_INT9_LVL 0x02 +#define ELCRS_INT8_LVL 0x01 +#define ELCRM_INT7_LVL 0x80 +#define ELCRM_INT5_LVL 0x20 + +#define CFGPTR(dev) (0x80800000 | (1<<(dev>>3)) | ((dev&7)<<8) | offset) +#define DEVNO(dev) (dev>>3) + __prep int prep_pcibios_read_config_dword (unsigned char bus, unsigned char dev, unsigned char offset, unsigned int *val) { - unsigned long _val; + unsigned long _val; unsigned long *ptr; - dev >>= 3; - - if ((bus != 0) || (dev > MAX_DEVNR)) - { + + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) + { *val = 0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else + return PCIBIOS_DEVICE_NOT_FOUND; + } else { - ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); + ptr = (unsigned long *)CFGPTR(dev); _val = le32_to_cpu(*ptr); } *val = _val; @@ -339,16 +556,16 @@ int prep_pcibios_read_config_word (unsigned char bus, unsigned char dev, unsigned char offset, unsigned short *val) { - unsigned short _val; + unsigned short _val; unsigned short *ptr; - dev >>= 3; - if ((bus != 0) || (dev > MAX_DEVNR)) - { - *val = (unsigned short)0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else + + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) + { + *val = 0xFFFF; + return PCIBIOS_DEVICE_NOT_FOUND; + } else { - ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); + ptr = (unsigned short *)CFGPTR(dev); _val = le16_to_cpu(*ptr); } *val = _val; @@ -360,16 +577,16 @@ int prep_pcibios_read_config_byte (unsigned char bus, unsigned char dev, unsigned char offset, unsigned char *val) { - unsigned char _val; - volatile unsigned char *ptr; - dev >>= 3; - if ((bus != 0) || (dev > MAX_DEVNR)) - { - *(unsigned long *)val = (unsigned long) 0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else + unsigned char _val; + unsigned char *ptr; + + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) + { + *val = 0xFF; + return PCIBIOS_DEVICE_NOT_FOUND; + } else { - ptr = (unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)); + ptr = (unsigned char *)CFGPTR(dev); _val = *ptr; } *val = _val; @@ -383,14 +600,14 @@ prep_pcibios_write_config_dword (unsigned char bus, { unsigned long _val; unsigned long *ptr; - dev >>= 3; + _val = le32_to_cpu(val); - if ((bus != 0) || (dev > MAX_DEVNR)) + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) { return PCIBIOS_DEVICE_NOT_FOUND; } else { - ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); + ptr = (unsigned long *)CFGPTR(dev); *ptr = _val; } return PCIBIOS_SUCCESSFUL; @@ -403,14 +620,14 @@ prep_pcibios_write_config_word (unsigned char bus, { unsigned short _val; unsigned short *ptr; - dev >>= 3; + _val = le16_to_cpu(val); - if ((bus != 0) || (dev > MAX_DEVNR)) + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) { return PCIBIOS_DEVICE_NOT_FOUND; } else { - ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); + ptr = (unsigned short *)CFGPTR(dev); *ptr = _val; } return PCIBIOS_SUCCESSFUL; @@ -423,20 +640,151 @@ prep_pcibios_write_config_byte (unsigned char bus, { unsigned char _val; unsigned char *ptr; - dev >>= 3; + _val = val; - if ((bus != 0) || (dev > MAX_DEVNR)) + if ((bus != 0) || (DEVNO(dev) > MAX_DEVNR)) { return PCIBIOS_DEVICE_NOT_FOUND; } else { - ptr = (unsigned char *)(0x80800000 | (1<<dev) | (offset^1)); + ptr = (unsigned char *)CFGPTR(dev); *ptr = _val; } return PCIBIOS_SUCCESSFUL; } -__initfunc(unsigned long route_pci_interrupts(void)) +#define MOTOROLA_CPUTYPE_REG 0x800 +#define MOTOROLA_BASETYPE_REG 0x803 +#define MPIC_RAVEN_ID 0x48010000 +#define MPIC_HAWK_ID 0x48030000 +#define MOT_PROC2_BIT 0x800 + +static u_char mvme2600_openpic_initsenses[] __initdata = { + 1, /* MVME2600_INT_SIO */ + 0, /* MVME2600_INT_FALCN_ECC_ERR */ + 1, /* MVME2600_INT_PCI_ETHERNET */ + 1, /* MVME2600_INT_PCI_SCSI */ + 1, /* MVME2600_INT_PCI_GRAPHICS */ + 1, /* MVME2600_INT_PCI_VME0 */ + 1, /* MVME2600_INT_PCI_VME1 */ + 1, /* MVME2600_INT_PCI_VME2 */ + 1, /* MVME2600_INT_PCI_VME3 */ + 1, /* MVME2600_INT_PCI_INTA */ + 1, /* MVME2600_INT_PCI_INTB */ + 1, /* MVME2600_INT_PCI_INTC */ + 1, /* MVME2600_INT_PCI_INTD */ + 1, /* MVME2600_INT_LM_SIG0 */ + 1, /* MVME2600_INT_LM_SIG1 */ +}; + +#define MOT_RAVEN_PRESENT 0x1 +#define MOT_HAWK_PRESENT 0x2 + +int prep_keybd_present = 1; +int MotMPIC = 0; + +__initfunc(int raven_init(void)) +{ + unsigned int devid; + unsigned int pci_membase; + unsigned char base_mod; + + /* Check to see if the Raven chip exists. */ + if ( _prep_type != _PREP_Motorola) { + OpenPIC = NULL; + return 0; + } + + /* Check to see if this board is a type that might have a Raven. */ + if ((inb(MOTOROLA_CPUTYPE_REG) & 0xF0) != 0xE0) { + OpenPIC = NULL; + return 0; + } + + /* Check the first PCI device to see if it is a Raven. */ + pcibios_read_config_dword(0, 0, PCI_VENDOR_ID, &devid); + + switch (devid & 0xffff0000) { + case MPIC_RAVEN_ID: + MotMPIC = MOT_RAVEN_PRESENT; + break; + case MPIC_HAWK_ID: + MotMPIC = MOT_HAWK_PRESENT; + break; + default: + OpenPIC = NULL; + return 0; + } + + + /* Read the memory base register. */ + pcibios_read_config_dword(0, 0, PCI_BASE_ADDRESS_1, &pci_membase); + + if (pci_membase == 0) { + OpenPIC = NULL; + return 0; + } + + /* Map the Raven MPIC registers to virtual memory. */ + OpenPIC = (struct OpenPIC *)ioremap(pci_membase+0xC0000000, 0x22000); + + OpenPIC_InitSenses = mvme2600_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(mvme2600_openpic_initsenses); + + /* If raven is present on Motorola store the system config register + * for later use. + */ + ProcInfo = (unsigned long *)ioremap(0xfef80400, 4); + + /* This is a hack. If this is a 2300 or 2400 mot board then there is + * no keyboard controller and we have to indicate that. + */ + base_mod = inb(MOTOROLA_BASETYPE_REG); + if ((MotMPIC == MOT_HAWK_PRESENT) || (base_mod == 0xF9) || + (base_mod == 0xFA) || (base_mod == 0xE1)) + prep_keybd_present = 0; + + return 1; +} + +struct mot_info { + int cpu_type; /* 0x100 mask assumes for Raven and Hawk boards that the level/edge are set */ + /* 0x200 if this board has a Hawk chip. */ + int base_type; + int max_cpu; /* ored with 0x80 if this board should be checked for multi CPU */ + const char *name; + unsigned char *map; + unsigned char *routes; +} mot_info[] = { + {0x300, 0x00, 0x00, "MVME 2400", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x010, 0x00, 0x00, "Genesis", Genesis_pci_IRQ_map, Genesis_pci_IRQ_routes}, + {0x020, 0x00, 0x00, "Powerstack (Series E)", Comet_pci_IRQ_map, Comet_pci_IRQ_routes}, + {0x040, 0x00, 0x00, "Blackhawk (Powerstack)", Blackhawk_pci_IRQ_map, Blackhawk_pci_IRQ_routes}, + {0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)", Omaha_pci_IRQ_map, Omaha_pci_IRQ_routes}, + {0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)", Utah_pci_IRQ_map, Utah_pci_IRQ_routes}, + {0x0A0, 0x00, 0x00, "Powerstack (Series EX)", Comet2_pci_IRQ_map, Comet2_pci_IRQ_routes}, + {0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)", Sitka_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC", Mesquite_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF6, 0x80, "MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF6, 0x81, "Dual MTX Plus", MTXplus_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port", MTX_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xF9, 0x00, "MVME 2300", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFA, 0x00, "MVME 2300SC/2600", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011", Genesis2_pci_IRQ_map, Raven_pci_IRQ_routes}, + {0x000, 0x00, 0x00, "", NULL, NULL} +}; + +__initfunc(unsigned long prep_route_pci_interrupts(void)) { unsigned char *ibc_pirq = (unsigned char *)0x80800860; unsigned char *ibc_pcicon = (unsigned char *)0x80800840; @@ -445,49 +793,66 @@ __initfunc(unsigned long route_pci_interrupts(void)) if ( _prep_type == _PREP_Motorola) { unsigned short irq_mode; + unsigned char cpu_type; + unsigned char base_mod; + int entry; + int mot_entry = -1; - switch (inb(0x800) & 0xF0) - { - case 0x10: /* MVME16xx */ - Motherboard_map_name = "Genesis"; - Motherboard_map = Genesis_pci_IRQ_map; - Motherboard_routes = Genesis_pci_IRQ_routes; - break; - case 0x20: /* Series E */ - Motherboard_map_name = "Powerstack (Series E)"; - Motherboard_map = Comet_pci_IRQ_map; - Motherboard_routes = Comet_pci_IRQ_routes; - break; - case 0x50: /* PowerStackII Pro3000 */ - Motherboard_map_name = "Omaha (PowerStack II Pro3000)"; - Motherboard_map = Omaha_pci_IRQ_map; - Motherboard_routes = Omaha_pci_IRQ_routes; - break; - case 0x60: /* PowerStackII Pro4000 */ - Motherboard_map_name = "Utah (Powerstack II Pro4000)"; - Motherboard_map = Utah_pci_IRQ_map; - Motherboard_routes = Utah_pci_IRQ_routes; - break; - case 0xE0: /* MTX -- close enough?? to Genesis, so reuse it */ - Motherboard_map_name = "Motorola MTX"; - Motherboard_map = Genesis_pci_IRQ_map; - Motherboard_routes = Genesis_pci_IRQ_routes; - 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; - break; + cpu_type = inb(MOTOROLA_CPUTYPE_REG) & 0xF0; + base_mod = inb(MOTOROLA_BASETYPE_REG); + + for (entry = 0; mot_info[entry].cpu_type != 0; entry++) { + if (mot_info[entry].cpu_type & 0x200) { /* Check for Hawk chip */ + if (!(MotMPIC & MOT_HAWK_PRESENT)) + continue; + } else { /* Check non hawk boards */ + if ((mot_info[entry].cpu_type & 0xff) != cpu_type) + continue; + + if (mot_info[entry].base_type == 0) { + mot_entry = entry; + break; + } + + if (mot_info[entry].base_type != base_mod) + continue; + } + + if (!(mot_info[entry].max_cpu & 0x80)) { + mot_entry = entry; + break; + } + + /* processor 1 not present and max processor zero indicated */ + if ((*ProcInfo & MOT_PROC2_BIT) && !(mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } + + /* processor 1 present and max processor zero indicated */ + if (!(*ProcInfo & MOT_PROC2_BIT) && (mot_info[entry].max_cpu & 0x7f)) { + mot_entry = entry; + break; + } } - /* AJF adjust level/edge control according to routes */ - irq_mode = 0; - for (i = 1; i <= 4; i++) - { - irq_mode |= ( 1 << Motherboard_routes[i] ); + + if (mot_entry == -1) /* No particular cpu type found - assume Blackhawk */ + mot_entry = 3; + + Motherboard_map_name = (unsigned char *)mot_info[mot_entry].name; + Motherboard_map = mot_info[mot_entry].map; + Motherboard_routes = mot_info[mot_entry].routes; + + if (!(mot_info[entry].cpu_type & 0x100)) { + /* AJF adjust level/edge control according to routes */ + irq_mode = 0; + for (i = 1; i <= 4; i++) + { + irq_mode |= ( 1 << Motherboard_routes[i] ); + } + outb( irq_mode & 0xff, 0x4d0 ); + outb( (irq_mode >> 8) & 0xff, 0x4d1 ); } - outb( irq_mode & 0xff, 0x4d0 ); - outb( (irq_mode >> 8) & 0xff, 0x4d1 ); } else if ( _prep_type == _PREP_IBM ) { unsigned char pl_id; @@ -526,6 +891,71 @@ __initfunc(unsigned long route_pci_interrupts(void)) outb(pl_id|CAROLINA_IRQ_EDGE_MASK_HI, 0x04d1); pl_id=inb(0x04d1); /*printk("Hi mask now %#0x\n", pl_id);*/ + } else if ( _prep_type == _PREP_Radstone ) + { + unsigned char ucElcrM, ucElcrS; + + /* + * Set up edge/level + */ + switch(ucSystemType) + { + case RS_SYS_TYPE_PPC1: + { + if(ucBoardRevMaj<5) + { + ucElcrS=ELCRS_INT15_LVL; + } + else + { + ucElcrS=ELCRS_INT9_LVL | + ELCRS_INT11_LVL | + ELCRS_INT14_LVL | + ELCRS_INT15_LVL; + } + ucElcrM=ELCRM_INT5_LVL | ELCRM_INT7_LVL; + break; + } + + case RS_SYS_TYPE_PPC1a: + { + ucElcrS=ELCRS_INT9_LVL | + ELCRS_INT11_LVL | + ELCRS_INT14_LVL | + ELCRS_INT15_LVL; + ucElcrM=ELCRM_INT5_LVL; + break; + } + + case RS_SYS_TYPE_PPC2: + case RS_SYS_TYPE_PPC2a: + case RS_SYS_TYPE_PPC2ep: + case RS_SYS_TYPE_PPC4: + case RS_SYS_TYPE_PPC4a: + default: + { + ucElcrS=ELCRS_INT9_LVL | + ELCRS_INT10_LVL | + ELCRS_INT11_LVL | + ELCRS_INT14_LVL | + ELCRS_INT15_LVL; + ucElcrM=ELCRM_INT5_LVL | + ELCRM_INT7_LVL; + break; + } + } + + /* + * Write edge/level selection + */ + outb(ucElcrS, ISA8259_S_ELCR); + outb(ucElcrM, ISA8259_M_ELCR); + + /* + * Radstone boards have PCI interrupts all set up + * so leave well alone + */ + return 0; } else { printk("No known machine pci routing!\n"); @@ -542,3 +972,117 @@ __initfunc(unsigned long route_pci_interrupts(void)) return 0; } +__initfunc( +void +prep_pcibios_fixup(void)) +{ + struct pci_dev *dev; + extern unsigned char *Motherboard_map; + extern unsigned char *Motherboard_routes; + unsigned char i; + + if ( _prep_type == _PREP_Radstone ) + { + printk("Radstone boards require no PCI fixups\n"); + return; + } + + prep_route_pci_interrupts(); + + printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name); + if (OpenPIC) { + /* PCI interrupts are controlled by the OpenPIC */ + for(dev=pci_devices; dev; dev=dev->next) { + if (dev->bus->number == 0) { + dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); + pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_PIN, dev->irq); + } + } + return; + } + + for(dev=pci_devices; dev; dev=dev->next) + { + /* + * Use our old hard-coded kludge to figure out what + * irq this device uses. This is necessary on things + * without residual data. -- Cort + */ + unsigned char d = PCI_SLOT(dev->devfn); + dev->irq = Motherboard_routes[Motherboard_map[d]]; + + for ( i = 0 ; i <= 5 ; i++ ) + { + if ( dev->base_address[i] > 0x10000000 ) + { + printk("Relocating PCI address %lx -> %lx\n", + dev->base_address[i], + (dev->base_address[i] & 0x00FFFFFF) + | 0x01000000); + dev->base_address[i] = + (dev->base_address[i] & 0x00FFFFFF) | 0x01000000; + pci_write_config_dword(dev, + PCI_BASE_ADDRESS_0+(i*0x4), + dev->base_address[i] ); + } + } +#if 0 + /* + * If we have residual data and if it knows about this + * device ask it what the irq is. + * -- Cort + */ + ppcd = residual_find_device_id( ~0L, dev->device, + -1,-1,-1, 0); +#endif + } +} + +decl_config_access_method(indirect); + +__initfunc( +void +prep_setup_pci_ptrs(void)) +{ + PPC_DEVICE *hostbridge; + + printk("PReP architecture\n"); + if ( _prep_type == _PREP_Radstone ) + { + pci_config_address = (unsigned *)0x80000cf8; + pci_config_data = (char *)0x80000cfc; + set_config_access_method(indirect); + } + else + { + hostbridge = residual_find_device(PROCESSORDEVICE, NULL, + BridgeController, PCIBridge, -1, 0); + if (hostbridge && + hostbridge->DeviceId.Interface == PCIBridgeIndirect) { + PnP_TAG_PACKET * pkt; + set_config_access_method(indirect); + pkt = PnP_find_large_vendor_packet( + res->DevicePnPHeap+hostbridge->AllocatedOffset, + 3, 0); + if(pkt) + { +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + pci_config_address= (unsigned *)ld_le32((unsigned *) p.PPCData); + pci_config_data= (unsigned char *)ld_le32((unsigned *) (p.PPCData+8)); + } + else + { + pci_config_address= (unsigned *) 0x80000cf8; + pci_config_data= (unsigned char *) 0x80000cfc; + } + } + else + { + set_config_access_method(prep); + } + + } + + ppc_md.pcibios_fixup = prep_pcibios_fixup; +} + diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index 72752e11f..de18f465a 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -30,6 +30,9 @@ #include <linux/blk.h> #include <linux/ioport.h> #include <linux/console.h> +#include <linux/timex.h> +#include <linux/pci.h> +#include <linux/openpic.h> #include <asm/mmu.h> #include <asm/processor.h> @@ -38,12 +41,57 @@ #include <asm/pgtable.h> #include <asm/ide.h> #include <asm/cache.h> +#include <asm/dma.h> +#include <asm/machdep.h> +#include <asm/mk48t59.h> +#include <asm/prep_nvram.h> +#include <asm/raven.h> + + +#include "time.h" +#include "local_irq.h" +#include "i8259.h" +#include "open_pic.h" #if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE) #include <../drivers/sound/sound_config.h> #include <../drivers/sound/dev_table.h> #endif +unsigned char ucSystemType; +unsigned char ucBoardRev; +unsigned char ucBoardRevMaj, ucBoardRevMin; + +extern unsigned long mc146818_get_rtc_time(void); +extern int mc146818_set_rtc_time(unsigned long nowtime); +extern unsigned long mk48t59_get_rtc_time(void); +extern int mk48t59_set_rtc_time(unsigned long nowtime); + +extern unsigned char prep_nvram_read_val(int addr); +extern void prep_nvram_write_val(int addr, + unsigned char val); +extern unsigned char rs_nvram_read_val(int addr); +extern void rs_nvram_write_val(int addr, + unsigned char val); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; + +extern void prep_setup_pci_ptrs(void); +extern void chrp_do_IRQ(struct pt_regs *regs, int cpu, int isfake); +extern char saved_command_line[256]; + +int _prep_type; + +#define cached_21 (((char *)(ppc_cached_irq_mask))[3]) +#define cached_A1 (((char *)(ppc_cached_irq_mask))[2]) + /* for the mac fs */ kdev_t boot_dev; /* used in nasty hack for sound - see prep_setup_arch() -- Cort */ @@ -54,13 +102,15 @@ extern PTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern int probingmem; extern unsigned long loops_per_sec; -extern unsigned char aux_device_present; #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 +#ifdef CONFIG_VGA_CONSOLE +unsigned long vgacon_remap_base; +#endif __prep int @@ -136,6 +186,8 @@ prep_get_cpuinfo(char *buffer) break; } break; + default: + break; } @@ -163,11 +215,12 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) { extern char cmd_line[]; unsigned char reg; + unsigned char ucMothMemType; + unsigned char ucEquipPres1; /* init to some ~sane value until calibrate_delay() runs */ loops_per_sec = 50000000; - aux_device_present = 0xaa; /* Set up floppy in PS/2 mode */ outb(0x09, SIO_CONFIG_RA); reg = inb(SIO_CONFIG_RD); @@ -175,20 +228,78 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) outb(reg, SIO_CONFIG_RD); outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ + /* + * We need to set up the NvRAM access routines early as prep_init + * has yet to be called + */ + ppc_md.nvram_read_val = prep_nvram_read_val; + ppc_md.nvram_write_val = prep_nvram_write_val; + /* we should determine this according to what we find! -- Cort */ switch ( _prep_type ) { case _PREP_IBM: + /* Enable L2. Assume we don't need to flush -- Cort*/ + *(unsigned char *)(0x8000081c) |= 3; ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ break; case _PREP_Motorola: + /* Enable L2. Assume we don't need to flush -- Cort*/ + *(unsigned char *)(0x8000081c) |= 3; ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ break; + case _PREP_Radstone: + ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ + + /* + * Determine system type + */ + ucMothMemType=inb(0x866); + ucEquipPres1=inb(0x80c); + + ucSystemType=((ucMothMemType&0x03)<<1) | + ((ucEquipPres1&0x80)>>7); + ucSystemType^=7; + + /* + * Determine board revision for use by + * rev. specific code + */ + ucBoardRev=inb(0x854); + ucBoardRevMaj=ucBoardRev>>5; + ucBoardRevMin=ucBoardRev&0x1f; + + /* + * Most Radstone boards have memory mapped NvRAM + */ + if((ucSystemType==RS_SYS_TYPE_PPC1) && (ucBoardRevMaj<5)) + { + ppc_md.nvram_read_val = prep_nvram_read_val; + ppc_md.nvram_write_val = prep_nvram_write_val; + } + else + { + ppc_md.nvram_read_val = rs_nvram_read_val; + ppc_md.nvram_write_val = rs_nvram_write_val; + } + break; } - /* Enable L2. Assume we don't need to flush -- Cort*/ - *(unsigned char *)(0x8000081c) = *(unsigned char *)(0x8000081c)|3; - + /* Read in NVRAM data */ + init_prep_nvram(); + + /* if no bootargs, look in NVRAM */ + if ( cmd_line[0] == '\0' ) { + char *bootargs; + bootargs = prep_nvram_get_var("bootargs"); + if (bootargs != NULL) { + strcpy(cmd_line, bootargs); + + /* again.. */ + strcpy(saved_command_line, cmd_line); + } + } + printk("Boot arguments: %s\n", cmd_line); #ifdef CONFIG_SOUND_CS4232 @@ -238,12 +349,349 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) request_region(0x80,0x10,"dma page reg"); request_region(0xc0,0x20,"dma2"); + raven_init(); + #ifdef CONFIG_VGA_CONSOLE + /* remap the VGA memory */ + vgacon_remap_base = 0xf0000000; + /*vgacon_remap_base = ioremap(0xc0000000, 0xba000);*/ conswitchp = &vga_con; #endif } -__initfunc(void prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)) +/* + * Determine the decrementer frequency from the residual data + * This allows for a faster boot as we do not need to calibrate the + * decrementer against another clock. This is important for embedded systems. + */ +__initfunc(void prep_res_calibrate_decr(void)) +{ + int freq, divisor; + + freq = res->VitalProductData.ProcessorBusHz; + divisor = 4; + printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; +} + +/* + * 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 + * but on prep we have to figure it out. + * -- Cort + */ +int calibrate_done = 0; +volatile int *done_ptr = &calibrate_done; + +__initfunc(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) + { + t2 = get_dec(); + t2 = t1-t2; /* decr's in 1/HZ */ + t2 = t2*HZ; /* # decrs in 1s - thus in Hz */ + 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; + *done_ptr = 1; + } +} + +__initfunc(void prep_calibrate_decr(void)) +{ + unsigned long flags; + + + save_flags(flags); + +#define TIMER0_COUNT 0x40 +#define TIMER_CONTROL 0x43 + /* set timer to periodic mode */ + outb_p(0x34,TIMER_CONTROL);/* binary, mode 2, LSB/MSB, ch 0 */ + /* set the clock to ~100 Hz */ + outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */ + outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */ + + if (request_irq(0, prep_calibrate_decr_handler, 0, "timer", NULL) != 0) + panic("Could not allocate timer IRQ!"); + __sti(); + while ( ! *done_ptr ) /* nothing */; /* wait for calibrate */ + restore_flags(flags); + free_irq( 0, NULL); +} + + +/* We use the NVRAM RTC to time a second to calibrate the decrementer. */ +__initfunc(void mk48t59_calibrate_decr(void)) +{ + unsigned long freq, divisor; + unsigned long t1, t2; + unsigned char save_control; + long i; + unsigned char sec; + + + /* Make sure the time is not stopped. */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLB); + + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control & (~MK48T59_RTC_CB_STOP))); + + /* Now make sure the read bit is off so the value will change. */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + save_control &= ~MK48T59_RTC_CA_READ; + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + + /* Read the seconds value to see when it changes. */ + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + for (i = 0 ; i < 1000000 ; i++) { /* may take up to 1 second... */ + if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { + break; + } + } + t1 = get_dec(); + + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + for (i = 0 ; i < 1000000 ; i++) { /* Should take up 1 second... */ + if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { + break; + } + } + + t2 = t1 - get_dec(); + + 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; +} + +void +prep_restart(char *cmd) +{ + unsigned long i = 10000; + + + _disable_interrupts(); + + /* set exception prefix high - to the prom */ + _nmask_and_or_msr(0, MSR_IP); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( i != 0 ) i++; + panic("restart failed\n"); +} + +/* + * This function will restart a board regardless of port 92 functionality + */ +void +prep_direct_restart(char *cmd) +{ + u32 jumpaddr=0xfff00100; + u32 defaultmsr=MSR_IP; + + /* + * This will ALWAYS work regardless of port 92 + * functionality + */ + _disable_interrupts(); + + __asm__ __volatile__("\n\ + mtspr 26, %1 /* SRR0 */ + mtspr 27, %0 /* SRR1 */ + rfi" + : + : "r" (defaultmsr), "r" (jumpaddr)); + /* + * Not reached + */ +} + +void +prep_halt(void) +{ + unsigned long flags; + _disable_interrupts(); + /* set exception prefix high - to the prom */ + save_flags( flags ); + restore_flags( flags|MSR_IP ); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( 1 ) ; + /* + * Not reached + */ +} + +void +prep_power_off(void) +{ + prep_halt(); +} + +int prep_setup_residual(char *buffer) +{ + int len = 0; + + + /* PREP's without residual data will give incorrect values here */ + len += sprintf(len+buffer, "clock\t\t: "); + if ( res->ResidualLength ) + len += sprintf(len+buffer, "%ldMHz\n", + (res->VitalProductData.ProcessorHz > 1024) ? + res->VitalProductData.ProcessorHz>>20 : + res->VitalProductData.ProcessorHz); + else + len += sprintf(len+buffer, "???\n"); + + return len; +} + +u_int +prep_irq_cannonicalize(u_int irq) +{ + if (irq == 2) + { + return 9; + } + else + { + return irq; + } +} + +void +prep_do_IRQ(struct pt_regs *regs, int cpu, int isfake) +{ + int irq; + + if ( (irq = i8259_irq(0)) < 0 ) + { + printk(KERN_DEBUG "Bogus interrupt from PC = %lx\n", + regs->nip); + ppc_spurious_interrupts++; + return; + } + ppc_irq_dispatch_handler( regs, irq ); +} + +__initfunc(void +prep_init_IRQ(void)) +{ + int i; + + if (OpenPIC != NULL) { + for ( i = 16 ; i < 36 ; i++ ) + irq_desc[i].ctl = &open_pic; + openpic_init(1); + } + + for ( i = 0 ; i < 16 ; i++ ) + irq_desc[i].ctl = &i8259_pic; + i8259_init(); +#ifdef __SMP__ + request_irq(openpic_to_irq(OPENPIC_VEC_SPURIOUS), openpic_ipi_action, + 0, "IPI0", 0); +#endif /* __SMP__ */ +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +/* + * IDE stuff. + */ +void +prep_ide_insw(ide_ioreg_t port, void *buf, int ns) +{ + _insw((unsigned short *)((port)+_IO_BASE), buf, ns); +} + +void +prep_ide_outsw(ide_ioreg_t port, void *buf, int ns) +{ + _outsw((unsigned short *)((port)+_IO_BASE), buf, ns); +} + +int +prep_ide_default_irq(ide_ioreg_t base) +{ + switch (base) { + case 0x1f0: return 13; + case 0x170: return 13; + case 0x1e8: return 11; + case 0x168: return 10; + default: + return 0; + } +} + +ide_ioreg_t +prep_ide_default_io_base(int index) +{ + switch (index) { + case 0: return 0x1f0; + case 1: return 0x170; + case 2: return 0x1e8; + case 3: return 0x168; + default: + return 0; + } +} + +int +prep_ide_check_region(ide_ioreg_t from, unsigned int extent) +{ + return check_region(from, extent); +} + +void +prep_ide_request_region(ide_ioreg_t from, + unsigned int extent, + const char *name) +{ + request_region(from, extent, name); +} + +void +prep_ide_release_region(ide_ioreg_t from, + unsigned int extent) +{ + release_region(from, extent); +} + +void +prep_ide_fix_driveid(struct hd_driveid *id) +{ +} + +__initfunc(void +prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq)) { ide_ioreg_t port = base; int i = 8; @@ -254,6 +702,143 @@ __initfunc(void prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int if (irq != NULL) *irq = 0; } +#endif + +__initfunc(void +prep_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7)) +{ + /* make a copy of residual data */ + if ( r3 ) + { + memcpy((void *)res,(void *)(r3+KERNELBASE), + sizeof(RESIDUAL)); + } + + isa_io_base = PREP_ISA_IO_BASE; + isa_mem_base = PREP_ISA_MEM_BASE; + pci_dram_offset = PREP_PCI_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + /* figure out what kind of prep workstation we are */ + if ( res->ResidualLength != 0 ) + { + if ( !strncmp(res->VitalProductData.PrintableModel,"IBM",3) ) + _prep_type = _PREP_IBM; + else if (!strncmp(res->VitalProductData.PrintableModel, + "Radstone",8)) + { + extern char *Motherboard_map_name; + + _prep_type = _PREP_Radstone; + Motherboard_map_name= + res->VitalProductData.PrintableModel; + } + else + _prep_type = _PREP_Motorola; + } + else /* assume motorola if no residual (netboot?) */ + { + _prep_type = _PREP_Motorola; + } + + prep_setup_pci_ptrs(); + +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* take care of cmd line */ + if ( r6 && (((char *) r6) != '\0')) + { + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + + ppc_md.setup_arch = prep_setup_arch; + ppc_md.setup_residual = prep_setup_residual; + ppc_md.get_cpuinfo = prep_get_cpuinfo; + ppc_md.irq_cannonicalize = prep_irq_cannonicalize; + ppc_md.init_IRQ = prep_init_IRQ; + if ( !OpenPIC ) + ppc_md.do_IRQ = prep_do_IRQ; + else + ppc_md.do_IRQ = chrp_do_IRQ; + ppc_md.init = NULL; + + ppc_md.restart = prep_restart; + ppc_md.power_off = prep_power_off; + ppc_md.halt = prep_halt; + + ppc_md.time_init = NULL; + if (_prep_type == _PREP_Radstone) { + /* + * We require a direct restart as port 92 does not work on + * all Radstone boards + */ + ppc_md.restart = prep_direct_restart; + /* + * The RTC device used varies according to board type + */ + if(((ucSystemType==RS_SYS_TYPE_PPC1) && (ucBoardRevMaj>=5)) || + (ucSystemType==RS_SYS_TYPE_PPC1a)) + { + ppc_md.set_rtc_time = mk48t59_set_rtc_time; + ppc_md.get_rtc_time = mk48t59_get_rtc_time; + } + else + { + ppc_md.set_rtc_time = mc146818_set_rtc_time; + ppc_md.get_rtc_time = mc146818_get_rtc_time; + } + /* + * Determine the decrementer rate from the residual data + */ + ppc_md.calibrate_decr = prep_res_calibrate_decr; + } + else if (_prep_type == _PREP_IBM) { + ppc_md.set_rtc_time = mc146818_set_rtc_time; + ppc_md.get_rtc_time = mc146818_get_rtc_time; + ppc_md.calibrate_decr = prep_calibrate_decr; + } + else { + ppc_md.set_rtc_time = mk48t59_set_rtc_time; + ppc_md.get_rtc_time = mk48t59_get_rtc_time; + ppc_md.calibrate_decr = mk48t59_calibrate_decr; + } + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.insw = prep_ide_insw; + ppc_ide_md.outsw = prep_ide_outsw; + ppc_ide_md.default_irq = prep_ide_default_irq; + ppc_ide_md.default_io_base = prep_ide_default_io_base; + ppc_ide_md.check_region = prep_ide_check_region; + ppc_ide_md.request_region = prep_ide_request_region; + ppc_ide_md.release_region = prep_ide_release_region; + ppc_ide_md.fix_driveid = prep_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = prep_ide_init_hwif_ports; +#endif + ppc_ide_md.io_base = _IO_BASE; + +#ifdef CONFIG_VT + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.kbd_sysrq_xlate = pckbd_sysrq_xlate; +#endif +#endif +} #ifdef CONFIG_SOUND_MODULE EXPORT_SYMBOL(ppc_cs4232_dma); diff --git a/arch/ppc/kernel/prep_time.c b/arch/ppc/kernel/prep_time.c index f1dff8f13..5b8873d79 100644 --- a/arch/ppc/kernel/prep_time.c +++ b/arch/ppc/kernel/prep_time.c @@ -22,7 +22,9 @@ #include <asm/segment.h> #include <asm/io.h> #include <asm/processor.h> -#include <asm/nvram.h> +#include <asm/machdep.h> +#include <asm/prep_nvram.h> +#include <asm/mk48t59.h> #include "time.h" @@ -41,73 +43,29 @@ * is setup at boot time to use the correct addresses. * -- Cort */ -/* - * translate from mc146818 to m48t18 addresses - */ -unsigned int clock_transl[] __prepdata = { MOTO_RTC_SECONDS,0 /* alarm */, - MOTO_RTC_MINUTES,0 /* alarm */, - MOTO_RTC_HOURS,0 /* alarm */, /* 4,5 */ - MOTO_RTC_DAY_OF_WEEK, - MOTO_RTC_DAY_OF_MONTH, - MOTO_RTC_MONTH, - MOTO_RTC_YEAR, /* 9 */ - MOTO_RTC_CONTROLA, MOTO_RTC_CONTROLB /* 10,11 */ -}; - -__prep -int prep_cmos_clock_read(int addr) -{ - if ( _prep_type == _PREP_IBM ) - return CMOS_READ(addr); - else if ( _prep_type == _PREP_Motorola ) - { - outb(clock_transl[addr]>>8, NVRAM_AS1); - outb(clock_transl[addr], NVRAM_AS0); - return (inb(NVRAM_DATA)); - } - - printk("Unknown machine in prep_cmos_clock_read()!\n"); - return -1; -} - -__prep -void prep_cmos_clock_write(unsigned long val, int addr) -{ - if ( _prep_type == _PREP_IBM ) - { - CMOS_WRITE(val,addr); - return; - } - else if ( _prep_type == _PREP_Motorola ) - { - outb(clock_transl[addr]>>8, NVRAM_AS1); - outb(clock_transl[addr], NVRAM_AS0); - outb(val,NVRAM_DATA); - return; - } - printk("Unknown machine in prep_cmos_clock_write()!\n"); -} /* * Set the hardware clock. -- Cort */ __prep -int prep_set_rtc_time(unsigned long nowtime) +int mc146818_set_rtc_time(unsigned long nowtime) { unsigned char save_control, save_freq_select; struct rtc_time tm; to_tm(nowtime, &tm); - save_control = prep_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ - - prep_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); - - save_freq_select = prep_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ + /* tell the clock it's being set */ + save_control = CMOS_READ(RTC_CONTROL); - prep_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - tm.tm_year -= 1900; + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + + /* stop and reset prescaler */ + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); + + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year = (tm.tm_year - 1900) % 100; if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { BIN_TO_BCD(tm.tm_sec); BIN_TO_BCD(tm.tm_min); @@ -116,12 +74,12 @@ int prep_set_rtc_time(unsigned long nowtime) BIN_TO_BCD(tm.tm_mday); BIN_TO_BCD(tm.tm_year); } - prep_cmos_clock_write(tm.tm_sec,RTC_SECONDS); - prep_cmos_clock_write(tm.tm_min,RTC_MINUTES); - prep_cmos_clock_write(tm.tm_hour,RTC_HOURS); - prep_cmos_clock_write(tm.tm_mon,RTC_MONTH); - prep_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); - prep_cmos_clock_write(tm.tm_year,RTC_YEAR); + CMOS_WRITE(tm.tm_sec, RTC_SECONDS); + CMOS_WRITE(tm.tm_min, RTC_MINUTES); + CMOS_WRITE(tm.tm_hour, RTC_HOURS); + CMOS_WRITE(tm.tm_mon, RTC_MONTH); + CMOS_WRITE(tm.tm_mday, RTC_DAY_OF_MONTH); + CMOS_WRITE(tm.tm_year, RTC_YEAR); /* The following flags have to be released exactly in this order, * otherwise the DS12887 (popular MC146818A clone with integrated @@ -130,16 +88,14 @@ int prep_set_rtc_time(unsigned long nowtime) * the Dallas Semiconductor data sheets, but who believes data * sheets anyway ... -- Markus Kuhn */ - prep_cmos_clock_write(save_control, RTC_CONTROL); - prep_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); - if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) - time_state = TIME_OK; return 0; } __prep -unsigned long prep_get_rtc_time(void) +unsigned long mc146818_get_rtc_time(void) { unsigned int year, mon, day, hour, min, sec; int i; @@ -151,29 +107,123 @@ unsigned long prep_get_rtc_time(void) */ /* read RTC exactly on falling edge of update flag */ for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ - if (prep_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP) + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) break; for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ - if (!(prep_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP)) + if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) break; do { /* Isn't this overkill ? UIP above should guarantee consistency */ - sec = prep_cmos_clock_read(RTC_SECONDS); - min = prep_cmos_clock_read(RTC_MINUTES); - hour = prep_cmos_clock_read(RTC_HOURS); - day = prep_cmos_clock_read(RTC_DAY_OF_MONTH); - mon = prep_cmos_clock_read(RTC_MONTH); - year = prep_cmos_clock_read(RTC_YEAR); - } while (sec != prep_cmos_clock_read(RTC_SECONDS)); - if (!(prep_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - { - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + } while (sec != CMOS_READ(RTC_SECONDS)); + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) + || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } if ((year += 1900) < 1970) year += 100; return mktime(year, mon, day, hour, min, sec); } + +__prep +int mk48t59_set_rtc_time(unsigned long nowtime) +{ + unsigned char save_control; + struct rtc_time tm; + + + to_tm(nowtime, &tm); + + /* tell the clock it's being written */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control | MK48T59_RTC_CA_WRITE)); + + tm.tm_year = (tm.tm_year - 1900) % 100; + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + + ppc_md.nvram_write_val(MK48T59_RTC_SECONDS, tm.tm_sec); + ppc_md.nvram_write_val(MK48T59_RTC_MINUTES, tm.tm_min); + ppc_md.nvram_write_val(MK48T59_RTC_HOURS, tm.tm_hour); + ppc_md.nvram_write_val(MK48T59_RTC_MONTH, tm.tm_mon); + ppc_md.nvram_write_val(MK48T59_RTC_DAY_OF_MONTH, tm.tm_mday); + ppc_md.nvram_write_val(MK48T59_RTC_YEAR, tm.tm_year); + + /* Turn off the write bit. */ + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + return 0; +} + +__prep +unsigned long mk48t59_get_rtc_time(void) +{ + unsigned char save_control; + unsigned int year, mon, day, hour, min, sec; + int i; + + /* Make sure the time is not stopped. */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLB); + + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control & (~MK48T59_RTC_CB_STOP))); + + /* Now make sure the read bit is off so the value will change. */ + save_control = ppc_md.nvram_read_val(MK48T59_RTC_CONTROLA); + save_control &= ~MK48T59_RTC_CA_READ; + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + /* Read the seconds value to see when it changes. */ + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + + /* Wait until the seconds value changes, then read the value. */ + for (i = 0 ; i < 1000000 ; i++) { /* may take up to 1 second... */ + if (ppc_md.nvram_read_val(MK48T59_RTC_SECONDS) != sec) { + break; + } + } + + /* Set the register to read the value. */ + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, + (save_control | MK48T59_RTC_CA_READ)); + + sec = ppc_md.nvram_read_val(MK48T59_RTC_SECONDS); + min = ppc_md.nvram_read_val(MK48T59_RTC_MINUTES); + hour = ppc_md.nvram_read_val(MK48T59_RTC_HOURS); + day = ppc_md.nvram_read_val(MK48T59_RTC_DAY_OF_MONTH); + mon = ppc_md.nvram_read_val(MK48T59_RTC_MONTH); + year = ppc_md.nvram_read_val(MK48T59_RTC_YEAR); + + /* Let the time values change again. */ + ppc_md.nvram_write_val(MK48T59_RTC_CONTROLA, save_control); + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + + year = year + 1900; + if (year < 1970) { + year += 100; + } + + return mktime(year, mon, day, hour, min, sec); +} diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index ebe64a429..db87c2384 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -1,5 +1,5 @@ /* - * $Id: process.c,v 1.70 1999/01/07 16:28:59 cort Exp $ + * $Id: process.c,v 1.83 1999/05/10 04:43:43 cort Exp $ * * linux/arch/ppc/kernel/process.c * @@ -43,12 +43,18 @@ #include <asm/prom.h> int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); -void switch_to(struct task_struct *, struct task_struct *); - extern unsigned long _get_SP(void); -extern spinlock_t scheduler_lock; struct task_struct *last_task_used_math = NULL; +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct file * init_fd_array[NR_OPEN] = { NULL, }; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM; +union task_union init_task_union = { INIT_TASK }; +/* only used to get secondary processor up */ +struct task_struct *current_set[NR_CPUS] = {&init_task, }; #undef SHOW_TASK_SWITCHES 1 #undef CHECK_STACK 1 @@ -65,32 +71,28 @@ task_top(struct task_struct *tsk) return ((unsigned long)tsk) + sizeof(struct task_struct); } -static struct vm_area_struct init_mmap = INIT_MMAP; -static struct fs_struct init_fs = INIT_FS; -static struct file * init_fd_array[NR_OPEN] = { NULL, }; -static struct files_struct init_files = INIT_FILES; -static struct signal_struct init_signals = INIT_SIGNALS; - -struct mm_struct init_mm = INIT_MM; -union task_union init_task_union = { INIT_TASK }; - -/* only used to get secondary processor up */ -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 + if (regs->msr & MSR_FP) + giveup_fpu(current); memcpy(fpregs, ¤t->tss.fpr[0], sizeof(*fpregs)); return 1; } +void +enable_kernel_fp(void) +{ +#ifdef __SMP__ + if (current->tss.regs && (current->tss.regs->msr & MSR_FP)) + giveup_fpu(current); + else + giveup_fpu(NULL); /* just enables FP for kernel */ +#else + giveup_fpu(last_task_used_math); +#endif /* __SMP__ */ +} + /* check to make sure the kernel stack is healthy */ int check_stack(struct task_struct *tsk) { @@ -155,7 +157,8 @@ int check_stack(struct task_struct *tsk) } void -switch_to(struct task_struct *prev, struct task_struct *new) +_switch_to(struct task_struct *prev, struct task_struct *new, + struct task_struct **last) { struct thread_struct *new_tss, *old_tss; int s = _disable_interrupts(); @@ -165,10 +168,10 @@ switch_to(struct task_struct *prev, struct task_struct *new) #endif #ifdef SHOW_TASK_SWITCHES - printk("%s/%d -> %s/%d NIP %08lx cpu %d lock %x root %x/%x\n", + printk("%s/%d -> %s/%d NIP %08lx cpu %d root %x/%x\n", prev->comm,prev->pid, new->comm,new->pid,new->tss.regs->nip,new->processor, - scheduler_lock.lock,new->fs->root,prev->fs->root); + new->fs->root,prev->fs->root); #endif #ifdef __SMP__ /* avoid complexity of lazy save/restore of fpu @@ -176,18 +179,19 @@ switch_to(struct task_struct *prev, struct task_struct *new) * this task used the fpu during the last quantum. * * If it tries to use the fpu again, it'll trap and - * reload its fp regs. + * reload its fp regs. So we don't have to do a restore + * every switch, just a save. * -- Cort */ - if ( prev->tss.regs->msr & MSR_FP ) - smp_giveup_fpu(prev); + if (prev->tss.regs && (prev->tss.regs->msr & MSR_FP)) + giveup_fpu(prev); prev->last_processor = prev->processor; current_set[smp_processor_id()] = new; #endif /* __SMP__ */ new_tss = &new->tss; old_tss = ¤t->tss; - _switch(old_tss, new_tss, new->mm->context); + *last = _switch(old_tss, new_tss, new->mm->context); _enable_interrupts(s); } @@ -240,7 +244,12 @@ void instruction_dump (unsigned long *pc) printk("Instruction DUMP:"); for(i = -3; i < 6; i++) - printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>'); + { + unsigned long p; + if (__get_user( p, &pc[i] )) + break; + printk("%c%08lx%c",i?' ':'<',p,i?' ':'>'); + } printk("\n"); } @@ -268,8 +277,12 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, struct task_struct * p, struct pt_regs * regs) { - struct pt_regs * childregs; - + struct pt_regs * childregs, *kregs; +#ifdef __SMP__ + extern void ret_from_smpfork(void); +#else + extern void ret_from_syscall(void); +#endif /* Copy registers */ childregs = ((struct pt_regs *) ((unsigned long)p + sizeof(union task_union) @@ -278,8 +291,19 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, 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.regs = childregs; p->tss.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; - p->tss.regs = childregs; + p->tss.ksp -= sizeof(struct pt_regs ) + STACK_FRAME_OVERHEAD; + kregs = (struct pt_regs *)(p->tss.ksp + STACK_FRAME_OVERHEAD); +#ifdef __SMP__ + kregs->nip = (unsigned long)ret_from_smpfork; +#else + kregs->nip = (unsigned long)ret_from_syscall; +#endif + kregs->msr = MSR_KERNEL; + kregs->gpr[1] = (unsigned long)childregs - STACK_FRAME_OVERHEAD; + kregs->gpr[2] = (unsigned long)p; + if (usp >= (unsigned long) regs) { /* Stack is in kernel space - must adjust */ childregs->gpr[1] = (unsigned long)(childregs + 1); @@ -293,21 +317,14 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, * 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 + if (regs->msr & MSR_FP) + giveup_fpu(current); 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; @@ -374,11 +391,6 @@ asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, 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. @@ -397,9 +409,7 @@ asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, int res; - lock_kernel(); 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. @@ -408,10 +418,15 @@ asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, if ((current->pid == 0) && (current == &init_task)) res = 1; #endif /* __SMP__ */ - unlock_kernel(); return res; } +asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs); +} + asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs *regs) @@ -423,13 +438,8 @@ asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; -#ifdef __SMP__ - if ( regs->msr & MSR_FP ) - smp_giveup_fpu(current); -#else - if ( last_task_used_math == current ) - giveup_fpu(); -#endif + if (regs->msr & MSR_FP) + giveup_fpu(current); error = do_execve(filename, (char **) a1, (char **) a2, regs); putname(filename); out: diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c index da9d7a04c..b2221481a 100644 --- a/arch/ppc/kernel/prom.c +++ b/arch/ppc/kernel/prom.c @@ -1,5 +1,5 @@ /* - * $Id: prom.c,v 1.46 1998/11/11 03:55:09 paulus Exp $ + * $Id: prom.c,v 1.54 1999/05/10 04:43:46 cort Exp $ * * Procedures for interfacing to the Open Firmware PROM on * Power Macintosh computers. @@ -16,6 +16,7 @@ #include <linux/string.h> #include <linux/init.h> #include <linux/version.h> +#include <asm/spinlock.h> #include <asm/prom.h> #include <asm/page.h> #include <asm/processor.h> @@ -99,10 +100,12 @@ unsigned int old_rtas = 0; static struct device_node *allnodes = 0; static void clearscreen(void); +static void flushscreen(void); #ifdef CONFIG_BOOTX_TEXT static void drawchar(char c); +static void drawhex(unsigned long v); static void drawstring(const char *c); static void scrollscreen(void); @@ -167,6 +170,11 @@ boot_infos_t *boot_infos = 0; /* init it so it's in data segment not bss */ #define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) +/* Is boot-info compatible ? */ +#define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION) +#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) +#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) + __init static void prom_exit() @@ -256,6 +264,11 @@ __init void prom_init(int r3, int r4, prom_entry pp) { +#ifdef CONFIG_SMP + int cpu = 0, i; + phandle node; + char type[16], *path; +#endif unsigned long mem; ihandle prom_rtas; unsigned long offset = reloc_offset(); @@ -273,6 +286,9 @@ prom_init(int r3, int r4, prom_entry pp) unsigned long space; unsigned long ptr, x; char *model; +#ifdef CONFIG_BOOTX_TEXT + unsigned long flags; +#endif RELOC(boot_infos) = PTRUNRELOC(bi); @@ -283,32 +299,72 @@ prom_init(int r3, int r4, prom_entry pp) RELOC(g_loc_Y) = 0; RELOC(g_max_loc_X) = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8; RELOC(g_max_loc_Y) = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16; - prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE " booting...\n")); + + /* Test if boot-info is compatible. Done only in config CONFIG_BOOTX_TEXT since + there is nothing much we can do with an incompatible version, except display + a message and eventually hang the processor... + + I'll try to keep enough of boot-info compatible in the future to always allow + display of this message; + */ + if (!BOOT_INFO_IS_COMPATIBLE(bi)) + prom_print(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n")); + + prom_print(RELOC("Welcome to Linux, kernel " UTS_RELEASE "\n")); + prom_print(RELOC("\nstarted at : 0x")); + drawhex(reloc_offset() + KERNELBASE); + prom_print(RELOC("\nlinked at : 0x")); + drawhex(KERNELBASE); + prom_print(RELOC("\nframe buffer at : 0x")); + drawhex((unsigned long)bi->dispDeviceBase); + prom_print(RELOC(" (phys), 0x")); + drawhex((unsigned long)bi->logicalDisplayBase); + prom_print(RELOC(" (log)")); + prom_print(RELOC("\nMSR : 0x")); + __asm__ __volatile__ ("mfmsr %0" : "=r" ((flags)) : : "memory"); + drawhex(flags); + prom_print(RELOC("\n\n")); #endif - - /* - * XXX If this is an iMac, turn off the USB controller. + /* Out of the #if/#endif since it flushes the clearscreen too */ + flushscreen(); + + /* New BootX enters kernel with MMU off, i/os are not allowed + here. This hack will have been done by the boostrap anyway. */ - model = (char *) early_get_property - (r4 + bi->deviceTreeOffset, 4, RELOC("model")); - if (model && strcmp(model, RELOC("iMac,1")) == 0) { - out_le32((unsigned *)0x80880008, 1); /* XXX */ + if (bi->version < 4) { + /* + * XXX If this is an iMac, turn off the USB controller. + */ + model = (char *) early_get_property + (r4 + bi->deviceTreeOffset, 4, RELOC("model")); + if (model && strcmp(model, RELOC("iMac,1")) == 0) { + out_le32((unsigned *)0x80880008, 1); /* XXX */ + } } - + space = bi->deviceTreeOffset + bi->deviceTreeSize; if (bi->ramDisk) space = bi->ramDisk + bi->ramDiskSize; RELOC(klimit) = PTRUNRELOC((char *) bi + space); - /* - * Touch each page to make sure the PTEs for them - * are in the hash table - the aim is to try to avoid - * getting DSI exceptions while copying the kernel image. + /* New BootX will have flushed all TLBs and enters kernel with + MMU switched OFF, so this should not be useful anymore. */ - for (ptr = (KERNELBASE + offset) & PAGE_MASK; - ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) - x = *(volatile unsigned long *)ptr; - + if (bi->version < 4) { + /* + * Touch each page to make sure the PTEs for them + * are in the hash table - the aim is to try to avoid + * getting DSI exceptions while copying the kernel image. + */ + for (ptr = (KERNELBASE + offset) & PAGE_MASK; + ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) + x = *(volatile unsigned long *)ptr; + } + +#ifdef CONFIG_BOOTX_TEXT + prom_print(RELOC("booting...\n")); + flushscreen(); +#endif return; } @@ -379,7 +435,7 @@ prom_init(int r3, int r4, prom_entry pp) 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-offset); + prom_args.args[2] = ((void *)(RELOC(rtas_data)-KERNELBASE)); RELOC(prom)(&prom_args); if (prom_args.args[nargs] != 0) i = 0; @@ -393,6 +449,81 @@ prom_init(int r3, int r4, prom_entry pp) prom_print(RELOC(" done\n")); } RELOC(klimit) = (char *) (mem - offset); +#ifdef CONFIG_SMP + /* + * With CHRP SMP we need to use the OF to start the other + * processors so we can't wait until smp_boot_cpus (the OF is + * trashed by then) so we have to put the processors into + * a holding pattern controlled by the kernel (not OF) before + * we destroy the OF. + * + * This uses a chunk of high memory, puts some holding pattern + * code there and sends the other processors off to there until + * smp_boot_cpus tells them to do something. We do that by using + * physical address 0x0. The holding pattern checks that address + * until its cpu # is there, when it is that cpu jumps to + * __secondary_start(). smp_boot_cpus() takes care of setting those + * values. + * + * We also use physical address 0x4 here to tell when a cpu + * is in its holding pattern code. + * + * -- Cort + */ + { + extern void __secondary_hold(void); + unsigned long i; + char type[16]; + + + /* + * XXX: hack to make sure we're chrp, assume that if we're + * chrp we have a device_type property -- Cort + */ + node = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); + if ( (int)call_prom(RELOC("getprop"), 4, 1, node, + RELOC("device_type"),type, sizeof(type)) <= 0) + return; + + /* copy the holding pattern code to someplace safe (8M) */ + memcpy( (void *)(8<<20), RELOC(__secondary_hold), 0x10000 ); + for (i = 8<<20; i < ((8<<20)+0x10000); i += 32) + { + asm volatile("dcbf 0,%0" : : "r" (i) : "memory"); + asm volatile("icbi 0,%0" : : "r" (i) : "memory"); + } + } + + /* look for cpus */ + for (node = 0; prom_next_node(&node);) + { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("cpu")) != 0) + continue; + path = (char *) mem; + memset(path, 0, 256); + if ((int) call_prom(RELOC("package-to-path"), 3, 1, + node, path, 255) < 0) + continue; + /* XXX: hack - don't start cpu 0, this cpu -- Cort */ + if ( cpu++ == 0 ) + continue; + prom_print(RELOC("starting cpu ")); + prom_print(path); + *(unsigned long *)(0x4) = 0; + asm volatile("dcbf 0,%0": : "r" (0x4) : "memory"); + call_prom(RELOC("start-cpu"), 3, 0, node, 8<<20, cpu-1); + for ( i = 0 ; (i < 10000) && + (*(ulong *)(0x4) == (ulong)0); i++ ) + ; + if (*(ulong *)(0x4) == (ulong)cpu-1 ) + prom_print(RELOC("...ok\n")); + else + prom_print(RELOC("...failed\n")); + } +#endif } /* @@ -631,6 +762,12 @@ finish_node(struct device_node *np, unsigned long mem_start, mem_start = ifunc(np, mem_start); } + /* the f50 sets the name to 'display' and 'compatible' to what we + * expect for the name -- Cort + */ + if (!strcmp(np->name, "display")) + np->name = get_property(np, "compatible", 0); + if (!strcmp(np->name, "device-tree")) ifunc = interpret_root_props; else if (np->type == 0) @@ -1007,7 +1144,7 @@ device_is_compatible(struct device_node *device, const char *compat) if (cp == NULL) return 0; while (cplen > 0) { - if (strcasecmp(cp, compat) == 0) + if (strncasecmp(cp, compat, strlen(compat)) == 0) return 1; l = strlen(cp) + 1; cp += l; @@ -1143,6 +1280,8 @@ print_properties(struct device_node *np) } #endif +spinlock_t rtas_lock = SPIN_LOCK_UNLOCKED; + /* this can be called after setup -- Cort */ __openfirmware int @@ -1173,7 +1312,9 @@ call_rtas(const char *service, int nargs, int nret, for (i = 0; i < nargs; ++i) u.words[i+3] = va_arg(list, unsigned long); va_end(list); + spin_lock(&rtas_lock); enter_rtas((void *)__pa(&u)); + spin_unlock(&rtas_lock); if (nret > 1 && outputs != NULL) for (i = 0; i < nret-1; ++i) outputs[i] = u.words[i+nargs+4]; @@ -1191,8 +1332,11 @@ abort() prom_exit(); } -#define CALC_BASE(y) (bi->dispDeviceBase + bi->dispDeviceRect[0] * \ - (bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes * (y)) +/* Calc the base address of a given point (x,y) */ +#define CALC_BASE(x,y) ((BOOT_INFO_IS_V2_COMPATIBLE(bi) ? bi->logicalDisplayBase : \ + bi->dispDeviceBase) + (bi->dispDeviceRect[0] + (x)) * \ + (bi->dispDeviceDepth >> 3) + bi->dispDeviceRowBytes * \ + ((y) + bi->dispDeviceRect[1])) __init static void @@ -1200,7 +1344,7 @@ clearscreen(void) { unsigned long offset = reloc_offset(); boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned long *base = (unsigned long *)CALC_BASE(0); + unsigned long *base = (unsigned long *)CALC_BASE(0,0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; int i,j; @@ -1214,6 +1358,33 @@ clearscreen(void) } } +__inline__ void dcbst(const void* addr) +{ + __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr)); +} + +__init +static void +flushscreen(void) +{ + unsigned long offset = reloc_offset(); + boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + unsigned long *base = (unsigned long *)CALC_BASE(0,0); + unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 2; + int i,j; + + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) + { + unsigned long *ptr = base; + for(j=width; j>0; j-=8) { + dcbst(ptr); + ptr += 8; + } + base += (bi->dispDeviceRowBytes >> 2); + } +} + #ifdef CONFIG_BOOTX_TEXT __init @@ -1222,8 +1393,8 @@ scrollscreen(void) { unsigned long offset = reloc_offset(); boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned long *src = (unsigned long *)CALC_BASE(16); - unsigned long *dst = (unsigned long *)CALC_BASE(0); + unsigned long *src = (unsigned long *)CALC_BASE(0,16); + unsigned long *dst = (unsigned long *)CALC_BASE(0,0); unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3)) >> 2; int i,j; @@ -1252,20 +1423,17 @@ drawchar(char c) { unsigned long offset = reloc_offset(); - switch(c) - { - case '\r': RELOC(g_loc_X) = 0; break; - case '\n': RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; break; + switch(c) { + case '\r': RELOC(g_loc_X) = 0; break; + case '\n': RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; break; default: draw_byte(c, RELOC(g_loc_X)++, RELOC(g_loc_Y)); - if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) - { + if (RELOC(g_loc_X) >= RELOC(g_max_loc_X)) { RELOC(g_loc_X) = 0; RELOC(g_loc_Y)++; } } - while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) - { + while (RELOC(g_loc_Y) >= RELOC(g_max_loc_Y)) { scrollscreen(); RELOC(g_loc_Y)--; } @@ -1281,17 +1449,32 @@ drawstring(const char *c) __init static void +drawhex(unsigned long v) +{ + static char hex_table[] = "0123456789abcdef"; + unsigned long offset = reloc_offset(); + + drawchar(RELOC(hex_table)[(v >> 28) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 24) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 20) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 16) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 12) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 8) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 4) & 0x0000000FUL]); + drawchar(RELOC(hex_table)[(v >> 0) & 0x0000000FUL]); +} + + +__init +static void draw_byte(unsigned char c, long locX, long locY) { unsigned long offset = reloc_offset(); - boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); - unsigned char *base = bi->dispDeviceBase - + (bi->dispDeviceRowBytes * ((locY * 16) + bi->dispDeviceRect[1])) - + (bi->dispDeviceDepth >> 3) * ((locX * 8) + bi->dispDeviceRect[0]); - unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16]; + boot_infos_t* bi = PTRRELOC(RELOC(boot_infos)); + unsigned char *base = CALC_BASE(locX << 3, locY << 4); + unsigned char *font = &RELOC(vga_font)[((unsigned long)c) * 16]; - switch(bi->dispDeviceDepth) - { + switch(bi->dispDeviceDepth) { case 32: draw_byte_32(font, (unsigned long *)base); break; diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c index e2c6b13a0..b3a25fd2b 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -392,14 +392,8 @@ 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 + if (child->tss.regs->msr & MSR_FP) + giveup_fpu(child); tmp = ((long *)child->tss.fpr)[addr - PT_FPR0]; } else @@ -433,13 +427,8 @@ 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 (last_task_used_math == child) - giveup_fpu(); -#else - if (child->tss.regs->msr & MSR_FP ) - smp_giveup_fpu(child); -#endif + if (child->tss.regs->msr & MSR_FP) + giveup_fpu(child); ((long *)child->tss.fpr)[addr - PT_FPR0] = data; ret = 0; goto out; diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 50cd70889..2d38f3adc 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.122 1998/12/31 20:51:19 cort Exp $ + * $Id: setup.c,v 1.132 1999/03/24 00:32:19 cort Exp $ * Common prep/pmac/chrp boot and setup code. */ @@ -27,40 +27,74 @@ #include <asm/smp.h> #ifdef CONFIG_MBX #include <asm/mbx.h> +#include <asm/8xx_immap.h> #endif #include <asm/bootx.h> +#include <asm/machdep.h> +#include <asm/ide.h> -/* APUS defs */ -extern unsigned long m68k_machtype; -extern int parse_bootinfo(const struct bi_record *); -extern char _end[]; -#ifdef CONFIG_APUS -extern struct mem_info ramdisk; -unsigned long isa_io_base; -unsigned long isa_mem_base; -unsigned long pci_dram_offset; -#endif -/* END APUS defs */ +extern void pmac_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void chrp_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void prep_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void mbx_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void apus_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); +extern boot_infos_t *boot_infos; extern char cmd_line[512]; char saved_command_line[256]; unsigned char aux_device_present; -#if !defined(CONFIG_MACH_SPECIFIC) +struct ide_machdep_calls ppc_ide_md; + 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 */ + +/* Temporary hacks until machdep.h is fully done. */ +int _machine = 0; +/* do we have OF? */ +int have_of = 0; +int is_prep = 0; +int is_chrp = 0; +/* For MTX/MVME boards.. with Raven/Falcon Chipset + Real close to CHRP, but boot like PReP (via PPCbug) + There's probably a nicer way to do this.. --Troy */ +int is_powerplus = 0; + +struct machdep_calls ppc_md; + /* copy of the residual data */ +#ifndef CONFIG_MBX unsigned char __res[sizeof(RESIDUAL)] __prepdata = {0,}; -RESIDUAL *res = (RESIDUAL *)&__res; - -int _prep_type; +#else +unsigned char __res[sizeof(bd_t)] = {0,}; +#endif -extern boot_infos_t *boot_infos; +RESIDUAL *res = (RESIDUAL *)&__res; /* * Perhaps we can put the pmac screen_info[] here @@ -110,160 +144,28 @@ struct screen_info screen_info = { }; #endif /* CONFIG_MBX */ -/* cmd is ignored for now... */ void machine_restart(char *cmd) { -#ifndef CONFIG_MBX - struct adb_request req; - unsigned long flags; - unsigned long i = 10000; -#if 0 - int err; -#endif - - switch(_machine) - { - case _MACH_Pmac: - switch (adb_hardware) { - case ADB_VIACUDA: - cuda_request(&req, NULL, 2, CUDA_PACKET, - CUDA_RESET_SYSTEM); - for (;;) - cuda_poll(); - break; - case ADB_VIAPMU: - pmu_restart(); - 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); - printk("RTAS system-reboot returned %d\n", err); - 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(); - - /* set exception prefix high - to the prom */ - save_flags( flags ); - restore_flags( flags|MSR_IP ); - - /* make sure bit 0 (reset) is a 0 */ - outb( inb(0x92) & ~1L , 0x92 ); - /* signal a reset to system control port A - soft reset */ - outb( inb(0x92) | 1 , 0x92 ); - - while ( i != 0 ) i++; - panic("restart failed\n"); - break; - case _MACH_apus: - cli(); - - APUS_WRITE(APUS_REG_LOCK, - REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK2); - APUS_WRITE(APUS_REG_LOCK, - REGLOCK_BLACKMAGICK1|REGLOCK_BLACKMAGICK3); - APUS_WRITE(APUS_REG_LOCK, - REGLOCK_BLACKMAGICK2|REGLOCK_BLACKMAGICK3); - APUS_WRITE(APUS_REG_SHADOW, REGSHADOW_SELFRESET); - APUS_WRITE(APUS_REG_RESET, REGRESET_AMIGARESET); - for(;;); - break; - } -#else /* CONFIG_MBX */ - extern void MBX_gorom(void); - MBX_gorom(); -#endif /* CONFIG_MBX */ + ppc_md.restart(cmd); } - + void machine_power_off(void) { -#ifndef CONFIG_MBX - struct adb_request req; -#if 0 - int err; -#endif - - switch (_machine) { - case _MACH_Pmac: - switch (adb_hardware) { - case ADB_VIACUDA: - cuda_request(&req, NULL, 2, CUDA_PACKET, - CUDA_POWERDOWN); - for (;;) - cuda_poll(); - break; - case ADB_VIAPMU: - pmu_shutdown(); - 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("power-off", 2, 1, NULL, 0, 0); - printk("RTAS system-reboot returned %d\n", err); - for (;;); -#endif - - case _MACH_prep: - machine_restart(NULL); - case _MACH_apus: - for (;;); - } - for (;;); -#else /* CONFIG_MBX */ - machine_restart(NULL); -#endif /* CONFIG_MBX */ + ppc_md.power_off(); } - + void machine_halt(void) { - if ( _machine == _MACH_Pmac ) - { - machine_power_off(); - } - else /* prep, chrp or apus */ - machine_restart(NULL); - + ppc_md.halt(); } - + #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) { -#if !defined(CONFIG_MBX) && !defined(CONFIG_APUS) - switch (_machine) { -#if defined(CONFIG_BLK_DEV_IDE_PMAC) - case _MACH_Pmac: - pmac_ide_init_hwif_ports(p,base,irq); - break; -#endif - case _MACH_chrp: - chrp_ide_init_hwif_ports(p,base,irq); - break; - case _MACH_prep: - prep_ide_init_hwif_ports(p,base,irq); - break; + if (ppc_ide_md.ide_init_hwif != NULL) { + ppc_ide_md.ide_init_hwif(p, base, irq); } -#endif -#if defined(CONFIG_MBX) - mbx_ide_init_hwif_ports(p,base,irq); -#endif } -EXPORT_SYMBOL(ide_init_hwif_ports); #endif unsigned long cpu_temp(void) @@ -297,10 +199,6 @@ unsigned long cpu_temp(void) 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; @@ -364,7 +262,6 @@ int get_cpuinfo(char *buffer) break; } -#ifndef CONFIG_MBX /* * Assume here that all clock rates are the same in a * smp system. -- Cort @@ -381,33 +278,11 @@ int get_cpuinfo(char *buffer) len += sprintf(len+buffer, "clock\t\t: %dMHz\n", *fp / 1000000); } - - /* PREP's without residual data for some reason will give - incorrect values here */ - if ( is_prep ) - { - len += sprintf(len+buffer, "clock\t\t: "); - if ( res->ResidualLength ) - len += sprintf(len+buffer, "%ldMHz\n", - (res->VitalProductData.ProcessorHz > 1024) ? - res->VitalProductData.ProcessorHz>>20 : - res->VitalProductData.ProcessorHz); - else - len += sprintf(len+buffer, "???\n"); - } -#else /* CONFIG_MBX */ + + if (ppc_md.setup_residual != NULL) { - bd_t *bp; - extern RESIDUAL *res; - - bp = (bd_t *)res; - - len += sprintf(len+buffer,"clock\t\t: %dMHz\n" - "bus clock\t: %dMHz\n", - bp->bi_intfreq /*/ 1000000*/, - bp->bi_busfreq /*/ 1000000*/); + len += ppc_md.setup_residual(buffer + len); } -#endif /* CONFIG_MBX */ len += sprintf(len+buffer, "revision\t: %ld.%ld\n", (GET_PVR & 0xff00) >> 8, GET_PVR & 0xff); @@ -422,8 +297,8 @@ int get_cpuinfo(char *buffer) if ( i ) len += sprintf(buffer+len, "\n"); len += sprintf(buffer+len,"total bogomips\t: %lu.%02lu\n", - (bogosum+2500)/500000, - (bogosum+2500)/5000 % 100); + (bogosum+2500)/500000, + (bogosum+2500)/5000 % 100); #endif /* __SMP__ */ /* @@ -432,34 +307,21 @@ int get_cpuinfo(char *buffer) { len += sprintf(buffer+len,"zero pages\t: total %lu (%luKb) " "current: %lu (%luKb) hits: %lu/%lu (%lu%%)\n", - quicklists.zerototal, - (quicklists.zerototal*PAGE_SIZE)>>10, - quicklists.zero_sz, - (quicklists.zero_sz*PAGE_SIZE)>>10, - quicklists.zeropage_hits,quicklists.zeropage_calls, + zero_cache_total, + (zero_cache_total*PAGE_SIZE)>>10, + zero_cache_sz, + (zero_cache_sz*PAGE_SIZE)>>10, + zero_cache_hits,zero_cache_calls, /* : 1 below is so we don't div by zero */ - (quicklists.zeropage_hits*100) / - ((quicklists.zeropage_calls)?quicklists.zeropage_calls:1)); + (zero_cache_hits*100) / + ((zero_cache_calls)?zero_cache_calls:1)); } -#ifndef CONFIG_MBX - switch (_machine) + if (ppc_md.get_cpuinfo != NULL) { - case _MACH_Pmac: - len += pmac_get_cpuinfo(buffer+len); - break; - case _MACH_prep: - len += prep_get_cpuinfo(buffer+len); - break; - case _MACH_chrp: - len += chrp_get_cpuinfo(buffer+len); - break; - case _MACH_apus: - /* Not much point in printing m68k info when it is not - used. */ - break; + len += ppc_md.get_cpuinfo(buffer+len); } -#endif /* ndef CONFIG_MBX */ + return len; } @@ -471,25 +333,22 @@ unsigned long __init identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - extern void setup_pci_ptrs(void); #ifdef __SMP__ if ( first_cpu_booted ) return 0; #endif /* __SMP__ */ -#ifndef CONFIG_MBX #ifndef CONFIG_MACH_SPECIFIC /* boot loader will tell us if we're APUS */ if ( r3 == 0x61707573 ) { _machine = _MACH_apus; - have_of = 0; r3 = 0; } /* prep boot loader tells us if we're prep or not */ else if ( *(unsigned long *)(KERNELBASE) == (0xdeadc0de) ) { _machine = _MACH_prep; - have_of = 0; + is_prep = 1; } else { char *model; @@ -500,19 +359,49 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, /* ask the OF info if we're a chrp or pmac */ model = get_property(find_path_device("/"), "device_type", NULL); if ( model && !strncmp("chrp",model,4) ) + { _machine = _MACH_chrp; + is_chrp = 1; + } else { model = get_property(find_path_device("/"), "model", NULL); if ( model && !strncmp(model, "IBM", 3)) + { _machine = _MACH_chrp; + is_chrp = 1; + } else + { _machine = _MACH_Pmac; + is_prep = 1; + } } } -#endif /* CONFIG_MACH_SPECIFIC */ +#else /* CONFIG_MACH_SPECIFIC */ + +#ifdef CONFIG_PREP + _machine = _MACH_prep; + is_prep = 1; +#elif defined(CONFIG_CHRP) + _machine = _MACH_chrp; + is_chrp = 1; + have_of = 1; +#elif defined(CONFIG_PMAC) + _machine = _MACH_Pmac; + have_of = 1; +#elif defined(CONFIG_MBX) + _machine = _MACH_mbx; +#elif defined(CONFIG_FADS) + _machine = _MACH_fads; +#elif defined(CONFIG_APUS) + _machine = _MACH_apus; +#else +#error "Machine not defined correctly" +#endif /* CONFIG_APUS */ +#endif /* CONFIG_MACH_SPECIFIC */ if ( have_of ) { @@ -571,131 +460,31 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, cmd_line[sizeof(cmd_line) - 1] = 0; } - switch (_machine) { case _MACH_Pmac: - setup_pci_ptrs(); - /* isa_io_base gets set in pmac_find_bridges */ - isa_mem_base = PMAC_ISA_MEM_BASE; - pci_dram_offset = PMAC_PCI_DRAM_OFFSET; -#if !defined(CONFIG_MACH_SPECIFIC) - ISA_DMA_THRESHOLD = ~0L; - DMA_MODE_READ = 1; - DMA_MODE_WRITE = 2; -#endif /* ! CONFIG_MACH_SPECIFIC */ + pmac_init(r3, r4, r5, r6, r7); break; case _MACH_prep: - /* make a copy of residual data */ - if ( r3 ) - memcpy((void *)res,(void *)(r3+KERNELBASE), - sizeof(RESIDUAL)); - setup_pci_ptrs(); - isa_io_base = PREP_ISA_IO_BASE; - isa_mem_base = PREP_ISA_MEM_BASE; - pci_dram_offset = PREP_PCI_DRAM_OFFSET; -#if !defined(CONFIG_MACH_SPECIFIC) - ISA_DMA_THRESHOLD = 0x00ffffff; - DMA_MODE_READ = 0x44; - DMA_MODE_WRITE = 0x48; -#endif /* ! CONFIG_MACH_SPECIFIC */ - /* figure out what kind of prep workstation we are */ - if ( res->ResidualLength != 0 ) - { - if ( !strncmp(res->VitalProductData.PrintableModel,"IBM",3) ) - _prep_type = _PREP_IBM; - else - _prep_type = _PREP_Motorola; - } - else /* assume motorola if no residual (netboot?) */ - _prep_type = _PREP_Motorola; -#ifdef CONFIG_BLK_DEV_INITRD - /* take care of initrd if we have one */ - if ( r4 ) - { - initrd_start = r4 + KERNELBASE; - initrd_end = r5 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_INITRD */ - /* take care of cmd line */ - if ( r6 ) - { - *(char *)(r7+KERNELBASE) = 0; - strcpy(cmd_line, (char *)(r6+KERNELBASE)); - } + prep_init(r3, r4, r5, r6, r7); break; case _MACH_chrp: - setup_pci_ptrs(); -#ifdef CONFIG_BLK_DEV_INITRD - /* take care of initrd if we have one */ - if ( r3 ) - { - initrd_start = r3 + KERNELBASE; - initrd_end = r3 + r4 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_INITRD */ - /* isa_io_base set by setup_pci_ptrs() */ - isa_mem_base = CHRP_ISA_MEM_BASE; - pci_dram_offset = CHRP_PCI_DRAM_OFFSET; -#if !defined(CONFIG_MACH_SPECIFIC) - ISA_DMA_THRESHOLD = ~0L; - DMA_MODE_READ = 0x44; - DMA_MODE_WRITE = 0x48; -#endif /* ! CONFIG_MACH_SPECIFIC */ + chrp_init(r3, r4, r5, r6, r7); break; -#ifdef CONFIG_APUS +#ifdef CONFIG_APUS case _MACH_apus: - /* Parse bootinfo. The bootinfo is located right after - the kernel bss */ - parse_bootinfo((const struct bi_record *)&_end); -#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 ( ramdisk.addr ) { - initrd_start = (unsigned long) __va(ramdisk.addr); - initrd_end = (unsigned long) - __va(ramdisk.size + ramdisk.addr); - } - /* Make sure code below is not executed. */ - r4 = 0; - r6 = 0; -#endif /* CONFIG_BLK_DEV_INITRD */ -#if !defined(CONFIG_MACH_SPECIFIC) - ISA_DMA_THRESHOLD = 0x00ffffff; -#endif /* ! CONFIG_MACH_SPECIFIC */ + apus_init(r3, r4, r5, r6, r7); break; #endif +#ifdef CONFIG_MBX + case _MACH_mbx: + mbx_init(r3, r4, r5, r6, r7); + break; +#endif default: printk("Unknown machine type in identify_machine!\n"); } -#else /* CONFIG_MBX */ - - if ( r3 ) - memcpy( (void *)res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); - -#ifdef CONFIG_PCI - setup_pci_ptrs(); -#endif - -#ifdef CONFIG_BLK_DEV_INITRD - /* take care of initrd if we have one */ - if ( r4 ) - { - initrd_start = r4 + KERNELBASE; - initrd_end = r5 + KERNELBASE; - } -#endif /* CONFIG_BLK_DEV_INITRD */ - /* take care of cmd line */ - if ( r6 ) - { - - *(char *)(r7+KERNELBASE) = 0; - strcpy(cmd_line, (char *)(r6+KERNELBASE)); - } -#endif /* CONFIG_MBX */ - /* Check for nobats option (used in mapin_ram). */ if (strstr(cmd_line, "nobats")) { extern int __map_without_bats; @@ -717,14 +506,17 @@ void ppc_setup_l2cr(char *str, int *ints) } } +__initfunc(void + ppc_init(void)) +{ + if (ppc_md.init != NULL) { + ppc_md.init(); + } +} + __initfunc(void setup_arch(char **cmdline_p, - unsigned long * memory_start_p, unsigned long * memory_end_p)) + unsigned long * memory_start_p, unsigned long * memory_end_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 mbx_setup_arch(unsigned long *, unsigned long *); - extern void apus_setup_arch(unsigned long *, unsigned long *); extern int panic_timeout; extern char _etext[], _edata[]; extern char *klimit; @@ -737,7 +529,7 @@ __initfunc(void setup_arch(char **cmdline_p, if (strstr(cmd_line, "xmon")) xmon(0); #endif /* CONFIG_XMON */ - + /* reboot on panic */ panic_timeout = 180; @@ -753,27 +545,113 @@ __initfunc(void setup_arch(char **cmdline_p, *memory_start_p = find_available_memory(); *memory_end_p = (unsigned long) end_of_DRAM; -#ifdef CONFIG_MBX - mbx_setup_arch(memory_start_p,memory_end_p); -#else /* CONFIG_MBX */ - switch (_machine) { - case _MACH_Pmac: - pmac_setup_arch(memory_start_p, memory_end_p); - break; - case _MACH_prep: - prep_setup_arch(memory_start_p, memory_end_p); - break; - 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(memory_start_p,memory_end_p); - break; -#endif - default: - printk("Unknown machine %d in setup_arch()\n", _machine); - } -#endif /* CONFIG_MBX */ + ppc_md.setup_arch(memory_start_p, memory_end_p); +} + +void ppc_generic_ide_fix_driveid(struct hd_driveid *id) +{ + int i; + unsigned short *stringcast; + + + id->config = __le16_to_cpu(id->config); + id->cyls = __le16_to_cpu(id->cyls); + id->reserved2 = __le16_to_cpu(id->reserved2); + id->heads = __le16_to_cpu(id->heads); + id->track_bytes = __le16_to_cpu(id->track_bytes); + id->sector_bytes = __le16_to_cpu(id->sector_bytes); + id->sectors = __le16_to_cpu(id->sectors); + id->vendor0 = __le16_to_cpu(id->vendor0); + id->vendor1 = __le16_to_cpu(id->vendor1); + id->vendor2 = __le16_to_cpu(id->vendor2); + stringcast = (unsigned short *)&id->serial_no[0]; + for (i=0; i<(20/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->buf_type = __le16_to_cpu(id->buf_type); + id->buf_size = __le16_to_cpu(id->buf_size); + id->ecc_bytes = __le16_to_cpu(id->ecc_bytes); + stringcast = (unsigned short *)&id->fw_rev[0]; + for (i=0; i<(8/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + stringcast = (unsigned short *)&id->model[0]; + for (i=0; i<(40/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->dword_io = __le16_to_cpu(id->dword_io); + id->reserved50 = __le16_to_cpu(id->reserved50); + id->field_valid = __le16_to_cpu(id->field_valid); + id->cur_cyls = __le16_to_cpu(id->cur_cyls); + id->cur_heads = __le16_to_cpu(id->cur_heads); + id->cur_sectors = __le16_to_cpu(id->cur_sectors); + id->cur_capacity0 = __le16_to_cpu(id->cur_capacity0); + id->cur_capacity1 = __le16_to_cpu(id->cur_capacity1); + id->lba_capacity = __le32_to_cpu(id->lba_capacity); + id->dma_1word = __le16_to_cpu(id->dma_1word); + id->dma_mword = __le16_to_cpu(id->dma_mword); + id->eide_pio_modes = __le16_to_cpu(id->eide_pio_modes); + id->eide_dma_min = __le16_to_cpu(id->eide_dma_min); + id->eide_dma_time = __le16_to_cpu(id->eide_dma_time); + id->eide_pio = __le16_to_cpu(id->eide_pio); + id->eide_pio_iordy = __le16_to_cpu(id->eide_pio_iordy); + id->word69 = __le16_to_cpu(id->word69); + id->word70 = __le16_to_cpu(id->word70); + id->word71 = __le16_to_cpu(id->word71); + id->word72 = __le16_to_cpu(id->word72); + id->word73 = __le16_to_cpu(id->word73); + id->word74 = __le16_to_cpu(id->word74); + id->word75 = __le16_to_cpu(id->word75); + id->word76 = __le16_to_cpu(id->word76); + id->word77 = __le16_to_cpu(id->word77); + id->word78 = __le16_to_cpu(id->word78); + id->word79 = __le16_to_cpu(id->word79); + id->word80 = __le16_to_cpu(id->word80); + id->word81 = __le16_to_cpu(id->word81); + id->command_sets = __le16_to_cpu(id->command_sets); + id->word83 = __le16_to_cpu(id->word83); + id->word84 = __le16_to_cpu(id->word84); + id->word85 = __le16_to_cpu(id->word85); + id->word86 = __le16_to_cpu(id->word86); + id->word87 = __le16_to_cpu(id->word87); + id->dma_ultra = __le16_to_cpu(id->dma_ultra); + id->word89 = __le16_to_cpu(id->word89); + id->word90 = __le16_to_cpu(id->word90); + id->word91 = __le16_to_cpu(id->word91); + id->word92 = __le16_to_cpu(id->word92); + id->word93 = __le16_to_cpu(id->word93); + id->word94 = __le16_to_cpu(id->word94); + id->word95 = __le16_to_cpu(id->word95); + id->word96 = __le16_to_cpu(id->word96); + id->word97 = __le16_to_cpu(id->word97); + id->word98 = __le16_to_cpu(id->word98); + id->word99 = __le16_to_cpu(id->word99); + id->word100 = __le16_to_cpu(id->word100); + id->word101 = __le16_to_cpu(id->word101); + id->word102 = __le16_to_cpu(id->word102); + id->word103 = __le16_to_cpu(id->word103); + id->word104 = __le16_to_cpu(id->word104); + id->word105 = __le16_to_cpu(id->word105); + id->word106 = __le16_to_cpu(id->word106); + id->word107 = __le16_to_cpu(id->word107); + id->word108 = __le16_to_cpu(id->word108); + id->word109 = __le16_to_cpu(id->word109); + id->word110 = __le16_to_cpu(id->word110); + id->word111 = __le16_to_cpu(id->word111); + id->word112 = __le16_to_cpu(id->word112); + id->word113 = __le16_to_cpu(id->word113); + id->word114 = __le16_to_cpu(id->word114); + id->word115 = __le16_to_cpu(id->word115); + id->word116 = __le16_to_cpu(id->word116); + id->word117 = __le16_to_cpu(id->word117); + id->word118 = __le16_to_cpu(id->word118); + id->word119 = __le16_to_cpu(id->word119); + id->word120 = __le16_to_cpu(id->word120); + id->word121 = __le16_to_cpu(id->word121); + id->word122 = __le16_to_cpu(id->word122); + id->word123 = __le16_to_cpu(id->word123); + id->word124 = __le16_to_cpu(id->word124); + id->word125 = __le16_to_cpu(id->word125); + id->word126 = __le16_to_cpu(id->word126); + id->word127 = __le16_to_cpu(id->word127); + id->security = __le16_to_cpu(id->security); + for (i=0; i<127; i++) + id->reserved[i] = __le16_to_cpu(id->reserved[i]); } diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c index 3ffb7981e..17c0f55d1 100644 --- a/arch/ppc/kernel/signal.c +++ b/arch/ppc/kernel/signal.c @@ -1,7 +1,7 @@ /* * linux/arch/ppc/kernel/signal.c * - * $Id: signal.c,v 1.21 1998/10/22 19:37:49 paulus Exp $ + * $Id: signal.c,v 1.24 1999/04/03 11:25:16 paulus Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -218,13 +218,8 @@ 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 (regs->msr & MSR_FP ) + giveup_fpu(current); if (copy_from_user(saved_regs, &sr->gp_regs, sizeof(sr->gp_regs))) goto badframe; @@ -271,13 +266,8 @@ setup_frame(struct pt_regs *regs, struct sigregs *frame, if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) goto badframe; -#ifdef __SMP__ - if ( regs->msr & MSR_FP ) - smp_giveup_fpu(current); -#else - if (last_task_used_math == current) - giveup_fpu(); -#endif + if (regs->msr & MSR_FP) + giveup_fpu(current); if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) || __copy_to_user(&frame->fp_regs, current->tss.fpr, ELF_NFPREG * sizeof(double)) @@ -374,7 +364,7 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) if (!oldset) oldset = ¤t->blocked; - newsp = frame = regs->gpr[1] - sizeof(struct sigregs); + newsp = frame = 0; for (;;) { unsigned long signr; @@ -471,6 +461,13 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs) } } + if ( (ka->sa.sa_flags & SA_ONSTACK) + && (! on_sig_stack(regs->gpr[1]))) + newsp = (current->sas_ss_sp + current->sas_ss_size); + else + newsp = regs->gpr[1]; + newsp = frame = newsp - sizeof(struct sigregs); + /* Whee! Actually deliver the signal. */ handle_signal(signr, ka, &info, oldset, regs, &newsp, frame); } diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index ba505e133..c38fc3108 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -1,10 +1,13 @@ /* - * $Id: smp.c,v 1.39 1998/12/28 10:28:51 paulus Exp $ + * $Id: smp.c,v 1.49 1999/03/18 04:16:31 cort Exp $ * * Smp support for ppc. * * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great * deal of code from the sparc and intel versions. + * + * Support for PReP (Motorola MTX/MVME) SMP by Troy Benjegerdes + * (troy@microux.com, hozer@drgw.net) */ #include <linux/kernel.h> @@ -18,6 +21,7 @@ #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> #include <linux/init.h> +#include <linux/openpic.h> #include <asm/ptrace.h> #include <asm/atomic.h> @@ -29,9 +33,10 @@ #include <asm/softirq.h> #include <asm/init.h> #include <asm/io.h> +#include <asm/prom.h> #include "time.h" - +int first_cpu_booted = 0; int smp_threads_ready = 0; volatile int smp_commenced = 0; int smp_num_cpus = 1; @@ -42,7 +47,6 @@ volatile unsigned long ipi_count; spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; unsigned int prof_multiplier[NR_CPUS]; unsigned int prof_counter[NR_CPUS]; -int first_cpu_booted = 0; cycles_t cacheflush_time; /* all cpu mappings are 1-1 -- Cort */ @@ -51,6 +55,10 @@ volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; int start_secondary(void *); extern int cpu_idle(void *unused); +u_int openpic_read(volatile u_int *addr); + +/* register for interrupting the secondary processor on the powersurge */ +#define PSURGE_INTR ((volatile unsigned *)0xf80000c0) void smp_local_timer_interrupt(struct pt_regs * regs) { @@ -99,29 +107,33 @@ void smp_local_timer_interrupt(struct pt_regs * regs) /* * 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 at the same time - * we have race conditions. I avoided doing locks here since - * all that works right now is the stop cpu message. + * we have race conditions. The PowerSurge doesn't easily + * allow us to send IPI messages so we put the messages in + * smp_message[]. * + * This is because don't have several IPI's on the PowerSurge even though + * we do on the chrp. It would be nice to use the actual IPI's on the chrp + * rather than this but having two methods of doing IPI isn't a good idea + * right now. * -- Cort */ int smp_message[NR_CPUS]; void smp_message_recv(void) { int msg = smp_message[smp_processor_id()]; - - /* clear interrupt */ - *(volatile unsigned long *)(0xf80000c0) = ~0L; - eieio(); + + if ( _machine == _MACH_Pmac ) + { + /* clear interrupt */ + out_be32(PSURGE_INTR, ~0); + } /* make sure msg is for us */ if ( msg == -1 ) return; ipi_count++; - /*printk("SMP %d: smp_message_recv() msg %x\n", smp_processor_id(),msg);*/ switch( msg ) { @@ -158,12 +170,17 @@ void smp_send_stop(void) spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED; void smp_message_pass(int target, int msg, unsigned long data, int wait) { - if ( _machine != _MACH_Pmac ) + int i; + if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) ) return; -printk("SMP %d: sending smp message %x\n", current->processor, msg); -if (smp_processor_id() ) printk("pass from cpu 1\n"); + spin_lock(&mesg_pass_lock); -#define OTHER (~smp_processor_id() & 1) + + /* + * We assume here that the msg is not -1. If it is, + * the recipient won't know the message was destined + * for it. -- Cort + */ switch( target ) { @@ -171,105 +188,180 @@ if (smp_processor_id() ) printk("pass from cpu 1\n"); smp_message[smp_processor_id()] = msg; /* fall through */ case MSG_ALL_BUT_SELF: - smp_message[OTHER] = msg; + for ( i = 0 ; i < smp_num_cpus ; i++ ) + if ( i != smp_processor_id () ) + smp_message[i] = msg; break; default: smp_message[target] = msg; break; } - /* interrupt secondary processor */ - *(volatile unsigned long *)(0xf80000c0) = ~0L; - eieio(); - *(volatile unsigned long *)(0xf80000c0) = 0L; - eieio(); - /* interrupt primary */ - /**(volatile unsigned long *)(0xf3019000);*/ - spin_unlock(&mesg_pass_lock); + + if ( _machine == _MACH_Pmac ) + { + /* interrupt secondary processor */ + out_be32(PSURGE_INTR, ~0); + out_be32(PSURGE_INTR, 0); + /* + * Assume for now that the secondary doesn't send + * IPI's -- Cort + */ + /* interrupt primary */ + /**(volatile unsigned long *)(0xf3019000);*/ + } + + if ( _machine == _MACH_chrp ) + { + /* + * There has to be some way of doing this better - + * perhaps a sent-to-all or send-to-all-but-self + * in the openpic. This gets us going for now, though. + * -- Cort + */ + switch ( target ) + { + case MSG_ALL: + for ( i = 0 ; i < smp_num_cpus ; i++ ) + openpic_cause_IPI(i, 0, 0xffffffff ); + break; + case MSG_ALL_BUT_SELF: + for ( i = 0 ; i < smp_num_cpus ; i++ ) + if ( i != smp_processor_id () ) + openpic_cause_IPI(i, 0, + 0xffffffff & ~(1 << smp_processor_id())); + break; + default: + openpic_cause_IPI(target, 0, 1U << target); + break; + } + } + + spin_unlock(&mesg_pass_lock); } void __init smp_boot_cpus(void) { extern struct task_struct *current_set[NR_CPUS]; - extern void __secondary_start(void); + extern void __secondary_start_psurge(void); int i; struct task_struct *p; unsigned long a; printk("Entering SMP Mode...\n"); - + /* let other processors know to not do certain initialization */ first_cpu_booted = 1; - /*dcbf(&first_cpu_booted);*/ + + /* + * assume for now that the first cpu booted is + * cpu 0, the master -- Cort + */ + cpu_callin_map[0] = 1; + cpu_callin_map[1] = 0; + smp_store_cpu_info(0); + active_kernel_processor = 0; + current->processor = 0; for (i = 0; i < NR_CPUS; i++) { prof_counter[i] = 1; prof_multiplier[i] = 1; } - cpu_callin_map[0] = 1; - smp_store_cpu_info(0); - active_kernel_processor = 0; - current->processor = 0; - /* * XXX very rough, assumes 20 bus cycles to read a cache line, * timebase increments every 4 bus cycles, 32kB L1 data cache. */ cacheflush_time = 5 * 1024; - if ( _machine != _MACH_Pmac ) + if ( !(_machine & (_MACH_Pmac|_MACH_chrp)) ) { printk("SMP not supported on this machine.\n"); return; } - /* create a process for second processor */ - kernel_thread(start_secondary, NULL, CLONE_PID); - p = task[1]; - if ( !p ) - panic("No idle task for secondary processor\n"); - p->processor = 1; - p->has_cpu = 1; - current_set[1] = p; - - /* need to flush here since secondary bat's aren't setup */ - /* XXX ??? */ - for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32) - asm volatile("dcbf 0,%0" : : "r" (a) : "memory"); - asm volatile("sync"); - - /*dcbf((void *)¤t_set[1]);*/ - /* setup entry point of secondary processor */ - *(volatile unsigned long *)(0xf2800000) = - (unsigned long)__secondary_start-KERNELBASE; - eieio(); - /* interrupt secondary to begin executing code */ - *(volatile unsigned long *)(0xf80000c0) = ~0L; - eieio(); - *(volatile unsigned long *)(0xf80000c0) = 0L; - eieio(); + switch ( _machine ) + { + case _MACH_Pmac: + /* assume powersurge board - 2 processors -- Cort */ + smp_num_cpus = 2; + break; + case _MACH_chrp: + smp_num_cpus = ((openpic_read(&OpenPIC->Global.Feature_Reporting0) + & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT)+1; + /* get our processor # - we may not be cpu 0 */ + printk("SMP %d processors, boot CPU is %d (should be 0)\n", + smp_num_cpus, + 10/*openpic_read(&OpenPIC->Processor[0]._Who_Am_I)*/); + break; + } + /* - * wait to see if the secondary made a callin (is actually up). - * udelay() isn't accurate here since we haven't yet called - * calibrate_delay() so use this value that I found through - * experimentation. -- Cort + * only check for cpus we know exist. We keep the callin map + * with cpus at the bottom -- Cort */ - for ( i = 1000; i && !cpu_callin_map[1] ; i-- ) - udelay(100); + for ( i = 1 ; i < smp_num_cpus; i++ ) + { + int c; + + /* create a process for the processor */ + kernel_thread(start_secondary, NULL, CLONE_PID); + p = task[i]; + if ( !p ) + panic("No idle task for secondary processor\n"); + p->processor = i; + p->has_cpu = 1; + current_set[i] = p; + + /* need to flush here since secondary bats aren't setup */ + for (a = KERNELBASE; a < KERNELBASE + 0x800000; a += 32) + asm volatile("dcbf 0,%0" : : "r" (a) : "memory"); + asm volatile("sync"); + + /* wake up cpus */ + switch ( _machine ) + { + case _MACH_Pmac: + /* setup entry point of secondary processor */ + *(volatile unsigned long *)(0xf2800000) = + (unsigned long)__secondary_start_psurge-KERNELBASE; + eieio(); + /* interrupt secondary to begin executing code */ + out_be32(PSURGE_INTR, ~0); + out_be32(PSURGE_INTR, 0); + break; + case _MACH_chrp: + *(unsigned long *)KERNELBASE = i; + asm volatile("dcbf 0,%0"::"r"(KERNELBASE):"memory"); + break; + } + + /* + * wait to see if the cpu made a callin (is actually up). + * use this value that I found through experimentation. + * -- Cort + */ + for ( c = 1000; c && !cpu_callin_map[i] ; c-- ) + udelay(100); + + if ( cpu_callin_map[i] ) + { + printk("Processor %d found.\n", i); + /* this sync's the decr's -- Cort */ + if ( _machine == _MACH_Pmac ) + set_dec(decrementer_count); + } else { + printk("Processor %d is stuck.\n", i); + } + } - if(cpu_callin_map[1]) { - printk("Processor %d found.\n", smp_num_cpus); - smp_num_cpus++; -#if 1 /* this sync's the decr's, but we don't want this now -- Cort */ - set_dec(decrementer_count); -#endif - } else { - printk("Processor %d is stuck. \n", smp_num_cpus); + if ( _machine == _MACH_Pmac ) + { + /* reset the entry point so if we get another intr we won't + * try to startup again */ + *(volatile unsigned long *)(0xf2800000) = 0x100; + /* send interrupt to other processors to start decr's on all cpus */ + smp_message_pass(1,0xf0f0, 0, 0); } - /* reset the entry point so if we get another intr we won't - * try to startup again */ - *(volatile unsigned long *)(0xf2800000) = 0x100; - /* send interrupt to other processors to start decr's on all cpus */ - smp_message_pass(1,0xf0f0, 0, 0); } void __init smp_commence(void) @@ -308,7 +400,6 @@ void __init smp_callin(void) while(!smp_commenced) barrier(); __sti(); - printk("SMP %d: smp_callin done\n", current->processor); } void __init smp_setup(char *str, int *ints) diff --git a/arch/ppc/kernel/softemu8xx.c b/arch/ppc/kernel/softemu8xx.c index ae44266f7..b90760d7e 100644 --- a/arch/ppc/kernel/softemu8xx.c +++ b/arch/ppc/kernel/softemu8xx.c @@ -38,6 +38,7 @@ #define LFDU 51 #define STFD 54 #define STFDU 55 +#define FMR 63 /* * We return 0 on success, 1 on unimplemented instruction, and EFAULT @@ -49,6 +50,7 @@ Soft_emulate_8xx(struct pt_regs *regs) uint inst, instword; uint flreg, idxreg, disp; uint retval; + signed short sdisp; uint *ea, *ip; retval = 0; @@ -66,6 +68,11 @@ Soft_emulate_8xx(struct pt_regs *regs) switch ( inst ) { case LFD: + /* this is a 16 bit quantity that is sign extended + * so use a signed short here -- Cort + */ + sdisp = (instword & 0xffff); + ea = (uint *)(regs->gpr[idxreg] + sdisp); if (copy_from_user(ip, ea, sizeof(double))) retval = EFAULT; break; @@ -77,6 +84,11 @@ Soft_emulate_8xx(struct pt_regs *regs) regs->gpr[idxreg] = (uint)ea; break; case STFD: + /* this is a 16 bit quantity that is sign extended + * so use a signed short here -- Cort + */ + sdisp = (instword & 0xffff); + ea = (uint *)(regs->gpr[idxreg] + sdisp); if (copy_to_user(ea, ip, sizeof(double))) retval = EFAULT; break; @@ -87,6 +99,11 @@ Soft_emulate_8xx(struct pt_regs *regs) else regs->gpr[idxreg] = (uint)ea; break; + case FMR: + /* assume this is a fp move -- Cort */ + memcpy( ip, ¤t->tss.fpr[(instword>>11)&0x1f], + sizeof(double) ); + break; default: retval = 1; printk("Bad emulation %s/%d\n" @@ -98,7 +115,7 @@ Soft_emulate_8xx(struct pt_regs *regs) (instword>>16)&0x1f, (instword>>11)&0x1f, (instword>>6)&0x1f, - (instword>>1)&0x1f, + (instword>>1)&0x3ff, instword&1); { int pa; diff --git a/arch/ppc/kernel/syscalls.c b/arch/ppc/kernel/syscalls.c index b97456226..4d96a6f0a 100644 --- a/arch/ppc/kernel/syscalls.c +++ b/arch/ppc/kernel/syscalls.c @@ -205,12 +205,15 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, lock_kernel(); if (!(flags & MAP_ANONYMOUS)) { - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + file = fget(fd); + if (!file) goto out; } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); ret = do_mmap(file, addr, len, prot, flags, offset); + if (file) + fput(file); out: unlock_kernel(); return ret; diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c index 32f60a236..6f0eb798b 100644 --- a/arch/ppc/kernel/time.c +++ b/arch/ppc/kernel/time.c @@ -1,5 +1,5 @@ /* - * $Id: time.c,v 1.39 1998/12/28 10:28:51 paulus Exp $ + * $Id: time.c,v 1.47 1999/03/18 05:11:11 cort Exp $ * Common time routines among all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -18,8 +18,8 @@ * 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 * - * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 - * "A Kernel Model for Precision Timekeeping" by Dave Mills + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills */ #include <linux/config.h> @@ -41,18 +41,14 @@ #include <asm/processor.h> #include <asm/nvram.h> #include <asm/cache.h> -#ifdef CONFIG_MBX -#include <asm/mbx.h> -#endif +/* Fixme - Why is this here? - Corey */ #ifdef CONFIG_8xx #include <asm/8xx_immap.h> #endif +#include <asm/machdep.h> #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 */ @@ -76,9 +72,6 @@ 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(); hardirq_enter(cpu); #ifdef __SMP__ @@ -119,16 +112,21 @@ void timer_interrupt(struct pt_regs * regs) */ if ( xtime.tv_sec > last_rtc_update + 660 ) { - if (set_rtc_time(xtime.tv_sec) == 0) + if (ppc_md.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 */ + } + else { + /* do it again in 60 s */ + last_rtc_update = xtime.tv_sec - 60; + } } } } #ifdef __SMP__ smp_local_timer_interrupt(regs); #endif + + /* Fixme - make this more generic - Corey */ #ifdef CONFIG_APUS { extern void apus_heartbeat (void); @@ -136,33 +134,8 @@ void timer_interrupt(struct pt_regs * 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) -{ - printk("timebase_interrupt()\n"); -} - -/* 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 *)IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY; - ((immap_t *)IMAP_ADDR)->im_sit.sit_rtc = time; - ((immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY; - return(0); -} -#endif /* CONFIG_MBX */ - /* * This version of gettimeofday has microsecond resolution. */ @@ -198,9 +171,9 @@ void do_settimeofday(struct timeval *tv) xtime.tv_sec = tv->tv_sec; xtime.tv_usec = tv->tv_usec - frac_tick; set_dec(frac_tick * count_period_den / count_period_num); - time_adjust = 0; /* stop active adjtime() */ + time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; - time_state = TIME_ERROR; /* p. 24, (a) */ + time_state = TIME_ERROR; /* p. 24, (a) */ time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; restore_flags(flags); @@ -209,81 +182,23 @@ void do_settimeofday(struct timeval *tv) __initfunc(void time_init(void)) { -#ifndef CONFIG_MBX + if (ppc_md.time_init != NULL) + { + ppc_md.time_init(); + } + if ((_get_PVR() >> 16) == 1) { /* 601 processor: dec counts down by 128 every 128ns */ decrementer_count = DECREMENTER_COUNT_601; count_period_num = COUNT_PERIOD_NUM_601; count_period_den = COUNT_PERIOD_DEN_601; + } else if (!smp_processor_id()) { + ppc_md.calibrate_decr(); } - switch (_machine) { - case _MACH_Pmac: - xtime.tv_sec = pmac_get_rtc_time(); - if ( (_get_PVR() >> 16) != 1 && (!smp_processor_id()) ) - pmac_calibrate_decr(); - if ( !smp_processor_id() ) - set_rtc_time = pmac_set_rtc_time; - break; - case _MACH_chrp: - chrp_time_init(); - xtime.tv_sec = chrp_get_rtc_time(); - if ((_get_PVR() >> 16) != 1) - chrp_calibrate_decr(); - set_rtc_time = chrp_set_rtc_time; - break; - case _MACH_prep: - xtime.tv_sec = prep_get_rtc_time(); - prep_calibrate_decr(); - set_rtc_time = prep_set_rtc_time; - break; -#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; -#else /* CONFIG_MBX */ - 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 *)IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY; - ((immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY; - + xtime.tv_sec = ppc_md.get_rtc_time(); + xtime.tv_usec = 0; - /* Disable the RTC one second and alarm interrupts. - */ - ((immap_t *)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 *)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 *)IMAP_ADDR)->im_sit.sit_rtc; - xtime.tv_usec = 0; - -#endif /* CONFIG_MBX */ set_dec(decrementer_count); /* mark the rtc/on-chip timer as in sync * so we don't update right away @@ -291,109 +206,6 @@ __initfunc(void time_init(void)) last_rtc_update = xtime.tv_sec; } -#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 - * but on prep we have to figure it out. - * -- Cort - */ -int calibrate_done = 0; -volatile int *done_ptr = &calibrate_done; -__initfunc(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); - -#define TIMER0_COUNT 0x40 -#define TIMER_CONTROL 0x43 - /* set timer to periodic mode */ - outb_p(0x34,TIMER_CONTROL);/* binary, mode 2, LSB/MSB, ch 0 */ - /* set the clock to ~100 Hz */ - outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */ - outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */ - - if (request_irq(0, prep_calibrate_decr_handler, 0, "timer", NULL) != 0) - panic("Could not allocate timer IRQ!"); - __sti(); - while ( ! *done_ptr ) /* nothing */; /* wait for calibrate */ - restore_flags(flags); - free_irq( 0, NULL); -} - -__initfunc(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) - { - t2 = get_dec(); - t2 = t1-t2; /* decr's in 1/HZ */ - t2 = t2*HZ; /* # decrs in 1s - thus in Hz */ - 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; - *done_ptr = 1; - } -} - -#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. - */ -__initfunc(void mbx_calibrate_decr(void)) -{ - bd_t *binfo = (bd_t *)res; - int freq, fp, divisor; - - if ((((immap_t *)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 = (binfo->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 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. @@ -439,6 +251,49 @@ static int month_days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +/* + * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) + */ +void GregorianDay(struct rtc_time * tm) +{ + int leapsToDate; + int lastYear; + int day; + int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + lastYear=tm->tm_year-1; + + /* + * Number of leap corrections to apply up to end of last year + */ + leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; + + /* + * This year is a leap year if it is divisible by 4 except when it is + * divisible by 100 unless it is divisible by 400 + * + * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be + */ + if((tm->tm_year%4==0) && + ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && + (tm->tm_mon>2)) + { + /* + * We are past Feb. 29 in a leap year + */ + day=1; + } + else + { + day=0; + } + + day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + + tm->tm_mday; + + tm->tm_wday=day%7; +} + void to_tm(int tim, struct rtc_time * tm) { register int i; @@ -467,6 +322,11 @@ void to_tm(int tim, struct rtc_time * tm) /* Days are what is left over (+1) from all that. */ tm->tm_mday = day + 1; + + /* + * Determine the day of week + */ + GregorianDay(tm); } diff --git a/arch/ppc/kernel/time.h b/arch/ppc/kernel/time.h index 64a07250c..a1c912308 100644 --- a/arch/ppc/kernel/time.h +++ b/arch/ppc/kernel/time.h @@ -1,5 +1,5 @@ /* - * $Id: time.h,v 1.10 1998/04/01 07:46:03 geert Exp $ + * $Id: time.h,v 1.11 1999/03/18 04:16:34 cort Exp $ * Common time prototypes and such for all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -9,32 +9,15 @@ #include <linux/mc146818rtc.h> /* time.c */ -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; -extern unsigned long mktime(unsigned int, unsigned int,unsigned int, +extern unsigned long mktime(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); extern void to_tm(int tim, struct rtc_time * tm); extern unsigned long last_rtc_update; -/* pmac/prep/chrp_time.c */ -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 fa5184af2..87cc2bd68 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -191,13 +191,8 @@ 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 + if (regs->msr & MSR_FP) + giveup_fpu(current); fixed = fix_alignment(regs); if (fixed == 1) { regs->nip += 4; /* skip over emulated instruction */ |