diff options
Diffstat (limited to 'arch')
198 files changed, 10828 insertions, 5172 deletions
diff --git a/arch/alpha/Makefile b/arch/alpha/Makefile index bcdf3d5e2..d48c8e09f 100644 --- a/arch/alpha/Makefile +++ b/arch/alpha/Makefile @@ -19,6 +19,10 @@ old_gas := $(shell if $(AS) --version 2>&1 | grep 'version 2.7' > /dev/null; the # Determine if GCC understands the -mcpu= option. have_mcpu := $(shell if $(CC) -mcpu=ev5 -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo y; else echo n; fi) +have_mcpu_pca56 := $(shell if $(CC) -mcpu=pca56 -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo y; else echo n; fi) + +have_mcpu_ev6 := $(shell if $(CC) -mcpu=ev6 -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo y; else echo n; fi) + # Turn on the proper cpu optimizations. ifeq ($(have_mcpu),y) # If GENERIC, make sure to turn off any instruction set extensions that @@ -31,17 +35,34 @@ ifeq ($(have_mcpu),y) ifeq ($(CONFIG_ALPHA_EV4),y) CFLAGS := $(CFLAGS) -mcpu=ev4 endif - # Leave out EV5, since it is too hard to figure out whether we - # should use EV56 insns or not. + ifeq ($(CONFIG_ALPHA_PYXIS),y) + CFLAGS := $(CFLAGS) -mcpu=ev56 + endif + ifeq ($(CONFIG_ALPHA_POLARIS),y) + ifeq ($(have_mcpu_pca56),y) + CFLAGS := $(CFLAGS) -mcpu=pca56 + else + CFLAGS := $(CFLAGS) -mcpu=ev56 + endif + endif ifeq ($(CONFIG_ALPHA_EV6),y) - CFLAGS := $(CFLAGS) -mcpu=ev6 + ifeq ($(have_mcpu_ev6),y) + CFLAGS := $(CFLAGS) -mcpu=ev6 + else + ifeq ($(have_mcpu_pca56),y) + CFLAGS := $(CFLAGS) -mcpu=pca56 + else + CFLAGS := $(CFLAGS) -mcpu=ev56 + endif + endif endif endif # For TSUNAMI, we must have the assembler not emulate our instructions. +# The same is true for POLARIS. # BWX is most important, but we don't really want any emulation ever. ifeq ($(old_gas),y) - ifneq ($(CONFIG_ALPHA_GENERIC)$(CONFIG_ALPHA_TSUNAMI),) + ifneq ($(CONFIG_ALPHA_GENERIC)$(CONFIG_ALPHA_TSUNAMI)$(CONFIG_ALPHA_POLARIS),) # How do we do #error in make? CFLAGS := --error-please-upgrade-your-assembler endif @@ -52,6 +73,12 @@ else ifeq ($(CONFIG_ALPHA_PYXIS),y) CFLAGS := $(CFLAGS) -Wa,-m21164a -DBWIO_ENABLED endif + ifeq ($(CONFIG_ALPHA_POLARIS),y) + CFLAGS := $(CFLAGS) -Wa,-m21164pc + endif + ifeq ($(CONFIG_ALPHA_TSUNAMI),y) + CFLAGS := $(CFLAGS) -Wa,-mev6 + endif endif HEAD := arch/alpha/kernel/head.o diff --git a/arch/alpha/config.in b/arch/alpha/config.in index 48e924fd7..4e64ee826 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -13,7 +13,6 @@ mainmenu_option next_comment comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then - MODULES=y bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS bool 'Kernel module loader' CONFIG_KMOD fi @@ -44,6 +43,7 @@ choice 'Alpha system type' \ Platform2000 CONFIG_ALPHA_P2K \ Rawhide CONFIG_ALPHA_RAWHIDE \ Ruffian CONFIG_ALPHA_RUFFIAN \ + RX164 CONFIG_ALPHA_RX164 \ SX164 CONFIG_ALPHA_SX164 \ Sable CONFIG_ALPHA_SABLE \ Takara CONFIG_ALPHA_TAKARA" Generic @@ -52,7 +52,7 @@ choice 'Alpha system type' \ unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5 CONFIG_ALPHA_EV6 unset CONFIG_PCI CONFIG_ALPHA_EISA unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA -unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS +unset CONFIG_ALPHA_T2 CONFIG_ALPHA_PYXIS CONFIG_ALPHA_POLARIS unset CONFIG_ALPHA_TSUNAMI CONFIG_ALPHA_MCPCIA unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION @@ -131,6 +131,12 @@ then define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_MCPCIA y fi +if [ "$CONFIG_ALPHA_RX164" = "y" ] +then + define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_POLARIS y +fi if [ "$CONFIG_ALPHA_JENSEN" = "y" ] then define_bool CONFIG_ALPHA_EV4 y @@ -167,6 +173,8 @@ then define_bool CONFIG_ALPHA_AVANTI y fi +bool 'Symmetric multi-processing support' CONFIG_SMP + if [ "$CONFIG_PCI" = "y" ]; then bool 'PCI quirks' CONFIG_PCI_QUIRKS if [ "$CONFIG_PCI_QUIRKS" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then @@ -247,8 +255,6 @@ source drivers/char/Config.in source fs/Config.in -source fs/nls/Config.in - if [ "$CONFIG_VT" = "y" ]; then mainmenu_option next_comment comment 'Console drivers' diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig index 96666ced7..cfd366eb5 100644 --- a/arch/alpha/defconfig +++ b/arch/alpha/defconfig @@ -42,6 +42,7 @@ CONFIG_ALPHA_GENERIC=y # CONFIG_ALPHA_SX164 is not set # CONFIG_ALPHA_SABLE is not set # CONFIG_ALPHA_TAKARA is not set +# CONFIG_SMP is not set CONFIG_PCI=y CONFIG_ALPHA_NEED_ROUNDING_EMULATION=y # CONFIG_PCI_QUIRKS is not set diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index 68a130f4e..4032c8c0e 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile @@ -23,11 +23,11 @@ OX_OBJS := alpha_ksyms.o ifdef CONFIG_ALPHA_GENERIC O_OBJS += core_apecs.o core_cia.o core_lca.o core_mcpcia.o core_pyxis.o \ - core_t2.o core_tsunami.o \ + core_t2.o core_tsunami.o core_polaris.o \ sys_alcor.o sys_cabriolet.o sys_dp264.o sys_eb64p.o \ sys_jensen.o sys_miata.o sys_mikasa.o sys_noritake.o \ sys_rawhide.o sys_ruffian.o sys_sable.o sys_sio.o \ - sys_sx164.o sys_takara.o \ + sys_sx164.o sys_takara.o sys_rx164.o \ es1888.o smc37c669.o smc37c93x.o else @@ -53,6 +53,9 @@ endif ifdef CONFIG_ALPHA_TSUNAMI O_OBJS += core_tsunami.o endif +ifdef CONFIG_ALPHA_POLARIS +O_OBJS += core_polaris.o +endif # Board support ifneq ($(CONFIG_ALPHA_ALCOR)$(CONFIG_ALPHA_XLT),) @@ -85,6 +88,9 @@ endif ifdef CONFIG_ALPHA_RUFFIAN O_OBJS += sys_ruffian.o endif +ifdef CONFIG_ALPHA_RX164 +O_OBJS += sys_rx164.o +endif ifdef CONFIG_ALPHA_SABLE O_OBJS += sys_sable.o endif @@ -111,7 +117,7 @@ endif endif # GENERIC -ifdef SMP +ifdef CONFIG_SMP O_OBJS += smp.o endif diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c index 175df318b..65975c168 100644 --- a/arch/alpha/kernel/alpha_ksyms.c +++ b/arch/alpha/kernel/alpha_ksyms.c @@ -28,6 +28,7 @@ #include <asm/irq.h> #include <asm/machvec.h> #include <asm/pgtable.h> +#include <asm/semaphore.h> #define __KERNEL_SYSCALLS__ #include <asm/unistd.h> @@ -52,6 +53,7 @@ EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(screen_info); +EXPORT_SYMBOL(perf_irq); /* platform dependent support */ EXPORT_SYMBOL(_inb); @@ -85,6 +87,7 @@ EXPORT_SYMBOL(strnlen); EXPORT_SYMBOL(strncat); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strchr); EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(memcmp); @@ -138,6 +141,13 @@ EXPORT_SYMBOL_NOVERS(__do_clear_user); EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(__strlen_user); +/* + * The following are specially called from the semaphore assembly stubs. + */ +EXPORT_SYMBOL_NOVERS(__down_failed); +EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); +EXPORT_SYMBOL_NOVERS(__up_wakeup); + /* * SMP-specific symbols. */ diff --git a/arch/alpha/kernel/bios32.h b/arch/alpha/kernel/bios32.h index da08f45c0..8850517ba 100644 --- a/arch/alpha/kernel/bios32.h +++ b/arch/alpha/kernel/bios32.h @@ -32,6 +32,13 @@ #define DEFAULT_IO_BASE 0x8000 /* start at 8th slot */ /* + * We try to make the DEFAULT_MEM_BASE addresses *always* have more than + * a single bit set. This is so that devices like the broken Myrinet card + * will always have a PCI memory address that will never match a IDSEL + * address in PCI Config space, which can cause problems with early rev cards. + */ + +/* * An XL is AVANTI (APECS) family, *but* it has only 27 bits of ISA address * that get passed through the PCI<->ISA bridge chip. Although this causes * us to set the PCI->Mem window bases lower than normal, we still allocate @@ -45,22 +52,20 @@ #define XL_DEFAULT_MEM_BASE (16*MB + 2*MB) /* 16M to 64M-1 is avail */ /* - * We try to make this address *always* have more than 1 bit set. - * this is so that devices like the broken Myrinet card will always have - * a PCI memory address that will never match a IDSEL address in - * PCI Config space, which can cause problems with early rev cards. - * - * However, APECS and LCA have only 34 bits for physical addresses, thus - * limiting PCI bus memory addresses for SPARSE access to be less than 128Mb. + * APECS and LCA have only 34 bits for physical addresses, thus limiting PCI + * bus memory addresses for SPARSE access to be less than 128Mb. */ #define APECS_AND_LCA_DEFAULT_MEM_BASE (64*MB + 2*MB) /* - * We try to make this address *always* have more than 1 bit set. - * this is so that devices like the broken Myrinet card will always have - * a PCI memory address that will never match a IDSEL address in - * PCI Config space, which can cause problems with early rev cards. - * + * Because the MCPCIA core logic supports more bits for physical addresses, + * it should allow an expanded range of SPARSE memory addresses. + * However, we do not use them all, in order to avoid the HAE manipulation + * that would be needed. + */ +#define RAWHIDE_DEFAULT_MEM_BASE (64*MB + 2*MB) + +/* * Because CIA and PYXIS and T2 have more bits for physical addresses, * they support an expanded range of SPARSE memory addresses. */ diff --git a/arch/alpha/kernel/core_polaris.c b/arch/alpha/kernel/core_polaris.c new file mode 100644 index 000000000..b9945402f --- /dev/null +++ b/arch/alpha/kernel/core_polaris.c @@ -0,0 +1,275 @@ +/* + * linux/arch/alpha/kernel/core_polaris.c + * + * POLARIS chip-specific code + * + */ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> + +#include <asm/system.h> +#include <asm/ptrace.h> +#include <asm/pci.h> + +#define __EXTERN_INLINE inline +#include <asm/io.h> +#include <asm/core_polaris.h> +#undef __EXTERN_INLINE + +#include "proto.h" +#include "bios32.h" + +/* + * BIOS32-style PCI interface: + */ + +#ifdef DEBUG_CONFIG +# define DBG_CFG(args) printk args +#else +# define DBG_CFG(args) +#endif + +#define DEBUG_MCHECK +#ifdef DEBUG_MCHECK +# define DBG_MCK(args) printk args +/* #define DEBUG_MCHECK_DUMP */ +#else +# define DBG_MCK(args) +#endif + +static volatile unsigned int POLARIS_mcheck_expected = 0; +static volatile unsigned int POLARIS_mcheck_taken = 0; +static volatile unsigned short POLARIS_jd = 0; + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address. This is fairly straightforward + * on POLARIS, since the chip itself generates Type 0 or Type 1 + * cycles automatically depending on the bus number (Bus 0 is + * hardwired to Type 0, all others are Type 1. Peer bridges + * are not supported). + * + * All types: + * + * 3 3 3 3|3 3 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |1|1|1|1|1|0|0|1|1|1|1|1|1|1|1|0|B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|x|x| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., scsi and ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ + +static int +mk_conf_addr(u8 bus, u8 device_fn, u8 where, unsigned long *pci_addr, u8 *type1) +{ + *type1 = (bus == 0) ? 0 : 1; + *pci_addr = (bus << 16) | (device_fn << 8) | (where) | + POLARIS_DENSE_CONFIG_BASE; + + DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," + " returning address 0x%p\n" + bus, device_fn, where, *pci_addr)); + + return 0; +} + +int +polaris_hose_read_config_byte (u8 bus, u8 device_fn, u8 where, u8 *value, + struct linux_hose_info *hose) +{ + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *value = __kernel_ldbu(*(vucp)pci_addr); + return PCIBIOS_SUCCESSFUL; +} + + +int +polaris_hose_read_config_word (u8 bus, u8 device_fn, u8 where, u16 *value, + struct linux_hose_info *hose) +{ + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *value = __kernel_ldwu(*(vusp)pci_addr); + return PCIBIOS_SUCCESSFUL; +} + + +int +polaris_hose_read_config_dword (u8 bus, u8 device_fn, u8 where, u32 *value, + struct linux_hose_info *hose) +{ + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *value = *(vuip)pci_addr; + return PCIBIOS_SUCCESSFUL; +} + + +int +polaris_hose_write_config_byte (u8 bus, u8 device_fn, u8 where, u8 value, + struct linux_hose_info *hose) +{ + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + __kernel_stb(value, *(vucp)pci_addr); + mb(); + __kernel_ldbu(*(vucp)pci_addr); + return PCIBIOS_SUCCESSFUL; +} + + +int +polaris_hose_write_config_word (u8 bus, u8 device_fn, u8 where, u16 value, + struct linux_hose_info *hose) +{ + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + __kernel_stw(value, *(vusp)pci_addr); + mb(); + __kernel_ldbu(*(vusp)pci_addr); + return PCIBIOS_SUCCESSFUL; +} + + +int +polaris_hose_write_config_dword (u8 bus, u8 device_fn, u8 where, u32 value, + struct linux_hose_info *hose) +{ + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *(vuip)pci_addr = value; + mb(); + *(vuip)pci_addr; + return PCIBIOS_SUCCESSFUL; +} + +void __init +polaris_init_arch(unsigned long *mem_start, unsigned long *mem_end) +{ + /* May need to initialize error reporting (see PCICTL0/1), but + * for now assume that the firmware has done the right thing + * already. + */ +#if 0 + printk("polaris_init_arch(): trusting firmware for setup\n"); +#endif +} + +int polaris_pci_clr_err(void) +{ + POLARIS_jd = *((vusp)POLARIS_W_STATUS); + DBG_MCK(("POLARIS_pci_clr_err: POLARIS_W_STATUS after read 0x%x\n", + POLARIS_jd)); + /* Write 1's to settable bits to clear errors */ + *((vusp)POLARIS_W_STATUS) = 0x7800; mb(); + POLARIS_jd = *((vusp)POLARIS_W_STATUS); + return 0; +} + +void polaris_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + struct el_common *mchk_header; + struct el_POLARIS_sysdata_mcheck *mchk_sysdata; + + mchk_header = (struct el_common *)la_ptr; + + mchk_sysdata = + (struct el_POLARIS_sysdata_mcheck *)(la_ptr+mchk_header->sys_offset); + +#if 0 + DBG_MCK(("polaris_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->size, mchk_header->proc_offset, + mchk_header->sys_offset)); + DBG_MCK(("polaris_machine_check: expected %d status 0x%lx\n", + POLARIS_mcheck_expected, mchk_sysdata->psc_status)); +#endif +#ifdef DEBUG_MCHECK_DUMP + { + unsigned long *ptr; + int i; + + ptr = (unsigned long *)la_ptr; + for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { + printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]); + } + } +#endif /* DEBUG_MCHECK_DUMP */ + /* + * Check if machine check is due to a badaddr() and if so, + * ignore the machine check. + */ + mb(); + mb(); + if (POLARIS_mcheck_expected) { + DBG_MCK(("POLARIS machine check expected\n")); + POLARIS_mcheck_expected = 0; + POLARIS_mcheck_taken = 1; + mb(); + mb(); + draina(); + polaris_pci_clr_err(); + wrmces(0x7); + mb(); + } +#if 1 + else { + printk("POLARIS machine check NOT expected\n") ; + DBG_MCK(("polaris_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->size, mchk_header->proc_offset, + mchk_header->sys_offset)); + POLARIS_mcheck_expected = 0; + POLARIS_mcheck_taken = 1; + mb(); + mb(); + draina(); + polaris_pci_clr_err(); + wrmces(0x7); + mb(); + } +#endif +} diff --git a/arch/alpha/kernel/core_t2.c b/arch/alpha/kernel/core_t2.c index defceb053..c7ec2b6ee 100644 --- a/arch/alpha/kernel/core_t2.c +++ b/arch/alpha/kernel/core_t2.c @@ -143,7 +143,7 @@ static unsigned int conf_read(unsigned long addr, unsigned char type1) { unsigned long flags; - unsigned int stat0, value, cpu; + unsigned int value, cpu; unsigned long t2_cfg = 0; cpu = smp_processor_id(); @@ -153,11 +153,14 @@ conf_read(unsigned long addr, unsigned char type1) DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); #if 0 - /* Reset status register to avoid losing errors. */ - stat0 = *(vulp)T2_IOCSR; - *(vulp)T2_IOCSR = stat0; - mb(); - DBG(("conf_read: T2 IOCSR was 0x%x\n", stat0)); + { + unsigned long stat0; + /* Reset status register to avoid losing errors. */ + stat0 = *(vulp)T2_IOCSR; + *(vulp)T2_IOCSR = stat0; + mb(); + DBG(("conf_read: T2 IOCSR was 0x%x\n", stat0)); + } #endif /* If Type1 access, must set T2 CFG. */ @@ -202,7 +205,7 @@ static void conf_write(unsigned long addr, unsigned int value, unsigned char type1) { unsigned long flags; - unsigned int stat0, cpu; + unsigned int cpu; unsigned long t2_cfg = 0; cpu = smp_processor_id(); @@ -210,11 +213,14 @@ conf_write(unsigned long addr, unsigned int value, unsigned char type1) __save_and_cli(flags); /* avoid getting hit by machine check */ #if 0 - /* Reset status register to avoid losing errors. */ - stat0 = *(vulp)T2_IOCSR; - *(vulp)T2_IOCSR = stat0; - mb(); - DBG(("conf_write: T2 ERR was 0x%x\n", stat0)); + { + unsigned long stat0; + /* Reset status register to avoid losing errors. */ + stat0 = *(vulp)T2_IOCSR; + *(vulp)T2_IOCSR = stat0; + mb(); + DBG(("conf_write: T2 ERR was 0x%x\n", stat0)); + } #endif /* If Type1 access, must set T2 CFG. */ @@ -346,7 +352,6 @@ t2_hose_write_config_dword (u8 bus, u8 device_fn, u8 where, u32 value, void __init t2_init_arch(unsigned long *mem_start, unsigned long *mem_end) { - unsigned long t2_err; unsigned int i; for (i = 0; i < NR_CPUS; i++) { @@ -355,13 +360,15 @@ t2_init_arch(unsigned long *mem_start, unsigned long *mem_end) } #if 0 - /* - * Set up error reporting. - */ - t2_err = *(vulp)T2_IOCSR ; - t2_err |= (0x1 << 7) ; /* master abort */ - *(vulp)T2_IOCSR = t2_err ; - mb() ; + { + /* Set up error reporting. */ + unsigned long t2_err; + + t2_err = *(vulp)T2_IOCSR; + t2_err |= (0x1 << 7); /* master abort */ + *(vulp)T2_IOCSR = t2_err; + mb(); + } #endif printk("t2_init: HBASE was 0x%lx\n", *(vulp)T2_HBASE); diff --git a/arch/alpha/kernel/core_tsunami.c b/arch/alpha/kernel/core_tsunami.c index f2fd19acd..efcce56d5 100644 --- a/arch/alpha/kernel/core_tsunami.c +++ b/arch/alpha/kernel/core_tsunami.c @@ -9,7 +9,6 @@ #include <linux/config.h> #include <linux/kernel.h> -#include <linux/config.h> #include <linux/types.h> #include <linux/pci.h> #include <linux/sched.h> @@ -302,14 +301,22 @@ tsunami_init_one_pchip(tsunami_pchip *pchip, int index, * For now, windows 1,2 and 3 are disabled. In the future, * we may want to use them to do scatter/gather DMA. * - * Window 0 goes at 1 GB and is 1 GB large. + * Window 0 goes at 1 GB and is 1 GB large, mapping to 0. */ pchip->wsba[0].csr = 1L | (TSUNAMI_DMA_WIN_BASE_DEFAULT & 0xfff00000U); pchip->wsm[0].csr = (TSUNAMI_DMA_WIN_SIZE_DEFAULT - 1) & 0xfff00000UL; pchip->tba[0].csr = 0; +#if 0 pchip->wsba[1].csr = 0; +#else + /* make the second window at 2Gb for 1Gb mapping to 1Gb */ + pchip->wsba[1].csr = 1L | ((0x80000000U) & 0xfff00000U); + pchip->wsm[1].csr = (0x40000000UL - 1) & 0xfff00000UL; + pchip->tba[1].csr = 0x40000000; +#endif + pchip->wsba[2].csr = 0; pchip->wsba[3].csr = 0; mb(); @@ -359,7 +366,9 @@ tsunami_init_arch(unsigned long *mem_start, unsigned long *mem_end) /* Find how many hoses we have, and initialize them. */ tsunami_init_one_pchip(TSUNAMI_pchip0, 0, mem_start); - tsunami_init_one_pchip(TSUNAMI_pchip1, 1, mem_start); + /* must change this for TYPHOON which may have 4 */ + if (TSUNAMI_cchip->csc.csr & 1L<<14) + tsunami_init_one_pchip(TSUNAMI_pchip1, 1, mem_start); } static inline void @@ -378,7 +387,9 @@ tsunami_pci_clr_err(void) { int cpu = smp_processor_id(); tsunami_pci_clr_err_1(TSUNAMI_pchip0, cpu); - tsunami_pci_clr_err_1(TSUNAMI_pchip1, cpu); + /* must change this for TYPHOON which may have 4 */ + if (TSUNAMI_cchip->csc.csr & 1L<<14) + tsunami_pci_clr_err_1(TSUNAMI_pchip1, cpu); return 0; } diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S index f349ebd6b..380af2c9c 100644 --- a/arch/alpha/kernel/entry.S +++ b/arch/alpha/kernel/entry.S @@ -8,10 +8,9 @@ #define halt .long PAL_halt #define rti .long PAL_rti -#define SIGCHLD 20 +#define SIGCHLD 20 -#define NR_SYSCALLS 370 -#define osf_vfork sys_fork +#define NR_SYSCALLS 371 /* * These offsets must match with alpha_mv in <asm/machvec.h>. @@ -502,6 +501,17 @@ sys_clone: .end sys_clone .align 3 +.globl sys_vfork +.ent sys_vfork +sys_vfork: + bsr $1,do_switch_stack + bis $30,$30,$16 + jsr $26,alpha_vfork + bsr $1,undo_switch_stack + ret $31,($26),1 +.end sys_vfork + +.align 3 .globl alpha_switch_to .ent alpha_switch_to alpha_switch_to: @@ -689,7 +699,8 @@ ret_from_smpfork: .set at mb /* Make the changed data visible before the freed lock. */ stq $31,scheduler_lock - br ret_from_sys_call + lda $26,ret_from_sys_call + jsr $31,schedule_tail .set noat .end ret_from_smpfork #endif /* __SMP__ */ @@ -821,7 +832,7 @@ sys_call_table: .quad sys_getpgrp .quad sys_getpagesize .quad alpha_ni_syscall /* 65 */ - .quad osf_vfork + .quad sys_vfork .quad sys_newstat .quad sys_newlstat .quad alpha_ni_syscall @@ -1127,4 +1138,4 @@ sys_call_table: .quad sys_getcwd .quad sys_capget .quad sys_capset - .quad sys_ni_syscall /* 370 */ + .quad sys_sendfile /* 370 */ diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index 8f6afc14b..e1a53ab57 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -11,6 +11,7 @@ */ #include <linux/config.h> +#include <linux/kernel.h> #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/kernel_stat.h> @@ -38,13 +39,6 @@ unsigned int local_irq_count[NR_CPUS]; unsigned int local_bh_count[NR_CPUS]; unsigned long hardirq_no[NR_CPUS]; -#define RTC_IRQ 8 -#ifdef CONFIG_RTC -#define TIMER_IRQ 0 /* timer is the pit */ -#else -#define TIMER_IRQ RTC_IRQ /* the timer is, in fact, the rtc */ -#endif - #if NR_IRQS > 64 # error Unable to handle more than 64 irq levels. #endif @@ -84,6 +78,15 @@ generic_ack_irq(unsigned long irq) } } + + +static void dummy_perf(unsigned long vector, struct pt_regs *regs) +{ + printk(KERN_CRIT "Performance counter interrupt!\n"); +} + +void (*perf_irq)(unsigned long, struct pt_regs *) = dummy_perf; + /* * Dispatch device interrupts. */ @@ -102,6 +105,8 @@ generic_ack_irq(unsigned long irq) # define IACK_SC PYXIS_IACK_SC #elif defined(CONFIG_ALPHA_TSUNAMI) # define IACK_SC TSUNAMI_IACK_SC +#elif defined(CONFIG_ALPHA_POLARIS) +# define IACK_SC POLARIS_IACK_SC #else /* This is bogus but necessary to get it to compile on all platforms. */ # define IACK_SC 1L @@ -311,14 +316,14 @@ free_irq(unsigned int irq, void *dev_id) int get_irq_list(char *buf) { - int i, j; + int i; struct irqaction * action; char *p = buf; #ifdef __SMP__ p += sprintf(p, " "); - for (j = 0; j < smp_num_cpus; j++) - p += sprintf(p, "CPU%d ", j); + for (i = 0; i < smp_num_cpus; i++) + p += sprintf(p, "CPU%d ", i); *p++ = '\n'; #endif @@ -330,9 +335,12 @@ int get_irq_list(char *buf) #ifndef __SMP__ p += sprintf(p, "%10u ", kstat_irqs(i)); #else - for (j = 0; j < smp_num_cpus; j++) - p += sprintf(p, "%10u ", - kstat.irqs[cpu_logical_map(j)][i]); + { + int j; + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); + } #endif p += sprintf(p, " %c%s", (action->flags & SA_INTERRUPT)?'+':' ', @@ -815,7 +823,7 @@ probe_irq_on(void) * Wait about 100ms for spurious interrupts to mask themselves * out again... */ - for (delay = jiffies + HZ/10; delay > jiffies; ) + for (delay = jiffies + HZ/10; time_before(jiffies, delay); ) barrier(); /* Now filter out any obviously spurious interrupts. */ @@ -879,8 +887,8 @@ do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr, __restore_flags(flags); return; case 4: - printk("Performance counter interrupt\n"); - break; + perf_irq(vector, ®s); + return; default: printk("Hardware intr %ld %lx? Huh?\n", type, vector); } diff --git a/arch/alpha/kernel/irq.h b/arch/alpha/kernel/irq.h index c46d5df4e..6849e830b 100644 --- a/arch/alpha/kernel/irq.h +++ b/arch/alpha/kernel/irq.h @@ -8,6 +8,8 @@ * with the IRQ handling routines in irq.c. */ +#include <linux/config.h> + #define STANDARD_INIT_IRQ_PROLOG \ outb(0, DMA1_RESET_REG); \ outb(0, DMA2_RESET_REG); \ @@ -21,3 +23,11 @@ extern void isa_device_interrupt(unsigned long vector, struct pt_regs * regs); extern void srm_device_interrupt(unsigned long vector, struct pt_regs * regs); extern void handle_irq(int irq, int ack, struct pt_regs * regs); + +#define RTC_IRQ 8 +#ifdef CONFIG_RTC +#define TIMER_IRQ 0 /* timer is the pit */ +#else +#define TIMER_IRQ RTC_IRQ /* timer is the rtc */ +#endif + diff --git a/arch/alpha/kernel/machvec.h b/arch/alpha/kernel/machvec.h index 70c85c451..8420aaf9c 100644 --- a/arch/alpha/kernel/machvec.h +++ b/arch/alpha/kernel/machvec.h @@ -13,6 +13,11 @@ we can read and write it as we like. ;-) */ #define TSUNAMI_HAE_ADDRESS (&alpha_mv.hae_cache) +/* Whee. POLARIS doesn't have an HAE. Fix things up for the GENERIC + kernel by defining the HAE address to be that of the cache. Now + we can read and write it as we like. ;-) */ +#define POLARIS_HAE_ADDRESS (&alpha_mv.hae_cache) + /* Only a few systems don't define IACK_SC, handling all interrupts through the SRM console. But splitting out that one case from IO() below seems like such a pain. Define this to get things to compile. */ @@ -91,6 +96,7 @@ #define DO_LCA_IO IO(LCA,lca,lca) #define DO_MCPCIA_IO IO(MCPCIA,mcpcia,mcpcia) #define DO_PYXIS_IO IO(PYXIS,pyxis_bw,pyxis) +#define DO_POLARIS_IO IO(POLARIS,polaris,polaris) #define DO_T2_IO IO(T2,t2,t2) #define DO_TSUNAMI_IO IO(TSUNAMI,tsunami,tsunami) @@ -103,6 +109,7 @@ #define DO_LCA_BUS BUS(lca) #define DO_MCPCIA_BUS BUS(mcpcia) #define DO_PYXIS_BUS BUS(pyxis) +#define DO_POLARIS_BUS BUS(polaris) #define DO_T2_BUS BUS(t2) #define DO_TSUNAMI_BUS BUS(tsunami) diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 4c540f696..67c08778d 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -1128,11 +1128,16 @@ asmlinkage int osf_utimes(const char *filename, struct timeval32 *tvs) return ret; } +#define MAX_SELECT_SECONDS \ + ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) + asmlinkage int osf_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval32 *tvp) { - fd_set_buffer *fds; + fd_set_bits fds; + char *bits; + size_t size; unsigned long timeout; int ret; @@ -1145,28 +1150,46 @@ osf_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, || (ret = __get_user(usec, &tvp->tv_usec))) goto out_nofds; - timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); - timeout += sec * HZ; + ret = -EINVAL; + if (sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); + timeout += sec * (unsigned long) HZ; + } } + ret = -EINVAL; + if (n < 0 || n > KFDS_NR) + goto out_nofds; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ ret = -ENOMEM; - fds = (fd_set_buffer *) __get_free_page(GFP_KERNEL); - if (!fds) + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) goto out_nofds; - ret = -EINVAL; - if (n < 0) - goto out; - if (n > KFDS_NR) - n = KFDS_NR; - if ((ret = get_fd_set(n, inp->fds_bits, fds->in)) || - (ret = get_fd_set(n, outp->fds_bits, fds->out)) || - (ret = get_fd_set(n, exp->fds_bits, fds->ex))) + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); + + if ((ret = get_fd_set(n, inp->fds_bits, fds.in)) || + (ret = get_fd_set(n, outp->fds_bits, fds.out)) || + (ret = get_fd_set(n, exp->fds_bits, fds.ex))) goto out; - zero_fd_set(n, fds->res_in); - zero_fd_set(n, fds->res_out); - zero_fd_set(n, fds->res_ex); + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); - ret = do_select(n, fds, &timeout); + ret = do_select(n, &fds, &timeout); /* OSF does not copy back the remaining time. */ @@ -1179,12 +1202,12 @@ osf_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, ret = 0; } - set_fd_set(n, inp->fds_bits, fds->res_in); - set_fd_set(n, outp->fds_bits, fds->res_out); - set_fd_set(n, exp->fds_bits, fds->res_ex); + set_fd_set(n, inp->fds_bits, fds.res_in); + set_fd_set(n, outp->fds_bits, fds.res_out); + set_fd_set(n, exp->fds_bits, fds.res_ex); out: - free_page((unsigned long) fds); + kfree(bits); out_nofds: return ret; } @@ -1304,7 +1327,6 @@ asmlinkage int osf_usleep_thread(struct timeval32 *sleep, struct timeval32 *rema { struct timeval tmp; unsigned long ticks; - unsigned long tmp_timeout; if (get_tv32(&tmp, sleep)) goto fault; diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 562778366..55e211a23 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -257,13 +257,19 @@ void release_thread(struct task_struct *dead_task) * with parameters (SIGCHLD, 0). */ int alpha_clone(unsigned long clone_flags, unsigned long usp, - struct switch_stack * swstack) + struct switch_stack * swstack) { if (!usp) usp = rdusp(); return do_fork(clone_flags, usp, (struct pt_regs *) (swstack+1)); } +int alpha_vfork(struct switch_stack * swstack) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), + (struct pt_regs *) (swstack+1)); +} + extern void ret_from_sys_call(void); extern void ret_from_smpfork(void); /* diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h index 15096703d..bee005933 100644 --- a/arch/alpha/kernel/proto.h +++ b/arch/alpha/kernel/proto.h @@ -76,6 +76,22 @@ extern int mcpcia_hose_write_config_dword (u8, u8, u8, u32 value, extern void mcpcia_init_arch(unsigned long *, unsigned long *); extern void mcpcia_machine_check(u64, u64, struct pt_regs *); +/* core_polaris.c */ +extern int polaris_hose_read_config_byte (u8, u8, u8, u8 *value, + struct linux_hose_info *hose); +extern int polaris_hose_read_config_word (u8, u8, u8, u16 *value, + struct linux_hose_info *hose); +extern int polaris_hose_read_config_dword (u8, u8, u8, u32 *value, + struct linux_hose_info *hose); +extern int polaris_hose_write_config_byte (u8, u8, u8, u8 value, + struct linux_hose_info *hose); +extern int polaris_hose_write_config_word (u8, u8, u8, u16 value, + struct linux_hose_info *hose); +extern int polaris_hose_write_config_dword (u8, u8, u8, u32 value, + struct linux_hose_info *hose); +extern void polaris_init_arch(unsigned long *, unsigned long *); +extern void polaris_machine_check(u64, u64, struct pt_regs *); + /* core_pyxis.c */ extern int pyxis_hose_read_config_byte (u8, u8, u8, u8 *value, struct linux_hose_info *hose); @@ -129,8 +145,6 @@ extern void tsunami_init_arch(unsigned long *, unsigned long *); extern void tsunami_machine_check(u64, u64, struct pt_regs *); /* setup.c */ -extern void init_pit_rest(void); -extern void generic_init_pit (void); extern unsigned long srm_hae; /* smp.c */ @@ -143,6 +157,8 @@ extern void reset_for_srm(void); /* time.c */ extern void timer_interrupt(int irq, void *dev, struct pt_regs * regs); +extern void rtc_init_pit(void); +extern void generic_init_pit(void); extern unsigned long est_cycle_freq; /* smc37c93x.c */ diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c index b6194defa..18c9a8b13 100644 --- a/arch/alpha/kernel/ptrace.c +++ b/arch/alpha/kernel/ptrace.c @@ -541,7 +541,9 @@ sys_ptrace(long request, long pid, long addr, long data, /* When I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: + down(&child->mm->mmap_sem); ret = read_long(child, addr, &tmp); + up(&child->mm->mmap_sem); DBG(DBG_MEM, ("peek %#lx->%#lx\n", addr, tmp)); if (ret < 0) goto out; @@ -560,7 +562,9 @@ sys_ptrace(long request, long pid, long addr, long data, case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: DBG(DBG_MEM, ("poke %#lx<-%#lx\n", addr, data)); + down(&child->mm->mmap_sem); ret = write_long(child, addr, data); + up(&child->mm->mmap_sem); goto out; case PTRACE_POKEUSR: /* write the specified register */ diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index f357830e3..42821d903 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -94,62 +94,46 @@ struct screen_info screen_info = { * Declare all of the machine vectors. */ -extern struct alpha_machine_vector alcor_mv; -extern struct alpha_machine_vector alphabook1_mv; -extern struct alpha_machine_vector avanti_mv; -extern struct alpha_machine_vector cabriolet_mv; -extern struct alpha_machine_vector dp264_mv; -extern struct alpha_machine_vector eb164_mv; -extern struct alpha_machine_vector eb64p_mv; -extern struct alpha_machine_vector eb66_mv; -extern struct alpha_machine_vector eb66p_mv; -extern struct alpha_machine_vector jensen_mv; -extern struct alpha_machine_vector lx164_mv; -extern struct alpha_machine_vector miata_mv; -extern struct alpha_machine_vector mikasa_mv; -extern struct alpha_machine_vector mikasa_primo_mv; -extern struct alpha_machine_vector monet_mv; -extern struct alpha_machine_vector noname_mv; -extern struct alpha_machine_vector noritake_mv; -extern struct alpha_machine_vector noritake_primo_mv; -extern struct alpha_machine_vector p2k_mv; -extern struct alpha_machine_vector pc164_mv; -extern struct alpha_machine_vector rawhide_mv; -extern struct alpha_machine_vector ruffian_mv; -extern struct alpha_machine_vector sable_mv; -extern struct alpha_machine_vector sable_gamma_mv; -extern struct alpha_machine_vector sx164_mv; -extern struct alpha_machine_vector takara_mv; -extern struct alpha_machine_vector xl_mv; -extern struct alpha_machine_vector xlt_mv; -#pragma weak alcor_mv -#pragma weak alphabook1_mv -#pragma weak avanti_mv -#pragma weak cabriolet_mv -#pragma weak dp264_mv -#pragma weak eb164_mv -#pragma weak eb64p_mv -#pragma weak eb66_mv -#pragma weak eb66p_mv -#pragma weak jensen_mv -#pragma weak lx164_mv -#pragma weak miata_mv -#pragma weak mikasa_mv -#pragma weak mikasa_primo_mv -#pragma weak monet_mv -#pragma weak noname_mv -#pragma weak noritake_mv -#pragma weak noritake_primo_mv -#pragma weak p2k_mv -#pragma weak pc164_mv -#pragma weak rawhide_mv -#pragma weak ruffian_mv -#pragma weak sable_mv -#pragma weak sable_gamma_mv -#pragma weak sx164_mv -#pragma weak takara_mv -#pragma weak xl_mv -#pragma weak xlt_mv +/* GCC 2.7.2 (on alpha at least) is lame. It does not support either + __attribute__((weak)) or #pragma weak. Bypass it and talk directly + to the assembler. */ + +#define WEAK(X) \ + extern struct alpha_machine_vector X; \ + asm(".weak "#X) + +WEAK(alcor_mv); +WEAK(alphabook1_mv); +WEAK(avanti_mv); +WEAK(cabriolet_mv); +WEAK(dp264_mv); +WEAK(eb164_mv); +WEAK(eb64p_mv); +WEAK(eb66_mv); +WEAK(eb66p_mv); +WEAK(jensen_mv); +WEAK(lx164_mv); +WEAK(miata_mv); +WEAK(mikasa_mv); +WEAK(mikasa_primo_mv); +WEAK(monet_mv); +WEAK(noname_mv); +WEAK(noritake_mv); +WEAK(noritake_primo_mv); +WEAK(p2k_mv); +WEAK(pc164_mv); +WEAK(rawhide_mv); +WEAK(ruffian_mv); +WEAK(rx164_mv); +WEAK(sable_mv); +WEAK(sable_gamma_mv); +WEAK(sx164_mv); +WEAK(takara_mv); +WEAK(webbrick_mv); +WEAK(xl_mv); +WEAK(xlt_mv); + +#undef WEAK void __init @@ -264,7 +248,7 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, if (initrd_end > *memory_end_p) { printk("initrd extends beyond end of memory " "(0x%08lx > 0x%08lx)\ndisabling initrd\n", - initrd_end, memory_end_p); + initrd_end, (unsigned long) memory_end_p); initrd_start = initrd_end = 0; } } @@ -275,6 +259,16 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, if (alpha_mv.init_arch) alpha_mv.init_arch(memory_start_p, memory_end_p); + /* Initialize the timers. */ + /* ??? There is some circumstantial evidence that this needs + to be done now rather than later in time_init, which would + be more natural. Someone please explain or refute. */ +#if defined(CONFIG_RTC) + rtc_init_pit(); +#else + alpha_mv.init_pit(); +#endif + /* * Give us a default console. TGA users will see nothing until * chr_dev_init is called, rather late in the boot sequence. @@ -349,8 +343,8 @@ static char systype_names[][16] = { static char unofficial_names[][8] = {"100", "Ruffian"}; -static char eb164_names[][8] = {"EB164", "PC164", "LX164", "SX164"}; -static int eb164_indices[] = {0,0,0,1,1,1,1,1,2,2,2,2,3,3,3,3}; +static char eb164_names[][8] = {"EB164", "PC164", "LX164", "SX164", "RX164"}; +static int eb164_indices[] = {0,0,0,1,1,1,1,1,2,2,2,2,3,3,3,3,4}; static char alcor_names[][16] = {"Alcor", "Maverick", "Bret"}; static int alcor_indices[] = {0,0,0,1,1,1,0,0,0,0,0,0,2,2,2,2,2,2}; @@ -430,7 +424,7 @@ get_sysvec(long type, long variation, long cpu) static struct alpha_machine_vector *eb164_vecs[] __initlocaldata = { - &eb164_mv, &pc164_mv, &lx164_mv, &sx164_mv + &eb164_mv, &pc164_mv, &lx164_mv, &sx164_mv, &rx164_mv }; static struct alpha_machine_vector *eb64p_vecs[] __initlocaldata = @@ -455,7 +449,7 @@ get_sysvec(long type, long variation, long cpu) &monet_mv, /* monet */ &dp264_mv, /* clipper */ &dp264_mv, /* goldrush */ - &dp264_mv, /* webbrick */ + &webbrick_mv, /* webbrick */ &dp264_mv, /* catamaran */ }; @@ -556,10 +550,12 @@ get_sysvec_byname(const char *name) &pc164_mv, &rawhide_mv, &ruffian_mv, + &rx164_mv, &sable_mv, &sable_gamma_mv, &sx164_mv, &takara_mv, + &webbrick_mv, &xl_mv, &xlt_mv }; diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 19aa9a682..25615e43d 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -30,7 +30,14 @@ #include "proto.h" -struct ipi_msg_flush_tb_struct ipi_msg_flush_tb; +#define DEBUG_SMP 0 +#if DEBUG_SMP +#define DBGS(args) printk args +#else +#define DBGS(args) +#endif + +struct ipi_msg_flush_tb_struct ipi_msg_flush_tb __cacheline_aligned; struct cpuinfo_alpha cpu_data[NR_CPUS]; @@ -39,7 +46,6 @@ spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; unsigned int boot_cpu_id = 0; static int smp_activated = 0; -static unsigned long ipicnt[NR_CPUS] = {0,}; /* IPI counts */ int smp_found_config = 0; /* Have we found an SMP box */ static int max_cpus = -1; @@ -53,10 +59,12 @@ int smp_threads_ready = 0; volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; volatile unsigned long smp_spinning[NR_CPUS] = { 0, }; +cycles_t cacheflush_time; + unsigned int prof_multiplier[NR_CPUS]; unsigned int prof_counter[NR_CPUS]; -volatile int ipi_bits[NR_CPUS]; +volatile int ipi_bits[NR_CPUS] __cacheline_aligned; unsigned long boot_cpu_palrev; @@ -73,87 +81,80 @@ static void smp_setup_percpu_timer(void); static void secondary_cpu_start(int, struct task_struct *); static void send_cpu_msg(char *, int); -/* process bootcommand SMP options, like "nosmp" and "maxcpus=" */ -__initfunc(void smp_setup(char *str, int *ints)) +/* Process bootcommand SMP options, like "nosmp" and "maxcpus=" */ +void __init +smp_setup(char *str, int *ints) { if (ints && ints[0] > 0) max_cpus = ints[1]; - else + else max_cpus = 0; } -void smp_store_cpu_info(int id) +static void __init +smp_store_cpu_info(int id) { /* This is it on Alpha, so far. */ - cpu_data[id].loops_per_sec = loops_per_sec; + cpu_data[id].loops_per_sec = loops_per_sec; } -void smp_commence(void) +void __init +smp_commence(void) { /* Lets the callin's below out of their loop. */ mb(); smp_commenced = 1; } -void smp_callin(void) +void __init +smp_callin(void) { - int cpuid = hard_smp_processor_id(); + int cpuid = hard_smp_processor_id(); -#if 0 - printk("CALLIN %d state 0x%lx\n", cpuid, current->state); -#endif + DBGS(("CALLIN %d state 0x%lx\n", cpuid, current->state)); #ifdef HUH - local_flush_cache_all(); - local_flush_tlb_all(); + local_flush_cache_all(); + local_flush_tlb_all(); #endif #if 0 - set_irq_udt(mid_xlate[boot_cpu_id]); + set_irq_udt(mid_xlate[boot_cpu_id]); #endif - /* Get our local ticker going. */ - smp_setup_percpu_timer(); + /* Get our local ticker going. */ + smp_setup_percpu_timer(); #if 0 - calibrate_delay(); + calibrate_delay(); #endif - smp_store_cpu_info(cpuid); + smp_store_cpu_info(cpuid); #ifdef HUH - local_flush_cache_all(); - local_flush_tlb_all(); + local_flush_cache_all(); + local_flush_tlb_all(); #endif - /* Allow master to continue. */ - set_bit(cpuid, (unsigned long *)&cpu_callin_map[cpuid]); + /* Allow master to continue. */ + set_bit(cpuid, (unsigned long *)&cpu_callin_map[cpuid]); #ifdef HUH - local_flush_cache_all(); - local_flush_tlb_all(); + local_flush_cache_all(); + local_flush_tlb_all(); #endif #ifdef NOT_YET - while(!task[cpuid] || current_set[cpuid] != task[cpuid]) - barrier(); -#endif /* NOT_YET */ - -#if 0 - /* Fix idle thread fields. */ - __asm__ __volatile__("ld [%0], %%g6\n\t" - : : "r" (¤t_set[cpuid]) - : "memory" /* paranoid */); - current->mm->mmap->vm_page_prot = PAGE_SHARED; - current->mm->mmap->vm_start = PAGE_OFFSET; - current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; + while(!task[cpuid] || current_set[cpuid] != task[cpuid]) + barrier(); #endif - + #ifdef HUH - local_flush_cache_all(); - local_flush_tlb_all(); + local_flush_cache_all(); + local_flush_tlb_all(); #endif #if 0 - __sti(); + __sti(); #endif } -asmlinkage int start_secondary(void *unused) +asmlinkage int __init +start_secondary(void *unused) { extern asmlinkage void entInt(void); extern void paging_init_secondary(void); @@ -163,35 +164,83 @@ asmlinkage int start_secondary(void *unused) trap_init(); wrent(entInt, 0); - smp_callin(); - while (!smp_commenced) + smp_callin(); + while (!smp_commenced) barrier(); #if 1 -printk("start_secondary: commencing CPU %d current %p\n", - hard_smp_processor_id(), current); + printk("start_secondary: commencing CPU %d current %p\n", + hard_smp_processor_id(), current); #endif - cpu_idle(NULL); + cpu_idle(NULL); +} + +static void __init +smp_tune_scheduling (void) +{ + /* + * Rough estimation for SMP scheduling, this is the number of + * cycles it takes for a fully memory-limited process to flush + * the SMP-local cache. + * + * We are not told how much cache there is, so we have to guess. + */ + + struct percpu_struct *cpu; + unsigned long on_chip_cache; + unsigned long freq; + + cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset); + switch (cpu->type) + { + case EV45_CPU: + on_chip_cache = 16 + 16; + break; + + case EV5_CPU: + case EV56_CPU: + on_chip_cache = 8 + 8 + 96; + break; + + case PCA56_CPU: + on_chip_cache = 16 + 8; + break; + + case EV6_CPU: + on_chip_cache = 64 + 64; + break; + + default: + on_chip_cache = 8 + 8; + break; + } + + freq = hwrpb->cycle_freq ? : est_cycle_freq; + + /* Magic estimation stolen from x86 port. */ + cacheflush_time = freq / 1024 * on_chip_cache / 5000; } + /* * Cycle through the processors sending START msgs to boot each. */ -void smp_boot_cpus(void) +void __init +smp_boot_cpus(void) { - int cpucount = 0; - int i, first, prev; + int cpucount = 0; + int i, first, prev; - printk("smp_boot_cpus: Entering SMP Mode...\n"); + printk("Entering SMP Mode.\n"); #if 0 - __sti(); + __sti(); #endif - for(i=0; i < NR_CPUS; i++) { + for(i=0; i < NR_CPUS; i++) { cpu_number_map[i] = -1; cpu_logical_map[i] = -1; - prof_counter[i] = 1; - prof_multiplier[i] = 1; + prof_counter[i] = 1; + prof_multiplier[i] = 1; ipi_bits[i] = 0; } @@ -199,159 +248,155 @@ void smp_boot_cpus(void) cpu_logical_map[0] = boot_cpu_id; current->processor = boot_cpu_id; /* ??? */ - smp_store_cpu_info(boot_cpu_id); + smp_store_cpu_info(boot_cpu_id); + smp_tune_scheduling(); #ifdef NOT_YET - printk("CPU%d: ", boot_cpu_id); - print_cpu_info(&cpu_data[boot_cpu_id]); - set_irq_udt(mid_xlate[boot_cpu_id]); -#endif /* NOT_YET */ - smp_setup_percpu_timer(); + printk("CPU%d: ", boot_cpu_id); + print_cpu_info(&cpu_data[boot_cpu_id]); + set_irq_udt(mid_xlate[boot_cpu_id]); +#endif + smp_setup_percpu_timer(); #ifdef HUH - local_flush_cache_all(); + local_flush_cache_all(); #endif - if (smp_num_probed == 1) + if (smp_num_probed == 1) return; /* Not an MP box. */ #if NOT_YET - /* - * If SMP should be disabled, then really disable it! - */ - if (!max_cpus) + /* + * If SMP should be disabled, then really disable it! + */ + if (!max_cpus) { smp_found_config = 0; - printk(KERN_INFO "SMP mode deactivated.\n"); - } -#endif /* NOT_YET */ + printk(KERN_INFO "SMP mode deactivated.\n"); + } +#endif - for (i = 0; i < NR_CPUS; i++) { + for (i = 0; i < NR_CPUS; i++) { if (i == boot_cpu_id) continue; - if (cpu_present_map & (1 << i)) { - struct task_struct *idle; - int timeout; + if (cpu_present_map & (1 << i)) { + struct task_struct *idle; + int timeout; - /* Cook up an idler for this guy. */ - kernel_thread(start_secondary, NULL, CLONE_PID); - idle = task[++cpucount]; + /* Cook up an idler for this guy. */ + kernel_thread(start_secondary, NULL, CLONE_PID); + idle = task[++cpucount]; if (!idle) panic("No idle process for CPU %d", i); - idle->processor = i; + idle->processor = i; -#if 0 -printk("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n", - i, idle->state, idle->flags); -#endif + DBGS(("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n", + i, idle->state, idle->flags)); - /* whirrr, whirrr, whirrrrrrrrr... */ + /* whirrr, whirrr, whirrrrrrrrr... */ #ifdef HUH - local_flush_cache_all(); + local_flush_cache_all(); #endif - secondary_cpu_start(i, idle); + secondary_cpu_start(i, idle); - /* wheee... it's going... wait for 5 secs...*/ - for (timeout = 0; timeout < 50000; timeout++) { + /* wheee... it's going... wait for 5 secs...*/ + for (timeout = 0; timeout < 50000; timeout++) { if (cpu_callin_map[i]) break; - udelay(100); - } - if (cpu_callin_map[i]) { + udelay(100); + } + if (cpu_callin_map[i]) { /* Another "Red Snapper". */ cpu_number_map[i] = cpucount; - cpu_logical_map[cpucount] = i; - } else { + cpu_logical_map[cpucount] = i; + } else { cpucount--; - printk("smp_boot_cpus: Processor %d" + printk("smp_boot_cpus: Processor %d" " is stuck 0x%lx.\n", i, idle->flags); - } - } - if (!(cpu_callin_map[i])) { + } + } + if (!(cpu_callin_map[i])) { cpu_present_map &= ~(1 << i); - cpu_number_map[i] = -1; - } - } + cpu_number_map[i] = -1; + } + } #ifdef HUH - local_flush_cache_all(); + local_flush_cache_all(); #endif - if (cpucount == 0) { + if (cpucount == 0) { printk("smp_boot_cpus: ERROR - only one Processor found.\n"); - cpu_present_map = (1 << smp_processor_id()); - } else { + cpu_present_map = (1 << smp_processor_id()); + } else { unsigned long bogosum = 0; - for (i = 0; i < NR_CPUS; i++) { + for (i = 0; i < NR_CPUS; i++) { if (cpu_present_map & (1 << i)) bogosum += cpu_data[i].loops_per_sec; - } - printk("smp_boot_cpus: Total of %d Processors activated" + } + printk("smp_boot_cpus: Total of %d Processors activated" " (%lu.%02lu BogoMIPS).\n", - cpucount + 1, - (bogosum + 2500)/500000, - ((bogosum + 2500)/5000)%100); - smp_activated = 1; - smp_num_cpus = cpucount + 1; - } - - /* Setup CPU list for IRQ distribution scheme. */ - first = prev = -1; - for (i = 0; i < NR_CPUS; i++) { + cpucount + 1, + (bogosum + 2500)/500000, + ((bogosum + 2500)/5000)%100); + smp_activated = 1; + smp_num_cpus = cpucount + 1; + } + + /* Setup CPU list for IRQ distribution scheme. */ + first = prev = -1; + for (i = 0; i < NR_CPUS; i++) { if (cpu_present_map & (1 << i)) { if (first == -1) first = i; if (prev != -1) cpu_data[i].next = i; - prev = i; - } - } - cpu_data[prev].next = first; - - /* Ok, they are spinning and ready to go. */ - smp_processors_ready = 1; -} + prev = i; + } + } + cpu_data[prev].next = first; -__initfunc(void ioapic_pirq_setup(char *str, int *ints)) -{ - /* this is prolly INTEL-specific */ + /* Ok, they are spinning and ready to go. */ + smp_processors_ready = 1; } -static void smp_setup_percpu_timer(void) +static void __init +smp_setup_percpu_timer(void) { - int cpu = smp_processor_id(); + int cpu = smp_processor_id(); - prof_counter[cpu] = prof_multiplier[cpu] = 1; + prof_counter[cpu] = prof_multiplier[cpu] = 1; #ifdef NOT_YET - load_profile_irq(mid_xlate[cpu], lvl14_resolution); - if (cpu == boot_cpu_id) + load_profile_irq(mid_xlate[cpu], lvl14_resolution); + if (cpu == boot_cpu_id) enable_pil_irq(14); #endif } extern void update_one_process(struct task_struct *p, unsigned long ticks, - unsigned long user, unsigned long system, + unsigned long user, unsigned long system, int cpu); -void smp_percpu_timer_interrupt(struct pt_regs *regs) +void +smp_percpu_timer_interrupt(struct pt_regs *regs) { int cpu = smp_processor_id(); #ifdef NOT_YET - clear_profile_irq(mid_xlate[cpu]); - if(!user_mode(regs)) + clear_profile_irq(mid_xlate[cpu]); + if(!user_mode(regs)) alpha_do_profile(regs->pc); #endif - if (!--prof_counter[cpu]) { + if (!--prof_counter[cpu]) { int user = user_mode(regs); - if (current->pid) { + if (current->pid) { update_one_process(current, 1, user, !user, cpu); - if (--current->counter < 0) { + if (--current->counter < 0) { current->counter = 0; - current->need_resched = 1; - } + current->need_resched = 1; + } - spin_lock(&ticker_lock); - if (user) { + spin_lock(&ticker_lock); + if (user) { if (current->priority < DEF_PRIORITY) { kstat.cpu_nice++; kstat.per_cpu_nice[cpu]++; @@ -359,93 +404,86 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs) kstat.cpu_user++; kstat.per_cpu_user[cpu]++; } - } else { + } else { kstat.cpu_system++; kstat.per_cpu_system[cpu]++; - } - spin_unlock(&ticker_lock); - } - prof_counter[cpu] = prof_multiplier[cpu]; - } + } + spin_unlock(&ticker_lock); + } + prof_counter[cpu] = prof_multiplier[cpu]; + } } -int setup_profiling_timer(unsigned int multiplier) +int __init +setup_profiling_timer(unsigned int multiplier) { #ifdef NOT_YET - int i; - unsigned long flags; + int i; + unsigned long flags; - /* Prevent level14 ticker IRQ flooding. */ - if((!multiplier) || (lvl14_resolution / multiplier) < 500) - return -EINVAL; + /* Prevent level14 ticker IRQ flooding. */ + if((!multiplier) || (lvl14_resolution / multiplier) < 500) + return -EINVAL; - save_and_cli(flags); - for(i = 0; i < NR_CPUS; i++) { - if(cpu_present_map & (1 << i)) { - load_profile_irq(mid_xlate[i], lvl14_resolution / multip + save_and_cli(flags); + for(i = 0; i < NR_CPUS; i++) { + if(cpu_present_map & (1 << i)) { + load_profile_irq(mid_xlate[i], lvl14_resolution / multip lier); - prof_multiplier[i] = multiplier; - } - } - restore_flags(flags); + prof_multiplier[i] = multiplier; + } + } + restore_flags(flags); - return 0; + return 0; #endif return -EINVAL; } -/* Only broken Intel needs this, thus it should not even be referenced globally. -*/ -__initfunc(void initialize_secondary(void)) +/* Only broken Intel needs this, thus it should not even be + referenced globally. */ + +void __init +initialize_secondary(void) { - printk("initialize_secondary: entry\n"); } -static void +static void __init secondary_cpu_start(int cpuid, struct task_struct *idle) { struct percpu_struct *cpu; - int timeout; + int timeout; cpu = (struct percpu_struct *) ((char*)hwrpb - + hwrpb->processor_offset - + cpuid * hwrpb->processor_size); + + hwrpb->processor_offset + + cpuid * hwrpb->processor_size); - /* set context to idle thread this CPU will use when running */ - /* assumption is that the idle thread is all set to go... ??? */ + /* Set context to idle thread this CPU will use when running + assumption is that the idle thread is all set to go... ??? */ memcpy(&cpu->hwpcb[0], &idle->tss, sizeof(struct pcb_struct)); cpu->hwpcb[4] = cpu->hwpcb[0]; /* UNIQUE set to KSP ??? */ -#if 0 -printk("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n", - cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb); -printk("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n", - cpuid, idle->state, idle->tss.pal_flags); -#endif - /* setup HWRPB fields that SRM uses to activate secondary CPU */ - hwrpb->CPU_restart = __start_cpu; - hwrpb->CPU_restart_data = (unsigned long) idle; - - /* recalculate and update the HWRPB checksum */ - { - unsigned long sum, *lp1, *lp2; - sum = 0; - lp1 = (unsigned long *)hwrpb; - lp2 = &hwrpb->chksum; - while (lp1 < lp2) - sum += *lp1++; - *lp2 = sum; - } + DBGS(("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n", + cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb)); + DBGS(("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n", + cpuid, idle->state, idle->tss.pal_flags)); + + /* Setup HWRPB fields that SRM uses to activate secondary CPU */ + hwrpb->CPU_restart = __start_cpu; + hwrpb->CPU_restart_data = (unsigned long) idle; + + /* Recalculate and update the HWRPB checksum */ + hwrpb_update_checksum(hwrpb); /* * Send a "start" command to the specified processor. */ /* SRM III 3.4.1.3 */ - cpu->flags |= 0x22; /* turn on Context Valid and Restart Capable */ - cpu->flags &= ~1;/* turn off Bootstrap In Progress */ + cpu->flags |= 0x22; /* turn on Context Valid and Restart Capable */ + cpu->flags &= ~1; /* turn off Bootstrap In Progress */ mb(); send_cpu_msg("START\r\n", cpuid); @@ -454,7 +492,7 @@ printk("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n", for (timeout = 10000; !(cpu->flags & 1); timeout--) { if (timeout <= 0) { printk("Processor %d failed to start\n", cpuid); - /* needed for pset_info to work */ + /* needed for pset_info to work */ #if 0 ipc_processor_enable(cpu_to_processor(cpunum)); #endif @@ -462,49 +500,61 @@ printk("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n", } mdelay(1); } -#if 0 - printk("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid); -#endif + DBGS(("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid)); } static void send_cpu_msg(char *str, int cpuid) { struct percpu_struct *cpu; - register char *cp1, *cp2; - unsigned long cpumask; - int timeout; + register char *cp1, *cp2; + unsigned long cpumask; + size_t len; + int timeout; - cpu = (struct percpu_struct *) ((char*)hwrpb - + hwrpb->processor_offset - + cpuid * hwrpb->processor_size); - - cpumask = (1L << cpuid); - for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) { - if (timeout <= 0) { - printk("Processor %x not ready\n", cpuid); - return; - } - mdelay(1); - } - - cp1 = (char *) &cpu->ipc_buffer[1]; - cp2 = str; - while (*cp2) *cp1++ = *cp2++; - *(unsigned int *)&cpu->ipc_buffer[0] = cp2 - str; /* hack */ - - /* atomic test and set */ - set_bit(cpuid, &hwrpb->rxrdy); - - for (timeout = 10000; (hwrpb->txrdy & cpumask); timeout--) { - if (timeout <= 0) { - printk("Processor %x not ready\n", cpuid); - return; - } - mdelay(1); - } + + hwrpb->processor_offset + + cpuid * hwrpb->processor_size); + + cpumask = (1L << cpuid); + if (hwrpb->txrdy & cpumask) + goto delay1; + ready1: + + cp2 = str; + len = strlen(cp2); + *(unsigned int *)&cpu->ipc_buffer[0] = len; + cp1 = (char *) &cpu->ipc_buffer[1]; + memcpy(cp1, cp2, len); + + /* atomic test and set */ + set_bit(cpuid, &hwrpb->rxrdy); + + if (hwrpb->txrdy & cpumask) + goto delay2; + ready2: + return; + +delay1: + for (timeout = 10000; timeout > 0; --timeout) { + if (!(hwrpb->txrdy & cpumask)) + goto ready1; + udelay(100); + } + goto timeout; + +delay2: + for (timeout = 10000; timeout > 0; --timeout) { + if (!(hwrpb->txrdy & cpumask)) + goto ready2; + udelay(100); + } + goto timeout; + +timeout: + printk("Processor %x not ready\n", cpuid); + return; } /* @@ -512,7 +562,8 @@ send_cpu_msg(char *str, int cpuid) * * called from arch/alpha/kernel/setup.c:setup_arch() when __SMP__ defined */ -__initfunc(void setup_smp(void)) +void __init +setup_smp(void) { struct percpu_struct *cpubase, *cpu; int i; @@ -523,10 +574,10 @@ __initfunc(void setup_smp(void)) } if (hwrpb->nr_processors > 1) { -#if 0 -printk("setup_smp: nr_processors 0x%lx\n", - hwrpb->nr_processors); -#endif + + DBGS(("setup_smp: nr_processors %ld\n", + hwrpb->nr_processors)); + cpubase = (struct percpu_struct *) ((char*)hwrpb + hwrpb->processor_offset); boot_cpu_palrev = cpubase->pal_revision; @@ -541,12 +592,11 @@ printk("setup_smp: nr_processors 0x%lx\n", if (i != boot_cpu_id) cpu->pal_revision = boot_cpu_palrev; } -#if 0 -printk("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n", - i, cpu->flags, cpu->type); - printk("setup_smp: CPU %d: PAL rev 0x%lx\n", - i, cpu->pal_revision); -#endif + + DBGS(("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n", + i, cpu->flags, cpu->type)); + DBGS(("setup_smp: CPU %d: PAL rev 0x%lx\n", + i, cpu->pal_revision)); } } else { smp_num_probed = 1; @@ -560,132 +610,59 @@ printk("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n", static void secondary_console_message(void) { - int mycpu, i, cnt; + int mycpu, i, cnt; unsigned long txrdy = hwrpb->txrdy; char *cp1, *cp2, buf[80]; - struct percpu_struct *cpu; - - mycpu = hard_smp_processor_id(); - -#if 0 -printk("secondary_console_message: TXRDY 0x%lx.\n", txrdy); -#endif - for (i = 0; i < NR_CPUS; i++) { - if (txrdy & (1L << i)) { -#if 0 -printk("secondary_console_message: TXRDY contains CPU %d.\n", i); -#endif - cpu = (struct percpu_struct *) - ((char*)hwrpb - + hwrpb->processor_offset - + i * hwrpb->processor_size); -#if 1 - printk("secondary_console_message: on %d from %d" - " HALT_REASON 0x%lx FLAGS 0x%lx\n", - mycpu, i, cpu->halt_reason, cpu->flags); -#endif - cnt = cpu->ipc_buffer[0] >> 32; - if (cnt <= 0 || cnt >= 80) - strcpy(buf,"<<< BOGUS MSG >>>"); - else { - cp1 = (char *) &cpu->ipc_buffer[11]; - cp2 = buf; - while (cnt--) { - if (*cp1 == '\r' || *cp1 == '\n') { - *cp2++ = ' '; cp1++; - } else - *cp2++ = *cp1++; - } - *cp2 = 0; - } -#if 1 - printk("secondary_console_message: on %d message is '%s'\n", - mycpu, buf); -#endif - } - } - hwrpb->txrdy = 0; - return; -} - -static int -halt_on_panic(unsigned int this_cpu) -{ - halt(); - return 0; -} - -static int -local_flush_tlb_all(unsigned int this_cpu) -{ - tbia(); - clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask); - return 0; -} + struct percpu_struct *cpu; -static int -local_flush_tlb_mm(unsigned int this_cpu) -{ - struct mm_struct * mm = ipi_msg_flush_tb.p.flush_mm; - if (mm == current->mm) - flush_tlb_current(mm); - clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask); - return 0; -} + DBGS(("secondary_console_message: TXRDY 0x%lx.\n", txrdy)); -static int -local_flush_tlb_page(unsigned int this_cpu) -{ - struct vm_area_struct * vma = ipi_msg_flush_tb.p.flush_vma; - struct mm_struct * mm = vma->vm_mm; + mycpu = hard_smp_processor_id(); - if (mm == current->mm) - flush_tlb_current_page(mm, vma, ipi_msg_flush_tb.flush_addr); - clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask); - return 0; -} + for (i = 0; i < NR_CPUS; i++) { + if (!(txrdy & (1L << i))) + continue; -static int -wrapper_local_flush_tlb_page(unsigned int this_cpu) -{ -#if 0 - int cpu = smp_processor_id(); + DBGS(("secondary_console_message: " + "TXRDY contains CPU %d.\n", i)); + + cpu = (struct percpu_struct *) + ((char*)hwrpb + + hwrpb->processor_offset + + i * hwrpb->processor_size); + + printk("secondary_console_message: on %d from %d" + " HALT_REASON 0x%lx FLAGS 0x%lx\n", + mycpu, i, cpu->halt_reason, cpu->flags); + + cnt = cpu->ipc_buffer[0] >> 32; + if (cnt <= 0 || cnt >= 80) + strcpy(buf, "<<< BOGUS MSG >>>"); + else { + cp1 = (char *) &cpu->ipc_buffer[11]; + cp2 = buf; + strcpy(cp2, cp1); + + while ((cp2 = strchr(cp2, '\r')) != 0) { + *cp2 = ' '; + if (cp2[1] == '\n') + cp2[1] = ' '; + } + } - if (cpu) { - printk("wrapper: ipi_msg_flush_tb.flush_addr 0x%lx [%d]\n", - ipi_msg_flush_tb.flush_addr, atomic_read(&global_irq_count)); + printk("secondary_console_message: on %d message is '%s'\n", + mycpu, buf); } -#endif - local_flush_tlb_page(this_cpu); - return 0; -} -static int -unknown_ipi(unsigned int this_cpu) -{ - printk("unknown_ipi() on CPU %d: ", this_cpu); - return 1; + hwrpb->txrdy = 0; } enum ipi_message_type { - CPU_STOP, - TLB_ALL, - TLB_MM, - TLB_PAGE, - TLB_RANGE -}; - -static int (* ipi_func[32])(unsigned int) = { - halt_on_panic, - local_flush_tlb_all, - local_flush_tlb_mm, - wrapper_local_flush_tlb_page, - local_flush_tlb_mm, /* a.k.a. local_flush_tlb_range */ - unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, - unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, - unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, - unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, unknown_ipi, - unknown_ipi, unknown_ipi, unknown_ipi + IPI_TLB_ALL, + IPI_TLB_MM, + IPI_TLB_PAGE, + IPI_RESCHEDULE, + IPI_CPU_STOP }; void @@ -693,122 +670,165 @@ handle_ipi(struct pt_regs *regs) { int this_cpu = smp_processor_id(); volatile int * pending_ipis = &ipi_bits[this_cpu]; - int ops; + unsigned long ops; + + DBGS(("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n", + this_cpu, *pending_ipis, regs->pc)); + + mb(); /* Order interrupt and bit testing. */ + while ((ops = xchg(pending_ipis, 0)) != 0) { + mb(); /* Order bit clearing and data access. */ + do { + unsigned long which; + + which = ops & -ops; + ops &= ~which; + which = ffz(~which); + + if (which < IPI_RESCHEDULE) { + if (which == IPI_TLB_ALL) + tbia(); + else if (which == IPI_TLB_MM) { + struct mm_struct * mm; + mm = ipi_msg_flush_tb.p.flush_mm; + if (mm == current->mm) + flush_tlb_current(mm); + } + else /* IPI_TLB_PAGE */ { + struct vm_area_struct * vma; + struct mm_struct * mm; + unsigned long addr; - mb(); /* Order bit setting and interrupt. */ -#if 0 - printk("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n", - this_cpu, *pending_ipis, regs->pc); -#endif - while ((ops = *pending_ipis)) { - int first; - for (first = 0; (ops & 1) == 0; ++first, ops >>= 1) - ; /* look for the first thing to do */ - clear_bit(first, pending_ipis); - mb(); /* Order bit clearing and data access. */ - if ((*ipi_func[first])(this_cpu)) - printk("%d\n", first); - mb(); /* Order data access and bit clearing. */ + vma = ipi_msg_flush_tb.p.flush_vma; + mm = vma->vm_mm; + addr = ipi_msg_flush_tb.flush_addr; + + if (mm == current->mm) + flush_tlb_current_page(mm, vma, addr); + } + clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask); + } + else if (which == IPI_RESCHEDULE) { + /* Reschedule callback. Everything to be done + is done by the interrupt return path. */ + } + else if (which == IPI_CPU_STOP) { + halt(); + } + else { + printk(KERN_CRIT "unknown_ipi() on CPU %d: %lu\n", + this_cpu, which); + } + } while (ops); + mb(); /* Order data access and bit testing. */ } + + cpu_data[this_cpu].ipi_count++; + if (hwrpb->txrdy) - secondary_console_message(); + secondary_console_message(); } -void -send_ipi_message(long to_whom, enum ipi_message_type operation) +static void +send_ipi_message(unsigned long to_whom, enum ipi_message_type operation) { - int i; - unsigned int j; + long i, j; - mb(); /* Order out-of-band data and bit setting. */ - for (i = 0, j = 1; i < NR_CPUS; ++i, j += j) { - if ((to_whom & j) == 0) - continue; - set_bit(operation, &ipi_bits[i]); - mb(); /* Order bit setting and interrupt. */ - wripir(i); + /* Reduce the number of memory barriers by doing two loops, + one to set the bits, one to invoke the interrupts. */ + + mb(); /* Order out-of-band data and bit setting. */ + + for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { + if (to_whom & j) + set_bit(operation, &ipi_bits[i]); + } + + mb(); /* Order bit setting and interrupt. */ + + for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) { + if (to_whom & j) + wripir(i); } } int smp_info(char *buffer) { - int i; + long i; unsigned long sum = 0; for (i = 0; i < NR_CPUS; i++) - sum += ipicnt[i]; + sum += cpu_data[i].ipi_count; - return sprintf(buffer, "CPUs probed %d active %d map 0x%x IPIs %ld\n", + return sprintf(buffer, "CPUs probed %d active %d map 0x%x IPIs %ld\n", smp_num_probed, smp_num_cpus, cpu_present_map, sum); } -/* wrapper for call from panic() */ void -smp_message_pass(int target, int msg, unsigned long data, int wait) +smp_send_reschedule(int cpu) { - int me = smp_processor_id(); - - if (msg != MSG_STOP_CPU) - goto barf; + send_ipi_message(1 << cpu, IPI_RESCHEDULE); +} - send_ipi_message(CPU_STOP, cpu_present_map ^ (1 << me)); - return; -barf: - printk("Yeeee, trying to send SMP msg(%d) on CPU %d\n", msg, me); - panic("Bogon SMP message pass."); +void +smp_send_stop(void) +{ + unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); + send_ipi_message(to_whom, IPI_CPU_STOP); } void flush_tlb_all(void) { - unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id()); - int timeout = 10000; + unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); + long timeout = 1000000; spin_lock_own(&kernel_flag, "flush_tlb_all"); ipi_msg_flush_tb.flush_tb_mask = to_whom; - send_ipi_message(to_whom, TLB_ALL); + send_ipi_message(to_whom, IPI_TLB_ALL); tbia(); - while (ipi_msg_flush_tb.flush_tb_mask) { - if (--timeout < 0) { - printk("flush_tlb_all: STUCK on CPU %d mask 0x%x\n", - smp_processor_id(), - ipi_msg_flush_tb.flush_tb_mask); - ipi_msg_flush_tb.flush_tb_mask = 0; - break; - } - /* Wait for all clear from other CPUs. */ - udelay(100); + while (ipi_msg_flush_tb.flush_tb_mask && --timeout) { + udelay(1); + barrier(); + } + + if (timeout == 0) { + printk("flush_tlb_all: STUCK on CPU %d mask 0x%x\n", + smp_processor_id(), + ipi_msg_flush_tb.flush_tb_mask); + ipi_msg_flush_tb.flush_tb_mask = 0; } } void flush_tlb_mm(struct mm_struct *mm) { - unsigned int to_whom = cpu_present_map ^ (1 << smp_processor_id()); - int timeout = 10000; + unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id()); + long timeout = 1000000; spin_lock_own(&kernel_flag, "flush_tlb_mm"); - ipi_msg_flush_tb.p.flush_mm = mm; ipi_msg_flush_tb.flush_tb_mask = to_whom; - send_ipi_message(to_whom, TLB_MM); + ipi_msg_flush_tb.p.flush_mm = mm; + send_ipi_message(to_whom, IPI_TLB_MM); if (mm != current->mm) flush_tlb_other(mm); else flush_tlb_current(mm); - while (ipi_msg_flush_tb.flush_tb_mask) { - if (--timeout < 0) { - printk("flush_tlb_mm: STUCK on CPU %d mask 0x%x\n", - smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask); - ipi_msg_flush_tb.flush_tb_mask = 0; - break; - } - udelay(100); - ; /* Wait for all clear from other CPUs. */ + while (ipi_msg_flush_tb.flush_tb_mask && --timeout) { + udelay(1); + barrier(); + } + + if (timeout == 0) { + printk("flush_tlb_mm: STUCK on CPU %d mask 0x%x\n", + smp_processor_id(), + ipi_msg_flush_tb.flush_tb_mask); + ipi_msg_flush_tb.flush_tb_mask = 0; } } @@ -816,68 +836,40 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { int cpu = smp_processor_id(); - unsigned int to_whom = cpu_present_map ^ (1 << cpu); + unsigned long to_whom = cpu_present_map ^ (1 << cpu); struct mm_struct * mm = vma->vm_mm; - int timeout = 10000; + int timeout = 1000000; spin_lock_own(&kernel_flag, "flush_tlb_page"); + ipi_msg_flush_tb.flush_tb_mask = to_whom; ipi_msg_flush_tb.p.flush_vma = vma; ipi_msg_flush_tb.flush_addr = addr; - ipi_msg_flush_tb.flush_tb_mask = to_whom; - send_ipi_message(to_whom, TLB_PAGE); + send_ipi_message(to_whom, IPI_TLB_PAGE); if (mm != current->mm) flush_tlb_other(mm); else flush_tlb_current_page(mm, vma, addr); - while (ipi_msg_flush_tb.flush_tb_mask) { - if (--timeout < 0) { - printk("flush_tlb_page: STUCK on CPU %d [0x%x,0x%lx,%d]\n", - cpu, ipi_msg_flush_tb.flush_tb_mask, addr, - global_irq_holder); - ipi_msg_flush_tb.flush_tb_mask = 0; - break; - } - udelay(100); - ; /* Wait for all clear from other CPUs. */ + while (ipi_msg_flush_tb.flush_tb_mask && --timeout) { + udelay(1); + barrier(); + } + + if (timeout == 0) { + printk("flush_tlb_page: STUCK on CPU %d mask 0x%x\n", + smp_processor_id(), + ipi_msg_flush_tb.flush_tb_mask); + ipi_msg_flush_tb.flush_tb_mask = 0; } } void flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { -#if 0 + /* On the Alpha we always flush the whole user tlb. */ flush_tlb_mm(mm); -#else - unsigned int to_whom; - int timeout; - - timeout = 10000; - to_whom = cpu_present_map ^ (1 << smp_processor_id()); - - spin_lock_own(&kernel_flag, "flush_tlb_range"); - - ipi_msg_flush_tb.p.flush_mm = mm; - ipi_msg_flush_tb.flush_tb_mask = to_whom; - send_ipi_message(to_whom, TLB_MM); - - if (mm != current->mm) - flush_tlb_other(mm); - else - flush_tlb_current(mm); - - while (ipi_msg_flush_tb.flush_tb_mask) { - if (--timeout < 0) { - printk("flush_tlb_range: STUCK on CPU %d mask 0x%x\n", - smp_processor_id(), ipi_msg_flush_tb.flush_tb_mask); - ipi_msg_flush_tb.flush_tb_mask = 0; - break; - } - udelay(100); /* Wait for all clear from other CPUs. */ - } -#endif } #if DEBUG_SPINLOCK @@ -902,8 +894,8 @@ spinlock_restore_ipl(long prev) #else -#define spinlock_raise_ipl(LOCK) 0 -#define spinlock_restore_ipl(PREV) ((void)0) +#define spinlock_raise_ipl(LOCK) ((void)(LOCK), 0) +#define spinlock_restore_ipl(PREV) ((void)(PREV)) #endif /* MANAGE_SPINLOCK_IPL */ diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index 9ac7abb36..656e5d868 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -125,6 +125,7 @@ dp264_init_irq(void) outb(0, DMA1_RESET_REG); outb(0, DMA2_RESET_REG); outb(DMA_MODE_CASCADE, DMA2_MODE_REG); + outb(0, DMA2_MASK_REG); if (alpha_using_srm) alpha_mv.device_interrupt = dp264_srm_device_interrupt; @@ -287,6 +288,29 @@ monet_swizzle(struct pci_dev *dev, int *pinp) return slot; } +static int __init +webbrick_map_irq(struct pci_dev *dev, int slot, int pin) +{ + static char irq_tab[13][5] __initlocaldata = { + /*INT INTA INTB INTC INTD */ + { -1, -1, -1, -1, -1}, /* IdSel 7 ISA Bridge */ + { -1, -1, -1, -1, -1}, /* IdSel 8 unused */ + { 29, 29, 29, 29, 29}, /* IdSel 9 21143 #1 */ + { -1, -1, -1, -1, -1}, /* IdSel 10 unused */ + { 30, 30, 30, 30, 30}, /* IdSel 11 21143 #2 */ + { -1, -1, -1, -1, -1}, /* IdSel 12 unused */ + { -1, -1, -1, -1, -1}, /* IdSel 13 unused */ + { 47, 47, 46, 45, 44}, /* IdSel 14 slot 0 */ + { 39, 39, 38, 37, 36}, /* IdSel 15 slot 1 */ + { 43, 43, 42, 41, 40}, /* IdSel 16 slot 2 */ + { 35, 35, 34, 33, 32}, /* IdSel 17 slot 3 */ +}; + const long min_idsel = 7, max_idsel = 17, irqs_per_slot = 5; + int irq = COMMON_TABLE_LOOKUP; + + return irq; +} + static void __init dp264_pci_fixup(void) { @@ -304,6 +328,14 @@ monet_pci_fixup(void) SMC669_Init(); } +static void __init +webbrick_pci_fixup(void) +{ + layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); + common_pci_fixup(webbrick_map_irq, common_swizzle); + SMC669_Init(); +} + /* * The System Vectors @@ -353,5 +385,27 @@ struct alpha_machine_vector monet_mv __initmv = { pci_fixup: monet_pci_fixup, kill_arch: generic_kill_arch, }; -/* No alpha_mv alias for monet, since we compile it in unconditionally + +struct alpha_machine_vector webbrick_mv __initmv = { + vector_name: "Webbrick", + DO_EV6_MMU, + DO_DEFAULT_RTC, + DO_TSUNAMI_IO, + DO_TSUNAMI_BUS, + machine_check: tsunami_machine_check, + max_dma_address: ALPHA_MAX_DMA_ADDRESS, + + nr_irqs: 64, + irq_probe_mask: _PROBE_MASK(64), + update_irq_hw: dp264_update_irq_hw, + ack_irq: generic_ack_irq, + device_interrupt: dp264_device_interrupt, + + init_arch: tsunami_init_arch, + init_irq: dp264_init_irq, + init_pit: generic_init_pit, + pci_fixup: webbrick_pci_fixup, + kill_arch: generic_kill_arch, +}; +/* No alpha_mv alias for webbrick, since we compile it in unconditionally with DP264; setup_arch knows how to cope. */ diff --git a/arch/alpha/kernel/sys_rawhide.c b/arch/alpha/kernel/sys_rawhide.c index e8fc32fcd..19d79c060 100644 --- a/arch/alpha/kernel/sys_rawhide.c +++ b/arch/alpha/kernel/sys_rawhide.c @@ -159,7 +159,7 @@ rawhide_map_irq(struct pci_dev *dev, int slot, int pin) static void __init rawhide_pci_fixup(void) { - layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); + layout_all_busses(DEFAULT_IO_BASE, RAWHIDE_DEFAULT_MEM_BASE); common_pci_fixup(rawhide_map_irq, common_swizzle); } diff --git a/arch/alpha/kernel/sys_ruffian.c b/arch/alpha/kernel/sys_ruffian.c index a7ae730d8..4d52c256b 100644 --- a/arch/alpha/kernel/sys_ruffian.c +++ b/arch/alpha/kernel/sys_ruffian.c @@ -82,7 +82,7 @@ ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs) * then all the PCI slots/INTXs (12-31) * flash(5) :DWH: */ - pld &= 0x00000000ffffff9fUL; /* was ffff7f */ + pld &= 0x00000000ffffff9fUL; /* was ffff7f */ /* * Now for every possible bit set, work through them and call @@ -92,6 +92,12 @@ ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs) i = ffz(~pld); pld &= pld - 1; /* clear least bit set */ if (i == 7) { /* if ISA int */ + /* Ruffian does not have the RTC connected to + the CPU timer interrupt. Instead, it uses the + PIT connected to IRQ 0. So we must detect that + and route that specifically to where we expected + to find the timer interrupt come in. */ + /* Copy this code from isa_device_interrupt because we need to hook into int 0 for the timer. I refuse to soil device_interrupt with ifdefs. */ @@ -107,17 +113,17 @@ ruffian_device_interrupt(unsigned long vector, struct pt_regs *regs) if (j == 7 && !(inb(0x20) & 0x80)) { /* It's only a passive release... */ } else if (j == 0) { - handle_irq(8, -1, regs); /* fake it */ + handle_irq(TIMER_IRQ, -1, regs); ruffian_ack_irq(0); } else { handle_irq(j, j, regs); } - } else { /* if not an ISA int */ + } else { /* if not an ISA int */ handle_irq(16 + i, 16 + i, regs); } - *(vulp)PYXIS_INT_REQ = 1UL << i; mb(); - *(vulp)PYXIS_INT_REQ; /* read to force the write */ + *(vulp)PYXIS_INT_REQ = 1UL << i; mb(); + *(vulp)PYXIS_INT_REQ; /* read to force the write */ } } @@ -216,7 +222,7 @@ ruffian_init_arch(unsigned long *mem_start, unsigned long *mem_end) pyxis_enable_errors(); if (!pyxis_srm_window_setup()) { - printk("ruffian_init_arch: Skipping window register rewrites." + printk("ruffian_init_arch: Skipping window register rewrites." "\n... Trust DeskStation firmware!\n"); } pyxis_finish_init_arch(); @@ -227,7 +233,10 @@ ruffian_init_pit (void) { /* Ruffian depends on the system timer established in MILO! */ request_region(0x70, 0x10, "timer"); - init_pit_rest(); + + outb(0xb6, 0x43); /* pit counter 2: speaker */ + outb(0x31, 0x42); + outb(0x13, 0x42); } static void diff --git a/arch/alpha/kernel/sys_rx164.c b/arch/alpha/kernel/sys_rx164.c new file mode 100644 index 000000000..5d2cf5288 --- /dev/null +++ b/arch/alpha/kernel/sys_rx164.c @@ -0,0 +1,235 @@ +/* + * linux/arch/alpha/kernel/sys_rx164.c + * + * Copyright (C) 1995 David A Rusling + * Copyright (C) 1996 Jay A Estabrook + * Copyright (C) 1998 Richard Henderson + * + * Code supporting the RX164 (PCA56+POLARIS). + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/init.h> + +#include <asm/ptrace.h> +#include <asm/system.h> +#include <asm/dma.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/mmu_context.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/core_polaris.h> + +#include "proto.h" +#include "irq.h" +#include "bios32.h" +#include "machvec.h" + + +static void +rx164_update_irq_hw(unsigned long irq, unsigned long mask, int unmask_p) +{ + if (irq >= 16) { + unsigned int temp; + pcibios_write_config_dword(0, 0, 0x74, ~mask >> 16); + pcibios_read_config_dword(0, 0, 0x74, &temp); + } + else if (irq >= 8) + outb(mask >> 8, 0xA1); /* ISA PIC2 */ + else + outb(mask, 0x21); /* ISA PIC1 */ +} + +static void +rx164_srm_update_irq_hw(unsigned long irq, unsigned long mask, int unmask_p) +{ +#if 0 + if (irq >= 16) { + if (unmask_p) + cserve_ena(irq - 16); + else + cserve_dis(irq - 16); + } + else if (irq >= 8) + outb(mask >> 8, 0xA1); /* ISA PIC2 */ + else + outb(mask, 0x21); /* ISA PIC1 */ +#endif +} + +static void +rx164_isa_device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + unsigned long pic; + + /* + * It seems to me that the probability of two or more *device* + * interrupts occurring at almost exactly the same time is + * pretty low. So why pay the price of checking for + * additional interrupts here if the common case can be + * handled so much easier? + */ + /* + * The first read of the PIC gives you *all* interrupting lines. + * Therefore, read the mask register and and out those lines + * not enabled. Note that some documentation has 21 and a1 + * write only. This is not true. + */ + pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */ + pic &= ~alpha_irq_mask; /* apply mask */ + pic &= 0xFFFB; /* mask out cascade & hibits */ + + while (pic) { + int j = ffz(~pic); + pic &= pic - 1; + handle_irq(j, j, regs); + } +} + +static void +rx164_device_interrupt(unsigned long vector, struct pt_regs *regs) +{ + unsigned long pld; + int i; + + /* Read the interrupt summary register. On Polaris, + * this is the DIRR register in PCI config space (offset 0x84) + */ + pld = 0; + pcibios_read_config_dword(0, 0, 0x84, (unsigned int *)&pld); + +#if 0 + printk("PLD 0x%lx\n", pld); +#endif + + if (pld & 0xffffffff00000000UL) pld &= 0x00000000ffffffffUL; + + /* + * Now for every possible bit set, work through them and call + * the appropriate interrupt handler. + */ + while (pld) { + i = ffz(~pld); + pld &= pld - 1; /* clear least bit set */ + if (i == 20) { + rx164_isa_device_interrupt(vector, regs); + } else { + handle_irq(16+i, 16+i, regs); + } + } +} + +static void +rx164_init_irq(void) +{ + unsigned int temp; + + STANDARD_INIT_IRQ_PROLOG; + + pcibios_write_config_dword(0, 0, 0x74, (~alpha_irq_mask >> 16)); + pcibios_read_config_dword(0, 0, 0x74, &temp); + + enable_irq(16 + 20); /* enable ISA interrupts */ + enable_irq(2); /* enable cascade */ +} +/* The RX164 changed its interrupt routing between pass1 and pass2... + * + * PASS1: + * + * Slot IDSEL INTA INTB INTC INTD + * 0 6 5 10 15 20 + * 1 7 4 9 14 19 + * 2 5 3 8 13 18 + * 3 9 2 7 12 17 + * 4 10 1 6 11 16 + * + * PASS2: + * Slot IDSEL INTA INTB INTC INTD + * 0 5 1 7 12 17 + * 1 6 2 8 13 18 + * 2 8 3 9 14 19 + * 3 9 4 10 15 20 + * 4 10 5 11 16 6 + * + */ + +/* + * IdSel + * 5 32 bit PCI option slot 0 + * 6 64 bit PCI option slot 1 + * 7 PCI-ISA bridge + * 7 64 bit PCI option slot 2 + * 9 32 bit PCI option slot 3 + * 10 PCI-PCI bridge + * + */ + +static int __init +rx164_map_irq(struct pci_dev *dev, int slot, int pin) +{ +#if 0 + char irq_tab_pass1[6][5] = { + /*INT INTA INTB INTC INTD */ + { 16+3, 16+3, 16+8, 16+13, 16+18}, /* IdSel 5, slot 2 */ + { 16+5, 16+5, 16+10, 16+15, 16+20}, /* IdSel 6, slot 0 */ + { 16+4, 16+4, 16+9, 16+14, 16+19}, /* IdSel 7, slot 1 */ + { -1, -1, -1, -1, -1}, /* IdSel 8, PCI/ISA bridge */ + { 16+2, 16+2, 16+7, 16+12, 16+17}, /* IdSel 9, slot 3 */ + { 16+1, 16+1, 16+6, 16+11, 16+16}, /* IdSel 10, slot 4 */ + }; +#endif + char irq_tab[6][5] = { + /*INT INTA INTB INTC INTD */ + { 16+0, 16+0, 16+6, 16+11, 16+16}, /* IdSel 5, slot 0 */ + { 16+1, 16+1, 16+7, 16+12, 16+17}, /* IdSel 6, slot 1 */ + { -1, -1, -1, -1, -1}, /* IdSel 7, PCI/ISA bridge */ + { 16+2, 16+2, 16+8, 16+13, 16+18}, /* IdSel 8, slot 2 */ + { 16+3, 16+3, 16+9, 16+14, 16+19}, /* IdSel 9, slot 3 */ + { 16+4, 16+4, 16+10, 16+15, 16+5}, /* IdSel 10, PCI-PCI */ + }; + const long min_idsel = 5, max_idsel = 10, irqs_per_slot = 5; + /* JRP - Need to figure out how to distinguish pass1 from pass2, + * and use the correct table... + */ + return COMMON_TABLE_LOOKUP; +} + +void __init +rx164_pci_fixup(void) +{ + layout_all_busses(DEFAULT_IO_BASE, DEFAULT_MEM_BASE); + common_pci_fixup(rx164_map_irq, common_swizzle); +} + + +/* + * The System Vector + */ + +struct alpha_machine_vector rx164_mv __initmv = { + vector_name: "RX164", + DO_EV5_MMU, + DO_DEFAULT_RTC, + DO_POLARIS_IO, + DO_POLARIS_BUS, + machine_check: polaris_machine_check, + max_dma_address: ALPHA_MAX_DMA_ADDRESS, + + nr_irqs: 40, + irq_probe_mask: _PROBE_MASK(40), + update_irq_hw: rx164_update_irq_hw, + ack_irq: generic_ack_irq, + device_interrupt: rx164_device_interrupt, + + init_arch: polaris_init_arch, + init_irq: rx164_init_irq, + init_pit: generic_init_pit, + pci_fixup: rx164_pci_fixup, + kill_arch: generic_kill_arch, +}; +ALIAS_MV(rx164) diff --git a/arch/alpha/kernel/sys_sio.c b/arch/alpha/kernel/sys_sio.c index b5aaf5d81..70d8720e9 100644 --- a/arch/alpha/kernel/sys_sio.c +++ b/arch/alpha/kernel/sys_sio.c @@ -232,7 +232,7 @@ noname_pci_fixup(void) * selected... :-( */ layout_all_busses(DEFAULT_IO_BASE, APECS_AND_LCA_DEFAULT_MEM_BASE); - sio_pci_fixup(noname_map_irq, 0x0b0a0f09); + sio_pci_fixup(noname_map_irq, 0x0b0a0f0d); sio_fixup_irq_levels(sio_collect_irq_levels()); enable_ide(0x26e); } diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index acbb76896..2f9363113 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -10,6 +10,8 @@ * 1995-03-26 Markus Kuhn * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 * precision CMOS clock update + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills * 1997-01-09 Adrian Sun * use interval timer if CONFIG_RTC=y * 1997-10-29 John Bowman (bowman@math.ualberta.ca) @@ -35,12 +37,7 @@ #include <linux/timex.h> #include "proto.h" - -#ifdef CONFIG_RTC -#define TIMER_IRQ 0 /* using pit for timer */ -#else -#define TIMER_IRQ 8 /* using rtc for timer */ -#endif +#include "irq.h" static int set_rtc_mmss(unsigned long); @@ -117,10 +114,10 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs) * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be * called as close as possible to 500 ms before the new second starts. */ - if (time_state != TIME_BAD + if ((time_status & STA_UNSYNC) == 0 && xtime.tv_sec > state.last_rtc_update + 660 - && xtime.tv_usec >= 500000 - (tick >> 1) - && xtime.tv_usec <= 500000 + (tick >> 1)) { + && xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 + && xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { int tmp = set_rtc_mmss(xtime.tv_sec); state.last_rtc_update = xtime.tv_sec - (tmp ? 600 : 0); } @@ -163,43 +160,28 @@ static inline unsigned long mktime(unsigned int year, unsigned int mon, * drivers depend on them being initialized (e.g., joystick driver). */ -/* It is (normally) only counter 0 that presents config problems, so - provide this support function to do the rest of the job. */ - -void inline -init_pit_rest(void) -{ -#if 0 - /* Leave refresh timer alone---nobody should depend on a - particular value anyway. */ - outb(0x54, 0x43); /* counter 1: refresh timer */ - outb(0x18, 0x41); -#endif - - outb(0xb6, 0x43); /* counter 2: speaker */ - outb(0x31, 0x42); - outb(0x13, 0x42); -} - #ifdef CONFIG_RTC -static inline void +void rtc_init_pit (void) { unsigned char control; - /* Setup interval timer if /dev/rtc is being used */ + /* Turn off RTC interrupts before /dev/rtc is initialized */ + control = CMOS_READ(RTC_CONTROL); + control &= ~(RTC_PIE | RTC_AIE | RTC_UIE); + CMOS_WRITE(control, RTC_CONTROL); + (void) CMOS_READ(RTC_INTR_FLAGS); + + request_region(0x40, 0x20, "timer"); /* reserve pit */ + + /* Setup interval timer. */ outb(0x34, 0x43); /* binary, mode 2, LSB/MSB, ch 0 */ outb(LATCH & 0xff, 0x40); /* LSB */ outb(LATCH >> 8, 0x40); /* MSB */ - request_region(0x40, 0x20, "timer"); /* reserve pit */ - - /* Turn off RTC interrupts before /dev/rtc is initialized */ - control = CMOS_READ(RTC_CONTROL); - control &= ~(RTC_PIE | RTC_AIE | RTC_UIE); - CMOS_WRITE(control, RTC_CONTROL); - CMOS_READ(RTC_INTR_FLAGS); - init_pit_rest(); + outb(0xb6, 0x43); /* pit counter 2: speaker */ + outb(0x31, 0x42); + outb(0x13, 0x42); } #endif @@ -208,7 +190,7 @@ generic_init_pit (void) { unsigned char x; - /* Reset periodic interrupt frequency. */ + /* Reset periodic interrupt frequency. */ x = CMOS_READ(RTC_FREQ_SELECT) & 0x3f; if (x != 0x26 && x != 0x19 && x != 0x06) { printk("Setting RTC_FREQ to 1024 Hz (%x)\n", x); @@ -223,36 +205,26 @@ generic_init_pit (void) x &= ~(RTC_AIE | RTC_UIE); CMOS_WRITE(x, RTC_CONTROL); } - CMOS_READ(RTC_INTR_FLAGS); + (void) CMOS_READ(RTC_INTR_FLAGS); request_region(RTC_PORT(0), 0x10, "timer"); /* reserve rtc */ - /* Turn off the PIT. */ - outb(0x36, 0x43); /* counter 0: system timer */ + outb(0x36, 0x43); /* pit counter 0: system timer */ outb(0x00, 0x40); outb(0x00, 0x40); - init_pit_rest(); + outb(0xb6, 0x43); /* pit counter 2: speaker */ + outb(0x31, 0x42); + outb(0x13, 0x42); } -/* This probably isn't Right, but it is what the old code did. */ -#if defined(CONFIG_RTC) -# define init_pit rtc_init_pit -#else -# define init_pit alpha_mv.init_pit -#endif - - void time_init(void) { - void (*irq_handler)(int, void *, struct pt_regs *); + void (*irq_handler)(int, void *, struct pt_regs *); unsigned int year, mon, day, hour, min, sec, cc1, cc2; unsigned long cycle_freq; - /* Initialize the timers. */ - init_pit(); - /* * The Linux interpretation of the CMOS clock register contents: * When the Update-In-Progress (UIP) flag goes from 1 to 0, the @@ -327,8 +299,8 @@ time_init(void) state.partial_tick = 0L; /* setup timer */ - irq_handler = timer_interrupt; - if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL)) + irq_handler = timer_interrupt; + if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL)) panic("Could not allocate timer IRQ!"); } @@ -383,9 +355,11 @@ do_settimeofday(struct timeval *tv) { cli(); xtime = *tv; - time_state = TIME_BAD; - time_maxerror = 0x70000000; - time_esterror = 0x70000000; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; sti(); } @@ -396,6 +370,9 @@ do_settimeofday(struct timeval *tv) * nowtime is written into the registers of the CMOS clock, it will * jump to the next second precisely 500 ms later. Check the Motorola * MC146818A or Dallas DS12887 data sheet for details. + * + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you won't notice until after reboot! */ static int set_rtc_mmss(unsigned long nowtime) @@ -437,8 +414,12 @@ set_rtc_mmss(unsigned long nowtime) } CMOS_WRITE(real_seconds,RTC_SECONDS); CMOS_WRITE(real_minutes,RTC_MINUTES); - } else - retval = -1; + } else { + printk(KERN_WARNING + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } /* The following flags have to be released exactly in this order, * otherwise the DS12887 (popular MC146818A clone with integrated diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile index e87f8b903..400adf0e1 100644 --- a/arch/alpha/lib/Makefile +++ b/arch/alpha/lib/Makefile @@ -7,7 +7,7 @@ OBJS = __divqu.o __remqu.o __divlu.o __remlu.o memset.o memcpy.o io.o \ strcat.o strcpy.o strncat.o strncpy.o stxcpy.o stxncpy.o \ strchr.o strrchr.o \ copy_user.o clear_user.o strncpy_from_user.o strlen_user.o \ - csum_ipv6_magic.o strcasecmp.o \ + csum_ipv6_magic.o strcasecmp.o semaphore.o \ srm_dispatch.o srm_fixup.o srm_puts.o srm_printk.o lib.a: $(OBJS) diff --git a/arch/alpha/lib/semaphore.S b/arch/alpha/lib/semaphore.S new file mode 100644 index 000000000..3dbeeec5f --- /dev/null +++ b/arch/alpha/lib/semaphore.S @@ -0,0 +1,183 @@ +/* + * linux/arch/alpha/lib/semaphore.S + * + * Copyright (C) 1999 Richard Henderson + */ + +/* + * The semaphore operations have a special calling sequence that + * allow us to do a simpler in-line version of them. These routines + * need to convert that sequence back into the C sequence when + * there is contention on the semaphore. + */ + + .set noat + .set noreorder + .align 4 + +/* __down_failed takes the semaphore in $24, clobbers $24 and $28. */ + + .globl __down_failed + .ent __down_failed +__down_failed: + ldgp $29,0($27) + lda $30, -20*8($30) + stq $28, 0*8($30) + stq $0, 1*8($30) + stq $1, 2*8($30) + stq $2, 3*8($30) + stq $3, 4*8($30) + stq $4, 5*8($30) + stq $5, 6*8($30) + stq $6, 7*8($30) + stq $7, 8*8($30) + stq $16, 9*8($30) + stq $17, 10*8($30) + stq $18, 11*8($30) + stq $19, 12*8($30) + stq $20, 13*8($30) + stq $21, 14*8($30) + stq $22, 15*8($30) + stq $23, 16*8($30) + stq $25, 17*8($30) + stq $26, 18*8($30) + .frame $30, 20*8, $28 + .prologue 1 + + mov $24, $16 + jsr __down + + ldq $28, 0*8($30) + ldq $0, 1*8($30) + ldq $1, 2*8($30) + ldq $2, 3*8($30) + ldq $3, 4*8($30) + ldq $4, 5*8($30) + ldq $5, 6*8($30) + ldq $6, 7*8($30) + ldq $7, 8*8($30) + ldq $16, 9*8($30) + ldq $17, 10*8($30) + ldq $18, 11*8($30) + ldq $19, 12*8($30) + ldq $20, 13*8($30) + ldq $21, 14*8($30) + ldq $22, 15*8($30) + ldq $23, 16*8($30) + ldq $25, 17*8($30) + ldq $26, 18*8($30) + lda $30, 20*8($30) + ret $31, ($28), 0 + .end __down_failed + +/* __down_failed_interruptible takes the semaphore in $24, + clobbers $28, returns success in $24. */ + + .globl __down_failed_interruptible + .ent __down_failed_interruptible +__down_failed_interruptible: + ldgp $29,0($27) + lda $30, -20*8($30) + stq $28, 0*8($30) + stq $0, 1*8($30) + stq $1, 2*8($30) + stq $2, 3*8($30) + stq $3, 4*8($30) + stq $4, 5*8($30) + stq $5, 6*8($30) + stq $6, 7*8($30) + stq $7, 8*8($30) + stq $16, 9*8($30) + stq $17, 10*8($30) + stq $18, 11*8($30) + stq $19, 12*8($30) + stq $20, 13*8($30) + stq $21, 14*8($30) + stq $22, 15*8($30) + stq $23, 16*8($30) + stq $25, 17*8($30) + stq $26, 18*8($30) + .frame $30, 20*8, $28 + .prologue 1 + + mov $24, $16 + jsr __down_interruptible + mov $0, $24 + + ldq $28, 0*8($30) + ldq $0, 1*8($30) + ldq $1, 2*8($30) + ldq $2, 3*8($30) + ldq $3, 4*8($30) + ldq $4, 5*8($30) + ldq $5, 6*8($30) + ldq $6, 7*8($30) + ldq $7, 8*8($30) + ldq $16, 9*8($30) + ldq $17, 10*8($30) + ldq $18, 11*8($30) + ldq $19, 12*8($30) + ldq $20, 13*8($30) + ldq $21, 14*8($30) + ldq $22, 15*8($30) + ldq $23, 16*8($30) + ldq $25, 17*8($30) + ldq $26, 18*8($30) + lda $30, 20*8($30) + ret $31, ($28), 0 + .end __down_failed_interruptible + +/* __up_wakeup takes the semaphore in $24, clobbers $24 and $28. */ + + .globl __up_wakeup + .ent __up_wakeup +__up_wakeup: + ldgp $29,0($27) + lda $30, -20*8($30) + stq $28, 0*8($30) + stq $0, 1*8($30) + stq $1, 2*8($30) + stq $2, 3*8($30) + stq $3, 4*8($30) + stq $4, 5*8($30) + stq $5, 6*8($30) + stq $6, 7*8($30) + stq $7, 8*8($30) + stq $16, 9*8($30) + stq $17, 10*8($30) + stq $18, 11*8($30) + stq $19, 12*8($30) + stq $20, 13*8($30) + stq $21, 14*8($30) + stq $22, 15*8($30) + stq $23, 16*8($30) + stq $25, 17*8($30) + stq $26, 18*8($30) + .frame $30, 20*8, $28 + .prologue 1 + + mov $24, $16 + jsr __up + + ldq $28, 0*8($30) + ldq $0, 1*8($30) + ldq $1, 2*8($30) + ldq $2, 3*8($30) + ldq $3, 4*8($30) + ldq $4, 5*8($30) + ldq $5, 6*8($30) + ldq $6, 7*8($30) + ldq $7, 8*8($30) + ldq $16, 9*8($30) + ldq $17, 10*8($30) + ldq $18, 11*8($30) + ldq $19, 12*8($30) + ldq $20, 13*8($30) + ldq $21, 14*8($30) + ldq $22, 15*8($30) + ldq $23, 16*8($30) + ldq $25, 17*8($30) + ldq $26, 18*8($30) + lda $30, 20*8($30) + ret $31, ($28), 0 + .end __up_wakeup diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 5ef8ea75e..967ee6766 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -120,6 +120,7 @@ COMPRESSED_HEAD = head-nexuspci.o endif ifeq ($(CONFIG_ARCH_VNC),y) +TEXTADDR = 0xC000C000 MACHINE = vnc ARCHDIR = vnc endif @@ -141,7 +142,7 @@ OBJCOPY = $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S OBJDUMP = $(CROSS_COMPILE)objdump CPP = $(CC) -E ARCHCC := $(word 1,$(CC)) -GCCLIB := `$(ARCHCC) $(CFLAGS_PROC) --print-libgcc-file-name` +GCCLIB := `$(CC) $(CFLAGS_PROC) --print-libgcc-file-name` #GCCARCH := -B/usr/bin/arm-linuxelf- HOSTCFLAGS := $(CFLAGS:-fomit-frame-pointer=) ifeq ($(CONFIG_FRAME_POINTER),y) @@ -160,10 +161,10 @@ LIBS := arch/arm/lib/lib.a $(LIBS) $(GCCLIB) BLOCK_DRIVERS := drivers/block/block.a CDROM_DRIVERS := drivers/cdrom/cdrom.a ifeq ($(CONFIG_FB),y) -CHAR_DRIVERS := arch/arm/drivers/char1/char.a +CHAR_DRIVERS := arch/arm/drivers/char1/char1.a drivers/char/char.a arch/arm/drivers/char1/char1.a else ifeq ($(CONFIG_VGA_CONSOLE),y) -CHAR_DRIVERS := arch/arm/drivers/char1/char.a +CHAR_DRIVERS := arch/arm/drivers/char1/char1.a drivers/char/char.a arch/arm/drivers/char1/char1.a else CHAR_DRIVERS := arch/arm/drivers/char/char.a endif diff --git a/arch/arm/config.in b/arch/arm/config.in index 2b59a964c..467218db7 100644 --- a/arch/arm/config.in +++ b/arch/arm/config.in @@ -38,15 +38,22 @@ if [ "$CONFIG_ARCH_TBOX" = "y" ]; then define_bool CONFIG_BUS_I2C y fi -# These machines have PCI/may have PCI +# These machines always have PCI if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o \ "$CONFIG_ARCH_VNC" = "y" ]; then define_bool CONFIG_PCI y +fi +if [ "$CONFIG_ARCH_EBSA285" = "y" ]; then + bool "PCI support" CONFIG_PCI +fi + +# These machines have ISA-DMA +if [ "$CONFIG_CATS" = "y" -o \ + "$CONFIG_ARCH_VNC" = "y" ]; then + define_bool CONFIG_ISA_DMA y else - if [ "$CONFIG_ARCH_EBSA285" = "y" ]; then - bool "PCI support" CONFIG_PCI - fi + define_bool CONFIG_ISA_DMA n fi # Figure out whether this system uses 26-bit or 32-bit CPUs. Nobody has @@ -122,6 +129,11 @@ if [ "$CONFIG_PARPORT" != "n" ]; then fi fi fi +if [ "$CONFIG_ARCH_EBSA285" = "y" -o \ + "$CONFIG_ARCH_EBSA110" = "y" -o \ + "$CONFIG_ARCH_VNC" = "y" ]; then + string 'Initial kernel command string' CONFIG_CMDLINE +fi endmenu source drivers/pnp/Config.in @@ -132,16 +144,20 @@ if [ "$CONFIG_ARCH_ACORN" = "y" ]; then source drivers/acorn/block/Config.in fi -source arch/arm/drivers/char/Config.in +if [ "$CONFIG_VGA_CONSOLE" = "n" -a "$CONFIG_FB" = "n" ]; then + source arch/arm/drivers/char/Config.in +else + source drivers/char/Config.in +fi +if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + source drivers/acorn/char/Config.in +fi if [ "$CONFIG_VT" = "y" ]; then mainmenu_option next_comment comment 'Console drivers' if [ "$CONFIG_ARCH_ACORN" != "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then bool 'VGA text console' CONFIG_VGA_CONSOLE - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'Video mode selection support' CONFIG_VIDEO_SELECT - fi fi bool 'Support Frame buffer devices' CONFIG_FB source drivers/video/Config.in @@ -195,8 +211,6 @@ fi source fs/Config.in -source fs/nls/Config.in - mainmenu_option next_comment comment 'Kernel hacking' diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 542ca995a..23b2c1267 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -10,44 +10,54 @@ ENTRY_OBJ = entry-$(PROCESSOR).o O_TARGET := kernel.o O_OBJS := $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o setup.o \ - signal.o sys_arm.o time.o traps.o fiq.o + signal.o sys_arm.o time.o traps.o + +DMA_OBJS_arc = dma-arc.o +DMA_OBJS_a5k = dma-a5k.o +DMA_OBJS_rpc = dma-rpc.o +DMA_OBJS_ebsa110 = dma-dummy.o +DMA_OBJS_ebsa285 = dma-ebsa285.o +DMA_OBJS_nexuspci = +DMA_OBJS_vnc = dma-vnc.o + +O_OBJS_arc = ecard.o iic.o fiq.o oldlatches.o +O_OBJS_a5k = ecard.o iic.o fiq.o +O_OBJS_rpc = ecard.o iic.o fiq.o +O_OBJS_ebsa110 = leds-ebsa110.o +O_OBJS_ebsa285 = leds-ebsa285.o hw-ebsa285.o +O_OBJS_nexuspci = +O_OBJS_vnc = leds-ebsa285.o hw-vnc.o all: lib kernel.o $(HEAD_OBJ) init_task.o ifeq ($(CONFIG_MODULES),y) -OX_OBJS = armksyms.o + OX_OBJS = armksyms.o else -O_OBJS += armksyms.o + O_OBJS += armksyms.o endif -ifdef CONFIG_ARCH_ACORN - O_OBJS += ecard.o iic.o - ifdef CONFIG_ARCH_ARC - O_OBJS += oldlatches.o +ifeq ($(MACHINE),nexuspci) + ifdef CONFIG_PCI + O_OBJS += plx9080.o endif - O_OBJS += dma-$(MACHINE).o - OX_OBJS += dma.o -endif - -ifeq ($(MACHINE),ebsa110) - O_OBJS += dma-dummy.o leds-ebsa110.o -endif - -ifeq ($(MACHINE),ebsa285) - OX_OBJS += dma.o - O_OBJS += dma-ebsa285.o leds-ebsa285.o +else ifdef CONFIG_PCI O_OBJS += dec21285.o endif endif -ifeq ($(MACHINE),nexuspci) - O_OBJS += dma-dummy.o - ifdef CONFIG_PCI - O_OBJS += plx9080.o +ifneq ($(DMA_OBJS_$(MACHINE)),) + OX_OBJS += dma.o + O_OBJS += $(DMA_OBJS_$(MACHINE)) + ifeq ($(CONFIG_ISA_DMA),y) + O_OBJS += dma-isa.o endif +else + O_OBJS += dma-dummy.o endif +O_OBJS += $(O_OBJS_$(MACHINE)) + $(HEAD_OBJ): $(HEAD_OBJ:.o=.S) $(CC) -D__ASSEMBLY__ -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@ diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S index 751f9616a..46f71fa92 100644 --- a/arch/arm/kernel/calls.S +++ b/arch/arm/kernel/calls.S @@ -9,7 +9,7 @@ #define NR_syscalls 256 #else -/* 0 */ .long SYMBOL_NAME(sys_setup) +/* 0 */ .long SYMBOL_NAME(sys_ni_syscall) .long SYMBOL_NAME(sys_exit) .long SYMBOL_NAME(sys_fork_wrapper) .long SYMBOL_NAME(sys_read) diff --git a/arch/arm/kernel/dec21285.c b/arch/arm/kernel/dec21285.c index de4861b82..aa66ee04a 100644 --- a/arch/arm/kernel/dec21285.c +++ b/arch/arm/kernel/dec21285.c @@ -3,14 +3,23 @@ * * Copyright (C) 1998 Russell King, Phil Blundell */ +#include <linux/sched.h> #include <linux/kernel.h> #include <linux/pci.h> +#include <linux/ptrace.h> +#include <linux/interrupt.h> #include <linux/init.h> +#include <asm/irq.h> #include <asm/system.h> #define MAX_SLOTS 20 +extern void pcibios_fixup_ebsa285(struct pci_dev *dev); +extern void pcibios_init_ebsa285(void); +extern void pcibios_fixup_vnc(struct pci_dev *dev); +extern void pcibios_init_vnc(void); + int pcibios_present(void) { @@ -24,12 +33,11 @@ pcibios_base_address(unsigned char bus, unsigned char dev_fn) int slot = PCI_SLOT(dev_fn); if (slot < MAX_SLOTS) - return 0xf8c00000 + (slot << 11); + return 0xf8c00000 + (slot << 11) + (PCI_FUNC(dev_fn) << 8); else return 0; - } else { + } else return 0xf9000000 | (bus << 16) | (dev_fn << 8); - } } int @@ -119,56 +127,35 @@ pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, return PCIBIOS_SUCCESSFUL; } -static int irqmap_ebsa[] __initdata = { 9, 8, 18, 11 }; -static int irqmap_cats[] __initdata = { 18, 8, 9, 11 }; - -__initfunc(static int ebsa_irqval(struct pci_dev *dev)) +__initfunc(void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set)) { - unsigned char pin; - - pcibios_read_config_byte(dev->bus->number, - dev->devfn, - PCI_INTERRUPT_PIN, - &pin); - - return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3]; + unsigned short cmd; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd = (cmd & ~clear) | set; + pci_write_config_word(dev, PCI_COMMAND, cmd); } -__initfunc(static int cats_irqval(struct pci_dev *dev)) +__initfunc(void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr)) { - if (dev->irq >= 128) - return 32 + (dev->irq & 0x1f); - - switch (dev->irq) { - case 1: - case 2: - case 3: - case 4: - return irqmap_cats[dev->irq - 1]; - case 0: - return 0; - } + int reg = PCI_BASE_ADDRESS_0 + (idx << 2); - printk("PCI: device %02x:%02x has unknown irq line %x\n", - dev->bus->number, dev->devfn, dev->irq); - return 0; + pci_write_config_dword(dev, reg, addr); + pci_read_config_dword(dev, reg, &addr); + + dev->base_address[idx] = addr; } __initfunc(void pcibios_fixup(void)) { struct pci_dev *dev; - unsigned char cmd; for (dev = pci_devices; dev; dev = dev->next) { - /* sort out the irq mapping for this device */ - switch (machine_type) { - case MACH_TYPE_EBSA285: - dev->irq = ebsa_irqval(dev); - break; - case MACH_TYPE_CATS: - dev->irq = cats_irqval(dev); - break; - } + if (machine_is_ebsa285() || machine_is_cats()) + pcibios_fixup_ebsa285(dev); + if (machine_is_netwinder()) + pcibios_fixup_vnc(dev); + pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_LINE, dev->irq); @@ -176,34 +163,19 @@ __initfunc(void pcibios_fixup(void)) "PCI: %02x:%02x [%04x/%04x] on irq %d\n", dev->bus->number, dev->devfn, dev->vendor, dev->device, dev->irq); - - /* Turn on bus mastering - boot loader doesn't - * - perhaps it should! - dag - */ - pcibios_read_config_byte(dev->bus->number, dev->devfn, - PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_MASTER; - pcibios_write_config_byte(dev->bus->number, dev->devfn, - PCI_COMMAND, cmd); } + if (machine_is_netwinder()) + hw_init(); } __initfunc(void pcibios_init(void)) { - int rev; - - rev = *(unsigned char *)0xfe000008; - printk("DEC21285 PCI revision %02X\n", rev); - - /* - * Map our SDRAM at a known address in PCI space, just in case - * the firmware had other ideas. Using a nonzero base is slightly - * bizarre but apparently necessary to avoid problems with some - * video cards. - * - * We should really only do this if we are the configuration master. - */ - *((unsigned long *)0xfe000018) = 0x10000000; + if (machine_is_ebsa285() || machine_is_cats()) + pcibios_init_ebsa285(); + if (machine_is_netwinder()) + pcibios_init_vnc(); + + printk("DEC21285 PCI revision %02X\n", *(unsigned char *)0xfe000008); } __initfunc(void pcibios_fixup_bus(struct pci_bus *bus)) diff --git a/arch/arm/kernel/dma-a5k.c b/arch/arm/kernel/dma-a5k.c index 50ee42435..18bbf0c9c 100644 --- a/arch/arm/kernel/dma-a5k.c +++ b/arch/arm/kernel/dma-a5k.c @@ -16,7 +16,7 @@ #include "dma.h" -static struct fiq_handler fh = { "floppydma", NULL }; +static struct fiq_handler fh = { NULL, "floppydma", NULL, NULL }; int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_id) { diff --git a/arch/arm/kernel/dma-ebsa285.c b/arch/arm/kernel/dma-ebsa285.c index 74af95584..f1c42dac2 100644 --- a/arch/arm/kernel/dma-ebsa285.c +++ b/arch/arm/kernel/dma-ebsa285.c @@ -4,6 +4,9 @@ * Copyright (C) 1998 Phil Blundell * * DMA functions specific to EBSA-285/CATS architectures + * + * Changelog: + * 09/11/1998 RMK Split out ISA DMA functions to dma-isa.c */ #include <linux/config.h> @@ -19,44 +22,19 @@ #include <asm/hardware.h> #include "dma.h" - -/* 8237 DMA controllers */ -#define IO_DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */ -#define IO_DMA2_BASE 0xC0 /* 16 bit master DMA, ch 4(=slave input)..7 */ - -/* 8237 DMA controller registers */ -#define DMA1_CMD_REG 0x08 /* command register (w) */ -#define DMA1_STAT_REG 0x08 /* status register (r) */ -#define DMA1_REQ_REG 0x09 /* request register (w) */ -#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */ -#define DMA1_MODE_REG 0x0B /* mode register (w) */ -#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */ -#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ -#define DMA1_RESET_REG 0x0D /* Master Clear (w) */ -#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ -#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ - -#define DMA2_CMD_REG 0xD0 /* command register (w) */ -#define DMA2_STAT_REG 0xD0 /* status register (r) */ -#define DMA2_REQ_REG 0xD2 /* request register (w) */ -#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */ -#define DMA2_MODE_REG 0xD6 /* mode register (w) */ -#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */ -#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ -#define DMA2_RESET_REG 0xDA /* Master Clear (w) */ -#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ -#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ +#include "dma-isa.h" int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name) { - /* 21285 internal channels */ - if (channel == 0 || channel == 1) + switch (channel) { + case 0: + case 1: /* 21285 internal channels */ return 0; - /* ISA channels */ -// if (machine_is_cats() && ((channel >= 2 && channel <= 5) || -// (channel >= 7 && channel <= 9))) -// return 0; + case 2 ... 9: + if (machine_is_cats()) + return isa_request_dma(channel - 2, dma, dev_name); + } return -EINVAL; } @@ -75,14 +53,9 @@ int arch_get_dma_residue(dmach_t channel, dma_t *dma) case 1: break; #ifdef CONFIG_CATS - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: + case 2 ... 9: + if (machine_is_cats()) + residue = isa_get_dma_residue(channel - 2); #endif } return residue; @@ -98,56 +71,9 @@ void arch_enable_dma(dmach_t channel, dma_t *dma) */ break; #ifdef CONFIG_CATS - case 2: - case 3: - case 4: - case 5: - case 7: - case 8: - case 9: - if (dma->invalid) { - static unsigned char dma_page[] = { 0x87, 0x83, 0x81, 0x82, - 0x00, 0x8b, 0x89, 0x8a }; - unsigned long int address = dma->buf.address, - length = dma->buf.length - 1; - outb(address >> 24, dma_page[channel - DMA_ISA_BASE] | 0x400); - outb(address >> 16, dma_page[channel - DMA_ISA_BASE]); - if (channel >= DMA_ISA_BASE + 5) { - outb(0, DMA2_CLEAR_FF_REG); - outb(address >> 1, - IO_DMA2_BASE + ((channel - DMA_ISA_BASE - 4) << 2)); - outb(address >> 9, - IO_DMA2_BASE + ((channel - DMA_ISA_BASE - 4) << 2)); - outb((length >> 1) & 0xfe, - IO_DMA2_BASE + 1 + ((channel - DMA_ISA_BASE - 4) << 2)); - outb(length >> 9, - IO_DMA2_BASE + 1 + ((channel - DMA_ISA_BASE - 4) << 2)); - outb(dma->dma_mode | (channel - DMA_ISA_BASE - 4), DMA2_MODE_REG); - } else { - outb(0, DMA1_CLEAR_FF_REG); - outb(address >> 0, IO_DMA1_BASE + ((channel - DMA_ISA_BASE) << 1)); - outb(address >> 8, IO_DMA1_BASE + ((channel - DMA_ISA_BASE) << 1)); - outb(length >> 0, - IO_DMA1_BASE + 1 + ((channel - DMA_ISA_BASE) << 1)); - outb(length >> 8, - IO_DMA1_BASE + 1 + ((channel - DMA_ISA_BASE) << 1)); - outb(dma->dma_mode | (channel - DMA_ISA_BASE), DMA1_MODE_REG); - } - switch (dma->dma_mode) { - case DMA_MODE_READ: - dma_cache_inv(__bus_to_virt(address), length + 1); - break; - case DMA_MODE_WRITE: - dma_cache_wback(__bus_to_virt(address), length + 1); - break; - } - dma->invalid = 0; - } - - if (channel >= DMA_ISA_BASE + 5) - outb(channel - DMA_ISA_BASE - 4, DMA2_MASK_REG); - else - outb(channel - DMA_ISA_BASE, DMA1_MASK_REG); + case 2 ... 9: + if (machine_is_cats()) + isa_enable_dma(channel - 2, dma); #endif } } @@ -162,18 +88,9 @@ void arch_disable_dma(dmach_t channel, dma_t *dma) */ break; #ifdef CONFIG_CATS - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - if (channel >= DMA_ISA_BASE + 5) - outb(channel - DMA_ISA_BASE, DMA2_MASK_REG); - else - outb((channel - DMA_ISA_BASE) | 4, DMA1_MASK_REG); + case 2 ... 9: + if (machine_is_cats()) + isa_disable_dma(channel - 2, dma); #endif } } diff --git a/arch/arm/kernel/dma-isa.c b/arch/arm/kernel/dma-isa.c new file mode 100644 index 000000000..bdf7c6147 --- /dev/null +++ b/arch/arm/kernel/dma-isa.c @@ -0,0 +1,107 @@ +/* + * arch/arm/kernel/dma-isa.c: ISA DMA primitives + * + * Copyright (C) Russell King + * + * Taken from various sources, including: + * linux/include/asm/dma.h: Defines for using and allocating dma channels. + * Written by Hennus Bergman, 1992. + * High DMA channel support & info by Hannu Savolainen and John Boyd, Nov. 1992. + * arch/arm/kernel/dma-ebsa285.c + * Copyright (C) 1998 Phil Blundell + */ +#include <linux/sched.h> + +#include <asm/dma.h> +#include <asm/io.h> + +#include "dma.h" +#include "dma-isa.h" + +#define ISA_DMA_MASK 0 +#define ISA_DMA_MODE 1 +#define ISA_DMA_CLRFF 2 +#define ISA_DMA_PGHI 3 +#define ISA_DMA_PGLO 4 +#define ISA_DMA_ADDR 5 +#define ISA_DMA_COUNT 6 + +static unsigned int isa_dma_port[8][7] = { + /* MASK MODE CLRFF PAGE_HI PAGE_LO ADDR COUNT */ + { 0x0a, 0x0b, 0x0c, 0x487, 0x087, 0x00, 0x01 }, + { 0x0a, 0x0b, 0x0c, 0x483, 0x083, 0x02, 0x03 }, + { 0x0a, 0x0b, 0x0c, 0x481, 0x081, 0x04, 0x05 }, + { 0x0a, 0x0b, 0x0c, 0x482, 0x082, 0x06, 0x07 }, + { 0xd4, 0xd6, 0xd8, 0x000, 0x000, 0xc0, 0xc2 }, + { 0xd4, 0xd6, 0xd8, 0x48b, 0x08b, 0xc4, 0xc6 }, + { 0xd4, 0xd6, 0xd8, 0x489, 0x089, 0xc8, 0xca }, + { 0xd4, 0xd6, 0xd8, 0x48a, 0x08a, 0xcc, 0xce } +}; + +int isa_request_dma(int channel, dma_t *dma, const char *dev_name) +{ + if (channel != 4) + return 0; + + return -EINVAL; +} + +void isa_free_dma(int channel, dma_t *dma) +{ + /* nothing to do */ +} + +int isa_get_dma_residue(int channel, dma_t *dma) +{ + unsigned int io_port = isa_dma_port[channel][ISA_DMA_COUNT]; + int count; + + count = 1 + inb(io_port) + (inb(io_port) << 8); + + return channel < 4 ? count : (count << 1); +} + +void isa_enable_dma(int channel, dma_t *dma) +{ + unsigned long address, length; + + if (dma->invalid) { + address = dma->buf.address; + length = dma->buf.length - 1; + + outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]); + outb(address >> 16, isa_dma_port[channel][ISA_DMA_PGLO]); + + if (channel >= 4) { + address >>= 1; + length = (length >> 1) & 0xfe; /* why &0xfe? */ + } + + outb(0, isa_dma_port[channel][ISA_DMA_CLRFF]); + + outb(address, isa_dma_port[channel][ISA_DMA_ADDR]); + outb(address >> 8, isa_dma_port[channel][ISA_DMA_ADDR]); + + outb(length, isa_dma_port[channel][ISA_DMA_COUNT]); + outb(length >> 8, isa_dma_port[channel][ISA_DMA_COUNT]); + + outb(dma->dma_mode | (channel & 3), isa_dma_port[channel][ISA_DMA_MODE]); + + switch (dma->dma_mode) { + case DMA_MODE_READ: + dma_cache_inv(__bus_to_virt(dma->buf.address), dma->buf.length); + break; + + case DMA_MODE_WRITE: + dma_cache_wback(__bus_to_virt(dma->buf.address), dma->buf.length); + break; + } + dma->invalid = 0; + } + outb(channel & 3, isa_dma_port[channel][ISA_DMA_MASK]); +} + +void isa_disable_dma(int channel, dma_t *dma) +{ + outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]); +} diff --git a/arch/arm/kernel/dma-isa.h b/arch/arm/kernel/dma-isa.h new file mode 100644 index 000000000..3fcbdb3c7 --- /dev/null +++ b/arch/arm/kernel/dma-isa.h @@ -0,0 +1,25 @@ +/* + * Request an ISA DMA channel + */ +int isa_request_dma(int channel, dma_t *dma, const char *dev_name); + +/* + * Free an ISA DMA channel + */ +void isa_free_dma(int channel, dma_t *dma); + +/* + * Get ISA DMA channel residue + */ +int isa_get_dma_residue(int channel, dma_t *dma); + +/* + * Enable (and set up) an ISA DMA channel + */ +void isa_enable_dma(int channel, dma_t *dma); + +/* + * Disable an ISA DMA channel + */ +void isa_disable_dma(int channel, dma_t *dma); + diff --git a/arch/arm/kernel/dma-vnc.c b/arch/arm/kernel/dma-vnc.c new file mode 100644 index 000000000..132fa627a --- /dev/null +++ b/arch/arm/kernel/dma-vnc.c @@ -0,0 +1,51 @@ +/* + * arch/arm/kernel/dma-vnc.c + * + * Copyright (C) 1998 Russell King + */ +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/mman.h> +#include <linux/init.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/hardware.h> + +#include "dma.h" +#include "dma-isa.h" + +int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name) +{ + if (channel < 8) + return isa_request_dma(channel, dma, dev_name); + return -EINVAL; +} + +void arch_free_dma(dmach_t channel, dma_t *dma) +{ + isa_free_dma(channel, dma); +} + +int arch_get_dma_residue(dmach_t channel, dma_t *dma) +{ + return isa_get_dma_residue(channel, dma); +} + +void arch_enable_dma(dmach_t channel, dma_t *dma) +{ + isa_enable_dma(channel, dma); +} + +void arch_disable_dma(dmach_t channel, dma_t *dma) +{ + isa_disable_dma(channel, dma); +} + +__initfunc(void arch_dma_init(dma_t *dma)) +{ + /* Nothing to do */ +} + diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c index 592cc979c..a164073ae 100644 --- a/arch/arm/kernel/dma.c +++ b/arch/arm/kernel/dma.c @@ -26,6 +26,26 @@ #include <asm/hardware.h> #include <asm/io.h> #include <asm/dma.h> +#include <asm/spinlock.h> + + +/* A note on resource allocation: + * + * All drivers needing DMA channels, should allocate and release them + * through the public routines `request_dma()' and `free_dma()'. + * + * In order to avoid problems, all processes should allocate resources in + * the same sequence and release them in the reverse order. + * + * So, when allocating DMAs and IRQs, first allocate the IRQ, then the DMA. + * When releasing them, first release the DMA, then release the IRQ. + * If you don't, you may cause allocation requests to fail unnecessarily. + * This doesn't really matter now, but it will once we get real semaphores + * in the kernel. + */ + + +spinlock_t dma_spin_lock = SPIN_LOCK_UNLOCKED; #include "dma.h" diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c index f7d204787..fe1c75f5c 100644 --- a/arch/arm/kernel/ecard.c +++ b/arch/arm/kernel/ecard.c @@ -3,7 +3,7 @@ * * Find all installed expansion cards, and handle interrupts from them. * - * Copyright 1995,1996,1997 Russell King + * Copyright 1995-1998 Russell King * * Created from information from Acorns RiscOS3 PRMs * @@ -14,6 +14,7 @@ * 29-Sep-1997 RMK Expansion card interrupt hardware not being re-enabled on reset from * Linux. (Caused cards not to respond under RiscOS without hard reset). * 15-Feb-1998 RMK Added DMA support + * 12-Sep-1998 RMK Added EASI support */ #define ECARD_C @@ -79,7 +80,7 @@ static signed char irqno_to_expcard[16]; static unsigned int ecard_numcards, ecard_numirqcards; static unsigned int have_expmask; -static void ecard_def_irq_enable (ecard_t *ec, int irqnr) +static void ecard_def_irq_enable(ecard_t *ec, int irqnr) { #ifdef HAS_EXPMASK if (irqnr < 4 && have_expmask) { @@ -89,7 +90,7 @@ static void ecard_def_irq_enable (ecard_t *ec, int irqnr) #endif } -static void ecard_def_irq_disable (ecard_t *ec, int irqnr) +static void ecard_def_irq_disable(ecard_t *ec, int irqnr) { #ifdef HAS_EXPMASK if (irqnr < 4 && have_expmask) { @@ -99,14 +100,14 @@ static void ecard_def_irq_disable (ecard_t *ec, int irqnr) #endif } -static void ecard_def_fiq_enable (ecard_t *ec, int fiqnr) +static void ecard_def_fiq_enable(ecard_t *ec, int fiqnr) { - panic ("ecard_def_fiq_enable called - impossible"); + panic("ecard_def_fiq_enable called - impossible"); } -static void ecard_def_fiq_disable (ecard_t *ec, int fiqnr) +static void ecard_def_fiq_disable(ecard_t *ec, int fiqnr) { - panic ("ecard_def_fiq_disable called - impossible"); + panic("ecard_def_fiq_disable called - impossible"); } static expansioncard_ops_t ecard_default_ops = { @@ -122,7 +123,7 @@ static expansioncard_ops_t ecard_default_ops = { * * They are not meant to be called directly, but via enable/disable_irq. */ -void ecard_enableirq (unsigned int irqnr) +void ecard_enableirq(unsigned int irqnr) { irqnr &= 7; if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) { @@ -132,14 +133,14 @@ void ecard_enableirq (unsigned int irqnr) ec->ops = &ecard_default_ops; if (ec->claimed && ec->ops->irqenable) - ec->ops->irqenable (ec, irqnr); + ec->ops->irqenable(ec, irqnr); else - printk (KERN_ERR "ecard: rejecting request to " + printk(KERN_ERR "ecard: rejecting request to " "enable IRQs for %d\n", irqnr); } } -void ecard_disableirq (unsigned int irqnr) +void ecard_disableirq(unsigned int irqnr) { irqnr &= 7; if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) { @@ -149,11 +150,11 @@ void ecard_disableirq (unsigned int irqnr) ec->ops = &ecard_default_ops; if (ec->ops && ec->ops->irqdisable) - ec->ops->irqdisable (ec, irqnr); + ec->ops->irqdisable(ec, irqnr); } } -void ecard_enablefiq (unsigned int fiqnr) +void ecard_enablefiq(unsigned int fiqnr) { fiqnr &= 7; if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) { @@ -163,14 +164,14 @@ void ecard_enablefiq (unsigned int fiqnr) ec->ops = &ecard_default_ops; if (ec->claimed && ec->ops->fiqenable) - ec->ops->fiqenable (ec, fiqnr); + ec->ops->fiqenable(ec, fiqnr); else - printk (KERN_ERR "ecard: rejecting request to " + printk(KERN_ERR "ecard: rejecting request to " "enable FIQs for %d\n", fiqnr); } } -void ecard_disablefiq (unsigned int fiqnr) +void ecard_disablefiq(unsigned int fiqnr) { fiqnr &= 7; if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) { @@ -180,7 +181,7 @@ void ecard_disablefiq (unsigned int fiqnr) ec->ops = &ecard_default_ops; if (ec->ops->fiqdisable) - ec->ops->fiqdisable (ec, fiqnr); + ec->ops->fiqdisable(ec, fiqnr); } } @@ -198,8 +199,27 @@ static void ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs) } } cli(); - if (called == 0) - printk (KERN_WARNING "Wild interrupt from backplane?\n"); + if (called == 0) { + static int last, lockup; + + if (last == jiffies) { + lockup += 1; + if (lockup > 1000000) { + printk(KERN_ERR "\nInterrupt lockup detected - disabling expansion card IRQs\n"); + disable_irq(intr_no); + printk("Expansion card IRQ state:\n"); + for (i = 0; i < num_cards; i++) + printk(" %d: %sclaimed, irqaddr = %p, irqmask = %X, status=%X\n", expcard[i].irq - 32, + expcard[i].claimed ? "" : "not", expcard[i].irqaddr, expcard[i].irqmask, *expcard[i].irqaddr); + } + } else + lockup = 0; + + if (!last || time_after(jiffies, last + 5*HZ)) { + last = jiffies; + printk(KERN_ERR "\nUnrecognised interrupt from backplane\n"); + } + } } #ifdef HAS_EXPMASK @@ -214,7 +234,7 @@ static unsigned char first_set[] = 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00 }; -static void ecard_irq_expmask (int intr_no, void *dev_id, struct pt_regs *regs) +static void ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs) { const unsigned int statusmask = 15; unsigned int status; @@ -239,22 +259,22 @@ again: */ oldexpmask = have_expmask; EXPMASK_ENABLE = (have_expmask &= priority_masks[irqno]); - sti (); - do_ecard_IRQ (ec->irq, regs); - cli (); + sti(); + do_ecard_IRQ(ec->irq, regs); + cli(); EXPMASK_ENABLE = have_expmask = oldexpmask; status = EXPMASK_STATUS & statusmask; if (status) goto again; } else { - printk (KERN_WARNING "card%d: interrupt from unclaimed card???\n", irqno); + printk(KERN_WARNING "card%d: interrupt from unclaimed card???\n", irqno); EXPMASK_ENABLE = (have_expmask &= ~(1 << irqno)); } } else - printk (KERN_WARNING "Wild interrupt from backplane (masks)\n"); + printk(KERN_WARNING "Wild interrupt from backplane (masks)\n"); } -static int ecard_checkirqhw (void) +static int ecard_checkirqhw(void) { int found; @@ -267,7 +287,7 @@ static int ecard_checkirqhw (void) } #endif -static void ecard_readbytes (void *addr, ecard_t *ec, int off, int len, int useld) +static void ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld) { extern int ecard_loader_read(int off, volatile unsigned int pa, loader_t loader); unsigned char *a = (unsigned char *)addr; @@ -287,7 +307,7 @@ static void ecard_readbytes (void *addr, ecard_t *ec, int off, int len, int usel * If we require a low address or address 0, then reset, and start again... */ if (!off || lowaddress > laddr) { - outb (0, ec->podaddr); + outb(0, ec->podaddr); lowaddress = 0; } while (lowaddress <= laddr) { @@ -314,15 +334,136 @@ static void ecard_readbytes (void *addr, ecard_t *ec, int off, int len, int usel } } +static int ecard_prints(char *buffer, ecard_t *ec) +{ + char *start = buffer; + + buffer += sprintf(buffer, "\n %d: ", ec->slot_no); + + if (ec->cid.id == 0) { + struct in_chunk_dir incd; + + buffer += sprintf(buffer, "[%04X:%04X] ", + ec->cid.manufacturer, ec->cid.product); + + if (!ec->card_desc && ec->cid.is && ec->cid.cd && + ecard_readchunk(&incd, ec, 0xf5, 0)) + ec->card_desc = incd.d.string; + + if (!ec->card_desc) + ec->card_desc = "*unknown*"; + + buffer += sprintf(buffer, "%s", ec->card_desc); + } else + buffer += sprintf(buffer, "Simple card %d", ec->cid.id); + + return buffer - start; +} + +static inline unsigned short ecard_getu16(unsigned char *v) +{ + return v[0] | v[1] << 8; +} + +static inline signed long ecard_gets24(unsigned char *v) +{ + return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0); +} + +/* + * Probe for an expansion card. + * + * If bit 1 of the first byte of the card is set, then the + * card does not exist. + */ +__initfunc(static int ecard_probe(int card, int freeslot, card_type_t type)) +{ + ecard_t *ec = expcard + freeslot; + struct ex_ecid cid; + char buffer[200]; + int i; + + irqno_to_expcard[card] = -1; + + ec->slot_no = card; + ec->irq = NO_IRQ; + ec->fiq = NO_IRQ; + ec->dma = NO_DMA; + ec->card_desc = NULL; + ec->ops = &ecard_default_ops; + + if ((ec->podaddr = ecard_address(ec, type, ECARD_SYNC)) == 0) + return 0; + + cid.r_zero = 1; + ecard_readbytes(&cid, ec, 0, 16, 0); + if (cid.r_zero) + return 0; + + irqno_to_expcard[card] = freeslot; + + ec->type = type; + ec->cid.id = cid.r_id; + ec->cid.cd = cid.r_cd; + ec->cid.is = cid.r_is; + ec->cid.w = cid.r_w; + ec->cid.manufacturer = ecard_getu16(cid.r_manu); + ec->cid.product = ecard_getu16(cid.r_prod); + ec->cid.country = cid.r_country; + ec->cid.irqmask = cid.r_irqmask; + ec->cid.irqoff = ecard_gets24(cid.r_irqoff); + ec->cid.fiqmask = cid.r_fiqmask; + ec->cid.fiqoff = ecard_gets24(cid.r_fiqoff); + ec->fiqaddr = + ec->irqaddr = (unsigned char *)BUS_ADDR(ec->podaddr); + + if (ec->cid.cd && ec->cid.is) { + ec->irqmask = ec->cid.irqmask; + ec->irqaddr += ec->cid.irqoff; + ec->fiqmask = ec->cid.fiqmask; + ec->fiqaddr += ec->cid.fiqoff; + } else { + ec->irqmask = 1; + ec->fiqmask = 4; + } + + for (i = 0; i < sizeof(blacklist) / sizeof(*blacklist); i++) + if (blacklist[i].manufacturer == ec->cid.manufacturer && + blacklist[i].product == ec->cid.product) { + ec->loader = blacklist[i].loader; + ec->card_desc = blacklist[i].type; + break; + } + + ecard_prints(buffer, ec); + printk("%s", buffer); + + ec->irq = 32 + card; +#ifdef IO_EC_MEMC8_BASE + if (card == 8) + ec->irq = 11; +#endif +#ifdef CONFIG_ARCH_RPC + /* On RiscPC, only first two slots have DMA capability */ + if (card < 2) + ec->dma = 2 + card; +#endif +#if 0 /* We don't support FIQs on expansion cards at the moment */ + ec->fiq = 96 + card; +#endif + + return 1; +} + /* * This is called to reset the loaders for each expansion card on reboot. * * This is required to make sure that the card is in the correct state * that RiscOS expects it to be. */ -void ecard_reset (int card) +void ecard_reset(int card) { - extern int ecard_loader_reset (volatile unsigned int pa, loader_t loader); + extern int ecard_loader_reset(volatile unsigned int pa, loader_t loader); if (card >= ecard_numcards) return; @@ -330,11 +471,11 @@ void ecard_reset (int card) if (card < 0) { for (card = 0; card < ecard_numcards; card++) if (expcard[card].loader) - ecard_loader_reset (BUS_ADDR(expcard[card].podaddr), + ecard_loader_reset(BUS_ADDR(expcard[card].podaddr), expcard[card].loader); } else if (expcard[card].loader) - ecard_loader_reset (BUS_ADDR(expcard[card].podaddr), + ecard_loader_reset(BUS_ADDR(expcard[card].podaddr), expcard[card].loader); #ifdef HAS_EXPMASK @@ -347,18 +488,19 @@ void ecard_reset (int card) static unsigned int ecard_startcard; -void ecard_startfind (void) +void ecard_startfind(void) { ecard_startcard = 0; } -ecard_t *ecard_find (int cld, const card_ids *cids) +ecard_t *ecard_find(int cid, const card_ids *cids) { int card; + if (!cids) { for (card = ecard_startcard; card < ecard_numcards; card++) if (!expcard[card].claimed && - ((expcard[card].cld.ecld ^ cld) & 0x78) == 0) + (expcard[card].cid.id ^ cid) == 0) break; } else { for (card = ecard_startcard; card < ecard_numcards; card++) { @@ -368,8 +510,8 @@ ecard_t *ecard_find (int cld, const card_ids *cids) if (expcard[card].claimed) continue; - manufacturer = expcard[card].cld.manufacturer; - product = expcard[card].cld.product; + manufacturer = expcard[card].cid.manufacturer; + product = expcard[card].cid.product; for (i = 0; cids[i].manufacturer != 65535; i++) if (manufacturer == cids[i].manufacturer && @@ -380,16 +522,21 @@ ecard_t *ecard_find (int cld, const card_ids *cids) break; } } + ecard_startcard = card + 1; + return card < ecard_numcards ? &expcard[card] : NULL; } -int ecard_readchunk (struct in_chunk_dir *cd, ecard_t *ec, int id, int num) +int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num) { struct ex_chunk_dir excd; int index = 16; int useld = 0; + if (!ec->cid.is || !ec->cid.cd) + return 0; + while(1) { ecard_readbytes(&excd, ec, index, 8, useld); index += 8; @@ -427,124 +574,49 @@ int ecard_readchunk (struct in_chunk_dir *cd, ecard_t *ec, int id, int num) } } cd->start_offset = c_start(&excd); - memcpy (cd->d.string, excd.d.string, 256); + memcpy(cd->d.string, excd.d.string, 256); return 1; } -unsigned int ecard_address (ecard_t *ec, card_type_t type, card_speed_t speed) +unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed) { switch (ec->slot_no) { case 0 ... 3: switch (type) { case ECARD_MEMC: - return MEMCECIO_BASE + (ec->slot_no << 12); + return IO_EC_MEMC_BASE + (ec->slot_no << 12); case ECARD_IOC: - return IOCECIO_BASE + (speed << 17) + (ec->slot_no << 12); + return IO_EC_IOC_BASE + (speed << 17) + (ec->slot_no << 12); - default: - return 0; +#ifdef IO_EC_EASI_BASE + case ECARD_EASI: + return IO_EC_EASI_BASE + (ec->slot_no << 22); +#endif } + break; -#ifdef IOCEC4IO_BASE case 4 ... 7: - if (type != ECARD_IOC) - return 0; - - return IOCEC4IO_BASE + (speed << 17) + ((ec->slot_no - 4) << 12); + switch (type) { +#ifdef IO_EC_IOC4_BASE + case ECARD_IOC: + return IO_EC_IOC4_BASE + (speed << 17) + ((ec->slot_no - 4) << 12); #endif -#ifdef MEMCEC8IO_BASE - case 8: - return MEMCEC8IO_BASE; +#ifdef IO_EC_EASI_BASE + case ECARD_EASI: + return IO_EC_EASI_BASE + (ec->slot_no << 22); #endif - } - return 0; -} - -/* - * Probe for an expansion card. - * - * If bit 1 of the first byte of the card is set, - * then the card does not exist. - */ -__initfunc(static int ecard_probe (int card, int freeslot)) -{ - ecard_t *ec = expcard + freeslot; - struct ex_ecld excld; - const char *card_desc = NULL; - int i; - - irqno_to_expcard[card] = -1; - - ec->slot_no = card; - if ((ec->podaddr = ecard_address (ec, ECARD_IOC, ECARD_SYNC)) == 0) - return 0; - - excld.r_ecld = 2; - ecard_readbytes (&excld, ec, 0, 16, 0); - if (excld.r_ecld & 2) - return 0; - - irqno_to_expcard[card] = freeslot; - - ec->irq = NO_IRQ; - ec->fiq = NO_IRQ; - ec->dma = NO_DMA; - ec->cld.ecld = e_ecld(&excld); - ec->cld.manufacturer = e_manu(&excld); - ec->cld.product = e_prod(&excld); - ec->cld.country = e_country(&excld); - ec->cld.fiqmask = e_fiqmask(&excld); - ec->cld.irqmask = e_irqmask(&excld); - ec->cld.fiqaddr = e_fiqaddr(&excld); - ec->cld.irqaddr = e_irqaddr(&excld); - ec->fiqaddr = - ec->irqaddr = (unsigned char *)BUS_ADDR(ec->podaddr); - ec->fiqmask = 4; - ec->irqmask = 1; - ec->ops = &ecard_default_ops; - - for (i = 0; i < sizeof (blacklist) / sizeof (*blacklist); i++) - if (blacklist[i].manufacturer == ec->cld.manufacturer && - blacklist[i].product == ec->cld.product) { - ec->loader = blacklist[i].loader; - card_desc = blacklist[i].type; + default: break; } + break; - ec->irq = 32 + card; -#if 0 - /* We don't support FIQs on expansion cards at the moment */ - ec->fiq = 96 + card; -#endif -#ifdef CONFIG_ARCH_RPC - if (card != 8) { - /* On RiscPC, only first two slots have DMA capability - */ - if (card < 2) - ec->dma = 2 + card; - } else - ec->irq = 11; +#ifdef IO_EC_MEMC8_BASE + case 8: + return IO_EC_MEMC8_BASE; #endif - - if ((ec->cld.ecld & 0x78) == 0) { - struct in_chunk_dir incd; - printk ("\n %d: [%04X:%04X] ", card, ec->cld.manufacturer, ec->cld.product); - if (e_is (&excld)) { - ec->fiqmask = e_fiqmask (&excld); - ec->irqmask = e_irqmask (&excld); - ec->fiqaddr += e_fiqaddr (&excld); - ec->irqaddr += e_irqaddr (&excld); - } - if (!card_desc && e_cd (&excld) && ecard_readchunk (&incd, ec, 0xf5, 0)) - card_desc = incd.d.string; - if (card_desc) - printk ("%s", card_desc); - else - printk ("*Unknown*"); - } else - printk("\n %d: Simple card %d\n", card, (ec->cld.ecld >> 3) & 15); - return 1; + } + return 0; } static struct irqaction irqexpansioncard = { @@ -565,11 +637,11 @@ __initfunc(void ecard_init(void)) { int i, nc = 0; - memset (expcard, 0, sizeof (expcard)); + memset(expcard, 0, sizeof(expcard)); #ifdef HAS_EXPMASK if (ecard_checkirqhw()) { - printk (KERN_DEBUG "Expansion card interrupt management hardware found\n"); + printk(KERN_DEBUG "Expansion card interrupt management hardware found\n"); irqexpansioncard.handler = ecard_irq_expmask; irqexpansioncard.flags |= SA_IRQNOMASK; have_expmask = -1; @@ -581,8 +653,8 @@ __initfunc(void ecard_init(void)) /* * First of all, probe all cards on the expansion card interrupt line */ - for (i = 0; i < 4; i++) - if (ecard_probe (i, nc)) + for (i = 0; i < 8; i++) + if (ecard_probe(i, nc, ECARD_IOC) || ecard_probe(i, nc, ECARD_EASI)) nc += 1; else have_expmask &= ~(1<<i); @@ -591,8 +663,8 @@ __initfunc(void ecard_init(void)) /* Now probe other cards with different interrupt lines */ -#ifdef MEMCEC8IO_BASE - if (ecard_probe (8, nc)) +#ifdef IO_EC_MEMC8_BASE + if (ecard_probe(8, nc, ECARD_IOC)) nc += 1; #endif @@ -600,7 +672,7 @@ __initfunc(void ecard_init(void)) ecard_numcards = nc; if (nc && setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) { - printk ("Could not allocate interrupt for expansion cards\n"); + printk("Could not allocate interrupt for expansion cards\n"); return; } @@ -609,5 +681,5 @@ __initfunc(void ecard_init(void)) EXPMASK_ENABLE = have_expmask; #endif - oldlatch_init (); + oldlatch_init(); } diff --git a/arch/arm/kernel/entry-armo.S b/arch/arm/kernel/entry-armo.S index 4c65fc892..3ca29cd2c 100644 --- a/arch/arm/kernel/entry-armo.S +++ b/arch/arm/kernel/entry-armo.S @@ -271,26 +271,18 @@ vector_undefinstr: mov fp, #0 teqp pc, #I_BIT | MODE_SVC .Lbug_undef: - adr r1, .LC2 - ldmia r1, {r1, r4} - ldr r1, [r1] - get_current_task r2 - teq r1, r2 - stmnefd sp!, {ip, lr} - blne SYMBOL_NAME(math_state_restore) - ldmnefd sp!, {ip, lr} + ldr r4, .LC2 ldr pc, [r4] @ Call FP module USR entry point .globl SYMBOL_NAME(fpundefinstr) SYMBOL_NAME(fpundefinstr): @ Called by FP module on undefined instr -SYMBOL_NAME(fpundefinstrsvc): mov r0, lr mov r1, sp teqp pc, #MODE_SVC bl SYMBOL_NAME(do_undefinstr) b ret_from_exception @ Normal FP exit -__und_svc: SVC_SAVE_ALL @ Non-user mode +__und_svc: SVC_SAVE_ALL @ Non-user mode mask_pc r0, lr and r2, lr, #3 sub r0, r0, #4 @@ -298,8 +290,44 @@ __und_svc: SVC_SAVE_ALL @ Non-user mode bl SYMBOL_NAME(do_undefinstr) SVC_RESTORE_ALL -.LC2: .word SYMBOL_NAME(last_task_used_math) - .word SYMBOL_NAME(fp_enter) +/* We get here if an undefined instruction happens and the floating + * point emulator is not present. If the offending instruction was + * a WFS, we just perform a normal return as if we had emulated the + * operation. This is a hack to allow some basic userland binaries + * to run so that the emulator module proper can be loaded. --philb + */ +fpe_not_present: + adr r10, wfs_mask_data + ldmia r10, {r4, r5, r6, r7, r8} + ldr r10, [sp, #S_PC] @ Load PC + sub r10, r10, #4 + mask_pc r10, r10 + ldrt r10, [r10] @ get instruction + and r5, r10, r5 + teq r5, r4 @ Is it WFS? + beq ret_from_exception + and r5, r10, r8 + teq r5, r6 @ Is it LDF/STF on sp or fp? + teqne r5, r7 + bne fpundefinstr + tst r10, #0x00200000 @ Does it have WB + beq ret_from_exception + and r4, r10, #255 @ get offset + and r6, r10, #0x000f0000 + tst r10, #0x00800000 @ +/- + rsbeq r4, r4, #0 + ldr r5, [sp, r6, lsr #14] @ Load reg + add r5, r5, r4, lsl #2 + str r5, [sp, r6, lsr #14] @ Save reg + b ret_from_exception + +wfs_mask_data: .word 0x0e200110 @ WFS + .word 0x0fff0fff + .word 0x0d0d0100 @ LDF [sp]/STF [sp] + .word 0x0d0b0100 @ LDF [fp]/STF [fp] + .word 0x0f0f0f00 + +.LC2: .word SYMBOL_NAME(fp_enter) /*============================================================================= * Prefetch abort handler diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 7ecf1d59a..bcc938b32 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -231,60 +231,68 @@ irq_prio_ebsa110: .macro disable_fiq .endm + .equ pci_iack_high, PCI_IACK & 0xff000000 + .equ pci_iack_low, PCI_IACK & 0x00ff0000 + .macro get_irqnr_and_base, irqnr, irqstat, base mov r4, #IO_BASE_ARM_CSR - ldr \irqstat, [r4, #IRQ_STATUS] @ just show us the unmasked ones + ldr \irqstat, [r4, #CSR_IRQ_STATUS] @ just show us the unmasked ones @ run through hard priorities @ timer tst \irqstat, #IRQ_MASK_TIMER0 movne \irqnr, #IRQ_TIMER0 - bne 1f + bne 1001f @ ether10 tst \irqstat, #IRQ_MASK_ETHER10 movne \irqnr, #IRQ_ETHER10 - bne 1f + bne 1001f @ ether100 tst \irqstat, #IRQ_MASK_ETHER100 movne \irqnr, #IRQ_ETHER100 - bne 1f + bne 1001f @ video compressor - tst \irqstat, #IRQ_VIDCOMP_MASK + tst \irqstat, #IRQ_MASK_VIDCOMP movne \irqnr, #IRQ_VIDCOMP - bne 1f + bne 1001f @ now try all the PIC sources @ determine whether we have an irq tst \irqstat, #IRQ_MASK_EXTERN_IRQ - beq 3f - mov r4, #(IO_BASE_PCI_IACK & 0xff000000) - orr r4, r4, #(IO_BASE_PCI_IACK & 0x00ff0000) + beq 1002f + mov r4, #pci_iack_high + orr r4, r4, #pci_iack_low ldrb \irqnr, [r4] @ get the IACK byte - b 1f + b 1001f -3: @ PCI errors +1002: @ PCI errors tst \irqstat, #IRQ_MASK_PCI_ERR movne \irqnr, #IRQ_PCI_ERR - bne 1f + bne 1001f @ softint - tst \irqstat, #IRQ_MASK_SOFT_IRQ - movne \irqnr, #IRQ_SOFT_IRQ - bne 1f + tst \irqstat, #IRQ_MASK_SOFTIRQ + movne \irqnr, #IRQ_SOFTIRQ + bne 1001f @ debug uart tst \irqstat, #IRQ_MASK_UART_DEBUG - movne \irqnr, #IRQ_UART_DEBUG - bne 1f + movne \irqnr, #IRQ_CONRX + bne 1001f @ watchdog - tst \irqstat, #IRQ_WATCHDOG_MASK + tst \irqstat, #IRQ_MASK_WATCHDOG movne \irqnr, #IRQ_WATCHDOG -1: @ If Z is set, then we will not enter an interrupt +1001: @ If Z is set, then we will not enter an interrupt + .endm + + .macro irq_prio_table + .endm + #else #error Unknown architecture #endif @@ -696,8 +704,8 @@ __und_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go adrsvc al, lr, fpundefinstr @ lr = undefined instr return 1: get_current_task r10 - mov lr, #1 - strb lr, [r10, #TSK_USED_MATH] @ set current->used_math + mov r8, #1 + strb r8, [r10, #TSK_USED_MATH] @ set current->used_math add r10, r10, #TSS_FPESAVE @ r10 = workspace ldr r4, .LC2 ldr pc, [r4] @ Call FP module USR entry point @@ -742,6 +750,43 @@ __und_invalid: sub sp, sp, #S_FRAME_SIZE and r2, r6, #31 @ int mode b SYMBOL_NAME(bad_mode) @ Does not ever return... +/* We get here if an undefined instruction happens and the floating + * point emulator is not present. If the offending instruction was + * a WFS, we just perform a normal return as if we had emulated the + * operation. This is a hack to allow some basic userland binaries + * to run so that the emulator module proper can be loaded. --philb + */ +fpe_not_present: + adr r10, wfs_mask_data + ldmia r10, {r4, r5, r6, r7, r8} + ldr r10, [sp, #S_PC] @ Load PC + sub r10, r10, #4 + mask_pc r10, r10 + ldrt r10, [r10] @ get instruction + and r5, r10, r5 + teq r5, r4 @ Is it WFS? + moveq pc, r9 + and r5, r10, r8 + teq r5, r6 @ Is it LDF/STF on sp or fp? + teqne r5, r7 + movne pc, lr + tst r10, #0x00200000 @ Does it have WB + moveq pc, r9 + and r4, r10, #255 @ get offset + and r6, r10, #0x000f0000 + tst r10, #0x00800000 @ +/- + rsbeq r4, r4, #0 + ldr r5, [sp, r6, lsr #14] @ Load reg + add r5, r5, r4, lsl #2 + str r5, [sp, r6, lsr #14] @ Save reg + mov pc, r9 + +wfs_mask_data: .word 0x0e200110 @ WFS + .word 0x0fff0fff + .word 0x0d0d0100 @ LDF [sp]/STF [sp] + .word 0x0d0b0100 @ LDF [fp]/STF [fp] + .word 0x0f0f0f00 + #include "entry-common.S" .data diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 6e0e9e38c..c77c0ea51 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -288,4 +288,4 @@ ENTRY(trap_init) .data ENTRY(fp_enter) - .word fpundefinstr + .word fpe_not_present diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c index c32fef3c8..a5da15c7f 100644 --- a/arch/arm/kernel/fiq.c +++ b/arch/arm/kernel/fiq.c @@ -106,14 +106,7 @@ void set_fiq_handler(void *start, unsigned int length) memcpy((void *)FIQ_VECTOR, start, length); protect_page_0(); -#if 0 - /* This doesn'w work correctly. Ok, it's a misuse - * of the DMA flushing code, but it ought to work. - * More investigation required. Maybe it really - * needs the cache flushed. - */ - dma_cache_wback(FIQ_VECTOR, length); -#else +#ifdef CONFIG_CPU_32 processor.u.armv3v4._flush_cache_area(FIQ_VECTOR, FIQ_VECTOR + length, 1); #endif } diff --git a/arch/arm/kernel/head-armo.S b/arch/arm/kernel/head-armo.S index 1dc4fde13..d6b2b79e6 100644 --- a/arch/arm/kernel/head-armo.S +++ b/arch/arm/kernel/head-armo.S @@ -58,6 +58,3 @@ Loldparams: mov r4, #0x02000000 cmp r4, r3 blt 1b movs pc, lr - - .align 13 -ENTRY(this_must_match_init_task) diff --git a/arch/arm/kernel/head-armv.S b/arch/arm/kernel/head-armv.S index 88e71adab..cd4be86cb 100644 --- a/arch/arm/kernel/head-armv.S +++ b/arch/arm/kernel/head-armv.S @@ -8,9 +8,23 @@ #include <linux/config.h> #include <linux/linkage.h> +#ifndef CONFIG_ARCH_VNC #if (TEXTADDR & 0xffff) != 0x8000 #error TEXTADDR must start at 0xXXXX8000 #endif +#else + .text + mov r0, r0 + mov r0, r0 + mov r0, r0 + mov r0, r0 + mov r0, r0 + mov r0, r0 + mov r0, r0 + mov r0, r0 + mov r0, #0 + mov r1, #5 +#endif #define DEBUG @@ -97,6 +111,15 @@ __entry: teq r0, #0 @ check for illegal entry... add r3, r3, #1 << 20 teq r0, r2 bne 1b +#ifdef CONFIG_ARCH_VNC + add r0, r4, #0x3f00 + add r0, r0, #0x00f8 + mov r3, #0x7c000000 + orr r3, r3, r8 + str r3, [r0], #4 + add r3, r3, #1 << 20 + str r3, [r0], #4 +#endif #endif #ifdef CONFIG_ARCH_RPC /* Map in screen at 0x02000000 & SCREEN2_BASE @@ -339,7 +362,7 @@ __entry: teq r0, #0 @ check for illegal entry... beq 1001b .endm -#elif defined(CONFIG_ARCH_EBSA285) || defined(CONFIG_ARCH_VNC) +#elif defined(CONFIG_ARCH_EBSA285) .macro addruart,rx mov \rx, #0xfe000000 .endm @@ -374,6 +397,30 @@ __entry: teq r0, #0 @ check for illegal entry... .macro waituart,rd,rx .endm + +#elif defined(CONFIG_ARCH_VNC) + .macro addruart,rx + mov \rx, #0xff000000 + orr \rx, \rx, #0x00e00000 + orr \rx, \rx, #0x000003f8 + .endm + + .macro senduart,rd,rx + strb \rd, [\rx] + .endm + + .macro busyuart,rd,rx +1002: ldrb \rd, [\rx, #0x5] + and \rd, \rd, #0x60 + teq \rd, #0x60 + bne 1002b + .endm + + .macro waituart,rd,rx +1001: ldrb \rd, [\rx, #0x6] + tst \rd, #0x10 + beq 1001b + .endm #else #error Unknown architecture #endif diff --git a/arch/arm/kernel/hw-ebsa285.c b/arch/arm/kernel/hw-ebsa285.c new file mode 100644 index 000000000..e3385696b --- /dev/null +++ b/arch/arm/kernel/hw-ebsa285.c @@ -0,0 +1,161 @@ +/* + * arch/arm/kernel/hw-ebsa286.c + * + * EBSA285 hardware specific functions + * + * Copyright (C) 1998 Russell King, Phil Blundel + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/ptrace.h> +#include <linux/interrupt.h> +#include <linux/init.h> + +#include <asm/irq.h> +#include <asm/system.h> + +extern int setup_arm_irq(int, struct irqaction *); + +extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set); +extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr); +extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq); + +static int irqmap_ebsa[] __initdata = { 9, 8, 18, 11 }; +static int irqmap_cats[] __initdata = { 18, 8, 9, 11 }; + +__initfunc(static int ebsa_irqval(struct pci_dev *dev)) +{ + unsigned char pin; + + pcibios_read_config_byte(dev->bus->number, + dev->devfn, + PCI_INTERRUPT_PIN, + &pin); + + return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3]; +} + +__initfunc(static int cats_irqval(struct pci_dev *dev)) +{ + if (dev->irq >= 128) + return 32 + (dev->irq & 0x1f); + + switch (dev->irq) { + case 1: + case 2: + case 3: + case 4: + return irqmap_cats[dev->irq - 1]; + case 0: + return 0; + } + + printk("PCI: device %02x:%02x has unknown irq line %x\n", + dev->bus->number, dev->devfn, dev->irq); + return 0; +} + +__initfunc(void pcibios_fixup_ebsa285(struct pci_dev *dev)) +{ + char cmd; + + /* sort out the irq mapping for this device */ + switch (machine_type) { + case MACH_TYPE_EBSA285: + dev->irq = ebsa_irqval(dev); + break; + case MACH_TYPE_CATS: + dev->irq = cats_irqval(dev); + break; + } + + /* Turn on bus mastering - boot loader doesn't + * - perhaps it should! - dag + */ + pci_read_config_byte(dev, PCI_COMMAND, &cmd); + pci_write_config_byte(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER); +} + +static void irq_pci_err(int irq, void *dev_id, struct pt_regs *regs) +{ + const char *err = "unknown"; + unsigned long cmd = *(unsigned long *)0xfe000004 & 0xffff; + unsigned long ctrl = *(unsigned long *)0xfe00013c & 0xffffde07; + static unsigned long next_warn[7]; + int idx = 6; + + switch(irq) { + case IRQ_PCIPARITY: + *(unsigned long *)0xfe000004 = cmd | 1 << 31; + idx = 0; + err = "parity"; + break; + + case IRQ_PCITARGETABORT: + *(unsigned long *)0xfe000004 = cmd | 1 << 28; + idx = 1; + err = "target abort"; + break; + + case IRQ_PCIMASTERABORT: + *(unsigned long *)0xfe000004 = cmd | 1 << 29; + idx = 2; + err = "master abort"; + break; + + case IRQ_PCIDATAPARITY: + *(unsigned long *)0xfe000004 = cmd | 1 << 24; + idx = 3; + err = "data parity"; + break; + + case IRQ_DISCARDTIMER: + *(unsigned long *)0xfe00013c = ctrl | 1 << 8; + idx = 4; + err = "discard timer"; + break; + + case IRQ_SERR: + *(unsigned long *)0xfe00013c = ctrl | 1 << 3; + idx = 5; + err = "system"; + break; + } + if (time_after_eq(jiffies, next_warn[idx])) { + next_warn[idx] = jiffies + 3 * HZ / 100; + printk(KERN_ERR "PCI %s error detected\n", err); + } +} + +static struct irqaction irq_pci_error = { + irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL +}; + +__initfunc(void pcibios_init_ebsa285(void)) +{ + setup_arm_irq(IRQ_PCIPARITY, &irq_pci_error); + setup_arm_irq(IRQ_PCITARGETABORT, &irq_pci_error); + setup_arm_irq(IRQ_PCIMASTERABORT, &irq_pci_error); + setup_arm_irq(IRQ_PCIDATAPARITY, &irq_pci_error); + setup_arm_irq(IRQ_DISCARDTIMER, &irq_pci_error); + setup_arm_irq(IRQ_SERR, &irq_pci_error); + + /* + * Map our SDRAM at a known address in PCI space, just in case + * the firmware had other ideas. Using a nonzero base is slightly + * bizarre but apparently necessary to avoid problems with some + * video cards. + * + * We should really only do this if the central function is enabled. + */ + *(unsigned long *)0xfe000010 = 0; + *(unsigned long *)0xfe000018 = 0xe0000000; + *(unsigned long *)0xfe0000f8 = 0; + *(unsigned long *)0xfe0000fc = 0; + *(unsigned long *)0xfe000100 = 0x01fc0000; + *(unsigned long *)0xfe000104 = 0; + *(unsigned long *)0xfe000108 = 0x80000000; + *(unsigned long *)0xfe000004 = 0x17; +} diff --git a/arch/arm/kernel/init_task.c b/arch/arm/kernel/init_task.c index 689c43589..99577f1b7 100644 --- a/arch/arm/kernel/init_task.c +++ b/arch/arm/kernel/init_task.c @@ -6,7 +6,6 @@ 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; diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index b4950ace2..332e8940d 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -119,7 +119,9 @@ int get_irq_list(char *buf) *p++ = '\n'; } +#ifdef CONFIG_ACORN p += get_fiq_list(p); +#endif return p - buf; } @@ -354,7 +356,7 @@ unsigned long probe_irq_on(void) /* * wait for spurious interrupts to mask themselves out again */ - for (delay = jiffies + HZ/10; delay > jiffies; ) + for (delay = jiffies + HZ/10; time_before(jiffies, delay); ) /* min 100ms delay */; /* @@ -424,6 +426,8 @@ __initfunc(void init_IRQ(void)) } irq_init_irq(); +#ifdef CONFIG_ARCH_ACORN init_FIQ(); +#endif init_dma(); } diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 767faff9e..cddc3fab3 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -57,13 +57,15 @@ #endif #ifndef CONFIG_CMDLINE -#define CONFIG_CMDLINE "root=nfs rw console=ttyS1,38400n8" +#define CONFIG_CMDLINE "root=/dev/nfs rw" #endif #define MEM_SIZE (16*1024*1024) #define COMMAND_LINE_SIZE 256 struct drive_info_struct { char dummy[32]; } drive_info; struct screen_info screen_info = { + orig_video_lines: 30, + orig_video_cols: 80, orig_video_mode: 0, orig_video_ega_bx: 0, orig_video_isVGA: 1, @@ -210,6 +212,8 @@ setup_params(unsigned long *mem_end_p)) #ifdef CONFIG_ARCH_ACORN *mem_end_p = GET_MEMORY_END(params); +#elif defined(CONFIG_ARCH_EBSA285) + *mem_end_p = PAGE_OFFSET + params->u1.s.page_size * params->u1.s.nr_pages; #else *mem_end_p = PAGE_OFFSET + MEM_SIZE; #endif @@ -305,18 +309,18 @@ static char command_line[COMMAND_LINE_SIZE] = { 0, }; __initfunc(static void setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_end)) { - char c = ' ', *to = command_line; + char c, *to = command_line; int len = 0; *mem_start = (unsigned long)&_end; for (;;) { - if (c == ' ' && - cmd_line[0] == 'm' && - cmd_line[1] == 'e' && - cmd_line[2] == 'm' && - cmd_line[3] == '=') { - *mem_end = simple_strtoul(cmd_line+4, &cmd_line, 0); + if (cmd_line[0] == ' ' && + cmd_line[1] == 'm' && + cmd_line[2] == 'e' && + cmd_line[3] == 'm' && + cmd_line[4] == '=') { + *mem_end = simple_strtoul(cmd_line+5, &cmd_line, 0); switch(*cmd_line) { case 'M': case 'm': @@ -336,7 +340,7 @@ setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_end)) *to++ = c; } - *to = '\0'; + *to = '\0'; } __initfunc(void @@ -381,33 +385,27 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * mem conswitchp = &dummy_con; #endif #endif -printascii("setup_arch done\n"); } +static const struct { + char *machine_name; + char *bus_name; +} machine_desc[] = { + { "DEC-EBSA110", "DEC" }, + { "Acorn-RiscPC", "Acorn" }, + { "Nexus-NexusPCI", "PCI" }, + { "DEC-EBSA285", "PCI" }, + { "Corel-Netwinder", "PCI/ISA" }, + { "Chalice-CATS", "PCI" }, + { "unknown-TBOX", "PCI" } +}; + #if defined(CONFIG_ARCH_ARC) #define HARDWARE "Acorn-Archimedes" #define IO_BUS "Acorn" #elif defined(CONFIG_ARCH_A5K) #define HARDWARE "Acorn-A5000" #define IO_BUS "Acorn" -#elif defined(CONFIG_ARCH_RPC) -#define HARDWARE "Acorn-RiscPC" -#define IO_BUS "Acorn" -#elif defined(CONFIG_ARCH_EBSA110) -#define HARDWARE "DEC-EBSA110" -#define IO_BUS "DEC" -#elif defined(CONFIG_ARCH_EBSA285) -#define HARDWARE "DEC-EBSA285" -#define IO_BUS "PCI" -#elif defined(CONFIG_ARCH_NEXUSPCI) -#define HARDWARE "Nexus-NexusPCI" -#define IO_BUS "PCI" -#elif defined(CONFIG_ARCH_VNC) -#define HARDWARE "Corel-VNC" -#define IO_BUS "PCI" -#else -#define HARDWARE "unknown" -#define IO_BUS "unknown" #endif #if defined(CONFIG_CPU_ARM2) @@ -439,8 +437,17 @@ int get_cpuinfo(char * buffer) (int)processor_id & 15, (loops_per_sec+2500) / 500000, ((loops_per_sec+2500) / 5000) % 100, +#ifdef HARDWARE HARDWARE, +#else + machine_desc[machine_type].machine_name, +#endif OPTIMISATION, - IO_BUS); +#ifdef IO_BUS + IO_BUS +#else + machine_desc[machine_type].bus_name +#endif + ); return len; } diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c index 56ada1dc7..5996398f8 100644 --- a/arch/arm/kernel/sys_arm.c +++ b/arch/arm/kernel/sys_arm.c @@ -77,7 +77,8 @@ asmlinkage int old_mmap(struct mmap_arg_struct *arg) goto out; if (!(a.flags & MAP_ANONYMOUS)) { error = -EBADF; - if (a.fd >= NR_OPEN || !(file = current->files->fd[a.fd])) + if (a.fd >= current->files->max_fds || + !(file = current->files->fd[a.fd])) goto out; } a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 7445921d9..22c3639da 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -9,7 +9,7 @@ * * 1994-07-02 Alan Modra * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime - * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * 1998-12-20 Updated NTP code according to technical memorandum Jan '96 * "A Kernel Model for Precision Timekeeping" by Dave Mills */ #include <linux/errno.h> @@ -125,9 +125,11 @@ void do_settimeofday(struct timeval *tv) } xtime = *tv; - time_state = TIME_BAD; - time_maxerror = MAXPHASE; - time_esterror = MAXPHASE; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; sti (); } diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 6da813aa2..5d04f325b 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -51,7 +51,7 @@ static int verify_stack_pointer (unsigned long stackptr, int size) if (stackptr < 0x02048000 || stackptr + size > 0x03000000) return -EFAULT; #else - if (stackptr < 0xc0000000 || stackptr + size > (unsigned long)high_memory) + if (stackptr < PAGE_OFFSET || stackptr + size > (unsigned long)high_memory) return -EFAULT; #endif return 0; @@ -175,7 +175,7 @@ void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret) printk("(sp underflow)"); printk("\n"); - dump_mem(cstack, sstack + 4096); + dump_mem(cstack - 16, sstack + 4096); frameptr = regs->ARM_fp; if (frameptr) { diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index d47a092cb..684db2a47 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -26,6 +26,10 @@ ifeq ($(MACHINE),ebsa110) L_OBJS += io-ebsa110.o endif +ifeq ($(MACHINE),vnc) + L_OBJS += io-ebsa285.o +endif + ifeq ($(MACHINE),ebsa285) L_OBJS += io-ebsa285.o endif diff --git a/arch/arm/lib/io-ebsa285.S b/arch/arm/lib/io-ebsa285.S index 0ee1e37fc..a86983d43 100644 --- a/arch/arm/lib/io-ebsa285.S +++ b/arch/arm/lib/io-ebsa285.S @@ -104,24 +104,75 @@ ENTRY(outswb) ENTRY(outsw) add r0, r0, #0xff000000 add r0, r0, #0x00e00000 -1: teq r2, #0 - ldrneh r3, [r1], #2 - strneh r3, [r0] - subne r2, r2, #1 - bne 1b +1: subs r2, r2, #1 + ldrgeh r3, [r1], #2 + strgeh r3, [r0] + bgt 1b mov pc, lr ENTRY(inswb) mov r2, r2, lsr #1 ENTRY(insw) + stmfd sp!, {r4, r5, lr} add r0, r0, #0xff000000 add r0, r0, #0x00e00000 -1: teq r2, #0 + @ + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 + subs ip, r2, #8 + blo too_little + @ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + ands lr, r1, #3 @ check alignment + beq 1f + + ldrh r3, [r0] + strh r3, [r1], #2 + sub ip, ip, #1 + cmn ip, #8 + blo too_little + +1: ldrh r2, [r0] + ldrh r3, [r0] + orr r2, r2, r3, lsl #16 + ldrh r3, [r0] + ldrh r4, [r0] + orr r3, r3, r4, lsl #16 + ldrh r4, [r0] + ldrh r5, [r0] + orr r4, r4, r5, lsl #16 + ldrh r5, [r0] + ldrh lr, [r0] + orr r5, r5, lr, lsl #16 + stmia r1!, {r2, r3, r4, r5} + subs ip, ip, #8 + @ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 + 0 + 1 + bhs 1b + @ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 8 - 7 + cmn ip, #4 + ldrhsh r2, [r0] @ ... ... ... ... - 4 - 3 - 2 - 1 ... ... + ldrhsh r3, [r0] + orrhs r2, r2, r3, lsl #16 + ldrhsh r3, [r0] + ldrhsh r4, [r0] + orrhs r3, r3, r4, lsl #16 + stmhsia r1!, {r2, r3} + + tst ip, #2 + ldrneh r2, [r0] @ ... ... - 6 - 5 ... ... - 2 - 1 ... ... ldrneh r3, [r0] - strneh r3, [r1], #2 - subne r2, r2, #1 - bne 1b - mov pc, lr + orrne r2, r2, r3, lsl #16 + strne r2, [r1], #4 + + tst ip, #1 + ldrneh r2, [r0] + strneh r2, [r1], #2 + + ldmfd sp!, {r4, r5, pc} + +too_little: subs r2, r2, #1 + ldrgeh r3, [r0] + strgeh r3, [r1], #2 + bgt too_little + + ldmfd sp!, {r4, r5, pc} ENTRY(insb) diff --git a/arch/arm/mm/fault-armo.c b/arch/arm/mm/fault-armo.c index 4e244a297..6fe1f30ff 100644 --- a/arch/arm/mm/fault-armo.c +++ b/arch/arm/mm/fault-armo.c @@ -174,7 +174,7 @@ bad_area: } /* Are we prepared to handle this kernel fault? */ - if ((fixup = search_exception_table(regs->ARM_pc)) != 0) { + if ((fixup = search_exception_table(instruction_pointer(regs))) != 0) { printk(KERN_DEBUG "%s: Exception at [<%lx>] addr=%lx (fixup: %lx)\n", tsk->comm, regs->ARM_pc, addr, fixup); regs->ARM_pc = fixup; diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index 98134abac..f090c5f2c 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -208,7 +208,7 @@ bad_area: } /* Are we prepared to handle this kernel fault? */ - if ((fixup = search_exception_table(regs->ARM_pc)) != 0) { + if ((fixup = search_exception_table(instruction_pointer(regs))) != 0) { printk(KERN_DEBUG "%s: Exception at [<%lx>] addr=%lx (fixup: %lx)\n", tsk->comm, regs->ARM_pc, addr, fixup); regs->ARM_pc = fixup; diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 613fefce1..b3b0ecf56 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -192,6 +192,15 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) reservedpages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10)); + +#ifdef CONFIG_CPU_26 + if (max_mapnr <= 128) { + extern int sysctl_overcommit_memory; + /* On a machine this small we won't get anywhere without + overcommit, so turn it on by default. */ + sysctl_overcommit_memory = 1; + } +#endif } void free_initmem (void) diff --git a/arch/arm/mm/mm-ebsa285.c b/arch/arm/mm/mm-ebsa285.c index 82bbce899..a5b17c6b9 100644 --- a/arch/arm/mm/mm-ebsa285.c +++ b/arch/arm/mm/mm-ebsa285.c @@ -5,7 +5,6 @@ * * Copyright (C) 1998 Russell King, Dave Gilbert. */ -#include <linux/config.h> #include <linux/sched.h> #include <linux/mm.h> #include <linux/init.h> @@ -14,46 +13,7 @@ #include <asm/page.h> #include <asm/io.h> #include <asm/proc/mm-init.h> - -/* - * These two functions convert PCI bus addresses to virtual addresses - * and back again. - */ -unsigned long __virt_to_bus(unsigned long res) -{ - if (res < PAGE_OFFSET || res >= 0xD0000000) { - printk("__virt_to_bus: invalid address 0x%08lx\n", res); -#ifdef CONFIG_DEBUG_ERRORS - __backtrace(); -#endif - } else - res = (res - PAGE_OFFSET) + 0x10000000; - - return res; -} - -unsigned long __bus_to_virt(unsigned long res) -{ - if (res < 0x10000000 || res >= 0x20000000) { - printk("__bus_to_virt: invalid address 0x%08lx\n", res); -#ifdef CONFIG_DEBUG_ERRORS - __backtrace(); -#endif - } else - res = (res - 0x10000000) + PAGE_OFFSET; - - return res; -} - -/* Logical Physical - * 0xfff00000 0x40000000 X-Bus - * 0xffe00000 0x7c000000 PCI I/O space - * 0xfe000000 0x42000000 CSR - * 0xfd000000 0x78000000 Outbound write flush - * 0xfc000000 0x79000000 PCI IACK/special space - * 0xf9000000 0x7a000000 PCI Config type 1 - * 0xf8000000 0x7b000000 PCI Config type 0 - */ +#include <asm/dec21285.h> /* * This is to allow us to fiddle with the EEPROM @@ -65,15 +25,15 @@ unsigned long __bus_to_virt(unsigned long res) * until we're happy with them... */ #define MAPPING \ - { 0xd8000000, 0x41000000, 0x00400000, DOMAIN_USER, 1, 1 }, /* EEPROM */ \ - { 0xdc000000, 0x7c000000, 0x00100000, DOMAIN_USER, 1, 1 }, /* VGA */ \ - { 0xe0000000, 0x80000000, 0x10000000, DOMAIN_USER, 1, 1 }, /* VGA */ \ - { 0xf8000000, 0x7b000000, 0x01000000, DOMAIN_IO , 0, 1 }, /* Type 0 Config */ \ - { 0xf9000000, 0x7a000000, 0x01000000, DOMAIN_IO , 0, 1 }, /* Type 1 Config */ \ - { 0xfc000000, 0x79000000, 0x01000000, DOMAIN_IO , 0, 1 }, /* PCI IACK */ \ - { 0xfd000000, 0x78000000, 0x01000000, DOMAIN_IO , 0, 1 }, /* Outbound wflsh*/ \ - { 0xfe000000, 0x42000000, 0x01000000, DOMAIN_IO , 0, 1 }, /* CSR */ \ - { 0xffe00000, 0x7c000000, 0x00100000, DOMAIN_IO , 0, 1 }, /* PCI I/O */ \ - { 0xfff00000, 0x40000000, 0x00100000, DOMAIN_IO , 0, 1 }, /* X-Bus */ + { 0xd8000000, DC21285_FLASH, 0x00400000, DOMAIN_USER, 1, 1 }, /* EEPROM */ \ + { 0xdc000000, 0x7c000000, 0x00100000, DOMAIN_USER, 1, 1 }, /* VGA */ \ + { 0xe0000000, DC21285_PCI_MEM, 0x18000000, DOMAIN_USER, 1, 1 }, /* VGA */ \ + { 0xf8000000, DC21285_PCI_TYPE_0_CONFIG, 0x01000000, DOMAIN_IO , 0, 1 }, /* Type 0 Config */ \ + { 0xf9000000, DC21285_PCI_TYPE_1_CONFIG, 0x01000000, DOMAIN_IO , 0, 1 }, /* Type 1 Config */ \ + { PCI_IACK, DC21285_PCI_IACK, 0x01000000, DOMAIN_IO , 0, 1 }, /* PCI IACK */ \ + { 0xfd000000, DC21285_OUTBOUND_WRITE_FLUSH, 0x01000000, DOMAIN_IO , 0, 1 }, /* Out wrflsh */ \ + { 0xfe000000, DC21285_ARMCSR_BASE, 0x01000000, DOMAIN_IO , 0, 1 }, /* CSR */ \ + { 0xffe00000, DC21285_PCI_IO, 0x00100000, DOMAIN_IO , 0, 1 }, /* PCI I/O */ \ + { 0xfff00000, 0x40000000, 0x00100000, DOMAIN_IO , 0, 1 }, /* X-Bus */ #include "mm-armv.c" diff --git a/arch/arm/mm/mm-rpc.c b/arch/arm/mm/mm-rpc.c index 18ebe4a47..1c755faaf 100644 --- a/arch/arm/mm/mm-rpc.c +++ b/arch/arm/mm/mm-rpc.c @@ -91,7 +91,8 @@ init_dram_banks(struct param_struct *params)) #define MAPPING \ { SCREEN2_BASE, SCREEN_START, 2*1048576, DOMAIN_IO, 0, 1 }, /* VRAM */ \ - { IO_BASE, IO_START, IO_SIZE , DOMAIN_IO, 0, 1 } /* IO space */ + { IO_BASE, IO_START, IO_SIZE , DOMAIN_IO, 0, 1 }, /* IO space */ \ + { EASI_BASE, EASI_START, EASI_SIZE, DOMAIN_IO, 0, 1 } /* EASI space */ /* * Include common routine to set up page tables */ diff --git a/arch/arm/mm/mm-vnc.c b/arch/arm/mm/mm-vnc.c index eed49eb29..94e037485 100644 --- a/arch/arm/mm/mm-vnc.c +++ b/arch/arm/mm/mm-vnc.c @@ -13,20 +13,19 @@ #include <asm/page.h> #include <asm/io.h> #include <asm/proc/mm-init.h> +#include <asm/dec21285.h> /* Table describing the MMU translation mapping * mainly used to set up the I/O mappings. */ #define MAPPING \ - { 0xe0000000, DC21285_PCI_IO, 0x00100000, DOMAIN_IO, 0, 1 }, /* PCI I/O */ \ - { 0xe0100000, DC21285_PCI_TYPE_0_CONFIG, 0x00f00000, DOMAIN_IO, 0, 1 }, /* Type 0 Config */ \ - { 0xe1000000, DC21285_ARMCSR_BASE, 0x00100000, DOMAIN_IO, 0, 1 }, /* ARM CSR */ \ - { 0xe1100000, DC21285_PCI_IACK, 0x00100000, DOMAIN_IO, 0, 1 }, /* PCI IACK */ \ - { 0xe1300000, DC21285_OUTBOUND_WRITE_FLUSH, 0x00100000, DOMAIN_IO, 0, 1 }, /* Out wrflsh */ \ - { 0xe1400000, DC21285_OUTBOUND_WRITE_FLUSH, 0x00100000, DOMAIN_IO, 0, 1 }, /* Out wrflsh */ \ - { 0xe1500000, DC21285_OUTBOUND_WRITE_FLUSH, 0x00100000, DOMAIN_IO, 0, 1 }, /* Out wrflsh */ \ - { 0xe1600000, DC21285_OUTBOUND_WRITE_FLUSH, 0x00100000, DOMAIN_IO, 0, 1 }, /* Out wrflsh */ \ - { 0xe1700000, DC21285_OUTBOUND_WRITE_FLUSH, 0x00100000, DOMAIN_IO, 0, 1 }, /* Out wrflsh */ \ - { 0xe1800000, DC21285_FLASH, 0x00800000, DOMAIN_IO, 0, 1 } /* Flash */ + { 0xd0000000, DC21285_FLASH, 0x00800000, DOMAIN_IO , 0, 1 }, /* Flash */ \ + { 0xe0000000, DC21285_PCI_MEM, 0x18000000, DOMAIN_IO , 0, 1 }, /* PCI Mem */ \ + { 0xf8000000, DC21285_PCI_TYPE_0_CONFIG, 0x01000000, DOMAIN_IO , 0, 1 }, /* Type 0 Config */ \ + { 0xf9000000, DC21285_PCI_TYPE_1_CONFIG, 0x01000000, DOMAIN_IO , 0, 1 }, /* Type 1 Config */ \ + { PCI_IACK, DC21285_PCI_IACK, 0x01000000, DOMAIN_IO , 0, 1 }, /* PCI IACK */ \ + { 0xfd000000, DC21285_OUTBOUND_WRITE_FLUSH, 0x01000000, DOMAIN_IO , 0, 1 }, /* Out wrflsh */ \ + { 0xfe000000, DC21285_ARMCSR_BASE, 0x01000000, DOMAIN_IO , 0, 1 }, /* CSR */ \ + { 0xffe00000, DC21285_PCI_IO, 0x00100000, DOMAIN_IO , 0, 1 }, /* PCI I/O */ \ #include "mm-armv.c" diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S index d077ab1d2..221797862 100644 --- a/arch/arm/mm/proc-sa110.S +++ b/arch/arm/mm/proc-sa110.S @@ -10,6 +10,11 @@ #include <asm/assembler.h> #include "../lib/constants.h" +/* This is the maximum size of an area which will be flushed. If the area + * is larger than this, then we flush the whole cache + */ +#define MAX_AREA_SIZE 32768 + .data Lclean_switch: .long 0 .text @@ -52,7 +57,7 @@ _sa110_flush_cache_all_r2: .align 5 _sa110_flush_cache_area: sub r3, r1, r0 - cmp r3, #32768 + cmp r3, #MAX_AREA_SIZE bgt _sa110_flush_cache_all_r2 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry mcr p15, 0, r0, c7, c6, 1 @ flush D entry @@ -79,16 +84,17 @@ _sa110_flush_cache_area: .align 5 _sa110_cache_wback_area: sub r3, r1, r0 - cmp r3, #32768 - movgt r2, #0 - bgt _sa110_flush_cache_all + cmp r3, #MAX_AREA_SIZE + mov r2, #0 + bgt _sa110_flush_cache_all_r2 + bic r0, r0, #31 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #32 mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #32 cmp r0, r1 blt 1b - mcr p15, 0, r0, c7, c10, 4 @ drain WB + mcr p15, 0, r2, c7, c10, 4 @ drain WB mov pc, lr /* diff --git a/arch/i386/Makefile b/arch/i386/Makefile index 3e5c4e7ff..322b53210 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -35,6 +35,10 @@ ifdef CONFIG_M586 CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=586 endif +ifdef CONFIG_M586TSC +CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=586 +endif + ifdef CONFIG_M686 CFLAGS := $(CFLAGS) -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=686 endif diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile index ff26d087c..64b9377a2 100644 --- a/arch/i386/boot/Makefile +++ b/arch/i386/boot/Makefile @@ -40,7 +40,7 @@ zlilo: $(CONFIGURE) $(BOOTIMAGE) if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi install: $(CONFIGURE) $(BOOTIMAGE) - sh -x ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) $(BOOTIMAGE) $(TOPDIR)/System.map "$(INSTALL_PATH)" + sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map "$(INSTALL_PATH)" tools/build: tools/build.c $(HOSTCC) $(HOSTCFLAGS) -o $@ $< -I$(TOPDIR)/include diff --git a/arch/i386/config.in b/arch/i386/config.in index a22692bca..3c42052e4 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -14,12 +14,28 @@ comment 'Processor type and features' choice 'Processor family' \ "386 CONFIG_M386 \ 486/Cx486 CONFIG_M486 \ - Pentium/K5/5x86/6x86 CONFIG_M586 \ - PPro/K6/6x86MX CONFIG_M686" Pentium -bool 'Math emulation' CONFIG_MATH_EMULATION -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR + 586/K5/5x86/6x86 CONFIG_M586 \ + Pentium/K6/TSC CONFIG_M586TSC \ + PPro/6x86MX CONFIG_M686" PPro +# +# Define implied options from the CPU selection here +# +if [ "$CONFIG_M386" != "y" ]; then + define_bool CONFIG_X86_WP_WORKS_OK y + define_bool CONFIG_X86_INVLPG y + define_bool CONFIG_X86_BSWAP y + define_bool CONFIG_X86_POPAD_OK y +fi +if [ "$CONFIG_M686" = "y" -o "$CONFIG_M586TSC" = "y" ]; then + define_bool CONFIG_X86_TSC y fi +if [ "$CONFIG_M686" = "y" ]; then + define_bool CONFIG_X86_GOOD_APIC y +fi + +bool 'Math emulation' CONFIG_MATH_EMULATION +bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR +bool 'Symmetric multi-processing support' CONFIG_SMP endmenu mainmenu_option next_comment @@ -37,8 +53,16 @@ comment 'General setup' bool 'Networking support' CONFIG_NET bool 'PCI support' CONFIG_PCI if [ "$CONFIG_PCI" = "y" ]; then - bool ' PCI BIOS support' CONFIG_PCI_BIOS - bool ' PCI direct access support' CONFIG_PCI_DIRECT + choice 'PCI access mode' \ + "BIOS CONFIG_PCI_GOBIOS \ + Direct CONFIG_PCI_GODIRECT \ + Any CONFIG_PCI_GOANY" Any + if [ "$CONFIG_PCI_GOBIOS" = "y" -o "$CONFIG_PCI_GOANY" = "y" ]; then + define_bool CONFIG_PCI_BIOS y + fi + if [ "$CONFIG_PCI_GODIRECT" = "y" -o "$CONFIG_PCI_GOANY" = "y" ]; then + define_bool CONFIG_PCI_DIRECT y + fi bool ' PCI quirks' CONFIG_PCI_QUIRKS if [ "$CONFIG_PCI_QUIRKS" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE @@ -46,6 +70,17 @@ if [ "$CONFIG_PCI" = "y" ]; then bool ' Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC fi bool 'MCA support' CONFIG_MCA +bool 'SGI Visual Workstation support' CONFIG_VISWS +if [ "$CONFIG_VISWS" = "y" ]; then + define_bool CONFIG_X86_VISWS_APIC y + define_bool CONFIG_X86_LOCAL_APIC y +else + if [ "$CONFIG_SMP" = "y" ]; then + define_bool CONFIG_X86_IO_APIC y + define_bool CONFIG_X86_LOCAL_APIC y + fi +fi + bool 'System V IPC' CONFIG_SYSVIPC bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL @@ -73,6 +108,8 @@ if [ "$CONFIG_APM" = "y" ]; then bool ' Power off on shutdown' CONFIG_APM_POWER_OFF bool ' Ignore multiple suspend' CONFIG_APM_IGNORE_MULTIPLE_SUSPEND bool ' Ignore multiple suspend/resume cycles' CONFIG_APM_IGNORE_SUSPEND_BOUNCE + bool ' RTC stores time in GMT' CONFIG_APM_RTC_IS_GMT + bool ' Allow interrupts during APM BIOS calls' CONFIG_APM_ALLOW_INTS fi endmenu @@ -108,12 +145,15 @@ fi source net/ax25/Config.in +source net/irda/Config.in + mainmenu_option next_comment comment 'ISDN subsystem' - -tristate 'ISDN support' CONFIG_ISDN -if [ "$CONFIG_ISDN" != "n" ]; then - source drivers/isdn/Config.in +if [ "$CONFIG_NET" != "n" ]; then + tristate 'ISDN support' CONFIG_ISDN + if [ "$CONFIG_ISDN" != "n" ]; then + source drivers/isdn/Config.in + fi fi endmenu @@ -130,8 +170,6 @@ source drivers/char/Config.in source fs/Config.in -source fs/nls/Config.in - if [ "$CONFIG_VT" = "y" ]; then mainmenu_option next_comment comment 'Console drivers' diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 78351e2d5..200716f59 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -12,9 +12,18 @@ # # CONFIG_M386 is not set # CONFIG_M486 is not set -CONFIG_M586=y -# CONFIG_M686 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +CONFIG_M686=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_TSC=y +CONFIG_X86_GOOD_APIC=y # CONFIG_MATH_EMULATION is not set +# CONFIG_MTRR is not set +CONFIG_SMP=y # # Loadable module support @@ -28,11 +37,17 @@ CONFIG_MODULES=y # CONFIG_NET=y CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y CONFIG_PCI_BIOS=y CONFIG_PCI_DIRECT=y CONFIG_PCI_QUIRKS=y CONFIG_PCI_OLD_PROC=y # CONFIG_MCA is not set +# CONFIG_VISWS is not set +CONFIG_X86_IO_APIC=y +CONFIG_X86_LOCAL_APIC=y CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y @@ -139,6 +154,7 @@ CONFIG_SCSI_CONSTANTS=y # SCSI low-level drivers # # CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AHA152X is not set # CONFIG_SCSI_AHA1542 is not set # CONFIG_SCSI_AHA1740 is not set @@ -146,14 +162,16 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set # CONFIG_SCSI_BUSLOGIC is not set # CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_DMA is not set # CONFIG_SCSI_EATA_PIO is not set -# CONFIG_SCSI_EATA is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set CONFIG_SCSI_NCR53C8XX=y @@ -206,6 +224,8 @@ CONFIG_EEXPRESS_PRO100=y # CONFIG_NET_RADIO is not set # CONFIG_TR is not set # CONFIG_HOSTESS_SV11 is not set +# CONFIG_COSA is not set +# CONFIG_RCPCI is not set # CONFIG_WAN_DRIVERS is not set # CONFIG_LAPBETHER is not set # CONFIG_X25_ASY is not set @@ -237,6 +257,10 @@ CONFIG_SERIAL=y CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 CONFIG_MOUSE=y + +# +# Mice +# # CONFIG_ATIXL_BUSMOUSE is not set # CONFIG_BUSMOUSE is not set # CONFIG_MS_BUSMOUSE is not set @@ -245,9 +269,17 @@ CONFIG_82C710_MOUSE=y # CONFIG_PC110_PAD is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set # CONFIG_RTC is not set + +# +# Video For Linux +# # CONFIG_VIDEO_DEV is not set -# CONFIG_NVRAM is not set + +# +# Joystick support +# # CONFIG_JOYSTICK is not set # @@ -259,36 +291,43 @@ CONFIG_82C710_MOUSE=y # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_MINIX_FS is not set -CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=y -# CONFIG_JOLIET is not set +CONFIG_AUTOFS_FS=y +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set # CONFIG_FAT_FS is not set # CONFIG_MSDOS_FS is not set # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set CONFIG_NFS_FS=y -CONFIG_NFSD=y # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set # CONFIG_NCP_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_NTFS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set -# CONFIG_ROMFS_FS is not set -CONFIG_AUTOFS_FS=y -# CONFIG_UFS_FS is not set + +# +# Partition Types +# # CONFIG_BSD_DISKLABEL is not set +# CONFIG_MAC_PARTITION is not set # CONFIG_SMD_DISKLABEL is not set # CONFIG_SOLARIS_X86_PARTITION is not set -CONFIG_DEVPTS_FS=y -# CONFIG_MAC_PARTITION is not set # CONFIG_NLS is not set # diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index b8a94af18..0c3f24889 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -38,8 +38,16 @@ ifdef CONFIG_APM OX_OBJS += apm.o endif -ifdef SMP -O_OBJS += io_apic.o smp.o trampoline.o +ifdef CONFIG_SMP +O_OBJS += smp.o trampoline.o +endif + +ifdef CONFIG_X86_IO_APIC +O_OBJS += io_apic.o +endif + +ifdef CONFIG_X86_VISWS_APIC +O_OBJS += visws_apic.o endif head.o: head.S $(TOPDIR)/include/linux/tasks.h diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index 1cbc824f5..6eba89025 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -31,6 +31,8 @@ * Aug 1998, Version 1.5 * Sep 1998, Version 1.6 * Nov 1998, Version 1.7 + * Jan 1999, Version 1.8 + * Jan 1999, Version 1.9 * * History: * 0.6b: first version in official kernel, Linux 1.3.46 @@ -72,6 +74,22 @@ * Make boot messages far less verbose by default * Make asm safer * Stephen Rothwell + * 1.8: Add CONFIG_APM_RTC_IS_GMT + * Richard Gooch <rgooch@atnf.csiro.au> + * change APM_NOINTS to CONFIG_APM_ALLOW_INTS + * remove dependency on CONFIG_PROC_FS + * Stephen Rothwell + * 1.9: Fix small typo. <laslo@ilo.opole.pl> + * Try to cope with BIOS's that need to have all display + * devices blanked and not just the first one. + * Ross Paterson <ross@soi.city.ac.uk> + * Fix segment limit setting it has always been wrong as + * the segments needed to have byte granularity. + * Mark a few things __init. + * Add hack to allow power off of SMP systems by popular request. + * Use CONFIG_SMP instead of __SMP__ + * Ignore BOUNCES for three seconds. + * Stephen Rothwell * * APM 1.1 Reference: * @@ -105,10 +123,8 @@ #include <linux/fcntl.h> #include <linux/malloc.h> #include <linux/linkage.h> -#ifdef CONFIG_PROC_FS #include <linux/stat.h> #include <linux/proc_fs.h> -#endif #include <linux/miscdevice.h> #include <linux/apm_bios.h> #include <linux/init.h> @@ -202,13 +218,6 @@ extern unsigned long get_cmos_time(void); #define ALWAYS_CALL_BUSY /* - * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call - * should turn interrupts on before it does a 'hlt'). - * This reportedly needs undefining for the ThinkPad 600. - */ -#define APM_NOINTS - -/* * Define to make the APM BIOS calls zero all data segment registers (so * that an incorrect BIOS implementation will cause a kernel panic if it * tries to write to arbitrary memory). @@ -216,7 +225,7 @@ extern unsigned long get_cmos_time(void); #define APM_ZERO_SEGS /* - * Define to make all set_limit calls use 64k limits. The APM 1.1 BIOS is + * Define to make all _set_limit calls use 64k limits. The APM 1.1 BIOS is * supposed to provide limit information that it recognizes. Many machines * do this correctly, but many others do not restrict themselves to their * claimed limit. When this happens, they will cause a segmentation @@ -245,6 +254,12 @@ extern unsigned long get_cmos_time(void); #define APM_CHECK_TIMEOUT (HZ) /* + * If CONFIG_APM_IGNORE_SUSPEND_BOUNCE is defined then + * ignore suspend events for this amount of time + */ +#define BOUNCE_INTERVAL (3 * HZ) + +/* * Save a segment register away */ #define savesegment(seg, where) \ @@ -266,9 +281,7 @@ static ssize_t do_read(struct file *, char *, size_t , loff_t *); static unsigned int do_poll(struct file *, poll_table *); static int do_ioctl(struct inode *, struct file *, u_int, u_long); -#ifdef CONFIG_PROC_FS static int apm_get_info(char *, char **, off_t, int, int); -#endif extern int apm_register_callback(int (*)(apm_event_t)); extern void apm_unregister_callback(int (*)(apm_event_t)); @@ -281,6 +294,7 @@ static asmlinkage struct { unsigned short segment; } apm_bios_entry; static int apm_enabled = 0; +static int smp_hack = 0; #ifdef CONFIG_APM_CPU_IDLE static int clock_slowed = 0; #endif @@ -290,8 +304,13 @@ static int standbys_pending = 0; static int waiting_for_resume = 0; #endif +#ifdef CONFIG_APM_RTC_IS_GMT +# define clock_cmos_diff 0 +# define got_clock_diff 1 +#else static long clock_cmos_diff; static int got_clock_diff = 0; +#endif static int debug = 0; static int apm_disabled = 0; @@ -300,7 +319,7 @@ static struct apm_bios_struct * user_list = NULL; static struct timer_list apm_timer; -static char driver_version[] = "1.7"; /* no spaces */ +static char driver_version[] = "1.9"; /* no spaces */ #ifdef APM_DEBUG static char * apm_event_name[] = { @@ -375,22 +394,22 @@ static const lookup_t error_table[] = { #define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t)) /* - * These are the actual BIOS calls. Depending on APM_ZERO_SEGS - * and APM_NOINTS, we are being really paranoid here! Not only are - * interrupts disabled, but all the segment registers (except SS) are - * saved and zeroed this means that if the BIOS tries to reference any - * data without explicitly loading the segment registers, the kernel will - * fault immediately rather than have some unforeseen circumstances for - * the rest of the kernel. And it will be very obvious! :-) Doing this - * depends on CS referring to the same physical memory as DS so that DS - * can be zeroed before the call. Unfortunately, we can't do anything + * These are the actual BIOS calls. Depending on APM_ZERO_SEGS and + * CONFIG_APM_ALLOW_INTS, we are being really paranoid here! Not only + * are interrupts disabled, but all the segment registers (except SS) + * are saved and zeroed this means that if the BIOS tries to reference + * any data without explicitly loading the segment registers, the kernel + * will fault immediately rather than have some unforeseen circumstances + * for the rest of the kernel. And it will be very obvious! :-) Doing + * this depends on CS referring to the same physical memory as DS so that + * DS can be zeroed before the call. Unfortunately, we can't do anything * about the stack segment/pointer. Also, we tell the compiler that * everything could change. * * Also, we KNOW that for the non error case of apm_bios_call, there * is no useful data returned in the low order 8 bits of eax. */ -#ifdef APM_NOINTS +#ifndef CONFIG_APM_ALLOW_INTS # define APM_DO_CLI __cli() #else # define APM_DO_CLI @@ -526,7 +545,15 @@ static int apm_set_power_state(u_short state) void apm_power_off(void) { - if (apm_enabled) + /* + * smp_hack == 2 means that we would have enabled APM support + * except there is more than one processor and so most of + * the APM stuff is unsafe. We will still try power down + * because is is useful to some people and they know what + * they are doing because they booted with the smp-power-off + * kernel option. + */ + if (apm_enabled || (smp_hack == 2)) (void) apm_set_power_state(APM_STATE_OFF); } @@ -534,12 +561,19 @@ void apm_power_off(void) /* Called by apm_display_blank and apm_display_unblank when apm_enabled. */ static int apm_set_display_power_state(u_short state) { - return set_power_state(0x0100, state); + int error; + + /* Blank the first display device */ + error = set_power_state(0x0100, state); + if (error == APM_BAD_DEVICE) + /* try to blank them all instead */ + error = set_power_state(0x01ff, state); + return error; } #endif #ifdef CONFIG_APM_DO_ENABLE -static int apm_enable_power_management(void) +static int __init apm_enable_power_management(void) { u32 eax; @@ -568,12 +602,9 @@ static int apm_get_power_status(u_short *status, u_short *bat, u_short *life) return APM_SUCCESS; } -#if 0 -/* not used anywhere */ -static int apm_get_battery_status(u_short which, +static int apm_get_battery_status(u_short which, u_short *status, u_short *bat, u_short *life, u_short *nbat) { - u_short status; u32 eax; u32 ebx; u32 ecx; @@ -585,20 +616,20 @@ static int apm_get_battery_status(u_short which, if (which != 1) return APM_BAD_DEVICE; *nbat = 1; - return apm_get_power_status(&status, bat, life); + return apm_get_power_status(status, bat, life); } if (apm_bios_call(0x530a, (0x8000 | (which)), 0, &eax, &ebx, &ecx, &edx, &esi)) return (eax >> 8) & 0xff; + *status = ebx; *bat = ecx; *life = edx; *nbat = esi; return APM_SUCCESS; } -#endif -static int apm_engage_power_management(u_short device) +static int __init apm_engage_power_management(u_short device) { u32 eax; @@ -747,14 +778,17 @@ static void suspend(void) unsigned long flags; int err; - /* Estimate time zone so that set_time can - update the clock */ +#ifndef CONFIG_APM_RTC_IS_GMT + /* + * Estimate time zone so that set_time can update the clock + */ save_flags(flags); clock_cmos_diff = -get_cmos_time(); cli(); clock_cmos_diff += CURRENT_TIME; got_clock_diff = 1; restore_flags(flags); +#endif err = apm_set_power_state(APM_STATE_SUSPEND); if (err) @@ -826,7 +860,7 @@ static void check_events(void) apm_event_t event; #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE static unsigned long last_resume = 0; - static int did_resume = 0; + static int ignore_bounce = 0; #endif while ((event = get_event()) != 0) { @@ -838,6 +872,11 @@ static void check_events(void) printk(KERN_DEBUG "apm: received unknown " "event 0x%02x\n", event); #endif +#ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE + if (ignore_bounce + && ((jiffies - last_resume) > BOUNCE_INTERVAL)) + ignore_bounce = 0; +#endif switch (event) { case APM_SYS_STANDBY: case APM_USER_STANDBY: @@ -859,7 +898,7 @@ static void check_events(void) #endif case APM_SYS_SUSPEND: #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE - if (did_resume && ((jiffies - last_resume) < HZ)) + if (ignore_bounce) break; #endif #ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND @@ -880,7 +919,7 @@ static void check_events(void) #endif #ifdef CONFIG_APM_IGNORE_SUSPEND_BOUNCE last_resume = jiffies; - did_resume = 1; + ignore_bounce = 1; #endif set_time(); send_event(event, 0, NULL); @@ -1139,13 +1178,13 @@ static int do_open(struct inode * inode, struct file * filp) return 0; } -#ifdef CONFIG_PROC_FS int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy) { char * p; unsigned short bx; unsigned short cx; unsigned short dx; + unsigned short nbat; unsigned short error; unsigned short ac_line_status = 0xff; unsigned short battery_status = 0xff; @@ -1167,13 +1206,8 @@ int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy) if (apm_bios_info.version > 0x100) { battery_flag = (cx >> 8) & 0xff; if (dx != 0xffff) { - if ((dx & 0x8000) == 0x8000) { - units = "min"; - time_units = dx & 0x7ffe; - } else { - units = "sec"; - time_units = dx & 0x7fff; - } + units = (dx & 0x8000) ? "min" : "sec"; + time_units = dx & 0x7fff; } } } @@ -1228,7 +1262,6 @@ int apm_get_info(char *buf, char **start, off_t fpos, int length, int dummy) return p - buf; } -#endif void __init apm_setup(char *str, int *dummy) { @@ -1244,6 +1277,8 @@ void __init apm_setup(char *str, int *dummy) str += 3; if (strncmp(str, "debug", 5) == 0) debug = !invert; + if (strncmp(str, "smp-power-off", 13) == 0) + smp_hack = !invert; str = strchr(str, ','); if (str != NULL) str += strspn(str, ", \t"); @@ -1284,17 +1319,18 @@ void __init apm_bios_init(void) /* BIOS < 1.2 doesn't set cseg_16_len */ if (apm_bios_info.version < 0x102) - apm_bios_info.cseg_16_len = 0xFFFF; /* 64k */ + apm_bios_info.cseg_16_len = 0; /* 64k */ if (debug) { printk(KERN_INFO "apm: entry %x:%lx cseg16 %x dseg %x", apm_bios_info.cseg, apm_bios_info.offset, apm_bios_info.cseg_16, apm_bios_info.dseg); if (apm_bios_info.version > 0x100) - printk(" cseg len %x, cseg16 len %x, dseg len %x", + printk(" cseg len %x, dseg len %x", apm_bios_info.cseg_len, - apm_bios_info.cseg_16_len, apm_bios_info.dseg_len); + if (apm_bios_info.version > 0x101) + printk(" cseg16 len %x", apm_bios_info.cseg_16_len); printk("\n"); } @@ -1302,12 +1338,6 @@ void __init apm_bios_init(void) printk(KERN_NOTICE "apm: disabled on user request.\n"); return; } -#ifdef __SMP__ - if (smp_num_cpus > 1) { - printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); - return; - } -#endif /* * Set up a segment that references the real mode segment 0x40 @@ -1317,7 +1347,7 @@ void __init apm_bios_init(void) */ set_base(gdt[APM_40 >> 3], __va((unsigned long)0x40 << 4)); - set_limit(gdt[APM_40 >> 3], 4096 - (0x40 << 4)); + _set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4)); apm_bios_entry.offset = apm_bios_info.offset; apm_bios_entry.segment = APM_CS; @@ -1327,23 +1357,36 @@ void __init apm_bios_init(void) __va((unsigned long)apm_bios_info.cseg_16 << 4)); set_base(gdt[APM_DS >> 3], __va((unsigned long)apm_bios_info.dseg << 4)); - if (apm_bios_info.version == 0x100) { - set_limit(gdt[APM_CS >> 3], 64 * 1024); - set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); - set_limit(gdt[APM_DS >> 3], 64 * 1024); - } else { -#ifdef APM_RELAX_SEGMENTS +#ifndef APM_RELAX_SEGMENTS + if (apm_bios_info.version == 0x100) +#endif + { /* For ASUS motherboard, Award BIOS rev 110 (and others?) */ - set_limit(gdt[APM_CS >> 3], 64 * 1024); + _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1); /* For some unknown machine. */ - set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); + _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1); /* For the DEC Hinote Ultra CT475 (and others?) */ - set_limit(gdt[APM_DS >> 3], 64 * 1024); -#else - set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len); - set_limit(gdt[APM_CS_16 >> 3], apm_bios_info.cseg_16_len); - set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len); + _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1); + } +#ifndef APM_RELAX_SEGMENTS + else { + _set_limit((char *)&gdt[APM_CS >> 3], + (apm_bios_info.cseg_len - 1) & 0xffff); + _set_limit((char *)&gdt[APM_CS_16 >> 3], + (apm_bios_info.cseg_16_len - 1) & 0xffff); + _set_limit((char *)&gdt[APM_DS >> 3], + (apm_bios_info.dseg_len - 1) & 0xffff); + } +#endif +#ifdef CONFIG_SMP + if (smp_num_cpus > 1) { + printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); + if (smp_hack) + smp_hack = 2; + return; + } #endif + if (apm_bios_info.version > 0x100) { /* * We only support BIOSs up to version 1.2 */ @@ -1355,7 +1398,7 @@ void __init apm_bios_init(void) } } if (debug) { - printk(KERN_INFO "apm: onnection version %d.%d\n", + printk(KERN_INFO "apm: Connection version %d.%d\n", (apm_bios_info.version >> 8) & 0xff, apm_bios_info.version & 0xff ); @@ -1376,23 +1419,23 @@ void __init apm_bios_init(void) case 3: bat_stat = "charging"; break; default: bat_stat = "unknown"; break; } - printk(KERN_INFO "apm: AC %s, battery status %s, battery life ", + printk(KERN_INFO + "apm: AC %s, battery status %s, battery life ", power_stat, bat_stat); if ((cx & 0xff) == 0xff) printk("unknown\n"); else printk("%d%%\n", cx & 0xff); if (apm_bios_info.version > 0x100) { - printk("apm: battery flag 0x%02x, battery life ", + printk(KERN_INFO + "apm: battery flag 0x%02x, battery life ", (cx >> 8) & 0xff); if (dx == 0xffff) printk("unknown\n"); - else { - if ((dx & 0x8000)) - printk("%d minutes\n", dx & 0x7ffe ); - else - printk("%d seconds\n", dx & 0x7fff ); - } + else + printk("%d %s\n", dx & 0x7fff, + (dx & 0x8000) ? + "minutes" : "seconds"); } } } @@ -1422,10 +1465,9 @@ void __init apm_bios_init(void) apm_timer.expires = APM_CHECK_TIMEOUT + jiffies; add_timer(&apm_timer); -#ifdef CONFIG_PROC_FS ent = create_proc_entry("apm", 0, 0); - ent->get_info = apm_get_info; -#endif + if (ent != NULL) + ent->get_info = apm_get_info; misc_register(&apm_device); diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c index 9543fc7ba..e7383e55b 100644 --- a/arch/i386/kernel/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -352,6 +352,10 @@ __initfunc(int pci_sanity_check(struct pci_access *a)) { u16 dfn, x; +#ifdef CONFIG_VISWS + return 1; /* Lithium PCI Bridges are non-standard */ +#endif + if (pci_probe & PCI_NO_CHECKS) return 1; for(dfn=0; dfn < 0x100; dfn++) @@ -1051,7 +1055,7 @@ __initfunc(void pcibios_fixup_devices(void)) pci_write_config_word(dev, PCI_COMMAND, cmd); } } -#ifdef __SMP__ +#if defined(CONFIG_X86_IO_APIC) /* * Recalculate IRQ numbers if we use the I/O APIC */ diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 390c10c1f..0153c4b40 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -153,10 +153,10 @@ ENTRY(lcall7) ALIGN .globl ret_from_fork ret_from_fork: - GET_CURRENT(%ebx) #ifdef __SMP__ - btrl $0, SYMBOL_NAME(scheduler_lock) + call SYMBOL_NAME(schedule_tail) #endif /* __SMP__ */ + GET_CURRENT(%ebx) jmp ret_from_sys_call /* @@ -193,6 +193,7 @@ restore_all: ALIGN signal_return: + sti # we can get here from an interrupt handler testl $(VM_MASK),EFLAGS(%esp) movl %esp,%eax jne v86_signal_return @@ -558,13 +559,14 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_sendfile) .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ + .long SYMBOL_NAME(sys_vfork) /* 190 */ /* - * NOTE!! This doesn' thave to be exact - we just have + * NOTE!! This doesn't have to be exact - we just have * to make sure we have _enough_ of the "sys_ni_syscall" * entries. Don't panic if you notice that this hasn't * been shrunk every time we add a new system call. */ - .rept NR_syscalls-189 + .rept NR_syscalls-190 .long SYMBOL_NAME(sys_ni_syscall) .endr diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index 7343cd64f..acbc3e325 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -534,10 +534,14 @@ ENTRY(gdt_table) .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ .quad 0x0000000000000000 /* not used */ .quad 0x0000000000000000 /* not used */ - .quad 0x00c0920000000000 /* 0x40 APM set up for bad BIOS's */ - .quad 0x00c09a0000000000 /* 0x48 APM CS code */ - .quad 0x00809a0000000000 /* 0x50 APM CS 16 code (16 bit) */ - .quad 0x00c0920000000000 /* 0x58 APM DS data */ + /* + * The APM segments have byte granularity and their bases + * and limits are set at run time. + */ + .quad 0x0040920000000000 /* 0x40 APM set up for bad BIOS's */ + .quad 0x00409a0000000000 /* 0x48 APM CS code */ + .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ + .quad 0x0040920000000000 /* 0x58 APM DS data */ .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */ /* diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index e1833f43c..cd9074796 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -58,6 +58,10 @@ EXPORT_SYMBOL_NOVERS(__put_user_1); EXPORT_SYMBOL_NOVERS(__put_user_2); EXPORT_SYMBOL_NOVERS(__put_user_4); +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strstr); + EXPORT_SYMBOL(strncpy_from_user); EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(clear_user); @@ -83,7 +87,6 @@ EXPORT_SYMBOL(__global_cli); EXPORT_SYMBOL(__global_sti); EXPORT_SYMBOL(__global_save_flags); EXPORT_SYMBOL(__global_restore_flags); -EXPORT_SYMBOL(smp_message_pass); EXPORT_SYMBOL(mtrr_hook); #endif diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index fa8ef26bb..232abf78d 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -225,6 +225,13 @@ static void __init clear_IO_APIC_pin(unsigned int pin) int pirq_entries [MAX_PIRQS]; int pirqs_enabled; +void __init ioapic_setup(char *str, int *ints) +{ + extern int skip_ioapic_setup; /* defined in arch/i386/kernel/smp.c */ + + skip_ioapic_setup = 1; +} + void __init ioapic_pirq_setup(char *str, int *ints) { int i, max; @@ -675,7 +682,8 @@ void __init print_IO_APIC(void) printk(".... register #01: %08X\n", *(int *)®_01); printk("....... : max redirection entries: %04X\n", reg_01.entries); if ( (reg_01.entries != 0x0f) && /* ISA-only Neptune boards */ - (reg_01.entries != 0x17) /* ISA+PCI boards */ + (reg_01.entries != 0x17) && /* ISA+PCI boards */ + (reg_01.entries != 0x3F) /* Xeon boards */ ) UNEXPECTED_IO_APIC(); if (reg_01.entries == 0x0f) @@ -683,7 +691,8 @@ void __init print_IO_APIC(void) printk("....... : IO APIC version: %04X\n", reg_01.version); if ( (reg_01.version != 0x10) && /* oldest IO-APICs */ - (reg_01.version != 0x11) /* my IO-APIC */ + (reg_01.version != 0x11) && /* Pentium/Pro IO-APICs */ + (reg_01.version != 0x13) /* Xeon IO-APICs */ ) UNEXPECTED_IO_APIC(); if (reg_01.__reserved_1 || reg_01.__reserved_2) @@ -946,7 +955,7 @@ static inline void self_IPI(unsigned int irq) if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { desc->status = status | IRQ_REPLAY; - send_IPI(APIC_DEST_SELF, IO_APIC_VECTOR(irq)); + send_IPI_self(IO_APIC_VECTOR(irq)); } } diff --git a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c index 2e3beb11b..445a26613 100644 --- a/arch/i386/kernel/ioport.c +++ b/arch/i386/kernel/ioport.c @@ -58,7 +58,7 @@ asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32)) return -EINVAL; - if (!capable(CAP_SYS_RAWIO)) + if (turn_on && !capable(CAP_SYS_RAWIO)) return -EPERM; /* * If it's the first ioperm() call in this thread's lifetime, set the @@ -91,11 +91,15 @@ asmlinkage int sys_iopl(unsigned long unused) { struct pt_regs * regs = (struct pt_regs *) &unused; unsigned int level = regs->ebx; + unsigned int old = (regs->eflags >> 12) & 3; if (level > 3) return -EINVAL; - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; + /* Trying to gain more privileges? */ + if (level > old) { + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + } regs->eflags = (regs->eflags & 0xffffcfff) | (level << 12); return 0; } diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index e0fd62653..37878f59f 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -15,6 +15,7 @@ * Naturally it's not a 1:1 relation, but there are similarities. */ +#include <linux/config.h> #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/kernel_stat.h> @@ -47,46 +48,28 @@ unsigned int local_irq_count[NR_CPUS]; atomic_t nmi_counter; /* - * About the IO-APIC, the architecture is 'merged' into our - * current irq architecture, seemlessly. (i hope). It is only - * visible through a few more more hardware interrupt lines, but - * otherwise drivers are unaffected. The main code is believed - * to be NR_IRQS-safe (nothing anymore thinks we have 16 - * irq lines only), but there might be some places left ... + * Linux has a controller-independent x86 interrupt architecture. + * every controller has a 'controller-template', that is used + * by the main code to do the right thing. Each driver-visible + * interrupt source is transparently wired to the apropriate + * controller. Thus drivers need not be aware of the + * interrupt-controller. + * + * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, + * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. + * (IO-APICs assumed to be messaging to Pentium local-APICs) + * + * the code is designed to be easily extended with new/different + * interrupt controllers, without having to do assembly magic. */ /* - * This contains the irq mask for both 8259A irq controllers, + * Micro-access to controllers is serialized over the whole + * system. We never hold this lock when we call the actual + * IRQ handler. */ -static unsigned int cached_irq_mask = 0xffff; - -#define __byte(x,y) (((unsigned char *)&(y))[x]) -#define __word(x,y) (((unsigned short *)&(y))[x]) -#define __long(x,y) (((unsigned int *)&(y))[x]) - -#define cached_21 (__byte(0,cached_irq_mask)) -#define cached_A1 (__byte(1,cached_irq_mask)) - spinlock_t irq_controller_lock; -/* - * Not all IRQs can be routed through the IO-APIC, eg. on certain (older) - * boards the timer interrupt is not connected to any IO-APIC pin, it's - * fed to the CPU IRQ line directly. - * - * Any '1' bit in this mask means the IRQ is routed through the IO-APIC. - * this 'mixed mode' IRQ handling costs us one more branch in do_IRQ, - * but we have _much_ higher compatibility and robustness this way. - */ -unsigned long long io_apic_irqs = 0; - -static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs); -static void enable_8259A_irq(unsigned int irq); -void disable_8259A_irq(unsigned int irq); - -/* startup is the same as "enable", shutdown is same as "disable" */ -#define startup_8259A_irq enable_8259A_irq -#define shutdown_8259A_irq disable_8259A_irq /* * Dummy controller type for unused interrupts @@ -108,6 +91,19 @@ static struct hw_interrupt_type no_irq_type = { disable_none }; +/* + * This is the 'legacy' 8259A Programmable Interrupt Controller, + * present in the majority of PC/AT boxes. + */ + +static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs); +static void enable_8259A_irq(unsigned int irq); +void disable_8259A_irq(unsigned int irq); + +/* startup is the same as "enable", shutdown is same as "disable" */ +#define startup_8259A_irq enable_8259A_irq +#define shutdown_8259A_irq disable_8259A_irq + static struct hw_interrupt_type i8259A_irq_type = { "XT-PIC", startup_8259A_irq, @@ -117,11 +113,38 @@ static struct hw_interrupt_type i8259A_irq_type = { disable_8259A_irq }; -irq_desc_t irq_desc[NR_IRQS] = { - [0 ... 15] = { 0, &i8259A_irq_type, }, /* default to standard ISA IRQs */ - [16 ... NR_IRQS-1] = { 0, &no_irq_type, }, /* 'high' PCI IRQs filled in on demand */ -}; +/* + * Controller mappings for all interrupt sources: + */ +irq_desc_t irq_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = { 0, &no_irq_type, }}; + + +/* + * 8259A PIC functions to handle ISA devices: + */ + +/* + * This contains the irq mask for both 8259A irq controllers, + */ +static unsigned int cached_irq_mask = 0xffff; +#define __byte(x,y) (((unsigned char *)&(y))[x]) +#define __word(x,y) (((unsigned short *)&(y))[x]) +#define __long(x,y) (((unsigned int *)&(y))[x]) + +#define cached_21 (__byte(0,cached_irq_mask)) +#define cached_A1 (__byte(1,cached_irq_mask)) + +/* + * Not all IRQs can be routed through the IO-APIC, eg. on certain (older) + * boards the timer interrupt is not connected to any IO-APIC pin, it's + * fed to the CPU IRQ line directly. + * + * Any '1' bit in this mask means the IRQ is routed through the IO-APIC. + * this 'mixed mode' IRQ handling costs us one more branch in do_IRQ, + * but we have _much_ higher compatibility and robustness this way. + */ +unsigned long long io_apic_irqs = 0; /* * These have to be protected by the irq controller spinlock @@ -149,6 +172,77 @@ static void enable_8259A_irq(unsigned int irq) } } +int i8259A_irq_pending(unsigned int irq) +{ + unsigned int mask = 1<<irq; + + if (irq < 8) + return (inb(0x20) & mask); + return (inb(0xA0) & (mask >> 8)); +} + +void make_8259A_irq(unsigned int irq) +{ + disable_irq(irq); + __long(0,io_apic_irqs) &= ~(1<<irq); + irq_desc[irq].handler = &i8259A_irq_type; + enable_irq(irq); +} + +/* + * Careful! The 8259A is a fragile beast, it pretty + * much _has_ to be done exactly like this (mask it + * first, _then_ send the EOI, and the order of EOI + * to the two 8259s is important! + */ +static inline void mask_and_ack_8259A(unsigned int irq) +{ + cached_irq_mask |= 1 << irq; + if (irq & 8) { + inb(0xA1); /* DUMMY */ + outb(cached_A1,0xA1); + outb(0x62,0x20); /* Specific EOI to cascade */ + outb(0x20,0xA0); + } else { + inb(0x21); /* DUMMY */ + outb(cached_21,0x21); + outb(0x20,0x20); + } +} + +static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs) +{ + struct irqaction * action; + irq_desc_t *desc = irq_desc + irq; + + spin_lock(&irq_controller_lock); + { + unsigned int status; + mask_and_ack_8259A(irq); + status = desc->status & ~IRQ_REPLAY; + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) + action = desc->action; + desc->status = status | IRQ_INPROGRESS; + } + spin_unlock(&irq_controller_lock); + + /* Exit early if we had no action or it was disabled */ + if (!action) + return; + + handle_IRQ_event(irq, regs, action); + + spin_lock(&irq_controller_lock); + { + unsigned int status = desc->status & ~IRQ_INPROGRESS; + desc->status = status; + if (!(status & IRQ_DISABLED)) + enable_8259A_irq(irq); + } + spin_unlock(&irq_controller_lock); +} + /* * This builds up the IRQ handler stubs using some ugly macros in irq.h * @@ -168,8 +262,7 @@ BUILD_IRQ(4) BUILD_IRQ(5) BUILD_IRQ(6) BUILD_IRQ(7) BUILD_IRQ(8) BUILD_IRQ(9) BUILD_IRQ(10) BUILD_IRQ(11) BUILD_IRQ(12) BUILD_IRQ(13) BUILD_IRQ(14) BUILD_IRQ(15) -#ifdef __SMP__ - +#ifdef CONFIG_X86_IO_APIC /* * The IO-APIC gives us many more interrupt sources.. */ @@ -185,11 +278,13 @@ BUILD_IRQ(48) BUILD_IRQ(49) BUILD_IRQ(50) BUILD_IRQ(51) BUILD_IRQ(52) BUILD_IRQ(53) BUILD_IRQ(54) BUILD_IRQ(55) BUILD_IRQ(56) BUILD_IRQ(57) BUILD_IRQ(58) BUILD_IRQ(59) BUILD_IRQ(60) BUILD_IRQ(61) BUILD_IRQ(62) BUILD_IRQ(63) +#endif +#ifdef __SMP__ /* * The following vectors are part of the Linux architecture, there * is no hardware IRQ pin equivalent for them, they are triggered - * through the ICC by us (IPIs), via smp_message_pass(): + * through the ICC by us (IPIs) */ BUILD_SMP_INTERRUPT(reschedule_interrupt) BUILD_SMP_INTERRUPT(invalidate_interrupt) @@ -213,7 +308,7 @@ static void (*interrupt[NR_IRQS])(void) = { IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt, IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt -#ifdef __SMP__ +#ifdef CONFIG_X86_IO_APIC ,IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt, IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt, IRQ24_interrupt, IRQ25_interrupt, IRQ26_interrupt, IRQ27_interrupt, @@ -231,12 +326,16 @@ static void (*interrupt[NR_IRQS])(void) = { #endif }; + /* * Initial irq handlers. */ -static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } +void no_action(int cpl, void *dev_id, struct pt_regs *regs) +{ +} +#ifndef CONFIG_VISWS /* * Note that on a 486, we don't want to do a SIGFPE on an irq13 * as the irq is unreliable, and exception 16 works correctly @@ -262,7 +361,13 @@ static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL }; /* * IRQ2 is cascade interrupt to second interrupt controller */ + static struct irqaction irq2 = { no_action, 0, 0, "cascade", NULL, NULL}; +#endif + +/* + * Generic, controller-independent functions: + */ int get_irq_list(char *buf) { @@ -297,7 +402,7 @@ int get_irq_list(char *buf) } p += sprintf(p, "NMI: %10u\n", atomic_read(&nmi_counter)); #ifdef __SMP__ - p += sprintf(p, "IPI: %10lu\n", ipi_count); + p += sprintf(p, "ERR: %10lu\n", ipi_count); #endif return p - buf; } @@ -335,22 +440,22 @@ static void show(char * str) int i; unsigned long *stack; int cpu = smp_processor_id(); + extern char *get_options(char *str, int *ints); 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]); printk("bh: %d [%d %d]\n", atomic_read(&global_bh_count), local_bh_count[0], local_bh_count[1]); - stack = (unsigned long *) &str; + stack = (unsigned long *) &stack; for (i = 40; i ; i--) { unsigned long x = *++stack; - if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) { + if (x > (unsigned long) &get_options && x < (unsigned long) &vsprintf) { printk("<[%08lx]> ", x); } } } - #define MAXCOUNT 100000000 static inline void wait_on_bh(void) @@ -607,79 +712,6 @@ int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * return status; } -int i8259A_irq_pending(unsigned int irq) -{ - unsigned int mask = 1<<irq; - - if (irq < 8) - return (inb(0x20) & mask); - return (inb(0xA0) & (mask >> 8)); -} - - -void make_8259A_irq(unsigned int irq) -{ - disable_irq(irq); - __long(0,io_apic_irqs) &= ~(1<<irq); - irq_desc[irq].handler = &i8259A_irq_type; - enable_irq(irq); -} - -/* - * Careful! The 8259A is a fragile beast, it pretty - * much _has_ to be done exactly like this (mask it - * first, _then_ send the EOI, and the order of EOI - * to the two 8259s is important! - */ -static inline void mask_and_ack_8259A(unsigned int irq) -{ - cached_irq_mask |= 1 << irq; - if (irq & 8) { - inb(0xA1); /* DUMMY */ - outb(cached_A1,0xA1); - outb(0x62,0x20); /* Specific EOI to cascade */ - outb(0x20,0xA0); - } else { - inb(0x21); /* DUMMY */ - outb(cached_21,0x21); - outb(0x20,0x20); - } -} - -static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs) -{ - struct irqaction * action; - irq_desc_t *desc = irq_desc + irq; - - spin_lock(&irq_controller_lock); - { - unsigned int status; - mask_and_ack_8259A(irq); - status = desc->status & ~IRQ_REPLAY; - action = NULL; - if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) - action = desc->action; - desc->status = status | IRQ_INPROGRESS; - } - spin_unlock(&irq_controller_lock); - - /* Exit early if we had no action or it was disabled */ - if (!action) - return; - - handle_IRQ_event(irq, regs, action); - - spin_lock(&irq_controller_lock); - { - unsigned int status = desc->status & ~IRQ_INPROGRESS; - desc->status = status; - if (!(status & IRQ_DISABLED)) - enable_8259A_irq(irq); - } - spin_unlock(&irq_controller_lock); -} - - /* * Generic enable/disable code: this just calls * down into the PIC-specific version for the actual @@ -691,8 +723,10 @@ void disable_irq(unsigned int irq) unsigned long flags; spin_lock_irqsave(&irq_controller_lock, flags); - irq_desc[irq].status |= IRQ_DISABLED; - irq_desc[irq].handler->disable(irq); + if (!irq_desc[irq].depth++) { + irq_desc[irq].status |= IRQ_DISABLED; + irq_desc[irq].handler->disable(irq); + } spin_unlock_irqrestore(&irq_controller_lock, flags); if (irq_desc[irq].status & IRQ_INPROGRESS) @@ -704,16 +738,18 @@ void enable_irq(unsigned int irq) unsigned long flags; spin_lock_irqsave(&irq_controller_lock, flags); - /* - * In contrast to the above, we should _not_ have any concurrent - * interrupt activity here, so we just clear both disabled bits. - * - * This allows us to have IRQ_INPROGRESS set until we actually - * install a handler for this interrupt (make irq autodetection - * work by just looking at the status field for the irq) - */ - irq_desc[irq].status &= ~(IRQ_DISABLED | IRQ_INPROGRESS); - irq_desc[irq].handler->enable(irq); + switch (irq_desc[irq].depth) { + case 1: + irq_desc[irq].status &= ~(IRQ_DISABLED | IRQ_INPROGRESS); + irq_desc[irq].handler->enable(irq); + /* fall throught */ + default: + irq_desc[irq].depth--; + break; + case 0: + printk("enable_irq() unbalanced from %p\n", + __builtin_return_address(0)); + } spin_unlock_irqrestore(&irq_controller_lock, flags); } @@ -798,6 +834,7 @@ int setup_x86_irq(unsigned int irq, struct irqaction * new) *p = new; if (!shared) { + irq_desc[irq].depth = 0; irq_desc[irq].status &= ~(IRQ_DISABLED | IRQ_INPROGRESS); irq_desc[irq].handler->startup(irq); } @@ -894,7 +931,7 @@ unsigned long probe_irq_on(void) /* * Wait for spurious interrupts to trigger */ - for (delay = jiffies + HZ/10; delay > jiffies; ) + for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) /* about 100ms delay */ synchronize_irq(); /* @@ -949,21 +986,75 @@ int probe_irq_off(unsigned long unused) return irq_found; } -__initfunc(void init_IRQ(void)) +/* + * Silly, horrible hack + */ +static char uglybuffer[10*256]; + +__asm__("\n" __ALIGN_STR"\n" + "common_unexpected:\n\t" + SAVE_ALL + "pushl $ret_from_intr\n\t" + "jmp strange_interrupt"); + +void strange_interrupt(int irqnum) +{ + printk("Unexpected interrupt %d\n", irqnum & 255); + for (;;); +} + +extern int common_unexpected; +__initfunc(void init_unexpected_irq(void)) { int i; + for (i = 0; i < 256; i++) { + char *code = uglybuffer + 10*i; + unsigned long jumpto = (unsigned long) &common_unexpected; - /* set the clock to 100 Hz */ - outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ - outb_p(LATCH & 0xff , 0x40); /* LSB */ - outb(LATCH >> 8 , 0x40); /* MSB */ + jumpto -= (unsigned long)(code+10); + code[0] = 0x68; /* pushl */ + *(int *)(code+1) = i - 512; + code[5] = 0xe9; /* jmp */ + *(int *)(code+6) = jumpto; + + set_intr_gate(i,code); + } +} + + +void init_ISA_irqs (void) +{ + int i; - for (i=0; i<NR_IRQS; i++) + for (i = 0; i < NR_IRQS; i++) { irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 0; + + if (i < 16) { + /* + * 16 old-style INTA-cycle interrupt gates: + */ + irq_desc[i].handler = &i8259A_irq_type; + } else { + /* + * 'high' PCI IRQs filled in on demand + */ + irq_desc[i].handler = &no_irq_type; + } + } +} + +__initfunc(void init_IRQ(void)) +{ + int i; + +#ifndef CONFIG_X86_VISWS_APIC + init_ISA_irqs(); +#else + init_VISWS_APIC_irqs(); +#endif - /* - * 16 old-style INTA-cycle interrupt gates: - */ for (i = 0; i < 16; i++) set_intr_gate(0x20+i,interrupt[i]); @@ -983,31 +1074,41 @@ __initfunc(void init_IRQ(void)) */ /* IPI for rescheduling */ - set_intr_gate(0x30, reschedule_interrupt); + set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); /* IPI for invalidation */ - set_intr_gate(0x31, invalidate_interrupt); + set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); /* IPI for CPU halt */ - set_intr_gate(0x40, stop_cpu_interrupt); + set_intr_gate(STOP_CPU_VECTOR, stop_cpu_interrupt); /* self generated IPI for local APIC timer */ - set_intr_gate(0x41, apic_timer_interrupt); + set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt); /* IPI for MTRR control */ - set_intr_gate(0x50, mtrr_interrupt); + set_intr_gate(MTRR_CHANGE_VECTOR, mtrr_interrupt); /* IPI vector for APIC spurious interrupts */ - set_intr_gate(0xff, spurious_interrupt); + set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); #endif request_region(0x20,0x20,"pic1"); request_region(0xa0,0x20,"pic2"); + + /* + * Set the clock to 100 Hz, we already have a valid + * vector now: + */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + +#ifndef CONFIG_VISWS setup_x86_irq(2, &irq2); setup_x86_irq(13, &irq13); +#endif } -#ifdef __SMP__ - +#ifdef CONFIG_X86_IO_APIC __initfunc(void init_IRQ_SMP(void)) { int i; @@ -1015,5 +1116,5 @@ __initfunc(void init_IRQ_SMP(void)) if (IO_APIC_VECTOR(i) > 0) set_intr_gate(IO_APIC_VECTOR(i), interrupt[i]); } - #endif + diff --git a/arch/i386/kernel/irq.h b/arch/i386/kernel/irq.h index 9f0b3e457..982ab101e 100644 --- a/arch/i386/kernel/irq.h +++ b/arch/i386/kernel/irq.h @@ -37,10 +37,31 @@ typedef struct { unsigned int status; /* IRQ status - IRQ_INPROGRESS, IRQ_DISABLED */ struct hw_interrupt_type *handler; /* handle/enable/disable functions */ struct irqaction *action; /* IRQ action list */ - unsigned int unused[3]; + unsigned int depth; /* Disable depth for nested irq disables */ } irq_desc_t; -#define IRQ0_TRAP_VECTOR 0x51 +/* + * Special IRQ vectors used by the SMP architecture: + * + * (some of the following vectors are 'rare', they might be merged + * into a single vector to save vector space. TLB, reschedule and + * local APIC vectors are performance-critical.) + */ +#define RESCHEDULE_VECTOR 0x30 +#define INVALIDATE_TLB_VECTOR 0x31 +#define STOP_CPU_VECTOR 0x40 +#define LOCAL_TIMER_VECTOR 0x41 +#define MTRR_CHANGE_VECTOR 0x50 + +/* + * First vector available to drivers: (vectors 0x51-0xfe) + */ +#define IRQ0_TRAP_VECTOR 0x51 + +/* + * This IRQ should never happen, but we print a message nevertheless. + */ +#define SPURIOUS_APIC_VECTOR 0xff extern irq_desc_t irq_desc[NR_IRQS]; extern int irq_vector[NR_IRQS]; @@ -48,6 +69,7 @@ extern int irq_vector[NR_IRQS]; extern void init_IRQ_SMP(void); extern int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); +extern int setup_x86_irq(unsigned int, struct irqaction *); /* * Various low-level irq details needed by irq.c, process.c, @@ -56,17 +78,21 @@ extern int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); * Interrupt entry/exit code at both C and assembly level */ -void mask_irq(unsigned int irq); -void unmask_irq(unsigned int irq); -void disable_8259A_irq(unsigned int irq); -int i8259A_irq_pending(unsigned int irq); -void ack_APIC_irq(void); -void setup_IO_APIC(void); -int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn); -void make_8259A_irq(unsigned int irq); -void send_IPI(int dest, int vector); -void init_pic_mode(void); -void print_IO_APIC(void); +extern void no_action(int cpl, void *dev_id, struct pt_regs *regs); +extern void mask_irq(unsigned int irq); +extern void unmask_irq(unsigned int irq); +extern void disable_8259A_irq(unsigned int irq); +extern int i8259A_irq_pending(unsigned int irq); +extern void ack_APIC_irq(void); +extern void FASTCALL(send_IPI_self(int vector)); +extern void smp_send_mtrr(void); +extern void init_VISWS_APIC_irqs(void); +extern void setup_IO_APIC(void); +extern int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn); +extern void make_8259A_irq(unsigned int irq); +extern void send_IPI(int dest, int vector); +extern void init_pic_mode(void); +extern void print_IO_APIC(void); extern unsigned long long io_apic_irqs; @@ -81,11 +107,7 @@ extern int mp_bus_id_to_pci_bus [MAX_MP_BUSSES]; extern char ioapic_OEM_ID [16]; extern char ioapic_Product_ID [16]; -extern spinlock_t irq_controller_lock; /* - * Protects both the 8259 and the - * IO-APIC - */ - +extern spinlock_t irq_controller_lock; #ifdef __SMP__ diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index 324e8cec7..16c767b4a 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -164,6 +164,9 @@ #include <asm/bitops.h> #include <asm/atomic.h> +#include <asm/hardirq.h> +#include "irq.h" + #define MTRR_VERSION "1.26 (19981001)" #define TRUE 1 @@ -612,10 +615,11 @@ static void do_all_cpus (void (*handler) (struct set_mtrr_context *ctxt, /* Send a message to all other CPUs and wait for them to enter the barrier */ atomic_set (&undone_count, smp_num_cpus - 1); - smp_message_pass (MSG_ALL_BUT_SELF, MSG_MTRR_CHANGE, 0, 0); + smp_send_mtrr(); /* Wait for it to be done */ timeout = jiffies + JIFFIE_TIMEOUT; - while ( (atomic_read (&undone_count) > 0) && (jiffies < timeout) ) + while ( (atomic_read (&undone_count) > 0) && + time_before(jiffies, timeout) ) barrier (); if (atomic_read (&undone_count) > 0) { diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 00f39d4ed..00f7e0ba2 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -105,19 +105,24 @@ static void hard_idle(void) */ static int cpu_idle(void *unused) { - unsigned long start_idle = jiffies; + int work = 1; + unsigned long start_idle = 0; /* endless idle loop with no priority at all */ + current->priority = 0; + current->counter = -100; for (;;) { + if (work) + start_idle = jiffies; + if (jiffies - start_idle > HARD_IDLE_TIMEOUT) hard_idle(); else { if (boot_cpu_data.hlt_works_ok && !hlt_counter && !current->need_resched) __asm__("hlt"); } - if (current->need_resched) - start_idle = jiffies; - current->policy = SCHED_YIELD; + + work = current->need_resched; schedule(); check_pgt_cache(); } @@ -131,14 +136,21 @@ static int cpu_idle(void *unused) int cpu_idle(void *unused) { - /* endless idle loop with no priority at all */ + current->priority = 0; + current->counter = -100; while(1) { - if (current_cpu_data.hlt_works_ok && !hlt_counter && !current->need_resched) + if (current_cpu_data.hlt_works_ok && !hlt_counter && + !current->need_resched) __asm__("hlt"); - current->policy = SCHED_YIELD; - schedule(); - check_pgt_cache(); + /* + * although we are an idle CPU, we do not want to + * get into the scheduler unnecessarily. + */ + if (current->need_resched) { + schedule(); + check_pgt_cache(); + } } } @@ -463,24 +475,27 @@ void free_task_struct(struct task_struct *p) void release_segments(struct mm_struct *mm) { - /* forget local segments */ - __asm__ __volatile__("movl %w0,%%fs ; movl %w0,%%gs" - : /* no outputs */ - : "r" (0)); if (mm->segments) { void * ldt = mm->segments; - - /* - * Get the LDT entry from init_task. - */ - current->tss.ldt = _LDT(0); - load_ldt(0); - mm->segments = NULL; vfree(ldt); } } +void forget_segments(void) +{ + /* forget local segments */ + __asm__ __volatile__("movl %w0,%%fs ; movl %w0,%%gs" + : /* no outputs */ + : "r" (0)); + + /* + * Get the LDT entry from init_task. + */ + current->tss.ldt = _LDT(0); + load_ldt(0); +} + /* * Create a kernel thread */ @@ -579,7 +594,6 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, *childregs = *regs; childregs->eax = 0; childregs->esp = esp; - childregs->eflags = regs->eflags & 0xffffcfff; /* iopl always 0 for a new process */ p->tss.esp = (unsigned long) childregs; p->tss.esp0 = (unsigned long) (childregs+1); @@ -771,6 +785,21 @@ asmlinkage int sys_clone(struct pt_regs regs) } /* + * This is trivial, and on the face of it looks like it + * could equally well be done in user mode. + * + * Not so, for quite unobvious reasons - register pressure. + * In user mode vfork() cannot have a stack frame, and if + * done by calling the "clone()" system call directly, you + * do not have enough call-clobbered registers to hold all + * the information you need. + */ +asmlinkage int sys_vfork(struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s); +} + +/* * sys_execve() executes a new program. */ asmlinkage int sys_execve(struct pt_regs regs) diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index 52a1543c3..b0eca4345 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -367,8 +367,6 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ret = 0; goto out; } - if (pid == 1) /* you may not mess with init */ - goto out; ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); @@ -376,6 +374,8 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if (!child) goto out; ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out; if (request == PTRACE_ATTACH) { if (child == current) goto out; @@ -420,7 +420,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_PEEKDATA: { unsigned long tmp; + down(&child->mm->mmap_sem); ret = read_long(child, addr, &tmp); + up(&child->mm->mmap_sem); if (ret >= 0) ret = put_user(tmp,(unsigned long *) data); goto out; @@ -451,7 +453,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: + down(&child->mm->mmap_sem); ret = write_long(child,addr,data); + up(&child->mm->mmap_sem); goto out; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 490c4db82..c3f34270a 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -38,6 +38,7 @@ #include <asm/system.h> #include <asm/io.h> #include <asm/smp.h> +#include <asm/cobalt.h> /* * Machine setup.. @@ -45,7 +46,6 @@ char ignore_irq13 = 0; /* set if exception 16 works */ struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; -static char Cx86_step[8]; /* decoded Cyrix step number */ /* * Bus types .. @@ -108,6 +108,133 @@ extern unsigned long cpu_hz; #define RAMDISK_PROMPT_FLAG 0x8000 #define RAMDISK_LOAD_FLAG 0x4000 +#ifdef CONFIG_VISWS +char visws_board_type = -1; +char visws_board_rev = -1; + +#define PIIX_PM_START 0x0F80 + +#define SIO_GPIO_START 0x0FC0 + +#define SIO_PM_START 0x0FC8 + +#define PMBASE PIIX_PM_START +#define GPIREG0 (PMBASE+0x30) +#define GPIREG(x) (GPIREG0+((x)/8)) +#define PIIX_GPI_BD_ID1 18 +#define PIIX_GPI_BD_REG GPIREG(PIIX_GPI_BD_ID1) + +#define PIIX_GPI_BD_SHIFT (PIIX_GPI_BD_ID1 % 8) + +#define SIO_INDEX 0x2e +#define SIO_DATA 0x2f + +#define SIO_DEV_SEL 0x7 +#define SIO_DEV_ENB 0x30 +#define SIO_DEV_MSB 0x60 +#define SIO_DEV_LSB 0x61 + +#define SIO_GP_DEV 0x7 + +#define SIO_GP_BASE SIO_GPIO_START +#define SIO_GP_MSB (SIO_GP_BASE>>8) +#define SIO_GP_LSB (SIO_GP_BASE&0xff) + +#define SIO_GP_DATA1 (SIO_GP_BASE+0) + +#define SIO_PM_DEV 0x8 + +#define SIO_PM_BASE SIO_PM_START +#define SIO_PM_MSB (SIO_PM_BASE>>8) +#define SIO_PM_LSB (SIO_PM_BASE&0xff) +#define SIO_PM_INDEX (SIO_PM_BASE+0) +#define SIO_PM_DATA (SIO_PM_BASE+1) + +#define SIO_PM_FER2 0x1 + +#define SIO_PM_GP_EN 0x80 + +static void +visws_get_board_type_and_rev(void) +{ + int raw; + + visws_board_type = (char)(inb_p(PIIX_GPI_BD_REG) & PIIX_GPI_BD_REG) + >> PIIX_GPI_BD_SHIFT; +/* + * Get Board rev. + * First, we have to initialize the 307 part to allow us access + * to the GPIO registers. Let's map them at 0x0fc0 which is right + * after the PIIX4 PM section. + */ + outb_p(SIO_DEV_SEL, SIO_INDEX); + outb_p(SIO_GP_DEV, SIO_DATA); /* Talk to GPIO regs. */ + + outb_p(SIO_DEV_MSB, SIO_INDEX); + outb_p(SIO_GP_MSB, SIO_DATA); /* MSB of GPIO base address */ + + outb_p(SIO_DEV_LSB, SIO_INDEX); + outb_p(SIO_GP_LSB, SIO_DATA); /* LSB of GPIO base address */ + + outb_p(SIO_DEV_ENB, SIO_INDEX); + outb_p(1, SIO_DATA); /* Enable GPIO registers. */ + +/* + * Now, we have to map the power management section to write + * a bit which enables access to the GPIO registers. + * What lunatic came up with this shit? + */ + outb_p(SIO_DEV_SEL, SIO_INDEX); + outb_p(SIO_PM_DEV, SIO_DATA); /* Talk to GPIO regs. */ + + outb_p(SIO_DEV_MSB, SIO_INDEX); + outb_p(SIO_PM_MSB, SIO_DATA); /* MSB of PM base address */ + + outb_p(SIO_DEV_LSB, SIO_INDEX); + outb_p(SIO_PM_LSB, SIO_DATA); /* LSB of PM base address */ + + outb_p(SIO_DEV_ENB, SIO_INDEX); + outb_p(1, SIO_DATA); /* Enable PM registers. */ + +/* + * Now, write the PM register which enables the GPIO registers. + */ + outb_p(SIO_PM_FER2, SIO_PM_INDEX); + outb_p(SIO_PM_GP_EN, SIO_PM_DATA); + +/* + * Now, initialize the GPIO registers. + * We want them all to be inputs which is the + * power on default, so let's leave them alone. + * So, let's just read the board rev! + */ + raw = inb_p(SIO_GP_DATA1); + raw &= 0x7f; /* 7 bits of valid board revision ID. */ + + if (visws_board_type == VISWS_320) { + if (raw < 0x6) { + visws_board_rev = 4; + } else if (raw < 0xc) { + visws_board_rev = 5; + } else { + visws_board_rev = 6; + + } + } else if (visws_board_type == VISWS_540) { + visws_board_rev = 2; + } else { + visws_board_rev = raw; + } + + printk("Silicon Graphics %s (rev %d)\n", + visws_board_type == VISWS_320 ? "320" : + (visws_board_type == VISWS_540 ? "540" : + "unknown"), + visws_board_rev); + } +#endif + + static char command_line[COMMAND_LINE_SIZE] = { 0, }; char saved_command_line[COMMAND_LINE_SIZE]; @@ -123,6 +250,10 @@ __initfunc(void setup_arch(char **cmdline_p, return; smptrap=1; +#ifdef CONFIG_VISWS + visws_get_board_type_and_rev(); +#endif + ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; @@ -146,12 +277,6 @@ __initfunc(void setup_arch(char **cmdline_p, } #endif -#define VMALLOC_RESERVE (64 << 20) /* 64MB for vmalloc */ -#define MAXMEM ((unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE)) - - if (memory_end > MAXMEM) - memory_end = MAXMEM; - memory_end &= PAGE_MASK; #ifdef CONFIG_BLK_DEV_RAM rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; @@ -201,10 +326,28 @@ __initfunc(void setup_arch(char **cmdline_p, } *to = '\0'; *cmdline_p = command_line; + +#define VMALLOC_RESERVE (64 << 20) /* 64MB for vmalloc */ +#define MAXMEM ((unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE)) + + if (memory_end > MAXMEM) + { + memory_end = MAXMEM; + printk(KERN_WARNING "Warning only %ldMB will be used.\n", + MAXMEM>>20); + } + memory_end += PAGE_OFFSET; *memory_start_p = memory_start; *memory_end_p = memory_end; +#ifdef __SMP__ + /* + * Save possible boot-time SMP configuration: + */ + init_smp_config(); +#endif + #ifdef CONFIG_BLK_DEV_INITRD if (LOADER_TYPE) { initrd_start = INITRD_START ? INITRD_START + PAGE_OFFSET : 0; @@ -232,6 +375,10 @@ __initfunc(void setup_arch(char **cmdline_p, conswitchp = &dummy_con; #endif #endif + /* + * Check the bugs that will bite us before we get booting + */ + } __initfunc(static int amd_model(struct cpuinfo_x86 *c)) @@ -245,6 +392,7 @@ __initfunc(static int amd_model(struct cpuinfo_x86 *c)) cpuid(0x80000000, &n, &dummy, &dummy, &dummy); if (n < 4) return 0; + cpuid(0x80000001, &dummy, &dummy, &dummy, &(c->x86_capability)); v = (unsigned int *) c->x86_model_id; cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); @@ -254,9 +402,9 @@ __initfunc(static int amd_model(struct cpuinfo_x86 *c)) } /* - * Use the Cyrix DEVID CPU registers if avail. to get more detailed info. + * Read Cyrix DEVID registers (DIR) to get more detailed info. about the CPU */ -__initfunc(static void do_cyrix_devid(struct cpuinfo_x86 *c)) +static inline void do_cyrix_devid(unsigned char *dir0, unsigned char *dir1) { unsigned char ccr2, ccr3; @@ -272,22 +420,28 @@ __initfunc(static void do_cyrix_devid(struct cpuinfo_x86 *c)) getCx86(0xc0); /* dummy */ if (getCx86(CX86_CCR2) == ccr2) /* old Cx486SLC/DLC */ - c->x86_model = 0xfd; + *dir0 = 0xfd; else { /* Cx486S A step */ setCx86(CX86_CCR2, ccr2); - c->x86_model = 0xfe; + *dir0 = 0xfe; } } else { setCx86(CX86_CCR3, ccr3); /* restore CCR3 */ /* read DIR0 and DIR1 CPU registers */ - c->x86_model = getCx86(CX86_DIR0); - c->x86_mask = getCx86(CX86_DIR1); + *dir0 = getCx86(CX86_DIR0); + *dir1 = getCx86(CX86_DIR1); } sti(); } +/* + * Cx86_dir0_msb is a HACK needed by check_cx686_cpuid/slop in bugs.h in + * order to identify the Cyrix CPU model after we're out of setup.c + */ +unsigned char Cx86_dir0_msb __initdata = 0; + static char Cx86_model[][9] __initdata = { "Cx486", "Cx486", "5x86 ", "6x86", "MediaGX ", "6x86MX ", "M II ", "Unknown" @@ -305,27 +459,28 @@ static char Cx486D_name[][4] __initdata = { static char Cx86_cb[] __initdata = "?.5x Core/Bus Clock"; static char cyrix_model_mult1[] __initdata = "12??43"; static char cyrix_model_mult2[] __initdata = "12233445"; -static char cyrix_model_oldstep[] __initdata = "A step"; __initfunc(static void cyrix_model(struct cpuinfo_x86 *c)) { - unsigned char dir0_msn, dir0_lsn, dir1; + unsigned char dir0, dir0_msn, dir0_lsn, dir1 = 0; char *buf = c->x86_model_id; const char *p = NULL; - do_cyrix_devid(c); + do_cyrix_devid(&dir0, &dir1); - dir0_msn = c->x86_model >> 4; - dir0_lsn = c->x86_model & 0xf; - dir1 = c->x86_mask; + Cx86_dir0_msb = dir0_msn = dir0 >> 4; /* identifies CPU "family" */ + dir0_lsn = dir0 & 0xf; /* model or clock multiplier */ - /* common case stepping number -- exceptions handled below */ - sprintf(Cx86_step, "%d.%d", (dir1 >> 4) + 1, dir1 & 0x0f); + /* common case step number/rev -- exceptions handled below */ + c->x86_model = (dir1 >> 4) + 1; + c->x86_mask = dir1 & 0xf; /* Now cook; the original recipe is by Channing Corn, from Cyrix. * We do the same thing for each generation: we work out - * the model, multiplier and stepping. + * the model, multiplier and stepping. Black magic included, + * to make the silicon step/rev numbers match the printed ones. */ + switch (dir0_msn) { unsigned char tmp; @@ -349,37 +504,41 @@ __initfunc(static void cyrix_model(struct cpuinfo_x86 *c)) if (dir1 > 0x21) { /* 686L */ Cx86_cb[0] = 'L'; p = Cx86_cb; - Cx86_step[0]++; + (c->x86_model)++; } else /* 686 */ p = Cx86_cb+1; break; case 4: /* MediaGX/GXm */ + /* + * Life sometimes gets weiiiiiiiird if we use this + * on the MediaGX. So we turn it off for now. + */ + /* GXm supports extended cpuid levels 'ala' AMD */ if (c->cpuid_level == 2) { amd_model(c); /* get CPU marketing name */ + c->x86_capability&=~X86_FEATURE_TSC; return; } else { /* MediaGX */ Cx86_cb[2] = (dir0_lsn & 1) ? '3' : '4'; p = Cx86_cb+2; - Cx86_step[0] = (dir1 & 0x20) ? '1' : '2'; + c->x86_model = (dir1 & 0x20) ? 1 : 2; + c->x86_capability&=~X86_FEATURE_TSC; } break; case 5: /* 6x86MX/M II */ - /* the TSC is broken (for now) */ - c->x86_capability &= ~16; - if (dir1 > 7) dir0_msn++; /* M II */ tmp = (!(dir0_lsn & 7) || dir0_lsn & 1) ? 2 : 0; Cx86_cb[tmp] = cyrix_model_mult2[dir0_lsn & 7]; p = Cx86_cb+tmp; if (((dir1 & 0x0f) > 4) || ((dir1 & 0xf0) == 0x20)) - Cx86_step[0]++; + (c->x86_model)++; break; - case 0xf: /* Cyrix 486 without DIR registers */ + case 0xf: /* Cyrix 486 without DEVID registers */ switch (dir0_lsn) { case 0xd: /* either a 486SLC or DLC w/o DEVID */ dir0_msn = 0; @@ -389,11 +548,13 @@ __initfunc(static void cyrix_model(struct cpuinfo_x86 *c)) case 0xe: /* a 486S A step */ dir0_msn = 0; p = Cx486S_name[0]; - strcpy(Cx86_step, cyrix_model_oldstep); - c->x86_mask = 1; /* must != 0 to print */ break; break; } + + default: /* unknown (shouldn't happen, we know everyone ;-) */ + dir0_msn = 7; + break; } strcpy(buf, Cx86_model[dir0_msn & 7]); if (p) strcat(buf, p); @@ -445,15 +606,15 @@ static struct cpu_model_info cpu_models[] __initdata = { "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }}, { X86_VENDOR_AMD, 5, - { "K5/SSA5 (PR75, PR90, PR100)", "K5 (PR120, PR133)", - "K5 (PR166)", "K5 (PR200)", NULL, NULL, - "K6 (PR166 - PR266)", "K6 (PR166 - PR300)", "K6-2 (PR233 - PR333)", - "K6-3 (PR300 - PR450)", NULL, NULL, NULL, NULL, NULL, NULL }}, + { "K5/SSA5", "K5", + "K5", "K5", NULL, NULL, + "K6", "K6", "K6-2", + "K6-3", NULL, NULL, NULL, NULL, NULL, NULL }}, { X86_VENDOR_UMC, 4, { NULL, "U5D", "U5S", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, { X86_VENDOR_CENTAUR, 5, - { NULL, NULL, NULL, NULL, "C6", NULL, NULL, NULL, NULL, NULL, NULL, + { NULL, NULL, NULL, NULL, "C6", NULL, NULL, NULL, "C6-2", NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, { X86_VENDOR_NEXGEN, 5, { "Nx586", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -479,6 +640,9 @@ __initfunc(void identify_cpu(struct cpuinfo_x86 *c)) return; } + if (c->x86_vendor == X86_VENDOR_AMD && amd_model(c)) + return; + for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) { if (c->cpuid_level > 1) { /* supports eax=2 call */ @@ -543,15 +707,39 @@ __initfunc(void identify_cpu(struct cpuinfo_x86 *c)) return; } - if (c->x86_vendor == X86_VENDOR_AMD && amd_model(c)) - return; - sprintf(c->x86_model_id, "%02x/%02x", c->x86_vendor, c->x86_model); } +/* + * Perform early boot up checks for a valid TSC. See arch/i386/kernel/time.c + */ + +__initfunc(void dodgy_tsc(void)) +{ + get_cpu_vendor(&boot_cpu_data); + + if(boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX) + { + return; + } + cyrix_model(&boot_cpu_data); +} + + +#define rdmsr(msr,val1,val2) \ + __asm__ __volatile__("rdmsr" \ + : "=a" (val1), "=d" (val2) \ + : "c" (msr)) + +#define wrmsr(msr,val1,val2) \ + __asm__ __volatile__("wrmsr" \ + : /* no outputs */ \ + : "c" (msr), "a" (val1), "d" (val2)) + static char *cpu_vendor_names[] __initdata = { "Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur" }; + __initfunc(void print_cpu_info(struct cpuinfo_x86 *c)) { char *vendor = NULL; @@ -569,11 +757,19 @@ __initfunc(void print_cpu_info(struct cpuinfo_x86 *c)) else printk("%s", c->x86_model_id); - if (c->x86_mask) { - if (c->x86_vendor == X86_VENDOR_CYRIX) - printk(" stepping %s", Cx86_step); - else - printk(" stepping %02x", c->x86_mask); + if (c->x86_mask || c->cpuid_level>=0) + printk(" stepping %02x", c->x86_mask); + + if(c->x86_vendor == X86_VENDOR_CENTAUR) + { + u32 hv,lv; + rdmsr(0x107, lv, hv); + printk("\nCentaur FSR was 0x%X ",lv); + lv|=(1<<8); + lv|=(1<<7); + /* lv|=(1<<6); - may help too if the board can cope */ + printk("now 0x%X", lv); + wrmsr(0x107, lv, hv); } printk("\n"); } @@ -597,7 +793,7 @@ int get_cpuinfo(char * buffer) for(n=0; n<NR_CPUS; n++, c++) { #ifdef __SMP__ - if (!(cpu_present_map & (1<<n))) + if (!(cpu_online_map & (1<<n))) continue; #endif p += sprintf(p,"processor\t: %d\n" @@ -611,12 +807,9 @@ int get_cpuinfo(char * buffer) c->x86_model, c->x86_model_id[0] ? c->x86_model_id : "unknown"); - if (c->x86_mask) { - if (c->x86_vendor == X86_VENDOR_CYRIX) - p += sprintf(p, "stepping\t: %s\n", Cx86_step); - else - p += sprintf(p, "stepping\t: %d\n", c->x86_mask); - } else + if (c->x86_mask) + p += sprintf(p, "stepping\t: %d\n", c->x86_mask); + else p += sprintf(p, "stepping\t: unknown\n"); if (c->x86_capability & X86_FEATURE_TSC) { @@ -629,21 +822,21 @@ int get_cpuinfo(char * buffer) p += sprintf(p, "cache size\t: %d KB\n", c->x86_cache_size); /* Modify the capabilities according to chip type */ - if (c->x86_mask) { - if (c->x86_vendor == X86_VENDOR_CYRIX) { - x86_cap_flags[24] = "cxmmx"; - } else if (c->x86_vendor == X86_VENDOR_AMD) { - x86_cap_flags[16] = "fcmov"; - x86_cap_flags[31] = "amd3d"; - } else if (c->x86_vendor == X86_VENDOR_INTEL) { - x86_cap_flags[6] = "pae"; - x86_cap_flags[9] = "apic"; - x86_cap_flags[12] = "mtrr"; - x86_cap_flags[14] = "mca"; - x86_cap_flags[16] = "pat"; - x86_cap_flags[17] = "pse36"; - x86_cap_flags[24] = "osfxsr"; - } + if (c->x86_vendor == X86_VENDOR_CYRIX) { + x86_cap_flags[24] = "cxmmx"; + } else if (c->x86_vendor == X86_VENDOR_AMD) { + x86_cap_flags[16] = "fcmov"; + x86_cap_flags[31] = "3dnow"; + if (c->x86 == 5 && c->x86_model == 6) + x86_cap_flags[10] = "sep"; + } else if (c->x86_vendor == X86_VENDOR_INTEL) { + x86_cap_flags[6] = "pae"; + x86_cap_flags[9] = "apic"; + x86_cap_flags[12] = "mtrr"; + x86_cap_flags[14] = "mca"; + x86_cap_flags[16] = "pat"; + x86_cap_flags[17] = "pse36"; + x86_cap_flags[24] = "osfxsr"; } sep_bug = c->x86_vendor == X86_VENDOR_INTEL && diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index a5f1f2de0..2960d521c 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -3,12 +3,14 @@ * hosts. * * (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net> + * (c) 1998 Ingo Molnar + * * Supported by Caldera http://www.caldera.com. * Much of the core SMP work is based on previous work by Thomas Radke, to * whom a great many thanks are extended. * - * Thanks to Intel for making available several different Pentium and - * Pentium Pro MP machines. + * Thanks to Intel for making available several different Pentium, + * Pentium Pro and Pentium-II/Xeon MP machines. * * This code is released under the GNU public license version 2 or * later. @@ -26,34 +28,17 @@ * Ingo Molnar : Added APIC timers, based on code * from Jose Renau * Alan Cox : Added EBDA scanning + * Ingo Molnar : various cleanups and rewrites */ #include <linux/config.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/sched.h> #include <linux/mm.h> #include <linux/kernel_stat.h> #include <linux/delay.h> #include <linux/mc146818rtc.h> -#include <asm/i82489.h> -#include <linux/smp.h> #include <linux/smp_lock.h> -#include <linux/interrupt.h> #include <linux/init.h> -#include <asm/pgtable.h> -#include <asm/bitops.h> -#include <asm/pgtable.h> -#include <asm/smp.h> -#include <asm/io.h> - -#ifdef CONFIG_MTRR -# include <asm/mtrr.h> -#endif - -#define __KERNEL_SYSCALLS__ -#include <linux/unistd.h> +#include <asm/mtrr.h> #include "irq.h" @@ -104,31 +89,24 @@ extern void update_one_process( struct task_struct *p, spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; /* - * Why isn't this somewhere standard ?? - * - * Maybe because this procedure is horribly buggy, and does - * not deserve to live. Think about signedness issues for five - * seconds to see why. - Linus + * function prototypes: */ - -extern __inline int max(int a,int b) -{ - if (a>b) - return a; - return b; -} +static void cache_APIC_registers (void); +static void stop_this_cpu (void); static int smp_b_stepping = 0; /* Set if we find a B stepping CPU */ static int max_cpus = -1; /* Setup configured maximum number of CPUs to activate */ int smp_found_config=0; /* Have we found an SMP box */ -unsigned long cpu_present_map = 0; /* Bitmask of existing CPUs */ +unsigned long cpu_present_map = 0; /* Bitmask of physically existing CPUs */ +unsigned long cpu_online_map = 0; /* Bitmask of currently online CPUs */ int smp_num_cpus = 1; /* Total count of live CPUs */ int smp_threads_ready=0; /* Set when the idlers are all forked */ volatile int cpu_number_map[NR_CPUS]; /* which CPU maps to which logical number */ volatile int __cpu_logical_map[NR_CPUS]; /* which logical number maps to which CPU */ -volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; /* We always use 0 the rest is ready for parallel delivery */ +static volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; /* We always use 0 the rest is ready for parallel delivery */ +static volatile unsigned long cpu_callout_map[NR_CPUS] = {0,}; /* We always use 0 the rest is ready for parallel delivery */ volatile unsigned long smp_invalidate_needed; /* Used for the invalidate map that's also checked in the spinlock */ volatile unsigned long kstack_ptr; /* Stack vector for booting CPUs */ struct cpuinfo_x86 cpu_data[NR_CPUS]; /* Per CPU bogomips and other parameters */ @@ -137,19 +115,13 @@ unsigned long mp_ioapic_addr = 0xFEC00000; /* Address of the I/O apic (not yet unsigned char boot_cpu_id = 0; /* Processor that is doing the boot up */ static int smp_activated = 0; /* Tripped once we need to start cross invalidating */ int apic_version[NR_CPUS]; /* APIC version number */ -static volatile int smp_commenced=0; /* Tripped when we start scheduling */ unsigned long apic_retval; /* Just debugging the assembler.. */ -static volatile unsigned char smp_cpu_in_msg[NR_CPUS]; /* True if this processor is sending an IPI */ - volatile unsigned long kernel_counter=0; /* Number of times the processor holds the lock */ volatile unsigned long syscall_count=0; /* Number of times the processor holds the syscall lock */ volatile unsigned long ipi_count; /* Number of IPIs delivered */ -volatile unsigned long smp_proc_in_lock[NR_CPUS] = {0,};/* for computing process time */ -volatile int smp_process_available=0; - const char lk_lockmsg[] = "lock from interrupt context at %p\n"; int mp_bus_id_to_type [MAX_MP_BUSSES] = { -1, }; @@ -159,6 +131,7 @@ extern int mpc_default_type; int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { -1, }; int mp_current_pci_id = 0; unsigned long mp_lapic_addr = 0; +int skip_ioapic_setup = 0; /* 1 if "noapic" boot option passed */ /* #define SMP_DEBUG */ @@ -169,6 +142,11 @@ unsigned long mp_lapic_addr = 0; #endif /* + * IA s/w dev Vol 3, Section 7.4 + */ +#define APIC_DEFAULT_PHYS_BASE 0xfee00000 + +/* * Setup routine for controlling SMP activation * * Command-line option of "nosmp" or "maxcpus=0" will disable SMP @@ -199,6 +177,11 @@ void ack_APIC_irq(void) } /* + * Intel MP BIOS table parsing routines: + */ + +#ifndef CONFIG_X86_VISWS_APIC +/* * Checksum an MP configuration block. */ @@ -250,7 +233,7 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) if (memcmp(mpc->mpc_signature,MPC_SIGNATURE,4)) { - printk("Bad signature [%c%c%c%c].\n", + panic("SMP mptable: bad signature [%c%c%c%c]!\n", mpc->mpc_signature[0], mpc->mpc_signature[1], mpc->mpc_signature[2], @@ -259,7 +242,7 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) } if (mpf_checksum((unsigned char *)mpc,mpc->mpc_length)) { - printk("Checksum error.\n"); + panic("SMP mptable: checksum error!\n"); return 1; } if (mpc->mpc_spec!=0x01 && mpc->mpc_spec!=0x04) @@ -405,7 +388,11 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) } } if (ioapics > 1) + { printk("Warning: Multiple IO-APICs not yet supported.\n"); + printk("Warning: switching to non APIC mode.\n"); + skip_ioapic_setup=1; + } return num_processors; } @@ -413,7 +400,7 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) * Scan the memory blocks for an SMP configuration block. */ -int __init smp_scan_config(unsigned long base, unsigned long length) +static int __init smp_scan_config(unsigned long base, unsigned long length) { unsigned long *bp=phys_to_virt(base); struct intel_mp_floating *mpf; @@ -447,7 +434,7 @@ int __init smp_scan_config(unsigned long base, unsigned long length) unsigned long cfg; /* local APIC has default address */ - mp_lapic_addr = 0xFEE00000; + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; /* * We need to know what the local * APIC id of the boot CPU is! @@ -564,6 +551,76 @@ int __init smp_scan_config(unsigned long base, unsigned long length) return 0; } +void __init init_intel_smp (void) +{ + /* + * FIXME: Linux assumes you have 640K of base ram.. + * this continues the error... + * + * 1) Scan the bottom 1K for a signature + * 2) Scan the top 1K of base RAM + * 3) Scan the 64K of bios + */ + if (!smp_scan_config(0x0,0x400) && + !smp_scan_config(639*0x400,0x400) && + !smp_scan_config(0xF0000,0x10000)) { + /* + * If it is an SMP machine we should know now, unless the + * configuration is in an EISA/MCA bus machine with an + * extended bios data area. + * + * there is a real-mode segmented pointer pointing to the + * 4K EBDA area at 0x40E, calculate and scan it here. + * + * NOTE! There are Linux loaders that will corrupt the EBDA + * area, and as such this kind of SMP config may be less + * trustworthy, simply because the SMP table may have been + * stomped on during early boot. These loaders are buggy and + * should be fixed. + */ + unsigned int address; + + address = *(unsigned short *)phys_to_virt(0x40E); + address<<=4; + smp_scan_config(address, 0x1000); + if (smp_found_config) + printk(KERN_WARNING "WARNING: MP table in the EBDA can be UNSAFE, contact linux-smp@vger.rutgers.edu if you experience SMP problems!\n"); + } +} + +#else + +/* + * The Visual Workstation is Intel MP compliant in the hardware + * sense, but it doesnt have a BIOS(-configuration table). + * No problem for Linux. + */ +void __init init_visws_smp(void) +{ + smp_found_config = 1; + + cpu_present_map |= 2; /* or in id 1 */ + apic_version[1] |= 0x10; /* integrated APIC */ + apic_version[0] |= 0x10; + + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; +} + +#endif + +/* + * - Intel MP Configuration Table + * - or SGI Visual Workstation configuration + */ +void __init init_smp_config (void) +{ +#ifndef CONFIG_VISWS + init_intel_smp(); +#else + init_visws_smp(); +#endif +} + /* * Trampoline 80x86 program as an array. */ @@ -630,13 +687,17 @@ void __init smp_store_cpu_info(int id) * we use to track CPUs as they power up. */ +static atomic_t smp_commenced = ATOMIC_INIT(0); + void __init smp_commence(void) { /* * Lets the callins below out of their loop. */ SMP_PRINTK(("Setting commenced=1, go go go\n")); - smp_commenced=1; + + wmb(); + atomic_set(&smp_commenced,1); } void __init enable_local_APIC(void) @@ -653,6 +714,26 @@ void __init enable_local_APIC(void) value &= ~APIC_TPRI_MASK; /* Set Task Priority to 'accept all' */ apic_write(APIC_TASKPRI,value); + /* + * Set arbitrarion priority to 0 + */ + value = apic_read(APIC_ARBPRI); + value &= ~APIC_ARBPRI_MASK; + apic_write(APIC_ARBPRI, value); + + /* + * Set the logical destination ID to 'all', just to be safe. + * also, put the APIC into flat delivery mode. + */ + value = apic_read(APIC_LDR); + value &= ~APIC_LDR_MASK; + value |= SET_APIC_LOGICAL_ID(0xff); + apic_write(APIC_LDR,value); + + value = apic_read(APIC_DFR); + value |= SET_APIC_DFR(0xf); + apic_write(APIC_DFR, value); + udelay(100); /* B safe */ ack_APIC_irq(); udelay(100); @@ -660,12 +741,11 @@ void __init enable_local_APIC(void) unsigned long __init init_smp_mappings(unsigned long memory_start) { - unsigned long apic_phys, ioapic_phys; + unsigned long apic_phys; memory_start = PAGE_ALIGN(memory_start); if (smp_found_config) { apic_phys = mp_lapic_addr; - ioapic_phys = mp_ioapic_addr; } else { /* * set up a fake all zeroes page to simulate the @@ -674,30 +754,81 @@ unsigned long __init init_smp_mappings(unsigned long memory_start) * this way if some buggy code writes to this page ... */ apic_phys = __pa(memory_start); - ioapic_phys = __pa(memory_start+PAGE_SIZE); - memset((void *)memory_start, 0, 2*PAGE_SIZE); - memory_start += 2*PAGE_SIZE; + memset((void *)memory_start, 0, PAGE_SIZE); + memory_start += PAGE_SIZE; } - set_fixmap(FIX_APIC_BASE,apic_phys); - set_fixmap(FIX_IO_APIC_BASE,ioapic_phys); - printk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys); - printk("mapped IOAPIC to %08lx (%08lx)\n", fix_to_virt(FIX_IO_APIC_BASE), ioapic_phys); + +#ifdef CONFIG_X86_IO_APIC + { + unsigned long ioapic_phys; + + if (smp_found_config) { + ioapic_phys = mp_ioapic_addr; + } else { + ioapic_phys = __pa(memory_start); + memset((void *)memory_start, 0, PAGE_SIZE); + memory_start += PAGE_SIZE; + } + set_fixmap(FIX_IO_APIC_BASE,ioapic_phys); + printk("mapped IOAPIC to %08lx (%08lx)\n", + fix_to_virt(FIX_IO_APIC_BASE), ioapic_phys); + } +#endif return memory_start; } +extern void calibrate_delay(void); + void __init smp_callin(void) { - extern void calibrate_delay(void); - int cpuid=GET_APIC_ID(apic_read(APIC_ID)); + int cpuid; + unsigned long timeout; + + /* + * (This works even if the APIC is not enabled.) + */ + cpuid = GET_APIC_ID(apic_read(APIC_ID)); + + SMP_PRINTK(("CPU#%d waiting for CALLOUT\n", cpuid)); + + /* + * STARTUP IPIs are fragile beasts as they might sometimes + * trigger some glue motherboard logic. Complete APIC bus + * silence for 1 second, this overestimates the time the + * boot CPU is spending to send the up to 2 STARTUP IPIs + * by a factor of two. This should be enough. + */ + + /* + * Waiting 2s total for startup (udelay is not yet working) + */ + timeout = jiffies + 2*HZ; + while (time_before(jiffies,timeout)) + { + /* + * Has the boot CPU finished it's STARTUP sequence? + */ + if (test_bit(cpuid, (unsigned long *)&cpu_callout_map[0])) + break; + } + + while (!time_before(jiffies,timeout)) { + printk("BUG: CPU%d started up but did not get a callout!\n", + cpuid); + stop_this_cpu(); + } /* - * Activate our APIC + * the boot CPU has finished the init stage and is spinning + * on callin_map until we finish. We are free to set up this + * CPU, first the APIC. (this is probably redundant on most + * boards) */ - SMP_PRINTK(("CALLIN %d %d\n",hard_smp_processor_id(), smp_processor_id())); + SMP_PRINTK(("CALLIN, before enable_local_APIC().\n")); enable_local_APIC(); /* @@ -705,7 +836,12 @@ void __init smp_callin(void) */ setup_APIC_clock(); - sti(); + __sti(); + +#ifdef CONFIG_MTRR + /* Must be done before calibration delay is computed */ + mtrr_init_secondary_cpu (); +#endif /* * Get our bogomips. */ @@ -732,13 +868,14 @@ extern int cpu_idle(void * unused); */ int __init start_secondary(void *unused) { -#ifdef CONFIG_MTRR - /* Must be done before calibration delay is computed */ - mtrr_init_secondary_cpu (); -#endif + /* + * Dont put anything before smp_callin(), SMP + * booting is too fragile that we want to limit the + * things done here to the most necessary things. + */ smp_callin(); - while (!smp_commenced) - barrier(); + while (!atomic_read(&smp_commenced)) + /* nothing */ ; return cpu_idle(NULL); } @@ -761,11 +898,7 @@ void __init initialize_secondary(void) /* * We don't actually need to load the full TSS, * basically just the stack pointer and the eip. - * - * Get the scheduler lock, because we're going - * to release it as part of the "reschedule" return. */ - spin_lock(&scheduler_lock); asm volatile( "movl %0,%%esp\n\t" @@ -859,8 +992,7 @@ static void __init do_boot_cpu(int i) apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */ cfg=apic_read(APIC_ICR); cfg&=~0xCDFFF; /* Clear bits */ - cfg |= (APIC_DEST_FIELD | APIC_DEST_LEVELTRIG - | APIC_DEST_ASSERT | APIC_DEST_DM_INIT); + cfg |= (APIC_DEST_LEVELTRIG | APIC_DEST_ASSERT | APIC_DEST_DM_INIT); apic_write(APIC_ICR, cfg); /* Send IPI */ udelay(200); @@ -871,8 +1003,7 @@ static void __init do_boot_cpu(int i) apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */ cfg=apic_read(APIC_ICR); cfg&=~0xCDFFF; /* Clear bits */ - cfg |= (APIC_DEST_FIELD | APIC_DEST_LEVELTRIG - | APIC_DEST_DM_INIT); + cfg |= (APIC_DEST_LEVELTRIG | APIC_DEST_DM_INIT); apic_write(APIC_ICR, cfg); /* Send IPI */ /* @@ -908,19 +1039,23 @@ static void __init do_boot_cpu(int i) apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(i)); /* Target chip */ cfg=apic_read(APIC_ICR); cfg&=~0xCDFFF; /* Clear bits */ - cfg |= (APIC_DEST_FIELD - | APIC_DEST_DM_STARTUP - | (start_eip >> 12)); /* Boot on the stack */ + cfg |= (APIC_DEST_DM_STARTUP | (start_eip >> 12)); /* Boot on the stack */ SMP_PRINTK(("Before start apic_write.\n")); apic_write(APIC_ICR, cfg); /* Kick the second */ SMP_PRINTK(("Startup point 1.\n")); + timeout = 0; + SMP_PRINTK(("Waiting for send to finish...\n")); do { - SMP_PRINTK(("Sleeping.\n")); mdelay(1000); - udelay(10); - } while ( (send_status = (apic_read(APIC_ICR) & 0x1000)) - && (timeout++ < 1000)); + SMP_PRINTK(("+")); + udelay(100); + send_status = apic_read(APIC_ICR) & 0x1000; + } while (send_status && (timeout++ < 1000)); + + /* + * Give the other CPU some time to accept the IPI. + */ udelay(200); accept_status = (apic_read(APIC_ESR) & 0xEF); } @@ -933,6 +1068,13 @@ static void __init do_boot_cpu(int i) if ( !(send_status || accept_status) ) { + /* + * allow APs to start initializing. + */ + SMP_PRINTK(("Before Callout %d.\n", i)); + set_bit(i, (unsigned long *)&cpu_callout_map[0]); + SMP_PRINTK(("After Callout %d.\n", i)); + for(timeout=0;timeout<50000;timeout++) { if (cpu_callin_map[0]&(1<<i)) @@ -973,6 +1115,43 @@ static void __init do_boot_cpu(int i) *((volatile unsigned long *)phys_to_virt(8192)) = 0; } +cycles_t cacheflush_time; +extern unsigned long cpu_hz; + +static void smp_tune_scheduling (void) +{ + unsigned long cachesize; + /* + * Rough estimation for SMP scheduling, this is the number of + * cycles it takes for a fully memory-limited process to flush + * the SMP-local cache. + * + * (For a P5 this pretty much means we will choose another idle + * CPU almost always at wakeup time (this is due to the small + * L1 cache), on PIIs it's around 50-100 usecs, depending on + * the cache size) + */ + + if (!cpu_hz) { + /* + * this basically disables processor-affinity + * scheduling on SMP without a TSC. + */ + cacheflush_time = 0; + return; + } else { + cachesize = boot_cpu_data.x86_cache_size; + if (cachesize == -1) + cachesize = 8; /* Pentiums */ + + cacheflush_time = cpu_hz/1024*cachesize/5000; + } + + printk("per-CPU timeslice cutoff: %ld.%02ld usecs.\n", + (long)cacheflush_time/(cpu_hz/1000000), + ((long)cacheflush_time*100/(cpu_hz/1000000)) % 100); +} + unsigned int prof_multiplier[NR_CPUS]; unsigned int prof_counter[NR_CPUS]; @@ -983,7 +1162,6 @@ unsigned int prof_counter[NR_CPUS]; void __init smp_boot_cpus(void) { int i; - unsigned long cfg; #ifdef CONFIG_MTRR /* Must be done before other processors booted */ @@ -1005,21 +1183,31 @@ void __init smp_boot_cpus(void) */ smp_store_cpu_info(boot_cpu_id); /* Final full version of the data */ + smp_tune_scheduling(); printk("CPU%d: ", boot_cpu_id); print_cpu_info(&cpu_data[boot_cpu_id]); + /* + * not necessary because the MP table should list the boot + * CPU too, but we do it for the sake of robustness anyway. + * (and for the case when a non-SMP board boots an SMP kernel) + */ cpu_present_map |= (1 << hard_smp_processor_id()); + cpu_number_map[boot_cpu_id] = 0; /* - * If we don't conform to the Intel MPS standard, get out - * of here now! + * If we couldnt find an SMP configuration at boot time, + * get out of here now! */ if (!smp_found_config) { printk(KERN_NOTICE "SMP motherboard not detected. Using dummy APIC emulation.\n"); +#ifndef CONFIG_VISWS io_apic_irqs = 0; +#endif + cpu_online_map = cpu_present_map; goto smp_done; } @@ -1082,6 +1270,14 @@ void __init smp_boot_cpus(void) * Now scan the CPU present map and fire up the other CPUs. */ + /* + * Add all detected CPUs. (later on we can down individual + * CPUs which will change cpu_online_map but not necessarily + * cpu_present_map. We are pretty much ready for hot-swap CPUs.) + */ + cpu_online_map = cpu_present_map; + mb(); + SMP_PRINTK(("CPU map: %lx\n", cpu_present_map)); for(i=0;i<NR_CPUS;i++) @@ -1092,7 +1288,7 @@ void __init smp_boot_cpus(void) if (i == boot_cpu_id) continue; - if ((cpu_present_map & (1 << i)) + if ((cpu_online_map & (1 << i)) && (max_cpus < 0 || max_cpus > cpucount+1)) { do_boot_cpu(i); @@ -1102,9 +1298,9 @@ void __init smp_boot_cpus(void) * Make sure we unmap all failed CPUs */ - if (cpu_number_map[i] == -1 && (cpu_present_map & (1 << i))) { - printk("CPU #%d not responding. Removing from cpu_present_map.\n",i); - cpu_present_map &= ~(1 << i); + if (cpu_number_map[i] == -1 && (cpu_online_map & (1 << i))) { + printk("CPU #%d not responding. Removing from cpu_online_map.\n",i); + cpu_online_map &= ~(1 << i); } } @@ -1112,29 +1308,34 @@ void __init smp_boot_cpus(void) * Cleanup possible dangling ends... */ - /* - * Install writable page 0 entry. - */ - - cfg = pg0[0]; - pg0[0] = 3; /* writeable, present, addr 0 */ - local_flush_tlb(); +#ifndef CONFIG_VISWS + { + unsigned long cfg; - /* - * Paranoid: Set warm reset code and vector here back - * to default values. - */ + /* + * Install writable page 0 entry. + */ + cfg = pg0[0]; + pg0[0] = 3; /* writeable, present, addr 0 */ + local_flush_tlb(); + + /* + * Paranoid: Set warm reset code and vector here back + * to default values. + */ - CMOS_WRITE(0, 0xf); + CMOS_WRITE(0, 0xf); - *((volatile long *) phys_to_virt(0x467)) = 0; + *((volatile long *) phys_to_virt(0x467)) = 0; - /* - * Restore old page 0 entry. - */ + /* + * Restore old page 0 entry. + */ - pg0[0] = cfg; - local_flush_tlb(); + pg0[0] = cfg; + local_flush_tlb(); + } +#endif /* * Allow the user to impress friends. @@ -1144,14 +1345,14 @@ void __init smp_boot_cpus(void) if (cpucount==0) { printk(KERN_ERR "Error: only one processor found.\n"); - cpu_present_map=(1<<hard_smp_processor_id()); + cpu_online_map = (1<<hard_smp_processor_id()); } else { unsigned long bogosum=0; for(i=0;i<32;i++) { - if (cpu_present_map&(1<<i)) + if (cpu_online_map&(1<<i)) bogosum+=cpu_data[i].loops_per_sec; } printk(KERN_INFO "Total of %d processors activated (%lu.%02lu BogoMIPS).\n", @@ -1166,267 +1367,289 @@ void __init smp_boot_cpus(void) printk(KERN_WARNING "WARNING: SMP operation may be unreliable with B stepping processors.\n"); SMP_PRINTK(("Boot done.\n")); + cache_APIC_registers(); +#ifndef CONFIG_VISWS /* * Here we can be sure that there is an IO-APIC in the system. Let's * go and set it up: */ - setup_IO_APIC(); + if (!skip_ioapic_setup) + setup_IO_APIC(); +#endif smp_done: } -void send_IPI(int dest, int vector) -{ - unsigned long cfg; - unsigned long flags; - - __save_flags(flags); - __cli(); +/* + * the following functions deal with sending IPIs between CPUs. + * + * We use 'broadcast', CPU->CPU IPIs and self-IPIs too. + */ - /* - * prepare target chip field - */ - cfg = apic_read(APIC_ICR2) & 0x00FFFFFF; - apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(dest)); +/* + * Silly serialization to work around CPU bug in P5s. + * We can safely turn it off on a 686. + */ +#ifdef CONFIG_X86_GOOD_APIC +# define FORCE_APIC_SERIALIZATION 0 +#else +# define FORCE_APIC_SERIALIZATION 1 +#endif - cfg = apic_read(APIC_ICR); - cfg &= ~0xFDFFF; - cfg |= APIC_DEST_FIELD|APIC_DEST_DM_FIXED|vector; - cfg |= dest; - - /* - * Send the IPI. The write to APIC_ICR fires this off. - */ - - apic_write(APIC_ICR, cfg); - __restore_flags(flags); -} +static unsigned int cached_APIC_ICR; +static unsigned int cached_APIC_ICR2; /* - * A non wait message cannot pass data or CPU source info. This current setup - * is only safe because the kernel lock owner is the only person who can send - * a message. - * - * Wrapping this whole block in a spinlock is not the safe answer either. A - * processor may get stuck with IRQs off waiting to send a message and thus - * not replying to the person spinning for a reply. + * Caches reserved bits, APIC reads are (mildly) expensive + * and force otherwise unnecessary CPU synchronization. * - * In the end flush tlb ought to be the NMI and a very short function - * (to avoid the old IDE disk problems), and other messages sent with IRQs - * enabled in a civilised fashion. That will also boost performance. + * (We could cache other APIC registers too, but these are the + * main ones used in RL.) */ +#define slow_ICR (apic_read(APIC_ICR) & ~0xFDFFF) +#define slow_ICR2 (apic_read(APIC_ICR2) & 0x00FFFFFF) -void smp_message_pass(int target, int msg, unsigned long data, int wait) +void cache_APIC_registers (void) { - unsigned long cfg; - unsigned long dest = 0; - unsigned long target_map; - int p=smp_processor_id(); - int irq; - int ct=0; + cached_APIC_ICR = slow_ICR; + cached_APIC_ICR2 = slow_ICR2; + mb(); +} +static inline unsigned int __get_ICR (void) +{ +#if FORCE_APIC_SERIALIZATION /* - * During boot up send no messages + * Wait for the APIC to become ready - this should never occur. It's + * a debugging check really. */ - - if (!smp_activated || !smp_commenced) - return; + int count = 0; + unsigned int cfg; + while (count < 1000) + { + cfg = slow_ICR; + if (!(cfg&(1<<12))) { + if (count) + atomic_add(count, (atomic_t*)&ipi_count); + return cfg; + } + count++; + udelay(10); + } + printk("CPU #%d: previous IPI still not cleared after 10mS\n", + smp_processor_id()); + return cfg; +#else + return cached_APIC_ICR; +#endif +} - /* - * Skip the reschedule if we are waiting to clear a - * message at this time. The reschedule cannot wait - * but is not critical. - */ +static inline unsigned int __get_ICR2 (void) +{ +#if FORCE_APIC_SERIALIZATION + return slow_ICR2; +#else + return cached_APIC_ICR2; +#endif +} - switch (msg) { - case MSG_RESCHEDULE: - irq = 0x30; - if (smp_cpu_in_msg[p]) - return; - break; +static inline int __prepare_ICR (unsigned int shortcut, int vector) +{ + unsigned int cfg; - case MSG_INVALIDATE_TLB: - /* make this a NMI some day */ - irq = 0x31; - break; + cfg = __get_ICR(); + cfg |= APIC_DEST_DM_FIXED|shortcut|vector; - case MSG_STOP_CPU: - irq = 0x40; - break; + return cfg; +} - case MSG_MTRR_CHANGE: - irq = 0x50; - break; +static inline int __prepare_ICR2 (unsigned int dest) +{ + unsigned int cfg; - default: - printk("Unknown SMP message %d\n", msg); - return; - } + cfg = __get_ICR2(); + cfg |= SET_APIC_DEST_FIELD(dest); - /* - * Sanity check we don't re-enter this across CPUs. Only the kernel - * lock holder may send messages. For a STOP_CPU we are bringing the - * entire box to the fastest halt we can. A reschedule carries - * no data and can occur during a flush. Guess what panic - * I got to notice this bug. - */ - - /* - * We are busy. - */ - - smp_cpu_in_msg[p]++; + return cfg; +} -/* printk("SMP message pass #%d to %d of %d\n", - p, msg, target);*/ +static inline void __send_IPI_shortcut(unsigned int shortcut, int vector) +{ + unsigned int cfg; +/* + * Subtle. In the case of the 'never do double writes' workaround we + * have to lock out interrupts to be safe. Otherwise it's just one + * single atomic write to the APIC, no need for cli/sti. + */ +#if FORCE_APIC_SERIALIZATION + unsigned long flags; - /* - * Wait for the APIC to become ready - this should never occur. It's - * a debugging check really. - */ - - while (ct<1000) - { - cfg=apic_read(APIC_ICR); - if (!(cfg&(1<<12))) - break; - ct++; - udelay(10); - } + __save_flags(flags); + __cli(); +#endif /* - * Just pray... there is nothing more we can do + * No need to touch the target chip field */ - - if (ct==1000) - printk("CPU #%d: previous IPI still not cleared after 10mS\n", p); + + cfg = __prepare_ICR(shortcut, vector); /* - * Set the target requirement + * Send the IPI. The write to APIC_ICR fires this off. */ - - if (target==MSG_ALL_BUT_SELF) - { - dest=APIC_DEST_ALLBUT; - target_map=cpu_present_map; - cpu_callin_map[0]=(1<<p); - } - else if (target==MSG_ALL) - { - dest=APIC_DEST_ALLINC; - target_map=cpu_present_map; - cpu_callin_map[0]=0; - } - else - { - dest=0; - target_map=(1<<target); - cpu_callin_map[0]=0; - } + apic_write(APIC_ICR, cfg); +#if FORCE_APIC_SERIALIZATION + __restore_flags(flags); +#endif +} + +static inline void send_IPI_allbutself(int vector) +{ + __send_IPI_shortcut(APIC_DEST_ALLBUT, vector); +} + +static inline void send_IPI_all(int vector) +{ + __send_IPI_shortcut(APIC_DEST_ALLINC, vector); +} + +void send_IPI_self(int vector) +{ + __send_IPI_shortcut(APIC_DEST_SELF, vector); +} + +static inline void send_IPI_single(int dest, int vector) +{ + unsigned long cfg; +#if FORCE_APIC_SERIALIZATION + unsigned long flags; + + __save_flags(flags); + __cli(); +#endif /* - * Program the APIC to deliver the IPI + * prepare target chip field */ - send_IPI(dest,irq); + cfg = __prepare_ICR2(dest); + apic_write(APIC_ICR2, cfg); /* - * Spin waiting for completion + * program the ICR */ + cfg = __prepare_ICR(0, vector); - switch(wait) - { - int stuck; - case 1: - stuck = 50000000; - while(cpu_callin_map[0]!=target_map) { - --stuck; - if (!stuck) { - printk("stuck on target_map IPI wait\n"); - break; - } - } - break; - case 2: - stuck = 50000000; - /* Wait for invalidate map to clear */ - while (smp_invalidate_needed) { - /* Take care of "crossing" invalidates */ - if (test_bit(p, &smp_invalidate_needed)) - clear_bit(p, &smp_invalidate_needed); - --stuck; - if (!stuck) { - printk("stuck on smp_invalidate_needed IPI wait (CPU#%d)\n",p); - break; - } - } - break; - } - /* - * Record our completion + * Send the IPI. The write to APIC_ICR fires this off. */ - - smp_cpu_in_msg[p]--; + apic_write(APIC_ICR, cfg); +#if FORCE_APIC_SERIALIZATION + __restore_flags(flags); +#endif } /* - * This is fraught with deadlocks. Linus does a flush tlb at a whim - * even with IRQs off. We have to avoid a pair of crossing flushes - * or we are doomed. See the notes about smp_message_pass. + * This is fraught with deadlocks. Probably the situation is not that + * bad as in the early days of SMP, so we might ease some of the + * paranoia here. */ void smp_flush_tlb(void) { + int cpu = smp_processor_id(); + int stuck; unsigned long flags; -/* printk("SMI-");*/ - /* - * The assignment is safe because it's volatile so the compiler cannot reorder it, - * because the i586 has strict memory ordering and because only the kernel lock holder - * may issue a tlb flush. If you break any one of those three change this to an atomic - * bus locked or. + * it's important that we do not generate any APIC traffic + * until the AP CPUs have booted up! */ + if (cpu_online_map) { + /* + * The assignment is safe because it's volatile so the + * compiler cannot reorder it, because the i586 has + * strict memory ordering and because only the kernel + * lock holder may issue a tlb flush. If you break any + * one of those three change this to an atomic bus + * locked or. + */ - smp_invalidate_needed=cpu_present_map; + smp_invalidate_needed = cpu_online_map; - /* - * Processors spinning on the lock will see this IRQ late. The smp_invalidate_needed map will - * ensure they don't do a spurious flush tlb or miss one. - */ + /* + * Processors spinning on some lock with IRQs disabled + * will see this IRQ late. The smp_invalidate_needed + * map will ensure they don't do a spurious flush tlb + * or miss one. + */ - __save_flags(flags); - __cli(); - smp_message_pass(MSG_ALL_BUT_SELF, MSG_INVALIDATE_TLB, 0L, 2); + __save_flags(flags); + __cli(); + + send_IPI_allbutself(INVALIDATE_TLB_VECTOR); + + /* + * Spin waiting for completion + */ + + stuck = 50000000; + while (smp_invalidate_needed) { + /* + * Take care of "crossing" invalidates + */ + if (test_bit(cpu, &smp_invalidate_needed)) + clear_bit(cpu, &smp_invalidate_needed); + --stuck; + if (!stuck) { + printk("stuck on TLB IPI wait (CPU#%d)\n",cpu); + break; + } + } + __restore_flags(flags); + } /* * Flush the local TLB */ - local_flush_tlb(); - __restore_flags(flags); - - /* - * Completed. - */ - -/* printk("SMID\n");*/ } +/* + * this function sends a 'reschedule' IPI to another CPU. + * it goes straight through and wastes no time serializing + * anything. Worst case is that we lose a reschedule ... + */ + void smp_send_reschedule(int cpu) { - unsigned long flags; + send_IPI_single(cpu, RESCHEDULE_VECTOR); +} - __save_flags(flags); - __cli(); - smp_message_pass(cpu, MSG_RESCHEDULE, 0L, 0); - __restore_flags(flags); +/* + * this function sends a 'stop' IPI to all other CPUs in the system. + * it goes straight through. + */ + +void smp_send_stop(void) +{ + send_IPI_allbutself(STOP_CPU_VECTOR); +} + +/* + * this function sends an 'reload MTRR state' IPI to all other CPUs + * in the system. it goes straight through, completion processing + * is done on the mttr.c level. + */ + +void smp_send_mtrr(void) +{ + send_IPI_allbutself(MTRR_CHANGE_VECTOR); } /* @@ -1546,14 +1769,24 @@ asmlinkage void smp_invalidate_interrupt(void) ack_APIC_irq(); } +static void stop_this_cpu (void) +{ + /* + * Remove this CPU: + */ + clear_bit(smp_processor_id(), &cpu_online_map); + + if (cpu_data[smp_processor_id()].hlt_works_ok) + for(;;) __asm__("hlt"); + for (;;); +} + /* * CPU halt call-back */ asmlinkage void smp_stop_cpu_interrupt(void) { - if (cpu_data[smp_processor_id()].hlt_works_ok) - for(;;) __asm__("hlt"); - for (;;) ; + stop_this_cpu(); } void (*mtrr_hook) (void) = NULL; @@ -1627,12 +1860,9 @@ void setup_APIC_timer(unsigned int clocks) * Unfortunately the local APIC timer cannot be set up into NMI * mode. With the IO APIC we can re-route the external timer * interrupt and broadcast it as an NMI to all CPUs, so no pain. - * - * NOTE: this trap vector (0x41) and the gate in - * BUILD_SMP_TIMER_INTERRUPT should be the same ;) */ tmp_value = apic_read(APIC_LVTT); - lvtt1_value = APIC_LVT_TIMER_PERIODIC | 0x41; + lvtt1_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; apic_write(APIC_LVTT , lvtt1_value); /* @@ -1741,7 +1971,7 @@ int __init calibrate_APIC_clock(void) ((long)(t2-t1)/LOOPS)/(1000000/HZ), ((long)(t2-t1)/LOOPS)%(1000000/HZ) ); - printk("..... APIC bus clock speed is %ld.%04ld MHz.\n", + printk("..... system bus clock speed is %ld.%04ld MHz.\n", calibration_result/(1000000/HZ), calibration_result%(1000000/HZ) ); #undef LOOPS diff --git a/arch/i386/kernel/sys_i386.c b/arch/i386/kernel/sys_i386.c index d95d64069..f7987718b 100644 --- a/arch/i386/kernel/sys_i386.c +++ b/arch/i386/kernel/sys_i386.c @@ -108,108 +108,94 @@ asmlinkage int old_select(struct sel_arg_struct *arg) * * This is really horribly ugly. */ -asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth) +asmlinkage int sys_ipc (uint call, int first, int second, + int third, void *ptr, long fifth) { int version, ret; - lock_kernel(); version = call >> 16; /* hack for backward compatibility */ call &= 0xffff; if (call <= SEMCTL) switch (call) { case SEMOP: - ret = sys_semop (first, (struct sembuf *)ptr, second); - goto out; + return sys_semop (first, (struct sembuf *)ptr, second); case SEMGET: - ret = sys_semget (first, second, third); - goto out; + return sys_semget (first, second, third); case SEMCTL: { union semun fourth; - ret = -EINVAL; if (!ptr) - goto out; - ret = -EFAULT; + return -EINVAL; if (get_user(fourth.__pad, (void **) ptr)) - goto out; - ret = sys_semctl (first, second, third, fourth); - goto out; + return -EFAULT; + return sys_semctl (first, second, third, fourth); } default: - ret = -EINVAL; - goto out; + return -EINVAL; } + if (call <= MSGCTL) switch (call) { case MSGSND: - ret = sys_msgsnd (first, (struct msgbuf *) ptr, + return sys_msgsnd (first, (struct msgbuf *) ptr, second, third); - goto out; case MSGRCV: switch (version) { case 0: { struct ipc_kludge tmp; - ret = -EINVAL; if (!ptr) - goto out; - ret = -EFAULT; - if (copy_from_user(&tmp,(struct ipc_kludge *) ptr, - sizeof (tmp))) - goto out; - ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third); - goto out; + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); } - case 1: default: - ret = sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, third); - goto out; + default: + return sys_msgrcv (first, + (struct msgbuf *) ptr, + second, fifth, third); } case MSGGET: - ret = sys_msgget ((key_t) first, second); - goto out; + return sys_msgget ((key_t) first, second); case MSGCTL: - ret = sys_msgctl (first, second, (struct msqid_ds *) ptr); - goto out; + return sys_msgctl (first, second, + (struct msqid_ds *) ptr); default: - ret = -EINVAL; - goto out; + return -EINVAL; } if (call <= SHMCTL) switch (call) { case SHMAT: switch (version) { - case 0: default: { + default: { ulong raddr; - ret = sys_shmat (first, (char *) ptr, second, &raddr); + ret = sys_shmat (first, (char *) ptr, + second, &raddr); if (ret) - goto out; - ret = put_user (raddr, (ulong *) third); - goto out; + return ret; + return put_user (raddr, (ulong *) third); } case 1: /* iBCS2 emulator entry point */ - ret = -EINVAL; if (!segment_eq(get_fs(), get_ds())) - goto out; - ret = sys_shmat (first, (char *) ptr, second, (ulong *) third); - goto out; + return -EINVAL; + return sys_shmat (first, (char *) ptr, + second, (ulong *) third); } case SHMDT: - ret = sys_shmdt ((char *)ptr); - goto out; + return sys_shmdt ((char *)ptr); case SHMGET: - ret = sys_shmget (first, second, third); - goto out; + return sys_shmget (first, second, third); case SHMCTL: - ret = sys_shmctl (first, second, (struct shmid_ds *) ptr); - goto out; + return sys_shmctl (first, second, + (struct shmid_ds *) ptr); default: - ret = -EINVAL; - goto out; + return -EINVAL; } - else - ret = -EINVAL; -out: - unlock_kernel(); - return ret; + + return -EINVAL; } /* diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index dbd6e1942..ec2ea5d60 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c @@ -12,6 +12,8 @@ * precision CMOS clock update * 1996-05-03 Ingo Molnar * fixed time warps in do_[slow|fast]_gettimeoffset() + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills * 1998-09-05 (Various) * More robust do_fast_gettimeoffset() algorithm implemented * (works with APM, Cyrix 6x86MX and Centaur C6), @@ -20,6 +22,12 @@ * (C. Scott Ananian <cananian@alumni.princeton.edu>, Andrew D. * Balsa <andrebalsa@altern.org>, Philip Gladstone <philip@raptor.com>; * ported from 2.0.35 Jumbo-9 by Michael Krause <m.krause@tu-harburg.de>). + * 1998-12-16 Andrea Arcangeli + * Fixed Jumbo-9 code in 2.1.131: do_gettimeofday was missing 1 jiffy + * because was not accounting lost_ticks. + * 1998-12-24 Copyright (C) 1998 Andrea Arcangeli + * Fixed a xtime SMP race (we need the xtime_lock rw spinlock to + * serialize accesses to xtime/lost_ticks). */ /* What about the "updated NTP code" stuff in 2.0 time.c? It's not in @@ -57,12 +65,14 @@ #include <linux/timex.h> #include <linux/config.h> +#include <asm/fixmap.h> +#include <asm/cobalt.h> + /* * for x86_do_profile() */ #include "irq.h" -extern int setup_x86_irq(int, struct irqaction *); unsigned long cpu_hz; /* Detected as we calibrate the TSC */ @@ -78,7 +88,9 @@ static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */ */ static unsigned long fast_gettimeoffset_quotient=0; -static unsigned long do_fast_gettimeoffset(void) +extern rwlock_t xtime_lock; + +static inline unsigned long do_fast_gettimeoffset(void) { register unsigned long eax asm("ax"); register unsigned long edx asm("dx"); @@ -88,13 +100,12 @@ static unsigned long do_fast_gettimeoffset(void) :"=a" (eax), "=d" (edx)); /* .. relative to previous jiffy (32 bits is enough) */ - edx = 0; eax -= last_tsc_low; /* tsc_low delta */ /* - * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient. - * = (tsc_low delta) / (clocks_per_usec) - * = (tsc_low delta) / (clocks_per_jiffy / usecs_per_jiffy) + * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient + * = (tsc_low delta) * (usecs_per_clock) + * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) * * Using a mull instead of a divl saves up to 31 clock cycles * in the critical path. @@ -102,13 +113,17 @@ static unsigned long do_fast_gettimeoffset(void) __asm__("mull %2" :"=a" (eax), "=d" (edx) - :"r" (fast_gettimeoffset_quotient), - "0" (eax), "1" (edx)); + :"g" (fast_gettimeoffset_quotient), + "0" (eax)); /* our adjusted time offset in microseconds */ - return edx + delay_at_last_interrupt; + return delay_at_last_interrupt + edx; } +#define TICK_SIZE tick + +#ifndef CONFIG_X86_TSC + /* This function must be called with interrupts disabled * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs * @@ -141,8 +156,6 @@ static unsigned long do_fast_gettimeoffset(void) * comp.protocols.time.ntp! */ -#define TICK_SIZE tick - static unsigned long do_slow_gettimeoffset(void) { int count; @@ -224,28 +237,45 @@ static unsigned long do_slow_gettimeoffset(void) static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; +#else + +#define do_gettimeoffset() do_fast_gettimeoffset() + +#endif + /* * This version of gettimeofday has microsecond resolution * and better than microsecond precision on fast x86 machines with TSC. */ void do_gettimeofday(struct timeval *tv) { + extern volatile unsigned long lost_ticks; unsigned long flags; + unsigned long usec, sec; + + read_lock_irqsave(&xtime_lock, flags); + usec = do_gettimeoffset(); + { + unsigned long lost = lost_ticks; + if (lost) + usec += lost * (1000000 / HZ); + } + sec = xtime.tv_sec; + usec += xtime.tv_usec; + read_unlock_irqrestore(&xtime_lock, flags); - save_flags(flags); - cli(); - *tv = xtime; - tv->tv_usec += do_gettimeoffset(); - if (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; + while (usec >= 1000000) { + usec -= 1000000; + sec++; } - restore_flags(flags); + + tv->tv_sec = sec; + tv->tv_usec = usec; } void do_settimeofday(struct timeval *tv) { - cli(); + write_lock_irq(&xtime_lock); /* This is revolting. We need to set the xtime.tv_usec * correctly. However, the value in this location is * is value at the last tick. @@ -254,16 +284,18 @@ void do_settimeofday(struct timeval *tv) */ tv->tv_usec -= do_gettimeoffset(); - if (tv->tv_usec < 0) { + while (tv->tv_usec < 0) { tv->tv_usec += 1000000; tv->tv_sec--; } xtime = *tv; - time_state = TIME_BAD; - time_maxerror = MAXPHASE; - time_esterror = MAXPHASE; - sti(); + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_unlock_irq(&xtime_lock); } /* @@ -338,8 +370,12 @@ static long last_rtc_update = 0; * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick */ -static inline void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { +#ifdef CONFIG_VISWS + /* Clear the interrupt */ + co_cpu_write(CO_CPU_STAT,co_cpu_read(CO_CPU_STAT) & ~CO_STAT_TIMEINTR); +#endif do_timer(regs); /* * In the SMP case we use the local APIC timer interrupt to do the @@ -359,21 +395,15 @@ static inline void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be * called as close as possible to 500 ms before the new second starts. */ - if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) { + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && + xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ } -#if 0 - /* As we return to user mode fire off the other CPU schedulers.. this is - basically because we don't yet share IRQ's around. This message is - rigged to be safe on the 386 - basically it's a hack, so don't look - closely for now.. */ - smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); -#endif #ifdef CONFIG_MCA if( MCA_bus ) { @@ -392,36 +422,56 @@ static inline void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) #endif } +static int use_tsc = 0; + /* * This is the same as the above, except we _also_ save the current * Time Stamp Counter value at the time of the timer interrupt, so that * we later on can estimate the time of day more exactly. */ -static void pentium_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int count, flags; + int count; - /* It is important that these two operations happen almost at the - * same time. We do the RDTSC stuff first, since it's faster. To - * avoid any inconsistencies, we disable interrupts locally. - */ + /* + * Here we are in the timer irq handler. We just have irqs locally + * disabled but we don't know if the timer_bh is running on the other + * CPU. We need to avoid to SMP race with it. NOTE: we don' t need + * the irq version of write_lock because as just said we have irq + * locally disabled. -arca + */ + write_lock(&xtime_lock); + + if (use_tsc) + { + /* + * It is important that these two operations happen almost at + * the same time. We do the RDTSC stuff first, since it's + * faster. To avoid any inconsistencies, we need interrupts + * disabled locally. + */ + + /* + * Interrupts are just disabled locally since the timer irq + * has the SA_INTERRUPT flag set. -arca + */ - __save_flags(flags); - __cli(); - /* read Pentium cycle counter */ - __asm__("rdtsc" - :"=a" (last_tsc_low):: "eax", "edx"); + /* read Pentium cycle counter */ + __asm__("rdtsc" : "=a" (last_tsc_low) : : "edx"); - outb_p(0x00, 0x43); /* latch the count ASAP */ + outb_p(0x00, 0x43); /* latch the count ASAP */ - count = inb_p(0x40); /* read the latched count */ - count |= inb(0x40) << 8; + count = inb_p(0x40); /* read the latched count */ + count |= inb(0x40) << 8; + + count = ((LATCH-1) - count) * TICK_SIZE; + delay_at_last_interrupt = (count + LATCH/2) / LATCH; + } + + do_timer_interrupt(irq, NULL, regs); + + write_unlock(&xtime_lock); - count = ((LATCH-1) - count) * TICK_SIZE; - delay_at_last_interrupt = (count + LATCH/2) / LATCH; - __restore_flags(flags); - - timer_interrupt(irq, NULL, regs); } /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. @@ -591,10 +641,25 @@ __initfunc(void time_init(void)) * to disk; this won't break the kernel, though, 'cuz we're * smart. See arch/i386/kernel/apm.c. */ + /* + * Firstly we have to do a CPU check for chips with + * a potentially buggy TSC. At this point we haven't run + * the ident/bugs checks so we must run this hook as it + * may turn off the TSC flag. + * + * NOTE: this doesnt yet handle SMP 486 machines where only + * some CPU's have a TSC. Thats never worked and nobody has + * moaned if you have the only one in the world - you fix it! + */ + + dodgy_tsc(); + if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) { +#ifndef do_gettimeoffset do_gettimeoffset = do_fast_gettimeoffset; +#endif do_get_fast_time = do_gettimeofday; - irq0.handler = pentium_timer_interrupt; + use_tsc = 1; fast_gettimeoffset_quotient = calibrate_tsc(); /* report CPU clock rate in Hz. @@ -609,5 +674,22 @@ __initfunc(void time_init(void)) printk("Detected %ld Hz processor.\n", cpu_hz); } } + +#ifdef CONFIG_VISWS + printk("Starting Cobalt Timer system clock\n"); + + /* Set the countdown value */ + co_cpu_write(CO_CPU_TIMEVAL, CO_TIME_HZ/HZ); + + /* Start the timer */ + co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) | CO_CTRL_TIMERUN); + + /* Enable (unmask) the timer interrupt */ + co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) & ~CO_CTRL_TIMEMASK); + + /* Wire cpu IDT entry to s/w handler (and Cobalt APIC to IDT) */ + setup_x86_irq(CO_IRQ_TIMER, &irq0); +#else setup_x86_irq(0, &irq0); +#endif } diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 6e9a95423..f0dc06092 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -34,6 +34,14 @@ #include <asm/debugreg.h> #include <asm/desc.h> +#include <asm/smp.h> + +#ifdef CONFIG_X86_VISWS_APIC +#include <asm/fixmap.h> +#include <asm/cobalt.h> +#include <asm/lithium.h> +#endif + asmlinkage int system_call(void); asmlinkage void lcall7(void); @@ -499,15 +507,18 @@ __initfunc(void trap_init_f00f_bug(void)) } #define _set_gate(gate_addr,type,dpl,addr) \ -__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \ - "movw %2,%%dx\n\t" \ +do { \ + int __d0, __d1; \ + __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \ + "movw %4,%%dx\n\t" \ "movl %%eax,%0\n\t" \ "movl %%edx,%1" \ :"=m" (*((long *) (gate_addr))), \ - "=m" (*(1+(long *) (gate_addr))) \ + "=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \ :"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ - "d" ((char *) (addr)),"a" (__KERNEL_CS << 16) \ - :"ax","dx") + "3" ((char *) (addr)),"2" (__KERNEL_CS << 16)); \ +} while (0) + /* * This needs to use 'idt_table' rather than 'idt', and @@ -566,9 +577,100 @@ void set_ldt_desc(unsigned int n, void *addr, unsigned int size) _set_tssldt_desc(gdt_table+FIRST_LDT_ENTRY+(n<<1), (int)addr, ((size << 3) - 1), 0x82); } +#ifdef CONFIG_X86_VISWS_APIC + +/* + * On Rev 005 motherboards legacy device interrupt lines are wired directly + * to Lithium from the 307. But the PROM leaves the interrupt type of each + * 307 logical device set appropriate for the 8259. Later we'll actually use + * the 8259, but for now we have to flip the interrupt types to + * level triggered, active lo as required by Lithium. + */ + +#define REG 0x2e /* The register to read/write */ +#define DEV 0x07 /* Register: Logical device select */ +#define VAL 0x2f /* The value to read/write */ + +static void +superio_outb(int dev, int reg, int val) +{ + outb(DEV, REG); + outb(dev, VAL); + outb(reg, REG); + outb(val, VAL); +} + +static int __attribute__ ((unused)) +superio_inb(int dev, int reg) +{ + outb(DEV, REG); + outb(dev, VAL); + outb(reg, REG); + return inb(VAL); +} + +#define FLOP 3 /* floppy logical device */ +#define PPORT 4 /* parallel logical device */ +#define UART5 5 /* uart2 logical device (not wired up) */ +#define UART6 6 /* uart1 logical device (THIS is the serial port!) */ +#define IDEST 0x70 /* int. destination (which 307 IRQ line) reg. */ +#define ITYPE 0x71 /* interrupt type register */ + +/* interrupt type bits */ +#define LEVEL 0x01 /* bit 0, 0 == edge triggered */ +#define ACTHI 0x02 /* bit 1, 0 == active lo */ + +static void +superio_init(void) +{ + if (visws_board_type == VISWS_320 && visws_board_rev == 5) { + superio_outb(UART6, IDEST, 0); /* 0 means no intr propagated */ + printk("SGI 320 rev 5: disabling 307 uart1 interrupt\n"); + } +} + +static void +lithium_init(void) +{ + set_fixmap(FIX_LI_PCIA, LI_PCI_A_PHYS); + printk("Lithium PCI Bridge A, Bus Number: %d\n", + li_pcia_read16(LI_PCI_BUSNUM) & 0xff); + set_fixmap(FIX_LI_PCIB, LI_PCI_B_PHYS); + printk("Lithium PCI Bridge B (PIIX4), Bus Number: %d\n", + li_pcib_read16(LI_PCI_BUSNUM) & 0xff); + + /* XXX blindly enables all interrupts */ + li_pcia_write16(LI_PCI_INTEN, 0xffff); + li_pcib_write16(LI_PCI_INTEN, 0xffff); +} + +static void +cobalt_init(void) +{ + /* + * On normal SMP PC this is used only with SMP, but we have to + * use it and set it up here to start the Cobalt clock + */ + set_fixmap(FIX_APIC_BASE, APIC_PHYS_BASE); + printk("Local APIC ID %lx\n", apic_read(APIC_ID)); + printk("Local APIC Version %lx\n", apic_read(APIC_VERSION)); + + set_fixmap(FIX_CO_CPU, CO_CPU_PHYS); + printk("Cobalt Revision %lx\n", co_cpu_read(CO_CPU_REV)); + + set_fixmap(FIX_CO_APIC, CO_APIC_PHYS); + printk("Cobalt APIC ID %lx\n", co_apic_read(CO_APIC_ID)); + + /* Enable Cobalt APIC being careful to NOT change the ID! */ + co_apic_write(CO_APIC_ID, co_apic_read(CO_APIC_ID)|CO_APIC_ENABLE); + + printk("Cobalt APIC enabled: ID reg %lx\n", co_apic_read(CO_APIC_ID)); +} +#endif void __init trap_init(void) { - int i; + /* Initially up all of the IDT to jump to unexpected */ + init_unexpected_irq(); if (readl(0x0FFFD9) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24)) EISA_bus = 1; @@ -591,8 +693,6 @@ void __init trap_init(void) set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); - for (i=18;i<48;i++) - set_trap_gate(i,&reserved); set_system_gate(0x80,&system_call); /* set up GDT task & ldt entries */ @@ -603,4 +703,9 @@ void __init trap_init(void) __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); load_TR(0); load_ldt(0); +#ifdef CONFIG_X86_VISWS_APIC + superio_init(); + lithium_init(); + cobalt_init(); +#endif } diff --git a/arch/i386/kernel/visws_apic.c b/arch/i386/kernel/visws_apic.c new file mode 100644 index 000000000..f7dabc15d --- /dev/null +++ b/arch/i386/kernel/visws_apic.c @@ -0,0 +1,407 @@ +/* + * linux/arch/i386/kernel/visws_apic.c + * + * Copyright (C) 1999 Bent Hagemark, Ingo Molnar + * + * SGI Visual Workstation interrupt controller + * + * The Cobalt system ASIC in the Visual Workstation contains a "Cobalt" APIC + * which serves as the main interrupt controller in the system. Non-legacy + * hardware in the system uses this controller directly. Legacy devices + * are connected to the PIIX4 which in turn has its 8259(s) connected to + * a of the Cobalt APIC entry. + */ + +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/malloc.h> +#include <linux/random.h> +#include <linux/smp.h> +#include <linux/tasks.h> +#include <linux/smp_lock.h> +#include <linux/init.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/smp.h> +#include <asm/pgtable.h> +#include <asm/delay.h> +#include <asm/desc.h> + +#include <asm/cobalt.h> + +#include "irq.h" + +/* + * This is the PIIX4-based 8259 that is wired up indirectly to Cobalt + * -- not the manner expected by the normal 8259 code in irq.c. + * + * there is a 'master' physical interrupt source that gets sent to + * the CPU. But in the chipset there are various 'virtual' interrupts + * waiting to be handled. We represent this to Linux through a 'master' + * interrupt controller type, and through a special virtual interrupt- + * controller. Device drivers only see the virtual interrupt sources. + */ + +#define CO_IRQ_BASE 0x20 /* This is the 0x20 in init_IRQ()! */ + +static void startup_piix4_master_irq(unsigned int irq); +static void shutdown_piix4_master_irq(unsigned int irq); +static void do_piix4_master_IRQ(unsigned int irq, struct pt_regs * regs); +#define enable_piix4_master_irq startup_piix4_master_irq +#define disable_piix4_master_irq shutdown_piix4_master_irq + +static struct hw_interrupt_type piix4_master_irq_type = { + "PIIX4-master", + startup_piix4_master_irq, + shutdown_piix4_master_irq, + do_piix4_master_IRQ, + enable_piix4_master_irq, + disable_piix4_master_irq +}; + +static void enable_piix4_virtual_irq(unsigned int irq); +static void disable_piix4_virtual_irq(unsigned int irq); +#define startup_piix4_virtual_irq enable_piix4_virtual_irq +#define shutdown_piix4_virtual_irq disable_piix4_virtual_irq + +static struct hw_interrupt_type piix4_virtual_irq_type = { + "PIIX4-virtual", + startup_piix4_virtual_irq, + shutdown_piix4_virtual_irq, + 0, /* no handler, it's never called physically */ + enable_piix4_virtual_irq, + disable_piix4_virtual_irq +}; + +/* + * This is the SGI Cobalt (IO-)APIC: + */ + +static void do_cobalt_IRQ(unsigned int irq, struct pt_regs * regs); +static void enable_cobalt_irq(unsigned int irq); +static void disable_cobalt_irq(unsigned int irq); +static void startup_cobalt_irq(unsigned int irq); +#define shutdown_cobalt_irq disable_cobalt_irq + +static struct hw_interrupt_type cobalt_irq_type = { + "Cobalt-APIC", + startup_cobalt_irq, + shutdown_cobalt_irq, + do_cobalt_IRQ, + enable_cobalt_irq, + disable_cobalt_irq +}; + + +/* + * Not an initfunc, needed by the reboot code + */ +void init_pic_mode(void) +{ + /* Nop on Cobalt */ +} + +/* + * Cobalt (IO)-APIC functions to handle PCI devices. + */ + +static void disable_cobalt_irq(unsigned int irq) +{ + /* XXX undo the APIC entry here? */ + + /* + * definitely, we do not want to have IRQ storms from + * unused devices --mingo + */ +} + +static void enable_cobalt_irq(unsigned int irq) +{ +} + +/* + * Set the given Cobalt APIC Redirection Table entry to point + * to the given IDT vector/index. + */ +static void co_apic_set(int entry, int idtvec) +{ + co_apic_write(CO_APIC_LO(entry), CO_APIC_LEVEL | (CO_IRQ_BASE+idtvec)); + co_apic_write(CO_APIC_HI(entry), 0); + + printk("Cobalt APIC Entry %d IDT Vector %d\n", entry, idtvec); +} + +/* + * "irq" really just serves to identify the device. Here is where we + * map this to the Cobalt APIC entry where it's physically wired. + * This is called via request_irq -> setup_x86_irq -> irq_desc->startup() + */ +static void startup_cobalt_irq(unsigned int irq) +{ + /* + * These "irq"'s are wired to the same Cobalt APIC entries + * for all (known) motherboard types/revs + */ + switch (irq) { + case CO_IRQ_TIMER: co_apic_set(CO_APIC_CPU, CO_IRQ_TIMER); + return; + + case CO_IRQ_ENET: co_apic_set(CO_APIC_ENET, CO_IRQ_ENET); + return; + + case CO_IRQ_SERIAL: return; /* XXX move to piix4-8259 "virtual" */ + + case CO_IRQ_8259: co_apic_set(CO_APIC_8259, CO_IRQ_8259); + return; + + case CO_IRQ_IDE: + switch (visws_board_type) { + case VISWS_320: + switch (visws_board_rev) { + case 5: + co_apic_set(CO_APIC_0_5_IDE0, CO_IRQ_IDE); + co_apic_set(CO_APIC_0_5_IDE1, CO_IRQ_IDE); + return; + case 6: + co_apic_set(CO_APIC_0_6_IDE0, CO_IRQ_IDE); + co_apic_set(CO_APIC_0_6_IDE1, CO_IRQ_IDE); + return; + } + case VISWS_540: + switch (visws_board_rev) { + case 2: + co_apic_set(CO_APIC_1_2_IDE0, CO_IRQ_IDE); + return; + } + } + break; + default: + panic("huh?"); + } +} + +/* + * This is the handle() op in do_IRQ() + */ +static void do_cobalt_IRQ(unsigned int irq, struct pt_regs * regs) +{ + struct irqaction * action; + irq_desc_t *desc = irq_desc + irq; + + spin_lock(&irq_controller_lock); + { + unsigned int status; + /* XXX APIC EOI? */ + status = desc->status & ~IRQ_REPLAY; + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) + action = desc->action; + desc->status = status | IRQ_INPROGRESS; + } + spin_unlock(&irq_controller_lock); + + /* Exit early if we had no action or it was disabled */ + if (!action) + return; + + handle_IRQ_event(irq, regs, action); + + (void)co_cpu_read(CO_CPU_REV); /* Sync driver ack to its h/w */ + apic_write(APIC_EOI, APIC_EIO_ACK); /* Send EOI to Cobalt APIC */ + + spin_lock(&irq_controller_lock); + { + unsigned int status = desc->status & ~IRQ_INPROGRESS; + desc->status = status; + if (!(status & IRQ_DISABLED)) + enable_cobalt_irq(irq); + } + spin_unlock(&irq_controller_lock); +} + +/* + * PIIX4-8259 master/virtual functions to handle: + * + * floppy + * parallel + * serial + * audio (?) + * + * None of these get Cobalt APIC entries, neither do they have IDT + * entries. These interrupts are purely virtual and distributed from + * the 'master' interrupt source: CO_IRQ_8259. + * + * When the 8259 interrupts its handler figures out which of these + * devices is interrupting and dispatches to it's handler. + * + * CAREFUL: devices see the 'virtual' interrupt only. Thus disable/ + * enable_irq gets the right irq. This 'master' irq is never directly + * manipulated by any driver. + */ + +static void startup_piix4_master_irq(unsigned int irq) +{ + /* ICW1 */ + outb(0x11, 0x20); + outb(0x11, 0xa0); + + /* ICW2 */ + outb(0x08, 0x21); + outb(0x70, 0xa1); + + /* ICW3 */ + outb(0x04, 0x21); + outb(0x02, 0xa1); + + /* ICW4 */ + outb(0x01, 0x21); + outb(0x01, 0xa1); + + /* OCW1 - disable all interrupts in both 8259's */ + outb(0xff, 0x21); + outb(0xff, 0xa1); + + startup_cobalt_irq(irq); +} + +static void shutdown_piix4_master_irq(unsigned int irq) +{ + /* + * [we skip the 8259 magic here, not strictly necessary] + */ + + shutdown_cobalt_irq(irq); +} + +static void do_piix4_master_IRQ(unsigned int irq, struct pt_regs * regs) +{ + int realirq, mask; + + /* Find out what's interrupting in the PIIX4 8259 */ + + spin_lock(&irq_controller_lock); + outb(0x0c, 0x20); /* OCW3 Poll command */ + realirq = inb(0x20); + + if (!(realirq & 0x80)) { + /* + * Bit 7 == 0 means invalid/spurious + */ + goto out_unlock; + } + realirq &= 0x7f; + + /* + * mask and ack the 8259 + */ + mask = inb(0x21); + if ((mask >> realirq) & 0x01) + /* + * This IRQ is masked... ignore + */ + goto out_unlock; + + outb(mask | (1<<realirq), 0x21); + /* + * OCW2 - non-specific EOI + */ + outb(0x20, 0x20); + + spin_unlock(&irq_controller_lock); + + /* + * handle this 'virtual interrupt' as a Cobalt one now. + */ + kstat.irqs[smp_processor_id()][irq]++; + do_cobalt_IRQ(realirq, regs); + + spin_lock(&irq_controller_lock); + { + irq_desc_t *desc = irq_desc + realirq; + + if (!(desc->status & IRQ_DISABLED)) + enable_piix4_virtual_irq(realirq); + } + spin_unlock(&irq_controller_lock); + return; + +out_unlock: + spin_unlock(&irq_controller_lock); + return; +} + +static void enable_piix4_virtual_irq(unsigned int irq) +{ + /* + * assumes this irq is one of the legacy devices + */ + + unsigned int mask = inb(0x21); + mask &= ~(1 << irq); + outb(mask, 0x21); + enable_cobalt_irq(irq); +} + +/* + * assumes this irq is one of the legacy devices + */ +static void disable_piix4_virtual_irq(unsigned int irq) +{ + unsigned int mask; + + disable_cobalt_irq(irq); + + mask = inb(0x21); + mask &= ~(1 << irq); + outb(mask, 0x21); +} + +static struct irqaction master_action = + { no_action, 0, 0, "PIIX4-8259", NULL, NULL }; + +void init_VISWS_APIC_irqs(void) +{ + int i; + + for (i = 0; i < 16; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 0; + + /* + * Cobalt IRQs are mapped to standard ISA + * interrupt vectors: + */ + switch (i) { + /* + * Only CO_IRQ_8259 will be raised + * externally. + */ + case CO_IRQ_8259: + irq_desc[i].handler = &piix4_master_irq_type; + break; + case CO_IRQ_FLOPPY: + case CO_IRQ_PARLL: + irq_desc[i].handler = &piix4_virtual_irq_type; + break; + default: + irq_desc[i].handler = &cobalt_irq_type; + break; + } + } + + /* + * The master interrupt is always present: + */ + setup_x86_irq(CO_IRQ_8259, &master_action); +} + diff --git a/arch/i386/lib/Makefile b/arch/i386/lib/Makefile index 6490984b1..c2cb3e5a6 100644 --- a/arch/i386/lib/Makefile +++ b/arch/i386/lib/Makefile @@ -6,6 +6,7 @@ $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o L_TARGET = lib.a -L_OBJS = checksum.o semaphore.o delay.o usercopy.o getuser.o putuser.o +L_OBJS = checksum.o old-checksum.o semaphore.o delay.o \ + usercopy.o getuser.o putuser.o include $(TOPDIR)/Rules.make diff --git a/arch/i386/lib/checksum.S b/arch/i386/lib/checksum.S new file mode 100644 index 000000000..46527a85d --- /dev/null +++ b/arch/i386/lib/checksum.S @@ -0,0 +1,447 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * IP/TCP/UDP checksumming routines + * + * Authors: Jorge Cwik, <jorge@laser.satlink.net> + * Arnt Gulbrandsen, <agulbra@nvg.unit.no> + * Tom May, <ftom@netcom.com> + * Pentium Pro/II routines: + * Alexander Kjeldaas <astor@guardian.no> + * Finn Arne Gangstad <finnag@guardian.no> + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception + * handling. + * Andi Kleen, add zeroing on error + * converted to pure assembler + * + * 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 <asm/errno.h> + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ + +/* +unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) + */ + +.text +.align 4 +.globl csum_partial + +#if CPU!=686 + + /* + * Experiments with Ethernet and SLIP connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. We get at + * least a twofold speedup on 486 and Pentium if it is 4-byte aligned. + * Fortunately, it is easy to convert 2-byte alignment to 4-byte + * alignment for the unrolled loop. + */ +csum_partial: + pushl %esi + pushl %ebx + movl 20(%esp),%eax # Function arg: unsigned int sum + movl 16(%esp),%ecx # Function arg: int len + movl 12(%esp),%esi # Function arg: unsigned char *buff + testl $2, %esi # Check alignment. + jz 2f # Jump if alignment is ok. + subl $2, %ecx # Alignment uses up two bytes. + jae 1f # Jump if we had at least two bytes. + addl $2, %ecx # ecx was < 2. Deal with it. + jmp 4f +1: movw (%esi), %bx + addl $2, %esi + addw %bx, %ax + adcl $0, %eax +2: + movl %ecx, %edx + shrl $5, %ecx + jz 2f + testl %esi, %esi +1: movl (%esi), %ebx + adcl %ebx, %eax + movl 4(%esi), %ebx + adcl %ebx, %eax + movl 8(%esi), %ebx + adcl %ebx, %eax + movl 12(%esi), %ebx + adcl %ebx, %eax + movl 16(%esi), %ebx + adcl %ebx, %eax + movl 20(%esi), %ebx + adcl %ebx, %eax + movl 24(%esi), %ebx + adcl %ebx, %eax + movl 28(%esi), %ebx + adcl %ebx, %eax + lea 32(%esi), %esi + dec %ecx + jne 1b + adcl $0, %eax +2: movl %edx, %ecx + andl $0x1c, %edx + je 4f + shrl $2, %edx # This clears CF +3: adcl (%esi), %eax + lea 4(%esi), %esi + dec %edx + jne 3b + adcl $0, %eax +4: andl $3, %ecx + jz 7f + cmpl $2, %ecx + jb 5f + movw (%esi),%cx + leal 2(%esi),%esi + je 6f + shll $16,%ecx +5: movb (%esi),%cl +6: addl %ecx,%eax + adcl $0, %eax +7: + popl %ebx + popl %esi + ret + +#else /* CPU==686 */ + +csum_partial: + movl 12(%esp),%eax # Function arg: unsigned int sum + movl 8(%esp),%ecx # Function arg: int len + movl 4(%esp),%esi # Function arg: const unsigned char *buf + + testl $2, %esi + jnz 30f +10: + movl %ecx, %edx + movl %ecx, %ebx + andl $0x7c, %ebx + shrl $7, %ecx + addl %ebx,%esi + shrl $2, %ebx + negl %ebx + lea 45f(%ebx,%ebx,2), %ebx + testl %esi, %esi + jmp *%ebx + + # Handle 2-byte-aligned regions +20: addw (%esi), %ax + lea 2(%esi), %esi + adcl $0, %eax + jmp 10b + +30: subl $2, %ecx + ja 20b + je 32f + movzbl (%esi),%ebx # csumming 1 byte, 2-aligned + addl %ebx, %eax + adcl $0, %eax + jmp 80f +32: + addw (%esi), %ax # csumming 2 bytes, 2-aligned + adcl $0, %eax + jmp 80f + +40: + addl -128(%esi), %eax + adcl -124(%esi), %eax + adcl -120(%esi), %eax + adcl -116(%esi), %eax + adcl -112(%esi), %eax + adcl -108(%esi), %eax + adcl -104(%esi), %eax + adcl -100(%esi), %eax + adcl -96(%esi), %eax + adcl -92(%esi), %eax + adcl -88(%esi), %eax + adcl -84(%esi), %eax + adcl -80(%esi), %eax + adcl -76(%esi), %eax + adcl -72(%esi), %eax + adcl -68(%esi), %eax + adcl -64(%esi), %eax + adcl -60(%esi), %eax + adcl -56(%esi), %eax + adcl -52(%esi), %eax + adcl -48(%esi), %eax + adcl -44(%esi), %eax + adcl -40(%esi), %eax + adcl -36(%esi), %eax + adcl -32(%esi), %eax + adcl -28(%esi), %eax + adcl -24(%esi), %eax + adcl -20(%esi), %eax + adcl -16(%esi), %eax + adcl -12(%esi), %eax + adcl -8(%esi), %eax + adcl -4(%esi), %eax +45: + lea 128(%esi), %esi + adcl $0, %eax + dec %ecx + jge 40b + movl %edx, %ecx +50: andl $3, %ecx + jz 80f + + # Handle the last 1-3 bytes without jumping + notl %ecx # 1->2, 2->1, 3->0, higher bits are masked + movl $0xffffff,%ebx # by the shll and shrl instructions + shll $3,%ecx + shrl %cl,%ebx + andl -128(%esi),%ebx # esi is 4-aligned so should be ok + addl %ebx,%eax + adcl $0,%eax +80: + ret + +#endif /* CPU==686 */ + +/* +unsigned int csum_partial_copy_generic (const char *src, char *dst, + int len, int sum, int *src_err_ptr, int *dst_err_ptr) + */ + +/* + * Copy from ds while checksumming, otherwise like csum_partial + * + * The macros SRC and DST specify the type of access for the instruction. + * thus we can call a custom exception handler for all access types. + * + * FIXME: could someone double-check whether I haven't mixed up some SRC and + * DST definitions? It's damn hard to trigger all cases. I hope I got + * them all but there's no guarantee. + */ + +#define SRC(y...) \ + 9999: y; \ + .section __ex_table, "a"; \ + .long 9999b, 6001f ; \ + .previous + +#define DST(y...) \ + 9999: y; \ + .section __ex_table, "a"; \ + .long 9999b, 6002f ; \ + .previous + +.align 4 +.globl csum_partial_copy_generic + +#if CPU!=686 + +#define ARGBASE 16 +#define FP 12 + +csum_partial_copy_generic: + subl $4,%esp + pushl %edi + pushl %esi + pushl %ebx + movl ARGBASE+16(%esp),%eax # sum + movl ARGBASE+12(%esp),%ecx # len + movl ARGBASE+4(%esp),%esi # src + movl ARGBASE+8(%esp),%edi # dst + + testl $2, %edi # Check alignment. + jz 2f # Jump if alignment is ok. + subl $2, %ecx # Alignment uses up two bytes. + jae 1f # Jump if we had at least two bytes. + addl $2, %ecx # ecx was < 2. Deal with it. + jmp 4f +SRC(1: movw (%esi), %bx ) + addl $2, %esi +DST( movw %bx, (%edi) ) + addl $2, %edi + addw %bx, %ax + adcl $0, %eax +2: + movl %ecx, FP(%esp) + shrl $5, %ecx + jz 2f + testl %esi, %esi +SRC(1: movl (%esi), %ebx ) +SRC( movl 4(%esi), %edx ) + adcl %ebx, %eax +DST( movl %ebx, (%edi) ) + adcl %edx, %eax +DST( movl %edx, 4(%edi) ) + +SRC( movl 8(%esi), %ebx ) +SRC( movl 12(%esi), %edx ) + adcl %ebx, %eax +DST( movl %ebx, 8(%edi) ) + adcl %edx, %eax +DST( movl %edx, 12(%edi) ) + +SRC( movl 16(%esi), %ebx ) +SRC( movl 20(%esi), %edx ) + adcl %ebx, %eax +DST( movl %ebx, 16(%edi) ) + adcl %edx, %eax +DST( movl %edx, 20(%edi) ) + +SRC( movl 24(%esi), %ebx ) +SRC( movl 28(%esi), %edx ) + adcl %ebx, %eax +DST( movl %ebx, 24(%edi) ) + adcl %edx, %eax +DST( movl %edx, 28(%edi) ) + + lea 32(%esi), %esi + lea 32(%edi), %edi + dec %ecx + jne 1b + adcl $0, %eax +2: movl FP(%esp), %edx + movl %edx, %ecx + andl $0x1c, %edx + je 4f + shrl $2, %edx # This clears CF +SRC(3: movl (%esi), %ebx ) + adcl %ebx, %eax +DST( movl %ebx, (%edi) ) + lea 4(%esi), %esi + lea 4(%edi), %edi + dec %edx + jne 3b + adcl $0, %eax +4: andl $3, %ecx + jz 7f + cmpl $2, %ecx + jb 5f +SRC( movw (%esi), %cx ) + leal 2(%esi), %esi +DST( movw %cx, (%edi) ) + leal 2(%edi), %edi + je 6f + shll $16,%ecx +SRC(5: movb (%esi), %cl ) +DST( movb %cl, (%edi) ) +6: addl %ecx, %eax + adcl $0, %eax +7: +5000: + +# Exception handler: +.section .fixup, "ax" + +6001: + movl ARGBASE+20(%esp), %ebx # src_err_ptr + movl $-EFAULT, (%ebx) + + # zero the complete destination - computing the rest + # is too much work + movl ARGBASE+8(%esp), %edi # dst + movl ARGBASE+12(%esp), %ecx # len + xorl %eax,%eax + rep ; stosb + + jmp 5000b + +6002: + movl ARGBASE+24(%esp), %ebx # dst_err_ptr + movl $-EFAULT,(%ebx) + jmp 5000b + +.previous + + popl %ebx + popl %esi + popl %edi + popl %ecx # equivalent to addl $4,%esp + ret + +#else + +/* Version for PentiumII/PPro */ + +#define ROUND1(x) \ + SRC(movl x(%esi), %ebx ) ; \ + addl %ebx, %eax\n ; \ + DST(movl %ebx, x(%edi) ) ; + +#define ROUND(x) \ + SRC(movl x(%esi), %ebx ) ; \ + adcl %ebx, %eax ; \ + DST(movl %ebx, x(%edi) ) ; + +#define ARGBASE 12 + +csum_partial_copy_generic: + pushl %ebx + pushl %edi + pushl %esi + movl ARGBASE+4(%esp),%esi #src + movl ARGBASE+8(%esp),%edi #dst + movl ARGBASE+12(%esp),%ecx #len + movl ARGBASE+16(%esp),%eax #sum + movl %ecx, %edx + movl %ecx, %ebx + shrl $6, %ecx + andl $0x3c, %ebx + negl %ebx + subl %ebx, %esi + subl %ebx, %edi + lea 3f(%ebx,%ebx), %ebx + testl %esi, %esi + jmp *%ebx +1: addl $64,%esi + addl $64,%edi + ROUND1(-64) ROUND(-60) ROUND(-56) ROUND(-52) + ROUND (-48) ROUND(-44) ROUND(-40) ROUND(-36) + ROUND (-32) ROUND(-28) ROUND(-24) ROUND(-20) + ROUND (-16) ROUND(-12) ROUND(-8) ROUND(-4) +3: adcl $0,%eax + dec %ecx + jge 1b +4: andl $3, %edx + jz 7f + cmpl $2, %edx + jb 5f +SRC( movw (%esi), %dx ) + leal 2(%esi), %esi +DST( movw %dx, (%edi) ) + leal 2(%edi), %edi + je 6f + shll $16,%edx +5: +SRC( movb (%esi), %dl ) +DST( movb %dl, (%edi) ) +6: addl %edx, %eax + adcl $0, %eax +7: +.section .fixup, "ax" +6001: movl ARGBASE+20(%esp), %ebx # src_err_ptr + movl $-EFAULT, (%ebx) + # zero the complete destination (computing the rest is too much work) + movl ARGBASE+8(%esp),%edi # dst + movl ARGBASE+12(%esp),%ecx # len + xorl %eax,%eax + rep; stosb + jmp 7b +6002: movl ARGBASE+24(%esp), %ebx # dst_err_ptr + movl $-EFAULT, (%ebx) + jmp 7b +.previous + + popl %esi + popl %edi + popl %ebx + ret + +#undef ROUND +#undef ROUND1 + +#endif /* CPU==i686 */ diff --git a/arch/i386/lib/checksum.c b/arch/i386/lib/checksum.c deleted file mode 100644 index 51a9219db..000000000 --- a/arch/i386/lib/checksum.c +++ /dev/null @@ -1,459 +0,0 @@ -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * IP/TCP/UDP checksumming routines - * - * Authors: Jorge Cwik, <jorge@laser.satlink.net> - * Arnt Gulbrandsen, <agulbra@nvg.unit.no> - * Tom May, <ftom@netcom.com> - * Pentium Pro/II routines: - * Alexander Kjeldaas <astor@guardian.no> - * Finn Arne Gangstad <finnag@guardian.no> - * Lots of code moved from tcp.c and ip.c; see those files - * for more names. - * - * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception - * handling. - * Andi Kleen, add zeroing on error, fix constraints. - * - * To fix: - * Convert to pure asm, because this file is too hard - * for gcc's register allocator and it is not clear if the - * contraints are correct. - * - * 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 <net/checksum.h> - -/* - * computes a partial checksum, e.g. for TCP/UDP fragments - */ - -#if CPU!=686 - -unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) { - /* - * Experiments with Ethernet and SLIP connections show that buff - * is aligned on either a 2-byte or 4-byte boundary. We get at - * least a twofold speedup on 486 and Pentium if it is 4-byte aligned. - * Fortunately, it is easy to convert 2-byte alignment to 4-byte - * alignment for the unrolled loop. - */ - __asm__(" - testl $2, %%esi # Check alignment. - jz 2f # Jump if alignment is ok. - subl $2, %%ecx # Alignment uses up two bytes. - jae 1f # Jump if we had at least two bytes. - addl $2, %%ecx # ecx was < 2. Deal with it. - jmp 4f -1: movw (%%esi), %%bx - addl $2, %%esi - addw %%bx, %%ax - adcl $0, %%eax -2: - movl %%ecx, %%edx - shrl $5, %%ecx - jz 2f - testl %%esi, %%esi -1: movl (%%esi), %%ebx - adcl %%ebx, %%eax - movl 4(%%esi), %%ebx - adcl %%ebx, %%eax - movl 8(%%esi), %%ebx - adcl %%ebx, %%eax - movl 12(%%esi), %%ebx - adcl %%ebx, %%eax - movl 16(%%esi), %%ebx - adcl %%ebx, %%eax - movl 20(%%esi), %%ebx - adcl %%ebx, %%eax - movl 24(%%esi), %%ebx - adcl %%ebx, %%eax - movl 28(%%esi), %%ebx - adcl %%ebx, %%eax - lea 32(%%esi), %%esi - dec %%ecx - jne 1b - adcl $0, %%eax -2: movl %%edx, %%ecx - andl $0x1c, %%edx - je 4f - shrl $2, %%edx # This clears CF -3: adcl (%%esi), %%eax - lea 4(%%esi), %%esi - dec %%edx - jne 3b - adcl $0, %%eax -4: andl $3, %%ecx - jz 7f - cmpl $2, %%ecx - jb 5f - movw (%%esi),%%cx - leal 2(%%esi),%%esi - je 6f - shll $16,%%ecx -5: movb (%%esi),%%cl -6: addl %%ecx,%%eax - adcl $0, %%eax -7: " - : "=a"(sum) - : "0"(sum), "c"(len), "S"(buff) - : "bx", "dx", "si", "cx", "memory"); - return(sum); -} - -#else /* 686 */ - -unsigned int csum_partial(const unsigned char * buf, int len, unsigned int sum) { - __asm__ (" - testl $2, %%esi - jnz 30f -10: - movl %%ecx, %%edx - movl %%ecx, %%ebx - andl $0x7c, %%ebx - shrl $7, %%ecx - addl %%ebx,%%esi - shrl $2, %%ebx - negl %%ebx - lea 45f(%%ebx,%%ebx,2), %%ebx - testl %%esi, %%esi - jmp *%%ebx - - # Handle 2-byte-aligned regions -20: addw (%%esi), %%ax - lea 2(%%esi), %%esi - adcl $0, %%eax - jmp 10b - -30: subl $2, %%ecx - ja 20b - je 32f - movzbl (%%esi),%%ebx # csumming 1 byte, 2-aligned - addl %%ebx, %%eax - adcl $0, %%eax - jmp 80f -32: - addw (%%esi), %%ax # csumming 2 bytes, 2-aligned - adcl $0, %%eax - jmp 80f - -40: - addl -128(%%esi), %%eax - adcl -124(%%esi), %%eax - adcl -120(%%esi), %%eax - adcl -116(%%esi), %%eax - adcl -112(%%esi), %%eax - adcl -108(%%esi), %%eax - adcl -104(%%esi), %%eax - adcl -100(%%esi), %%eax - adcl -96(%%esi), %%eax - adcl -92(%%esi), %%eax - adcl -88(%%esi), %%eax - adcl -84(%%esi), %%eax - adcl -80(%%esi), %%eax - adcl -76(%%esi), %%eax - adcl -72(%%esi), %%eax - adcl -68(%%esi), %%eax - adcl -64(%%esi), %%eax - adcl -60(%%esi), %%eax - adcl -56(%%esi), %%eax - adcl -52(%%esi), %%eax - adcl -48(%%esi), %%eax - adcl -44(%%esi), %%eax - adcl -40(%%esi), %%eax - adcl -36(%%esi), %%eax - adcl -32(%%esi), %%eax - adcl -28(%%esi), %%eax - adcl -24(%%esi), %%eax - adcl -20(%%esi), %%eax - adcl -16(%%esi), %%eax - adcl -12(%%esi), %%eax - adcl -8(%%esi), %%eax - adcl -4(%%esi), %%eax -45: - lea 128(%%esi), %%esi - adcl $0, %%eax - dec %%ecx - jge 40b - movl %%edx, %%ecx -50: andl $3, %%ecx - jz 80f - - # Handle the last 1-3 bytes without jumping - notl %%ecx # 1->2, 2->1, 3->0, higher bits are masked - movl $0xffffff,%%ebx # by the shll and shrl instructions - shll $3,%%ecx - shrl %%cl,%%ebx - andl -128(%%esi),%%ebx # esi is 4-aligned so should be ok - addl %%ebx,%%eax - adcl $0,%%eax -80: " - : "=a"(sum) - : "0"(sum), "c"(len), "S"(buf) - : "bx", "dx", "cx", "si", "memory"); - return(sum); -} - -#endif - -/* - * Copy from ds while checksumming, otherwise like csum_partial - * - * The macros SRC and DST specify the type of access for the instruction. - * thus we can call a custom exception handler for all access types. - * - * FIXME: could someone double-check whether I haven't mixed up some SRC and - * DST definitions? It's damn hard to trigger all cases. I hope I got - * them all but there's no guarantee. - */ - -#define SRC(y...) \ -" 9999: "#y"; \n \ - .section __ex_table, \"a\"; \n \ - .long 9999b, 6001f \n \ - .previous\n" - -#define DST(y...) \ -" 9999: "#y"; \n \ - .section __ex_table, \"a\"; \n \ - .long 9999b, 6002f \n \ - .previous\n" - -#if CPU!=686 - -unsigned int csum_partial_copy_generic (const char *src, char *dst, - int len, int sum, int *src_err_ptr, int *dst_err_ptr) -{ - __u32 tmp_var; - - __asm__ __volatile__ ( " - movl %6,%%edi - testl $2, %%edi # Check alignment. - jz 2f # Jump if alignment is ok. - subl $2, %%ecx # Alignment uses up two bytes. - jae 1f # Jump if we had at least two bytes. - addl $2, %%ecx # ecx was < 2. Deal with it. - jmp 4f -"SRC( 1: movw (%%esi), %%bx )" - addl $2, %%esi -"DST( movw %%bx, (%%edi) )" - addl $2, %%edi - addw %%bx, %%ax - adcl $0, %%eax - 2: - movl %%ecx, %8 - shrl $5, %%ecx - jz 2f - testl %%esi, %%esi -"SRC( 1: movl (%%esi), %%ebx )" -"SRC( movl 4(%%esi), %%edx )" - adcl %%ebx, %%eax -"DST( movl %%ebx, (%%edi) )" - adcl %%edx, %%eax -"DST( movl %%edx, 4(%%edi) )" - -"SRC( movl 8(%%esi), %%ebx )" -"SRC( movl 12(%%esi), %%edx )" - adcl %%ebx, %%eax -"DST( movl %%ebx, 8(%%edi) )" - adcl %%edx, %%eax -"DST( movl %%edx, 12(%%edi) )" - -"SRC( movl 16(%%esi), %%ebx )" -"SRC( movl 20(%%esi), %%edx )" - adcl %%ebx, %%eax -"DST( movl %%ebx, 16(%%edi) )" - adcl %%edx, %%eax -"DST( movl %%edx, 20(%%edi) )" - -"SRC( movl 24(%%esi), %%ebx )" -"SRC( movl 28(%%esi), %%edx )" - adcl %%ebx, %%eax -"DST( movl %%ebx, 24(%%edi) )" - adcl %%edx, %%eax -"DST( movl %%edx, 28(%%edi) )" - -"SRC( lea 32(%%esi), %%esi )" -"DST( lea 32(%%edi), %%edi )" - dec %%ecx - jne 1b - adcl $0, %%eax - 2: movl %8, %%edx - movl %%edx, %%ecx - andl $0x1c, %%edx - je 4f - shrl $2, %%edx # This clears CF -"SRC( 3: movl (%%esi), %%ebx )" - adcl %%ebx, %%eax -"DST( movl %%ebx, (%%edi) )" -"SRC( lea 4(%%esi), %%esi )" -"DST( lea 4(%%edi), %%edi )" - dec %%edx - jne 3b - adcl $0, %%eax - 4: andl $3, %%ecx - jz 7f - cmpl $2, %%ecx - jb 5f -"SRC( movw (%%esi), %%cx )" -"SRC( leal 2(%%esi), %%esi )" -"DST( movw %%cx, (%%edi) )" -"DST( leal 2(%%edi), %%edi )" - je 6f - shll $16,%%ecx -"SRC( 5: movb (%%esi), %%cl )" -"DST( movb %%cl, (%%edi) )" - 6: addl %%ecx, %%eax - adcl $0, %%eax - 7: - -5000: - -# Exception handler: -################################################ - # -.section .fixup, \"ax\" # - # -6000: # - # - movl %7, (%%ebx) # - # -# zero the complete destination - computing the rest -# is too much work - movl %6, %%edi - movl %9, %%ecx - xorl %%eax,%%eax - rep ; stosb - # - jmp 5000b # - # -6001: # - movl %1, %%ebx # - jmp 6000b # - # -6002: # - movl %2, %%ebx # - jmp 6000b # - # -.previous # - # -################################################ - -" - : "=a" (sum) - : "m" (src_err_ptr), "m" (dst_err_ptr), - "0" (sum), "c" (len), "S" (src), "m" (dst), - "i" (-EFAULT), "m"(tmp_var), - "m" (len) - : "bx", "dx", "si", "di", "cx", "memory" ); - - return(sum); -} - -#else /* CPU == 686 */ - -#define ROUND1(x) \ - SRC(movl x(%%esi), %%ebx ) \ - "addl %%ebx, %%eax\n" \ - DST(movl %%ebx, x(%%edi) ) - -#define ROUND(x) \ - SRC(movl x(%%esi), %%ebx ) \ - "adcl %%ebx, %%eax\n" \ - DST(movl %%ebx, x(%%edi) ) - -unsigned int csum_partial_copy_generic (const char *src, char *dst, - int len, int sum, int *src_err_ptr, int *dst_err_ptr) -{ - __asm__ __volatile__ (" - movl %4,%%ecx - movl %%ecx, %%edx - movl %%ecx, %%ebx - shrl $6, %%ecx - andl $0x3c, %%ebx - negl %%ebx - subl %%ebx, %%esi - subl %%ebx, %%edi - lea 3f(%%ebx,%%ebx), %%ebx - testl %%esi, %%esi - jmp *%%ebx -1: addl $64,%%esi - addl $64,%%edi\n" -ROUND1(-64) ROUND(-60) ROUND(-56) ROUND(-52) -ROUND (-48) ROUND(-44) ROUND(-40) ROUND(-36) -ROUND (-32) ROUND(-28) ROUND(-24) ROUND(-20) -ROUND (-16) ROUND(-12) ROUND(-8) ROUND(-4) -"3: adcl $0,%%eax - dec %%ecx - jge 1b -4: andl $3, %%edx - jz 7f - cmpl $2, %%edx - jb 5f - " SRC(movw (%%esi), %%dx )" - leal 2(%%esi), %%esi - " DST(movw %%dx, (%%edi) )" - leal 2(%%edi), %%edi - je 6f - shll $16,%%edx -5:" SRC(movb (%%esi), %%dl )" - " DST(movb %%dl, (%%edi) )" -6: addl %%edx, %%eax - adcl $0, %%eax -7: -.section .fixup, \"ax\" -6000: movl %7, (%%ebx) -# zero the complete destination (computing the rest is too much work) - movl %8,%%edi - movl %4,%%ecx - xorl %%eax,%%eax - rep ; stosb - jmp 7b -6001: movl %1, %%ebx - jmp 6000b -6002: movl %2, %%ebx - jmp 6000b -.previous - " - : "=a"(sum) - : "m"(src_err_ptr), "m"(dst_err_ptr), - "0"(sum), "m"(len), "S"(src), "D" (dst), - "i" (-EFAULT), - "m" (dst) - : "bx", "cx", "si", "di", "dx", "memory" ); - return(sum); -} - -#undef ROUND -#undef ROUND1 - -#endif - - -#undef SRC -#undef DST - -/* - * FIXME: old compatibility stuff, will be removed soon. - */ - -unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum) -{ - int src_err=0, dst_err=0; - - sum = csum_partial_copy_generic ( src, dst, len, sum, &src_err, &dst_err); - - if (src_err || dst_err) - printk("old csum_partial_copy_fromuser(), tell mingo to convert me.\n"); - - return sum; -} - - diff --git a/arch/i386/lib/delay.c b/arch/i386/lib/delay.c index ae8aec636..6918451a6 100644 --- a/arch/i386/lib/delay.c +++ b/arch/i386/lib/delay.c @@ -19,23 +19,23 @@ void __delay(unsigned long loops) { + int d0; __asm__ __volatile__( "\tjmp 1f\n" ".align 16\n" "1:\tjmp 2f\n" ".align 16\n" "2:\tdecl %0\n\tjns 2b" - :/* no outputs */ - :"a" (loops) - :"ax"); + :"=&a" (d0) + :"0" (loops)); } inline void __const_udelay(unsigned long xloops) { + int d0; __asm__("mull %0" - :"=d" (xloops) - :"a" (xloops),"0" (current_cpu_data.loops_per_sec) - :"ax"); + :"=d" (xloops), "=&a" (d0) + :"1" (xloops),"0" (current_cpu_data.loops_per_sec)); __delay(xloops); } diff --git a/arch/i386/lib/old-checksum.c b/arch/i386/lib/old-checksum.c new file mode 100644 index 000000000..ae3a38043 --- /dev/null +++ b/arch/i386/lib/old-checksum.c @@ -0,0 +1,19 @@ +/* + * FIXME: old compatibility stuff, will be removed soon. + */ + +#include <net/checksum.h> + +unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum) +{ + int src_err=0, dst_err=0; + + sum = csum_partial_copy_generic ( src, dst, len, sum, &src_err, &dst_err); + + if (src_err || dst_err) + printk("old csum_partial_copy_fromuser(), tell mingo to convert me.\n"); + + return sum; +} + + diff --git a/arch/i386/lib/usercopy.c b/arch/i386/lib/usercopy.c index d5b052c20..f43be511f 100644 --- a/arch/i386/lib/usercopy.c +++ b/arch/i386/lib/usercopy.c @@ -29,6 +29,8 @@ __generic_copy_from_user(void *to, const void *from, unsigned long n) */ #define __do_strncpy_from_user(dst,src,count,res) \ +do { \ + int __d0, __d1, __d2; \ __asm__ __volatile__( \ " testl %1,%1\n" \ " jz 2f\n" \ @@ -41,16 +43,18 @@ __generic_copy_from_user(void *to, const void *from, unsigned long n) "1: subl %1,%0\n" \ "2:\n" \ ".section .fixup,\"ax\"\n" \ - "3: movl %2,%0\n" \ + "3: movl %5,%0\n" \ " jmp 2b\n" \ ".previous\n" \ ".section __ex_table,\"a\"\n" \ " .align 4\n" \ " .long 0b,3b\n" \ ".previous" \ - : "=d"(res), "=c"(count) \ - : "i"(-EFAULT), "0"(count), "1"(count), "S"(src), "D"(dst) \ - : "si", "di", "ax", "memory") + : "=d"(res), "=c"(count), "=&a" (__d0), "=&S" (__d1), \ + "=&D" (__d2) \ + : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), "4"(dst) \ + : "memory"); \ +} while (0) long __strncpy_from_user(char *dst, const char *src, long count) @@ -74,14 +78,16 @@ strncpy_from_user(char *dst, const char *src, long count) * Zero Userspace */ -#define __do_clear_user(addr,size) \ - __asm__ __volatile__( \ +#define __do_clear_user(addr,size) \ +do { \ + int __d0; \ + __asm__ __volatile__( \ "0: rep; stosl\n" \ - " movl %1,%0\n" \ + " movl %2,%0\n" \ "1: rep; stosb\n" \ "2:\n" \ ".section .fixup,\"ax\"\n" \ - "3: lea 0(%1,%0,4),%0\n" \ + "3: lea 0(%2,%0,4),%0\n" \ " jmp 2b\n" \ ".previous\n" \ ".section __ex_table,\"a\"\n" \ @@ -89,9 +95,9 @@ strncpy_from_user(char *dst, const char *src, long count) " .long 0b,3b\n" \ " .long 1b,2b\n" \ ".previous" \ - : "=&c"(size) \ - : "r"(size & 3), "0"(size / 4), "D"(addr), "a"(0) \ - : "di") + : "=&c"(size), "=&D" (__d0) \ + : "r"(size & 3), "0"(size / 4), "1"(addr), "a"(0)); \ +} while (0) unsigned long clear_user(void *to, unsigned long n) diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 693072b1a..dc96ad4bb 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -119,24 +119,28 @@ int do_check_pgt_cache(int low, int high) pte_t * __bad_pagetable(void) { extern char empty_bad_page_table[PAGE_SIZE]; - - __asm__ __volatile__("cld ; rep ; stosl": - :"a" (pte_val(BAD_PAGE)), - "D" ((long) empty_bad_page_table), - "c" (PAGE_SIZE/4) - :"di","cx"); + int d0, d1; + + __asm__ __volatile__("cld ; rep ; stosl" + : "=&D" (d0), "=&c" (d1) + : "a" (pte_val(BAD_PAGE)), + "0" ((long) empty_bad_page_table), + "1" (PAGE_SIZE/4) + : "memory"); return (pte_t *) empty_bad_page_table; } pte_t __bad_page(void) { extern char empty_bad_page[PAGE_SIZE]; - - __asm__ __volatile__("cld ; rep ; stosl": - :"a" (0), - "D" ((long) empty_bad_page), - "c" (PAGE_SIZE/4) - :"di","cx"); + int d0, d1; + + __asm__ __volatile__("cld ; rep ; stosl" + : "=&D" (d0), "=&c" (d1) + : "a" (0), + "0" ((long) empty_bad_page), + "1" (PAGE_SIZE/4) + : "memory"); return pte_mkdirty(mk_pte((unsigned long) empty_bad_page, PAGE_SHARED)); } @@ -275,38 +279,6 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_ * kernel. * It may also hold the MP configuration table when we are booting SMP. */ -#ifdef __SMP__ - /* - * FIXME: Linux assumes you have 640K of base ram.. - * this continues the error... - * - * 1) Scan the bottom 1K for a signature - * 2) Scan the top 1K of base RAM - * 3) Scan the 64K of bios - */ - if (!smp_scan_config(0x0,0x400) && - !smp_scan_config(639*0x400,0x400) && - !smp_scan_config(0xF0000,0x10000)) { - /* - * If it is an SMP machine we should know now, unless the - * configuration is in an EISA/MCA bus machine with an - * extended bios data area. - * - * there is a real-mode segmented pointer pointing to the - * 4K EBDA area at 0x40E, calculate and scan it here. - * - * NOTE! There are Linux loaders that will corrupt the EBDA - * area, and as such this kind of SMP config may be less - * trustworthy, simply because the SMP table may have been - * stomped on during early boot. - */ - address = *(unsigned short *)phys_to_virt(0x40E); - address<<=4; - smp_scan_config(address, 0x1000); - if (smp_found_config) - printk(KERN_WARNING "WARNING: MP table in the EBDA can be UNSAFE, contact linux-smp@vger.rutgers.edu if you experience SMP problems!\n"); - } -#endif start_mem = PAGE_ALIGN(start_mem); address = PAGE_OFFSET; pg_dir = swapper_pg_dir; @@ -403,7 +375,7 @@ __initfunc(void test_wp_bit(void)) if (boot_cpu_data.wp_works_ok < 0) { boot_cpu_data.wp_works_ok = 0; printk("No.\n"); -#ifndef CONFIG_M386 +#ifdef CONFIG_X86_WP_WORKS_OK panic("This kernel doesn't support CPU's with broken WP. Recompile it for a 386!"); #endif } else diff --git a/arch/i386/vmlinux.lds b/arch/i386/vmlinux.lds index c23007bc8..203b9a927 100644 --- a/arch/i386/vmlinux.lds +++ b/arch/i386/vmlinux.lds @@ -45,9 +45,13 @@ SECTIONS . = ALIGN(4096); __init_end = .; + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + . = ALIGN(4096); .data.page_aligned : { *(.data.idt) } + __bss_start = .; /* BSS */ .bss : { *(.bss) diff --git a/arch/m68k/Makefile b/arch/m68k/Makefile index 9cd2d1838..cb60ea268 100644 --- a/arch/m68k/Makefile +++ b/arch/m68k/Makefile @@ -29,14 +29,21 @@ LINKFLAGS = -T $(TOPDIR)/arch/m68k/vmlinux.lds # without -fno-strength-reduce the 53c7xx.c driver fails ;-( CFLAGS += -pipe -fno-strength-reduce -ffixed-a2 -ifdef CONFIG_OPTIMIZE_040 +# enable processor switch if compiled only for a single cpu +ifndef CONFIG_M68020 +ifndef CONFIG_M68030 + +ifndef CONFIG_M68060 CFLAGS := $(CFLAGS) -m68040 endif -ifdef CONFIG_OPTIMIZE_060 +ifndef CONFIG_M68040 CFLAGS := $(CFLAGS) -m68060 endif +endif +endif + ifdef CONFIG_KGDB # If configured for kgdb support, include debugging infos and keep the # frame pointer @@ -116,6 +123,7 @@ endif archclean: rm -f vmlinux.gz + rm -f arch/m68k/kernel/m68k_defs.h arch/m68k/kernel/m68k_defs.d archmrproper: diff --git a/arch/m68k/amiga/amiints.c b/arch/m68k/amiga/amiints.c index 25955213e..4fb886456 100644 --- a/arch/m68k/amiga/amiints.c +++ b/arch/m68k/amiga/amiints.c @@ -15,7 +15,7 @@ * - IRQ_FLG_SLOW: handler is inserted at bottom of list and before * they're executed irq level is set to the previous * one, but handlers don't need to be reentrant, if - * reentrance occured, slow handlers will be just + * reentrance occurred, slow handlers will be just * called again. * The whole interrupt handling for CIAs is moved to cia.c * /Roman Zippel @@ -367,7 +367,7 @@ void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server) for (;;) { for (; node; node = node->next) node->handler(irq, node->dev_id, fp); - /* if reentrance occured, serve slow handlers again */ + /* if reentrance occurred, serve slow handlers again */ custom.intena = ami_intena_vals[irq]; if (!server->reentrance) { server->count--; diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c index 63cb5265c..37b5bde3c 100644 --- a/arch/m68k/amiga/config.c +++ b/arch/m68k/amiga/config.c @@ -52,7 +52,6 @@ static void amiga_sched_init(void (*handler)(int, void *, struct pt_regs *)); /* amiga specific keyboard functions */ extern int amiga_keyb_init(void); extern int amiga_kbdrate (struct kbd_repeat *); -extern void amiga_kbd_reset_setup(char*, int); /* amiga specific irq functions */ extern void amiga_init_IRQ (void); extern void (*amiga_default_handler[]) (int, void *, struct pt_regs *); @@ -343,7 +342,6 @@ __initfunc(void config_amiga(void)) mach_sched_init = amiga_sched_init; mach_keyb_init = amiga_keyb_init; mach_kbdrate = amiga_kbdrate; - kbd_reset_setup = amiga_kbd_reset_setup; mach_init_IRQ = amiga_init_IRQ; mach_default_handler = &amiga_default_handler; mach_request_irq = amiga_request_irq; diff --git a/arch/m68k/amiga/pcmcia.c b/arch/m68k/amiga/pcmcia.c index 3faac7eda..e1b29387a 100644 --- a/arch/m68k/amiga/pcmcia.c +++ b/arch/m68k/amiga/pcmcia.c @@ -14,6 +14,7 @@ #include <linux/types.h> #include <linux/sched.h> +#include <linux/timer.h> #include <asm/amigayle.h> #include <asm/amipcmcia.h> @@ -26,7 +27,7 @@ void pcmcia_reset(void) unsigned char b; gayle_reset = 0x00; - while (jiffies - reset_start_time < 1*HZ/100); + while (time_before(jiffies, reset_start_time + 1*HZ/100)); b = gayle_reset; } diff --git a/arch/m68k/apollo/dn_ints.c b/arch/m68k/apollo/dn_ints.c index c4cffcdf9..18e29814e 100644 --- a/arch/m68k/apollo/dn_ints.c +++ b/arch/m68k/apollo/dn_ints.c @@ -30,7 +30,7 @@ void dn_process_int(int irq, struct pt_regs *fp) { dn_irqs[irq-160].handler(irq,dn_irqs[irq-160].dev_id,fp); } else { - printk("spurious irq %d occured\n",irq); + printk("spurious irq %d occurred\n",irq); } #if 0 diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index e6535f6ff..79ef5f9fd 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -825,7 +825,7 @@ __initfunc(int atari_keyb_init(void)) /* wait for a period of inactivity (here: 0.25s), then assume the IKBD's * self-test is finished */ self_test_last_rcv = jiffies; - while( jiffies < self_test_last_rcv + HZ/4 ) + while (time_before(jiffies, self_test_last_rcv + HZ/4)) barrier(); /* if not incremented: no 0xf1 received */ if (ikbd_self_test == 1) @@ -861,8 +861,3 @@ int atari_kbdrate( struct kbd_repeat *k ) return( 0 ); } - -/* for "kbd-reset" cmdline param */ -__initfunc(void atari_kbd_reset_setup(char *str, int *ints)) -{ -} diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c index 8db027ab4..5be79acaf 100644 --- a/arch/m68k/atari/config.c +++ b/arch/m68k/atari/config.c @@ -60,7 +60,6 @@ static int atari_get_hardware_list(char *buffer); extern int atari_keyb_init(void); extern int atari_kbdrate (struct kbd_repeat *); extern void atari_kbd_leds (unsigned int); -extern void atari_kbd_reset_setup(char*, int); /* atari specific irq functions */ extern void atari_init_IRQ (void); extern int atari_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *), @@ -251,7 +250,6 @@ __initfunc(void config_atari(void)) mach_keyb_init = atari_keyb_init; mach_kbdrate = atari_kbdrate; mach_kbd_leds = atari_kbd_leds; - kbd_reset_setup = atari_kbd_reset_setup; mach_init_IRQ = atari_init_IRQ; mach_request_irq = atari_request_irq; mach_free_irq = atari_free_irq; diff --git a/arch/m68k/bvme6000/bvmeints.c b/arch/m68k/bvme6000/bvmeints.c index a79f5555d..04633375f 100644 --- a/arch/m68k/bvme6000/bvmeints.c +++ b/arch/m68k/bvme6000/bvmeints.c @@ -119,9 +119,12 @@ void bvme6000_free_irq(unsigned int irq, void *dev_id) void bvme6000_process_int (unsigned long vec, struct pt_regs *fp) { if (vec > 255) - panic ("bvme6000_process_int: Illegal vector %ld", vec); - irq_tab[vec].count++; - irq_tab[vec].handler(vec, irq_tab[vec].dev_id, fp); + printk ("bvme6000_process_int: Illegal vector %ld", vec); + else + { + irq_tab[vec].count++; + irq_tab[vec].handler(vec, irq_tab[vec].dev_id, fp); + } } int bvme6000_get_irq_list (char *buf) diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c index 74d15e995..543b04c74 100644 --- a/arch/m68k/bvme6000/config.c +++ b/arch/m68k/bvme6000/config.c @@ -91,8 +91,7 @@ void bvme6000_reset() static void bvme6000_get_model(char *model) { - /* XXX Need to detect if BVME4000 or BVME6000 */ - sprintf(model, "BVME6000"); + sprintf(model, "BVME%d000", m68k_cputype == CPU_68060 ? 6 : 4); } @@ -152,13 +151,17 @@ __initfunc(void config_bvme6000(void)) pit->pbddr = 0xf3; /* Mostly outputs */ pit->pcdr = 0x01; /* PA transceiver disabled */ pit->pcddr = 0x03; /* WDOG disable */ + + /* Disable snooping for Ethernet and VME accesses */ + + bvme_acr_addrctl = 0; } void bvme6000_abort_int (int irq, void *dev_id, struct pt_regs *fp) { unsigned long *new = (unsigned long *)vectors; - unsigned long *old = (unsigned long *)0xf8000000;; + unsigned long *old = (unsigned long *)0xf8000000; /* Wait for button release */ while (*config_reg_ptr & BVME_ABORT_STATUS) diff --git a/arch/m68k/config.in b/arch/m68k/config.in index 8fe84aecd..a67227d60 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -44,6 +44,7 @@ bool 'HP9000/300 support' CONFIG_HP300 if [ "$CONFIG_HP300" = "y" ]; then bool 'DIO bus support' CONFIG_DIO fi +define_bool CONFIG_SUN3 n if [ "$CONFIG_PCI" = "y" ]; then bool 'Backward-compatible /proc/pci' CONFIG_PCI_OLD_PROC fi @@ -53,22 +54,11 @@ bool '68020 support' CONFIG_M68020 bool '68030 support' CONFIG_M68030 bool '68040 support' CONFIG_M68040 bool '68060 support' CONFIG_M68060 -if [ "$CONFIG_M68020" = "n" -a "$CONFIG_M68030" = "n" ]; then - if [ "$CONFIG_M68040" = "y" -a "$CONFIG_M68060" = "n" ]; then - bool 'Use 68040 specific optimizations' CONFIG_OPTIMIZE_040 - fi - if [ "$CONFIG_M68040" = "n" -a "$CONFIG_M68060" = "y" ]; then - bool 'Use 68060 specific optimizations' CONFIG_OPTIMIZE_060 - fi -fi -if [ "$CONFIG_VME" = "y" -a "$CONFIG_M68060" = "y" ]; then - define_bool CONFIG_060_WRITETHROUGH y -fi bool 'Advanced configuration options' CONFIG_ADVANCED if [ "$CONFIG_ADVANCED" = "y" ]; then bool 'Use read-modify-write instructions' CONFIG_RMW_INSNS bool 'Use one physical chunk of memory only' CONFIG_SINGLE_MEMORY_CHUNK - if [ "$CONFIG_M68060" = "y" -a "$CONFIG_VME" = "n" ]; then + if [ "$CONFIG_M68060" = "y" ]; then bool 'Use write-through caching for 68060 supervisor accesses' CONFIG_060_WRITETHROUGH fi fi @@ -140,19 +130,19 @@ mainmenu_option next_comment comment 'SCSI low-level drivers' if [ "$CONFIG_AMIGA" = "y" ]; then - tristate 'A3000 WD33C93A support' CONFIG_A3000_SCSI + dep_tristate 'A3000 WD33C93A support' CONFIG_A3000_SCSI $CONFIG_SCSI if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'A4000T SCSI support' CONFIG_A4000T_SCSI fi fi if [ "$CONFIG_ZORRO" = "y" ]; then - tristate 'A2091 WD33C93A support' CONFIG_A2091_SCSI - tristate 'GVP Series II WD33C93A support' CONFIG_GVP11_SCSI - bool 'CyberStorm SCSI support' CONFIG_CYBERSTORM_SCSI - bool 'CyberStorm Mk II SCSI support' CONFIG_CYBERSTORMII_SCSI - bool 'Blizzard 2060 SCSI support' CONFIG_BLZ2060_SCSI - bool 'Blizzard 1230IV/1260 SCSI support' CONFIG_BLZ1230_SCSI - bool 'Fastlane SCSI support' CONFIG_FASTLANE_SCSI + dep_tristate 'A2091 WD33C93A support' CONFIG_A2091_SCSI $CONFIG_SCSI + dep_tristate 'GVP Series II WD33C93A support' CONFIG_GVP11_SCSI $CONFIG_SCSI + dep_tristate 'CyberStorm SCSI support' CONFIG_CYBERSTORM_SCSI $CONFIG_SCSI + dep_tristate 'CyberStorm Mk II SCSI support' CONFIG_CYBERSTORMII_SCSI $CONFIG_SCSI + dep_tristate 'Blizzard 2060 SCSI support' CONFIG_BLZ2060_SCSI $CONFIG_SCSI + dep_tristate 'Blizzard 1230IV/1260 SCSI support' CONFIG_BLZ1230_SCSI $CONFIG_SCSI + dep_tristate 'Fastlane SCSI support' CONFIG_FASTLANE_SCSI $CONFIG_SCSI if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'A4091 SCSI support' CONFIG_A4091_SCSI bool 'WarpEngine SCSI support' CONFIG_WARPENGINE_SCSI @@ -173,7 +163,7 @@ if [ "$CONFIG_ATARI" = "y" ]; then fi if [ "$CONFIG_MAC" = "y" ]; then bool 'MAC NCR5380 SCSI' CONFIG_MAC_SCSI - bool 'MAC NCR53c9[46] SCSI' CONFIG_SCSI_MAC_ESP + dep_tristate 'MAC NCR53c9[46] SCSI' CONFIG_SCSI_MAC_ESP $CONFIG_SCSI fi #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI @@ -226,14 +216,14 @@ if [ "$CONFIG_APOLLO" = "y" ] ; then fi if [ "$CONFIG_MAC" = "y" ]; then bool 'Mac NS 8390 based ethernet cards' CONFIG_DAYNAPORT - bool 'AV Macintosh onboard MACE ethernet' CONFIG_MACMACE - bool 'Macintosh onboard SONIC ethernet' CONFIG_MACSONIC +# bool 'Macintosh (AV) onboard MACE ethernet' CONFIG_MACMACE + bool 'Macintosh (Quadra) onboard SONIC ethernet' CONFIG_MACSONIC fi if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME16x" = "y" ]; then - bool 'MVME16x Ethernet support' CONFIG_MVME16x_NET + tristate 'MVME16x Ethernet support' CONFIG_MVME16x_NET fi if [ "$CONFIG_VME" = "y" -a "$CONFIG_BVME6000" = "y" ]; then - bool 'BVME6000 Ethernet support' CONFIG_BVME6000_NET + tristate 'BVME6000 Ethernet support' CONFIG_BVME6000_NET fi if [ "$CONFIG_ATARI" = "y" ]; then tristate 'Atari Lance support' CONFIG_ATARILANCE @@ -367,8 +357,6 @@ if [ "$CONFIG_VME" = "n" ]; then endmenu fi -source fs/nls/Config.in - mainmenu_option next_comment comment 'Kernel hacking' diff --git a/arch/m68k/defconfig b/arch/m68k/defconfig index 5a9f39e52..ab9152f5d 100644 --- a/arch/m68k/defconfig +++ b/arch/m68k/defconfig @@ -28,8 +28,6 @@ CONFIG_M68020=y CONFIG_M68030=y CONFIG_M68040=y # CONFIG_M68060 is not set -# CONFIG_OPTIMIZE_040 is not set -# CONFIG_OPTIMIZE_060 is not set # CONFIG_ADVANCED is not set # CONFIG_SINGLE_MEMORY_CHUNK is not set # CONFIG_RMW_INSNS is not set @@ -77,21 +75,26 @@ CONFIG_BLK_DEV_INITRD=y # # Networking options # +CONFIG_PACKET=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set # CONFIG_NET_ALIAS is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set -# CONFIG_IP_ACCT is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set +# CONFIG_SYN_COOKIES is not set # # (it is safe to leave these untouched) # -# CONFIG_INET_PCTCP is not set # CONFIG_INET_RARP is not set -CONFIG_PATH_MTU_DISCOVERY=y CONFIG_IP_NOSR=y # CONFIG_SKB_LARGE is not set # CONFIG_IPV6 is not set @@ -101,8 +104,20 @@ CONFIG_IP_NOSR=y # # CONFIG_IPX is not set # CONFIG_ATALK is not set -# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set # CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set +# CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set # # SCSI support @@ -115,12 +130,15 @@ CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set # CONFIG_CHR_DEV_SG is not set # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # # CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers @@ -149,6 +167,7 @@ CONFIG_NETDEVICES=y # CONFIG_SLIP is not set # CONFIG_PPP is not set # CONFIG_ARIADNE is not set +# CONFIG_ARIADNE2 is not set # CONFIG_A2065 is not set # CONFIG_HYDRA is not set # CONFIG_APNE is not set @@ -186,7 +205,10 @@ CONFIG_FB_AMIGA_OCS=y CONFIG_FB_AMIGA_ECS=y CONFIG_FB_AMIGA_AGA=y # CONFIG_FB_CYBER is not set +# CONFIG_FB_VIRGE is not set +# CONFIG_FB_CVPPC is not set # CONFIG_FB_RETINAZ3 is not set +# CONFIG_FB_CLGEN is not set # CONFIG_FB_ATARI is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set diff --git a/arch/m68k/fpsp040/skeleton.S b/arch/m68k/fpsp040/skeleton.S index 1e4d0cf72..06d22b7b7 100644 --- a/arch/m68k/fpsp040/skeleton.S +++ b/arch/m68k/fpsp040/skeleton.S @@ -40,6 +40,7 @@ #include <linux/linkage.h> #include <asm/entry.h> +#include "../kernel/m68k_defs.h" |SKELETON idnt 2,1 | Motorola 040 Floating Point Software Package @@ -375,12 +376,12 @@ fpsp_fmt_error: .global fpsp_done fpsp_done: btst #0x5,%sp@ | supervisor bit set in saved SR? - beq Lnotkern + beq .Lnotkern rte -Lnotkern: +.Lnotkern: SAVE_ALL_INT GET_CURRENT(%d0) - tstl %curptr@(LTASK_NEEDRESCHED) + tstl %curptr@(TASK_NEEDRESCHED) jne SYMBOL_NAME(ret_from_exception) | deliver signals, | reschedule etc.. RESTORE_ALL diff --git a/arch/m68k/hp300/config.c b/arch/m68k/hp300/config.c index 97fb6e480..6808de799 100644 --- a/arch/m68k/hp300/config.c +++ b/arch/m68k/hp300/config.c @@ -55,11 +55,6 @@ static void hp300_kbd_leds(unsigned int leds) { } -/* for "kbd-reset" cmdline param */ -__initfunc(void hp300_kbd_reset_setup(char *str, int i)) -{ -} - static void hp300_get_model(char *model) { strcpy(model, "HP9000/300"); @@ -74,7 +69,6 @@ __initfunc(void config_hp300(void)) mach_init_IRQ = hp300_init_IRQ; mach_request_irq = hp300_request_irq; mach_free_irq = hp300_free_irq; - kbd_reset_setup = hp300_kbd_reset_setup; mach_get_model = hp300_get_model; mach_get_irq_list = hp300_get_irq_list; mach_gettimeoffset = hp300_gettimeoffset; diff --git a/arch/m68k/ifpsp060/iskeleton.S b/arch/m68k/ifpsp060/iskeleton.S index cde14e120..859ba3dd3 100644 --- a/arch/m68k/ifpsp060/iskeleton.S +++ b/arch/m68k/ifpsp060/iskeleton.S @@ -36,6 +36,7 @@ #include <linux/linkage.h> #include <asm/entry.h> +#include "../kernel/m68k_defs.h" |################################ @@ -69,12 +70,12 @@ .global _060_isp_done _060_isp_done: btst #0x5,%sp@ | supervisor bit set in saved SR? - beq Lnotkern + beq .Lnotkern rte -Lnotkern: +.Lnotkern: SAVE_ALL_INT GET_CURRENT(%d0) - tstl %curptr@(LTASK_NEEDRESCHED) + tstl %curptr@(TASK_NEEDRESCHED) jne SYMBOL_NAME(ret_from_exception) | deliver signals, | reschedule etc.. RESTORE_ALL diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile index 977120c1e..280a5b450 100644 --- a/arch/m68k/kernel/Makefile +++ b/arch/m68k/kernel/Makefile @@ -26,10 +26,13 @@ endif head.o: head.S m68k_defs.h -m68k_defs.h: m68k_defs.c m68k_defs.head $(TOPDIR)/include/linux/sched.h - $(CC) ${CFLAGS} -S m68k_defs.c +m68k_defs.h: m68k_defs.c m68k_defs.head + rm -f m68k_defs.d + SUNPRO_DEPENDENCIES="m68k_defs.d m68k_defs.h" \ + $(CC) $(filter-out -MD,$(CFLAGS)) -S m68k_defs.c cp m68k_defs.head m68k_defs.h - sed -n < m68k_defs.s >> m68k_defs.h '/^#define/s/ #/ /p' + grep '^#define' m68k_defs.s >> m68k_defs.h rm m68k_defs.s +-include m68k_defs.d include $(TOPDIR)/Rules.make diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index b3e15387c..e21e4b21c 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -34,8 +34,10 @@ #include <linux/config.h> #include <linux/linkage.h> #include <asm/entry.h> +#include <asm/errno.h> #include <asm/setup.h> #include <asm/segment.h> +#include <asm/traps.h> #include "m68k_defs.h" @@ -43,7 +45,7 @@ .globl SYMBOL_NAME(resume), SYMBOL_NAME(ret_from_exception) .globl SYMBOL_NAME(ret_from_signal) .globl SYMBOL_NAME(inthandler), SYMBOL_NAME(sys_call_table) -.globl SYMBOL_NAME(sys_fork), SYMBOL_NAME(sys_clone) +.globl SYMBOL_NAME(sys_fork), SYMBOL_NAME(sys_clone), SYMBOL_NAME(sys_vfork) .globl SYMBOL_NAME(ret_from_interrupt), SYMBOL_NAME(bad_interrupt) .text @@ -65,24 +67,24 @@ ENTRY(trap) ENTRY(reschedule) | save top of frame - movel %sp,%curptr@(TS_ESP0) + movel %sp,%curptr@(TASK_TSS+TSS_ESP0) pea SYMBOL_NAME(ret_from_exception) jmp SYMBOL_NAME(schedule) badsys: - movel #-LENOSYS,LPT_OFF_D0(%sp) + movel #-ENOSYS,PT_D0(%sp) jra SYMBOL_NAME(ret_from_exception) do_trace: - movel #-LENOSYS,LPT_OFF_D0(%sp) | needed for strace + movel #-ENOSYS,PT_D0(%sp) | needed for strace subql #4,%sp SAVE_SWITCH_STACK jbsr SYMBOL_NAME(syscall_trace) RESTORE_SWITCH_STACK addql #4,%sp jbsr @(SYMBOL_NAME(sys_call_table),%d2:l:4)@(0) - movel %d0,%sp@(LPT_OFF_D0) | save the return value + movel %d0,%sp@(PT_D0) | save the return value subql #4,%sp | dummy return address SAVE_SWITCH_STACK jbsr SYMBOL_NAME(syscall_trace) @@ -98,34 +100,34 @@ ENTRY(system_call) GET_CURRENT(%d0) | save top of frame - movel %sp,%curptr@(TS_ESP0) + movel %sp,%curptr@(TASK_TSS+TSS_ESP0) cmpl #NR_syscalls,%d2 jcc badsys - btst #LPF_TRACESYS_BIT,%curptr@(LTASK_FLAGS+LPF_TRACESYS_OFF) + btst #PF_TRACESYS_BIT,%curptr@(TASK_FLAGS+PF_TRACESYS_OFF) jne do_trace jbsr @(SYMBOL_NAME(sys_call_table),%d2:l:4)@(0) - movel %d0,%sp@(LPT_OFF_D0) | save the return value + movel %d0,%sp@(PT_D0) | save the return value SYMBOL_NAME_LABEL(ret_from_exception) - btst #5,%sp@(LPT_OFF_SR) | check if returning to kernel + btst #5,%sp@(PT_SR) | check if returning to kernel bnes 2f | if so, skip resched, signals | only allow interrupts when we are really the last one on the | kernel stack, otherwise stack overflow can occur during | heavy interupt load andw #ALLOWINT,%sr - tstl %curptr@(LTASK_NEEDRESCHED) + tstl %curptr@(TASK_NEEDRESCHED) jne SYMBOL_NAME(reschedule) cmpl #SYMBOL_NAME(task),%curptr | task[0] cannot have signals jeq 2f | check for delayed trace - bclr #LPF_DTRACE_BIT,%curptr@(LTASK_FLAGS+LPF_DTRACE_OFF) + bclr #PF_DTRACE_BIT,%curptr@(TASK_FLAGS+PF_DTRACE_OFF) jne do_delayed_trace 5: - tstl %curptr@(LTASK_STATE) | state + tstl %curptr@(TASK_STATE) | state jne SYMBOL_NAME(reschedule) - tstl %curptr@(LTASK_SIGPENDING) + tstl %curptr@(TASK_SIGPENDING) jne Lsignal_return 2: RESTORE_ALL @@ -141,7 +143,7 @@ Lsignal_return: RESTORE_ALL do_delayed_trace: - bclr #7,%sp@(LPT_OFF_SR) | clear trace bit in SR + bclr #7,%sp@(PT_SR) | clear trace bit in SR pea 1 | send SIGTRAP movel %curptr,%sp@- pea LSIGTRAP @@ -158,7 +160,7 @@ SYMBOL_NAME_LABEL(inthandler) GET_CURRENT(%d0) addql #1,SYMBOL_NAME(local_irq_count) | put exception # in d0 - bfextu %sp@(LPT_OFF_FORMATVEC){#4,#10},%d0 + bfextu %sp@(PT_VECTOR){#4,#10},%d0 movel %sp,%sp@- movel %d0,%sp@- | put vector # on stack @@ -172,7 +174,7 @@ SYMBOL_NAME_LABEL(ret_from_interrupt) RESTORE_ALL 1: #if 1 - bfextu %sp@(LPT_OFF_SR){#5,#3},%d0 | Check for nested interrupt. + bfextu %sp@(PT_SR){#5,#3},%d0 | Check for nested interrupt. #if MAX_NOINT_IPL > 0 cmpiw #MAX_NOINT_IPL,%d0 #endif @@ -210,6 +212,14 @@ ENTRY(sys_clone) RESTORE_SWITCH_STACK rts +ENTRY(sys_vfork) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr SYMBOL_NAME(m68k_vfork) + addql #4,%sp + RESTORE_SWITCH_STACK + rts + ENTRY(sys_sigsuspend) SAVE_SWITCH_STACK pea %sp@(SWITCH_STACK_SIZE) @@ -240,37 +250,31 @@ ENTRY(sys_rt_sigreturn) SYMBOL_NAME_LABEL(resume) /* - * Beware - when entering resume, offset of tss is in d1, - * prev (the current task) is in a0, next (the new task) - * is in a1 and d2.b is non-zero if the mm structure is - * shared between the tasks, so don't change these + * Beware - when entering resume, prev (the current task) is + * in a0, next (the new task) is in a1,so don't change these * registers until their contents are no longer needed. */ - /* offset of tss struct (processor state) from beginning - of task struct */ - addl %d1,%a0 - /* save sr */ - movew %sr,%a0@(LTSS_SR) + movew %sr,%a0@(TASK_TSS+TSS_SR) /* save fs (sfc,%dfc) (may be pointing to kernel memory) */ movec %sfc,%d0 - movew %d0,%a0@(LTSS_FS) + movew %d0,%a0@(TASK_TSS+TSS_FS) /* save usp */ /* it is better to use a movel here instead of a movew 8*) */ movec %usp,%d0 - movel %d0,%a0@(LTSS_USP) + movel %d0,%a0@(TASK_TSS+TSS_USP) /* save non-scratch registers on stack */ SAVE_SWITCH_STACK /* save current kernel stack pointer */ - movel %sp,%a0@(LTSS_KSP) + movel %sp,%a0@(TASK_TSS+TSS_KSP) /* save floating point context */ - fsave %a0@(LTSS_FPCTXT+27*4) + fsave %a0@(TASK_TSS+TSS_FPSTATE) #if defined(CONFIG_M68060) #if !defined(CPU_M68060_ONLY) @@ -278,27 +282,27 @@ SYMBOL_NAME_LABEL(resume) beqs 1f #endif /* The 060 FPU keeps status in bits 15-8 of the first longword */ - tstb %a0@(LTSS_FPCTXT+27*4+2) + tstb %a0@(TASK_TSS+TSS_FPSTATE+2) jeq 3f #if !defined(CPU_M68060_ONLY) jra 2f #endif #endif /* CONFIG_M68060 */ #if !defined(CPU_M68060_ONLY) -1: tstb %a0@(LTSS_FPCTXT+27*4) +1: tstb %a0@(TASK_TSS+TSS_FPSTATE) jeq 3f #endif -2: fmovemx %fp0-%fp7,%a0@(LTSS_FPCTXT) - fmoveml %fpcr/%fpsr/%fpiar,%a0@(LTSS_FPCTXT+24*4) +2: fmovemx %fp0-%fp7,%a0@(TASK_TSS+TSS_FPREG) + fmoveml %fpcr/%fpsr/%fpiar,%a0@(TASK_TSS+TSS_FPCNTL) 3: - /* get pointer to tss struct (a1 contains new task) */ + /* switch to new task (a1 contains new task) */ movel %a1,%curptr - addl %d1,%a1 /* Skip address space switching if they are the same. */ - tstb %d2 - jne 4f + movel %a0@(TASK_MM),%d0 + cmpl %a1@(TASK_MM),%d0 + jeq 4f #if defined(CPU_M68020_OR_M68030) && defined(CPU_M68040_OR_M68060) /* 68040 or 68060 ? */ @@ -316,7 +320,7 @@ SYMBOL_NAME_LABEL(resume) movec %d0,%cacr /* switch the root pointer */ - pmove %a1@(LTSS_CRP),%crp + pmove %a1@(TASK_TSS+TSS_CRP),%crp #endif #if defined(CPU_M68020_OR_M68030) && defined(CPU_M68040_OR_M68060) @@ -333,7 +337,7 @@ SYMBOL_NAME_LABEL(resume) pflushan /* switch the root pointer */ - movel %a1@(LTSS_CRP+4),%d0 + movel %a1@(TASK_TSS+TSS_CRP+4),%d0 movec %d0,%urp #if defined (CONFIG_M68060) @@ -359,37 +363,37 @@ SYMBOL_NAME_LABEL(resume) beqs 1f #endif /* The 060 FPU keeps status in bits 15-8 of the first longword */ - tstb %a1@(LTSS_FPCTXT+27*4+2) + tstb %a1@(TASK_TSS+TSS_FPSTATE+2) jeq 3f #if !defined(CPU_M68060_ONLY) jra 2f #endif #endif /* CONFIG_M68060 */ #if !defined(CPU_M68060_ONLY) -1: tstb %a1@(LTSS_FPCTXT+27*4) +1: tstb %a1@(TASK_TSS+TSS_FPSTATE) jeq 3f #endif -2: fmovemx %a1@(LTSS_FPCTXT),%fp0-%fp7 - fmoveml %a1@(LTSS_FPCTXT+24*4),%fpcr/%fpsr/%fpiar -3: frestore %a1@(LTSS_FPCTXT+27*4) +2: fmovemx %a1@(TASK_TSS+TSS_FPREG),%fp0-%fp7 + fmoveml %a1@(TASK_TSS+TSS_FPCNTL),%fpcr/%fpsr/%fpiar +3: frestore %a1@(TASK_TSS+TSS_FPSTATE) /* restore the kernel stack pointer */ - movel %a1@(LTSS_KSP),%sp + movel %a1@(TASK_TSS+TSS_KSP),%sp /* restore non-scratch registers */ RESTORE_SWITCH_STACK /* restore user stack pointer */ - movel %a1@(LTSS_USP),%a0 + movel %a1@(TASK_TSS+TSS_USP),%a0 movel %a0,%usp /* restore fs (sfc,%dfc) */ - movew %a1@(LTSS_FS),%a0 + movew %a1@(TASK_TSS+TSS_FS),%a0 movec %a0,%sfc movec %a0,%dfc /* restore status register */ - movew %a1@(LTSS_SR),%sr + movew %a1@(TASK_TSS+TSS_SR),%sr rts @@ -586,6 +590,7 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_sendfile) .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ + .long SYMBOL_NAME(sys_vfork) /* 190 */ .rept NR_syscalls-(.-SYMBOL_NAME(sys_call_table))/4 .long SYMBOL_NAME(sys_ni_syscall) diff --git a/arch/m68k/kernel/head.S b/arch/m68k/kernel/head.S index e98f39fa8..482c15f50 100644 --- a/arch/m68k/kernel/head.S +++ b/arch/m68k/kernel/head.S @@ -7,9 +7,12 @@ ** ** 68040 fixes by Michael Rausch ** 68060 fixes by Roman Hodek +** MMU cleanup by Randy Thelen +** Final MMU cleanup by Roman Zippel ** ** Atari support by Andreas Schwab, using ideas of Robert de Vries ** and Bjoern Brauel +** VME Support by Richard Hirst ** ** 94/11/14 Andreas Schwab: put kernel at PAGESIZE ** 94/11/18 Andreas Schwab: remove identity mapping of STRAM for Atari @@ -18,6 +21,8 @@ ** 96/04/26 Guenther Kelleter: fixed identity mapping for Falcon with ** Magnum- and FX-alternate ram ** 98/04/25 Phil Blundell: added HP300 support +** 1998/08/30 David Kilzer: Added support for fbcon_font_desc structures +** for linux-2.1.115 ** ** This file is subject to the terms and conditions of the GNU General Public ** License. See the file README.legal in the main directory of this archive @@ -34,69 +39,275 @@ * Put us in supervisor state. * * The kernel setup code takes the following steps: - * Raise interrupt level - * Set up initial kernel memory mapping. - * This sets up a mapping of the 4M of memory the kernel - * is located in. It also does a mapping of any initial - * machine specific areas. - * Note that the kernel is located at virtual address 0x1000 == _start - * Enable cache memories - * Jump to kernel startup - * - * Register d6 contains the CPU flags and d4 the machine type - * from the boot_info information for most of this file. - * The upper word of d6 contains a bit for '040 or '060, since these two - * are quite similar for initial mm setup. Another bit in d6 allows - * distinction of the '060. The lower word of d6 contains the cache mode - * that should be applied to pages containing descriptors. This mode is - * non-cached/non-serialized for the '040 and cacheable/write-through for - * the '060. - * - * General register usage: - * a6 - start of unused memory - * new pages can be allocated from here - * a5 - mmu root table - * a4 - mmu pointer table - * a3 - mmu page tables - * a2 - points to the page table entry for a6 - * cache status can be changed (used for '0[46]0) - * you must increase a2 if alloc a new page - * d7 - used for debug output and some macros - * d6 - cpu type and cache mode - * d5 - physical start address of kernel - * d4 - machine type + * . Raise interrupt level + * . Set up initial kernel memory mapping. + * . This sets up a mapping of the 4M of memory the kernel is located in. + * . It also does a mapping of any initial machine specific areas. + * . Enable the MMU + * . Enable cache memories + * . Jump to kernel startup + * + * Much of the file restructuring was to accomplish: + * 1) Remove register dependency through-out the file. + * 2) Increase use of subroutines to perform functions + * 3) Increase readability of the code + * + * Of course, readability is a subjective issue, so it will never be + * argued that that goal was accomplished. It was merely a goal. + * A key way to help make code more readable is to give good + * documentation. So, the first thing you will find is exaustive + * write-ups on the structure of the file, and the features of the + * functional subroutines. + * + * General Structure: + * ------------------ + * Without a doubt the single largest chunk of head.S is spent + * mapping the kernel and I/O physical space into the logical range + * for the kernel. + * There are new subroutines and data structures to make MMU + * support cleaner and easier to understand. + * First, you will find a routine call "mmu_map" which maps + * a logical to a physical region for some length given a cache + * type on behalf of the caller. This routine makes writing the + * actual per-machine specific code very simple. + * A central part of the code, but not a subroutine in itself, + * is the mmu_init code which is broken down into mapping the kernel + * (the same for all machines) and mapping machine-specific I/O + * regions. + * Also, there will be a description of engaging the MMU and + * caches. + * You will notice that there is a chunk of code which + * can emit the entire MMU mapping of the machine. This is present + * only in debug modes and can be very helpful. + * Further, there is a new console driver in head.S that is + * also only engaged in debug mode. Currently, it's only supported + * on the Macintosh class of machines. However, it is hoped that + * others will plug-in support for specific machines. + * + * ###################################################################### + * + * mmu_map + * ------- + * mmu_map was written for two key reasons. First, it was clear + * that it was very difficult to read the previous code for mapping + * regions of memory. Second, the Macintosh required such extensive + * memory allocations that it didn't make sense to propogate the + * existing code any further. + * mmu_map requires some parameters: + * + * mmu_map (logical, physical, length, cache_type) + * + * While this essentially describes the function in the abstract, you'll + * find more indepth description of other parameters at the implementation site. + * + * mmu_get_root_table_entry + * ------------------------ + * mmu_get_ptr_table_entry + * ----------------------- + * mmu_get_page_table_entry + * ------------------------ + * + * These routines are used by other mmu routines to get a pointer into + * a table, if necessary a new table is allocated. These routines are working + * basically like pmd_alloc() and pte_alloc() in <asm/pgtable.h>. The root + * table needs of course only to be allocated once in mmu_get_root_table_entry, + * so that here also some mmu specific initialization is done. The second page + * at the start of the kernel (the first page is unmapped later) is used for + * the kernel_pg_dir. It must be at a position known at link time (as it's used + * to initialize the init task struct) and since it needs special cache + * settings, it's the easiest to use this page, the rest of the page is used + * for further pointer tables. + * mmu_get_page_table_entry allocates always a whole page for page tables, this + * means 1024 pages and so 4MB of memory can be mapped. It doesn't make sense + * to manage page tables in smaller pieces as nearly all mappings have that + * size. + * + * ###################################################################### + * + * + * ###################################################################### + * + * mmu_engage + * ---------- + * Thanks to a small helping routine enabling the mmu got quiet simple + * and there is only one way left. mmu_engage makes a complete a new mapping + * that only includes the absolute necessary to be able to jump to the final + * postion and to restore the original mapping. + * As this code doesn't need a transparent translation register anymore this + * means all registers are free to be used by machines that needs them for + * other purposes. + * + * ###################################################################### + * + * mmu_print + * --------- + * This algorithm will print out the page tables of the system as + * appropriate for an 030 or an 040. This is useful for debugging purposes + * and as such is enclosed in #ifdef MMU_PRINT/#endif clauses. + * + * ###################################################################### + * + * console_init + * ------------ + * The console is also able to be turned off. The console in head.S + * is specifically for debugging and can be very useful. It is surrounded by + * #ifdef CONSOLE/#endif clauses so it doesn't have to ship in known-good + * kernels. It's basic algorithm is to determine the size of the screen + * (in height/width and bit depth) and then use that information for + * displaying an 8x8 font or an 8x16 (widthxheight). I prefer the 8x8 for + * debugging so I can see more good data. But it was trivial to add support + * for both fonts, so I included it. + * Also, the algorithm for plotting pixels is abstracted so that in + * theory other platforms could add support for different kinds of frame + * buffers. This could be very useful. + * + * console_put_penguin + * ------------------- + * An important part of any Linux bring up is the penguin and there's + * nothing like getting the Penguin on the screen! This algorithm will work + * on any machine for which there is a console_plot_pixel. + * + * console_scroll + * -------------- + * My hope is that the scroll algorithm does the right thing on the + * various platforms, but it wouldn't be hard to add the test conditions + * and new code if it doesn't. + * + * console_putc + * ------------- + * + * ###################################################################### + * + * Register usage has greatly simplified within head.S. Every subroutine + * saves and restores all registers that it modifies (except it returns a + * value in there of course). So the only register that needs to be initialized + * is the stack pointer. + * All other init code and data is now placed in the init section, so it will + * be automatically freed at the end of the kernel initialization. + * + * ###################################################################### + * + * options + * ------- + * There are many options availble in a build of this file. I've + * taken the time to describe them here to save you the time of searching + * for them and trying to understand what they mean. + * + * CONFIG_xxx: These are the obvious machine configuration defines created + * during configuration. These are defined in include/linux/autoconf.h. + * + * CONSOLE: There is support for head.S console in this file. This + * console can talk to a Mac frame buffer, but could easily be extrapolated + * to extend it to support other platforms. + * + * TEST_MMU: This is a test harness for running on any given machine but + * getting an MMU dump for another class of machine. The classes of machines + * that can be tested are any of the makes (Atari, Amiga, Mac, VME, etc.) + * and any of the models (030, 040, 060, etc.). + * + * NOTE: TEST_MMU is NOT permanent! It is scheduled to be removed + * When head.S boots on Atari, Amiga, Macintosh, and VME + * machines. At that point the underlying logic will be + * believed to be solid enough to be trusted, and TEST_MMU + * can be dropped. Do note that that will clean up the + * head.S code significantly as large blocks of #if/#else + * clauses can be removed. + * + * MMU_NOCACHE_KERNEL: On the Macintosh platform there was an inquiry into + * determing why devices don't appear to work. A test case was to remove + * the cacheability of the kernel bits. + * + * MMU_PRINT: There is a routine built into head.S that can display the + * MMU data structures. It outputs its result through the serial_putc + * interface. So where ever that winds up driving data, that's where the + * mmu struct will appear. On the Macintosh that's typically the console. + * + * SERIAL_DEBUG: There are a series of putc() macro statements + * scattered through out the code to give progress of status to the + * person sitting at the console. This constant determines whether those + * are used. + * + * DEBUG: This is the standard DEBUG flag that can be set for building + * the kernel. It has the effect adding additional tests into + * the code. + * + * FONT_6x11: + * FONT_8x8: + * FONT_8x16: + * In theory these could be determined at run time or handed + * over by the booter. But, let's be real, it's a fine hard + * coded value. (But, you will notice the code is run-time + * flexible!) A pointer to the font's struct fbcon_font_desc + * is kept locally in Lconsole_font. It is used to determine + * font size information dynamically. + * + * Atari constants: + * USE_PRINTER: Use the printer port for serial debug. + * USE_SCC_B: Use the SCC port A (Serial2) for serial debug. + * USE_SCC_A: Use the SCC port B (Modem2) for serial debug. + * USE_MFP: Use the ST-MFP port (Modem1) for serial debug. + * + * Macintosh constants: + * MAC_SERIAL_DEBUG: Turns on serial debug output for the Macintosh. + * MAC_USE_SCC_A: Use the SCC port A (modem) for serial debug. + * MAC_USE_SCC_B: Use the SCC port B (printer) for serial debug (default). */ #include <linux/config.h> #include <linux/linkage.h> +#include <linux/init.h> #include <asm/bootinfo.h> #include <asm/setup.h> #include <asm/pgtable.h> +#include "m68k_defs.h" -.globl SYMBOL_NAME(kernel_pg_dir), SYMBOL_NAME(kpt) -.globl SYMBOL_NAME(availmem) -.globl SYMBOL_NAME(m68k_pgtable_cachemode) -.globl SYMBOL_NAME(kernel_pmd_table), SYMBOL_NAME(swapper_pg_dir) +#ifdef CONFIG_MAC -#if defined(CONFIG_MVME16x) -.globl SYMBOL_NAME(mvme_bdid_ptr) -#endif +#include <asm/machw.h> /* - * Added m68k_supervisor_cachemode for 68060 boards where some drivers - * need writethrough caching for supervisor accesses. Drivers known to - * be effected are 53c7xx.c and apricot.c (when used on VME boards). - * Richard Hirst. + * Macintosh console support */ -#ifdef CONFIG_060_WRITETHROUGH +#define CONSOLE + +/* + * Macintosh serial debug support; outputs boot info to the printer + * and/or modem serial ports + */ +#undef MAC_SERIAL_DEBUG + +/* + * Macintosh serial debug port selection; define one or both; + * requires MAC_SERIAL_DEBUG to be defined + */ +#define MAC_USE_SCC_A /* Macintosh modem serial port */ +#define MAC_USE_SCC_B /* Macintosh printer serial port */ + +#endif /* CONFIG_MAC */ + +#undef MMU_PRINT +#undef MMU_NOCACHE_KERNEL +#define SERIAL_DEBUG +#undef DEBUG + +/* + * For the head.S console, there are three supported fonts, 6x11, 8x16 and 8x8. + * The 8x8 font is harder to read but fits more on the screen. + */ +#define FONT_8x8 /* default */ +/* #define FONT_8x16 */ /* 2nd choice */ +/* #define FONT_6x11 */ /* 3rd choice */ + +.globl SYMBOL_NAME(kernel_pg_dir) +.globl SYMBOL_NAME(availmem) +.globl SYMBOL_NAME(m68k_pgtable_cachemode) .globl SYMBOL_NAME(m68k_supervisor_cachemode) -#endif -D6B_0460 = 16 /* indicates 680[46]0 in d6 */ -D6B_060 = 17 /* indicates 68060 in d6 */ -D6F_040 = 1<<D6B_0460 -D6F_060 = (1<<D6B_0460)+(1<<D6B_060) +CPUTYPE_040 = 1 /* indicates an 040 */ +CPUTYPE_060 = 2 /* indicates an 060 */ +CPUTYPE_0460 = 3 /* if either above are set, this is set */ +CPUTYPE_020 = 4 /* indicates an 020 */ /* Translation control register */ TC_ENABLE = 0x8000 @@ -144,6 +355,7 @@ CC3_ENABLE_I = 0x00000001 /* enable instruction cache (68030) */ /* Miscellaneous definitions */ PAGESIZE = 4096 +PAGESHIFT = 12 ROOT_TABLE_SIZE = 128 PTR_TABLE_SIZE = 128 @@ -152,32 +364,182 @@ ROOT_INDEX_SHIFT = 25 PTR_INDEX_SHIFT = 18 PAGE_INDEX_SHIFT = 12 -TABLENR_4MB = 16 /* # of page tables needed to page 4 MB */ -TABLENR_16MB = 64 /* same for 16 MB */ +#ifdef DEBUG +/* When debugging use readable names for labels */ +#ifdef __STDC__ +#define L(name) .head.S.##name +#else +#define L(name) .head.S./**/name +#endif +#else +#ifdef __STDC__ +#define L(name) .L##name +#else +#define L(name) .L/**/name +#endif +#endif -#define putc(ch) moveq &ch,%d7; jbsr Lserial_putc -#define putr() putc(13); putc(10) -#define putn(nr) movel nr,%d7; jbsr Lserial_putnum +/* Several macros to make the writing of subroutines easier: + * - func_start marks the beginning of the routine which setups the frame + * register and saves the registers, it also defines another macro + * to automatically restore the registers again. + * - func_return marks the end of the routine and simply calls the prepared + * macro to restore registers and jump back to the caller. + * - func_define generates another macro to automatically put arguments + * onto the stack call the subroutine and cleanup the stack again. + */ -#define is_not_amiga(lab) moveq &MACH_AMIGA,%d7; cmpl %d4,%d7; jne lab -#define is_not_atari(lab) moveq &MACH_ATARI,%d7; cmpl %d4,%d7; jne lab -#define is_not_mvme16x(lab) moveq &MACH_MVME16x,%d7; cmpl %d4,%d7; jne lab -#define is_not_bvme6000(lab) moveq &MACH_BVME6000,%d7; cmpl %d4,%d7; jne lab -#define is_not_hp300(lab) moveq &MACH_HP300,%d7 ; cmpl %d4,%d7; jne lab +/* Within subroutines these macros can be used to access the arguments + * on the stack. With STACK some allocated memory on the stack can be + * accessed and ARG0 points to the return address (used by mmu_engage). + */ +#define STACK %a6@(stackstart) +#define ARG0 %a6@(4) +#define ARG1 %a6@(8) +#define ARG2 %a6@(12) +#define ARG3 %a6@(16) +#define ARG4 %a6@(20) + +.macro func_start name,saveregs,stack=0 +L(\name): + linkw %a6,#-\stack + moveml \saveregs,%sp@- +.set stackstart,-\stack + +.macro func_return_\name + moveml %sp@+,\saveregs + unlk %a6 + rts +.endm +.endm + +.macro func_return name + func_return_\name +.endm + +.macro func_call name + jbsr L(\name) +.endm + +.macro move_stack nr,arg1,arg2,arg3,arg4 +.if \nr + move_stack "(\nr-1)",\arg2,\arg3,\arg4 + movel \arg1,%sp@- +.endif +.endm + +.macro func_define name,nr=0 +.macro \name arg1,arg2,arg3,arg4 + move_stack \nr,\arg1,\arg2,\arg3,\arg4 + func_call \name +.if \nr + lea %sp@(\nr*4),%sp +.endif +.endm +.endm + +func_define mmu_map,4 +func_define mmu_map_tt,4 +func_define mmu_fixup_page_mmu_cache,1 +func_define mmu_temp_map,2 +func_define mmu_engage +func_define mmu_get_root_table_entry,1 +func_define mmu_get_ptr_table_entry,2 +func_define mmu_get_page_table_entry,2 +func_define mmu_print +func_define get_new_page +#ifdef CONFIG_HP300 +func_define set_leds +#endif + +.macro mmu_map_eq arg1,arg2,arg3 + mmu_map \arg1,\arg1,\arg2,\arg3 +.endm + +.macro get_bi_record record + pea \record + func_call get_bi_record + addql #4,%sp +.endm + +func_define serial_putc,1 +func_define console_putc,1 + +.macro putc ch +#if defined(CONSOLE) || defined(SERIAL_DEBUG) + pea \ch +#endif +#ifdef CONSOLE + func_call console_putc +#endif +#ifdef SERIAL_DEBUG + func_call serial_putc +#endif +#if defined(CONSOLE) || defined(SERIAL_DEBUG) + addql #4,%sp +#endif +.endm + +.macro dputc ch +#ifdef DEBUG + putc \ch +#endif +.endm + +func_define putn,1 + +.macro dputn nr +#ifdef DEBUG + putn \nr +#endif +.endm + +.macro puts string +#if defined(CONSOLE) || defined(SERIAL_DEBUG) + __INITDATA +.Lstr\@: + .string "\string" + __FINIT + pea %pc@(.Lstr\@) + func_call puts + addql #4,%sp +#endif +.endm + +.macro dputs string +#ifdef DEBUG + puts "\string" +#endif +.endm -#define is_040_or_060(lab) btst &D6B_0460,%d6; jne lab -#define is_not_040_or_060(lab) btst &D6B_0460,%d6; jeq lab -#define is_060(lab) btst &D6B_060,%d6; jne lab -#define is_not_060(lab) btst &D6B_060,%d6; jeq lab + +#define is_not_amiga(lab) cmpl &MACH_AMIGA,%pc@(m68k_machtype); jne lab +#define is_not_atari(lab) cmpl &MACH_ATARI,%pc@(m68k_machtype); jne lab +#define is_not_mac(lab) cmpl &MACH_MAC,%pc@(m68k_machtype); jne lab +#define is_not_mvme16x(lab) cmpl &MACH_MVME16x,%pc@(m68k_machtype); jne lab +#define is_not_bvme6000(lab) cmpl &MACH_BVME6000,%pc@(m68k_machtype); jne lab +#define is_not_hp300(lab) cmpl &MACH_HP300,%pc@(m68k_machtype); jne lab + +#define is_040_or_060(lab) btst &CPUTYPE_0460,%pc@(L(cputype)+3); jne lab +#define is_not_040_or_060(lab) btst &CPUTYPE_0460,%pc@(L(cputype)+3); jeq lab +#define is_040(lab) btst &CPUTYPE_040,%pc@(L(cputype)+3); jne lab +#define is_060(lab) btst &CPUTYPE_060,%pc@(L(cputype)+3); jne lab +#define is_not_060(lab) btst &CPUTYPE_060,%pc@(L(cputype)+3); jeq lab +#define is_020(lab) btst &CPUTYPE_020,%pc@(L(cputype)+3); jne lab +#define is_not_020(lab) btst &CPUTYPE_020,%pc@(L(cputype)+3); jeq lab /* On the HP300 we use the on-board LEDs for debug output before the console is running. Writing a 1 bit turns the corresponding LED _off_ - on the 340 bit 7 is towards the back panel of the machine. */ +.macro leds mask #ifdef CONFIG_HP300 -#define leds(x) is_not_hp300(42f) ; moveb #(x),%d7 ; jbsr Lset_leds; 42: -#else -#define leds(x) + is_not_hp300(.Lled\@) + pea \mask + func_call set_leds + addql #4,%sp +.Lled\@: #endif +.endm .text ENTRY(_stext) @@ -192,81 +554,193 @@ ENTRY(_stext) .long MACH_ATARI, ATARI_BOOTI_VERSION .long MACH_MVME16x, MVME16x_BOOTI_VERSION .long MACH_BVME6000, BVME6000_BOOTI_VERSION + .long MACH_MAC, MAC_BOOTI_VERSION .long 0 -1: jra SYMBOL_NAME(_start) +1: jra SYMBOL_NAME(__start) -.equ SYMBOL_NAME(kernel_pmd_table),SYMBOL_NAME(_stext) -.equ SYMBOL_NAME(kernel_pg_dir),SYMBOL_NAME(kernel_pmd_table) -.equ SYMBOL_NAME(swapper_pg_dir),SYMBOL_NAME(kernel_pg_dir)+(ROOT_TABLE_SIZE<<2) -.equ Lavail_pmd_table,SYMBOL_NAME(swapper_pg_dir)+(ROOT_TABLE_SIZE<<2) +.equ SYMBOL_NAME(kernel_pg_dir),SYMBOL_NAME(_stext) .equ .,SYMBOL_NAME(_stext)+PAGESIZE ENTRY(_start) + jra SYMBOL_NAME(__start) +__INIT +ENTRY(__start) /* * Setup initial stack pointer */ - lea %pc@(SYMBOL_NAME(_stext):w),%sp + lea %pc@(SYMBOL_NAME(_stext)),%sp /* * Record the CPU and machine type. */ - movew #BI_MACHTYPE,%d0 - jbsr Lget_bi_record - movel %a0@,%d4 - lea %pc@(SYMBOL_NAME(m68k_machtype)),%a0 - movel %d4,%a0@ - movew #BI_FPUTYPE,%d0 - jbsr Lget_bi_record - movel %a0@,%d0 - lea %pc@(SYMBOL_NAME(m68k_fputype)),%a0 - movel %d0,%a0@ - movew #BI_MMUTYPE,%d0 - jbsr Lget_bi_record - movel %a0@,%d0 - lea %pc@(SYMBOL_NAME(m68k_mmutype)),%a0 - movel %d0,%a0@ - movew #BI_CPUTYPE,%d0 - jbsr Lget_bi_record + get_bi_record BI_MACHTYPE + lea %pc@(SYMBOL_NAME(m68k_machtype)),%a1 + movel %a0@,%a1@ + + get_bi_record BI_FPUTYPE + lea %pc@(SYMBOL_NAME(m68k_fputype)),%a1 + movel %a0@,%a1@ + + get_bi_record BI_MMUTYPE + lea %pc@(SYMBOL_NAME(m68k_mmutype)),%a1 + movel %a0@,%a1@ + + get_bi_record BI_CPUTYPE + lea %pc@(SYMBOL_NAME(m68k_cputype)),%a1 + movel %a0@,%a1@ + +#ifdef CONFIG_MAC +/* + * For Macintosh, we need to determine the display parameters early (at least + * while debugging it). + */ + + is_not_mac(L(test_notmac)) + + get_bi_record BI_MAC_VADDR + lea %pc@(L(mac_videobase)),%a1 + movel %a0@,%a1@ + + get_bi_record BI_MAC_VDEPTH + lea %pc@(L(mac_videodepth)),%a1 + movel %a0@,%a1@ + + get_bi_record BI_MAC_VDIM + lea %pc@(L(mac_dimensions)),%a1 + movel %a0@,%a1@ + + get_bi_record BI_MAC_VROW + lea %pc@(L(mac_rowbytes)),%a1 + movel %a0@,%a1@ + +#ifdef MAC_SERIAL_DEBUG + get_bi_record BI_MAC_SCCBASE + lea %pc@(L(mac_sccbase)),%a1 + movel %a0@,%a1@ +#endif /* MAC_SERIAL_DEBUG */ + +#if 0 + /* + * Clear the screen + */ + lea %pc@(L(mac_videobase)),%a0 + movel %a0@,%a1 + lea %pc@(L(mac_dimensions)),%a0 + movel %a0@,%d1 + swap %d1 /* #rows is high bytes */ + andl #0xFFFF,%d1 /* rows */ + subl #10,%d1 + lea %pc@(L(mac_rowbytes)),%a0 +loopy2: movel %a0@,%d0 - lea %pc@(SYMBOL_NAME(m68k_cputype)),%a0 - movel %d0,%a0@ + subql #1,%d0 +loopx2: + moveb #0x55, %a1@+ + dbra %d0,loopx2 + dbra %d1,loopy2 +#endif + +L(test_notmac): +#endif /* CONFIG_MAC */ + +/* + * There are ultimately two pieces of information we want for all kinds of + * processors CpuType and CacheBits. The CPUTYPE was passed in from booter + * and is converted here from a booter type definition to a separate bit + * number which allows for the standard is_0x0 macro tests. + */ + movel %pc@(SYMBOL_NAME(m68k_cputype)),%d0 + /* + * Assume it's an 030 + */ + clrl %d1 + + /* + * Test the BootInfo cputype for 060 + */ btst #CPUB_68060,%d0 jeq 1f - /* '060: d6 := BIT0460|BIT060, cache mode 0x60 (no-cache/non-ser) */ - movel #D6F_060+_PAGE_CACHE040W,%d6 - jra 2f -1: btst #CPUB_68040,%d0 - jeq 1f - /* '040: d6 := BIT0460, cache mode 0x00 (write-through) */ - movel #D6F_040+_PAGE_CACHE040W,%d6 - jra 2f -1: /* '020 or '030: d6 := no CPU bit, cache mode unused */ - moveq #0,%d6 + bset #CPUTYPE_060,%d1 + bset #CPUTYPE_0460,%d1 + jra 3f +1: + /* + * Test the BootInfo cputype for 040 + */ + btst #CPUB_68040,%d0 + jeq 2f + bset #CPUTYPE_040,%d1 + bset #CPUTYPE_0460,%d1 + jra 3f +2: + /* + * Test the BootInfo cputype for 020 + */ + btst #CPUB_68020,%d0 + jeq 3f + bset #CPUTYPE_020,%d1 + jra 3f +3: + /* + * Record the cpu type + */ + lea %pc@(L(cputype)),%a0 + movel %d1,%a0@ -2: lea %pc@(SYMBOL_NAME(m68k_pgtable_cachemode)),%a0 - moveq #0,%d0 - movew %d6,%d0 - movel %d0,%a0@ /* save cache mode for page tables */ + /* + * NOTE: + * + * Now the macros are valid: + * is_040_or_060 + * is_not_040_or_060 + * is_040 + * is_060 + * is_not_060 + */ + + /* + * Determine the cache mode for pages holding MMU tables + * and for supervisor mode, unused for '020 and '030 + */ + clrl %d0 + clrl %d1 + is_not_040_or_060(L(save_cachetype)) + + /* + * '040 or '060 + * d1 := cacheable write-through + * NOTE: The 68040 manual strongly recommends non-cached for MMU tables, + * but we have been using write-through since at least 2.0.29 so I + * guess it is OK. + */ +#ifdef CONFIG_060_WRITETHROUGH /* * If this is a 68060 board using drivers with cache coherency * problems, then supervisor memory accesses need to be write-through - * also; otherwise, we want copyback. + * also; otherwise, we want copyback. */ -#if defined(CONFIG_060_WRITETHROUGH) - is_not_060(Lset_norm) - jra 1f -Lset_norm: - move.w #_PAGE_CACHE040,%d0 + is_not_060(1f) + movel #_PAGE_CACHE040W,%d0 + jra L(save_cachetype) +#endif /* CONFIG_060_WRITETHROUGH */ 1: - lea %pc@(SYMBOL_NAME(m68k_supervisor_cachemode)),%a0 + movew #_PAGE_CACHE040,%d0 + + movel #_PAGE_CACHE040W,%d1 + +L(save_cachetype): + /* Save cache mode for supervisor mode and page tables + */ + lea %pc@(SYMBOL_NAME(m68k_supervisor_cachemode)),%a0 movel %d0,%a0@ -#endif + lea %pc@(SYMBOL_NAME(m68k_pgtable_cachemode)),%a0 + movel %d1,%a0@ /* * raise interrupt level @@ -293,288 +767,120 @@ Lset_norm: */ #ifdef CONFIG_ATARI - is_not_atari(Lnotypetest) + is_not_atari(L(notypetest)) /* get special machine type (Medusa/Hades/AB40) */ moveq #0,%d3 /* default if tag doesn't exist */ - movew #BI_ATARI_MCH_TYPE,%d0 - jbsr Lget_bi_record + get_bi_record BI_ATARI_MCH_TYPE tstl %d0 jbmi 1f movel %a0@,%d3 -1: - /* %d3 is not clobbered until Atari page tables are set up, - * where it is used again. */ - + lea %pc@(SYMBOL_NAME(atari_mch_type)),%a0 + movel %d3,%a0@ +1: /* On the Hades, the iobase must be set up before opening the * serial port. There are no I/O regs at 0x00ffxxxx at all. */ moveq #0,%d0 cmpl #ATARI_MACH_HADES,%d3 jbne 1f movel #0xff000000,%d0 /* Hades I/O base addr: 0xff000000 */ -1: lea %pc@(Liobase),%a0 +1: lea %pc@(L(iobase)),%a0 movel %d0,%a0@ -Lnotypetest: + +L(notypetest): #endif /* * Initialize serial port */ - - jbsr Lserial_init - - putr() - putc('A') + jbsr L(serial_init) /* - * Get address at end of bootinfo and mask off at a page boundary. + * Initialize console */ - moveq #0,%d0 - jbsr Lget_bi_record - addw #PAGESIZE-1,%a0 - movel %a0,%d0 - andl #-PAGESIZE,%d0 - movel %d0,%a6 - - putc('B') +#ifdef CONFIG_MAC + is_not_mac(L(nocon)) +#ifdef CONSOLE + jbsr L(console_init) +#ifdef CONSOLE_PENGUIN + jbsr L(console_put_penguin) +#endif /* CONSOLE_PENGUIN */ + jbsr L(console_put_stats) +#endif /* CONSOLE */ +L(nocon): +#endif /* CONFIG_MAC */ + + + putc '\n' + putc 'A' + dputn %pc@(L(cputype)) + dputn %pc@(SYMBOL_NAME(m68k_supervisor_cachemode)) + dputn %pc@(SYMBOL_NAME(m68k_pgtable_cachemode)) + dputc '\n' /* * Save physical start address of kernel */ - lea %pc@(SYMBOL_NAME(_stext)-PAGESIZE:w),%a0 - movel %a0,%d5 - -/* - * initialize the kernel root table. - */ - lea %pc@(SYMBOL_NAME(kernel_pg_dir):w),%a5 - movel %a5,%a0 - moveq #ROOT_TABLE_SIZE-1,%d1 -1: clrl %a0@+ - dbra %d1,1b - - /* - * Initialize root table descriptor pointing to the kernel pointer - * table. - */ - lea %pc@(Lavail_pmd_table:w),%a4 - moveq #_PAGE_TABLE,%d0 - addl %a4,%d0 - movel %d0,%a5@ - - putc('C') - -/* - * Initialize the pointer tables referred to above. They either point - * to page tables in the case of the 680[46]0 or contain early - * termination page descriptors in the case of the 68851 or 68030. - * - * Each pointer table entry points to a 64 entry page table. 16 of these - * page tables are grouped to form a single 1024 entry page table which - * fits in a single 4096 byte page. - * - * Some register usages: - * a0 -> pointer table descriptor address - * a1 -> pointer table descriptor - * d1 -> counter - * d2 -> pointer table descriptor increment (varies according to CPU) - */ - - /* clear the kernel pointer table */ - movel %a4,%a0 - moveq #PTR_TABLE_SIZE-1,%d1 -1: clrl %a0@+ - dbra %d1,1b + lea %pc@(L(phys_kernel_start)),%a0 + lea %pc@(SYMBOL_NAME(_stext)),%a1 + subl #SYMBOL_NAME(_stext),%a1 + movel %a1,%a0@ - movel %a4,%a0 - moveq #15,%d1 + putc 'B' - /* - * base value of pointer table descriptor is either - * the address of the first page table (680[46]0) - * or the base address of physical memory (68030). - */ - is_040_or_060(1f) - - /* 680[23]0 */ - movel %d5,%a1 /* base address */ - addql #_PAGE_PRESENT,%a1 /* descriptor type */ - movel #PAGE_TABLE_SIZE*PAGESIZE,%d2 /* increment */ - jra 2f - -1: /* 680[46]0 */ - movel %a6,%a3 /* base address */ - addw #PAGESIZE,%a6 /* allocate page for 16 page tables */ - lea %pc@(SYMBOL_NAME(kpt)),%a1 - movel %a3,%a1@ /* save address of page table */ - movel %a3,%a1 - addw #_PAGE_TABLE+_PAGE_ACCESSED,%a1 /* descriptor type */ - movel #PAGE_TABLE_SIZE<<2,%d2 /* increment */ - -2: movel %a1,%a0@+ - addl %d2,%a1 - dbra %d1,2b - - putc('D') + leds 0x4 /* - * If we are running on a 680[46]0, we have a kernel page table and - * must initialize it. Make the entries point to the first - * 4M of physical memory (the memory we are residing in). - * Set the cache mode bits to Cacheable, Copyback. Set the Global bits - * in the descriptors also. + * mmu_init + * + * This block of code does what's necessary to map in the various kinds + * of machines for execution of Linux. + * First map the first 4 MB of kernel code & data */ - is_not_040_or_060(Lnot040) - putc('F') - - movel %a3,%a0 - movel %d5,%a1 -#if defined(CONFIG_060_WRITETHROUGH) - addw #_PAGE_GLOBAL040+_PAGE_PRESENT+_PAGE_ACCESSED,%a1 - addl m68k_supervisor_cachemode,%a1 -#else - addw #_PAGE_GLOBAL040+_PAGE_CACHE040+_PAGE_PRESENT+_PAGE_ACCESSED,%a1 -#endif - movew #(PAGE_TABLE_SIZE*TABLENR_4MB)-1,%d1 - movel #PAGESIZE,%d2 -1: movel %a1,%a0@+ - addl %d2,%a1 - dbra %d1,1b - - /* - * on the 68040, pages used to hold mmu tables should - * be initialized as noncachable; the '060 allows write-through. - * Do this for the root table page (which also contains - * all pointer tables utilized thus far) and the - * kernel page table. - */ - movel %a5,%d0 - subl %d5,%d0 - moveq #PAGE_INDEX_SHIFT,%d2 - lsrl %d2,%d0 - lea %a3@(%d0:l:4),%a2 - movel %a2@,%d1 - andw #_CACHEMASK040,%d1 - orw %d6,%d1 - movel %d1,%a2@ + mmu_map #0,%pc@(L(phys_kernel_start)),#4*1024*1024,\ + %pc@(SYMBOL_NAME(m68k_supervisor_cachemode)) - movel %a3,%d0 - subl %d5,%d0 - lsrl %d2,%d0 - lea %a3@(%d0:l:4),%a2 - movel %a2@,%d1 - andw #_CACHEMASK040,%d1 - orw %d6,%d1 - movel %d1,%a2@+ - /* - * %a2 points now to the page table entry for available pages at %a6, - * hence caching modes for new pages can easily set unless increasing - * of %a2 are forgotten. - */ -Lnot040: + putc 'C' - leds(0x4) - -/* - * Do any machine specific page table initializations. - */ #ifdef CONFIG_AMIGA - is_not_amiga(Lnotami) +L(mmu_init_amiga): + + is_not_amiga(L(mmu_init_not_amiga)) /* - * Setup a mapping of the first 16M of physical address space at virtual - * address 0x80000000, using early termination page descriptors for the - * 68030, and proper page tables for the 680[46]0. Set this area as - * non-cacheable. + * mmu_init_amiga */ - putc('H') + putc 'D' - is_040_or_060(Lspami68040) + is_not_040_or_060(1f) /* - * for the 68030, just setup a translation to map in the first - * 32M of physical address space at virtual address 0x80000000 - * using an early termination page descriptor. + * 040: Map the 16Meg range physical 0x0 upto logical 0x8000.0000 */ + mmu_map #0x80000000,#0,#0x01000000,#_PAGE_NOCACHE_S - putc('I') - - movel #_PAGE_NOCACHE030+_PAGE_PRESENT+_PAGE_ACCESSED,%d0 - movel %d0,%a5@(0x40<<2) - - jra Lmapphys - -Lspami68040: - - /* - * for the 680[46]0, use another pointer table, and allocate 4 more - * page tables. Initialize the pointer table to point to the - * page tables. Then initialize the page tables to point to - * the first 16M of memory, with no caching (noncachable/serialized). - */ - - /* clear the amiga pointer table */ - lea %a4@(PTR_TABLE_SIZE<<2),%a4 - moveq #PTR_TABLE_SIZE-1,%d1 -1: clrl %a0@+ - dbra %d1,1b - - /* allocate 4 pages for 64 page tables */ - movel %a6,%a3 - addw #4*PAGESIZE,%a6 - - /* initialize the pointer table */ - movel %a4,%a0 - movel %a3,%a1 - addw #_PAGE_TABLE+_PAGE_ACCESSED,%a1 /* base descriptor */ - movel #PAGE_TABLE_SIZE<<2,%d2 /* increment */ - moveq #TABLENR_16MB-1,%d1 - -1: movel %a1,%a0@+ - addl %d2,%a1 - dbra %d1,1b - - /* ensure that the root table points to the pointer table */ - movel %a4,%a0 - addw #_PAGE_TABLE+_PAGE_ACCESSED,%a0 - movel %a0,%a5@(0x40<<2) + jbra L(mmu_init_done) +1: /* - * initialize the page tables - * descriptor bits include noncachable/serialized and global bits. + * 030: Map the 32Meg range physical 0x0 upto logical 0x8000.0000 */ - movel %a3,%a0 - movew #_PAGE_GLOBAL040+_PAGE_NOCACHE_S+_PAGE_PRESENT+_PAGE_ACCESSED,%a1 - movel #PAGESIZE,%d2 - movew #(PAGE_TABLE_SIZE*TABLENR_16MB)-1,%d1 + mmu_map #0x80000000,#0,#0x02000000,#_PAGE_NOCACHE030 -1: movel %a1,%a0@+ - addl %d2,%a1 - dbra %d1,1b + jbra L(mmu_init_done) - /* - * Finally, since we just allocated 4 page tables, make sure that - * the virtual mapping of the 4 page tables indicates - * noncachable/serialized. - */ - moveq #3,%d0 -1: movel %a2@,%d1 /* a2 already points to root table offset */ - andw #_CACHEMASK040,%d1 - orw %d6,%d1 - movel %d1,%a2@+ - dbra %d0,1b - - jra Lmapphys - -Lnotami: +L(mmu_init_not_amiga): #endif #ifdef CONFIG_ATARI - is_not_atari(Lnotatari) - move.w #PAGESIZE,%sp +L(mmu_init_atari): + + is_not_atari(L(mmu_init_not_atari)) + + putc 'E' /* On the Atari, we map the I/O region (phys. 0x00ffxxxx) by mapping the last 16 MB of virtual address space to the first 16 MB (i.e. @@ -591,103 +897,57 @@ Lnotami: /* I/O base addr for non-Medusa, non-Hades: 0x00000000 */ moveq #0,%d0 + movel %pc@(SYMBOL_NAME(atari_mch_type)),%d3 cmpl #ATARI_MACH_MEDUSA,%d3 jbeq 2f cmpl #ATARI_MACH_HADES,%d3 jbne 1f 2: movel #0xff000000,%d0 /* Medusa/Hades base addr: 0xff000000 */ 1: movel %d0,%d3 - - /* Let the root table point to the new pointer table */ - lea %a4@(PTR_TABLE_SIZE<<2),%a4 - movel %a4,%a0 - addw #_PAGE_TABLE+_PAGE_ACCESSED,%a0 - movel %a0,%a5@(0x7f<<2) /* 0xFE000000 - 0xFFFFFFFF */ - - /* clear lower half of the pointer table (0xfexxxxxx) */ - movel %a4,%a0 - movel #(PTR_TABLE_SIZE/2)-1,%d2 -1: clrl %a0@+ - dbra %d2,1b - - is_040_or_060(Lspata68040) - -/* Mapping of the last 16M of virtual address space to the first 16M - for efficient addressing of hardware registers */ - movel #PAGE_TABLE_SIZE*PAGESIZE,%d1 - movel #(PTR_TABLE_SIZE/2)-1,%d2 - movel %d3,%d0 - orw #_PAGE_PRESENT+_PAGE_ACCESSED,%d0 -1: movel %d0,%a0@+ - addl %d1,%d0 - dbra %d2,1b - moveq #_PAGE_NOCACHE030,%d0 /* make non-cachable */ - addl %d0,%a4@(0x7f<<2) /* 0xFFFC0000-0xFFFFFFFF (I/O space) */ -/* GK: 0xFFF00000-0xFFF3FFFF (IDE-bus) has to be non-cachable too */ - addl %d0,%a4@(0x7c<<2) - - jra Lmapphys - -Lspata68040: - /* allocate 4 page tables */ - movel %a6,%a3 - addw #4*PAGESIZE,%a6 - - /* Initialize the upper half of the pointer table (a0 is still valid) */ - movel %a3,%a1 - addw #_PAGE_TABLE+_PAGE_ACCESSED,%a1 - movel #PAGE_TABLE_SIZE<<2,%d2 - moveq #TABLENR_16MB-1,%d1 -1: movel %a1,%a0@+ - addl %d2,%a1 - dbra %d1,1b - - /* Initialize the page tables as noncacheable/serialized! */ - movel %a3,%a0 - movel %d3,%a1 - addw #_PAGE_GLOBAL040+_PAGE_NOCACHE_S+_PAGE_PRESENT+_PAGE_ACCESSED,%a1 - movel #PAGESIZE,%d2 - movew #(PAGE_TABLE_SIZE*TABLENR_16MB)-1,%d1 -1: movel %a1,%a0@+ - addl %d2,%a1 - dbra %d1,1b - /* - * Finally, since we just allocated 4 page tables, make sure that - * the virtual mapping of the 4 page tables indicates - * noncachable or write-through. - */ - moveq #3,%d0 -1: movel %a2@,%d1 /* a2 already points to root table offset */ - andw #_CACHEMASK040,%d1 - orw %d6,%d1 - movel %d1,%a2@+ - dbra %d0,1b + is_040_or_060(L(spata68040)) + + /* Map everything non-cacheable, though not all parts really + * need to disable caches (crucial only for 0xff8000..0xffffff + * (standard I/O) and 0xf00000..0xf3ffff (IDE)). The remainder + * isn't really used, except for sometimes peeking into the + * ROMs (mirror at phys. 0x0), so caching isn't necessary for + * this. */ + mmu_map #0xff000000,%d3,#0x01000000,#_PAGE_NOCACHE030 + + jbra L(mmu_init_done) + +L(spata68040): + + mmu_map #0xff000000,%d3,#0x01000000,#_PAGE_NOCACHE_S + + jbra L(mmu_init_done) -Lnotatari: +L(mmu_init_not_atari): #endif #ifdef CONFIG_HP300 - is_not_hp300(Lnothp300) + is_not_hp300(L(nothp300)) /* On the HP300, we map the ROM, INTIO and DIO regions (phys. 0x00xxxxxx) - by mapping 32MB from 0xf0xxxxxx -> 0x00xxxxxx) using an 030 early - termination page descriptor. The ROM mapping is needed because the LEDs + by mapping 32MB from 0xf0xxxxxx -> 0x00xxxxxx) using an 030 early + termination page descriptor. The ROM mapping is needed because the LEDs are mapped there too. */ - movel #_PAGE_NOCACHE030+_PAGE_PRESENT+_PAGE_ACCESSED,%d0 - movel %d0,%a5@(0x78<<2) + mmu_map #0xf0000000,#0,#0x02000000,#_PAGE_NOCACHE030 + +L(nothp300): -Lnothp300: - #endif -#if defined(CONFIG_MVME16x) - is_not_mvme16x(Lnot16x) +#ifdef CONFIG_MVME16x + + is_not_mvme16x(L(not16x)) /* Get pointer to board ID data */ movel %d2,%sp@- - .long 0x4e4f0070 /* trap 0x70 - .BRD_ID */ + trap #15 + .word 0x70 /* trap 0x70 - .BRD_ID */ movel %sp@+,%d2 lea %pc@(SYMBOL_NAME(mvme_bdid_ptr)),%a0 movel %d2,%a0@ @@ -696,392 +956,330 @@ Lnothp300: * On MVME16x we have already created kernel page tables for * 4MB of RAM at address 0, so now need to do a transparent * mapping of the top of memory space. Make it 0.5GByte for now. + * Supervisor only access, so transparent mapping doesn't + * clash with User code virtual address space. + * this covers IO devices, PROM and SRAM. The PROM and SRAM + * mapping is needed to allow 167Bug to run. + * IO is in the range 0xfff00000 to 0xfffeffff. + * PROM is 0xff800000->0xffbfffff and SRAM is + * 0xffe00000->0xffe1ffff. */ - movel #0xe01f0000,%d2 /* logical address base */ - orw #0xa040,%d2 /* add in magic bits */ - .long 0x4e7b2005 /* movec d2,ittr1 */ - .long 0x4e7b2007 /* movec d2,dttr1 */ + mmu_map_tt 1,#0xe0000000,#0x20000000,#_PAGE_NOCACHE_S -Lnot16x: -#endif + jbra L(mmu_init_done) + +L(not16x): +#endif /* CONFIG_MVME162 | CONFIG_MVME167 */ + +#ifdef CONFIG_BVME6000 -#if defined(CONFIG_BVME6000) - is_not_bvme6000(Lnotbvm) + is_not_bvme6000(L(not6000)) /* * On BVME6000 we have already created kernel page tables for * 4MB of RAM at address 0, so now need to do a transparent - * mapping of the top of memory space. Make it 0.5GByte for now. + * mapping of the top of memory space. Make it 0.5GByte for now, + * so we can access on-board i/o areas. + * Supervisor only access, so transparent mapping doesn't + * clash with User code virtual address space. */ - movel #0xe01f0000,%d2 /* logical address base */ - orw #0xa040,%d2 /* add in magic bits */ - .long 0x4e7b2005 /* movec d2,ittr1 */ - .long 0x4e7b2007 /* movec d2,dttr1 */ - .long 0x4e7b2004 /* movec d2,ittr0 */ - .long 0x4e7b2006 /* movec d2,dttr0 */ + mmu_map_tt 1,#0xe0000000,#0x20000000,#_PAGE_NOCACHE_S -Lnotbvm: -#endif + jbra L(mmu_init_done) + +L(not6000): +#endif /* CONFIG_BVME6000 */ /* - * Setup a transparent mapping of the physical memory we are executing in. + * mmu_init_mac + * + * The Macintosh mappings are less clear. * - * Only do this if the physical memory is not in the first 16M Meg, or not on - * an Amiga since the first 16M is already identity mapped on the Amiga. + * Even as of this writing, it is unclear how the + * Macintosh mappings will be done. However, as + * the first author of this code I'm proposing the + * following model: + * + * Map the kernel (that's already done), + * Map the I/O (on most machines that's the + * 0x5000.0000 ... 0x5200.0000 range, + * Map the video frame buffer using as few pages + * as absolutely (this requirement mostly stems from + * the fact that when the frame buffer is at + * 0x0000.0000 then we know there is valid RAM just + * above the screen that we don't want to waste!). + * + * By the way, if the frame buffer is at 0x0000.0000 + * then the Macintosh is known as an RBV based Mac. + * + * By the way 2, the code currently maps in a bunch of + * regions. But I'd like to cut that out. (And move most + * of the mappings up into the kernel proper ... or only + * map what's necessary.) */ -Lmapphys: - putc('J') - leds(0x8) -#ifdef CONFIG_AMIGA - is_not_amiga(Lmapphysnotamiga) +#ifdef CONFIG_MAC -/* - * The virtual address of the start of the kernel is 0x1000. We transparently - * translate the memory where we running in and can enable then the MMU. Hence - * we have now two locations of the kernel in memory and can jump to the final - * place. Except if the physical location is in the first 16MB, translation - * will overlap later virtual location, but as we already mapped the first - * 16MB to 0x80000000, we can jump there after translation and MMU is enabled - * and then we can switch off translation and go to the final place. - * On 020/030 we must emulate transparant translation, since 020 doesn't know - * it, but due to early termination pointer this is easy to do. - * When MMU is enabled, stack pointer and Lcustom will become again valid and - * stack points to the unused first page. - */ +L(mmu_init_mac): -/* - * Setup Supervisor Root Pointer register to point to page directory, - * setup translation register contents and enable translation. - */ - putc('K') + is_not_mac(L(mmu_init_not_mac)) - movew #PAGESIZE,%sp + putc 'F' - /* fixup the Amiga custom register location before printing */ - lea %pc@(Lcustom),%a0 - movel #0x80000000,%a0@ + lea %pc@(L(mac_videobase)),%a0 + lea %pc@(L(console_video_virtual)),%a1 + movel %a0@,%a1@ - is_040_or_060(Lamimmu68040) + is_not_040_or_060(1f) - moveq #ROOT_INDEX_SHIFT,%d2 - movel %d5,%d0 - lsrl %d2,%d0 - movel %d0,%d1 - lsll %d2,%d1 - orw #_PAGE_PRESENT+_PAGE_ACCESSED,%d1 - lsll #2,%d0 - movel %a5@(%d0:w),%d2 - movel %d1,%a5@(%d0:w) - lea %pc@(Lmmu),%a3 - /* no limit, 4byte descriptors */ - movel #0x80000002,%a3@ - movel %a5,%a3@(4) - pmove %a3@,%srp - pmove %a3@,%crp - pflusha + moveq #_PAGE_NOCACHE_S,%d3 + jbra 2f +1: + moveq #_PAGE_NOCACHE030,%d3 +2: /* - * enable,super root enable,4096 byte pages,7 bit root index, - * 7 bit pointer index, 6 bit page table index. + * Mac Note: screen address of logical 0xF000.0000 -> <screen physical> + * we simply map the 4MB that contains the videomem */ - movel #0x82c07760,%a3@ - pmove %a3@,%tc /* enable the MMU */ - tstl %d0 - jne 1f - jmp %pc@(2f+0x80000000) -1: jmp 2f:w -2: movel %d2,%a5@(%d0:w) - pflusha - jmp LdoneMMUenable:w -Lamimmu68040: + movel #VIDEOMEMMASK,%d0 + andl L(mac_videobase),%d0 - .chip 68040 - lea 2f:w,%a0 - movel %d5,%d0 - andl #0xff000000,%d0 - jne 1f - lea %pc@(2f+0x80000000),%a0 -1: orw #TTR_ENABLE+TTR_KERNELMODE+_PAGE_NOCACHE_S,%d0 - movec %d0,%itt0 - movec %a5,%urp - movec %a5,%srp - pflusha - movel #TC_ENABLE+TC_PAGE4K,%d0 - /* - * this value is also ok for the 68060, we don`t use the cache - * mode/protection defaults - */ - movec %d0,%tc /* enable the MMU */ - jmp %a0@ -2: moveq #0,%d0 - movec %d0,%itt0 - jmp LdoneMMUenable:w - .chip 68k + mmu_map #VIDEOMEMBASE,%d0,#VIDEOMEMSIZE,%d3 + mmu_map_eq #0x40800000,#0x02000000,%d3 /* rom ? */ + mmu_map_eq #0x50000000,#0x02000000,%d3 + mmu_map_eq #0x60000000,#0x00400000,%d3 + mmu_map_eq #0x9c000000,#0x00400000,%d3 + mmu_map_tt 1,#0xf8000000,#0x08000000,%d3 -Lmapphysnotamiga: + jbra L(mmu_init_done) + +L(mmu_init_not_mac): #endif -#ifdef CONFIG_ATARI - is_not_atari(Lmapphysnotatari) +L(mmu_init_done): + + putc 'G' + leds 0x8 /* - * If the kernel physical address is different from its virtual address, we - * will temporarily set up an identity mapping of the 16MB chunk with - * transparent translation where the kernel is executing. + * mmu_fixup + * + * On the 040 class machines, all pages that are used for the + * mmu have to be fixed up. According to Motorola, pages holding mmu + * tables should be non-cacheable on a '040 and write-through on a + * '060. But analysis of the reasons for this, and practical + * experience, showed that write-through also works on a '040. + * + * Allocated memory so far goes from kernel_end to memory_start that + * is used for all kind of tables, for that the cache attributes + * are now fixed. */ - putc('L') - - /* fixup the Atari iobase register location before printing */ - lea %pc@(Liobase),%a0 - movel #0xff000000,%a0@ +L(mmu_fixup): - is_040_or_060(Latarimmu68040) + is_not_040_or_060(L(mmu_fixup_done)) - .chip 68030 - lea %pc@(Lmmu),%a3 - movel %d5,%d0 - jne 1f - lea LdoneMMUenable:w,%a0 - jra 3f -1: lea 4f:w,%a0 - andl #0xff000000,%d0 /* logical address base */ - jeq 2f - orw #TTR_ENABLE+TTR_CI+TTR_RWM+TTR_FCB2+TTR_FCM1+TTR_FCM0,%d0 - movel %d0,%a3@ - pmove %a3@,%tt0 - jra 3f - /* tt0 doesn't work if physical and virtual address of kernel is in - * the same 16M area (Falcon with Magnum/FX, kernel in alternate ram) - * Transparent translation through kernel pointer table - * Requires that this code until after MMU enabling lies in - * the 256K page around %d5 - */ -2: movel %a5@,%d1 - andw #0xfff0,%d1 - movel %d1,%a1 - movel %d5,%d1 - moveq #PTR_INDEX_SHIFT,%d0 - lsrl %d0,%d1 - lea %a1@(%d1:l:4),%a1 - movel %d5,%d1 - orw #_PAGE_PRESENT+_PAGE_ACCESSED,%d1 - movel %a1@,%d2 - movel %d1,%a1@ - lea 5f:w,%a0 - /* no limit, 4byte descriptors */ -3: movel #0x80000002,%a3@ - movel %a5,%a3@(4) - pmove %a3@,%srp - pmove %a3@,%crp - pflusha - /* - * enable,super root enable,4096 byte pages,7 bit root index, - * 7 bit pointer index, 6 bit page table index. - */ - movel #0x82c07760,%a3@ - pmove %a3@,%tc /* enable the MMU */ - jmp %a0@ -4: clrl %a3@ - pmove %a3@,%tt0 - jra LdoneMMUenable -5: movel %d2,%a1@ - jra LdoneMMUenable - .chip 68k - -Latarimmu68040: - .chip 68040 - movel %d5,%d0 - jne 1f - lea LdoneMMUenable:w,%a0 - jra 2f -1: lea 3f:w,%a0 - andl #0xff000000,%d0 /* logical address base */ - orw #TTR_ENABLE+TTR_KERNELMODE+_PAGE_NOCACHE_S,%d0 - movec %d0,%itt0 -2: nop - pflusha - movec %a5,%srp - movec %a5,%urp - movel #TC_ENABLE+TC_PAGE4K,%d0 - /* - * this value is also ok for the 68060, we don`t use the cache - * mode/protection defaults - */ - movec %d0,%tc /* enable the MMU */ - jmp %a0@ -3: moveq #0,%d0 - movec %d0,%itt0 - jra LdoneMMUenable - .chip 68k - -Lmapphysnotatari: +#ifdef MMU_NOCACHE_KERNEL + jbra L(mmu_fixup_done) #endif -#if defined(CONFIG_MVME16x) - is_not_mvme16x(Lmapphysnot16x) - /* - * save physaddr of phys mem in register a3 + /* first fix the page at the start of the kernel, that + * contains also kernel_pg_dir. */ - moveq #'L',%d7 - jbsr Lserial_putc - - .word 0xf4d8 /* CINVA I/D */ - .word 0xf518 /* pflusha */ - .long 0x4e7bd807 /* movec a5,srp */ - .long 0x4e7bd806 /* movec a5,urp */ - movel #(TC_ENABLE+TC_PAGE4K),%d0 - .long 0x4e7b0003 /* movec d0,tc (enable the MMU) */ - jra LdoneMMUenable /* branch to continuation of startup */ + movel %pc@(L(phys_kernel_start)),%d0 + lea %pc@(SYMBOL_NAME(_stext)),%a0 + subl %d0,%a0 + mmu_fixup_page_mmu_cache %a0 + + movel %pc@(L(kernel_end)),%a0 + subl %d0,%a0 + movel %pc@(L(memory_start)),%a1 + subl %d0,%a1 + bra 2f +1: + mmu_fixup_page_mmu_cache %a0 + addw #PAGESIZE,%a0 +2: + cmpl %a0,%a1 + jgt 1b -Lmapphysnot16x: +L(mmu_fixup_done): +#ifdef MMU_PRINT + mmu_print #endif -#if defined(CONFIG_HP300) - is_not_hp300(Lmapphysnothp300) - /* - * Physical RAM is at 0xff000000. We want to map the kernel at 0x00000000. - * In order to avoid disaster when we enable the MMU we need to make a - * transparent mapping of the RAM we're executing out of as well. + * mmu_engage + * + * This chunk of code performs the gruesome task of engaging the MMU. + * The reason its gruesome is because when the MMU becomes engaged it + * maps logical addresses to physical addresses. The Program Counter + * register is then passed through the MMU before the next instruction + * is fetched (the instruction following the engage MMU instruction). + * This may mean one of two things: + * 1. The Program Counter falls within the logical address space of + * the kernel of which there are two sub-possibilities: + * A. The PC maps to the correct instruction (logical PC == physical + * code location), or + * B. The PC does not map through and the processor will read some + * data (or instruction) which is not the logically next instr. + * As you can imagine, A is good and B is bad. + * Alternatively, + * 2. The Program Counter does not map through the MMU. The processor + * will take a Bus Error. + * Clearly, 2 is bad. + * It doesn't take a wiz kid to figure you want 1.A. + * This code creates that possibility. + * There are two possible 1.A. states (we now ignore the other above states): + * A. The kernel is located at physical memory addressed the same as + * the logical memory for the kernel, i.e., 0x01000. + * B. The kernel is located some where else. e.g., 0x0400.0000 + * + * Under some conditions the Macintosh can look like A or B. + * [A friend and I once noted that Apple hardware engineers should be + * wacked twice each day: once when they show up at work (as in, Whack!, + * "This is for the screwy hardware we know you're going to design today."), + * and also at the end of the day (as in, Whack! "I don't know what + * you designed today, but I'm sure it wasn't good."). -- rst] + * + * This code works on the following premise: + * If the kernel start (%d5) is within the first 16 Meg of RAM, + * then create a mapping for the kernel at logical 0x8000.0000 to + * the physical location of the pc. And, create a transparent + * translation register for the first 16 Meg. Then, after the MMU + * is engaged, the PC can be moved up into the 0x8000.0000 range + * and then the transparent translation can be turned off and then + * the PC can jump to the correct logical location and it will be + * home (finally). This is essentially the code that the Amiga used + * to use. Now, it's generalized for all processors. Which means + * that a fresh (but temporary) mapping has to be created. The mapping + * is made in page 0 (an as of yet unused location -- except for the + * stack!). This temporary mapping will only require 1 pointer table + * and a single page table (it can map 256K). + * + * OK, alternatively, imagine that the Program Counter is not within + * the first 16 Meg. Then, just use Transparent Translation registers + * to do the right thing. + * + * Last, if _start is already at 0x01000, then there's nothing special + * to do (in other words, in a degenerate case of the first case above, + * do nothing). + * + * Let's do it. + * + * */ - /* - * save physaddr of phys mem in register a3 - */ - .chip 68030 - lea %pc@(Lmmu),%a3 - movel %d5,%d0 - andl #0xff000000,%d0 /* logical address base */ - orw #TTR_ENABLE+TTR_CI+TTR_RWM+TTR_FCB2+TTR_FCM1+TTR_FCM0,%d0 - movel %d0,%a3@ - pmove %a3@,%tt0 - /* no limit, 4byte descriptors */ - movel #0x80000002,%a3@ - movel %a5,%a3@(4) - pmove %a3@,%srp - pmove %a3@,%crp - pflusha - /* - * enable,super root enable,4096 byte pages,7 bit root index, - * 7 bit pointer index, 6 bit page table index. - */ - movel #0x82c07760,%a3@ - pmove %a3@,%tc /* enable the MMU */ - jmp 1f -1: - .chip 68k + putc 'H' - /* - * Fix up the custom register to point to the new location of the LEDs. - */ - lea %pc@(Lcustom),%a1 - movel #0xf0000000,%a1@ + mmu_engage - /* - * Energise the FPU and caches. - */ - orl #0x64, 0xf05f400c - -Lmapphysnothp300: +#ifdef CONFIG_AMIGA + is_not_amiga(1f) + /* fixup the Amiga custom register location before printing */ + clrl L(custom) +1: +#endif + +#ifdef CONFIG_ATARI + is_not_atari(1f) + /* fixup the Atari iobase register location before printing */ + movel #0xff000000,L(iobase) +1: +#endif +#ifdef CONFIG_MAC + is_not_mac(1f) + movel #~VIDEOMEMMASK,%d0 + andl L(mac_videobase),%d0 + addl #VIDEOMEMBASE,%d0 + movel %d0,L(mac_videobase) +1: #endif -#if defined(CONFIG_BVME6000) - is_not_bvme6000(Lmapphysnotbvm) +#ifdef CONFIG_HP300 + is_not_hp300(1f) /* - * save physaddr of phys mem in register a3 + * Fix up the custom register to point to the new location of the LEDs. */ - moveq #'L',%d7 - jbsr Lserial_putc + movel #0xf0000000,L(custom) - .word 0xf4d8 /* CINVA I/D */ - .word 0xf518 /* pflusha */ - .long 0x4e7bd807 /* movec a5,srp */ - .long 0x4e7bd806 /* movec a5,urp */ - movel #(TC_ENABLE+TC_PAGE4K),%d0 /* - * this value is also ok for the 68060, we don`t use the cache - * mode/protection defaults + * Energise the FPU and caches. */ - .long 0x4e7b0003 /* movec d0,tc (enable the MMU) */ - jra LdoneMMUenable /* branch to continuation of startup */ - -Lmapphysnotbvm: - + movel #0x60,0xf05f400c +1: #endif -LdoneMMUenable: - /* * Fixup the addresses for the kernel pointer table and availmem. * Convert them from physical addresses to virtual addresses. */ - putc('M') - leds(0x10) - - /* - * d5 contains physaddr of kernel start - */ - subl %d5,SYMBOL_NAME(kpt) + putc 'I' + leds 0x10 - /* - * do the same conversion on the first available memory + /* do the same conversion on the first available memory * address (in a6). */ - subl %d5,%a6 - movel %a6,SYMBOL_NAME(availmem) /* first available memory address */ - - putc('N') + movel L(memory_start),%d0 + movel %d0,SYMBOL_NAME(availmem) /* * Enable caches */ - is_040_or_060(Lcache680460) - movel #CC3_ENABLE_DB+CC3_CLR_D+CC3_ENABLE_D+CC3_ENABLE_IB+CC3_CLR_I+CC3_ENABLE_I,%d0 - movec %d0,%cacr - jra 1f + is_not_040_or_060(L(cache_not_680460)) -Lcache680460: +L(cache680460): .chip 68040 + nop cpusha %bc - .chip 68k + nop - is_060(Lcache68060) + is_060(L(cache68060)) movel #CC6_ENABLE_D+CC6_ENABLE_I,%d0 /* MMU stuff works in copyback mode now, so enable the cache */ movec %d0,%cacr - jra 1f + jra L(cache_done) -Lcache68060: - .chip 68060 +L(cache68060): movel #CC6_ENABLE_D+CC6_ENABLE_I+CC6_ENABLE_SB+CC6_PUSH_DPI+CC6_ENABLE_B+CC6_CLRA_B,%d0 /* MMU stuff works in copyback mode now, so enable the cache */ movec %d0,%cacr /* enable superscalar dispatch in PCR */ moveq #1,%d0 + .chip 68060 movec %d0,%pcr + + jbra L(cache_done) +L(cache_not_680460): +L(cache68030): + .chip 68030 + movel #CC3_ENABLE_DB+CC3_CLR_D+CC3_ENABLE_D+CC3_ENABLE_IB+CC3_CLR_I+CC3_ENABLE_I,%d0 + movec %d0,%cacr + + jra L(cache_done) .chip 68k -1: +L(cache_done): + + putc 'J' /* * Setup initial stack pointer - * We need to get current loaded up with our first task... */ lea SYMBOL_NAME(init_task_union),%a2 - lea 8192(%a2),%sp + lea 0x2000(%a2),%sp /* jump to the kernel start */ - putr() - leds(0x55) + putc '\n' + leds 0x55 - subl %a6,%a6 /* clear a6 for gdb */ + subl %a6,%a6 /* clear a6 for gdb */ jbsr SYMBOL_NAME(start_kernel) /* @@ -1090,33 +1288,1260 @@ Lcache68060: * Returns: d0: size (-1 if not found) * a0: data pointer (end-of-records if not found) */ -Lget_bi_record: +func_start get_bi_record,%d1 + + movel ARG1,%d0 lea %pc@(SYMBOL_NAME(_end)),%a0 -1: tstw %a0@(BIR_tag) +1: tstw %a0@(BIR_TAG) jeq 3f - cmpw %a0@(BIR_tag),%d0 + cmpw %a0@(BIR_TAG),%d0 jeq 2f - addw %a0@(BIR_size),%a0 + addw %a0@(BIR_SIZE),%a0 jra 1b 2: moveq #0,%d0 - movew %a0@(BIR_size),%d0 - lea %a0@(BIR_data),%a0 - rts + movew %a0@(BIR_SIZE),%d0 + lea %a0@(BIR_DATA),%a0 + jra 4f 3: moveq #-1,%d0 - lea %a0@(BIR_size),%a0 + lea %a0@(BIR_SIZE),%a0 +4: +func_return get_bi_record + + +/* + * MMU Initialization Begins Here + * + * The structure of the MMU tables on the 68k machines + * is thus: + * Root Table + * Logical addresses are translated through + * a hierarchical translation mechanism where the high-order + * seven bits of the logical address (LA) are used as an + * index into the "root table." Each entry in the root + * table has a bit which specifies if it's a valid pointer to a + * pointer table. Each entry defines a 32KMeg range of memory. + * If an entry is invalid then that logical range of 32M is + * invalid and references to that range of memory (when the MMU + * is enabled) will fault. If the entry is valid, then it does + * one of two things. On 040/060 class machines, it points to + * a pointer table which then describes more finely the memory + * within that 32M range. On 020/030 class machines, a technique + * called "early terminating descriptors" are used. This technique + * allows an entire 32Meg to be described by a single entry in the + * root table. Thus, this entry in the root table, contains the + * physical address of the memory or I/O at the logical address + * which the entry represents and it also contains the necessary + * cache bits for this region. + * + * Pointer Tables + * Per the Root Table, there will be one or more + * pointer tables. Each pointer table defines a 32M range. + * Not all of the 32M range need be defined. Again, the next + * seven bits of the logical address are used an index into + * the pointer table to point to page tables (if the pointer + * is valid). There will undoubtedly be more than one + * pointer table for the kernel because each pointer table + * defines a range of only 32M. Valid pointer table entries + * point to page tables, or are early terminating entries + * themselves. + * + * Page Tables + * Per the Pointer Tables, each page table entry points + * to the physical page in memory that supports the logical + * address that translates to the particular index. + * + * In short, the Logical Address gets translated as follows: + * bits 31..26 - index into the Root Table + * bits 25..18 - index into the Pointer Table + * bits 17..12 - index into the Page Table + * bits 11..0 - offset into a particular 4K page + * + * The algorithms which follows do one thing: they abstract + * the MMU hardware. For example, there are three kinds of + * cache settings that are relevant. Either, memory is + * being mapped in which case it is either Kernel Code (or + * the RamDisk) or it is MMU data. On the 030, the MMU data + * option also describes the kernel. Or, I/O is being mapped + * in which case it has its own kind of cache bits. There + * are constants which abstract these notions from the code that + * actually makes the call to map some range of memory. + * + * + * + */ + +#ifdef MMU_PRINT +/* + * mmu_print + * + * This algorithm will print out the current MMU mappings. + * + * Input: + * %a5 points to the root table. Everything else is calculated + * from this. + */ + +#define mmu_next_valid 0 +#define mmu_start_logical 4 +#define mmu_next_logical 8 +#define mmu_start_physical 12 +#define mmu_next_physical 16 + +#define MMU_PRINT_INVALID -1 +#define MMU_PRINT_VALID 1 +#define MMU_PRINT_UNINITED 0 + +#define putZc(z,n) jbne 1f; putc z; jbra 2f; 1: putc n; 2: + +func_start mmu_print,%a0-%a6/%d0-%d7 + + movel %pc@(L(kernel_pgdir_ptr)),%a5 + lea %pc@(L(mmu_print_data)),%a0 + movel #MMU_PRINT_UNINITED,%a0@(mmu_next_valid) + + is_not_040_or_060(mmu_030_print) + +mmu_040_print: + puts "\nMMU040\n" + puts "rp:" + putn %a5 + putc '\n' +#if 0 + /* + * The following #if/#endif block is a tight algorithm for dumping the 040 + * MMU Map in gory detail. It really isn't that practical unless the + * MMU Map algorithm appears to go awry and you need to debug it at the + * entry per entry level. + */ + movel #ROOT_TABLE_SIZE,%d5 +#if 0 + movel %a5@+,%d7 | Burn an entry to skip the kernel mappings, + subql #1,%d5 | they (might) work +#endif +1: tstl %d5 + jbeq mmu_print_done + subq #1,%d5 + movel %a5@+,%d7 + btst #1,%d7 + jbeq 1b + +2: putn %d7 + andil #0xFFFFFE00,%d7 + movel %d7,%a4 + movel #PTR_TABLE_SIZE,%d4 + putc ' ' +3: tstl %d4 + jbeq 11f + subq #1,%d4 + movel %a4@+,%d7 + btst #1,%d7 + jbeq 3b + +4: putn %d7 + andil #0xFFFFFF00,%d7 + movel %d7,%a3 + movel #PAGE_TABLE_SIZE,%d3 +5: movel #8,%d2 +6: tstl %d3 + jbeq 31f + subq #1,%d3 + movel %a3@+,%d6 + btst #0,%d6 + jbeq 6b +7: tstl %d2 + jbeq 8f + subq #1,%d2 + putc ' ' + jbra 91f +8: putc '\n' + movel #8+1+8+1+1,%d2 +9: putc ' ' + dbra %d2,9b + movel #7,%d2 +91: putn %d6 + jbra 6b + +31: putc '\n' + movel #8+1,%d2 +32: putc ' ' + dbra %d2,32b + jbra 3b + +11: putc '\n' + jbra 1b +#endif /* MMU 040 Dumping code that's gory and detailed */ + + lea %pc@(SYMBOL_NAME(kernel_pg_dir)),%a5 + movel %a5,%a0 /* a0 has the address of the root table ptr */ + movel #0x00000000,%a4 /* logical address */ + moveql #0,%d0 +40: + /* Increment the logical address and preserve in d5 */ + movel %a4,%d5 + addil #PAGESIZE<<13,%d5 + movel %a0@+,%d6 + btst #1,%d6 + jbne 41f + jbsr mmu_print_tuple_invalidate + jbra 48f +41: + movel #0,%d1 + andil #0xfffffe00,%d6 + movel %d6,%a1 +42: + movel %a4,%d5 + addil #PAGESIZE<<6,%d5 + movel %a1@+,%d6 + btst #1,%d6 + jbne 43f + jbsr mmu_print_tuple_invalidate + jbra 47f +43: + movel #0,%d2 + andil #0xffffff00,%d6 + movel %d6,%a2 +44: + movel %a4,%d5 + addil #PAGESIZE,%d5 + movel %a2@+,%d6 + btst #0,%d6 + jbne 45f + jbsr mmu_print_tuple_invalidate + jbra 46f +45: + moveml %d0-%d1,%sp@- + movel %a4,%d0 + movel %d6,%d1 + andil #0xfffff4e0,%d1 + lea %pc@(mmu_040_print_flags),%a6 + jbsr mmu_print_tuple + moveml %sp@+,%d0-%d1 +46: + movel %d5,%a4 + addq #1,%d2 + cmpib #64,%d2 + jbne 44b +47: + movel %d5,%a4 + addq #1,%d1 + cmpib #128,%d1 + jbne 42b +48: + movel %d5,%a4 /* move to the next logical address */ + addq #1,%d0 + cmpib #128,%d0 + jbne 40b + + .chip 68040 + movec %dtt1,%d0 + movel %d0,%d1 + andiw #0x8000,%d1 /* is it valid ? */ + jbeq 1f /* No, bail out */ + + movel %d0,%d1 + andil #0xff000000,%d1 /* Get the address */ + putn %d1 + puts "==" + putn %d1 + + movel %d0,%d6 + jbsr mmu_040_print_flags_tt +1: + movec %dtt0,%d0 + movel %d0,%d1 + andiw #0x8000,%d1 /* is it valid ? */ + jbeq 1f /* No, bail out */ + + movel %d0,%d1 + andil #0xff000000,%d1 /* Get the address */ + putn %d1 + puts "==" + putn %d1 + + movel %d0,%d6 + jbsr mmu_040_print_flags_tt +1: + .chip 68k + + jbra mmu_print_done + +mmu_040_print_flags: + btstl #10,%d6 + putZc(' ','G') /* global bit */ + btstl #7,%d6 + putZc(' ','S') /* supervisor bit */ +mmu_040_print_flags_tt: + btstl #6,%d6 + jbne 3f + putc 'C' + btstl #5,%d6 + putZc('w','c') /* write through or copy-back */ + jbra 4f +3: + putc 'N' + btstl #5,%d6 + putZc('s',' ') /* serialized non-cacheable, or non-cacheable */ +4: + rts + +mmu_030_print_flags: + btstl #6,%d6 + putZc('C','I') /* write through or copy-back */ + rts + +mmu_030_print: + puts "\nMMU030\n" + puts "\nrp:" + putn %a5 + putc '\n' + movel %a5,%d0 + andil #0xfffffff0,%d0 + movel %d0,%a0 + movel #0x00000000,%a4 /* logical address */ + movel #0,%d0 +30: + movel %a4,%d5 + addil #PAGESIZE<<13,%d5 + movel %a0@+,%d6 + btst #1,%d6 /* is it a ptr? */ + jbne 31f /* yes */ + btst #0,%d6 /* is it early terminating? */ + jbeq 1f /* no */ + jbsr mmu_030_print_helper + jbra 38f +1: + jbsr mmu_print_tuple_invalidate + jbra 38f +31: + movel #0,%d1 + andil #0xfffffff0,%d6 + movel %d6,%a1 +32: + movel %a4,%d5 + addil #PAGESIZE<<6,%d5 + movel %a1@+,%d6 + btst #1,%d6 + jbne 33f + btst #0,%d6 + jbeq 1f /* no */ + jbsr mmu_030_print_helper + jbra 37f +1: + jbsr mmu_print_tuple_invalidate + jbra 37f +33: + movel #0,%d2 + andil #0xfffffff0,%d6 + movel %d6,%a2 +34: + movel %a4,%d5 + addil #PAGESIZE,%d5 + movel %a2@+,%d6 + btst #0,%d6 + jbne 35f + jbsr mmu_print_tuple_invalidate + jbra 36f +35: + jbsr mmu_030_print_helper +36: + movel %d5,%a4 + addq #1,%d2 + cmpib #64,%d2 + jbne 34b +37: + movel %d5,%a4 + addq #1,%d1 + cmpib #128,%d1 + jbne 32b +38: + movel %d5,%a4 /* move to the next logical address */ + addq #1,%d0 + cmpib #128,%d0 + jbne 30b + +mmu_print_done: + puts "\n\n" + +func_return mmu_print + + +mmu_030_print_helper: + moveml %d0-%d1,%sp@- + movel %a4,%d0 + movel %d6,%d1 + lea %pc@(mmu_030_print_flags),%a6 + jbsr mmu_print_tuple + moveml %sp@+,%d0-%d1 + rts + +mmu_print_tuple_invalidate: + moveml %a0/%d7,%sp@- + + lea %pc@(L(mmu_print_data)),%a0 + tstl %a0@(mmu_next_valid) + jbmi mmu_print_tuple_invalidate_exit + + movel #MMU_PRINT_INVALID,%a0@(mmu_next_valid) + + putn %a4 + + puts "##\n" + +mmu_print_tuple_invalidate_exit: + moveml %sp@+,%a0/%d7 + rts + + +mmu_print_tuple: + moveml %d0-%d7/%a0,%sp@- + + lea %pc@(L(mmu_print_data)),%a0 + + tstl %a0@(mmu_next_valid) + jble mmu_print_tuple_print + + cmpl %a0@(mmu_next_physical),%d1 + jbeq mmu_print_tuple_increment + +mmu_print_tuple_print: + putn %d0 + puts "->" + putn %d1 + + movel %d1,%d6 + jbsr %a6@ + +mmu_print_tuple_record: + movel #MMU_PRINT_VALID,%a0@(mmu_next_valid) + + movel %d1,%a0@(mmu_next_physical) + +mmu_print_tuple_increment: + movel %d5,%d7 + subl %a4,%d7 + addl %d7,%a0@(mmu_next_physical) + +mmu_print_tuple_exit: + moveml %sp@+,%d0-%d7/%a0 rts +mmu_print_machine_cpu_types: + puts "machine: " + + is_not_amiga(1f) + puts "amiga" + jbra 9f +1: + is_not_atari(2f) + puts "atari" + jbra 9f +2: + is_not_mac(3f) + puts "macintosh" + jbra 9f +3: puts "unknown" +9: putc '\n' + + puts "cputype: 0" + is_not_060(1f) + putc '6' + jbra 9f +1: + is_not_040_or_060(2f) + putc '4' + jbra 9f +2: putc '3' +9: putc '0' + putc '\n' + + rts +#endif /* MMU_PRINT */ + +/* + * mmu_map_tt + * + * This is a specific function which works on all 680x0 machines. + * On 030, 040 & 060 it will attempt to use Transparent Translation + * registers (tt1). + * On 020 it will call the standard mmu_map which will use early + * terminating descriptors. + */ +func_start mmu_map_tt,%d0/%d1/%a0,4 + + dputs "mmu_map_tt:" + dputn ARG1 + dputn ARG2 + dputn ARG3 + dputn ARG4 + dputc '\n' + + is_020(L(do_map)) + + /* Extract the highest bit set + */ + bfffo ARG3{#0,#32},%d1 + cmpw #8,%d0 + jcc L(do_map) + + /* And get the mask + */ + moveq #-1,%d0 + lsrl %d1,%d0 + lsrl #1,%d0 + + /* Mask the address + */ + movel %d0,%d1 + notl %d1 + andl ARG2,%d1 + + /* Generate the upper 16bit of the tt register + */ + lsrl #8,%d0 + orl %d0,%d1 + clrw %d1 + + is_040_or_060(L(mmu_map_tt_040)) + + /* set 030 specific bits (read/write access for supervisor mode + * (highest function code set, lower two bits masked)) + */ + orw #TTR_ENABLE+TTR_RWM+TTR_FCB2+TTR_FCM1+TTR_FCM0,%d1 + movel ARG4,%d0 + btst #6,%d0 + jeq 1f + orw #TTR_CI,%d1 + +1: lea STACK,%a0 + dputn %d1 + movel %d1,%a0@ + .chip 68030 + tstl ARG1 + jne 1f + pmove %a0@,%tt0 + jra 2f +1: pmove %a0@,%tt1 +2: .chip 68k + jra L(mmu_map_tt_done) + + /* set 040 specific bits + */ +L(mmu_map_tt_040): + orw #TTR_ENABLE+TTR_KERNELMODE,%d1 + orl ARG4,%d1 + dputn %d1 + + .chip 68040 + tstl ARG1 + jne 1f + movec %d1,%itt0 + movec %d1,%dtt0 + jra 2f +1: movec %d1,%itt1 + movec %d1,%dtt1 +2: .chip 68k + + jra L(mmu_map_tt_done) + +L(do_map): + mmu_map_eq ARG2,ARG3,ARG4 + +L(mmu_map_tt_done): + +func_return mmu_map_tt + +/* + * mmu_map + * + * This routine will map a range of memory using a pointer + * table and allocating the pages on the fly from the kernel. + * The pointer table does not have to be already linked into + * the root table, this routine will do that if necessary. + * + * NOTE + * This routine will assert failure and use the serial_putc + * routines in the case of a run-time error. For example, + * if the address is already mapped. + * + * NOTE-2 + * This routine will use early terminating descriptors + * where possible for the 68020+68851 and 68030 type + * processors. + */ +func_start mmu_map,%d0-%d4/%a0-%a4 + + dputs "\nmmu_map:" + dputn ARG1 + dputn ARG2 + dputn ARG3 + dputn ARG4 + dputc '\n' + + /* Get logical address and round it down to 256KB + */ + movel ARG1,%d0 + andl #-(PAGESIZE*PAGE_TABLE_SIZE),%d0 + movel %d0,%a3 + + /* Get the end address + */ + movel ARG1,%a4 + addl ARG3,%a4 + subql #1,%a4 + + /* Get physical address and round it down to 256KB + */ + movel ARG2,%d0 + andl #-(PAGESIZE*PAGE_TABLE_SIZE),%d0 + movel %d0,%a2 + + /* Add page attributes to the physical address + */ + movel ARG4,%d0 + orw #_PAGE_PRESENT+_PAGE_ACCESSED+_PAGE_DIRTY,%d0 + addw %d0,%a2 + + dputn %a2 + dputn %a3 + dputn %a4 + + is_not_040_or_060(L(mmu_map_030)) + + addw #_PAGE_GLOBAL040,%a2 +/* + * MMU 040 & 060 Support + * + * The MMU usage for the 040 and 060 is different enough from + * the 030 and 68851 that there is separate code. This comment + * block describes the data structures and algorithms built by + * this code. + * + * The 040 does not support early terminating descriptors, as + * the 030 does. Therefore, a third level of table is needed + * for the 040, and that would be the page table. In Linux, + * page tables are allocated directly from the memory above the + * kernel. + * + */ + +L(mmu_map_040): + /* Calculate the offset into the root table + */ + movel %a3,%d0 + moveq #ROOT_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + mmu_get_root_table_entry %d0 + + /* Calculate the offset into the pointer table + */ + movel %a3,%d0 + moveq #PTR_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + andl #PTR_TABLE_SIZE-1,%d0 + mmu_get_ptr_table_entry %a0,%d0 + + /* Calculate the offset into the page table + */ + movel %a3,%d0 + moveq #PAGE_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + andl #PAGE_TABLE_SIZE-1,%d0 + mmu_get_page_table_entry %a0,%d0 + + /* The page table entry must not no be busy + */ + tstl %a0@ + jne L(mmu_map_error) + + /* Do the mapping and advance the pointers + */ + movel %a2,%a0@ +2: + addw #PAGESIZE,%a2 + addw #PAGESIZE,%a3 + + /* Ready with mapping? + */ + lea %a3@(-1),%a0 + cmpl %a0,%a4 + jhi L(mmu_map_040) + jra L(mmu_map_done) + +L(mmu_map_030): + /* Calculate the offset into the root table + */ + movel %a3,%d0 + moveq #ROOT_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + mmu_get_root_table_entry %d0 + + /* Check if logical address 32MB aligned, + * so we can try to map it once + */ + movel %a3,%d0 + andl #(PTR_TABLE_SIZE*PAGE_TABLE_SIZE*PAGESIZE-1)&(-ROOT_TABLE_SIZE),%d0 + jne 1f + + /* Is there enough to map for 32MB at once + */ + lea %a3@(PTR_TABLE_SIZE*PAGE_TABLE_SIZE*PAGESIZE-1),%a1 + cmpl %a1,%a4 + jcs 1f + + addql #1,%a1 + + /* The root table entry must not no be busy + */ + tstl %a0@ + jne L(mmu_map_error) + + /* Do the mapping and advance the pointers + */ + dputs "early term1" + dputn %a2 + dputn %a3 + dputn %a1 + dputc '\n' + movel %a2,%a0@ + + movel %a1,%a3 + lea %a2@(PTR_TABLE_SIZE*PAGE_TABLE_SIZE*PAGESIZE),%a2 + jra L(mmu_mapnext_030) +1: + /* Calculate the offset into the pointer table + */ + movel %a3,%d0 + moveq #PTR_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + andl #PTR_TABLE_SIZE-1,%d0 + mmu_get_ptr_table_entry %a0,%d0 + + /* The pointer table entry must not no be busy + */ + tstl %a0@ + jne L(mmu_map_error) + + /* Do the mapping and advance the pointers + */ + dputs "early term2" + dputn %a2 + dputn %a3 + dputc '\n' + movel %a2,%a0@ + + addl #PAGE_TABLE_SIZE*PAGESIZE,%a2 + addl #PAGE_TABLE_SIZE*PAGESIZE,%a3 + +L(mmu_mapnext_030): + /* Ready with mapping? + */ + lea %a3@(-1),%a0 + cmpl %a0,%a4 + jhi L(mmu_map_030) + jra L(mmu_map_done) + +L(mmu_map_error): + + dputs "mmu_map error:" + dputn %a2 + dputn %a3 + dputc '\n' + +L(mmu_map_done): + +func_return mmu_map + +/* + * mmu_fixup + * + * On the 040 class machines, all pages that are used for the + * mmu have to be fixed up. + */ + +func_start mmu_fixup_page_mmu_cache,%d0/%a0 + + dputs "mmu_fixup_page_mmu_cache" + dputn ARG1 + + /* Calculate the offset into the root table + */ + movel ARG1,%d0 + moveq #ROOT_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + mmu_get_root_table_entry %d0 + + /* Calculate the offset into the pointer table + */ + movel ARG1,%d0 + moveq #PTR_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + andl #PTR_TABLE_SIZE-1,%d0 + mmu_get_ptr_table_entry %a0,%d0 + + /* Calculate the offset into the page table + */ + movel ARG1,%d0 + moveq #PAGE_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + andl #PAGE_TABLE_SIZE-1,%d0 + mmu_get_page_table_entry %a0,%d0 + + movel %a0@,%d0 + andil #_CACHEMASK040,%d0 + orl %pc@(SYMBOL_NAME(m68k_pgtable_cachemode)),%d0 + movel %d0,%a0@ + + dputc '\n' + +func_return mmu_fixup_page_mmu_cache + +/* + * mmu_temp_map + * + * create a temporary mapping to enable the mmu, + * this we don't need any transparation translation tricks. + */ + +func_start mmu_temp_map,%d0/%d1/%a0/%a1 + + dputs "mmu_temp_map" + dputn ARG1 + dputn ARG2 + dputc '\n' + + lea %pc@(L(temp_mmap_mem)),%a1 + + /* Calculate the offset in the root table + */ + movel ARG2,%d0 + moveq #ROOT_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + mmu_get_root_table_entry %d0 + + /* Check if the table is temporary allocated, so we have to reuse it + */ + movel %a0@,%d0 + cmpl %pc@(L(memory_start)),%d0 + jcc 1f + + /* Temporary allocate a ptr table and insert it into the root table + */ + movel %a1@,%d0 + addl #PTR_TABLE_SIZE*4,%a1@ + orw #_PAGE_TABLE+_PAGE_ACCESSED,%d0 + movel %d0,%a0@ + dputs " (new)" +1: + dputn %d0 + /* Mask the root table entry for the ptr table + */ + andw #-ROOT_TABLE_SIZE,%d0 + movel %d0,%a0 + + /* Calculate the offset into the pointer table + */ + movel ARG2,%d0 + moveq #PTR_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + andl #PTR_TABLE_SIZE-1,%d0 + lea %a0@(%d0*4),%a0 + dputn %a0 + + /* Check if a temporary page table is already allocated + */ + movel %a0@,%d0 + jne 1f + + /* Temporary allocate a page table and insert it into the ptr table + */ + movel %a1@,%d0 + addl #PTR_TABLE_SIZE*4,%a1@ + orw #_PAGE_TABLE+_PAGE_ACCESSED,%d0 + movel %d0,%a0@ + dputs " (new)" +1: + dputn %d0 + /* Mask the ptr table entry for the page table + */ + andw #-PTR_TABLE_SIZE,%d0 + movel %d0,%a0 + + /* Calculate the offset into the page table + */ + movel ARG2,%d0 + moveq #PAGE_INDEX_SHIFT,%d1 + lsrl %d1,%d0 + andl #PAGE_TABLE_SIZE-1,%d0 + lea %a0@(%d0*4),%a0 + dputn %a0 + + /* Insert the address into the page table + */ + movel ARG1,%d0 + andw #-PAGESIZE,%d0 + orw #_PAGE_PRESENT+_PAGE_ACCESSED+_PAGE_DIRTY,%d0 + movel %d0,%a0@ + dputn %d0 + + dputc '\n' + +func_return mmu_temp_map + +func_start mmu_engage,%d0-%d2/%a0-%a3 + + moveq #ROOT_TABLE_SIZE-1,%d0 + /* Temporarily use a different root table. */ + lea %pc@(L(kernel_pgdir_ptr)),%a0 + movel %a0@,%a2 + movel %pc@(L(memory_start)),%a1 + movel %a1,%a0@ + movel %a2,%a0 +1: + movel %a0@+,%a1@+ + dbra %d0,1b + + lea %pc@(L(temp_mmap_mem)),%a0 + movel %a1,%a0@ + + movew #PAGESIZE-1,%d0 +1: + clrl %a1@+ + dbra %d0,1b + + lea %pc@(1b),%a0 + movel #1b,%a1 + /* Skip temp mappings if phys == virt */ + cmpl %a0,%a1 + jeq 1f + + mmu_temp_map %a0,%a0 + mmu_temp_map %a0,%a1 + + addw #PAGESIZE,%a0 + addw #PAGESIZE,%a1 + mmu_temp_map %a0,%a0 + mmu_temp_map %a0,%a1 +1: + movel %pc@(L(memory_start)),%a3 + movel %pc@(L(phys_kernel_start)),%d2 + + is_not_040_or_060(L(mmu_engage_030)) + +L(mmu_engage_040): + .chip 68040 + nop + cinva %bc + nop + pflusha + nop + movec %a3,%srp + movel #TC_ENABLE+TC_PAGE4K,%d0 + movec %d0,%tc /* enable the MMU */ + jmp 1f:l +1: nop + movec %a2,%srp + nop + cinva %bc + nop + pflusha + .chip 68k + jra L(mmu_engage_cleanup) + +L(mmu_engage_030_temp): + .space 12 +L(mmu_engage_030): + .chip 68030 + lea %pc@(L(mmu_engage_030_temp)),%a0 + movel #0x80000002,%a0@ + movel %a3,%a0@(4) + movel #0x0808,%d0 + movec %d0,%cacr + pmove %a0@,%srp + pflusha + /* + * enable,super root enable,4096 byte pages,7 bit root index, + * 7 bit pointer index, 6 bit page table index. + */ + movel #0x82c07760,%a0@(8) + pmove %a0@(8),%tc /* enable the MMU */ + jmp 1f:l +1: movel %a2,%a0@(4) + movel #0x0808,%d0 + movec %d0,%cacr + pmove %a0@,%srp + pflusha + .chip 68k + +L(mmu_engage_cleanup): + subl %d2,%a2 + movel %a2,L(kernel_pgdir_ptr) + subl %d2,%fp + subl %d2,%sp + subl %d2,ARG0 + subl %d2,L(memory_start) + +func_return mmu_engage + +func_start mmu_get_root_table_entry,%d0/%a1 + +#if 0 + dputs "mmu_get_root_table_entry:" + dputn ARG1 + dputs " =" +#endif + + movel %pc@(L(kernel_pgdir_ptr)),%a0 + tstl %a0 + jne 2f + + dputs "\nmmu_init:" + + /* Find the start of free memory, get_bi_record does this for us, + * as the bootinfo structure is located directly behind the kernel + * and and we simply search for the last entry. + */ + get_bi_record BI_LAST + addw #PAGESIZE-1,%a0 + movel %a0,%d0 + andw #-PAGESIZE,%d0 + + dputn %d0 + + lea %pc@(L(memory_start)),%a0 + movel %d0,%a0@ + lea %pc@(L(kernel_end)),%a0 + movel %d0,%a0@ + + /* we have to return the first page at _stext since the init code + * in mm/init.c simply expects kernel_pg_dir there, the rest of + * page is used for further ptr tables in get_ptr_table. + */ + lea %pc@(SYMBOL_NAME(_stext)),%a0 + lea %pc@(L(mmu_cached_pointer_tables)),%a1 + movel %a0,%a1@ + addl #ROOT_TABLE_SIZE*4,%a1@ + + lea %pc@(L(mmu_num_pointer_tables)),%a1 + addql #1,%a1@ + + /* clear the page + */ + movel %a0,%a1 + movew #PAGESIZE/4-1,%d0 +1: + clrl %a1@+ + dbra %d0,1b + + lea %pc@(L(kernel_pgdir_ptr)),%a1 + movel %a0,%a1@ + + dputn %a0 + dputc '\n' +2: + movel ARG1,%d0 + lea %a0@(%d0*4),%a0 + +#if 0 + dputn %a0 + dputc '\n' +#endif + +func_return mmu_get_root_table_entry + + + +func_start mmu_get_ptr_table_entry,%d0/%a1 + +#if 0 + dputs "mmu_get_ptr_table_entry:" + dputn ARG1 + dputn ARG2 + dputs " =" +#endif + + movel ARG1,%a0 + movel %a0@,%d0 + jne 2f + + /* Keep track of the number of pointer tables we use + */ + dputs "\nmmu_get_new_ptr_table:" + lea %pc@(L(mmu_num_pointer_tables)),%a0 + movel %a0@,%d0 + addql #1,%a0@ + + /* See if there is a free pointer table in our cache of pointer tables + */ + lea %pc@(L(mmu_cached_pointer_tables)),%a1 + andw #7,%d0 + jne 1f + + /* Get a new pointer table page from above the kernel memory + */ + get_new_page + movel %a0,%a1@ +1: + /* There is an unused pointer table in our cache... use it + */ + movel %a1@,%d0 + addl #PTR_TABLE_SIZE*4,%a1@ + + dputn %d0 + dputc '\n' + + /* Insert the new pointer table into the root table + */ + movel ARG1,%a0 + orw #_PAGE_TABLE+_PAGE_ACCESSED,%d0 + movel %d0,%a0@ +2: + /* Extract the pointer table entry + */ + andw #-PTR_TABLE_SIZE,%d0 + movel %d0,%a0 + movel ARG2,%d0 + lea %a0@(%d0*4),%a0 + +#if 0 + dputn %a0 + dputc '\n' +#endif + +func_return mmu_get_ptr_table_entry + + +func_start mmu_get_page_table_entry,%d0/%a1 + +#if 0 + dputs "mmu_get_page_table_entry:" + dputn ARG1 + dputn ARG2 + dputs " =" +#endif + + movel ARG1,%a0 + movel %a0@,%d0 + jne 2f + + /* If the page table entry doesn't exist, we allocate a complete new + * page and use it as one continues big page table which can cover + * 4MB of memory, nearly almost all mappings have that alignment. + */ + get_new_page + addw #_PAGE_TABLE+_PAGE_ACCESSED,%a0 + + /* align pointer table entry for a page of page tables + */ + movel ARG1,%d0 + andw #-(PAGESIZE/PAGE_TABLE_SIZE),%d0 + movel %d0,%a1 + + /* Insert the page tables into the pointer entries + */ + moveq #PAGESIZE/PAGE_TABLE_SIZE/4-1,%d0 +1: + movel %a0,%a1@+ + lea %a0@(PAGE_TABLE_SIZE*4),%a0 + dbra %d0,1b + + /* Now we can get the initialized pointer table entry + */ + movel ARG1,%a0 + movel %a0@,%d0 +2: + /* Extract the page table entry + */ + andw #-PAGE_TABLE_SIZE,%d0 + movel %d0,%a0 + movel ARG2,%d0 + lea %a0@(%d0*4),%a0 + +#if 0 + dputn %a0 + dputc '\n' +#endif + +func_return mmu_get_page_table_entry + +/* + * get_new_page + * + * Return a new page from the memory start and clear it. + */ +func_start get_new_page,%d0/%a1 + + dputs "\nget_new_page:" + + /* allocate the page and adjust memory_start + */ + lea %pc@(L(memory_start)),%a0 + movel %a0@,%a1 + addl #PAGESIZE,%a0@ + + /* clear the new page + */ + movel %a1,%a0 + movew #PAGESIZE/4-1,%d0 +1: + clrl %a1@+ + dbra %d0,1b + + dputn %a0 + dputc '\n' + +func_return get_new_page + + + /* * Debug output support * Atarians have a choice between the parallel port, the serial port * from the MFP or a serial port of the SCC */ +#ifdef CONFIG_MAC + +L(scc_initable_mac): + .byte 9,12 /* Reset */ + .byte 4,0x44 /* x16, 1 stopbit, no parity */ + .byte 3,0xc0 /* receiver: 8 bpc */ + .byte 5,0xe2 /* transmitter: 8 bpc, assert dtr/rts */ + .byte 9,0 /* no interrupts */ + .byte 10,0 /* NRZ */ + .byte 11,0x50 /* use baud rate generator */ + .byte 12,10,13,0 /* 9600 baud */ + .byte 14,1 /* Baud rate generator enable */ + .byte 3,0xc1 /* enable receiver */ + .byte 5,0xea /* enable transmitter */ + .byte -1 + .even +#endif + #ifdef CONFIG_ATARI /* #define USE_PRINTER */ -/* #define USE_SCC */ +/* #define USE_SCC_B */ +/* #define USE_SCC_A */ #define USE_MFP +#if defined(USE_SCC_A) || defined(USE_SCC_B) +#define USE_SCC +/* Initialisation table for SCC */ +L(scc_initable): + .byte 9,12 /* Reset */ + .byte 4,0x44 /* x16, 1 stopbit, no parity */ + .byte 3,0xc0 /* receiver: 8 bpc */ + .byte 5,0xe2 /* transmitter: 8 bpc, assert dtr/rts */ + .byte 9,0 /* no interrupts */ + .byte 10,0 /* NRZ */ + .byte 11,0x50 /* use baud rate generator */ + .byte 12,24,13,0 /* 9600 baud */ + .byte 14,2,14,3 /* use master clock for BRG, enable */ + .byte 3,0xc1 /* enable receiver */ + .byte 5,0xea /* enable transmitter */ + .byte -1 + .even +#endif + #ifdef USE_PRINTER LPSG_SELECT = 0xff8800 @@ -1129,13 +2554,18 @@ LSTMFP_GPIP = 0xfffa01 LSTMFP_DDR = 0xfffa05 LSTMFP_IERB = 0xfffa09 -#elif defined(USE_SCC) - -LSCC_CTRL_B = 0xff8c85 -LSCC_DATA_B = 0xff8c87 +#elif defined(USE_SCC_B) + +LSCC_CTRL = 0xff8c85 +LSCC_DATA = 0xff8c87 + +#elif defined(USE_SCC_A) + +LSCC_CTRL = 0xff8c81 +LSCC_DATA = 0xff8c83 /* Initialisation table for SCC */ -scc_initable: +L(scc_initable): .byte 9,12 /* Reset */ .byte 4,0x44 /* x16, 1 stopbit, no parity */ .byte 3,0xc0 /* receiver: 8 bpc */ @@ -1159,45 +2589,48 @@ LMFP_TSR = 0xfffa2d LMFP_UDR = 0xfffa2f #endif -#endif - -#if defined (CONFIG_BVME6000) -BVME_SCC_CTRL_A = 0xffb0000b -BVME_SCC_DATA_A = 0xffb0000f -#endif +#endif /* CONFIG_ATARI */ /* * Serial port output support. */ -LSERPER = 0xdff032 -LSERDAT = 0xdff030 -LSERDATR = 0xdff018 -LSERIAL_CNTRL = 0xbfd000 -LSERIAL_DTR = 7 /* * Initialize serial port hardware for 9600/8/1 - * a0 thrashed - * Amiga d0 trashed - * Atari d0 trashed (a1 in case of SCC) */ - .even -Lserial_init: +func_start serial_init,%d0/%d1/%a0/%a1 + /* + * Some of the register usage that follows + * CONFIG_AMIGA + * a0 = pointer to boot info record + * d0 = boot info offset + * CONFIG_ATARI + * a0 = address of SCC + * a1 = Liobase address/address of scc_initable + * d0 = init data for serial port + * CONFIG_MAC + * a0 = address of SCC + * a1 = address of scc_initable_mac + * d0 = init data for serial port + */ + #ifdef CONFIG_AMIGA - cmpil #MACH_AMIGA,%d4 - jne 1f - bclr #LSERIAL_DTR,LSERIAL_CNTRL - movew #BI_AMIGA_SERPER,%d0 - jbsr Lget_bi_record - movew %a0@,LSERPER - jra 9f +#define SERIAL_DTR 7 +#define SERIAL_CNTRL CIABBASE+C_PRA + + is_not_amiga(1f) + lea %pc@(L(custom)),%a0 + movel #-ZTWOBASE,%a0@ + bclr #SERIAL_DTR,SERIAL_CNTRL-ZTWOBASE + get_bi_record BI_AMIGA_SERPER + movew %a0@,CUSTOMBASE+C_SERPER-ZTWOBASE +| movew #61,CUSTOMBASE+C_SERPER-ZTWOBASE 1: #endif #ifdef CONFIG_ATARI - cmpil #MACH_ATARI,%d4 - jne 4f - movel %pc@(Liobase),%a1 -#ifdef USE_PRINTER + is_not_atari(4f) + movel %pc@(L(iobase)),%a1 +#if defined(USE_PRINTER) bclr #0,%a1@(LSTMFP_IERB) bclr #0,%a1@(LSTMFP_DDR) moveb #LPSG_CONTROL,%a1@(LPSG_SELECT) @@ -1209,8 +2642,8 @@ Lserial_init: bset #5,%d0 moveb %d0,%a1@(LPSG_WRITE) #elif defined(USE_SCC) - lea %a1@(LSCC_CTRL_B),%a0 - lea %pc@(scc_initable:w),%a1 + lea %a1@(LSCC_CTRL),%a0 + lea %pc@(L(scc_initable)),%a1 2: moveb %a1@+,%d0 jmi 3f moveb %d0,%a0@ @@ -1225,174 +2658,854 @@ Lserial_init: orb #1,%a1@(LMFP_TDCDR) bset #1,%a1@(LMFP_TSR) #endif + jra L(serial_init_done) 4: #endif -9: - rts - -#ifdef CONFIG_HP300 -/* Set LEDs to %d7 */ - .even -Lset_leds: - moveml %a0/%a1,%sp@- - movel %pc@(Lcustom),%a1 - moveb %d7,%a1@(0x1ffff) - moveml %sp@+,%a0/%a1 - rts +#ifdef CONFIG_MAC + is_not_mac(L(serial_init_not_mac)) +#ifdef MAC_SERIAL_DEBUG +#if !defined(MAC_USE_SCC_A) && !defined(MAC_USE_SCC_B) +#define MAC_USE_SCC_B #endif - +#define mac_scc_cha_b_ctrl_offset 0x0 +#define mac_scc_cha_a_ctrl_offset 0x2 +#define mac_scc_cha_b_data_offset 0x4 +#define mac_scc_cha_a_data_offset 0x6 + +#ifdef MAC_USE_SCC_A + /* Initialize channel A */ + movel %pc@(L(mac_sccbase)),%a0 + lea %pc@(L(scc_initable_mac)),%a1 +5: moveb %a1@+,%d0 + jmi 6f + moveb %d0,%a0@(mac_scc_cha_a_ctrl_offset) + moveb %a1@+,%a0@(mac_scc_cha_a_ctrl_offset) + jra 5b +6: +#endif /* MAC_USE_SCC_A */ + +#ifdef MAC_USE_SCC_B + /* Initialize channel B */ +#ifndef MAC_USE_SCC_A /* Load mac_sccbase only if needed */ + movel %pc@(L(mac_sccbase)),%a0 +#endif /* MAC_USE_SCC_A */ + lea %pc@(L(scc_initable_mac)),%a1 +7: moveb %a1@+,%d0 + jmi 8f + moveb %d0,%a0@(mac_scc_cha_b_ctrl_offset) + moveb %a1@+,%a0@(mac_scc_cha_b_ctrl_offset) + jra 7b +8: +#endif /* MAC_USE_SCC_B */ +#endif /* MAC_SERIAL_DEBUG */ + + jra L(serial_init_done) +L(serial_init_not_mac): +#endif /* CONFIG_MAC */ + +L(serial_init_done): +func_return serial_init + /* - * Output character in d7 on serial port. - * d7 thrashed. + * Output character on serial port. */ -Lserial_putc: - moveml %a0/%a1,%sp@- -#if defined(CONFIG_MVME16x) - cmpil #MACH_MVME16x,%d4 - jne 2f - moveb %d7,%sp@- - .long 0x4e4f0020 - jra 9f -2: -#endif -#ifdef CONFIG_BVME6000 - cmpil #MACH_BVME6000,%d4 - jne 2f -1: btst #2,BVME_SCC_CTRL_A - jeq 1b - moveb %d7,BVME_SCC_DATA_A - jra 9f -2: -#endif +func_start serial_putc,%d0/%d1/%a0/%a1 + + movel ARG1,%d0 + cmpib #'\n',%d0 + jbne 1f + + /* A little safe recursion is good for the soul */ + serial_putc #'\r' +1: + #ifdef CONFIG_AMIGA - cmpil #MACH_AMIGA,%d4 - jne 2f - andw #0x00ff,%d7 - oriw #0x0100,%d7 - movel %pc@(Lcustom),%a1 - movew %d7,%a1@(LSERDAT) -1: movew %a1@(LSERDATR),%d7 - andw #0x2000,%d7 + is_not_amiga(2f) + andw #0x00ff,%d0 + oriw #0x0100,%d0 + movel %pc@(L(custom)),%a0 + movew %d0,%a0@(CUSTOMBASE+C_SERDAT) +1: movew %a0@(CUSTOMBASE+C_SERDATR),%d0 + andw #0x2000,%d0 jeq 1b - jra 9f + jra L(serial_putc_done) 2: #endif + +#ifdef CONFIG_MAC + is_not_mac(5f) + +#ifdef CONSOLE + console_putc %d0 +#endif /* CONSOLE */ + +#ifdef MAC_SERIAL_DEBUG + +#ifdef MAC_USE_SCC_A + movel %pc@(L(mac_sccbase)),%a1 +3: btst #2,%a1@(mac_scc_cha_a_ctrl_offset) + jeq 3b + moveb %d0,%a1@(mac_scc_cha_a_data_offset) +#endif /* MAC_USE_SCC_A */ + +#ifdef MAC_USE_SCC_B +#ifndef MAC_USE_SCC_A /* Load mac_sccbase only if needed */ + movel %pc@(L(mac_sccbase)),%a1 +#endif /* MAC_USE_SCC_A */ +4: btst #2,%a1@(mac_scc_cha_b_ctrl_offset) + jeq 4b + moveb %d0,%a1@(mac_scc_cha_b_data_offset) +#endif /* MAC_USE_SCC_B */ + +#endif /* MAC_SERIAL_DEBUG */ + + jra L(serial_putc_done) +5: +#endif /* CONFIG_MAC */ + #ifdef CONFIG_ATARI - cmpil #MACH_ATARI,%d4 - jne 4f - movel %pc@(Liobase),%a1 -#ifdef USE_PRINTER + is_not_atari(4f) + movel %pc@(L(iobase)),%a1 +#if defined(USE_PRINTER) 3: btst #0,%a1@(LSTMFP_GPIP) jne 3b moveb #LPSG_IO_B,%a1@(LPSG_SELECT) - moveb %d7,%a1@(LPSG_WRITE) + moveb %d0,%a1@(LPSG_WRITE) moveb #LPSG_IO_A,%a1@(LPSG_SELECT) - moveb %a1@(LPSG_READ),%d7 - bclr #5,%d7 - moveb %d7,%a1@(LPSG_WRITE) + moveb %a1@(LPSG_READ),%d0 + bclr #5,%d0 + moveb %d0,%a1@(LPSG_WRITE) nop nop - bset #5,%d7 - moveb %d7,%a1@(LPSG_WRITE) + bset #5,%d0 + moveb %d0,%a1@(LPSG_WRITE) #elif defined(USE_SCC) -3: btst #2,%a1@(LSCC_CTRL_B) +3: btst #2,%a1@(LSCC_CTRL) jeq 3b - moveb %d7,%a1@(LSCC_DATA_B) + moveb %d0,%a1@(LSCC_DATA) #elif defined(USE_MFP) 3: btst #7,%a1@(LMFP_TSR) jeq 3b - moveb %d7,%a1@(LMFP_UDR) + moveb %d0,%a1@(LMFP_UDR) #endif + jra L(serial_putc_done) 4: +#endif /* CONFIG_ATARI */ + +#ifdef CONFIG_MVME16x + is_not_mvme16x(2f) + /* + * The VME 16x class has PROM support for serial output + * of some kind; the TRAP table is still valid. + */ + moveml %d0-%d7/%a2-%a6,%sp@- + moveb %d0,%sp@- + trap #15 + .word 0x0020 /* TRAP 0x020 */ + moveml %sp@+,%d0-%d7/%a2-%a6 + jbra L(serial_putc_done) +2: +#endif CONFIG_MVME162 | CONFIG_MVME167 + +#ifdef CONFIG_BVME6000 + is_not_bvme6000(2f) + /* + * The BVME6000 machine has a serial port ... + */ +1: btst #2,BVME_SCC_CTRL_A + jeq 1b + moveb %d0,BVME_SCC_DATA_A + jbra L(serial_putc_done) +2: +#endif + +L(serial_putc_done): +func_return serial_putc + +/* + * Output a string. + */ +func_start puts,%d0/%a0 + + movel ARG1,%a0 + jra 2f +1: +#ifdef CONSOLE + console_putc %d0 +#endif +#ifdef SERIAL_DEBUG + serial_putc %d0 +#endif +2: moveb %a0@+,%d0 + jne 1b + +func_return puts + +/* + * Output number in hex notation. + */ + +func_start putn,%d0-%d2 + + putc ' ' + + movel ARG1,%d0 + moveq #7,%d1 +1: roll #4,%d0 + move %d0,%d2 + andb #0x0f,%d2 + addb #'0',%d2 + cmpb #'9',%d2 + jls 2f + addb #'A'-('9'+1),%d2 +2: +#ifdef CONSOLE + console_putc %d2 +#endif +#ifdef SERIAL_DEBUG + serial_putc %d2 #endif -9: - moveml %sp@+,%a0/%a1 + dbra %d1,1b + +func_return putn + +#ifdef CONFIG_MAC +/* + * mac_serial_print + * + * This routine takes its parameters on the stack. It then + * turns around and calls the internal routine. This routine + * is used until the Linux console driver initializes itself. + * + * The calling parameters are: + * void mac_serial_print(const char *str); + * + * This routine does NOT understand variable arguments only + * simple strings! + */ +ENTRY(mac_serial_print) + moveml %d0/%a0,%sp@- +#if 1 + move %sr,%sp@- + ori #0x0700,%sr +#endif + movel %sp@(10),%a0 /* fetch parameter */ + jra 2f +1: serial_putc %d0 +2: moveb %a0@+,%d0 + jne 1b +#if 1 + move %sp@+,%sr +#endif + moveml %sp@+,%d0/%a0 rts +#endif /* CONFIG_MAC */ + +#ifdef CONFIG_HP300 +func_start set_leds,%d0/%a0 + movel ARG1,%d0 + movel %pc@(Lcustom),%a0 + moveb %d0,%a0@(0x1ffff) +func_return set_leds +#endif +#ifdef CONSOLE /* - * Output string pointed to by a0 to serial port. - * a0 trashed. + * For continuity, see the data alignment + * to which this structure is tied. */ -Lserial_puts: - movel %d7,%sp@- -1: moveb %a0@+,%d7 - jeq 2f - jbsr Lserial_putc - jra 1b -2: movel %sp@+,%d7 +#define Lconsole_struct_cur_column 0 +#define Lconsole_struct_cur_row 4 +#define Lconsole_struct_num_columns 8 +#define Lconsole_struct_num_rows 12 +#define Lconsole_struct_left_edge 16 +#define Lconsole_struct_penguin_putc 20 + +L(console_init): + /* + * Some of the register usage that follows + * a0 = pointer to boot_info + * a1 = pointer to screen + * a2 = pointer to Lconsole_globals + * d3 = pixel width of screen + * d4 = pixel height of screen + * (d3,d4) ~= (x,y) of a point just below + * and to the right of the screen + * NOT on the screen! + * d5 = number of bytes per scan line + * d6 = number of bytes on the entire screen + */ + moveml %a0-%a4/%d0-%d7,%sp@- + + lea %pc@(L(console_globals)),%a2 + lea %pc@(L(mac_videobase)),%a0 + movel %a0@,%a1 + lea %pc@(L(mac_rowbytes)),%a0 + movel %a0@,%d5 + lea %pc@(L(mac_dimensions)),%a0 + movel %a0@,%d3 /* -> low byte */ + movel %d3,%d4 + swap %d4 /* -> high byte */ + andl #0xffff,%d3 /* d3 = screen width in pixels */ + andl #0xffff,%d4 /* d4 = screen height in pixels */ + + movel %d5,%d6 + subl #20,%d6 + mulul %d4,%d6 /* scan line bytes x num scan lines */ + divul #8,%d6 /* we'll clear 8 bytes at a time */ + subq #1,%d6 + +console_clear_loop: + movel #0xffffffff,%a1@+ /* Mac_black */ + movel #0xffffffff,%a1@+ /* Mac_black */ + dbra %d6,console_clear_loop + + /* Calculate font size */ + +#if defined(FONT_8x8) + lea %pc@(SYMBOL_NAME(font_vga_8x8)), %a0 +#elif defined(FONT_8x16) + lea %pc@(SYMBOL_NAME(font_vga_8x16)),%a0 +#elif defined(FONT_6x11) + lea %pc@(SYMBOL_NAME(font_vga_6x11)),%a0 +#else /* (FONT_8x8) default */ + lea %pc@(SYMBOL_NAME(font_vga_8x8)), %a0 +#endif + + /* + * At this point we make a shift in register usage + * a1 = address of Lconsole_font pointer + */ + lea %pc@(L(console_font)),%a1 + movel %a0,%a1@ /* store pointer to struct fbcon_font_desc in Lconsole_font */ + + /* + * Calculate global maxs + * Note - we can use either an + * 8 x 16 or 8 x 8 character font + * 6 x 11 also supported + */ + /* ASSERT: a0 = contents of Lconsole_font */ + movel %d3,%d0 /* screen width in pixels */ + divul %a0@(FBCON_FONT_DESC_WIDTH),%d0 /* d0 = max num chars per row */ + + movel %d4,%d1 /* screen height in pixels */ + divul %a0@(FBCON_FONT_DESC_HEIGHT),%d1 /* d1 = max num rows */ + + movel %d0,%a2@(Lconsole_struct_num_columns) + movel %d1,%a2@(Lconsole_struct_num_rows) + + /* + * Clear the current row and column + */ + clrl %a2@(Lconsole_struct_cur_column) + clrl %a2@(Lconsole_struct_cur_row) + clrl %a2@(Lconsole_struct_left_edge) + + /* + * Initialization is complete + */ + moveml %sp@+,%a0-%a4/%d0-%d7 + rts + +L(console_put_stats): + /* + * Some of the register usage that follows + * a0 = pointer to boot_info + * d7 = value of boot_info fields + */ + moveml %a0/%d7,%sp@- + + puts "\nMacLinux\n\n" + +#ifdef SERIAL_DEBUG + puts " vidaddr:" + putn %pc@(L(mac_videobase)) /* video addr. */ + + puts "\n _stext:" + lea %pc@(SYMBOL_NAME(_stext)),%a0 + putn %a0 + + puts "\nbootinfo:" + lea %pc@(SYMBOL_NAME(_end)),%a0 + putn %a0 + + puts "\ncpuid:" + putn %pc@(L(cputype)) + putc '\n' + +# if defined(MMU_PRINT) + jbsr mmu_print_machine_cpu_types +# endif /* MMU_PRINT */ +#endif /* SERIAL_DEBUG */ + + moveml %sp@+,%a0/%d7 + rts + +#ifdef CONSOLE_PENGUIN +L(console_put_penguin): + /* + * Get 'that_penguin' onto the screen in the upper right corner + * penguin is 64 x 74 pixels, align against right edge of screen + */ + moveml %a0-%a1/%d0-%d7,%sp@- + + lea %pc@(L(mac_dimensions)),%a0 + movel %a0@,%d0 + andil #0xffff,%d0 + subil #64,%d0 /* snug up against the right edge */ + clrl %d1 /* start at the top */ + movel #73,%d7 + lea %pc@(SYMBOL_NAME(that_penguin)),%a1 +console_penguin_row: + movel #31,%d6 +console_penguin_pixel_pair: + moveb %a1@,%d2 + lsrb #4,%d2 + jbsr console_plot_pixel + addq #1,%d0 + moveb %a1@+,%d2 + jbsr console_plot_pixel + addq #1,%d0 + dbra %d6,console_penguin_pixel_pair + + subil #64,%d0 + addq #1,%d1 + dbra %d7,console_penguin_row + + moveml %sp@+,%a0-%a1/%d0-%d7 + rts +#endif + +console_scroll: + moveml %a0-%a4/%d0-%d7,%sp@- + + /* + * Calculate source and destination addresses + * output a1 = dest + * a2 = source + */ + lea %pc@(L(mac_videobase)),%a0 + movel %a0@,%a1 + movel %a1,%a2 + lea %pc@(L(mac_rowbytes)),%a0 + movel %a0@,%d5 + movel %pc@(L(console_font)),%a0 + mulul %a0@(FBCON_FONT_DESC_HEIGHT),%d5 /* account for # scan lines per character */ + addal %d5,%a2 + + /* + * Get dimensions + */ + lea %pc@(L(mac_dimensions)),%a0 + movel %a0@,%d3 + movel %d3,%d4 + swap %d4 + andl #0xffff,%d3 /* d3 = screen width in pixels */ + andl #0xffff,%d4 /* d4 = screen height in pixels */ + + /* + * Calculate number of bytes to move + */ + lea %pc@(L(mac_rowbytes)),%a0 + movel %a0@,%d6 + movel %pc@(L(console_font)),%a0 + subl %a0@(FBCON_FONT_DESC_HEIGHT),%d4 /* we're not scrolling the top row! */ + mulul %d4,%d6 /* scan line bytes x num scan lines */ + divul #32,%d6 /* we'll move 8 longs at a time */ + subq #1,%d6 + +console_scroll_loop: + movel %a2@+,%a1@+ + movel %a2@+,%a1@+ + movel %a2@+,%a1@+ + movel %a2@+,%a1@+ + movel %a2@+,%a1@+ + movel %a2@+,%a1@+ + movel %a2@+,%a1@+ + movel %a2@+,%a1@+ + dbra %d6,console_scroll_loop + + lea %pc@(L(mac_rowbytes)),%a0 + movel %a0@,%d6 + movel %pc@(L(console_font)),%a0 + mulul %a0@(FBCON_FONT_DESC_HEIGHT),%d6 /* scan line bytes x font height */ + divul #32,%d6 /* we'll move 8 words at a time */ + subq #1,%d6 + + moveq #-1,%d0 +console_scroll_clear_loop: + movel %d0,%a1@+ + movel %d0,%a1@+ + movel %d0,%a1@+ + movel %d0,%a1@+ + movel %d0,%a1@+ + movel %d0,%a1@+ + movel %d0,%a1@+ + movel %d0,%a1@+ + dbra %d6,console_scroll_clear_loop + + moveml %sp@+,%a0-%a4/%d0-%d7 rts + +func_start console_putc,%a0/%a1/%d0-%d7 + + is_not_mac(console_exit) + + /* Output character in d7 on console. + */ + movel ARG1,%d7 + cmpib #'\n',%d7 + jbne 1f + + /* A little safe recursion is good for the soul */ + console_putc #'\r' +1: + lea %pc@(L(console_globals)),%a0 + + cmpib #10,%d7 + jne console_not_lf + movel %a0@(Lconsole_struct_cur_row),%d0 + addil #1,%d0 + movel %d0,%a0@(Lconsole_struct_cur_row) + movel %a0@(Lconsole_struct_num_rows),%d1 + cmpl %d1,%d0 + jcs 1f + subil #1,%d0 + movel %d0,%a0@(Lconsole_struct_cur_row) + jbsr console_scroll +1: + jra console_exit + +console_not_lf: + cmpib #13,%d7 + jne console_not_cr + clrl %a0@(Lconsole_struct_cur_column) + jra console_exit + +console_not_cr: + cmpib #1,%d7 + jne console_not_home + clrl %a0@(Lconsole_struct_cur_row) + clrl %a0@(Lconsole_struct_cur_column) + jra console_exit + /* - * Output number in d7 in hex notation on serial port. + * At this point we know that the %d7 character is going to be + * rendered on the screen. Register usage is - + * a0 = pointer to console globals + * a1 = font data + * d0 = cursor column + * d1 = cursor row to draw the character + * d7 = character number */ +console_not_home: + movel %a0@(Lconsole_struct_cur_column),%d0 + addil #1,%a0@(Lconsole_struct_cur_column) + movel %a0@(Lconsole_struct_num_columns),%d1 + cmpl %d1,%d0 + jcs 1f + putc '\n' /* recursion is OK! */ +1: + movel %a0@(Lconsole_struct_cur_row),%d1 -Lserial_putnum: - moveml %d0-%d2/%d7,%sp@- - movel %d7,%d1 - moveq #4,%d0 - moveq #7,%d2 -L1: roll %d0,%d1 - moveb %d1,%d7 - andb #0x0f,%d7 - cmpb #0x0a,%d7 - jcc 1f - addb #'0',%d7 - jra 2f -1: addb #'A'-10,%d7 -2: jbsr Lserial_putc - dbra %d2,L1 - moveq #32,%d7 - jbsr Lserial_putc - moveml %sp@+,%d0-%d2/%d7 + /* + * At this point we make a shift in register usage + * a0 = address of pointer to font data (fbcon_font_desc) + */ + movel %pc@(L(console_font)),%a0 + movel %a0@(FBCON_FONT_DESC_DATA),%a1 /* Load fbcon_font_desc.data into a1 */ + andl #0x000000ff,%d7 + /* ASSERT: a0 = contents of Lconsole_font */ + mulul %a0@(FBCON_FONT_DESC_HEIGHT),%d7 /* d7 = index into font data */ + addl %d7,%a1 /* a1 = points to char image */ + + /* + * At this point we make a shift in register usage + * d0 = pixel coordinate, x + * d1 = pixel coordinate, y + * d2 = (bit 0) 1/0 for white/black (!) pixel on screen + * d3 = font scan line data (8 pixels) + * d6 = count down for the font's pixel width (8) + * d7 = count down for the font's pixel count in height + */ + /* ASSERT: a0 = contents of Lconsole_font */ + mulul %a0@(FBCON_FONT_DESC_WIDTH),%d0 + mulul %a0@(FBCON_FONT_DESC_HEIGHT),%d1 + movel %a0@(FBCON_FONT_DESC_HEIGHT),%d7 /* Load fbcon_font_desc.height into d7 */ + subq #1,%d7 +console_read_char_scanline: + moveb %a1@+,%d3 + + /* ASSERT: a0 = contents of Lconsole_font */ + movel %a0@(FBCON_FONT_DESC_WIDTH),%d6 /* Load fbcon_font_desc.width into d6 */ + subql #1,%d6 + +console_do_font_scanline: + lslb #1,%d3 + scsb %d2 /* convert 1 bit into a byte */ + jbsr console_plot_pixel + addq #1,%d0 + dbra %d6,console_do_font_scanline + + /* ASSERT: a0 = contents of Lconsole_font */ + subl %a0@(FBCON_FONT_DESC_WIDTH),%d0 + addq #1,%d1 + dbra %d7,console_read_char_scanline + +console_exit: + +func_return console_putc + +console_plot_pixel: + /* + * Input: + * d0 = x coordinate + * d1 = y coordinate + * d2 = (bit 0) 1/0 for white/black (!) + * All registers are preserved + */ + moveml %a0-%a1/%d0-%d4,%sp@- + + lea %pc@(L(mac_videobase)),%a0 + movel %a0@,%a1 + lea %pc@(L(mac_videodepth)),%a0 + movel %a0@,%d3 + lea %pc@(L(mac_rowbytes)),%a0 + mulul %a0@,%d1 + + /* + * Register usage: + * d0 = x coord becomes byte offset into frame buffer + * d1 = y coord + * d2 = black or white (0/1) + * d3 = video depth + * d4 = temp of x (d0) for many bit depths + * d5 = unused + * d6 = unused + * d7 = unused + */ +test_1bit: + cmpb #1,%d3 + jbne test_2bit + movel %d0,%d4 /* we need the low order 3 bits! */ + divul #8,%d0 + addal %d0,%a1 + addal %d1,%a1 + andb #7,%d4 + eorb #7,%d4 /* reverse the x-coordinate w/ screen-bit # */ + andb #1,%d2 + jbne white_1 + bsetb %d4,%a1@ + jbra console_plot_pixel_exit +white_1: + bclrb %d4,%a1@ + jbra console_plot_pixel_exit + +test_2bit: + cmpb #2,%d3 + jbne test_4bit + movel %d0,%d4 /* we need the low order 2 bits! */ + divul #4,%d0 + addal %d0,%a1 + addal %d1,%a1 + andb #3,%d4 + eorb #3,%d4 /* reverse the x-coordinate w/ screen-bit # */ + lsll #1,%d4 /* ! */ + andb #1,%d2 + jbne white_2 + bsetb %d4,%a1@ + addq #1,%d4 + bsetb %d4,%a1@ + jbra console_plot_pixel_exit +white_2: + bclrb %d4,%a1@ + addq #1,%d4 + bclrb %d4,%a1@ + jbra console_plot_pixel_exit + +test_4bit: + cmpb #4,%d3 + jbne test_8bit + movel %d0,%d4 /* we need the low order bit! */ + divul #2,%d0 + addal %d0,%a1 + addal %d1,%a1 + andb #1,%d4 + eorb #1,%d4 + lsll #2,%d4 /* ! */ + andb #1,%d2 + jbne white_4 + bsetb %d4,%a1@ + addq #1,%d4 + bsetb %d4,%a1@ + addq #1,%d4 + bsetb %d4,%a1@ + addq #1,%d4 + bsetb %d4,%a1@ + jbra console_plot_pixel_exit +white_4: + bclrb %d4,%a1@ + addq #1,%d4 + bclrb %d4,%a1@ + addq #1,%d4 + bclrb %d4,%a1@ + addq #1,%d4 + bclrb %d4,%a1@ + jbra console_plot_pixel_exit + +test_8bit: + cmpb #8,%d3 + jbne test_16bit + addal %d0,%a1 + addal %d1,%a1 + andb #1,%d2 + jbne white_8 + moveb #0xff,%a1@ + jbra console_plot_pixel_exit +white_8: + clrb %a1@ + jbra console_plot_pixel_exit + +test_16bit: + cmpb #16,%d3 + jbne console_plot_pixel_exit + addal %d0,%a1 + addal %d0,%a1 + addal %d1,%a1 + andb #1,%d2 + jbne white_16 + clrw %a1@ + jbra console_plot_pixel_exit +white_16: + movew #0x0fff,%a1@ + jbra console_plot_pixel_exit + +console_plot_pixel_exit: + moveml %sp@+,%a0-%a1/%d0-%d4 rts +#endif /* CONSOLE */ #if 0 -Lshowtest: +/* + * This is some old code lying around. I don't believe + * it's used or important anymore. My guess is it contributed + * to getting to this point, but it's done for now. + * It was still in the 2.1.77 head.S, so it's still here. + * (And still not used!) + */ +L(showtest): moveml %a0/%d7,%sp@- - putc('A') - putc('=') - putn(%a1) + puts "A=" + putn %a1 - ptestr #5,%a1@,#7,%a0 + .long 0xf0119f15 | ptestr #5,%a1@,#7,%a0 - putc('D') - putc('A') - putc('=') - putn(%a0) + puts "DA=" + putn %a0 - putc('D') - putc('=') - putn(%a0@) + puts "D=" + putn %a0@ - putc('S') - putc('=') - lea %pc@(Lmmu),%a0 - pmove %psr,%a0@ + puts "S=" + lea %pc@(L(mmu)),%a0 + .long 0xf0106200 | pmove %psr,%a0@ clrl %d7 movew %a0@,%d7 - jbsr Lserial_putnum + putn %d7 - putr() + putc '\n' moveml %sp@+,%a0/%d7 rts +#endif /* 0 */ + +__INITDATA + .align 4 + +#if defined(CONFIG_ATARI) || defined(CONFIG_AMIGA) || defined(CONFIG_HP300) +L(custom): +L(iobase): + .long 0 +#endif + +#ifdef CONFIG_MAC +L(console_video_virtual): + .long 0 +#endif /* CONFIG_MAC */ + +#if defined(CONSOLE) +L(console_globals): + .long 0 /* cursor column */ + .long 0 /* cursor row */ + .long 0 /* max num columns */ + .long 0 /* max num rows */ + .long 0 /* left edge */ + .long 0 /* mac putc */ +L(console_font): + .long 0 /* pointer to console font (struct fbcon_font_desc) */ +#endif /* CONSOLE */ + +#if defined(MMU_PRINT) +L(mmu_print_data): + .long 0 /* valid flag */ + .long 0 /* start logical */ + .long 0 /* next logical */ + .long 0 /* start physical */ + .long 0 /* next physical */ +#endif /* MMU_PRINT */ + +L(cputype): + .long 0 +L(mmu_cached_pointer_tables): + .long 0 +L(mmu_num_pointer_tables): + .long 0 +L(phys_kernel_start): + .long 0 +L(kernel_end): + .long 0 +L(memory_start): + .long 0 +L(kernel_pgdir_ptr): + .long 0 +L(temp_mmap_mem): + .long 0 + + +#if defined (CONFIG_BVME6000) +BVME_SCC_CTRL_A = 0xffb0000b +BVME_SCC_DATA_A = 0xffb0000f +#endif + +#if defined(CONFIG_MAC) +L(mac_booter_data): + .long 0 +L(mac_videobase): + .long 0 +L(mac_videodepth): + .long 0 +L(mac_dimensions): + .long 0 +L(mac_rowbytes): + .long 0 +#ifdef MAC_SERIAL_DEBUG +L(mac_sccbase): + .long 0 +#endif /* MAC_SERIAL_DEBUG */ #endif + +__FINIT .data - .even -Lcustom: -Liobase: - .long 0 -Lmmu: .quad 0 -SYMBOL_NAME_LABEL(kpt) - .long 0 + .align 4 + SYMBOL_NAME_LABEL(availmem) - .long 0 + .long 0 SYMBOL_NAME_LABEL(m68k_pgtable_cachemode) - .long 0 -#ifdef CONFIG_060_WRITETHROUGH + .long 0 SYMBOL_NAME_LABEL(m68k_supervisor_cachemode) - .long 0 -#endif + .long 0 #if defined(CONFIG_MVME16x) SYMBOL_NAME_LABEL(mvme_bdid_ptr) - .long 0 + .long 0 #endif diff --git a/arch/m68k/kernel/m68k_defs.c b/arch/m68k/kernel/m68k_defs.c index 122bf63be..e2e6715e7 100644 --- a/arch/m68k/kernel/m68k_defs.c +++ b/arch/m68k/kernel/m68k_defs.c @@ -10,14 +10,78 @@ #include <linux/stddef.h> #include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <asm/bootinfo.h> +#include <asm/irq.h> +#include <asm/amigahw.h> +#include <video/font.h> #define DEFINE(sym, val) \ - asm volatile("\n#define " #sym " %0" : : "i" (val)) + asm volatile("\n#define " #sym " %c0" : : "i" (val)) int main(void) { - DEFINE(TS_TSS, offsetof(struct task_struct, tss)); - DEFINE(TS_ESP0, offsetof(struct task_struct, tss.esp0)); - DEFINE(TS_FPU, offsetof(struct task_struct, tss.fp)); + /* offsets into the task struct */ + DEFINE(TASK_STATE, offsetof(struct task_struct, state)); + DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); + DEFINE(TASK_SIGPENDING, offsetof(struct task_struct, sigpending)); + DEFINE(TASK_NEEDRESCHED, offsetof(struct task_struct, need_resched)); + DEFINE(TASK_TSS, offsetof(struct task_struct, tss)); + DEFINE(TASK_MM, offsetof(struct task_struct, mm)); + + /* offsets into the thread struct */ + DEFINE(TSS_KSP, offsetof(struct thread_struct, ksp)); + DEFINE(TSS_USP, offsetof(struct thread_struct, usp)); + DEFINE(TSS_SR, offsetof(struct thread_struct, sr)); + DEFINE(TSS_FS, offsetof(struct thread_struct, fs)); + DEFINE(TSS_CRP, offsetof(struct thread_struct, crp)); + DEFINE(TSS_ESP0, offsetof(struct thread_struct, esp0)); + DEFINE(TSS_FPREG, offsetof(struct thread_struct, fp)); + DEFINE(TSS_FPCNTL, offsetof(struct thread_struct, fpcntl)); + DEFINE(TSS_FPSTATE, offsetof(struct thread_struct, fpstate)); + + /* offsets into the pt_regs */ + DEFINE(PT_D0, offsetof(struct pt_regs, d0)); + DEFINE(PT_ORIG_D0, offsetof(struct pt_regs, orig_d0)); + DEFINE(PT_SR, offsetof(struct pt_regs, sr)); + + /* bitfields are a bit difficult */ + DEFINE(PT_VECTOR, offsetof(struct pt_regs, pc) + 4); + + /* offsets into the irq_handler struct */ + DEFINE(IRQ_HANDLER, offsetof(struct irq_node, handler)); + DEFINE(IRQ_DEVID, offsetof(struct irq_node, dev_id)); + DEFINE(IRQ_NEXT, offsetof(struct irq_node, next)); + + /* offsets into the kernel_stat struct */ + DEFINE(STAT_IRQ, offsetof(struct kernel_stat, irqs)); + + /* offsets into the bi_record struct */ + DEFINE(BIR_TAG, offsetof(struct bi_record, tag)); + DEFINE(BIR_SIZE, offsetof(struct bi_record, size)); + DEFINE(BIR_DATA, offsetof(struct bi_record, data)); + + /* offsets into fbcon_font_desc (video/font.h) */ + DEFINE(FBCON_FONT_DESC_IDX, offsetof(struct fbcon_font_desc, idx)); + DEFINE(FBCON_FONT_DESC_NAME, offsetof(struct fbcon_font_desc, name)); + DEFINE(FBCON_FONT_DESC_WIDTH, offsetof(struct fbcon_font_desc, width)); + DEFINE(FBCON_FONT_DESC_HEIGHT, offsetof(struct fbcon_font_desc, height)); + DEFINE(FBCON_FONT_DESC_DATA, offsetof(struct fbcon_font_desc, data)); + DEFINE(FBCON_FONT_DESC_PREF, offsetof(struct fbcon_font_desc, pref)); + + /* offsets into the custom struct */ + DEFINE(CUSTOMBASE, &custom); + DEFINE(C_INTENAR, offsetof(struct CUSTOM, intenar)); + DEFINE(C_INTREQR, offsetof(struct CUSTOM, intreqr)); + DEFINE(C_INTENA, offsetof(struct CUSTOM, intena)); + DEFINE(C_INTREQ, offsetof(struct CUSTOM, intreq)); + DEFINE(C_SERDATR, offsetof(struct CUSTOM, serdatr)); + DEFINE(C_SERDAT, offsetof(struct CUSTOM, serdat)); + DEFINE(C_SERPER, offsetof(struct CUSTOM, serper)); + DEFINE(CIAABASE, &ciaa); + DEFINE(CIABBASE, &ciab); + DEFINE(C_PRA, offsetof(struct CIA, pra)); + DEFINE(ZTWOBASE, zTwoBase); + return 0; } diff --git a/arch/m68k/kernel/m68k_defs.h b/arch/m68k/kernel/m68k_defs.h index 992d390c7..b32e6a1c9 100644 --- a/arch/m68k/kernel/m68k_defs.h +++ b/arch/m68k/kernel/m68k_defs.h @@ -3,6 +3,6 @@ */ #define TS_MAGICKEY 0x5a5a5a5a -#define TS_TSS 478 -#define TS_ESP0 498 -#define TS_FPU 502 +#define TS_TSS 482 +#define TS_ESP0 502 +#define TS_FPU 506 diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c index 40b692e0c..793ca6ec7 100644 --- a/arch/m68k/kernel/m68k_ksyms.c +++ b/arch/m68k/kernel/m68k_ksyms.c @@ -13,6 +13,7 @@ #include <asm/machdep.h> #include <asm/pgtable.h> #include <asm/irq.h> +#include <asm/io.h> #include <asm/semaphore.h> #include <asm/checksum.h> #include <asm/hardirq.h> @@ -37,8 +38,10 @@ EXPORT_SYMBOL(mm_ptov); EXPORT_SYMBOL(mm_end_of_chunk); #endif EXPORT_SYMBOL(mm_vtop_fallback); +EXPORT_SYMBOL(m68k_realnum_memory); EXPORT_SYMBOL(m68k_memory); -EXPORT_SYMBOL(kernel_map); +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(m68k_debug_device); EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(dump_thread); @@ -49,6 +52,8 @@ EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(local_bh_count); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(kernel_set_cachemode); +EXPORT_SYMBOL(kernel_thread); /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index fb9bac400..c7c3f458e 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -56,16 +56,13 @@ asmlinkage void ret_from_exception(void); */ asmlinkage int sys_idle(void) { - int ret = -EPERM; - - lock_kernel(); if (current->pid != 0) - goto out; + return -EPERM; /* endless idle loop with no priority at all */ - current->priority = -100; + current->priority = 0; current->counter = -100; - for (;;){ + for (;;) { if (!current->need_resched) #if defined(CONFIG_ATARI) && !defined(CONFIG_AMIGA) && !defined(CONFIG_MAC) /* block out HSYNC on the atari (falcon) */ @@ -73,14 +70,9 @@ asmlinkage int sys_idle(void) #else /* portable version */ __asm__("stop #0x2000" : : : "cc"); #endif /* machine compilation types */ - check_pgt_cache(); - run_task_queue(&tq_scheduler); schedule(); + check_pgt_cache(); } - ret = 0; -out: - unlock_kernel(); - return ret; } void machine_restart(char * __unused) @@ -115,6 +107,44 @@ void show_regs(struct pt_regs * regs) printk("USP: %08lx\n", rdusp()); } +/* + * Create a kernel thread + */ +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + int pid; + mm_segment_t fs; + + fs = get_fs(); + set_fs (KERNEL_DS); + + { + register long retval __asm__ ("d0"); + register long clone_arg __asm__ ("d1") = flags | CLONE_VM; + + __asm__ __volatile__ + ("clrl %%d2\n\t" + "trap #0\n\t" /* Linux/m68k system call */ + "tstl %0\n\t" /* child or parent */ + "jne 1f\n\t" /* parent - jump */ + "lea %%sp@(-8192),%6\n\t" /* reload current */ + "movel %3,%%sp@-\n\t" /* push argument */ + "jsr %4@\n\t" /* call fn */ + "movel %0,%%d1\n\t" /* pass exit value */ + "movel %2,%0\n\t" /* exit */ + "trap #0\n" + "1:" + : "=d" (retval) + : "0" (__NR_clone), "i" (__NR_exit), + "r" (arg), "a" (fn), "d" (clone_arg), "r" (current) + : "d0", "d2"); + pid = retval; + } + + set_fs (fs); + return pid; +} + void flush_thread(void) { unsigned long zero = 0; @@ -137,6 +167,19 @@ asmlinkage int m68k_fork(struct pt_regs *regs) return do_fork(SIGCHLD, rdusp(), regs); } +asmlinkage int m68k_vfork(struct pt_regs *regs) +{ + int child; + struct semaphore sem = MUTEX_LOCKED; + + child = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs); + + if (child > 0) + down(&sem); + + return child; +} + asmlinkage int m68k_clone(struct pt_regs *regs) { unsigned long clone_flags; @@ -147,7 +190,7 @@ asmlinkage int m68k_clone(struct pt_regs *regs) newsp = regs->d2; if (!newsp) newsp = rdusp(); - return do_fork(clone_flags, newsp, regs); + return do_fork(clone_flags & ~CLONE_VFORK, newsp, regs); } int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index 9888d83c1..5aeb2534b 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -325,12 +325,15 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ret = 0; goto out; } - if (pid == 1) /* you may not mess with init */ - goto out; ret = -ESRCH; - if (!(child = find_task_by_pid(pid))) + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!!! */ + if (!child) goto out; ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out; if (request == PTRACE_ATTACH) { if (child == current) goto out; @@ -375,7 +378,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_PEEKDATA: { unsigned long tmp; + down(&child->mm->mmap_sem); ret = read_long(child, addr, &tmp); + up(&child->mm->mmap_sem); if (ret >= 0) ret = put_user(tmp, (unsigned long *) data); goto out; @@ -408,7 +413,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: + down(&child->mm->mmap_sem); ret = write_long(child,addr,data); + up(&child->mm->mmap_sem); goto out; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/m68k/kernel/setup.c b/arch/m68k/kernel/setup.c index e12786344..ade12c46d 100644 --- a/arch/m68k/kernel/setup.c +++ b/arch/m68k/kernel/setup.c @@ -48,6 +48,7 @@ extern int end; extern unsigned long availmem; int m68k_num_memory = 0; +int m68k_realnum_memory = 0; struct mem_info m68k_memory[NUM_MEMINFO]; static struct mem_info m68k_ramdisk = { 0, 0 }; @@ -62,8 +63,6 @@ void (*mach_sched_init) (void (*handler)(int, void *, struct pt_regs *)) __initd int (*mach_keyb_init) (void) __initdata; int (*mach_kbdrate) (struct kbd_repeat *) = NULL; void (*mach_kbd_leds) (unsigned int) = NULL; -/* machine dependent "kbd-reset" setup function */ -void (*kbd_reset_setup) (char *, int) __initdata = NULL; /* machine dependent irq functions */ void (*mach_init_IRQ) (void) __initdata; void (*(*mach_default_handler)[]) (int, void *, struct pt_regs *) = NULL; @@ -159,6 +158,7 @@ __initfunc(static void m68k_parse_bootinfo(const struct bi_record *record)) record = (struct bi_record *)((u_long)record+record->size); } + m68k_realnum_memory = m68k_num_memory; #ifdef CONFIG_SINGLE_MEMORY_CHUNK if (m68k_num_memory > 1) { printk("Ignoring last %i chunks of physical memory\n", @@ -398,9 +398,9 @@ void floppy_eject(void) } #endif -__initfunc(unsigned long arch_kbd_init(void)) +/* for "kbd-reset" cmdline param */ +void __init kbd_reset_setup(char *str, int *ints) { - return mach_keyb_init(); } void arch_gettod(int *year, int *mon, int *day, int *hour, diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index af5af69b6..a2b9ce1d4 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -361,7 +361,7 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext *usc, void *fp, : "a0"); #undef frame_offset /* - * If we ever get here an exception occured while + * If we ever get here an exception occurred while * building the above stack-frame. */ goto badframe; @@ -460,7 +460,7 @@ rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, : "a0"); #undef frame_offset /* - * If we ever get here an exception occured while + * If we ever get here an exception occurred while * building the above stack-frame. */ goto badframe; diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c index 75da52541..a85da4e10 100644 --- a/arch/m68k/kernel/sys_m68k.c +++ b/arch/m68k/kernel/sys_m68k.c @@ -112,7 +112,8 @@ asmlinkage int old_select(struct sel_arg_struct *arg) * * This is really horribly ugly. */ -asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth) +asmlinkage int sys_ipc (uint call, int first, int second, + int third, void *ptr, long fifth) { int version, ret; @@ -122,88 +123,76 @@ asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, if (call <= SEMCTL) switch (call) { case SEMOP: - ret = sys_semop (first, (struct sembuf *)ptr, second); - goto out; + return sys_semop (first, (struct sembuf *)ptr, second); case SEMGET: - ret = sys_semget (first, second, third); - goto out; + return sys_semget (first, second, third); case SEMCTL: { union semun fourth; - ret = -EINVAL; if (!ptr) - goto out; - if ((ret = get_user(fourth.__pad, (void **) ptr))) - goto out; - ret = sys_semctl (first, second, third, fourth); - goto out; + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); } default: - ret = -EINVAL; - goto out; + return -EINVAL; } if (call <= MSGCTL) switch (call) { case MSGSND: - ret = sys_msgsnd (first, (struct msgbuf *) ptr, + return sys_msgsnd (first, (struct msgbuf *) ptr, second, third); - goto out; case MSGRCV: switch (version) { case 0: { struct ipc_kludge tmp; - ret = -EINVAL; if (!ptr) - goto out; - ret = -EFAULT; - if (copy_from_user (&tmp, ptr, sizeof (tmp))) - goto out; - ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third); - goto out; + return -EINVAL; + if (copy_from_user (&tmp, + (struct ipc_kludge *)ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); } - case 1: default: - ret = sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, third); - goto out; + default: + return sys_msgrcv (first, + (struct msgbuf *) ptr, + second, fifth, third); } case MSGGET: - ret = sys_msgget ((key_t) first, second); - goto out; + return sys_msgget ((key_t) first, second); case MSGCTL: - ret = sys_msgctl (first, second, (struct msqid_ds *) ptr); - goto out; + return sys_msgctl (first, second, + (struct msqid_ds *) ptr); default: - ret = -EINVAL; - goto out; + return -EINVAL; } if (call <= SHMCTL) switch (call) { case SHMAT: switch (version) { - case 0: default: { + default: { ulong raddr; - ret = sys_shmat (first, (char *) ptr, second, &raddr); + ret = sys_shmat (first, (char *) ptr, + second, &raddr); if (ret) - goto out; - ret = put_user (raddr, (ulong *) third); - goto out; + return ret; + return put_user (raddr, (ulong *) third); } } case SHMDT: - ret = sys_shmdt ((char *)ptr); - goto out; + return sys_shmdt ((char *)ptr); case SHMGET: - ret = sys_shmget (first, second, third); - goto out; + return sys_shmget (first, second, third); case SHMCTL: - ret = sys_shmctl (first, second, (struct shmid_ds *) ptr); - goto out; + return sys_shmctl (first, second, + (struct shmid_ds *) ptr); default: - ret = -EINVAL; - goto out; + return -EINVAL; } - ret = -EINVAL; -out: - unlock_kernel(); - return ret; + + return -EINVAL; } asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) diff --git a/arch/m68k/kernel/time.c b/arch/m68k/kernel/time.c index 8f11a00d8..107d0de7f 100644 --- a/arch/m68k/kernel/time.c +++ b/arch/m68k/kernel/time.c @@ -5,6 +5,9 @@ * * This file contains the m68k-specific time handling details. * Most of the stuff is located in the machine specific files. + * + * 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> /* CONFIG_HEARTBEAT */ @@ -65,9 +68,10 @@ static void timer_interrupt(int irq, void *dummy, struct pt_regs * regs) * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be * called as close as possible to 500 ms before the new second starts. */ - if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) { + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && + xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else @@ -146,27 +150,38 @@ void time_init(void) mach_sched_init(timer_interrupt); } +extern rwlock_t xtime_lock; + /* * This version of gettimeofday has near microsecond resolution. */ void do_gettimeofday(struct timeval *tv) { + extern volatile unsigned long lost_ticks; unsigned long flags; - - save_flags(flags); - cli(); - *tv = xtime; - tv->tv_usec += mach_gettimeoffset(); - if (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; + unsigned long usec, sec, lost; + + read_lock_irqsave(&xtime_lock, flags); + usec = mach_gettimeoffset(); + lost = lost_ticks; + if (lost) + usec += lost * (1000000/HZ); + sec = xtime.tv_sec; + usec += xtime.tv_usec; + read_unlock_irqrestore(&xtime_lock, flags); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; } - restore_flags(flags); + + tv->tv_sec = sec; + tv->tv_usec = usec; } void do_settimeofday(struct timeval *tv) { - cli(); + write_lock_irq(&xtime_lock); /* This is revolting. We need to set the xtime.tv_usec * correctly. However, the value in this location is * is value at the last tick. @@ -175,14 +190,16 @@ void do_settimeofday(struct timeval *tv) */ tv->tv_usec -= mach_gettimeoffset(); - if (tv->tv_usec < 0) { + while (tv->tv_usec < 0) { tv->tv_usec += 1000000; tv->tv_sec--; } xtime = *tv; - time_state = TIME_BAD; - time_maxerror = MAXPHASE; - time_esterror = MAXPHASE; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; sti(); } diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c index f410182e0..8e902d0bd 100644 --- a/arch/m68k/mac/config.c +++ b/arch/m68k/mac/config.c @@ -55,9 +55,6 @@ extern char m68k_command_line[CL_SIZE]; void *mac_env; /* Loaded by the boot asm */ -/* The logical video addr. determined by head.S - testing */ -extern unsigned long mac_videobase; - /* The phys. video addr. - might be bogus on some machines */ unsigned long mac_orig_videoaddr; @@ -65,7 +62,6 @@ unsigned long mac_orig_videoaddr; extern int mac_keyb_init(void); extern int mac_kbdrate(struct kbd_repeat *k); extern void mac_kbd_leds(unsigned int leds); -extern void mac_kbd_reset_setup(char*, int); /* Mac specific irq functions */ extern void mac_init_IRQ (void); @@ -100,17 +96,15 @@ extern void mac_debug_init(void); extern void mac_debugging_long(int, long); #ifdef CONFIG_MAGIC_SYSRQ - -/* XXX FIXME: Atari scancodes still */ static char mac_sysrq_xlate[128] = - "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ - "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ - "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ - "bnm,./\000\000\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ - "\206\207\210\211\212\000\000\000\000\000-\000\000\000+\000"/* 0x40 - 0x4f */ - "\000\000\000\177\000\000\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ - "\000\000\000()/*789456123" /* 0x60 - 0x6f */ - "0.\r\000\000\000\000\000\000\000\000\000\000\000\000\000"; /* 0x70 - 0x7f */ + "\000sdfghzxcv\000bqwer" /* 0x00 - 0x0f */ + "yt123465=97-80)o" /* 0x10 - 0x1f */ + "u(ip\rlj'k;\\,/nm." /* 0x20 - 0x2f */ + "\t `\000\033\000\000\000\000\000\000\000\000\000\000\000" /* 0x30 - 0x3f */ + "\000.\000*\000+\000\000\000\000\000/\r\000-\000" /* 0x40 - 0x4f */ + "\000\00001234567a89\000\000\000" /* 0x50 - 0x5f */ + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" /* 0x60 - 0x6f */ + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"; /* 0x70 - 0x7f */ #endif extern void (*kd_mksound)(unsigned int, unsigned int); @@ -243,9 +237,7 @@ __initfunc(int mac_parse_bootinfo(const struct bi_record *record)) mac_bi_data.id = *data; break; case BI_MAC_VADDR: - /* save booter supplied videobase; use the one mapped in head.S! */ - mac_orig_videoaddr = *data; - mac_bi_data.videoaddr = mac_videobase; + mac_bi_data.videoaddr = VIDEOMEMBASE + (*data & ~VIDEOMEMMASK); break; case BI_MAC_VDEPTH: mac_bi_data.videodepth = *data; @@ -309,7 +301,6 @@ __initfunc(void config_mac(void)) mach_keyb_init = mac_keyb_init; mach_kbdrate = mac_kbdrate; mach_kbd_leds = mac_kbd_leds; - kbd_reset_setup = mac_kbd_reset_setup; mach_init_IRQ = mac_init_IRQ; mach_request_irq = mac_request_irq; mach_free_irq = mac_free_irq; @@ -336,7 +327,7 @@ __initfunc(void config_mac(void)) #endif kd_mksound = mac_mksound; #ifdef CONFIG_MAGIC_SYSRQ - mach_sysrq_key = 98; /* HELP */ + mach_sysrq_key = 114; /* HELP */ mach_sysrq_shift_state = 8; /* Alt */ mach_sysrq_shift_mask = 0xff; /* all modifiers except CapsLock */ mach_sysrq_xlate = mac_sysrq_xlate; @@ -399,10 +390,10 @@ static struct mac_model mac_data_table[]= * */ - { MAC_MODEL_II, "II", MAC_ADB_II, MAC_VIA_II, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_NUBUS}, - { MAC_MODEL_IIX, "IIx", MAC_ADB_II, MAC_VIA_II, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_NUBUS}, - { MAC_MODEL_IICX, "IIcx", MAC_ADB_II, MAC_VIA_II, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_NUBUS}, - { MAC_MODEL_SE30, "SE/30", MAC_ADB_II, MAC_VIA_II, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_NUBUS}, + { MAC_MODEL_II, "II", MAC_ADB_II, MAC_VIA_II, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_IIX, "IIx", MAC_ADB_II, MAC_VIA_II, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_IICX, "IIcx", MAC_ADB_II, MAC_VIA_II, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_SE30, "SE/30", MAC_ADB_II, MAC_VIA_II, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, /* * Weirdified MacII hardware - all subtley different. Gee thanks @@ -425,7 +416,7 @@ static struct mac_model mac_data_table[]= */ { MAC_MODEL_CLII, "Classic II", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_CCL, "Color Classic", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_CCL, "Color Classic", MAC_ADB_CUDA, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, /* * Some Mac LC machines. Basically the same as the IIci, ADB like IIsi @@ -461,11 +452,11 @@ static struct mac_model mac_data_table[]= * Performa - more LC type machines */ - { MAC_MODEL_P460, "Performa 460", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P460, "Performa 460", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_P475, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_P475F, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_P520, "Performa 520", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P550, "Performa 550", MAC_ADB_CUDA, MAC_VIA_IIci, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P550, "Performa 550", MAC_ADB_CUDA, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_P575, "Performa 575", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_P588, "Performa 588", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_TV, "TV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, @@ -477,8 +468,8 @@ static struct mac_model mac_data_table[]= * Centris - just guessing again; maybe like Quadra */ - { MAC_MODEL_C610, "Centris 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_C650, "Centris 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_C610, "Centris 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_C650, "Centris 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, /* @@ -551,7 +542,7 @@ void mac_identify(void) m++; } if(m->ident==-1) - mac_boom(5); + panic("mac model config data corrupt!\n"); } /* diff --git a/arch/m68k/mac/debug.c b/arch/m68k/mac/debug.c index 62a8a6187..5aa7ce6cf 100644 --- a/arch/m68k/mac/debug.c +++ b/arch/m68k/mac/debug.c @@ -36,7 +36,10 @@ extern unsigned long mac_videobase; extern unsigned long mac_videodepth; extern unsigned long mac_rowbytes; -#define DEBUG_SCREEN +extern void mac_serial_print(char *); + +#define DEBUG_HEADS +#undef DEBUG_SCREEN #define DEBUG_SERIAL /* @@ -129,121 +132,6 @@ void mac_debugging_long(int pos, long addr) #endif } -/* - * Penguin - used by head.S console; obsolete - */ -char that_penguin[]={ -#include "that_penguin.h" -}; - -#ifdef DEBUG_SCREEN -/* - * B/W version of penguin, unfinished - any takers?? - */ -static char bw_penguin[]={ -#include "bw_penguin.h" -}; -#endif - -void mac_debugging_penguin(int peng) -{ -#ifdef DEBUG_SCREEN - unsigned char *pengoffset; - unsigned char *pptr; - unsigned char *bwpdptr=bw_penguin; - int i; -#endif - -#ifdef DEBUG_SERIAL - printk("Penguin: #%d !\n", peng); -#endif - -#ifdef DEBUG_SCREEN - if (!MACH_IS_MAC) - return; - - if (mac_videodepth ==1) - pengoffset=(unsigned char *)(mac_videobase+80*mac_rowbytes) - +5*peng; - else - pengoffset=(unsigned char *)(mac_videobase+80*mac_rowbytes) - +20*peng; - - pptr=pengoffset; - - for(i=0;i<36;i++) - { - memcpy(pptr,bwpdptr,4); - bwpdptr+=4; - pptr+=mac_rowbytes; - } -#endif -} - -#ifdef DEBUG_SCREEN -/* - * B/W version of flaming Mac, unfinished (see above). - */ -static char bw_kaboom_map[]={ -#include "bw_mac.h" -}; -#endif - -#ifdef DEBUG_SCREEN -static void mac_boom_boom(void) -{ - static unsigned char *boomoffset=NULL; - unsigned char *pptr; - unsigned char *bwpdptr=bw_kaboom_map; - int i; - -#ifdef DEBUG_SERIAL - printk("BOOM !\n"); -#endif - - if (!MACH_IS_MAC) - return; - - if(!boomoffset) - if (mac_videodepth == 1) { - boomoffset=(unsigned char *)(mac_videobase+160*mac_rowbytes); - } else { - boomoffset=(unsigned char *)(mac_videobase+256*mac_rowbytes); - } - else - if (mac_videodepth == 1) - boomoffset+=5; - else - boomoffset+=32; - - pptr=boomoffset; - - for(i=0;i<36;i++) - { - memcpy(pptr,bwpdptr,4); - bwpdptr+=4; - pptr+=mac_rowbytes; - } -} -#endif - -void mac_boom(int booms) -{ -#ifdef DEBUG_SCREEN - int i; -#endif - - if (!MACH_IS_MAC) - return; - -#ifdef DEBUG_SCREEN - for(i=0;i<booms;i++) - mac_boom_boom(); - while(1); -#endif -} - - #ifdef DEBUG_SERIAL /* * TODO: serial debug code @@ -284,6 +172,29 @@ static struct console mac_console_driver = { NULL }; +/* + * Crude hack to get console output to the screen before the framebuffer + * is initialized (happens a lot later in 2.1!). + * We just use the console routines declared in head.S, this will interfere + * with regular framebuffer console output and should be used exclusively + * to debug kernel problems manifesting before framebuffer init (aka WSOD) + * + * To keep this hack from interfering with the regular console driver, either + * deregister this driver before/on framebuffer console init, or silence this + * function after the fbcon driver is running (will lose console messages!?). + * To debug real early bugs, need to write a 'mac_register_console_hack()' + * that is called from start_kernel() before setup_arch() and just registers + * this driver if Mac. + */ + +void mac_debug_console_write (struct console *co, const char *str, + unsigned int count) +{ + mac_serial_print(str); +} + + + /* Mac: loops_per_sec min. 1900000 ^= .5 us; MFPDELAY was 0.6 us*/ #define uSEC 1 @@ -486,19 +397,30 @@ __initfunc(void mac_debug_init(void)) /* Mac modem port */ mac_init_scc_port( B9600|CS8, 0 ); mac_console_driver.write = mac_scca_console_write; +#ifdef CONFIG_SERIAL_CONSOLE mac_console_driver.wait_key = mac_scca_console_wait_key; +#endif scc_port = 0; } else if (!strcmp( m68k_debug_device, "ser2" )) { /* Mac printer port */ mac_init_scc_port( B9600|CS8, 1 ); mac_console_driver.write = mac_sccb_console_write; +#ifdef CONFIG_SERIAL_CONSOLE mac_console_driver.wait_key = mac_sccb_console_wait_key; +#endif scc_port = 1; } +#endif +#ifdef DEBUG_HEADS + if ( !strcmp( m68k_debug_device, "scn" ) + || !strcmp( m68k_debug_device, "con" )) { + /* display, using head.S console routines */ + mac_console_driver.write = mac_debug_console_write; + } +#endif if (mac_console_driver.write) register_console(&mac_console_driver); -#endif } /* diff --git a/arch/m68k/mac/macboing.c b/arch/m68k/mac/macboing.c index 95078a384..1731c929c 100644 --- a/arch/m68k/mac/macboing.c +++ b/arch/m68k/mac/macboing.c @@ -1,6 +1,12 @@ /* * Mac bong noise generator. Note - we ought to put a boingy noise * here 8) + * + * ---------------------------------------------------------------------- + * 16.11.98: + * rewrote some functions, added support for Enhanced ASC (Quadras) + * after the NetBSD asc.c console bell patch by Colin Wood/Frederick Bruck + * Juergen Mellinger (juergen.mellinger@t-online.de) */ #include <linux/sched.h> @@ -9,124 +15,281 @@ #include <asm/macintosh.h> #include <asm/mac_asc.h> +static int mac_asc_inited = 0; +/* + * dumb triangular wave table + */ +static __u8 mac_asc_wave_tab[ 0x800 ]; + +/* + * Alan's original sine table; needs interpolating to 0x800 + * (hint: interpolate or hardwire [0 -> Pi/2[, it's symmetric) + */ static const signed char sine_data[] = { 0, 39, 75, 103, 121, 127, 121, 103, 75, 39, 0, -39, -75, -103, -121, -127, -121, -103, -75, -39 }; -#define DATA_SIZE (sizeof(sine_data)/sizeof(sine_data[0])) -static void nosound( unsigned long ignored ); -static struct timer_list sound_timer = { NULL, NULL, 0, 0, nosound }; +/* + * where the ASC hides ... + */ +static volatile __u8* mac_asc_regs = ( void* )0x50F14000; -static volatile unsigned char *asc_base=(void *)0x50F14000; +/* + * sample rate; is this a good default value? + */ +static unsigned long mac_asc_samplespersec = 11050; +static int mac_bell_duration = 0; +static unsigned long mac_bell_phase; /* 0..2*Pi -> 0..0x800 (wavetable size) */ +static unsigned long mac_bell_phasepersample; + +/* + * some function protos + */ +static void mac_init_asc( void ); +static void mac_nosound( unsigned long ); +static void mac_quadra_start_bell( unsigned int, unsigned int, unsigned int ); +static void mac_quadra_ring_bell( unsigned long ); +static void mac_av_start_bell( unsigned int, unsigned int, unsigned int ); +static void ( *mac_special_bell )( unsigned int, unsigned int, unsigned int ) = NULL; +/* + * our timer to start/continue/stop the bell + */ +static struct timer_list mac_sound_timer = { NULL, NULL, 0, 0, mac_nosound }; -void mac_mksound( unsigned int hz, unsigned int ticks ) +/* + * Sort of initialize the sound chip (called from mac_mksound on the first + * beep). + */ +static void mac_init_asc( void ) { - static int inited = 0; - unsigned long flags; - int samples=512; + int i; - if (macintosh_config->ident == MAC_MODEL_C660 - || macintosh_config->ident == MAC_MODEL_Q840) + /* + * do some machine specific initialization + * BTW: + * the NetBSD Quadra patch identifies the Enhanced Apple Sound Chip via + * mac_asc_regs[ 0x800 ] & 0xF0 != 0 + * this makes no sense here, because we have to set the default sample + * rate anyway if we want correct frequencies + */ + switch ( macintosh_config->ident ) { - /* - * The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O. - * It appears to be similar to the "AWACS" custom ASIC in the Power Mac - * [678]100. Because Singer and AWACS may have a similar hardware - * interface, this would imply that the code in drivers/sound/dmasound.c - * for AWACS could be used as a basis for Singer support. All we have to - * do is figure out how to do DMA on the 660AV/840AV through the PSC and - * figure out where the Singer hardware sits in memory. (I'd look in the - * vicinity of the AWACS location in a Power Mac [678]100 first, or the - * current location of the Apple Sound Chip--ASC--in other Macs.) The - * Power Mac [678]100 info can be found in MkLinux Mach kernel sources. - * - * Quoted from Apple's Tech Info Library, article number 16405: - * "Among desktop Macintosh computers, only the 660AV, 840AV, and Power - * Macintosh models have 16-bit audio input and output capability - * because of the AT&T DSP3210 hardware circuitry and the 16-bit Singer - * codec circuitry in the AVs. The Audio Waveform Amplifier and - * Converter (AWAC) chip in the Power Macintosh performs the same - * 16-bit I/O functionality. The PowerBook 500 series computers - * support 16-bit stereo output, but only mono input." - * - * http://til.info.apple.com/techinfo.nsf/artnum/n16405 - * - * --David Kilzer - */ + case MAC_MODEL_IIFX: + /* + * The IIfx is always special ... + */ + mac_asc_regs = ( void* )0x50010000; + break; + /* + * not sure about how correct this list is + * machines with the EASC enhanced apple sound chip + */ + case MAC_MODEL_Q630: + case MAC_MODEL_P475: + mac_special_bell = mac_quadra_start_bell; + mac_asc_samplespersec = 22150; + break; + case MAC_MODEL_Q650: + case MAC_MODEL_Q700: + case MAC_MODEL_Q800: + case MAC_MODEL_Q900: + case MAC_MODEL_Q950: + /* + * Currently not implemented! + */ + /* + * The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O. + * It appears to be similar to the "AWACS" custom ASIC in the Power Mac + * [678]100. Because Singer and AWACS may have a similar hardware + * interface, this would imply that the code in drivers/sound/dmasound.c + * for AWACS could be used as a basis for Singer support. All we have to + * do is figure out how to do DMA on the 660AV/840AV through the PSC and + * figure out where the Singer hardware sits in memory. (I'd look in the + * vicinity of the AWACS location in a Power Mac [678]100 first, or the + * current location of the Apple Sound Chip--ASC--in other Macs.) The + * Power Mac [678]100 info can be found in MkLinux Mach kernel sources. + * + * Quoted from Apple's Tech Info Library, article number 16405: + * "Among desktop Macintosh computers, only the 660AV, 840AV, and Power + * Macintosh models have 16-bit audio input and output capability + * because of the AT&T DSP3210 hardware circuitry and the 16-bit Singer + * codec circuitry in the AVs. The Audio Waveform Amplifier and + * Converter (AWAC) chip in the Power Macintosh performs the same + * 16-bit I/O functionality. The PowerBook 500 series computers + * support 16-bit stereo output, but only mono input." + * + * http://til.info.apple.com/techinfo.nsf/artnum/n16405 + * + * --David Kilzer + */ + mac_special_bell = mac_av_start_bell; + break; + } + + /* + * init the wave table with a simple triangular wave + * A sine wave would sure be nicer here ... + */ + for ( i = 0; i < 0x400; i++ ) + { + mac_asc_wave_tab[ i ] = i / 4; + mac_asc_wave_tab[ i + 0x400 ] = 0xFF - i / 4; + } + mac_asc_inited = 1; +} +/* + * Called to make noise; current single entry to the boing driver. + * Does the job for simple ASC, calls other routines else. + * XXX Fixme: + * Should be split into asc_mksound, easc_mksound, av_mksound and + * function pointer set in mac_init_asc which would be called at + * init time. + * _This_ is rather ugly ... + */ +void mac_mksound( unsigned int freq, unsigned int length ) +{ + __u32 cfreq = ( freq << 5 ) / 468; + __u32 flags; + int i; + + if ( !mac_asc_inited ) + mac_init_asc(); + + if ( mac_special_bell ) + { + mac_special_bell( freq, length, 128 ); return; } - - if(!inited) + + if ( freq < 20 || freq > 20000 || length == 0 ) { - int i=0; - int j=0; - int k=0; - int l=0; - - /* - * The IIfx strikes again! - */ - - if(macintosh_config->ident==MAC_MODEL_IIFX) - asc_base=(void *)0x50010000; - - for(i=0;i<samples;i++) - { - asc_base[i]=sine_data[j]; - asc_base[i+512]=sine_data[j]; - asc_base[i+1024]=sine_data[j]; - asc_base[i+1536]=sine_data[j]; - j++; - if(j==DATA_SIZE) - j=0; - if(i&1) - k++; - if(k==DATA_SIZE) - k=0; - if((i&3)==3) - l++; - if(l==DATA_SIZE) - l=0; - } - inited=1; + mac_nosound( 0 ); + return; } - save_flags(flags); + + save_flags( flags ); cli(); - del_timer( &sound_timer ); - if (hz > 20 && hz < 32767) { - int i; - u_long asc_pulses=((hz<<5)*samples)/468; - for(i=0;i<4;i++) + del_timer( &mac_sound_timer ); + + for ( i = 0; i < 0x800; i++ ) + mac_asc_regs[ i ] = 0; + for ( i = 0; i < 0x800; i++ ) + mac_asc_regs[ i ] = mac_asc_wave_tab[ i ]; + + for ( i = 0; i < 8; i++ ) + *( __u32* )( ( __u32 )mac_asc_regs + ASC_CONTROL + 0x814 + 8 * i ) = cfreq; + + mac_asc_regs[ 0x807 ] = 0; + mac_asc_regs[ ASC_VOLUME ] = 128; + mac_asc_regs[ 0x805 ] = 0; + mac_asc_regs[ 0x80F ] = 0; + mac_asc_regs[ ASC_MODE ] = ASC_MODE_SAMPLE; + mac_asc_regs[ ASC_ENABLE ] = ASC_ENABLE_SAMPLE; + + mac_sound_timer.expires = jiffies + length; + add_timer( &mac_sound_timer ); + + restore_flags( flags ); +} + +/* + * regular ASC: stop whining .. + */ +static void mac_nosound( unsigned long ignored ) +{ + mac_asc_regs[ ASC_ENABLE ] = 0; +} + +/* + * EASC entry; init EASC, don't load wavetable, schedule 'start whining'. + */ +static void mac_quadra_start_bell( unsigned int freq, unsigned int length, unsigned int volume ) +{ + __u32 flags; + + /* if the bell is already ringing, ring longer */ + if ( mac_bell_duration > 0 ) + { + mac_bell_duration += length; + return; + } + + mac_bell_duration = length; + mac_bell_phase = 0; + mac_bell_phasepersample = ( freq * sizeof( mac_asc_wave_tab ) ) / mac_asc_samplespersec; + /* this is reasonably big for small frequencies */ + + save_flags( flags ); + cli(); + + /* set the volume */ + mac_asc_regs[ 0x806 ] = volume; + + /* set up the ASC registers */ + if ( mac_asc_regs[ 0x801 ] != 1 ) + { + /* select mono mode */ + mac_asc_regs[ 0x807 ] = 0; + /* select sampled sound mode */ + mac_asc_regs[ 0x802 ] = 0; + /* ??? */ + mac_asc_regs[ 0x801 ] = 1; + mac_asc_regs[ 0x803 ] |= 0x80; + mac_asc_regs[ 0x803 ] &= 0x7F; + } + + mac_sound_timer.function = mac_quadra_ring_bell; + mac_sound_timer.expires = jiffies + 1; + add_timer( &mac_sound_timer ); + + restore_flags( flags ); +} + +/* + * EASC 'start/continue whining'; I'm not sure why the above function didn't + * already load the wave table, or at least call this one... + * This piece keeps reloading the wave table until done. + */ +static void mac_quadra_ring_bell( unsigned long ignored ) +{ + int i, count = mac_asc_samplespersec / HZ; + __u32 flags; + + /* + * we neither want a sound buffer overflow nor underflow, so we need to match + * the number of samples per timer interrupt as exactly as possible. + * using the asc interrupt will give better results in the future + * ...and the possibility to use a real sample (a boingy noise, maybe...) + */ + + save_flags( flags ); + cli(); + + del_timer( &mac_sound_timer ); + + if ( mac_bell_duration-- > 0 ) + { + for ( i = 0; i < count; i++ ) { - asc_base[ASC_FREQ(i,0)]=0x00; - asc_base[ASC_FREQ(i,1)]=20; - asc_base[ASC_FREQ(i,2)]=0x00; - asc_base[ASC_FREQ(i,3)]=20; - asc_base[ASC_FREQ(i,4)]=(asc_pulses>>24)&0xFF; - asc_base[ASC_FREQ(i,5)]=(asc_pulses>>16)&0xFF; - asc_base[ASC_FREQ(i,6)]=(asc_pulses>>8)&0xFF; - asc_base[ASC_FREQ(i,7)]=(asc_pulses>>0)&0xFF; + mac_bell_phase += mac_bell_phasepersample; + mac_asc_regs[ 0 ] = mac_asc_wave_tab[ mac_bell_phase & ( sizeof( mac_asc_wave_tab ) - 1 ) ]; } - asc_base[ASC_CHAN]=0x03; - asc_base[ASC_VOLUME]=128; - asc_base[ASC_MODE]=ASC_MODE_SAMPLE; - asc_base[ASC_ENABLE]=ASC_ENABLE_SAMPLE; - if (ticks) { - sound_timer.expires = jiffies + ticks; - add_timer( &sound_timer ); - } - } else { - nosound( 0 ); + mac_sound_timer.expires = jiffies + 1; + add_timer( &mac_sound_timer ); } - restore_flags(flags); + else + mac_asc_regs[ 0x801 ] = 0; + + restore_flags( flags ); } - -static void nosound( unsigned long ignored ) +/* + * AV code - please fill in. + */ +static void mac_av_start_bell( unsigned int freq, unsigned int length, unsigned int volume ) { - asc_base[ASC_ENABLE]=0; -} +} diff --git a/arch/m68k/mac/macints.c b/arch/m68k/mac/macints.c index b703cb275..87e6692d4 100644 --- a/arch/m68k/mac/macints.c +++ b/arch/m68k/mac/macints.c @@ -163,7 +163,14 @@ static unsigned long nubus_irqs[8]; static unsigned long *mac_irqs[8]; /* - * VIA2 / RBV register base pointers + * Some special nutcases ... + */ + +static unsigned long mac_ide_irqs = 0; +static unsigned long nubus_stuck_events = 0; + +/* + * VIA/RBV/OSS/PSC register base pointers */ volatile unsigned char *via2_regp=(volatile unsigned char *)VIA2_BAS; @@ -217,9 +224,13 @@ void mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs); static void via_do_nubus(int slot, void *via, struct pt_regs *regs); /* #define DEBUG_MACINTS */ -/* #define DEBUG_NUBUS_INT */ + +#define DEBUG_SPURIOUS +#define DEBUG_NUBUS_SPURIOUS +#define DEBUG_NUBUS_INT + /* #define DEBUG_VIA */ -/* #define DEBUG_VIA_NUBUS */ +#define DEBUG_VIA_NUBUS void mac_init_IRQ(void) { @@ -247,7 +258,7 @@ void mac_init_IRQ(void) /* yes, this is messy - the IIfx deserves a class of his own */ if (macintosh_config->ident == MAC_MODEL_IIFX) { - /* no real VIA2, the OSS seems _very_different */ + /* no real VIA2, the OSS seems _very_ different */ via2_is_oss = 1; /* IIfx has OSS, at a different base address than RBV */ rbv_regp = (unsigned char *) OSS_BAS; @@ -351,6 +362,12 @@ void mac_init_IRQ(void) mac_irqs[7] = &nubus_irqs[0]; /* + * Nubus Macs: turn off the Nubus dispatch interrupt for now + */ + + mac_turnoff_irq(IRQ_MAC_NUBUS); + + /* * AV Macs: shutup the PSC ints */ if (macintosh_config->ident == MAC_MODEL_C660 @@ -430,8 +447,10 @@ int mac_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_re return 0; } - /* add similar hack for Nubus pseudo-irq here - hide nubus_request_irq */ - + /* + * code below: only for VIA irqs currently + * add similar hack for Nubus pseudo-irq here - hide nubus_request_irq + */ via = (volatile unsigned char *) via_table[srcidx]; if (!via) return -EINVAL; @@ -459,7 +478,7 @@ int mac_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_re if (irq == IRQ_IDX(IRQ_MAC_SCSI)) { /* * Set vPCR for SCSI interrupts. (what about RBV here?) - * 980429 MS: RBV is ok, OSS seems to be differentt + * 980429 MS: RBV is ok, OSS seems to be different */ if (!via2_is_oss) if (macintosh_config->scsi_type == MAC_SCSI_OLD) { @@ -602,7 +621,10 @@ void mac_disable_irq (unsigned int irq) /* * In opposite to {en,dis}able_irq, requests between turn{off,on}_irq are not - * "stored". This is done with the VIA interrupt enable register + * "stored". This is done with the VIA interrupt enable register on VIAs. + * + * Note: Using these functions on non-VIA/OSS/PSC ints will panic, or at least + * have undesired side effects. */ void mac_turnon_irq( unsigned int irq ) @@ -615,14 +637,14 @@ void mac_turnon_irq( unsigned int irq ) if (!via) return; - if (srcidx == SRC_VIA2 && via2_is_rbv) + if (srcidx == SRC_VIA2 && via2_is_rbv) /* RBV as VIA2 */ via_write(via, rIER, via_read(via, rIER)|0x80|(1<<(irqidx))); - else if (srcidx == SRC_VIA2 && via2_is_oss) + else if (srcidx == SRC_VIA2 && via2_is_oss) /* OSS */ via_write(oss_regp, oss_map[irqidx]+8, 2); - else if (srcidx > SRC_VIA2) + else if (srcidx > SRC_VIA2) /* hope AVs have VIA2 */ via_write(via, (0x104 + 0x10*srcidx), via_read(via, (0x104 + 0x10*srcidx))|0x80|(1<<(irqidx))); - else + else /* VIA1+2 */ via_write(via, vIER, via_read(via, vIER)|0x80|(1<<(irqidx))); } @@ -637,9 +659,9 @@ void mac_turnoff_irq( unsigned int irq ) if (!via) return; - if (srcidx == SRC_VIA2 && via2_is_rbv) + if (srcidx == SRC_VIA2 && via2_is_rbv) /* RBV as VIA2 */ via_write(via, rIER, (via_read(via, rIER)&(1<<irqidx))); - else if (srcidx == SRC_VIA2 && via2_is_oss) + else if (srcidx == SRC_VIA2 && via2_is_oss) /* OSS */ via_write(oss_regp, oss_map[irqidx]+8, 0); /* * VIA2 is fixed. The stuff above VIA2 is for later @@ -648,7 +670,7 @@ void mac_turnoff_irq( unsigned int irq ) else if (srcidx > SRC_VIA2) via_write(via, (0x104 + 0x10*srcidx), via_read(via, (0x104 + 0x10*srcidx))|(1<<(irqidx))); - else + else /* VIA1+2 */ via_write(via, vIER, (via_read(via, vIER)&(1<<irqidx))); } @@ -694,12 +716,18 @@ int mac_irq_pending( unsigned int irq ) return (pending); } +/* + * for /proc/interrupts: log interrupt stats broken down by + * autovector int first, then by actual interrupt source. + */ + int mac_get_irq_list (char *buf) { int i, len = 0; int srcidx, irqidx; for (i = VIA1_SOURCE_BASE; i < NUM_MAC_SOURCES+8; ++i) { + /* XXX fixme: IRQ_SRC_MASK should cover VIA1 - Nubus */ srcidx = ((i & IRQ_SRC_MASK)>>3) - 1; irqidx = (i & IRQ_IDX_MASK); @@ -764,6 +792,32 @@ int mac_get_irq_list (char *buf) } if (num_spurious) len += sprintf(buf+len, "spurio.: %10u\n", num_spurious); + + /* + * XXX Fixme: Nubus sources are never logged above ... + */ + + len += sprintf(buf+len, "Nubus interrupts:\n"); + + for (i = 0; i < 7; i++) { + if (nubus_handler[i].handler == nubus_wtf) + continue; + len += sprintf(buf+len, "nubus %01X: %10lu ", + i+9, + nubus_irqs[i]); + len += sprintf(buf+len, "%s\n", + nubus_param[i].devname); + + } + len += sprintf(buf+len, "nubus spurious ints: %10lu\n", + nubus_irqs[7]); + len += sprintf(buf+len, "nubus stuck events : %10lu\n", + nubus_stuck_events); +#ifdef CONFIG_BLK_DEV_IDE + len += sprintf(buf+len, "nubus/IDE interrupt: %10lu\n", + mac_ide_irqs); +#endif + return len; } @@ -784,8 +838,9 @@ void via_scsi_clear(void) void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs) { -#ifdef DEBUG_VIA - printk("Unexpected IRQ %d\n", irq); +#ifdef DEBUG_SPURIOUS + if (console_loglevel > 6) + printk("Unexpected IRQ %d on device %p\n", irq, dev_id); #endif } @@ -861,6 +916,18 @@ void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) } /* + * Unexpected via interrupt + */ + +void via_wtf(int slot, void *via, struct pt_regs *regs) +{ +#ifdef DEBUG_SPURIOUS + if (console_loglevel > 6) + printk("Unexpected nubus event %d on via %p\n",slot,via); +#endif +} + +/* * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's * via6522.c :-), disable/pending masks added. * The int *viaidx etc. is just to keep the prototype happy ... @@ -1115,17 +1182,6 @@ void rbv_irq(int irq, void *dev_id, struct pt_regs *regs) } /* - * Unexpected via interrupt - */ - -void via_wtf(int slot, void *via, struct pt_regs *regs) -{ -#ifdef DEBUG_VIA - printk("Unexpected event %d on via %p\n",slot,via); -#endif -} - -/* * Nubus / SCSI interrupts; OSS style * The OSS is even more different than the RBV. OSS appears to stand for * Obscenely Screwed Silicon ... @@ -1264,8 +1320,9 @@ void oss_irq(int irq, void *dev_id, struct pt_regs *regs) void nubus_wtf(int slot, void *via, struct pt_regs *regs) { -#ifdef DEBUG_VIA_NUBUS - printk("Unexpected interrupt on nubus slot %d\n",slot); +#ifdef DEBUG_NUBUS_SPURIOUS + if (console_loglevel > 6) + printk("Unexpected interrupt on nubus slot %d\n",slot); #endif } @@ -1279,9 +1336,10 @@ void mac_SCC_handler(int irq, void *dev_id, struct pt_regs *regs) int i; /* 1+2: compatibility with PSC ! */ for (i = 1; i < 3; i++) /* currently only these two used */ - if (scc_handler[i].handler != mac_default_handler) + if (scc_handler[i].handler != mac_default_handler) { (scc_handler[i].handler)(i, scc_handler[i].dev_id, regs); - + scc_irqs[i]++; + } } /* @@ -1310,7 +1368,7 @@ void psc_irq(int irq, void *dev_id, struct pt_regs *regs) if(events==0) { #ifdef DEBUG_VIA - printk("rbv_irq: nothing pending, flags %x mask %x!\n", + printk("psc_irq: nothing pending, flags %x mask %x!\n", via_read(via, pIFR), via_read(via,pIER)); #endif mac_irqs[srcidx][7]++; @@ -1319,7 +1377,7 @@ void psc_irq(int irq, void *dev_id, struct pt_regs *regs) #ifdef DEBUG_VIA /* - * limited verbosity for RBV interrupts (add more if needed) + * limited verbosity for PSC interrupts (add more if needed) */ if ( srcidx == 1 && events != 1<<3 && events != 1<<1 ) /* SCSI IRQ */ printk("psc_irq: irq %d srcidx+1 %d events %x !\n", irq, srcidx+1, events); @@ -1423,6 +1481,7 @@ int nubus_request_irq(int slot, void *dev_id, void (*handler)(int,void *,struct if (!nubus_active && !via2_is_oss) { request_irq(IRQ_MAC_NUBUS, via_do_nubus, IRQ_FLG_LOCK, "nubus dispatch", via_do_nubus); + mac_turnon_irq(IRQ_MAC_NUBUS); } nubus_active|=1<<slot; @@ -1472,6 +1531,7 @@ int nubus_free_irq(int slot) * IDE interrupt hook */ extern void (*mac_ide_intr_hook)(int, void *, struct pt_regs *); +extern int (*mac_ide_irq_p_hook)(void); #endif /* @@ -1479,13 +1539,13 @@ extern void (*mac_ide_intr_hook)(int, void *, struct pt_regs *); */ static void via_do_nubus(int slot, void *via, struct pt_regs *regs) { - unsigned char map; + unsigned char map, allints; int i; int ct=0; - -/* printk("nubus interrupt\n");*/ + int ide_pending = 0; /* lock the nubus interrupt */ + /* That's just 'clear Nubus IRQ bit in VIA2' BTW. Pretty obsolete ? */ if (via2_is_rbv) via_write(rbv_regp, rIFR, 0x82); else @@ -1493,36 +1553,69 @@ static void via_do_nubus(int slot, void *via, struct pt_regs *regs) #ifdef CONFIG_BLK_DEV_MAC_IDE /* IDE hack */ - if (mac_ide_intr_hook) + if (mac_ide_intr_hook) { /* 'slot' is lacking the machspec bit in 2.0 */ /* need to pass proper dev_id = hwgroup here */ mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs); + mac_ide_irqs++; + } #endif while(1) { if (via2_is_rbv) - map = ~via_read(rbv_regp, rBufA); + allints = ~via_read(rbv_regp, rBufA); else - map = ~via_read(via2_regp, vBufA); + allints = ~via_read(via2_regp, vBufA); - if( (map = (map&nubus_active)) ==0 ) { -#ifdef DEBUG_NUBUS_INT - printk("nubus_irq: nothing pending, map %x mask %x\n", - map, nubus_active); +#ifdef CONFIG_BLK_DEV_MAC_IDE + if (mac_ide_irq_p_hook) + ide_pending = mac_ide_irq_p_hook(); +#endif + + if ( (map = (allints&nubus_active)) == 0 +#ifdef CONFIG_BLK_DEV_MAC_IDE + && !ide_pending #endif - nubus_irqs[7]++; + ) + { + if (ct == 0) { +#ifdef DEBUG_VIA_NUBUS + if (console_loglevel > 5) + printk("nubus_irq: nothing pending, map %x mask %x active %x\n", + allints, nubus_active, map); +#endif + nubus_irqs[7]++; + } + /* clear it */ + if (allints) + if (via2_is_rbv) + via_write(rbv_regp, rIFR, 0x02); + else + via_write(via2_regp, vIFR, 0x02); break; } -#ifdef DEBUG_NUBUS_INT - printk("nubus_irq: map %x mask %x\n", map, nubus_active); + +#ifdef DEBUG_VIA_NUBUS + if (console_loglevel > 6) + printk("nubus_irq: map %x mask %x active %x\n", + allints, nubus_active, map); +#endif + +#ifdef CONFIG_BLK_DEV_MAC_IDE + if (mac_ide_intr_hook && ide_pending) { + mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs); + mac_ide_irqs++; + } #endif if(ct++>2) { -#ifdef DEBUG_NUBUS_INT - printk("nubus stuck events - %d/%d\n", map, nubus_active); -#endif + if (console_loglevel > 5) + printk("nubus stuck events - %x/%x/%x ide %x\n", + allints, nubus_active, map, ide_pending); + nubus_stuck_events++; + return; } @@ -1579,12 +1672,14 @@ static void oss_do_nubus(int slot, void *via, struct pt_regs *regs) printk("nubus_irq: map %x mask %x\n", map, nubus_active); #endif if( (map = (map&nubus_active)) ==0 ) { + if (ct == 0) { #ifdef CONFIG_BLK_DEV_MAC_IDE - if (!mac_ide_intr_hook) - printk("nubus_irq: nothing pending, map %x mask %x\n", - map, nubus_active); + if (!mac_ide_intr_hook) + printk("nubus_irq: nothing pending, map %x mask %x\n", + map, nubus_active); #endif - nubus_irqs[7]++; + nubus_irqs[7]++; + } break; } diff --git a/arch/m68k/mac/mackeyb.c b/arch/m68k/mac/mackeyb.c index 64cd5e1f8..5a6ae7c75 100644 --- a/arch/m68k/mac/mackeyb.c +++ b/arch/m68k/mac/mackeyb.c @@ -1,5 +1,12 @@ /* - * linux/arch/m68k/mac/mackeyb.c + * linux/arch/m68k/mac/mackeyb.c + * + * Keyboard driver for Macintosh computers. + * + * Adapted from drivers/macintosh/key_mac.c and arch/m68k/atari/akakeyb.c + * (see that file for its authors and contributors). + * + * Copyright (C) 1997 Michael Schmitz. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive @@ -10,6 +17,7 @@ * misc. keyboard stuff (everything not in adb-bus.c or keyb_m68k.c) */ +#include <linux/config.h> #include <linux/types.h> #include <linux/mm.h> #include <linux/kd.h> @@ -59,8 +67,15 @@ extern void put_queue(int); static void mac_leds_done(struct adb_request *); static void keyboard_input(unsigned char *, int, struct pt_regs *); static void mouse_input(unsigned char *, int, struct pt_regs *); -/* Hook for mouse driver */ -void (*adb_mouse_interrupt_hook) (char *, int); + +#ifdef CONFIG_ADBMOUSE +/* XXX: Hook for mouse driver */ +void (*adb_mouse_interrupt_hook)(unsigned char *, int); +int adb_emulate_buttons = 0; +int adb_button2_keycode = 0x7d; /* right control key */ +int adb_button3_keycode = 0x7c; /* right option key */ +#endif + /* The mouse driver - for debugging */ extern void adb_mouse_interrupt(char *, int); /* end keyb */ @@ -275,9 +290,11 @@ input_keycode(int keycode, int repeat) kbd = kbd_table + fg_console; up_flag = (keycode & 0x80); keycode &= 0x7f; + if (!repeat) del_timer(&repeat_timer); +#ifdef CONFIG_ADBMOUSE /* * XXX: Add mouse button 2+3 fake codes here if mouse open. * As we only report up/down events, keep track of faked buttons. @@ -289,7 +306,8 @@ input_keycode(int keycode, int repeat) * (wanted: command and alt/option, or KP= and KP( ...) * Debug version; might be rewritten to be faster on normal keys. */ - if (adb_mouse_interrupt_hook || console_loglevel >= 8) { + if (adb_emulate_buttons + && (adb_mouse_interrupt_hook || console_loglevel >= 8)) { unsigned char button, button2, button3, fake_event; static unsigned char button2state=0, button3state=0; /* up */ /* faked ADB packet */ @@ -297,21 +315,20 @@ input_keycode(int keycode, int repeat) button = 0; fake_event = 0; - switch (keycode) { /* which 'button' ? */ - case 0x7c: /* R-option */ - button2 = (!up_flag); /* new state */ - if (button2 != button2state) /* change ? */ - button = 2; - button2state = button2; /* save state */ - fake_event = 2; - break; - case 0x7d: /* R-control */ - button3 = (!up_flag); /* new state */ - if (button3 != button3state) /* change ? */ - button = 3; - button3state = button3; /* save state */ - fake_event = 3; - break; + if (keycode == adb_button2_keycode) { /* which 'button' ? */ + /* R-option */ + button2 = (!up_flag); /* new state */ + if (button2 != button2state) /* change ? */ + button = 2; + button2state = button2; /* save state */ + fake_event = 2; + } else if (keycode == adb_button3_keycode) { + /* R-control */ + button3 = (!up_flag); /* new state */ + if (button3 != button3state) /* change ? */ + button = 3; + button3state = button3; /* save state */ + fake_event = 3; } #ifdef DEBUG_ADBMOUSE if (fake_event && console_loglevel >= 8) @@ -340,6 +357,7 @@ input_keycode(int keycode, int repeat) if (fake_event) return; } +#endif /* CONFIG_ADBMOUSE */ /* * Convert R-shift/control/option to L version. @@ -365,16 +383,18 @@ input_keycode(int keycode, int repeat) * transition into a key-down transition. * MSch: need to turn each caps-lock event into a down-up * double event (keyboard code assumes caps-lock is a toggle) + * 981127: fix LED behavior (kudos atong!) */ -#if 0 - if (keycode == 0x39 && up_flag && vc_kbd_led(kbd, VC_CAPSLOCK)) - up_flag = 0; -#else - if (keycode == 0x39) { + switch (keycode) { + case 0x39: handle_scancode(keycode); /* down */ up_flag = 0x80; /* see below ... */ + mark_bh(KEYBOARD_BH); + break; + case 0x47: + mark_bh(KEYBOARD_BH); + break; } -#endif } handle_scancode(keycode + up_flag); @@ -740,8 +760,3 @@ __initfunc(int mac_keyb_init(void)) return 0; } - -/* for "kbd-reset" cmdline param */ -__initfunc(void mac_kbd_reset_setup(char *str, int *ints)) -{ -} diff --git a/arch/m68k/mac/via6522.c b/arch/m68k/mac/via6522.c index 08ca49071..05e6f44e4 100644 --- a/arch/m68k/mac/via6522.c +++ b/arch/m68k/mac/via6522.c @@ -304,10 +304,11 @@ void mac_reset(void) unsigned long flags; unsigned long *reset_hook; - save_flags(flags); - cli(); - /* need ROMBASE in booter */ + /* indeed, plus need to MAP THE ROM !! */ + + if (mac_bi_data.rombase == 0) + mac_bi_data.rombase = 0x40800000; /* works on some */ rom_reset = (void *) (mac_bi_data.rombase + 0xa); @@ -318,12 +319,22 @@ void mac_reset(void) printk("ROM reset hook: %p\n", *reset_hook); rom_reset = *reset_hook; #endif + if (macintosh_config->ident == MAC_MODEL_SE30) { + /* + * MSch: Machines known to crash on ROM reset ... + */ + printk("System halted.\n"); + while(1); + } else { + save_flags(flags); + cli(); - rom_reset(); + rom_reset(); - restore_flags(flags); + restore_flags(flags); + } - /* We never make it this far... */ + /* We never make it this far... it usually panics above. */ printk ("Restart failed. Please restart manually.\n"); /* XXX - delay do we need to spin here ? */ diff --git a/arch/m68k/mm/fault.c b/arch/m68k/mm/fault.c index 62129782b..ef1b855bd 100644 --- a/arch/m68k/mm/fault.c +++ b/arch/m68k/mm/fault.c @@ -8,6 +8,7 @@ #include <linux/mm.h> #include <linux/kernel.h> #include <linux/ptrace.h> +#include <linux/interrupt.h> #include <asm/setup.h> #include <asm/traps.h> @@ -32,8 +33,7 @@ extern const int frame_extra_sizes[]; /* in m68k/kernel/signal.c */ asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long error_code) { - struct task_struct *tsk = current; - struct mm_struct *mm = tsk->mm; + struct mm_struct *mm = current->mm; struct vm_area_struct * vma; unsigned long fixup; int write; @@ -41,9 +41,17 @@ asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, #ifdef DEBUG printk ("regs->sr=%#x, regs->pc=%#lx, address=%#lx, %ld, %p\n", regs->sr, regs->pc, address, error_code, - tsk->mm->pgd); + current->mm->pgd); #endif + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || mm == &init_mm) + goto no_context; + down(&mm->mmap_sem); vma = find_vma(mm, address); @@ -86,7 +94,14 @@ good_area: if (!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; } - handle_mm_fault(current, vma, address, write); + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + if (!handle_mm_fault(current, vma, address, write)) + goto do_sigbus; /* There seems to be a missing invalidate somewhere in do_no_page. * Until I found it, this one cures the problem and makes @@ -106,10 +121,15 @@ bad_area: /* User mode accesses just cause a SIGSEGV */ if (user_mode(regs)) { - force_sig (SIGSEGV, tsk); + siginfo_t info; + info.si_signo = SIGSEGV; + info.si_code = SEGV_MAPERR; + info.si_addr = (void *)address; + force_sig_info(SIGSEGV, &info, current); return 1; } +no_context: /* Are we prepared to handle this kernel fault? */ if ((fixup = search_exception_table(regs->pc)) != 0) { struct pt_regs *tregs; @@ -136,5 +156,22 @@ bad_area: die_if_kernel("Oops", regs, error_code); do_exit(SIGKILL); +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +do_sigbus: + up(&mm->mmap_sem); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + force_sig(SIGBUS, current); + + /* Kernel mode? Handle exceptions or die */ + if (!user_mode(regs)) + goto no_context; + return 1; } diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 395fb41b6..8e520702f 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -28,8 +28,9 @@ #include <asm/atari_stram.h> #endif +#undef DEBUG + extern void die_if_kernel(char *,struct pt_regs *,long); -extern void init_kpointer_table(void); extern void show_net_buffers(void); int do_check_pgt_cache(int low, int high) @@ -122,17 +123,14 @@ void show_mem(void) unsigned long mm_cachebits = 0; #endif -pte_t *kernel_page_table (unsigned long *memavailp) +static pte_t *__init kernel_page_table(unsigned long *memavailp) { pte_t *ptablep; - if (memavailp) { - ptablep = (pte_t *)*memavailp; - *memavailp += PAGE_SIZE; - } - else - ptablep = (pte_t *)__get_free_page(GFP_KERNEL); + ptablep = (pte_t *)*memavailp; + *memavailp += PAGE_SIZE; + clear_page((unsigned long)ptablep); flush_page_to_ram((unsigned long) ptablep); flush_tlb_kernel_page((unsigned long) ptablep); nocache_page ((unsigned long)ptablep); @@ -140,199 +138,164 @@ pte_t *kernel_page_table (unsigned long *memavailp) return ptablep; } -__initfunc(static unsigned long -map_chunk (unsigned long addr, unsigned long size, unsigned long *memavailp)) -{ -#define ONEMEG (1024*1024) -#define L3TREESIZE (256*1024) +static pmd_t *last_pgtable __initdata = NULL; - static unsigned long mem_mapped = 0; - static unsigned long virtaddr = 0; - static pte_t *ktablep = NULL; - unsigned long *kpointerp; - unsigned long physaddr; - extern pte_t *kpt; - int pindex; /* index into pointer table */ - pgd_t *page_dir = pgd_offset_k (virtaddr); - - if (!pgd_present (*page_dir)) { - /* we need a new pointer table */ - kpointerp = (unsigned long *) get_kpointer_table (); - pgd_set (page_dir, (pmd_t *) kpointerp); - memset (kpointerp, 0, PTRS_PER_PMD * sizeof (pmd_t)); - } - else - kpointerp = (unsigned long *) pgd_page (*page_dir); +static pmd_t *__init kernel_ptr_table(unsigned long *memavailp) +{ + if (!last_pgtable) { + unsigned long pmd, last; + int i; - /* - * pindex is the offset into the pointer table for the - * descriptors for the current virtual address being mapped. - */ - pindex = (virtaddr >> 18) & 0x7f; + last = (unsigned long)kernel_pg_dir; + for (i = 0; i < PTRS_PER_PGD; i++) { + if (!pgd_val(kernel_pg_dir[i])) + continue; + pmd = pgd_page(kernel_pg_dir[i]); + if (pmd > last) + last = pmd; + } + last_pgtable = (pmd_t *)last; #ifdef DEBUG - printk ("mm=%ld, kernel_pg_dir=%p, kpointerp=%p, pindex=%d\n", - mem_mapped, kernel_pg_dir, kpointerp, pindex); + printk("kernel_ptr_init: %p\n", last_pgtable); #endif + } - /* - * if this is running on an '040, we already allocated a page - * table for the first 4M. The address is stored in kpt by - * arch/head.S - * - */ - if (CPU_IS_040_OR_060 && mem_mapped == 0) - ktablep = kpt; - - for (physaddr = addr; - physaddr < addr + size; - mem_mapped += L3TREESIZE, virtaddr += L3TREESIZE) { - -#ifdef DEBUG - printk ("pa=%#lx va=%#lx ", physaddr, virtaddr); -#endif + if (((unsigned long)(last_pgtable + PTRS_PER_PMD) & ~PAGE_MASK) == 0) { + last_pgtable = (pmd_t *)*memavailp; + *memavailp += PAGE_SIZE; - if (pindex > 127 && mem_mapped >= 32*ONEMEG) { - /* we need a new pointer table every 32M */ -#ifdef DEBUG - printk ("[new pointer]"); -#endif + clear_page((unsigned long)last_pgtable); + flush_page_to_ram((unsigned long)last_pgtable); + flush_tlb_kernel_page((unsigned long)last_pgtable); + nocache_page((unsigned long)last_pgtable); + } else + last_pgtable += PTRS_PER_PMD; - kpointerp = (unsigned long *)get_kpointer_table (); - pgd_set(pgd_offset_k(virtaddr), (pmd_t *)kpointerp); - pindex = 0; - } + return last_pgtable; +} - if (CPU_IS_040_OR_060) { - int i; - unsigned long ktable; +static unsigned long __init +map_chunk (unsigned long addr, long size, unsigned long *memavailp) +{ +#define PTRTREESIZE (256*1024) +#define ROOTTREESIZE (32*1024*1024) + static unsigned long virtaddr = 0; + unsigned long physaddr; + pgd_t *pgd_dir; + pmd_t *pmd_dir; + pte_t *pte_dir; - /* Don't map the first 4 MB again. The pagetables - * for this range have already been initialized - * in boot/head.S. Otherwise the pages used for - * tables would be reinitialized to copyback mode. - */ + physaddr = (addr | m68k_supervisor_cachemode | + _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY); + if (CPU_IS_040_OR_060) + physaddr |= _PAGE_GLOBAL040; - if (mem_mapped < 4 * ONEMEG) - { + while (size > 0) { +#ifdef DEBUG + if (!(virtaddr & (PTRTREESIZE-1))) + printk ("\npa=%#lx va=%#lx ", physaddr & PAGE_MASK, + virtaddr); +#endif + pgd_dir = pgd_offset_k(virtaddr); + if (virtaddr && CPU_IS_020_OR_030) { + if (!(virtaddr & (ROOTTREESIZE-1)) && + size >= ROOTTREESIZE) { #ifdef DEBUG - printk ("Already initialized\n"); + printk ("[very early term]"); #endif - physaddr += L3TREESIZE; - pindex++; + pgd_val(*pgd_dir) = physaddr; + size -= ROOTTREESIZE; + virtaddr += ROOTTREESIZE; + physaddr += ROOTTREESIZE; continue; } + } + if (!pgd_present(*pgd_dir)) { + pmd_dir = kernel_ptr_table(memavailp); #ifdef DEBUG - printk ("[setup table]"); + printk ("[new pointer %p]", pmd_dir); #endif + pgd_set(pgd_dir, pmd_dir); + } else + pmd_dir = pmd_offset(pgd_dir, virtaddr); - /* - * 68040, use page tables pointed to by the - * kernel pointer table. - */ - - if ((pindex & 15) == 0) { - /* Need new page table every 4M on the '040 */ + if (CPU_IS_020_OR_030) { + if (virtaddr) { #ifdef DEBUG - printk ("[new table]"); + printk ("[early term]"); #endif - ktablep = kernel_page_table (memavailp); - } - - ktable = virt_to_phys(ktablep); - - /* - * initialize section of the page table mapping - * this 256K portion. - */ - for (i = 0; i < 64; i++) { - pte_val(ktablep[i]) = physaddr | _PAGE_PRESENT - | m68k_supervisor_cachemode | _PAGE_GLOBAL040 - | _PAGE_ACCESSED; + pmd_dir->pmd[(virtaddr/PTRTREESIZE) & 15] = physaddr; + physaddr += PTRTREESIZE; + } else { + int i; +#ifdef DEBUG + printk ("[zero map]"); +#endif + pte_dir = (pte_t *)kernel_ptr_table(memavailp); + pmd_dir->pmd[0] = virt_to_phys(pte_dir) | + _PAGE_TABLE | _PAGE_ACCESSED; + pte_val(*pte_dir++) = 0; physaddr += PAGE_SIZE; + for (i = 1; i < 64; physaddr += PAGE_SIZE, i++) + pte_val(*pte_dir++) = physaddr; } - ktablep += 64; - - /* - * make the kernel pointer table point to the - * kernel page table. Each entries point to a - * 64 entry section of the page table. - */ - - kpointerp[pindex++] = ktable | _PAGE_TABLE | _PAGE_ACCESSED; + size -= PTRTREESIZE; + virtaddr += PTRTREESIZE; } else { - /* - * 68030, use early termination page descriptors. - * Each one points to 64 pages (256K). - */ -#ifdef DEBUG - printk ("[early term] "); -#endif - if (virtaddr == 0UL) { - /* map the first 256K using a 64 entry - * 3rd level page table. - * UNMAP the first entry to trap - * zero page (NULL pointer) references - */ - int i; - unsigned long *tbl; - - tbl = (unsigned long *)get_kpointer_table(); - - kpointerp[pindex++] = virt_to_phys(tbl) | _PAGE_TABLE |_PAGE_ACCESSED; - - for (i = 0; i < 64; i++, physaddr += PAGE_SIZE) - tbl[i] = physaddr | _PAGE_PRESENT | _PAGE_ACCESSED; - - /* unmap the zero page */ - tbl[0] = 0; - } else { - /* not the first 256K */ - kpointerp[pindex++] = physaddr | _PAGE_PRESENT | _PAGE_ACCESSED; + if (!pmd_present(*pmd_dir)) { #ifdef DEBUG - printk ("%lx=%lx ", virt_to_phys(&kpointerp[pindex-1]), - kpointerp[pindex-1]); + printk ("[new table]"); #endif - physaddr += 64 * PAGE_SIZE; + pte_dir = kernel_page_table(memavailp); + pmd_set(pmd_dir, pte_dir); } + pte_dir = pte_offset(pmd_dir, virtaddr); + + if (virtaddr) { + if (!pte_present(*pte_dir)) + pte_val(*pte_dir) = physaddr; + } else + pte_val(*pte_dir) = 0; + size -= PAGE_SIZE; + virtaddr += PAGE_SIZE; + physaddr += PAGE_SIZE; } + + } #ifdef DEBUG - printk ("\n"); + printk("\n"); #endif - } - return mem_mapped; + return virtaddr; } extern unsigned long free_area_init(unsigned long, unsigned long); +extern void init_pointer_table(unsigned long ptable); /* References to section boundaries */ extern char _text, _etext, _edata, __bss_start, _end; extern char __init_begin, __init_end; -extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; - /* * paging_init() continues the virtual memory environment setup which * was begun by the code in arch/head.S. */ -__initfunc(unsigned long paging_init(unsigned long start_mem, - unsigned long end_mem)) +unsigned long __init paging_init(unsigned long start_mem, + unsigned long end_mem) { int chunk; unsigned long mem_avail = 0; #ifdef DEBUG { - extern pte_t *kpt; - printk ("start of paging_init (%p, %p, %lx, %lx, %lx)\n", - kernel_pg_dir, kpt, availmem, start_mem, end_mem); + extern unsigned long availmem; + printk ("start of paging_init (%p, %lx, %lx, %lx)\n", + kernel_pg_dir, availmem, start_mem, end_mem); } #endif - init_kpointer_table(); - /* Fix the cache mode in the page descriptors for the 680[46]0. */ if (CPU_IS_040_OR_060) { int i; @@ -366,6 +329,7 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, m68k_memory[chunk].size, &start_mem); } + flush_tlb_all(); #ifdef DEBUG printk ("memory available is %ldKB\n", mem_avail >> 10); @@ -385,21 +349,16 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, start_mem += PAGE_SIZE; memset((void *)empty_zero_page, 0, PAGE_SIZE); -#if 0 /* * allocate the "swapper" page directory and * record in task 0 (swapper) tss */ - swapper_pg_dir = (pgd_t *)get_kpointer_table(); - - init_mm.pgd = swapper_pg_dir; -#endif - - memset (swapper_pg_dir, 0, sizeof(pgd_t)*PTRS_PER_PGD); + init_mm.pgd = (pgd_t *)kernel_ptr_table(&start_mem); + memset (init_mm.pgd, 0, sizeof(pgd_t)*PTRS_PER_PGD); /* setup CPU root pointer for swapper task */ task[0]->tss.crp[0] = 0x80000000 | _PAGE_TABLE; - task[0]->tss.crp[1] = virt_to_phys (swapper_pg_dir); + task[0]->tss.crp[1] = virt_to_phys(init_mm.pgd); #ifdef DEBUG printk ("task 0 pagedir at %p virt, %#lx phys\n", @@ -430,16 +389,16 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, #ifdef DEBUG printk ("before free_area_init\n"); #endif - - return PAGE_ALIGN(free_area_init (start_mem, end_mem)); + return PAGE_ALIGN(free_area_init(start_mem, end_mem)); } -__initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) +void __init mem_init(unsigned long start_mem, unsigned long end_mem) { int codepages = 0; int datapages = 0; int initpages = 0; unsigned long tmp; + int i; end_mem &= PAGE_MASK; high_memory = (void *) end_mem; @@ -480,6 +439,14 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) #endif free_page(tmp); } + + /* insert pointer tables allocated so far into the tablelist */ + init_pointer_table((unsigned long)kernel_pg_dir); + for (i = 0; i < PTRS_PER_PGD; i++) { + if (pgd_val(kernel_pg_dir[i])) + init_pointer_table(pgd_page(kernel_pg_dir[i])); + } + printk("Memory: %luk/%luk available (%dk kernel code, %dk data, %dk init)\n", (unsigned long) nr_free_pages << (PAGE_SHIFT-10), max_mapnr << (PAGE_SHIFT-10), diff --git a/arch/m68k/mm/kmap.c b/arch/m68k/mm/kmap.c index 802771ab4..d2cd29011 100644 --- a/arch/m68k/mm/kmap.c +++ b/arch/m68k/mm/kmap.c @@ -2,6 +2,9 @@ * linux/arch/m68k/mm/kmap.c * * Copyright (C) 1997 Roman Hodek + * + * 10/01/99 cleaned up the code and changing to the same interface + * used by other architectures /Roman Zippel */ #include <linux/mm.h> @@ -9,250 +12,88 @@ #include <linux/string.h> #include <linux/types.h> #include <linux/malloc.h> +#include <linux/vmalloc.h> #include <asm/setup.h> #include <asm/segment.h> #include <asm/page.h> #include <asm/pgtable.h> +#include <asm/io.h> #include <asm/system.h> +#undef DEBUG -extern pte_t *kernel_page_table (unsigned long *memavailp); - -/* Granularity of kernel_map() allocations */ -#define KMAP_STEP (256*1024) - -/* Size of pool of KMAP structures; that is needed, because kernel_map() can - * be called at times where kmalloc() isn't initialized yet. */ -#define KMAP_POOL_SIZE 16 - -/* structure for maintainance of kmap regions */ -typedef struct kmap { - struct kmap *next, *prev; /* linking of list */ - unsigned long addr; /* start address of region */ - unsigned long mapaddr; /* address returned to user */ - unsigned long size; /* size of region */ - unsigned free : 1; /* flag whether free or allocated */ - unsigned kmalloced : 1; /* flag whether got this from kmalloc() */ - unsigned pool_alloc : 1; /* flag whether got this is alloced in pool */ -} KMAP; - -KMAP kmap_pool[KMAP_POOL_SIZE] = { - { NULL, NULL, KMAP_START, KMAP_START, KMAP_END-KMAP_START, 1, 0, 1 }, - { NULL, NULL, 0, 0, 0, 0, 0, 0 }, -}; +#define PTRTREESIZE (256*1024) /* - * anchor of kmap region list - * - * The list is always ordered by addresses, and regions are always adjacent, - * i.e. there must be no holes between them! + * For 040/060 we can use the virtual memory area like other architectures, + * but for 020/030 we want to use early termination page descriptor and we + * can't mix this with normal page descriptors, so we have to copy that code + * (mm/vmalloc.c) and return appriorate aligned addresses. */ -KMAP *kmap_regions = &kmap_pool[0]; - -/* for protecting the kmap_regions list against races */ -static struct semaphore kmap_sem = MUTEX; +#ifdef CPU_M68040_OR_M68060_ONLY +#define IO_SIZE PAGE_SIZE -/* - * Low-level allocation and freeing of KMAP structures - */ -static KMAP *alloc_kmap( int use_kmalloc ) +static inline struct vm_struct *get_io_area(unsigned long size) { - KMAP *p; - int i; - - /* first try to get from the pool if possible */ - for( i = 0; i < KMAP_POOL_SIZE; ++i ) { - if (!kmap_pool[i].pool_alloc) { - kmap_pool[i].kmalloced = 0; - kmap_pool[i].pool_alloc = 1; - return( &kmap_pool[i] ); - } - } - - if (use_kmalloc && (p = (KMAP *)kmalloc( sizeof(KMAP), GFP_KERNEL ))) { - p->kmalloced = 1; - return( p ); - } - - return( NULL ); -} - -static void free_kmap( KMAP *p ) -{ - if (p->kmalloced) - kfree( p ); - else - p->pool_alloc = 0; + return get_vm_area(size); } -/* - * Get a free region from the kmap address range - */ -static KMAP *kmap_get_region( unsigned long size, int use_kmalloc ) +static inline void free_io_area(void *addr) { - KMAP *p, *q; - - /* look for a suitable free region */ - for( p = kmap_regions; p; p = p->next ) - if (p->free && p->size >= size) - break; - if (!p) { - printk( KERN_ERR "kernel_map: address space for " - "allocations exhausted\n" ); - return( NULL ); - } - - if (p->size > size) { - /* if free region is bigger than we need, split off the rear free part - * into a new region */ - if (!(q = alloc_kmap( use_kmalloc ))) { - printk( KERN_ERR "kernel_map: out of memory\n" ); - return( NULL ); - } - q->addr = p->addr + size; - q->size = p->size - size; - p->size = size; - q->free = 1; - - q->prev = p; - q->next = p->next; - p->next = q; - if (q->next) q->next->prev = q; - } - - p->free = 0; - return( p ); + return vfree((void *)(PAGE_MASK & (unsigned long)addr)); } +#else -/* - * Free a kernel_map region again - */ -static void kmap_put_region( KMAP *p ) -{ - KMAP *q; - - p->free = 1; +#define IO_SIZE (256*1024) - /* merge with previous region if possible */ - q = p->prev; - if (q && q->free) { - if (q->addr + q->size != p->addr) { - printk( KERN_ERR "kernel_malloc: allocation list destroyed\n" ); - return; - } - q->size += p->size; - q->next = p->next; - if (p->next) p->next->prev = q; - free_kmap( p ); - p = q; - } - - /* merge with following region if possible */ - q = p->next; - if (q && q->free) { - if (p->addr + p->size != q->addr) { - printk( KERN_ERR "kernel_malloc: allocation list destroyed\n" ); - return; - } - p->size += q->size; - p->next = q->next; - if (q->next) q->next->prev = p; - free_kmap( q ); - } -} +static struct vm_struct *iolist = NULL; - -/* - * kernel_map() helpers - */ -static inline pte_t * -pte_alloc_kernel_map(pmd_t *pmd, unsigned long address, - unsigned long *memavailp) +static struct vm_struct *get_io_area(unsigned long size) { - address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); - if (pmd_none(*pmd)) { - pte_t *page = kernel_page_table(memavailp); - if (pmd_none(*pmd)) { - if (page) { - pmd_set(pmd, page); - memset( page, 0, PAGE_SIZE ); - return page + address; - } - pmd_set(pmd, BAD_PAGETABLE); - return NULL; - } - if (memavailp) - panic("kernel_map: slept during init?!?"); - cache_page((unsigned long) page); - free_page((unsigned long) page); - } - if (pmd_bad(*pmd)) { - printk( KERN_ERR "Bad pmd in pte_alloc_kernel_map: %08lx\n", - pmd_val(*pmd)); - pmd_set(pmd, BAD_PAGETABLE); + unsigned long addr; + struct vm_struct **p, *tmp, *area; + + area = (struct vm_struct *)kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) return NULL; + addr = KMAP_START; + for (p = &iolist; (tmp = *p) ; p = &tmp->next) { + if (size + addr < (unsigned long)tmp->addr) + break; + if (addr > KMAP_END-size) + return NULL; + addr = tmp->size + (unsigned long)tmp->addr; } - return (pte_t *) pmd_page(*pmd) + address; -} - -static inline void -kernel_map_pte(pte_t *pte, unsigned long address, unsigned long size, - unsigned long phys_addr, pgprot_t prot) -{ - unsigned long end; - - address &= ~PMD_MASK; - end = address + size; - if (end > PMD_SIZE) - end = PMD_SIZE; - do { - pte_val(*pte) = phys_addr + pgprot_val(prot); - address += PAGE_SIZE; - phys_addr += PAGE_SIZE; - pte++; - } while (address < end); + area->addr = (void *)addr; + area->size = size + IO_SIZE; + area->next = *p; + *p = area; + return area; } -static inline int -kernel_map_pmd (pmd_t *pmd, unsigned long address, unsigned long size, - unsigned long phys_addr, pgprot_t prot, - unsigned long *memavailp) +static inline void free_io_area(void *addr) { - unsigned long end; + struct vm_struct **p, *tmp; - address &= ~PGDIR_MASK; - end = address + size; - if (end > PGDIR_SIZE) - end = PGDIR_SIZE; - phys_addr -= address; - - if (CPU_IS_040_OR_060) { - do { - pte_t *pte = pte_alloc_kernel_map(pmd, address, memavailp); - if (!pte) - return -ENOMEM; - kernel_map_pte(pte, address, end - address, - address + phys_addr, prot); - address = (address + PMD_SIZE) & PMD_MASK; - pmd++; - } while (address < end); - } else { - /* On the 68030 we use early termination page descriptors. - Each one points to 64 pages (256K). */ - int i = (address >> (PMD_SHIFT-4)) & 15; - do { - (&pmd_val(*pmd))[i++] = (address + phys_addr) | pgprot_val(prot); - address += PMD_SIZE / 16; - } while (address < end); + if (!addr) + return; + addr = (void *)((unsigned long)addr & -IO_SIZE); + for (p = &iolist ; (tmp = *p) ; p = &tmp->next) { + if (tmp->addr == addr) { + *p = tmp->next; + __iounmap(tmp->addr, tmp->size); + kfree(tmp); + return; + } } - return 0; } +#endif /* * Map some physical address range into the kernel address space. The @@ -260,304 +101,245 @@ kernel_map_pmd (pmd_t *pmd, unsigned long address, unsigned long size, */ /* Rewritten by Andreas Schwab to remove all races. */ -unsigned long kernel_map(unsigned long phys_addr, unsigned long size, - int cacheflag, unsigned long *memavailp) +void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) { - unsigned long retaddr, from, end; - pgd_t *dir; - pgprot_t prot; - KMAP *kmap; - - /* Round down 'phys_addr' to 256 KB and adjust size */ - retaddr = phys_addr & (KMAP_STEP-1); - size += retaddr; - phys_addr &= ~(KMAP_STEP-1); - /* Round up the size to 256 KB. It doesn't hurt if too much is - mapped... */ - size = (size + KMAP_STEP - 1) & ~(KMAP_STEP-1); - - down( &kmap_sem ); - kmap = kmap_get_region(size, memavailp == NULL); - if (!kmap) { - up(&kmap_sem); - return 0; - } - from = kmap->addr; - retaddr += from; - kmap->mapaddr = retaddr; - end = from + size; - up( &kmap_sem ); + struct vm_struct *area; + unsigned long virtaddr, retaddr; + long offset; + pgd_t *pgd_dir; + pmd_t *pmd_dir; + pte_t *pte_dir; + + /* + * Don't allow mappings that wrap.. + */ + if (!size || size > physaddr + size) + return NULL; +#ifdef DEBUG + printk("ioremap: 0x%lx,0x%lx(%d) - ", physaddr, size, cacheflag); +#endif + /* + * Mappings have to be aligned + */ + offset = physaddr & (IO_SIZE - 1); + physaddr &= -IO_SIZE; + size = (size + offset + IO_SIZE - 1) & -IO_SIZE; + + /* + * Ok, go for it.. + */ + area = get_io_area(size); + if (!area) + return NULL; + + virtaddr = (unsigned long)area->addr; + retaddr = virtaddr + offset; +#ifdef DEBUG + printk("0x%lx,0x%lx,0x%lx", physaddr, virtaddr, retaddr); +#endif + + /* + * add cache and table flags to physical address + */ if (CPU_IS_040_OR_060) { - pgprot_val(prot) = (_PAGE_PRESENT | _PAGE_GLOBAL040 | - _PAGE_ACCESSED | _PAGE_DIRTY); + physaddr |= (_PAGE_PRESENT | _PAGE_GLOBAL040 | + _PAGE_ACCESSED | _PAGE_DIRTY); switch (cacheflag) { - case KERNELMAP_FULL_CACHING: - pgprot_val(prot) |= _PAGE_CACHE040; + case IOMAP_FULL_CACHING: + physaddr |= _PAGE_CACHE040; break; - case KERNELMAP_NOCACHE_SER: + case IOMAP_NOCACHE_SER: default: - pgprot_val(prot) |= _PAGE_NOCACHE_S; + physaddr |= _PAGE_NOCACHE_S; break; - case KERNELMAP_NOCACHE_NONSER: - pgprot_val(prot) |= _PAGE_NOCACHE; + case IOMAP_NOCACHE_NONSER: + physaddr |= _PAGE_NOCACHE; break; - case KERNELMAP_NO_COPYBACK: - pgprot_val(prot) |= _PAGE_CACHE040W; + case IOMAP_WRITETHROUGH: + physaddr |= _PAGE_CACHE040W; break; } - } else - pgprot_val(prot) = (_PAGE_PRESENT | _PAGE_ACCESSED | - _PAGE_DIRTY | - ((cacheflag == KERNELMAP_FULL_CACHING || - cacheflag == KERNELMAP_NO_COPYBACK) - ? 0 : _PAGE_NOCACHE030)); - - phys_addr -= from; - dir = pgd_offset_k(from); - while (from < end) { - pmd_t *pmd = pmd_alloc_kernel(dir, from); - - if (kernel_map_pmd(pmd, from, end - from, phys_addr + from, - prot, memavailp)) { - printk( KERN_ERR "kernel_map: out of memory\n" ); - return 0UL; + } else { + physaddr |= (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY); + switch (cacheflag) { + case IOMAP_NOCACHE_SER: + case IOMAP_NOCACHE_NONSER: + default: + physaddr |= _PAGE_NOCACHE030; + break; + case IOMAP_FULL_CACHING: + case IOMAP_WRITETHROUGH: + break; } - from = (from + PGDIR_SIZE) & PGDIR_MASK; - dir++; } - return retaddr; -} - + while (size > 0) { +#ifdef DEBUG + if (!(virtaddr & (PTRTREESIZE-1))) + printk ("\npa=%#lx va=%#lx ", physaddr, virtaddr); +#endif + pgd_dir = pgd_offset_k(virtaddr); + pmd_dir = pmd_alloc_kernel(pgd_dir, virtaddr); + if (!pmd_dir) { + printk("ioremap: no mem for pmd_dir\n"); + return NULL; + } -/* - * kernel_unmap() helpers - */ -static inline void pte_free_kernel_unmap( pmd_t *pmd ) -{ - unsigned long page = pmd_page(*pmd); - mem_map_t *pagemap = &mem_map[MAP_NR(page)]; - - pmd_clear(pmd); - cache_page(page); - - if (PageReserved( pagemap )) { - /* need to unreserve pages that were allocated with memavailp != NULL; - * this works only if 'page' is page-aligned */ - if (page & ~PAGE_MASK) - return; - clear_bit( PG_reserved, &pagemap->flags ); - atomic_set( &pagemap->count, 1 ); - } - free_page( page ); -} + if (CPU_IS_020_OR_030) { + pmd_dir->pmd[(virtaddr/PTRTREESIZE)&-16] = physaddr; + physaddr += PTRTREESIZE; + virtaddr += PTRTREESIZE; + size -= PTRTREESIZE; + } else { + pte_dir = pte_alloc_kernel(pmd_dir, virtaddr); + if (!pte_dir) { + printk("ioremap: no mem for pte_dir\n"); + return NULL; + } -/* - * This not only unmaps the requested region, but also loops over the whole - * pmd to determine whether the other pte's are clear (so that the page can be - * freed.) If so, it returns 1, 0 otherwise. - */ -static inline int -kernel_unmap_pte_range(pmd_t * pmd, unsigned long address, unsigned long size) -{ - pte_t *pte; - unsigned long addr2, end, end2; - int all_clear = 1; - - if (pmd_none(*pmd)) - return( 0 ); - if (pmd_bad(*pmd)) { - printk( KERN_ERR "kernel_unmap_pte_range: bad pmd (%08lx)\n", - pmd_val(*pmd) ); - pmd_clear(pmd); - return( 0 ); - } - address &= ~PMD_MASK; - addr2 = 0; - pte = pte_offset(pmd, addr2); - end = address + size; - if (end > PMD_SIZE) - end = PMD_SIZE; - end2 = addr2 + PMD_SIZE; - while( addr2 < end2 ) { - if (!pte_none(*pte)) { - if (address <= addr2 && addr2 < end) - pte_clear(pte); - else - all_clear = 0; + pte_val(*pte_dir) = physaddr; + virtaddr += PAGE_SIZE; + physaddr += PAGE_SIZE; + size -= PAGE_SIZE; } - ++pte; - addr2 += PAGE_SIZE; } - return( all_clear ); -} - -static inline void -kernel_unmap_pmd_range(pgd_t * dir, unsigned long address, unsigned long size) -{ - pmd_t * pmd; - unsigned long end; +#ifdef DEBUG + printk("\n"); +#endif + flush_tlb_all(); - if (pgd_none(*dir)) - return; - if (pgd_bad(*dir)) { - printk( KERN_ERR "kernel_unmap_pmd_range: bad pgd (%08lx)\n", - pgd_val(*dir) ); - pgd_clear(dir); - return; - } - pmd = pmd_offset(dir, address); - address &= ~PGDIR_MASK; - end = address + size; - if (end > PGDIR_SIZE) - end = PGDIR_SIZE; - - if (CPU_IS_040_OR_060) { - do { - if (kernel_unmap_pte_range(pmd, address, end - address)) - pte_free_kernel_unmap( pmd ); - address = (address + PMD_SIZE) & PMD_MASK; - pmd++; - } while (address < end); - } else { - /* On the 68030 clear the early termination descriptors */ - int i = (address >> (PMD_SHIFT-4)) & 15; - do { - (&pmd_val(*pmd))[i++] = 0; - address += PMD_SIZE / 16; - } while (address < end); - } + return (void *)retaddr; } /* - * Unmap a kernel_map()ed region again + * Unmap a ioremap()ed region again */ -void kernel_unmap( unsigned long addr ) +void iounmap(void *addr) { - unsigned long end; - pgd_t *dir; - KMAP *p; - - down( &kmap_sem ); - - /* find region for 'addr' in list; must search for mapaddr! */ - for( p = kmap_regions; p; p = p->next ) - if (!p->free && p->mapaddr == addr) - break; - if (!p) { - printk( KERN_ERR "kernel_unmap: trying to free invalid region\n" ); - return; - } - addr = p->addr; - end = addr + p->size; - kmap_put_region( p ); - - dir = pgd_offset_k( addr ); - while( addr < end ) { - kernel_unmap_pmd_range( dir, addr, end - addr ); - addr = (addr + PGDIR_SIZE) & PGDIR_MASK; - dir++; - } - - up( &kmap_sem ); - /* flushing for a range would do, but there's no such function for kernel - * address space... */ - flush_tlb_all(); + free_io_area(addr); } - /* - * kernel_set_cachemode() helpers + * __iounmap unmaps nearly everything, so be careful + * it doesn't free currently pointer/page tables anymore but it + * wans't used anyway and might be added later. */ -static inline void set_cmode_pte( pmd_t *pmd, unsigned long address, - unsigned long size, unsigned cmode ) -{ pte_t *pte; - unsigned long end; - - if (pmd_none(*pmd)) - return; - - pte = pte_offset( pmd, address ); - address &= ~PMD_MASK; - end = address + size; - if (end >= PMD_SIZE) - end = PMD_SIZE; - - for( ; address < end; pte++ ) { - pte_val(*pte) = (pte_val(*pte) & ~_PAGE_NOCACHE) | cmode; - address += PAGE_SIZE; - } -} - - -static inline void set_cmode_pmd( pgd_t *dir, unsigned long address, - unsigned long size, unsigned cmode ) +void __iounmap(void *addr, unsigned long size) { - pmd_t *pmd; - unsigned long end; + unsigned long virtaddr = (unsigned long)addr; + pgd_t *pgd_dir; + pmd_t *pmd_dir; + pte_t *pte_dir; + + while (size > 0) { + pgd_dir = pgd_offset_k(virtaddr); + if (pgd_bad(*pgd_dir)) { + printk("iounmap: bad pgd(%08lx)\n", pgd_val(*pgd_dir)); + pgd_clear(pgd_dir); + return; + } + pmd_dir = pmd_offset(pgd_dir, virtaddr); - if (pgd_none(*dir)) - return; + if (CPU_IS_020_OR_030) { + int pmd_off = (virtaddr/PTRTREESIZE) & -16; - pmd = pmd_offset( dir, address ); - address &= ~PGDIR_MASK; - end = address + size; - if (end > PGDIR_SIZE) - end = PGDIR_SIZE; + if ((pmd_dir->pmd[pmd_off] & _DESCTYPE_MASK) == _PAGE_PRESENT) { + pmd_dir->pmd[pmd_off] = 0; + virtaddr += PTRTREESIZE; + size -= PTRTREESIZE; + continue; + } + } - if ((pmd_val(*pmd) & _DESCTYPE_MASK) == _PAGE_PRESENT) { - /* 68030 early termination descriptor */ - pmd_val(*pmd) = (pmd_val(*pmd) & ~_PAGE_NOCACHE) | cmode; - return; - } - else { - /* "normal" tables */ - for( ; address < end; pmd++ ) { - set_cmode_pte( pmd, address, end - address, cmode ); - address = (address + PMD_SIZE) & PMD_MASK; + if (pmd_bad(*pmd_dir)) { + printk("iounmap: bad pmd (%08lx)\n", pmd_val(*pmd_dir)); + pmd_clear(pmd_dir); + return; } + pte_dir = pte_offset(pmd_dir, virtaddr); + + pte_val(*pte_dir) = 0; + virtaddr += PAGE_SIZE; + size -= PAGE_SIZE; } -} + flush_tlb_all(); +} /* * Set new cache mode for some kernel address space. * The caller must push data for that range itself, if such data may already * be in the cache. */ -void kernel_set_cachemode( unsigned long address, unsigned long size, - unsigned cmode ) +void kernel_set_cachemode(void *addr, unsigned long size, int cmode) { - pgd_t *dir = pgd_offset_k( address ); - unsigned long end = address + size; - + unsigned long virtaddr = (unsigned long)addr; + pgd_t *pgd_dir; + pmd_t *pmd_dir; + pte_t *pte_dir; + if (CPU_IS_040_OR_060) { - switch( cmode ) { - case KERNELMAP_FULL_CACHING: + switch (cmode) { + case IOMAP_FULL_CACHING: cmode = _PAGE_CACHE040; break; - case KERNELMAP_NOCACHE_SER: - default: + case IOMAP_NOCACHE_SER: + default: cmode = _PAGE_NOCACHE_S; break; - case KERNELMAP_NOCACHE_NONSER: + case IOMAP_NOCACHE_NONSER: cmode = _PAGE_NOCACHE; break; - case KERNELMAP_NO_COPYBACK: + case IOMAP_WRITETHROUGH: cmode = _PAGE_CACHE040W; break; } - } else - cmode = ((cmode == KERNELMAP_FULL_CACHING || - cmode == KERNELMAP_NO_COPYBACK) ? - 0 : _PAGE_NOCACHE030); - - for( ; address < end; dir++ ) { - set_cmode_pmd( dir, address, end - address, cmode ); - address = (address + PGDIR_SIZE) & PGDIR_MASK; + } else { + switch (cmode) { + case IOMAP_NOCACHE_SER: + case IOMAP_NOCACHE_NONSER: + default: + cmode = _PAGE_NOCACHE030; + break; + case IOMAP_FULL_CACHING: + case IOMAP_WRITETHROUGH: + cmode = 0; + } + } + + while (size > 0) { + pgd_dir = pgd_offset_k(virtaddr); + if (pgd_bad(*pgd_dir)) { + printk("iocachemode: bad pgd(%08lx)\n", pgd_val(*pgd_dir)); + pgd_clear(pgd_dir); + return; + } + pmd_dir = pmd_offset(pgd_dir, virtaddr); + + if (CPU_IS_020_OR_030) { + int pmd_off = (virtaddr/PTRTREESIZE) & -16; + + if ((pmd_dir->pmd[pmd_off] & _DESCTYPE_MASK) == _PAGE_PRESENT) { + pmd_dir->pmd[pmd_off] = (pmd_dir->pmd[pmd_off] & + _CACHEMASK040) | cmode; + virtaddr += PTRTREESIZE; + size -= PTRTREESIZE; + continue; + } + } + + if (pmd_bad(*pmd_dir)) { + printk("iocachemode: bad pmd (%08lx)\n", pmd_val(*pmd_dir)); + pmd_clear(pmd_dir); + return; + } + pte_dir = pte_offset(pmd_dir, virtaddr); + + pte_val(*pte_dir) = (pte_val(*pte_dir) & _CACHEMASK040) | cmode; + virtaddr += PAGE_SIZE; + size -= PAGE_SIZE; } - /* flushing for a range would do, but there's no such function for kernel - * address space... */ + flush_tlb_all(); } diff --git a/arch/m68k/mm/memory.c b/arch/m68k/mm/memory.c index 39cc1d1a9..a97578ec2 100644 --- a/arch/m68k/mm/memory.c +++ b/arch/m68k/mm/memory.c @@ -10,6 +10,7 @@ #include <linux/string.h> #include <linux/types.h> #include <linux/malloc.h> +#include <linux/init.h> #include <asm/setup.h> #include <asm/segment.h> @@ -97,6 +98,31 @@ static ptable_desc ptable_list = { &ptable_list, &ptable_list }; #define PTABLE_SIZE (PTRS_PER_PMD * sizeof(pmd_t)) +void __init init_pointer_table(unsigned long ptable) +{ + ptable_desc *dp; + unsigned long page = ptable & PAGE_MASK; + unsigned char mask = 1 << ((ptable - page)/PTABLE_SIZE); + + dp = PAGE_PD(page); + if (!(PD_MARKBITS(dp) & mask)) { + PD_MARKBITS(dp) = 0xff; + (dp->prev = ptable_list.prev)->next = dp; + (dp->next = &ptable_list)->prev = dp; + } + + PD_MARKBITS(dp) &= ~mask; +#ifdef DEBUG + printk("init_pointer_table: %lx, %x\n", ptable, PD_MARKBITS(dp)); +#endif + + /* unreserve the page so it's possible to free that page */ + dp->flags &= ~(1 << PG_reserved); + atomic_set(&dp->count, 1); + + return; +} + pmd_t *get_pointer_table (void) { ptable_desc *dp = ptable_list.next; @@ -176,103 +202,6 @@ int free_pointer_table (pmd_t *ptable) return 0; } -/* maximum pages used for kpointer tables */ -#define KPTR_PAGES 4 -/* # of reserved slots */ -#define RESERVED_KPTR 4 -extern pmd_tablepage kernel_pmd_table; /* reserved in head.S */ - -static struct kpointer_pages { - pmd_tablepage *page[KPTR_PAGES]; - u_char alloced[KPTR_PAGES]; -} kptr_pages; - -void init_kpointer_table(void) { - short i = KPTR_PAGES-1; - - /* first page is reserved in head.S */ - kptr_pages.page[i] = &kernel_pmd_table; - kptr_pages.alloced[i] = ~(0xff>>RESERVED_KPTR); - for (i--; i>=0; i--) { - kptr_pages.page[i] = NULL; - kptr_pages.alloced[i] = 0; - } -} - -pmd_t *get_kpointer_table (void) -{ - /* For pointer tables for the kernel virtual address space, - * use the page that is reserved in head.S that can hold up to - * 8 pointer tables. 3 of these tables are always reserved - * (kernel_pg_dir, swapper_pg_dir and kernel pointer table for - * the first 16 MB of RAM). In addition, the 4th pointer table - * in this page is reserved. On Amiga and Atari, it is used to - * map in the hardware registers. It may be used for other - * purposes on other 68k machines. This leaves 4 pointer tables - * available for use by the kernel. 1 of them are usually used - * for the vmalloc tables. This allows mapping of 3 * 32 = 96 MB - * of physical memory. But these pointer tables are also used - * for other purposes, like kernel_map(), so further pages can - * now be allocated. - */ - pmd_tablepage *page; - pmd_table *table; - long nr, offset = -8; - short i; - - for (i=KPTR_PAGES-1; i>=0; i--) { - asm volatile("bfffo %1{%2,#8},%0" - : "=d" (nr) - : "d" ((u_char)~kptr_pages.alloced[i]), "d" (offset)); - if (nr) - break; - } - if (i < 0) { - printk("No space for kernel pointer table!\n"); - return NULL; - } - if (!(page = kptr_pages.page[i])) { - if (!(page = (pmd_tablepage *)get_free_page(GFP_KERNEL))) { - printk("No space for kernel pointer table!\n"); - return NULL; - } - flush_tlb_kernel_page((unsigned long) page); - nocache_page((u_long)(kptr_pages.page[i] = page)); - } - asm volatile("bfset %0@{%1,#1}" - : /* no output */ - : "a" (&kptr_pages.alloced[i]), "d" (nr-offset)); - table = &(*page)[nr-offset]; - memset(table, 0, sizeof(pmd_table)); - return ((pmd_t *)table); -} - -void free_kpointer_table (pmd_t *pmdp) -{ - pmd_table *table = (pmd_table *)pmdp; - pmd_tablepage *page = (pmd_tablepage *)((u_long)table & PAGE_MASK); - long nr; - short i; - - for (i=KPTR_PAGES-1; i>=0; i--) { - if (kptr_pages.page[i] == page) - break; - } - nr = ((u_long)table - (u_long)page) / sizeof(pmd_table); - if (!table || i < 0 || (i == KPTR_PAGES-1 && nr < RESERVED_KPTR)) { - printk("Attempt to free invalid kernel pointer table: %p\n", table); - return; - } - asm volatile("bfclr %0@{%1,#1}" - : /* no output */ - : "a" (&kptr_pages.alloced[i]), "d" (nr)); - if (!kptr_pages.alloced[i]) { - kptr_pages.page[i] = 0; - cache_page ((u_long)page); - free_page ((u_long)page); - } -} - static unsigned long transp_transl_matches( unsigned long regval, unsigned long vaddr ) { @@ -308,7 +237,6 @@ static unsigned long transp_transl_matches( unsigned long regval, */ unsigned long mm_vtop (unsigned long vaddr) { -#ifndef CONFIG_SINGLE_MEMORY_CHUNK int i=0; unsigned long voff = vaddr; unsigned long offset = 0; @@ -324,10 +252,6 @@ unsigned long mm_vtop (unsigned long vaddr) offset += m68k_memory[i].size; i++; }while (i < m68k_num_memory); -#else - if (vaddr < m68k_memory[0].size) - return m68k_memory[0].addr + vaddr; -#endif return mm_vtop_fallback(vaddr); } @@ -449,7 +373,6 @@ unsigned long mm_vtop_fallback (unsigned long vaddr) #ifndef CONFIG_SINGLE_MEMORY_CHUNK unsigned long mm_ptov (unsigned long paddr) { -#ifndef CONFIG_SINGLE_MEMORY_CHUNK int i = 0; unsigned long offset = 0; @@ -466,11 +389,6 @@ unsigned long mm_ptov (unsigned long paddr) offset += m68k_memory[i].size; i++; }while (i < m68k_num_memory); -#else - unsigned long base = m68k_memory[0].addr; - if (paddr >= base && paddr < (base + m68k_memory[0].size)) - return (paddr - base); -#endif /* * assume that the kernel virtual address is the same as the @@ -560,7 +478,7 @@ unsigned long mm_ptov (unsigned long paddr) * Jes was worried about performance (urhh ???) so its optional */ -extern void (*mach_l2_flush)(int) = NULL; +void (*mach_l2_flush)(int) = NULL; #endif /* diff --git a/arch/m68k/mvme16x/16xints.c b/arch/m68k/mvme16x/16xints.c index fbb370a07..9cc0d39d1 100644 --- a/arch/m68k/mvme16x/16xints.c +++ b/arch/m68k/mvme16x/16xints.c @@ -106,9 +106,12 @@ void mvme16x_free_irq(unsigned int irq, void *dev_id) void mvme16x_process_int (unsigned long vec, struct pt_regs *fp) { if (vec < 64 || vec > 255) - panic ("mvme16x_process_int: Illegal vector %ld", vec); - irq_tab[vec-64].count++; - irq_tab[vec-64].handler(vec, irq_tab[vec-64].dev_id, fp); + printk ("mvme16x_process_int: Illegal vector %ld", vec); + else + { + irq_tab[vec-64].count++; + irq_tab[vec-64].handler(vec, irq_tab[vec-64].dev_id, fp); + } } int mvme16x_get_irq_list (char *buf) diff --git a/arch/m68k/vmlinux.lds b/arch/m68k/vmlinux.lds index 88ee16b66..b79c9dd87 100644 --- a/arch/m68k/vmlinux.lds +++ b/arch/m68k/vmlinux.lds @@ -35,6 +35,9 @@ SECTIONS _edata = .; /* End of data section */ + . = ALIGN(16); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + . = ALIGN(4096); /* Init code and data */ __init_begin = .; .text.init : { *(.text.init) } diff --git a/arch/mips/config.in b/arch/mips/config.in index 8c66ff8c9..298e3d760 100644 --- a/arch/mips/config.in +++ b/arch/mips/config.in @@ -175,33 +175,35 @@ if [ "$CONFIG_NET" = "y" ]; then fi if [ "$CONFIG_SGI" != "y" ]; then - source drivers/net/hamradio/Config.in + source drivers/net/hamradio/Config.in - mainmenu_option next_comment - comment 'ISDN subsystem' - tristate 'ISDN support' CONFIG_ISDN - if [ "$CONFIG_ISDN" != "n" ]; then - source drivers/isdn/Config.in - fi - endmenu + mainmenu_option next_comment + comment 'ISDN subsystem' - mainmenu_option next_comment - comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' + if [ "$CONFIG_NET" != "n" ]; then + tristate 'ISDN support' CONFIG_ISDN + if [ "$CONFIG_ISDN" != "n" ]; then + source drivers/isdn/Config.in + fi + fi + endmenu - bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI - if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then - source drivers/cdrom/Config.in - fi - endmenu + + mainmenu_option next_comment + comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' + + bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI + if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/cdrom/Config.in + fi + endmenu fi source drivers/char/Config.in source fs/Config.in -source fs/nls/Config.in - if [ "$CONFIG_VT" = "y" ]; then mainmenu_option next_comment comment 'Console drivers' diff --git a/arch/mips/defconfig b/arch/mips/defconfig index 1b9ec2d0d..0b3de2ffe 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -142,6 +142,7 @@ CONFIG_SCSI_CONSTANTS=y # SCSI low-level drivers # # CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AHA152X is not set # CONFIG_SCSI_AHA1542 is not set # CONFIG_SCSI_AHA1740 is not set @@ -149,14 +150,16 @@ CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set # CONFIG_SCSI_BUSLOGIC is not set # CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_DMA is not set # CONFIG_SCSI_EATA_PIO is not set -# CONFIG_SCSI_EATA is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set CONFIG_SCSI_NCR53C8XX=y @@ -210,6 +213,8 @@ CONFIG_PCNET32=y # CONFIG_NET_RADIO is not set # CONFIG_TR is not set # CONFIG_HOSTESS_SV11 is not set +# CONFIG_COSA is not set +# CONFIG_RCPCI is not set # CONFIG_WAN_DRIVERS is not set # CONFIG_LAPBETHER is not set # CONFIG_X25_ASY is not set @@ -250,9 +255,17 @@ CONFIG_SERIAL=m # CONFIG_MOUSE is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set CONFIG_RTC=y + +# +# Video For Linux +# # CONFIG_VIDEO_DEV is not set -# CONFIG_NVRAM is not set + +# +# Joystick support +# # CONFIG_JOYSTICK is not set # @@ -264,35 +277,42 @@ CONFIG_RTC=y # Filesystems # CONFIG_QUOTA=y -CONFIG_MINIX_FS=m -CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=m -CONFIG_JOLIET=y +CONFIG_AUTOFS_FS=m +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m CONFIG_UMSDOS_FS=m CONFIG_VFAT_FS=m +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_MINIX_FS=m +# CONFIG_NTFS_FS is not set +CONFIG_HPFS_FS=m CONFIG_PROC_FS=y +CONFIG_ROMFS_FS=m +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set CONFIG_NFS_FS=m -CONFIG_NFSD=m # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=m CONFIG_LOCKD=m -# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set # CONFIG_NCP_FS is not set -CONFIG_HPFS_FS=m -# CONFIG_NTFS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set -CONFIG_ROMFS_FS=m -CONFIG_AUTOFS_FS=m -# CONFIG_UFS_FS is not set + +# +# Partition Types +# # CONFIG_BSD_DISKLABEL is not set +# CONFIG_MAC_PARTITION is not set # CONFIG_SMD_DISKLABEL is not set # CONFIG_SOLARIS_X86_PARTITION is not set -# CONFIG_MAC_PARTITION is not set CONFIG_NLS=y # @@ -323,6 +343,7 @@ CONFIG_NLS_ISO8859_6=m CONFIG_NLS_ISO8859_7=m CONFIG_NLS_ISO8859_8=m CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_15=m CONFIG_NLS_KOI8_R=m # diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index cc4919bff..e7372fbc3 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.12 1998/06/30 00:21:49 ralf Exp $ +/* $Id: irq.c,v 1.13 1999/01/03 17:50:50 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -334,7 +334,7 @@ unsigned long probe_irq_on (void) } /* wait for spurious interrupts to mask themselves out again */ - for (delay = jiffies + HZ/10; delay > jiffies; ) + for (delay = jiffies + HZ/10; time_before(jiffies, delay); ) /* about 100ms delay */; /* now filter out any obviously spurious interrupts */ diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c index 38bef5f56..302eee2b1 100644 --- a/arch/mips/kernel/mips_ksyms.c +++ b/arch/mips/kernel/mips_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: mips_ksyms.c,v 1.13 1999/02/07 21:56:32 ulfc Exp $ +/* $Id: mips_ksyms.c,v 1.14 1999/02/09 22:54:10 adevries Exp $ * * Export MIPS-specific functions needed for loadable modules. * @@ -50,6 +50,7 @@ EXPORT_SYMBOL_NOVERS(strncat); EXPORT_SYMBOL_NOVERS(strnlen); EXPORT_SYMBOL_NOVERS(strrchr); EXPORT_SYMBOL_NOVERS(strtok); +EXPORT_SYMBOL_NOVERS(strpbrk); EXPORT_SYMBOL(clear_page); EXPORT_SYMBOL(__mips_bh_counter); diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index ac7bfb771..6fb6735fd 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -1,4 +1,4 @@ -/* $Id: ptrace.c,v 1.9 1998/09/19 19:16:16 ralf Exp $ +/* $Id: ptrace.c,v 1.10 1999/01/03 17:50:52 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -337,7 +337,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_PEEKDATA: { unsigned long tmp; + down(&child->mm->mmap_sem); res = read_long(child, addr, &tmp); + up(&child->mm->mmap_sem); if (res < 0) goto out; res = put_user(tmp,(unsigned long *) data); @@ -406,7 +408,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: + down(&child->mm->mmap_sem); res = write_long(child,addr,data); + up(&child->mm->mmap_sem); goto out; case PTRACE_POKEUSR: { diff --git a/arch/mips/kernel/scall_o32.S b/arch/mips/kernel/scall_o32.S index 42cb4311d..b31a8b18e 100644 --- a/arch/mips/kernel/scall_o32.S +++ b/arch/mips/kernel/scall_o32.S @@ -44,6 +44,7 @@ NESTED(handle_sys, PT_SIZE, sp) beqz t2, illegal_syscall; subu t0, t3, 5 # 5 or more arguments? + sw a3, PT_R26(sp) # save a3 for syscall restarting bgez t0, stackargs stack_done: diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index bca8b228f..c9d832c92 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.16 1998/08/25 09:14:41 ralf Exp $ +/* $Id: signal.c,v 1.17 1998/08/28 23:29:05 tsbogend Exp $ * * linux/arch/mips/kernel/signal.c * @@ -392,6 +392,7 @@ static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) } /* fallthrough */ case ERESTARTNOINTR: /* Userland will reload $v0. */ + regs->regs[7] = regs->regs[26]; regs->cp0_epc -= 8; } @@ -525,6 +526,7 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) if (regs->regs[2] == ERESTARTNOHAND || regs->regs[2] == ERESTARTSYS || regs->regs[2] == ERESTARTNOINTR) { + regs->regs[7] = regs->regs[26]; regs->cp0_epc -= 8; } } diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 11415b79f..67e550cf3 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -1,10 +1,10 @@ -/* $Id: syscall.c,v 1.10 1998/08/20 14:38:40 ralf Exp $ +/* $Id: syscall.c,v 1.9 1998/08/25 09:14:41 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1995 - 1998 by Ralf Baechle + * Copyright (C) 1995 - 1999 by Ralf Baechle * * TODO: Implement the compatibility syscalls. * Don't waste that much memory for empty entries in the syscall @@ -80,11 +80,10 @@ out: asmlinkage int sys_idle(void) { unsigned long start_idle = 0; - int ret = -EPERM; - lock_kernel(); if (current->pid != 0) - goto out; + return -EPERM; + /* endless idle loop with no priority at all */ current->priority = 0; current->counter = 0; @@ -110,10 +109,8 @@ asmlinkage int sys_idle(void) start_idle = 0; schedule(); } - ret = 0; -out: - unlock_kernel(); - return ret; + + return 0; } asmlinkage int sys_fork(struct pt_regs regs) @@ -121,9 +118,7 @@ asmlinkage int sys_fork(struct pt_regs regs) int res; save_static(®s); - lock_kernel(); res = do_fork(SIGCHLD, regs.regs[29], ®s); - unlock_kernel(); return res; } @@ -134,13 +129,11 @@ asmlinkage int sys_clone(struct pt_regs regs) int res; save_static(®s); - lock_kernel(); clone_flags = regs.regs[4]; newsp = regs.regs[5]; if (!newsp) newsp = regs.regs[29]; res = do_fork(clone_flags, newsp, ®s); - unlock_kernel(); return res; } diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c index e6c27cd52..0c4503da5 100644 --- a/arch/mips/kernel/sysirix.c +++ b/arch/mips/kernel/sysirix.c @@ -1,4 +1,4 @@ -/* $Id: sysirix.c,v 1.14 1999/02/01 01:28:56 ralf Exp $ +/* $Id: sysirix.c,v 1.15 1999/02/06 05:12:57 adevries Exp $ * * sysirix.c: IRIX system call emulation. * @@ -2020,6 +2020,7 @@ out: asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, unsigned int count, int *eob) { struct file *file; + struct dentry *dentry; struct inode *inode; struct irix_dirent32 *lastdirent; struct irix_dirent32_callback buf; @@ -2031,44 +2032,55 @@ asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, unsigned int count current->pid, fd, dirent, count, eob); #endif error = -EBADF; - if (!(file = fget(fd))) + file = fget(fd); + if (!file) goto out; - /* Shouldn't it be ENOENT? */ - inode = file->f_dentry->d_inode; - if (!inode) - goto out_f; - - error = -ENOTDIR; - if (!file->f_op || !file->f_op->readdir) - goto out_f; + dentry = file->f_dentry; + if (!dentry) + goto out_putf; - error = -EFAULT; - if(!access_ok(VERIFY_WRITE, dirent, count) || - !access_ok(VERIFY_WRITE, eob, sizeof(*eob))) - goto out_f; + inode = dentry->d_inode; + if (!inode) + goto out_putf; - __put_user(0, eob); buf.current_dir = (struct irix_dirent32 *) dirent; buf.previous = NULL; buf.count = count; buf.error = 0; + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out_putf; + + /* + * Get the inode's semaphore to prevent changes + * to the directory while we read it. + */ + down(&inode->i_sem); error = file->f_op->readdir(file, &buf, irix_filldir32); + up(&inode->i_sem); if (error < 0) - goto out_f; + goto out_putf; + error = buf.error; lastdirent = buf.previous; - if (!lastdirent) { - error = buf.error; - goto out_f; + if (lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = count - buf.count; + } + + if (put_user(0, eob) < 0) { + error = EFAULT; + goto out_putf; } - lastdirent->d_off = (u32) file->f_pos; + + #ifdef DEBUG_GETDENTS printk("eob=%d returning %d\n", *eob, count - buf.count); #endif error = count - buf.count; -out_f: +out_putf: fput(file); out: unlock_kernel(); diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index cab6598db..1491da82d 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.9 1999/01/04 16:03:49 ralf Exp $ +/* $Id: time.c,v 1.10 1999/02/01 01:26:32 ralf Exp $ * * Copyright (C) 1991, 1992, 1995 Linus Torvalds * Copyright (C) 1996, 1997, 1998 Ralf Baechle @@ -253,9 +253,11 @@ void do_settimeofday(struct timeval *tv) } xtime = *tv; - time_state = TIME_BAD; - time_maxerror = MAXPHASE; - time_esterror = MAXPHASE; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; sti(); } @@ -265,6 +267,9 @@ void do_settimeofday(struct timeval *tv) * nowtime is written into the registers of the CMOS clock, it will * jump to the next second precisely 500 ms later. Check the Motorola * MC146818A or Dallas DS12887 data sheet for details. + * + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you won't notice until after reboot! */ static int set_rtc_mmss(unsigned long nowtime) { @@ -301,8 +306,12 @@ static int set_rtc_mmss(unsigned long nowtime) } CMOS_WRITE(real_seconds,RTC_SECONDS); CMOS_WRITE(real_minutes,RTC_MINUTES); - } else - retval = -1; + } else { + printk(KERN_WARNING + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } /* The following flags have to be released exactly in this order, * otherwise the DS12887 (popular MC146818A clone with integrated @@ -353,9 +362,10 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be * called as close as possible to 500 ms before the new second starts. */ - if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && + xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index f7fa99a37..5a9b651ff 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.9 1998/09/19 19:16:18 ralf Exp $ +/* $Id: init.c,v 1.10 1999/01/04 16:03:53 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -132,6 +132,7 @@ static inline unsigned long setup_zero_pages(void) pg = MAP_NR(empty_zero_page); while(pg < MAP_NR(empty_zero_page) + (1 << order)) { set_bit(PG_reserved, &mem_map[pg].flags); + atomic_set(&mem_map[pg].count, 0); pg++; } @@ -293,8 +294,9 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) #endif end_mem &= PAGE_MASK; - max_mapnr = num_physpages = MAP_NR(end_mem); + max_mapnr = MAP_NR(end_mem); high_memory = (void *)end_mem; + num_physpages = 0; /* mark usable pages in the mem_map[] */ start_mem = PAGE_ALIGN(start_mem); @@ -324,6 +326,7 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) datapages++; continue; } + num_physpages++; atomic_set(&mem_map[MAP_NR(tmp)].count, 1); #ifdef CONFIG_BLK_DEV_INITRD if (!initrd_start || (tmp < initrd_start || tmp >= diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c index 8c3401d6f..126b724be 100644 --- a/arch/ppc/8xx_io/enet.c +++ b/arch/ppc/8xx_io/enet.c @@ -1,4 +1,5 @@ /* + * $Id: enet.c,v 1.8 1998/11/15 19:58:07 cort Exp $ * Ethernet driver for Motorola MPC8xx. * Copyright (c) 1997 Dan Malek (dmalek@jlc.net) * @@ -657,7 +658,7 @@ __initfunc(int cpm_enet_init(void)) cp = cpmp; /* Get pointer to Communication Processor */ - immap = (immap_t *)MBX_IMAP_ADDR; /* and to internal registers */ + immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ /* Allocate some private information. */ @@ -719,11 +720,11 @@ __initfunc(int cpm_enet_init(void)) * These are relative offsets in the DP ram address space. * Initialize base addresses for the buffer descriptors. */ - i = mbx_cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE); + i = m8xx_cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE); ep->sen_genscc.scc_rbase = i; cep->rx_bd_base = (cbd_t *)&cp->cp_dpmem[i]; - i = mbx_cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE); + i = m8xx_cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE); ep->sen_genscc.scc_tbase = i; cep->tx_bd_base = (cbd_t *)&cp->cp_dpmem[i]; @@ -912,4 +913,3 @@ __initfunc(int cpm_enet_init(void)) return 0; } - diff --git a/arch/ppc/8xx_io/uart.c b/arch/ppc/8xx_io/uart.c index fdbd0c796..94901245f 100644 --- a/arch/ppc/8xx_io/uart.c +++ b/arch/ppc/8xx_io/uart.c @@ -1692,7 +1692,7 @@ static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout) schedule_timeout(char_time); if (signal_pending(current)) break; - if (timeout && ((orig_jiffies + timeout) < jiffies)) + if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; bdp = info->tx_cur; } while (bdp->cbd_sc & BD_SC_READY); diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile index cd98b4513..990b24b84 100644 --- a/arch/ppc/Makefile +++ b/arch/ppc/Makefile @@ -16,8 +16,6 @@ KERNELLOAD =0xc0000000 ifeq ($(shell uname -m),ppc) CHECKS = checks -else -CROSS_COMPILE = ppc-linux-elf- endif ASFLAGS = diff --git a/arch/ppc/amiga/amiints.c b/arch/ppc/amiga/amiints.c index 690173cc9..e8f72f298 100644 --- a/arch/ppc/amiga/amiints.c +++ b/arch/ppc/amiga/amiints.c @@ -19,7 +19,7 @@ * - IRQ_FLG_SLOW: handler is inserted at bottom of list and before * they're executed irq level is set to the previous * one, but handlers don't need to be reentrant, if - * reentrance occured, slow handlers will be just + * reentrance occurred, slow handlers will be just * called again. * The whole interrupt handling for CIAs is moved to cia.c * /Roman Zippel @@ -396,7 +396,7 @@ void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server) for (;;) { for (; node; node = node->next) node->handler(irq, node->dev_id, fp); - /* if reentrance occured, serve slow handlers again */ + /* if reentrance occurred, serve slow handlers again */ custom.intena = ami_intena_vals[irq]; if (!server->reentrance) { server->count--; diff --git a/arch/ppc/amiga/bootinfo.c b/arch/ppc/amiga/bootinfo.c index f24a0c5b8..7639de68e 100644 --- a/arch/ppc/amiga/bootinfo.c +++ b/arch/ppc/amiga/bootinfo.c @@ -17,8 +17,8 @@ extern char cmd_line[CL_SIZE]; int num_memory = 0; -extern struct mem_info memory[NUM_MEMINFO]; -extern struct mem_info ramdisk; +struct mem_info memory[NUM_MEMINFO]; +struct mem_info ramdisk; extern int amiga_parse_bootinfo(const struct bi_record *); extern int atari_parse_bootinfo(const struct bi_record *); diff --git a/arch/ppc/amiga/config.c b/arch/ppc/amiga/config.c index f79172390..2335adfb9 100644 --- a/arch/ppc/amiga/config.c +++ b/arch/ppc/amiga/config.c @@ -742,7 +742,7 @@ static void amiga_savekmsg_init(void) static void amiga_serial_putc(char c) { custom.serdat = (unsigned char)c | 0x100; - iobarrier (); + iobarrier_rw (); while (!(custom.serdatr & 0x2000)) ; } diff --git a/arch/ppc/apus_defconfig b/arch/ppc/apus_defconfig index 1e9499a9c..c9f900cfe 100644 --- a/arch/ppc/apus_defconfig +++ b/arch/ppc/apus_defconfig @@ -14,6 +14,7 @@ CONFIG_PMAC=y # CONFIG_ALL_PPC is not set # CONFIG_APUS is not set # CONFIG_MBX is not set +# CONFIG_SMP is not set CONFIG_MACH_SPECIFIC=y # diff --git a/arch/ppc/boot/Makefile b/arch/ppc/boot/Makefile index fde1ad4c6..da4945fd7 100644 --- a/arch/ppc/boot/Makefile +++ b/arch/ppc/boot/Makefile @@ -48,6 +48,9 @@ OBJECTS += mbxtty.o CFLAGS += -DCONFIG_MBX else OBJECTS += vreset.o kbd.o + ifeq ($(CONFIG_SERIAL_CONSOLE),y) + OBJECTS += ns16550.o + endif endif all: zImage diff --git a/arch/ppc/boot/misc.c b/arch/ppc/boot/misc.c index 42cb533da..470e1e4a9 100644 --- a/arch/ppc/boot/misc.c +++ b/arch/ppc/boot/misc.c @@ -1,7 +1,7 @@ /* * misc.c * - * $Id: misc.c,v 1.52 1998/09/19 01:21:24 cort Exp $ + * $Id: misc.c,v 1.53 1998/12/15 17:40:15 cort Exp $ * * Adapted for PowerPC by Gary Thomas * @@ -12,7 +12,7 @@ #include <linux/types.h> #include "../coffboot/zlib.h" #include "asm/residual.h" -#include <elf.h> +#include <linux/elf.h> #include <linux/config.h> #include <asm/page.h> #include <asm/processor.h> @@ -23,6 +23,10 @@ #ifdef CONFIG_FADS #include <asm/fads.h> #endif +#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) +#include "ns16550.h" +struct NS16550 *com_port; +#endif /* CONFIG_SERIAL_CONSOLE */ /* * Please send me load/board info and such data for hardware not @@ -42,6 +46,7 @@ char *end_avail; * save here and pass to the kernel (command line and board info). * On the MBX we grab some known memory holes to hold this information. */ +char cmd_preset[] = "console=tty0 console=ttyS0,9600n8"; char cmd_buf[256]; char *cmd_line = cmd_buf; @@ -108,12 +113,19 @@ static void scroll() tstc(void) { +#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) + return (CRT_tstc() || NS16550_tstc(com_port)); +#else return (CRT_tstc() ); +#endif /* CONFIG_SERIAL_CONSOLE */ } getc(void) { while (1) { +#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) + if (NS16550_tstc(com_port)) return (NS16550_getc(com_port)); +#endif /* CONFIG_SERIAL_CONSOLE */ if (CRT_tstc()) return (CRT_getc()); } } @@ -123,6 +135,11 @@ putc(const char c) { int x,y; +#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) + NS16550_putc(com_port, c); + if ( c == '\n' ) NS16550_putc(com_port, '\r'); +#endif /* CONFIG_SERIAL_CONSOLE */ + x = orig_x; y = orig_y; @@ -162,12 +179,21 @@ void puts(const char *s) y = orig_y; while ( ( c = *s++ ) != '\0' ) { +#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) + NS16550_putc(com_port, c); + if ( c == '\n' ) NS16550_putc(com_port, '\r'); +#endif /* CONFIG_SERIAL_CONSOLE */ + if ( c == '\n' ) { x = 0; if ( ++y >= lines ) { scroll(); y--; } + } else if (c == '\b') { + if (x > 0) { + x--; + } } else { vidmem [ ( x + cols * y ) * 2 ] = c; if ( ++x >= cols ) { @@ -358,6 +384,10 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R _put_MSR(_get_MSR() & ~0x0030); vga_init(0xC0000000); +#if defined(CONFIG_SERIAL_CONSOLE) && !defined(CONFIG_MBX) + com_port = (struct NS16550 *)NS16550_init(0); +#endif /* CONFIG_SERIAL_CONSOLE */ + if (residual) memcpy(hold_residual,residual,sizeof(RESIDUAL)); #else /* CONFIG_MBX */ @@ -510,6 +540,8 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R puts("\nLinux/PPC load: "); timer = 0; cp = cmd_line; + memcpy (cmd_line, cmd_preset, sizeof(cmd_preset)); + while ( *cp ) putc(*cp++); while (timer++ < 5*1000) { if (tstc()) { while ((ch = getc()) != '\n' && ch != '\r') { diff --git a/arch/ppc/boot/ns16550.c b/arch/ppc/boot/ns16550.c new file mode 100644 index 000000000..db0b94d03 --- /dev/null +++ b/arch/ppc/boot/ns16550.c @@ -0,0 +1,56 @@ +/* + * COM1 NS16550 support + */ + +#include "ns16550.h" +typedef struct NS16550 *NS16550_t; + +const NS16550_t COM_PORTS[] = { (NS16550_t) COM1, + (NS16550_t) COM2, + (NS16550_t) COM3, + (NS16550_t) COM4 }; + +volatile struct NS16550 * +NS16550_init(int chan) +{ + volatile struct NS16550 *com_port; + volatile unsigned char xx; + com_port = (struct NS16550 *) COM_PORTS[chan]; + /* See if port is present */ + com_port->lcr = 0x00; + com_port->ier = 0xFF; +#if 0 + if (com_port->ier != 0x0F) return ((struct NS16550 *)0); +#endif + com_port->ier = 0x00; + com_port->lcr = 0x80; /* Access baud rate */ + com_port->dll = 0xc; /* 9600 baud */ + com_port->dlm = 0xc >> 8; + com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */ + com_port->mcr = 0x03; /* RTS/DTR */ + com_port->fcr = 0x07; /* Clear & enable FIFOs */ + return (com_port); +} + + +NS16550_putc(volatile struct NS16550 *com_port, unsigned char c) +{ + volatile int i; + while ((com_port->lsr & LSR_THRE) == 0) ; + com_port->thr = c; +} + +unsigned char +NS16550_getc(volatile struct NS16550 *com_port) +{ + while ((com_port->lsr & LSR_DR) == 0) ; + return (com_port->rbr); +} + +NS16550_tstc(volatile struct NS16550 *com_port) +{ + return ((com_port->lsr & LSR_DR) != 0); +} + + + diff --git a/arch/ppc/boot/ns16550.h b/arch/ppc/boot/ns16550.h new file mode 100644 index 000000000..4baf4c1c5 --- /dev/null +++ b/arch/ppc/boot/ns16550.h @@ -0,0 +1,34 @@ +/* + * NS16550 Serial Port + */ + +struct NS16550 + { + unsigned char rbr; /* 0 */ + unsigned char ier; /* 1 */ + unsigned char fcr; /* 2 */ + unsigned char lcr; /* 3 */ + unsigned char mcr; /* 4 */ + unsigned char lsr; /* 5 */ + unsigned char msr; /* 6 */ + unsigned char scr; /* 7 */ + }; + +#define thr rbr +#define iir fcr +#define dll rbr +#define dlm ier + +#define LSR_DR 0x01 /* Data ready */ +#define LSR_OE 0x02 /* Overrun */ +#define LSR_PE 0x04 /* Parity error */ +#define LSR_FE 0x08 /* Framing error */ +#define LSR_BI 0x10 /* Break */ +#define LSR_THRE 0x20 /* Xmit holding register empty */ +#define LSR_TEMT 0x40 /* Xmitter empty */ +#define LSR_ERR 0x80 /* Error */ + +#define COM1 0x800003F8 +#define COM2 0x800002F8 +#define COM3 0x800003F8 +#define COM4 0x80000388 diff --git a/arch/ppc/chrp_defconfig b/arch/ppc/chrp_defconfig index dcaf4b2d5..9141b2d90 100644 --- a/arch/ppc/chrp_defconfig +++ b/arch/ppc/chrp_defconfig @@ -14,6 +14,7 @@ CONFIG_CHRP=y # CONFIG_ALL_PPC is not set # CONFIG_APUS is not set # CONFIG_MBX is not set +# CONFIG_SMP is not set CONFIG_MACH_SPECIFIC=y # diff --git a/arch/ppc/coffboot/Makefile b/arch/ppc/coffboot/Makefile index 72a6df455..4669d2228 100644 --- a/arch/ppc/coffboot/Makefile +++ b/arch/ppc/coffboot/Makefile @@ -3,18 +3,6 @@ # # Paul Mackerras January 1997 -# PowerPC (cross) tools -ifneq ($(shell uname -m),ppc) -CROSS_COMPILE =powerpc-eabi- -endif - - -# PowerPC (cross) tools -ifneq ($(shell uname -m),ppc) -#CROSS_COMPILE =powerpc-eabi- -CROSS_COMPILE =ppc-linux-elf- -endif - HOSTCC = gcc HOSTCFLAGS = -O -I$(TOPDIR)/include diff --git a/arch/ppc/common_defconfig b/arch/ppc/common_defconfig index 916af99f1..f58965709 100644 --- a/arch/ppc/common_defconfig +++ b/arch/ppc/common_defconfig @@ -14,6 +14,7 @@ CONFIG_6xx=y CONFIG_ALL_PPC=y # CONFIG_APUS is not set # CONFIG_MBX is not set +# CONFIG_SMP is not set # # General setup @@ -45,9 +46,9 @@ CONFIG_ADBMOUSE=y CONFIG_BLK_DEV_IDE_PMAC=y CONFIG_PROC_DEVICETREE=y # CONFIG_KGDB is not set -CONFIG_XMON=y +# CONFIG_XMON is not set # CONFIG_TOTALMP is not set -# CONFIG_BOOTX_TEXT is not set +CONFIG_BOOTX_TEXT=y # # Plug and Play support @@ -113,6 +114,10 @@ CONFIG_SKB_LARGE=y # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set # @@ -132,6 +137,7 @@ CONFIG_BLK_DEV_SR_VENDOR=y # SCSI low-level drivers # # CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AHA152X is not set # CONFIG_SCSI_AHA1542 is not set # CONFIG_SCSI_AHA1740 is not set @@ -139,14 +145,16 @@ CONFIG_BLK_DEV_SR_VENDOR=y # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set # CONFIG_SCSI_BUSLOGIC is not set # CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_DMA is not set # CONFIG_SCSI_EATA_PIO is not set -# CONFIG_SCSI_EATA is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set CONFIG_SCSI_NCR53C8XX=y @@ -170,7 +178,7 @@ CONFIG_SCSI_NCR53C8XX_IOMAPPED=y # CONFIG_SCSI_DEBUG is not set CONFIG_SCSI_MESH=y CONFIG_SCSI_MESH_SYNC_RATE=5 -CONFIG_SCSI_MAC53C94=m +CONFIG_SCSI_MAC53C94=y # # Network device support @@ -188,6 +196,7 @@ CONFIG_BMAC=y # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set # CONFIG_YELLOWFIN is not set +# CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y CONFIG_PCNET32=m @@ -219,6 +228,7 @@ CONFIG_SLIP=m # CONFIG_TR is not set # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set +# CONFIG_COSA is not set # # Amateur Radio support @@ -239,14 +249,31 @@ CONFIG_SLIP=m # Console drivers # CONFIG_DUMMY_CONSOLE=y -# CONFIG_FB_OF is not set +CONFIG_FB_OF=y +CONFIG_FB_CONTROL=y +CONFIG_FB_PLATINUM=y +CONFIG_FB_VALKYRIE=y +CONFIG_FB_ATY=y +CONFIG_FB_IMSTT=y +CONFIG_FB_CT65550=y +# CONFIG_FB_S3TRIO is not set # CONFIG_FB_MATROX is not set +CONFIG_FB_ATY=y # CONFIG_FB_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set +CONFIG_FBCON_CFB8=y +CONFIG_FBCON_CFB16=y +CONFIG_FBCON_CFB24=y +CONFIG_FBCON_CFB32=y # CONFIG_FBCON_FONTWIDTH8_ONLY is not set -# CONFIG_FBCON_FONTS is not set +CONFIG_FBCON_FONTS=y CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y +# CONFIG_FONT_SUN8x16 is not set +CONFIG_FONT_SUN12x22=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set # # Character devices @@ -258,6 +285,10 @@ CONFIG_SERIAL=m # CONFIG_SERIAL_NONSTANDARD is not set # CONFIG_UNIX98_PTYS is not set CONFIG_MOUSE=y + +# +# Mice +# # CONFIG_ATIXL_BUSMOUSE is not set # CONFIG_BUSMOUSE is not set # CONFIG_MS_BUSMOUSE is not set @@ -266,9 +297,17 @@ CONFIG_PSMOUSE=y # CONFIG_PC110_PAD is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set # CONFIG_RTC is not set + +# +# Video For Linux +# # CONFIG_VIDEO_DEV is not set -# CONFIG_NVRAM is not set + +# +# Joystick support +# # CONFIG_JOYSTICK is not set # @@ -280,36 +319,46 @@ CONFIG_PSMOUSE=y # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_MINIX_FS is not set -CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=y -# CONFIG_JOLIET is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +CONFIG_HFS_FS=y CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set CONFIG_NFS_FS=y -CONFIG_NFSD=y +CONFIG_NFSD=m # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_NTFS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_AFFS_FS is not set -CONFIG_HFS_FS=y -# CONFIG_ROMFS_FS is not set -# CONFIG_AUTOFS_FS is not set -# CONFIG_UFS_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# # CONFIG_BSD_DISKLABEL is not set +CONFIG_MAC_PARTITION=y # CONFIG_SMD_DISKLABEL is not set # CONFIG_SOLARIS_X86_PARTITION is not set -# CONFIG_ADFS_FS is not set -# CONFIG_QNX4FS_FS is not set -CONFIG_MAC_PARTITION=y +# CONFIG_UNIXWARE_DISKLABEL is not set CONFIG_NLS=y # @@ -340,6 +389,7 @@ CONFIG_NLS=y # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # @@ -368,6 +418,7 @@ CONFIG_SOUND_OSS=y # CONFIG_SOUND_OPL3SA2 is not set # CONFIG_SOUND_MAUI is not set # CONFIG_SOUND_SGALAXY is not set +# CONFIG_SOUND_AD1816 is not set # CONFIG_SOUND_OPL3SA1 is not set # CONFIG_SOUND_SOFTOSS is not set # CONFIG_SOUND_YM3812 is not set diff --git a/arch/ppc/config.in b/arch/ppc/config.in index 59223a45c..b15e90afd 100644 --- a/arch/ppc/config.in +++ b/arch/ppc/config.in @@ -22,6 +22,9 @@ choice 'Machine Type' \ if [ "$CONFIG_ALL_PPC" != "y" ];then define_bool CONFIG_MACH_SPECIFIC y fi + +bool 'Symmetric multi-processing support' CONFIG_SMP + endmenu if [ "$CONFIG_MBX" = "y" ];then @@ -164,7 +167,6 @@ endmenu source drivers/char/Config.in source fs/Config.in -source fs/nls/Config.in mainmenu_option next_comment comment 'Sound' diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig index ae1303bb4..811d599c0 100644 --- a/arch/ppc/defconfig +++ b/arch/ppc/defconfig @@ -15,6 +15,7 @@ CONFIG_PMAC=y # CONFIG_APUS is not set # CONFIG_MBX is not set CONFIG_MACH_SPECIFIC=y +# CONFIG_SMP is not set # # General setup @@ -137,6 +138,10 @@ CONFIG_ATALK=m # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set # @@ -164,6 +169,7 @@ CONFIG_SCSI_CONSTANTS=y # SCSI low-level drivers # # CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AHA152X is not set # CONFIG_SCSI_AHA1542 is not set # CONFIG_SCSI_AHA1740 is not set @@ -174,14 +180,16 @@ CONFIG_AIC7XXX_RESET_DELAY=15 # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set # CONFIG_SCSI_BUSLOGIC is not set # CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_DMA is not set # CONFIG_SCSI_EATA_PIO is not set -# CONFIG_SCSI_EATA is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set @@ -218,6 +226,7 @@ CONFIG_BMAC=y # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set # CONFIG_YELLOWFIN is not set +# CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y # CONFIG_PCNET32 is not set @@ -225,7 +234,7 @@ CONFIG_NET_EISA=y # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set CONFIG_DE4X5=y -CONFIG_DEC_ELCP=m +# CONFIG_DEC_ELCP is not set # CONFIG_DGRS is not set # CONFIG_EEXPRESS_PRO100 is not set # CONFIG_LNE390 is not set @@ -253,6 +262,7 @@ CONFIG_PPP=y # CONFIG_TR is not set # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set +# CONFIG_COSA is not set # # Amateur Radio support @@ -282,6 +292,7 @@ CONFIG_FB_IMSTT=y CONFIG_FB_CT65550=y # CONFIG_FB_S3TRIO is not set # CONFIG_FB_MATROX is not set +CONFIG_FB_ATY=y # CONFIG_FB_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set CONFIG_FBCON_CFB8=y @@ -303,7 +314,7 @@ CONFIG_FONT_SUN12x22=y # CONFIG_VT=y CONFIG_VT_CONSOLE=y -CONFIG_SERIAL=m +# CONFIG_SERIAL is not set # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set CONFIG_UNIX98_PTYS=y @@ -311,9 +322,17 @@ CONFIG_UNIX98_PTY_COUNT=256 # CONFIG_MOUSE is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set +CONFIG_NVRAM=y # CONFIG_RTC is not set + +# +# Video For Linux +# # CONFIG_VIDEO_DEV is not set -CONFIG_NVRAM=y + +# +# Joystick support +# # CONFIG_JOYSTICK is not set # @@ -325,37 +344,47 @@ CONFIG_NVRAM=y # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_MINIX_FS is not set -CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=y -# CONFIG_JOLIET is not set +CONFIG_AUTOFS_FS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +CONFIG_HFS_FS=y CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set CONFIG_VFAT_FS=m +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set CONFIG_NFS_FS=y CONFIG_NFSD=y # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_NTFS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_AFFS_FS is not set -CONFIG_HFS_FS=y -# CONFIG_ROMFS_FS is not set -CONFIG_AUTOFS_FS=y -# CONFIG_UFS_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# # CONFIG_BSD_DISKLABEL is not set +CONFIG_MAC_PARTITION=y # CONFIG_SMD_DISKLABEL is not set # CONFIG_SOLARIS_X86_PARTITION is not set -CONFIG_DEVPTS_FS=y -# CONFIG_ADFS_FS is not set -# CONFIG_QNX4FS_FS is not set -CONFIG_MAC_PARTITION=y +# CONFIG_UNIXWARE_DISKLABEL is not set CONFIG_NLS=y # @@ -386,6 +415,7 @@ CONFIG_NLS_CODEPAGE_437=y # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 4846d5ebb..d047c2086 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -15,8 +15,7 @@ OX_OBJS := ppc_ksyms.o setup.o O_OBJS := traps.o irq.o idle.o time.o process.o signal.o syscalls.o misc.o \ - bitops.o ptrace.o align.o ppc_htab.o feature.o - + bitops.o ptrace.o align.o ppc_htab.o ifdef CONFIG_PCI O_OBJS += pci.o endif @@ -33,15 +32,17 @@ else ifeq ($(CONFIG_APUS),y) O_OBJS += apus_setup.o prom.o openpic.o else +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 + residual.o prom.o openpic.o feature.o OX_OBJS += chrp_setup.o prep_setup.o endif endif +endif -ifdef SMP +ifdef CONFIG_SMP O_OBJS += smp.o endif diff --git a/arch/ppc/kernel/apus_setup.c b/arch/ppc/kernel/apus_setup.c index 93c2fe2d1..c3a81ad3e 100644 --- a/arch/ppc/kernel/apus_setup.c +++ b/arch/ppc/kernel/apus_setup.c @@ -329,9 +329,9 @@ unsigned long mm_ptov (unsigned long paddr) int i; for (i = 0; i < kmap_chunk_count;){ - unsigned long virt = kmap_chunks[i++]; - unsigned long size = kmap_chunks[i++]; unsigned long phys = kmap_chunks[i++]; + unsigned long size = kmap_chunks[i++]; + unsigned long virt = kmap_chunks[i++]; if (paddr >= phys && paddr < (phys + size)){ ret = virt + paddr - phys; diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index 4c528beb8..d7b791387 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.111 1998/11/10 01:10:32 paulus Exp $ + * $Id: head.S,v 1.114 1998/12/28 10:28:45 paulus Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -148,6 +148,10 @@ _start: * r4: virtual address of boot_infos_t * r5: 0 * + * APUS + * r3: 'APUS' + * Linux/m68k style BootInfo structure at &_end. + * * PREP * This is jumped to on prep systems right after the kernel is relocated * to its proper place in memory by the boot loader. The expected layout @@ -528,9 +532,10 @@ HardwareInterrupt: stw r3,(_CCR+4)(r21); #endif - addi r3,r1,STACK_FRAME_OVERHEAD; - li r20,MSR_KERNEL; - bl transfer_to_handler; + addi r3,r1,STACK_FRAME_OVERHEAD + li r20,MSR_KERNEL + li r4,0 + bl transfer_to_handler .long do_IRQ; .long int_return @@ -1195,18 +1200,34 @@ stack_ovf: * physical address of the hash table are known. These definitions * of Hash_base and Hash_bits below are just an example. */ -/* - * Note that the 603s won't come here, since the 603 - * loads tlb directly into the tlb from the linux tables, while - * others (601, 604, etc.) call hash_page() to load entries from - * the linux tables into the hash table. -- Cort - */ 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__ + lis r2,hash_table_lock@h + ori r2,r2,hash_table_lock@l + tophys(r2,r2,r6) + lis r6,100000000@h + mtctr r6 + lwz r0,PROCESSOR-TSS(r5) + or r0,r0,r6 +10: lwarx r6,0,r2 + cmpi 0,r6,0 + bne- 12f + stwcx. r0,0,r2 + beq+ 11f +12: cmpw r6,r0 + bdnzf 2,10b + tw 31,31,31 +11: +#endif /* Get PTE (linux-style) and check access */ lwz r5,PG_TABLES(r5) tophys(r5,r5,r2) /* convert to phys addr */ @@ -1253,6 +1274,9 @@ 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 @@ -1297,7 +1321,6 @@ hash_page_patch_C: bdnzf 2,2b beq+ found_empty -#if 1 /* * Choose an arbitrary slot in the primary PTEG to overwrite. * Since both the primary and secondary PTEGs are full, and we @@ -1313,26 +1336,6 @@ hash_page_patch_C: andi. r2,r2,0x38 stw r2,next_slot@l(0) add r3,r4,r2 -#else - /* now, allow 2nd hash as well as 1st */ - lwz r2,next_slot@l(0) - addi r2,r2,8 - andi. r2,r2,0x78 - stw r2,next_slot@l(0) - cmpi 0,0,r2,0x38 /* if it's the 2nd hash */ - bgt second_evict -first_evict: - xori r5,r5,0x40 /* clear H bit again */ - add r3,r4,r2 - b 11f -second_evict: - .globl hash_page_patch_D -hash_page_patch_D: - xoris r3,r4,Hash_msk>>16 /* compute secondary hash */ - xori r3,r3,0xffc0 - subi r2,r2,0x40 - addi r3,r3,r2 -#endif 11: /* update counter of evicted pages */ lis r2,htab_evicts@h @@ -1372,6 +1375,13 @@ found_slot: addi r3,r3,1 stw r3,0(r2) +#ifdef __SMP__ + lis r2,hash_table_lock@ha + tophys(r2,r2,r6) + li r0,0 + stw r0,hash_table_lock@l(r2) +#endif + /* Return from the exception */ lwz r3,_CCR(r21) lwz r4,_LINK(r21) @@ -1392,6 +1402,12 @@ found_slot: rfi hash_page_out: +#ifdef __SMP__ + lis r2,hash_table_lock@ha + tophys(r2,r2,r6) + li r0,0 + stw r0,hash_table_lock@l(r2) +#endif blr next_slot: .long 0 @@ -1709,6 +1725,9 @@ start_here: 2: SYNC /* Force all PTE updates to finish */ tlbia /* Clear all TLB entries */ +#ifdef __SMP__ + tlbsync +#endif #ifndef CONFIG_8xx mtspr SDR1,r6 li r0,16 /* load up segment register values */ @@ -1980,16 +1999,13 @@ _GLOBAL(_switch) /* FALL THROUGH into int_return */ #ifdef __SMP__ - /* drop scheduler_lock since we weren't called by schedule() */ + /* 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 - lis r5,scheduler_lock@ha stw r3,TSS_SMP_FORK_RET(r4) - stw r3,scheduler_lock@l+4(r5) /* owner_pc */ - stw r3,scheduler_lock@l+8(r5) /* owner_cpu */ - stw r3,scheduler_lock@l(r5) /* lock */ + bl schedule_tail #endif /* __SMP__ */ /* @@ -2089,6 +2105,7 @@ _GLOBAL(fake_interrupt) li r0,0x0fac stw r0,TRAP(r1) addi r3,r1,STACK_FRAME_OVERHEAD + li r4,1 bl do_IRQ addi r1,r1,INT_FRAME_SIZE+STACK_UNDERHEAD lwz r0,4(r1) @@ -2206,8 +2223,31 @@ _GLOBAL(flush_hash_segments) rlwinm r0,r0,16,27,31 lis r9,PVR_603_LIKE@h rlwnm. r0,r9,r0,0,0 - bne 99f + beq+ 99f + tlbia + isync + blr +99: #endif /* NO_RELOAD_HTAB */ +#ifdef __SMP__ + /* Note - we had better not do anything which could generate + a hash table miss while we have the hash table locked, + or we'll get a deadlock. -paulus */ + mfmsr r10 + sync + rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + mtmsr r0 + SYNC + lis r9,hash_table_lock@h + ori r9,r9,hash_table_lock@l + lwz r8,PROCESSOR(r2) + oris r8,r8,8 +10: lwarx r6,0,r9 + cmpi 0,r6,0 + bne- 10b + stwcx. r8,0,r9 + bne- 10b +#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 */ @@ -2229,8 +2269,16 @@ _GLOBAL(flush_hash_segments) stw r0,0(r5) /* invalidate entry */ 2: bdnz 1b /* continue with loop */ sync -99: tlbia + tlbia isync +#ifdef __SMP__ + tlbsync + lis r3,hash_table_lock@ha + li r0,0 + stw r0,hash_table_lock@l(r3) + mtmsr r10 + SYNC +#endif blr /* @@ -2244,8 +2292,31 @@ _GLOBAL(flush_hash_page) rlwinm r0,r0,16,27,31 lis r9,PVR_603_LIKE@h rlwnm. r0,r9,r0,0,0 - bne 99f + beq+ 99f + tlbie r4 /* in hw tlb too */ + isync + blr +99: #endif /* NO_RELOAD_HTAB */ +#ifdef __SMP__ + /* Note - we had better not do anything which could generate + a hash table miss while we have the hash table locked, + or we'll get a deadlock. -paulus */ + mfmsr r10 + sync + rlwinm r0,r10,0,17,15 /* clear bit 16 (MSR_EE) */ + mtmsr r0 + SYNC + lis r9,hash_table_lock@h + ori r9,r9,hash_table_lock@l + lwz r8,PROCESSOR(r2) + oris r8,r8,9 +10: lwarx r6,0,r9 + cmpi 0,r6,0 + bne- 10b + stwcx. r8,0,r9 + bne- 10b +#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 */ oris r3,r3,0x8000 /* set V (valid) bit */ @@ -2278,10 +2349,19 @@ _GLOBAL(flush_hash_page) 3: li r0,0 stw r0,0(r7) /* invalidate entry */ 4: sync -99: tlbie r4 /* in hw tlb too */ + tlbie r4 /* in hw tlb too */ isync +#ifdef __SMP__ + tlbsync + lis r3,hash_table_lock@h + li r0,0 + stw r0,hash_table_lock@l(r3) + mtmsr r10 + SYNC +#endif blr #endif /* CONFIG_8xx */ + /* * This routine is just here to keep GCC happy - sigh... */ diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index b6c338946..af163699b 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -1,5 +1,5 @@ /* - * $Id: idle.c,v 1.56 1998/10/13 19:14:36 paulus Exp $ + * $Id: idle.c,v 1.57 1998/12/28 10:28:46 paulus Exp $ * * Idle daemon for PowerPC. Idle daemon will handle any action * that needs to be taken when the system becomes idle. @@ -41,26 +41,25 @@ unsigned long powersave_nap = 0; int idled(void *unused) { - int ret = -EPERM; - + /* endless loop with no priority at all */ + current->priority = 0; + current->counter = -100; for (;;) { __sti(); - /* endless loop with no priority at all */ - current->priority = 0; - current->counter = 0; - check_pgt_cache(); if ( !current->need_resched && zero_paged_on ) zero_paged(); if ( !current->need_resched && htab_reclaim_on ) htab_reclaim(); if ( !current->need_resched ) power_save(); - run_task_queue(&tq_scheduler); - schedule(); + +#ifdef __SMP__ + if (current->need_resched) +#endif + schedule(); } - ret = 0; - return ret; + return 0; } #ifdef __SMP__ diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index a180e5f0a..f4a7c7143 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -1,4 +1,6 @@ /* + * $Id: irq.c,v 1.91 1998/12/28 10:28:47 paulus Exp $ + * * arch/ppc/kernel/irq.c * * Derived from arch/i386/kernel/irq.c @@ -6,6 +8,7 @@ * Adapted from arch/i386 by Gary Thomas * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) * Updated and modified by Cort Dougan (cort@cs.nmt.edu) + * Copyright (C) 1996 Cort Dougan * Adapted for Power Macintosh by Paul Mackerras * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). @@ -309,14 +312,14 @@ static void __openfirmware chrp_unmask_irq(unsigned int irq_nr) static void mbx_mask_irq(unsigned int irq_nr) { cached_irq_mask[0] &= ~(1 << (31-irq_nr)); - ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask = + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = cached_irq_mask[0]; } static void mbx_unmask_irq(unsigned int irq_nr) { cached_irq_mask[0] |= (1 << (31-irq_nr)); - ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_simask = + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = cached_irq_mask[0]; } #endif /* CONFIG_8xx */ @@ -454,7 +457,6 @@ static inline void wait_on_bh(void) } -#define MAXCOUNT 100000000 static inline void wait_on_irq(int cpu) { int count = MAXCOUNT; @@ -507,7 +509,7 @@ static inline void wait_on_irq(int cpu) void synchronize_bh(void) { if (atomic_read(&global_bh_count) && !in_interrupt()) - wait_on_bh(); + wait_on_bh(); } @@ -529,6 +531,8 @@ void synchronize_irq(void) static inline void get_irqlock(int cpu) { + unsigned int loops = MAXCOUNT; + if (test_and_set_bit(0,&global_irq_lock)) { /* do we already hold the lock? */ if ((unsigned char) cpu == global_irq_holder) @@ -536,12 +540,17 @@ static inline void get_irqlock(int cpu) /* Uhhuh.. Somebody else got it. Wait.. */ do { do { - + if (loops-- == 0) { + printk("get_irqlock(%d) waiting, global_irq_holder=%d\n", cpu, global_irq_holder); +#ifdef CONFIG_XMON + xmon(0); +#endif + } } while (test_bit(0,&global_irq_lock)); } while (test_and_set_bit(0,&global_irq_lock)); } /* - * We also to make sure that nobody else is running + * We also need to make sure that nobody else is running * in an interrupt context. */ wait_on_irq(cpu); @@ -637,7 +646,7 @@ void __global_restore_flags(unsigned long flags) #endif /* __SMP__ */ -asmlinkage void do_IRQ(struct pt_regs *regs) +asmlinkage void do_IRQ(struct pt_regs *regs, int isfake) { int irq; unsigned long bits; @@ -656,9 +665,14 @@ asmlinkage void do_IRQ(struct pt_regs *regs) #ifdef __SMP__ if ( cpu != 0 ) { - if (!atomic_read(&n_lost_interrupts)) + 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; } @@ -666,6 +680,25 @@ asmlinkage void do_IRQ(struct pt_regs *regs) 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 ) @@ -799,9 +832,13 @@ apus_out: } if (irq < 0) { - printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", - irq, regs->nip); - spurious_interrupts++; + /* 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; } @@ -809,7 +846,7 @@ apus_out: /* For MPC8xx, read the SIVEC register and shift the bits down * to get the irq number. */ - bits = ((immap_t *)MBX_IMAP_ADDR)->im_siu_conf.sc_sivec; + bits = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec; irq = bits >> 26; #endif /* CONFIG_8xx */ mask_and_ack_irq(irq); @@ -1089,17 +1126,6 @@ __initfunc(void init_IRQ(void)) */ if ( _prep_type == _PREP_IBM ) irq_mode2 |= 0xa0; - /* - * Sound on the Powerstack reportedly needs to be edge triggered - */ - if ( _prep_type == _PREP_Motorola ) - { - irq_mode2 &= ~0x04L; - irq_mode2 = 0xca; - outb( irq_mode1 , 0x4d0 ); - outb( irq_mode2 , 0x4d1 ); - } - } break; #ifdef CONFIG_APUS @@ -1116,8 +1142,7 @@ __initfunc(void init_IRQ(void)) /* This routine will fix some missing interrupt values in the device tree * on the gatwick mac-io controller used by some PowerBooks */ -__pmac -static void pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base) +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]; diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c index 9d26e27d1..90647dcd9 100644 --- a/arch/ppc/kernel/mbx_setup.c +++ b/arch/ppc/kernel/mbx_setup.c @@ -1,4 +1,6 @@ /* + * $Id: mbx_setup.c,v 1.5 1998/12/29 18:55:07 cort Exp $ + * * linux/arch/ppc/kernel/setup.c * * Copyright (C) 1995 Linus Torvalds @@ -51,17 +53,27 @@ extern int rd_image_start; /* starting block # of image */ extern char saved_command_line[256]; extern unsigned long find_available_memory(void); -extern void mbx_cpm_reset(uint); - +extern void m8xx_cpm_reset(uint); -void mbx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) +/* this really does make things cleaner -- Cort */ +void __init powermac_init(void) { +} - *p = 0; - *irq = 0; +void __init adbdev_init(void) +{ +} - if (base != 0) /* Only map the first ATA flash drive */ - return; +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) @@ -88,7 +100,7 @@ mbx_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) /* Reset the Communication Processor Module. */ - mbx_cpm_reset(cpm_page); + m8xx_cpm_reset(cpm_page); #ifdef notdef ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index f13508d96..e4589b1e0 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -383,6 +383,177 @@ _GLOBAL(_set_THRM3) mtspr THRM3,r3 blr +_GLOBAL(_get_PVR) + mfspr r3,PVR + blr +/* + L2CR functions + Copyright © 1997-1998 by PowerLogix R & D, Inc. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + Thur, Dec. 12, 1998. + - First public release, contributed by PowerLogix. + + Author: Terry Greeniaus (tgree@phys.ualberta.ca) + Please e-mail updates to this file to me, thanks! +*/ + +_GLOBAL(_set_L2CR) + /* Usage: + + When setting the L2CR register, you must do a few special things. If you are enabling the + cache, you must perform a global invalidate. If you are disabling the cache, you must + flush the cache contents first. This routine takes care of doing these things. When first + enabling the cache, make sure you pass in the L2CR you want, as well as passing in the + global invalidate bit set. A global invalidate will only be performed if the L2I bit is set + in applyThis. When enabling the cache, you should also set the L2E bit in applyThis. If you + want to modify the L2CR contents after the cache has been enabled, the recommended + procedure is to first call __setL2CR(0) to disable the cache and then call it again with + the new values for L2CR. Examples: + + _setL2CR(0) - disables the cache + _setL2CR(0xB3A04000) - enables my G3 upgrade card: + - L2E set to turn on the cache + - L2SIZ set to 1MB + - L2CLK set to 1:1 + - L2RAM set to pipelined syncronous late-write + - L2I set to perform a global invalidation + - L2OH set to 0.5 nS + - L2DF set because this upgrade card requires it + + A similar call should work for your card. You need to know the correct setting for your + card and then place them in the fields I have outlined above. Other fields support optional + features, such as L2DO which caches only data, or L2TS which causes cache pushes from + the L1 cache to go to the L2 cache instead of to main memory. + */ + + /* Make sure this is a 750 chip */ + mfspr r4,PVR + rlwinm r4,r4,16,16,31 + cmplwi r4,0x0008 + beq thisIs750 + li r3,-1 + blr + +thisIs750: + /* Get the current enable bit of the L2CR into r4 */ + mfspr r4,L2CR + rlwinm r4,r4,0,0,0 + + /* See if we want to perform a global inval this time. */ + rlwinm r6,r3,0,10,10 /* r6 contains the new invalidate bit */ + rlwinm. r5,r3,0,0,0 /* r5 contains the new enable bit */ + rlwinm r3,r3,0,11,9 /* Turn off the invalidate bit */ + rlwinm r3,r3,0,1,31 /* Turn off the enable bit */ + or r3,r3,r4 /* Keep the enable bit the same as it was for now. */ + bne dontDisableCache /* Only disable the cache if L2CRApply has the enable bit off */ + +disableCache: + /* Disable the cache. First, we turn off data relocation. */ + mfmsr r7 + rlwinm r4,r7,0,28,26 /* Turn off DR bit */ + rlwinm r4,r4,0,17,15 /* Turn off EE bit - an external exception while we are flushing + the cache is fatal (comment this line and see!) */ + sync + mtmsr r4 + sync + + /* + Now, read the first 2MB of memory to put new data in the cache. + (Actually we only need the size of the L2 cache plus + the size of the L1 cache, but 2MB will cover everything just to be safe). + */ + lis r4,0x0001 + mtctr r4 + li r4,0 +loadLoop: + lwzx r0,r0,r4 + addi r4,r4,0x0020 /* Go to start of next cache line */ + bdnz loadLoop + + /* Now, flush the first 2MB of memory */ + lis r4,0x0001 + mtctr r4 + li r4,0 + sync +flushLoop: + dcbf r0,r4 + addi r4,r4,0x0020 /* Go to start of next cache line */ + bdnz flushLoop + + /* Turn off the L2CR enable bit. */ + rlwinm r3,r3,0,1,31 + + /* Reenable data relocation. */ + sync + mtmsr r7 + sync + +dontDisableCache: + /* Set up the L2CR configuration bits */ + sync + mtspr L2CR,r3 + sync + cmplwi r6,0 + beq noInval + + /* Perform a global invalidation */ + oris r3,r3,0x0020 + sync + mtspr 1017,r3 + sync +invalCompleteLoop: /* Wait for the invalidation to complete */ + mfspr r3,1017 + rlwinm. r4,r3,0,31,31 + bne invalCompleteLoop + + rlwinm r3,r3,0,11,9; /* Turn off the L2I bit */ + sync + mtspr L2CR,r3 + sync + +noInval: + /* See if we need to enable the cache */ + cmplwi r5,0 + beqlr + +enableCache: + /* Enable the cache */ + oris r3,r3,0x8000 + mtspr L2CR,r3 + sync + blr + +_GLOBAL(_get_L2CR) + /* Make sure this is a 750 chip */ + mfspr r3,PVR + rlwinm r3,r3,16,16,31 + cmplwi r3,0x0008 + li r3,0 + bnelr + + /* Return the L2CR contents */ + mfspr r3,L2CR + blr + +/* --- End of PowerLogix code --- + */ + +/* _GLOBAL(_get_L2CR) mfspr r3,L2CR blr @@ -391,9 +562,7 @@ _GLOBAL(_set_L2CR) mtspr L2CR,r3 blr -_GLOBAL(_get_PVR) - mfspr r3,PVR - blr +*/ /* * These are used in the alignment trap handler when emulating @@ -441,16 +610,6 @@ _GLOBAL(__kernel_thread) bnelr /* return if parent */ mtlr r4 /* fn addr in lr */ mr r3,r5 /* load arg and call fn */ -#if 0/*def __SMP__*/ - /* drop scheduler_lock since schedule() called us */ - lis r4,scheduler_lock@ha - li r5,0 - stw r5,scheduler_lock@l+4(r4) /* owner_pc */ - stw r5,scheduler_lock@l+8(r4) /* owner_cpu */ - stw r5,scheduler_lock@l(r4) - sync - isync -#endif /* __SMP__ */ blrl li r0,__NR_exit /* exit after child exits */ li r3,0 diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 359446f4f..1dfa3a6c8 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1,5 +1,5 @@ /* - * $Id: pci.c,v 1.39 1998/10/13 20:59:04 cort Exp $ + * $Id: pci.c,v 1.43 1998/12/29 18:55:11 cort Exp $ * Common pmac/prep/chrp pci routines. -- Cort */ @@ -123,8 +123,8 @@ __initfunc(void pcibios_init(void)) __initfunc(void setup_pci_ptrs(void)) { - PPC_DEVICE *hostbridge; #ifndef CONFIG_MBX + PPC_DEVICE *hostbridge; switch (_machine) { case _MACH_prep: hostbridge=residual_find_device(PROCESSORDEVICE, NULL, @@ -188,13 +188,8 @@ __initfunc(void pcibios_fixup(void)) extern struct bridge_data **bridges; extern unsigned char *Motherboard_map; extern unsigned char *Motherboard_routes; - - /* - * FIXME: This is broken: We should not assign IRQ's to IRQless - * devices (look at PCI_INTERRUPT_PIN) and we also should - * honor the existence of multi-function devices where - * different functions have different interrupt pins. [mj] - */ + unsigned char i; +#ifndef CONFIG_MBX switch (_machine ) { case _MACH_prep: @@ -208,6 +203,21 @@ __initfunc(void pcibios_fixup(void)) */ 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 @@ -255,6 +265,11 @@ __initfunc(void pcibios_fixup(void)) } break; } +#else /* CONFIG_MBX */ + for(dev=pci_devices; dev; dev=dev->next) + { + } +#endif /* CONFIG_MBX */ } __initfunc(void pcibios_fixup_bus(struct pci_bus *bus)) diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c index 3aa0534ea..30123c076 100644 --- a/arch/ppc/kernel/ppc_htab.c +++ b/arch/ppc/kernel/ppc_htab.c @@ -1,5 +1,5 @@ /* - * $Id: ppc_htab.c,v 1.25 1998/08/26 10:28:26 davem Exp $ + * $Id: ppc_htab.c,v 1.26 1998/12/10 00:24:23 cort Exp $ * * PowerPC hash table management proc entry. Will show information * about the current hash table and will allow changes to it. @@ -569,6 +569,7 @@ int proc_dol2crvec(ctl_table *table, int write, struct file *filp, break; buffer += len; left -= len; + _set_L2CR(0); _set_L2CR(val); while ( _get_L2CR() & 0x1 ) /* wait for invalidate to finish */; diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index ea5f6db0d..1b7753dd0 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -25,6 +25,7 @@ #include <asm/pci-bridge.h> #include <asm/irq.h> #include <asm/feature.h> +#include <asm/spinlock.h> #define __KERNEL_SYSCALLS__ #include <linux/unistd.h> @@ -32,7 +33,7 @@ extern void transfer_to_handler(void); extern void int_return(void); extern void syscall_trace(void); -extern void do_IRQ(struct pt_regs *regs); +extern void do_IRQ(struct pt_regs *regs, int isfake); extern void MachineCheckException(struct pt_regs *regs); extern void AlignmentException(struct pt_regs *regs); extern void ProgramCheckException(struct pt_regs *regs); @@ -156,6 +157,19 @@ EXPORT_SYMBOL(_get_PVR); EXPORT_SYMBOL(giveup_fpu); EXPORT_SYMBOL(flush_icache_range); EXPORT_SYMBOL(xchg_u32); +#ifdef __SMP__ +EXPORT_SYMBOL(__global_cli); +EXPORT_SYMBOL(__global_sti); +EXPORT_SYMBOL(__global_save_flags); +EXPORT_SYMBOL(__global_restore_flags); +EXPORT_SYMBOL(_spin_lock); +EXPORT_SYMBOL(_spin_unlock); +EXPORT_SYMBOL(spin_trylock); +EXPORT_SYMBOL(_read_lock); +EXPORT_SYMBOL(_read_unlock); +EXPORT_SYMBOL(_write_lock); +EXPORT_SYMBOL(_write_unlock); +#endif #ifndef CONFIG_MACH_SPECIFIC EXPORT_SYMBOL(_machine); @@ -180,12 +194,15 @@ 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); EXPORT_SYMBOL(feature_clear); EXPORT_SYMBOL(feature_test); +#ifdef CONFIG_SCSI EXPORT_SYMBOL(note_scsi_host); +#endif EXPORT_SYMBOL(kd_mksound); #ifdef CONFIG_PMAC EXPORT_SYMBOL(nvram_read_byte); diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c index b7d94d208..b48f7a1fc 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.23 1998/10/21 10:52:24 cort Exp $ + * $Id: prep_pci.c,v 1.24 1998/12/10 02:39:51 cort Exp $ * PReP pci functions. * Originally by Gary Thomas * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) @@ -107,7 +107,7 @@ static char Omaha_pci_IRQ_routes[] __prepdata = }; /* Motorola PowerStack */ -static char Blackhawk_pci_IRQ_map[16] __prepdata = +static char Blackhawk_pci_IRQ_map[19] __prepdata = { 0, /* Slot 0 - unused */ 0, /* Slot 1 - unused */ @@ -125,6 +125,9 @@ static char Blackhawk_pci_IRQ_map[16] __prepdata = 0, /* Slot 13 - unused */ 1, /* Slot 14 - Ethernet */ 0, /* Slot 15 - unused */ + 1, /* Slot P7 */ + 2, /* Slot P6 */ + 3, /* Slot P5 */ }; static char Blackhawk_pci_IRQ_routes[] __prepdata = @@ -132,7 +135,7 @@ static char Blackhawk_pci_IRQ_routes[] __prepdata = 0, /* Line 0 - Unused */ 9, /* Line 1 */ 11, /* Line 2 */ - 14, /* Line 3 */ + 15, /* Line 3 */ 15 /* Line 4 */ }; @@ -226,6 +229,7 @@ static char ibm8xx_pci_IRQ_map[23] __prepdata = { 0, /* Slot 21 - unused */ 2, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ }; + static char ibm8xx_pci_IRQ_routes[] __prepdata = { 0, /* Line 0 - unused */ 13, /* Line 1 */ @@ -440,6 +444,8 @@ __initfunc(unsigned long route_pci_interrupts(void)) if ( _prep_type == _PREP_Motorola) { + unsigned short irq_mode; + switch (inb(0x800) & 0xF0) { case 0x10: /* MVME16xx */ @@ -474,6 +480,14 @@ __initfunc(unsigned long route_pci_interrupts(void)) Motherboard_routes = Blackhawk_pci_IRQ_routes; break; } + /* 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 ); } else if ( _prep_type == _PREP_IBM ) { unsigned char pl_id; diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index e596f9ea5..72752e11f 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -189,11 +189,6 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) /* Enable L2. Assume we don't need to flush -- Cort*/ *(unsigned char *)(0x8000081c) = *(unsigned char *)(0x8000081c)|3; - /* make the serial port the console */ - /* strcat(cmd_line,"console=ttyS0,9600n8"); */ - /* use the normal console but send output to the serial port, too */ - /*strcat(cmd_line,"console=tty0 console=ttyS0,9600n8");*/ - sprintf(cmd_line,"%s console=tty0 console=ttyS0,9600n8", cmd_line); printk("Boot arguments: %s\n", cmd_line); #ifdef CONFIG_SOUND_CS4232 diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index 5ea55cee9..ebe64a429 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -1,4 +1,6 @@ /* + * $Id: process.c,v 1.70 1999/01/07 16:28:59 cort Exp $ + * * linux/arch/ppc/kernel/process.c * * Derived from "arch/i386/kernel/process.c" @@ -180,13 +182,6 @@ switch_to(struct task_struct *prev, struct task_struct *new) if ( prev->tss.regs->msr & MSR_FP ) smp_giveup_fpu(prev); - /* be noisy about processor changes for debugging -- Cort */ - if ( (new->last_processor != NO_PROC_ID) && - (new->last_processor != new->processor) ) - printk("switch_to(): changing cpu's %d -> %d %s/%d\n", - new->last_processor,new->processor, - new->comm,new->pid); - prev->last_processor = prev->processor; current_set[smp_processor_id()] = new; #endif /* __SMP__ */ @@ -236,6 +231,19 @@ void show_regs(struct pt_regs * regs) out: } +void instruction_dump (unsigned long *pc) +{ + int i; + + if((((unsigned long) pc) & 3)) + return; + + printk("Instruction DUMP:"); + for(i = -3; i < 6; i++) + printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>'); + printk("\n"); +} + void exit_thread(void) { if (last_task_used_math == current) @@ -450,6 +458,7 @@ print_backtrace(unsigned long *sp) printk("\n"); } +#if 0 /* * Low level print for debugging - Cort */ @@ -537,3 +546,4 @@ __initfunc(void ll_puts(const char *s)) orig_x = x; orig_y = y; } +#endif diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c index 62b2b03af..e2c6b13a0 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -362,7 +362,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_PEEKDATA: { unsigned long tmp; + down(&child->mm->mmap_sem); ret = read_long(child, addr, &tmp); + up(&child->mm->mmap_sem); if (ret < 0) goto out; ret = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); @@ -410,7 +412,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) /* If I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: + down(&child->mm->mmap_sem); ret = write_long(child,addr,data); + up(&child->mm->mmap_sem); goto out; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 706c1dde2..50cd70889 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.117 1998/11/09 19:55:53 geert Exp $ + * $Id: setup.c,v 1.122 1998/12/31 20:51:19 cort Exp $ * Common prep/pmac/chrp boot and setup code. */ @@ -35,7 +35,7 @@ extern unsigned long m68k_machtype; extern int parse_bootinfo(const struct bi_record *); extern char _end[]; #ifdef CONFIG_APUS -struct mem_info ramdisk; +extern struct mem_info ramdisk; unsigned long isa_io_base; unsigned long isa_mem_base; unsigned long pci_dram_offset; @@ -259,6 +259,9 @@ void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) break; } #endif +#if defined(CONFIG_MBX) + mbx_ide_init_hwif_ports(p,base,irq); +#endif } EXPORT_SYMBOL(ide_init_hwif_ports); #endif @@ -464,9 +467,9 @@ int get_cpuinfo(char *buffer) * Find out what kind of machine we're on and save any data we need * from the early boot process (devtree is copied on pmac by prom_init() ) */ -__initfunc(unsigned long +unsigned long __init identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7)) + unsigned long r6, unsigned long r7) { extern void setup_pci_ptrs(void); @@ -491,6 +494,9 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, char *model; have_of = 1; + + /* prom_init has already been called from __start */ + finish_device_tree(); /* 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) ) @@ -510,8 +516,10 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, if ( have_of ) { +#ifdef CONFIG_MACH_SPECIFIC /* prom_init has already been called from __start */ finish_device_tree(); +#endif /* CONFIG_MACH_SPECIFIC */ /* * If we were booted via quik, r3 points to the physical * address of the command-line parameters. @@ -665,7 +673,7 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, #else /* CONFIG_MBX */ if ( r3 ) - memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); + memcpy( (void *)res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); #ifdef CONFIG_PCI setup_pci_ptrs(); @@ -693,9 +701,22 @@ identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, extern int __map_without_bats; __map_without_bats = 1; } + return 0; } +/* Checks "l2cr=xxxx" command-line option */ +void ppc_setup_l2cr(char *str, int *ints) +{ + if ( (_get_PVR() >> 16) == 8) + { + unsigned long val = simple_strtoul(str, NULL, 0); + printk(KERN_INFO "l2cr set to %lx\n", val); + _set_L2CR(0); + _set_L2CR(val); + } +} + __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)) { diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index 75445925f..ba505e133 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -1,5 +1,5 @@ /* - * $Id: smp.c,v 1.36 1998/10/08 01:17:48 cort Exp $ + * $Id: smp.c,v 1.39 1998/12/28 10:28:51 paulus Exp $ * * Smp support for ppc. * @@ -43,6 +43,7 @@ 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 */ int cpu_number_map[NR_CPUS] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,}; @@ -144,7 +145,14 @@ void smp_message_recv(void) void smp_send_reschedule(int cpu) { - smp_message_pass(cpu, MSG_RESCHEDULE, 0, 0); + /* This is only used if `cpu' is running an idle task, + so it will reschedule itself anyway... */ + /*smp_message_pass(cpu, MSG_RESCHEDULE, 0, 0);*/ +} + +void smp_send_stop(void) +{ + smp_message_pass(MSG_ALL_BUT_SELF, MSG_STOP_CPU, 0, 0); } spinlock_t mesg_pass_lock = SPIN_LOCK_UNLOCKED; @@ -152,7 +160,7 @@ void smp_message_pass(int target, int msg, unsigned long data, int wait) { if ( _machine != _MACH_Pmac ) return; - /*printk("SMP %d: sending smp message\n", current->processor);*/ +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) @@ -179,17 +187,18 @@ if (smp_processor_id() ) printk("pass from cpu 1\n"); spin_unlock(&mesg_pass_lock); } -__initfunc(void smp_boot_cpus(void)) +void __init smp_boot_cpus(void) { extern struct task_struct *current_set[NR_CPUS]; extern void __secondary_start(void); int i; struct task_struct *p; - + unsigned long a; + printk("Entering SMP Mode...\n"); first_cpu_booted = 1; - dcbf(&first_cpu_booted); + /*dcbf(&first_cpu_booted);*/ for (i = 0; i < NR_CPUS; i++) { prof_counter[i] = 1; @@ -200,7 +209,13 @@ __initfunc(void smp_boot_cpus(void)) 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 ) { printk("SMP not supported on this machine.\n"); @@ -213,10 +228,16 @@ __initfunc(void smp_boot_cpus(void)) 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 */ - dcbf((void *)¤t_set[1]); + /* 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; @@ -238,7 +259,7 @@ __initfunc(void smp_boot_cpus(void)) if(cpu_callin_map[1]) { printk("Processor %d found.\n", smp_num_cpus); smp_num_cpus++; -#if 0 /* this sync's the decr's, but we don't want this now -- Cort */ +#if 1 /* this sync's the decr's, but we don't want this now -- Cort */ set_dec(decrementer_count); #endif } else { @@ -251,19 +272,17 @@ __initfunc(void smp_boot_cpus(void)) smp_message_pass(1,0xf0f0, 0, 0); } -__initfunc(void smp_commence(void)) +void __init smp_commence(void) { printk("SMP %d: smp_commence()\n",current->processor); /* * Lets the callin's below out of their loop. */ - local_flush_tlb_all(); smp_commenced = 1; - local_flush_tlb_all(); } /* intel needs this */ -__initfunc(void initialize_secondary(void)) +void __init initialize_secondary(void) { } @@ -275,33 +294,34 @@ asmlinkage int __init start_secondary(void *unused) return cpu_idle(NULL); } -__initfunc(void smp_callin(void)) +void __init smp_callin(void) { printk("SMP %d: smp_callin()\n",current->processor); smp_store_cpu_info(current->processor); set_dec(decrementer_count); - +#if 0 current->mm->mmap->vm_page_prot = PAGE_SHARED; current->mm->mmap->vm_start = PAGE_OFFSET; current->mm->mmap->vm_end = init_task.mm->mmap->vm_end; - - cpu_callin_map[current->processor] = current->processor; +#endif + cpu_callin_map[current->processor] = 1; while(!smp_commenced) barrier(); __sti(); + printk("SMP %d: smp_callin done\n", current->processor); } -__initfunc(void smp_setup(char *str, int *ints)) +void __init smp_setup(char *str, int *ints) { printk("SMP %d: smp_setup()\n",current->processor); } -__initfunc(int setup_profiling_timer(unsigned int multiplier)) +int __init setup_profiling_timer(unsigned int multiplier) { return 0; } -__initfunc(void smp_store_cpu_info(int id)) +void __init smp_store_cpu_info(int id) { struct cpuinfo_PPC *c = &cpu_data[id]; diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c index 8bada0e69..32f60a236 100644 --- a/arch/ppc/kernel/time.c +++ b/arch/ppc/kernel/time.c @@ -1,5 +1,5 @@ /* - * $Id: time.c,v 1.36 1998/10/10 12:16:08 geert Exp $ + * $Id: time.c,v 1.39 1998/12/28 10:28:51 paulus Exp $ * Common time routines among all ppc machines. * * Written by Cort Dougan (cort@cs.nmt.edu) to merge @@ -17,6 +17,9 @@ * This is then divided by 4, providing a 8192 Hz clock into the PIT. * Since it is not possible to get a nice 100 Hz clock out of this, without * creating a software PLL, I have set HZ to 128. -- Dan + * + * 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> @@ -78,6 +81,26 @@ void timer_interrupt(struct pt_regs * regs) unsigned dcache_locked = unlock_dcache(); hardirq_enter(cpu); +#ifdef __SMP__ + { + unsigned int loops = 100000000; + 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__ */ while ((dval = get_dec()) < 0) { /* @@ -133,9 +156,9 @@ void timebase_interrupt(int irq, void * dev, struct pt_regs * regs) 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; + ((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 */ @@ -150,12 +173,15 @@ void do_gettimeofday(struct timeval *tv) save_flags(flags); cli(); *tv = xtime; + /* XXX we don't seem to have the decrementers synced properly yet */ +#ifndef __SMP__ tv->tv_usec += (decrementer_count - get_dec()) * count_period_num / count_period_den; if (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; tv->tv_sec++; } +#endif restore_flags(flags); } @@ -172,6 +198,11 @@ 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_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; restore_flags(flags); } @@ -227,13 +258,13 @@ __initfunc(void time_init(void)) * 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; + ((immap_t *)IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY; + ((immap_t *)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 &= + ((immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc &= ~(RTCSC_SIE | RTCSC_ALE); /* Enabling the decrementer also enables the timebase interrupts @@ -241,7 +272,7 @@ __initfunc(void time_init(void)) * 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 = + ((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) @@ -249,7 +280,7 @@ __initfunc(void time_init(void)) /* Get time from the RTC. */ - xtime.tv_sec = ((immap_t *)MBX_IMAP_ADDR)->im_sit.sit_rtc; + xtime.tv_sec = ((immap_t *)IMAP_ADDR)->im_sit.sit_rtc; xtime.tv_usec = 0; #endif /* CONFIG_MBX */ @@ -343,10 +374,10 @@ __initfunc(void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * */ __initfunc(void mbx_calibrate_decr(void)) { - bd_t *binfo = (bd_t *)&res; + bd_t *binfo = (bd_t *)res; int freq, fp, divisor; - if ((((immap_t *)MBX_IMAP_ADDR)->im_clkrst.car_sccr & 0x02000000) == 0) + 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 diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index af02252f9..fa5184af2 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -79,6 +79,7 @@ _exception(int signr, struct pt_regs *regs) debugger(regs); #endif print_backtrace((unsigned long *)regs->gpr[1]); + instruction_dump((unsigned long *)regs->nip); panic("Exception in kernel pc %lx signal %d",regs->nip,signr); } force_sig(signr, current); @@ -126,6 +127,7 @@ MachineCheckException(struct pt_regs *regs) debugger(regs); #endif print_backtrace((unsigned long *)regs->gpr[1]); + instruction_dump((unsigned long *)regs->nip); panic("machine check"); } _exception(SIGSEGV, regs); @@ -219,6 +221,7 @@ StackOverflow(struct pt_regs *regs) #endif show_regs(regs); print_backtrace((unsigned long *)regs->gpr[1]); + instruction_dump((unsigned long *)regs->nip); panic("kernel stack overflow"); } diff --git a/arch/ppc/lib/Makefile b/arch/ppc/lib/Makefile index 4cc49de17..8ca9a3cd5 100644 --- a/arch/ppc/lib/Makefile +++ b/arch/ppc/lib/Makefile @@ -8,7 +8,7 @@ O_TARGET = lib.o O_OBJS = checksum.o string.o strcase.o -ifdef SMP +ifdef CONFIG_SMP O_OBJS += locks.o endif diff --git a/arch/ppc/lib/locks.c b/arch/ppc/lib/locks.c index 073e7076c..2d2a2d8c0 100644 --- a/arch/ppc/lib/locks.c +++ b/arch/ppc/lib/locks.c @@ -1,5 +1,5 @@ /* - * $Id: locks.c,v 1.20 1998/10/08 01:17:32 cort Exp $ + * $Id: locks.c,v 1.21 1998/12/28 10:28:53 paulus Exp $ * * Locks for smp ppc * @@ -18,7 +18,7 @@ #define DEBUG_LOCKS 1 #undef INIT_STUCK -#define INIT_STUCK 0xffffffff +#define INIT_STUCK 200000000 /*0xffffffff*/ void _spin_lock(spinlock_t *lock) { @@ -76,9 +76,9 @@ void _spin_unlock(spinlock_t *lp) lp->owner_pc,lp->lock); #endif /* DEBUG_LOCKS */ lp->owner_pc = lp->owner_cpu = 0; - eieio(); - lp->lock = 0; - eieio(); + eieio(); /* actually I believe eieio only orders */ + lp->lock = 0; /* non-cacheable accesses (on 604 at least) */ + eieio(); /* - paulus. */ } /* diff --git a/arch/ppc/mbx_defconfig b/arch/ppc/mbx_defconfig index e0b94ce6a..54035b866 100644 --- a/arch/ppc/mbx_defconfig +++ b/arch/ppc/mbx_defconfig @@ -14,15 +14,17 @@ CONFIG_8xx=y # CONFIG_ALL_PPC is not set # CONFIG_APUS is not set CONFIG_MBX=y -CONFIG_SERIAL_CONSOLE=y +CONFIG_SMP=n CONFIG_MACH_SPECIFIC=y +CONFIG_SERIAL_CONSOLE=y # # General setup # -# CONFIG_EXPERIMENTAL is not set +CONFIG_EXPERIMENTAL=y # CONFIG_MODULES is not set CONFIG_PCI=y +# CONFIG_PCI_QUIRKS is not set CONFIG_PCI_OLD_PROC=y CONFIG_NET=y # CONFIG_SYSCTL is not set @@ -33,13 +35,19 @@ CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set # CONFIG_BINFMT_JAVA is not set # CONFIG_PARPORT is not set +# CONFIG_VGA_CONSOLE is not set +# CONFIG_FB is not set +# CONFIG_PMAC_PBOOK is not set # CONFIG_MAC_KEYBOARD is not set # CONFIG_MAC_FLOPPY is not set # CONFIG_MAC_SERIAL is not set +# CONFIG_ADBMOUSE is not set +# CONFIG_BLK_DEV_IDE_PMAC is not set # CONFIG_PROC_DEVICETREE is not set # CONFIG_KGDB is not set # CONFIG_XMON is not set -CONFIG_VGA_CONSOLE=y +# CONFIG_TOTALMP is not set +# CONFIG_BOOTX_TEXT is not set # # Plug and Play support @@ -47,7 +55,7 @@ CONFIG_VGA_CONSOLE=y # CONFIG_PNP is not set # -# Floppy, IDE, and other block devices +# Block devices # # CONFIG_BLK_DEV_FD is not set # CONFIG_BLK_DEV_IDE is not set @@ -84,8 +92,19 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_INET_RARP is not set CONFIG_IP_NOSR=y # CONFIG_SKB_LARGE is not set +# CONFIG_IPV6 is not set # CONFIG_IPX is not set # CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set +# CONFIG_CPU_IS_SLOW is not set +# CONFIG_NET_SCHED is not set # # SCSI support @@ -100,22 +119,26 @@ CONFIG_NETDEVICES=y # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set # CONFIG_NET_VENDOR_3COM is not set # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN is not set # CONFIG_NET_ISA is not set # CONFIG_NET_EISA is not set # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set +# CONFIG_HIPPI is not set # CONFIG_DLCI is not set # CONFIG_PPP is not set # CONFIG_SLIP is not set # CONFIG_NET_RADIO is not set # CONFIG_TR is not set -# CONFIG_WAN_DRIVERS is not set -# CONFIG_LAPBETHER is not set -# CONFIG_X25_ASY is not set +# CONFIG_SHAPER is not set +# CONFIG_HOSTESS_SV11 is not set # # Amateur Radio support @@ -133,6 +156,32 @@ CONFIG_NET_ETHERNET=y # CONFIG_CD_NO_IDESCSI is not set # +# Console drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set +# CONFIG_MOUSE is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_VIDEO_DEV is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# # Filesystems # # CONFIG_QUOTA is not set @@ -159,31 +208,15 @@ CONFIG_LOCKD=y # CONFIG_ROMFS_FS is not set # CONFIG_AUTOFS_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_SMD_DISKLABEL is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_ADFS_FS is not set +# CONFIG_QNX4FS_FS is not set # CONFIG_MAC_PARTITION is not set # CONFIG_NLS is not set # -# Character devices -# -# CONFIG_VT is not set -# CONFIG_SERIAL is not set -# CONFIG_SERIAL_EXTENDED is not set -# CONFIG_SERIAL_NONSTANDARD is not set -# CONFIG_MOUSE is not set -# CONFIG_QIC02_TAPE is not set -# CONFIG_WATCHDOG is not set -# CONFIG_RTC is not set -# CONFIG_VIDEO_DEV is not set -# CONFIG_NVRAM is not set -# CONFIG_JOYSTICK is not set -# CONFIG_MISC_RADIO is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_FTAPE is not set - -# # Sound # # CONFIG_SOUND is not set diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c index b45f4e6f4..42e2918b1 100644 --- a/arch/ppc/mm/fault.c +++ b/arch/ppc/mm/fault.c @@ -89,6 +89,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, printk("page fault in interrupt handler, addr=%lx\n", address); show_regs(regs); + instruction_dump((unsigned long *)regs->nip); #if defined(CONFIG_XMON) || defined(CONFIG_KGDB) if (debugger_kernel_faults) debugger(regs); @@ -174,6 +175,7 @@ bad_page_fault(struct pt_regs *regs, unsigned long address) /* kernel has accessed a bad area */ show_regs(regs); print_backtrace( (unsigned long *)regs->gpr[1] ); + instruction_dump((unsigned long *)regs->nip); #if defined(CONFIG_XMON) || defined(CONFIG_KGDB) if (debugger_kernel_faults) debugger(regs); diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index 44104fd4e..b80d4c21c 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -1,5 +1,5 @@ /* - * $Id: init.c,v 1.130 1998/11/10 10:09:20 paulus Exp $ + * $Id: init.c,v 1.139 1998/12/29 19:53:49 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -55,7 +55,7 @@ /* END APUS includes */ int prom_trashed; -int next_mmu_context; +atomic_t next_mmu_context; unsigned long *end_of_DRAM; int mem_init_done; extern pgd_t swapper_pg_dir[]; @@ -71,7 +71,8 @@ unsigned long ioremap_base; unsigned long ioremap_bot; unsigned long avail_start; struct pgtable_cache_struct quicklists; -struct mem_info memory[NUM_MEMINFO]; +extern int num_memory; +extern struct mem_info memory[NUM_MEMINFO]; extern boot_infos_t *boot_infos; void MMU_init(void); @@ -89,6 +90,27 @@ void map_page(struct task_struct *, unsigned long va, extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); + +/* + * The following stuff defines a data structure for representing + * areas of memory as an array of (address, length) pairs, and + * procedures for manipulating them. + */ +#define MAX_MEM_REGIONS 32 + +struct mem_pieces { + int n_regions; + struct reg_property regions[MAX_MEM_REGIONS]; +}; +struct mem_pieces phys_mem; +struct mem_pieces phys_avail; +struct mem_pieces prom_mem; + +static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int); +void *find_mem_piece(unsigned, unsigned); +static void print_mem_pieces(struct mem_pieces *); +static void append_mem_piece(struct mem_pieces *, unsigned, unsigned); + extern struct task_struct *current_set[NR_CPUS]; PTE *Hash, *Hash_end; @@ -529,38 +551,19 @@ mmu_context_overflow(void) } read_unlock(&tasklist_lock); flush_hash_segments(0x10, 0xffffff); - next_mmu_context = 0; + atomic_set(&next_mmu_context, 0); /* make sure current always has a context */ - current->mm->context = MUNGE_CONTEXT(++next_mmu_context); + current->mm->context = MUNGE_CONTEXT(atomic_inc_return(&next_mmu_context)); set_context(current->mm->context); #else /* We set the value to -1 because it is pre-incremented before * before use. */ - next_mmu_context = -1; + atomic_set(&next_mmu_context, -1); #endif } /* - * The following stuff defines a data structure for representing - * areas of memory as an array of (address, length) pairs, and - * procedures for manipulating them. - */ -#define MAX_MEM_REGIONS 32 - -struct mem_pieces { - int n_regions; - struct reg_property regions[MAX_MEM_REGIONS]; -}; -struct mem_pieces phys_mem; -struct mem_pieces phys_avail; -struct mem_pieces prom_mem; - -static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int); -void *find_mem_piece(unsigned, unsigned); -static void print_mem_pieces(struct mem_pieces *); - -/* * Scan a region for a piece of a given size with the required alignment. */ __initfunc(void * @@ -653,14 +656,26 @@ __initfunc(static void print_mem_pieces(struct mem_pieces *mp)) printk("\n"); } +/* + * Add some memory to an array of pieces + */ +__initfunc(static void + append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size)) +{ + struct reg_property *rp; + if (mp->n_regions >= MAX_MEM_REGIONS) + return; + rp = &mp->regions[mp->n_regions++]; + rp->address = start; + rp->size = size; +} #ifndef CONFIG_8xx static void hash_init(void); static void get_mem_prop(char *, struct mem_pieces *); static void sort_mem_pieces(struct mem_pieces *); static void coalesce_mem_pieces(struct mem_pieces *); -static void append_mem_piece(struct mem_pieces *, unsigned, unsigned); __initfunc(static void sort_mem_pieces(struct mem_pieces *mp)) { @@ -703,21 +718,6 @@ __initfunc(static void coalesce_mem_pieces(struct mem_pieces *mp)) } /* - * Add some memory to an array of pieces - */ -__initfunc(static void - append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size)) -{ - struct reg_property *rp; - - if (mp->n_regions >= MAX_MEM_REGIONS) - return; - rp = &mp->regions[mp->n_regions++]; - rp->address = start; - rp->size = size; -} - -/* * Read in a property describing some pieces of memory. */ @@ -963,8 +963,10 @@ __initfunc(void MMU_init(void)) #ifndef CONFIG_8xx if (have_of) end_of_DRAM = pmac_find_end_of_memory(); +#ifdef CONFIG_APUS else if (_machine == _MACH_apus ) end_of_DRAM = apus_find_end_of_memory(); +#endif else /* prep */ end_of_DRAM = prep_find_end_of_memory(); @@ -1023,8 +1025,10 @@ __initfunc(void MMU_init(void)) */ ioremap(NVRAM_ADDR, NVRAM_SIZE); ioremap(MBX_CSR_ADDR, MBX_CSR_SIZE); - ioremap(MBX_IMAP_ADDR, MBX_IMAP_SIZE); + ioremap(IMAP_ADDR, IMAP_SIZE); ioremap(PCI_CSR_ADDR, PCI_CSR_SIZE); + /* ide needs to be able to get at PCI space -- Cort */ + ioremap(0x80000000, 0x4000); #endif /* CONFIG_8xx */ } @@ -1189,7 +1193,7 @@ __initfunc(unsigned long *mbx_find_end_of_memory(void)) volatile memctl8xx_t *mcp; unsigned long *ret; - binfo = (bd_t *)&res; + binfo = (bd_t *)res; /* * The MBX does weird things with the mmaps for ram. @@ -1200,11 +1204,13 @@ __initfunc(unsigned long *mbx_find_end_of_memory(void)) * In fact, it might be the best idea to just read the DRAM * config registers and set the mem areas accordingly. */ - mcp = (memctl8xx_t *)(&(((immap_t *)MBX_IMAP_ADDR)->im_memctl)); + mcp = (memctl8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_memctl)); + append_mem_piece(&phys_mem, 0, binfo->bi_memsize); +#if 0 phys_mem.regions[0].address = 0; - phys_mem.regions[0].size = binfo->bi_memsize; - + phys_mem.regions[0].size = binfo->bi_memsize; phys_mem.n_regions = 1; +#endif ret = __va(phys_mem.regions[0].address+ phys_mem.regions[0].size); @@ -1293,7 +1299,7 @@ __initfunc(unsigned long *pmac_find_end_of_memory(void)) /* * This finds the amount of physical ram and does necessary * setup for prep. This is pretty architecture specific so - * this will likely stay seperate from the pmac. + * this will likely stay separate from the pmac. * -- Cort */ __initfunc(unsigned long *prep_find_end_of_memory(void)) @@ -1322,6 +1328,7 @@ __initfunc(unsigned long *prep_find_end_of_memory(void)) return (__va(total)); } +#ifdef CONFIG_APUS #define HARDWARE_MAPPED_SIZE (512*1024) __initfunc(unsigned long *apus_find_end_of_memory(void)) { @@ -1381,15 +1388,22 @@ __initfunc(unsigned long *apus_find_end_of_memory(void)) /* Remove the upper 512KB where the PPC exception vectors are mapped. */ top -= HARDWARE_MAPPED_SIZE; - remove_mem_piece(&phys_avail, top, - HARDWARE_MAPPED_SIZE, 0); +#if 0 + /* This would be neat, but it breaks on A3000 machines!? */ + remove_mem_piece(&phys_avail, top, 16384, 0); +#else + remove_mem_piece(&phys_avail, top, HARDWARE_MAPPED_SIZE, 0); +#endif + } - /* FIXME:APUS: Only handles one block of memory! Problem is - that the VTOP/PTOV code in head.S would be a mess if it had - to handle more than one block. */ + /* Linux/APUS only handles one block of memory -- the one on + the PowerUP board. Other system memory is horrible slow in + comparison. The user can use other memory for swapping + using the z2ram device. */ return __va(memory[0].addr + memory[0].size); } +#endif /* CONFIG_APUS */ /* * Initialize the hash table and patch the instructions in head.S. @@ -1400,7 +1414,7 @@ __initfunc(static void hash_init(void)) unsigned long h, ramsize; extern unsigned int hash_page_patch_A[], hash_page_patch_B[], - hash_page_patch_C[]; + hash_page_patch_C[], hash_page[]; /* * Allow 64k of hash table for every 16MB of memory, @@ -1475,7 +1489,17 @@ __initfunc(static void hash_init(void)) flush_icache_range((unsigned long) b(hash_page_patch_A), (unsigned long) b(hash_page_patch_C + 1)); } - else + else { Hash_end = 0; + /* + * Put a blr (procedure return) instruction at the + * start of hash_page, since we can still get DSI + * exceptions on a 603. + */ + *b(hash_page) = 0x4e800020; + flush_icache_range((unsigned long) b(hash_page), + (unsigned long) b(hash_page + 1)); + } } #endif /* ndef CONFIG_8xx */ + diff --git a/arch/ppc/pmac_defconfig b/arch/ppc/pmac_defconfig index ae1303bb4..811d599c0 100644 --- a/arch/ppc/pmac_defconfig +++ b/arch/ppc/pmac_defconfig @@ -15,6 +15,7 @@ CONFIG_PMAC=y # CONFIG_APUS is not set # CONFIG_MBX is not set CONFIG_MACH_SPECIFIC=y +# CONFIG_SMP is not set # # General setup @@ -137,6 +138,10 @@ CONFIG_ATALK=m # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set # @@ -164,6 +169,7 @@ CONFIG_SCSI_CONSTANTS=y # SCSI low-level drivers # # CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AHA152X is not set # CONFIG_SCSI_AHA1542 is not set # CONFIG_SCSI_AHA1740 is not set @@ -174,14 +180,16 @@ CONFIG_AIC7XXX_RESET_DELAY=15 # CONFIG_SCSI_ADVANSYS is not set # CONFIG_SCSI_IN2000 is not set # CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_MEGARAID is not set # CONFIG_SCSI_BUSLOGIC is not set # CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_DMA is not set # CONFIG_SCSI_EATA_PIO is not set -# CONFIG_SCSI_EATA is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set @@ -218,6 +226,7 @@ CONFIG_BMAC=y # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set # CONFIG_YELLOWFIN is not set +# CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y # CONFIG_PCNET32 is not set @@ -225,7 +234,7 @@ CONFIG_NET_EISA=y # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set CONFIG_DE4X5=y -CONFIG_DEC_ELCP=m +# CONFIG_DEC_ELCP is not set # CONFIG_DGRS is not set # CONFIG_EEXPRESS_PRO100 is not set # CONFIG_LNE390 is not set @@ -253,6 +262,7 @@ CONFIG_PPP=y # CONFIG_TR is not set # CONFIG_SHAPER is not set # CONFIG_HOSTESS_SV11 is not set +# CONFIG_COSA is not set # # Amateur Radio support @@ -282,6 +292,7 @@ CONFIG_FB_IMSTT=y CONFIG_FB_CT65550=y # CONFIG_FB_S3TRIO is not set # CONFIG_FB_MATROX is not set +CONFIG_FB_ATY=y # CONFIG_FB_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set CONFIG_FBCON_CFB8=y @@ -303,7 +314,7 @@ CONFIG_FONT_SUN12x22=y # CONFIG_VT=y CONFIG_VT_CONSOLE=y -CONFIG_SERIAL=m +# CONFIG_SERIAL is not set # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set CONFIG_UNIX98_PTYS=y @@ -311,9 +322,17 @@ CONFIG_UNIX98_PTY_COUNT=256 # CONFIG_MOUSE is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set +CONFIG_NVRAM=y # CONFIG_RTC is not set + +# +# Video For Linux +# # CONFIG_VIDEO_DEV is not set -CONFIG_NVRAM=y + +# +# Joystick support +# # CONFIG_JOYSTICK is not set # @@ -325,37 +344,47 @@ CONFIG_NVRAM=y # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_MINIX_FS is not set -CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=y -# CONFIG_JOLIET is not set +CONFIG_AUTOFS_FS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +CONFIG_HFS_FS=y CONFIG_FAT_FS=m CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set CONFIG_VFAT_FS=m +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set CONFIG_NFS_FS=y CONFIG_NFSD=y # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_NTFS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_AFFS_FS is not set -CONFIG_HFS_FS=y -# CONFIG_ROMFS_FS is not set -CONFIG_AUTOFS_FS=y -# CONFIG_UFS_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# # CONFIG_BSD_DISKLABEL is not set +CONFIG_MAC_PARTITION=y # CONFIG_SMD_DISKLABEL is not set # CONFIG_SOLARIS_X86_PARTITION is not set -CONFIG_DEVPTS_FS=y -# CONFIG_ADFS_FS is not set -# CONFIG_QNX4FS_FS is not set -CONFIG_MAC_PARTITION=y +# CONFIG_UNIXWARE_DISKLABEL is not set CONFIG_NLS=y # @@ -386,6 +415,7 @@ CONFIG_NLS_CODEPAGE_437=y # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # diff --git a/arch/ppc/prep_defconfig b/arch/ppc/prep_defconfig index 41db35a4a..14b4ea4a0 100644 --- a/arch/ppc/prep_defconfig +++ b/arch/ppc/prep_defconfig @@ -14,6 +14,7 @@ CONFIG_PREP=y # CONFIG_ALL_PPC is not set # CONFIG_APUS is not set # CONFIG_MBX is not set +# CONFIG_SMP is not set CONFIG_MACH_SPECIFIC=y # diff --git a/arch/ppc/vmlinux.lds b/arch/ppc/vmlinux.lds index f5c61bd6e..e322d138b 100644 --- a/arch/ppc/vmlinux.lds +++ b/arch/ppc/vmlinux.lds @@ -64,6 +64,9 @@ SECTIONS __ex_table : { *(__ex_table) } __stop___ex_table = .; + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + . = ALIGN(4096); __init_begin = .; .text.init : { *(.text.init) } diff --git a/arch/sparc/ap1000/util.c b/arch/sparc/ap1000/util.c index abb51426e..079c9f4c0 100644 --- a/arch/sparc/ap1000/util.c +++ b/arch/sparc/ap1000/util.c @@ -393,7 +393,7 @@ void ap_nfs_hook(unsigned long server) unsigned end = jiffies + 20*HZ; /* we are booting from another cell */ printk("waiting for the master cell\n"); - while (jiffies < end) ; + while (time_before(jiffies, end)) ; printk("continuing\n"); } } diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 97c970481..6220b2085 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -25,6 +25,7 @@ define_bool CONFIG_VT y define_bool CONFIG_VT_CONSOLE y bool 'Support for AP1000 multicomputer' CONFIG_AP1000 +bool 'Symmetric multi-processing support' CONFIG_SMP if [ "$CONFIG_AP1000" = "y" ]; then define_bool CONFIG_NO_KEYBOARD y @@ -175,8 +176,6 @@ fi source fs/Config.in -source fs/nls/Config.in - mainmenu_option next_comment comment 'Watchdog' diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index 7ea12a128..8e6d9e2f5 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -20,6 +20,7 @@ CONFIG_KMOD=y CONFIG_VT=y CONFIG_VT_CONSOLE=y # CONFIG_AP1000 is not set +# CONFIG_SMP is not set # CONFIG_SUN4 is not set # CONFIG_PCI is not set diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 18e487d86..d5f65dda3 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -30,7 +30,7 @@ ifdef CONFIG_SUN4 O_OBJS += sun4setup.o endif -ifdef SMP +ifdef CONFIG_SMP O_OBJS += trampoline.o smp.o sun4m_smp.o sun4d_smp.o endif @@ -50,11 +50,17 @@ check_asm: dummy @echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h @echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h @echo "" >> asm_offsets.h - @echo "#ifndef __SMP__" >> asm_offsets.h + @echo "#include <linux/config.h>" >> asm_offsets.h @echo "" >> asm_offsets.h - @echo "#include <linux/sched.h>" > tmp.c + @echo "#ifndef CONFIG_SMP" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#include <linux/config.h>" > tmp.c + @echo "#undef CONFIG_SMP" >> tmp.c + @echo "#include <linux/sched.h>" >> tmp.c $(CC) -E tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/config.h>" >> check_asm.c + @echo "#undef CONFIG_SMP" >> check_asm.c @echo "#include <linux/sched.h>" >> check_asm.c @echo 'struct task_struct _task;' >> check_asm.c @echo 'struct mm_struct _mm;' >> check_asm.c @@ -69,11 +75,17 @@ check_asm: dummy ./check_asm >> asm_offsets.h @rm -f check_asm check_asm.c @echo "" >> asm_offsets.h - @echo "#else /* __SMP__ */" >> asm_offsets.h + @echo "#else /* CONFIG_SMP */" >> asm_offsets.h @echo "" >> asm_offsets.h - @echo "#include <linux/sched.h>" > tmp.c + @echo "#include <linux/config.h>" > tmp.c + @echo "#undef CONFIG_SMP" >> tmp.c + @echo "#define CONFIG_SMP 1" >> tmp.c + @echo "#include <linux/sched.h>" >> tmp.c $(CC) -D__SMP__ -E tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/config.h>" >> check_asm.c + @echo "#undef CONFIG_SMP" >> check_asm.c + @echo "#define CONFIG_SMP 1" >> check_asm.c @echo "#include <linux/sched.h>" >> check_asm.c @echo 'struct task_struct _task;' >> check_asm.c @echo 'struct mm_struct _mm;' >> check_asm.c @@ -88,7 +100,7 @@ check_asm: dummy ./check_asm >> asm_offsets.h @rm -f check_asm check_asm.c @echo "" >> asm_offsets.h - @echo "#endif /* __SMP__ */" >> asm_offsets.h + @echo "#endif /* CONFIG_SMP */" >> asm_offsets.h @echo "" >> asm_offsets.h @echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h @if test -r $(HPATH)/asm/asm_offsets.h; then \ diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index 40e23fa0e..ed8fe6a25 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c @@ -592,7 +592,9 @@ asmlinkage void do_ptrace(struct pt_regs *regs) pt_error_return(regs, EINVAL); goto out; } + down(&child->mm->mmap_sem); res = read_long(child, addr, &tmp); + up(&child->mm->mmap_sem); if (res < 0) { pt_error_return(regs, -res); goto out; @@ -619,8 +621,10 @@ asmlinkage void do_ptrace(struct pt_regs *regs) pt_error_return(regs, EINVAL); goto out; } + down(&child->mm->mmap_sem); vma = find_extend_vma(child, addr); res = write_long(child, addr, data); + up(&child->mm->mmap_sem); if(res < 0) pt_error_return(regs, -res); else @@ -761,7 +765,9 @@ asmlinkage void do_ptrace(struct pt_regs *regs) goto out; } while(len) { + down(&child->mm->mmap_sem); res = read_byte(child, src, &tmp); + up(&child->mm->mmap_sem); if(res < 0) { pt_error_return(regs, -res); goto out; @@ -788,7 +794,9 @@ asmlinkage void do_ptrace(struct pt_regs *regs) unsigned long tmp; __get_user(tmp, src); + down(&child->mm->mmap_sem); res = write_byte(child, dest, tmp); + up(&child->mm->mmap_sem); if(res < 0) { pt_error_return(regs, -res); goto out; diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c index eac95ec98..9e5afcfcf 100644 --- a/arch/sparc/kernel/time.c +++ b/arch/sparc/kernel/time.c @@ -11,6 +11,9 @@ * Support for MicroSPARC-IIep, PCI CPU. * * This file handles the Sparc specific time handling details. + * + * 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> #include <linux/errno.h> @@ -89,9 +92,10 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) do_timer(regs); /* Determine when to update the Mostek clock. */ - if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) { + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && + xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else @@ -495,12 +499,18 @@ static void sbus_do_settimeofday(struct timeval *tv) } #endif xtime = *tv; - time_state = TIME_BAD; - time_maxerror = 0x70000000; - time_esterror = 0x70000000; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; sti(); } +/* + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you won't notice until after reboot! + */ static int set_rtc_mmss(unsigned long nowtime) { int real_seconds, real_minutes, mostek_minutes; @@ -531,9 +541,13 @@ static int set_rtc_mmss(unsigned long nowtime) iregs->clk.int_sec=real_seconds; iregs->clk.int_min=real_minutes; intersil_start(iregs); - } else + } else { + printk(KERN_WARNING + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); return -1; - + } + return 0; } #endif diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile index 9ce3a6630..0670ff273 100644 --- a/arch/sparc/lib/Makefile +++ b/arch/sparc/lib/Makefile @@ -7,7 +7,7 @@ OBJS = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \ strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \ copy_user.o locks.o atomic.o bitops.o debuglocks.o -ifdef SMP +ifdef CONFIG_SMP OBJS += irqlock.o endif @@ -54,7 +54,7 @@ atomic.o: atomic.S bitops.o: bitops.S $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o bitops.o bitops.S -ifdef SMP +ifdef CONFIG_SMP irqlock.o: irqlock.S $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o irqlock.o irqlock.S endif diff --git a/arch/sparc/lib/checksum.S b/arch/sparc/lib/checksum.S index 2f0c78168..d02b6dfb2 100644 --- a/arch/sparc/lib/checksum.S +++ b/arch/sparc/lib/checksum.S @@ -535,7 +535,7 @@ C_LABEL(__csum_partial_copy_end): /* %o1 is dst * %o3 is # bytes to zero out * %o4 is faulting address - * %o5 is %pc where fault occured */ + * %o5 is %pc where fault occurred */ clr %o2 31: /* %o0 is src @@ -543,7 +543,7 @@ C_LABEL(__csum_partial_copy_end): * %o2 is # of bytes to copy from src to dst * %o3 is # bytes to zero out * %o4 is faulting address - * %o5 is %pc where fault occured */ + * %o5 is %pc where fault occurred */ save %sp, -104, %sp mov %i5, %o0 mov %i7, %o1 diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile index a9e51c67f..ecb1943c3 100644 --- a/arch/sparc/mm/Makefile +++ b/arch/sparc/mm/Makefile @@ -14,7 +14,7 @@ O_OBJS += nosrmmu.o else O_OBJS += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o endif -ifdef SMP +ifdef CONFIG_SMP O_OBJS += nosun4c.o else O_OBJS += sun4c.o diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 69d40fa09..d94fd4083 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -470,7 +470,7 @@ static inline pte_t *srmmu_get_pte_fast(void) (unsigned int)ret->pprev_hash = mask & ~tmp; if (!(mask & ~tmp)) pte_quicklist = (unsigned long *)ret->next_hash; - ret = (struct page *)(PAGE_OFFSET + (ret->map_nr << PAGE_SHIFT) + off); + ret = (struct page *)(page_address(ret) + off); pgtable_cache_size--; } spin_unlock(&pte_spinlock); @@ -508,7 +508,7 @@ static inline pgd_t *srmmu_get_pgd_fast(void) (unsigned int)ret->pprev_hash = mask & ~tmp; if (!(mask & ~tmp)) pgd_quicklist = (unsigned long *)ret->next_hash; - ret = (struct page *)(PAGE_OFFSET + (ret->map_nr << PAGE_SHIFT) + off); + ret = (struct page *)(page_address(ret) + off); pgd_cache_size--; } spin_unlock(&pgd_spinlock); @@ -682,7 +682,7 @@ static void srmmu_set_pgdir(unsigned long address, pgd_t entry) spin_lock(&pgd_spinlock); address >>= SRMMU_PGDIR_SHIFT; for (page = (struct page *)pgd_quicklist; page; page = page->next_hash) { - pgd_t *pgd = (pgd_t *)(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT)); + pgd_t *pgd = (pgd_t *)page_address(page); unsigned int mask = (unsigned int)page->pprev_hash; if (mask & 1) @@ -2817,7 +2817,7 @@ static int srmmu_check_pgt_cache(int low, int high) page->next_hash = NULL; page->pprev_hash = NULL; pgtable_cache_size -= 16; - free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT)); + __free_page(page); freed++; if (page2) page = page2->next_hash; @@ -2843,7 +2843,7 @@ static int srmmu_check_pgt_cache(int low, int high) page->next_hash = NULL; page->pprev_hash = NULL; pgd_cache_size -= 4; - free_page(PAGE_OFFSET + (page->map_nr << PAGE_SHIFT)); + __free_page(page); freed++; if (page2) page = page2->next_hash; diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile index 01d44a3fb..9df70fc36 100644 --- a/arch/sparc64/Makefile +++ b/arch/sparc64/Makefile @@ -51,7 +51,7 @@ endif # Uncomment this to get spinlock/rwlock debugging on SMP. # DEBUG_SPINLOCK = 1 -ifdef SMP +ifdef CONFIG_SMP ifdef DEBUG_SPINLOCK CFLAGS += -DSPIN_LOCK_DEBUG AFLAGS += -DSPIN_LOCK_DEBUG diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index 103767d64..fa3539525 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -25,6 +25,7 @@ define_bool CONFIG_VT y define_bool CONFIG_VT_CONSOLE y bool 'Support for AP1000 multicomputer' CONFIG_AP1000 +bool 'Symmetric multi-processing support' CONFIG_SMP mainmenu_option next_comment comment 'Console drivers' @@ -238,8 +239,6 @@ fi source fs/Config.in -source fs/nls/Config.in - mainmenu_option next_comment comment 'Watchdog' diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 3b743920f..59b3b4618 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -20,6 +20,7 @@ CONFIG_KMOD=y CONFIG_VT=y CONFIG_VT_CONSOLE=y # CONFIG_AP1000 is not set +# CONFIG_SMP is not set # # Console drivers diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 4d1b1eb35..fbeb83126 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -27,7 +27,7 @@ ifdef CONFIG_PCI O_OBJS += ebus.o endif -ifdef SMP +ifdef CONFIG_SMP O_OBJS += smp.o trampoline.o endif @@ -63,11 +63,17 @@ check_asm: dummy @echo "#ifndef __ASM_OFFSETS_H__" >> asm_offsets.h @echo "#define __ASM_OFFSETS_H__" >> asm_offsets.h @echo "" >> asm_offsets.h - @echo "#ifndef __SMP__" >> asm_offsets.h + @echo "#include <linux/config.h>" >> asm_offsets.h @echo "" >> asm_offsets.h - @echo "#include <linux/sched.h>" > tmp.c + @echo "#ifndef CONFIG_SMP" >> asm_offsets.h + @echo "" >> asm_offsets.h + @echo "#include <linux/config.h>" > tmp.c + @echo "#undef CONFIG_SMP" >> tmp.c + @echo "#include <linux/sched.h>" >> tmp.c $(CC) -E tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/config.h>" >> check_asm.c + @echo "#undef CONFIG_SMP" >> check_asm.c @echo "#include <linux/sched.h>" >> check_asm.c @echo 'struct task_struct _task;' >> check_asm.c @echo 'struct mm_struct _mm;' >> check_asm.c @@ -87,13 +93,19 @@ check_asm: dummy ./check_asm >> asm_offsets.h @rm -f check_asm check_asm.c @echo "" >> asm_offsets.h - @echo "#else /* __SMP__ */" >> asm_offsets.h + @echo "#else /* CONFIG_SMP */" >> asm_offsets.h @echo "" >> asm_offsets.h @echo "#ifndef SPIN_LOCK_DEBUG" >>asm_offsets.h @echo "" >> asm_offsets.h - @echo "#include <linux/sched.h>" > tmp.c + @echo "#include <linux/config.h>" > tmp.c + @echo "#undef CONFIG_SMP" >> tmp.c + @echo "#define CONFIG_SMP 1" >> tmp.c + @echo "#include <linux/sched.h>" >> tmp.c $(CC) -D__SMP__ -E tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/config.h>" >> check_asm.c + @echo "#undef CONFIG_SMP" >> check_asm.c + @echo "#define CONFIG_SMP 1" >> check_asm.c @echo "#include <linux/sched.h>" >> check_asm.c @echo 'struct task_struct _task;' >> check_asm.c @echo 'struct mm_struct _mm;' >> check_asm.c @@ -118,6 +130,9 @@ check_asm: dummy @echo "#include <linux/sched.h>" > tmp.c $(CC) -D__SMP__ -DSPIN_LOCK_DEBUG -E tmp.c -o tmp.i @echo "/* Automatically generated. Do not edit. */" > check_asm.c + @echo "#include <linux/config.h>" >> check_asm.c + @echo "#undef CONFIG_SMP" >> check_asm.c + @echo "#define CONFIG_SMP 1" >> check_asm.c @echo "#include <linux/sched.h>" >> check_asm.c @echo 'struct task_struct _task;' >> check_asm.c @echo 'struct mm_struct _mm;' >> check_asm.c @@ -138,7 +153,7 @@ check_asm: dummy @rm -f check_asm check_asm.c @echo "#endif /* SPIN_LOCK_DEBUG */" >> asm_offsets.h @echo "" >> asm_offsets.h - @echo "#endif /* __SMP__ */" >> asm_offsets.h + @echo "#endif /* CONFIG_SMP */" >> asm_offsets.h @echo "" >> asm_offsets.h @echo "#endif /* __ASM_OFFSETS_H__ */" >> asm_offsets.h @if test -r $(HPATH)/asm/asm_offsets.h; then \ diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index e95bf0727..c826ce56d 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -53,9 +53,10 @@ static __inline__ void timer_check_rtc(void) static long last_rtc_update=0; /* Determine when to update the Mostek clock. */ - if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec > 500000 - (tick >> 1) && - xtime.tv_usec < 500000 + (tick >> 1)) { + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && + xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else @@ -458,10 +459,11 @@ void do_settimeofday(struct timeval *tv) } xtime = *tv; - time_state = TIME_BAD; - time_maxerror = 0x70000000; - time_esterror = 0x70000000; - + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; sti(); } |