From d33f179418bb169f58543d09ecd138ca926ba7ee Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:26:54 -0500 Subject: [PATCH] Import 2.3.14pre2 --- Documentation/Configure.help | 27 + Documentation/isapnp.txt | 147 ++ MAINTAINERS | 7 +- arch/alpha/kernel/machvec.h | 3 +- arch/alpha/lib/io.c | 91 +- arch/i386/defconfig | 6 + arch/i386/kernel/setup.c | 116 +- arch/m68k/Makefile | 5 + arch/m68k/amiga/amiints.c | 126 +- arch/m68k/amiga/cia.c | 35 +- arch/m68k/amiga/config.c | 13 +- arch/m68k/config.in | 7 + arch/m68k/kernel/entry.S | 48 +- arch/m68k/kernel/head.S | 8 + arch/m68k/kernel/ints.c | 12 +- arch/m68k/kernel/m68k_defs.h | 36 +- arch/m68k/kernel/process.c | 59 +- arch/m68k/kernel/ptrace.c | 24 +- arch/m68k/kernel/signal.c | 40 + arch/m68k/kernel/traps.c | 120 +- arch/m68k/math-emu/Makefile | 19 + arch/m68k/math-emu/fp_arith.c | 700 +++++++ arch/m68k/math-emu/fp_arith.h | 52 + arch/m68k/math-emu/fp_cond.S | 334 ++++ arch/m68k/math-emu/fp_decode.h | 417 ++++ arch/m68k/math-emu/fp_emu.h | 137 ++ arch/m68k/math-emu/fp_entry.S | 325 ++++ arch/m68k/math-emu/fp_log.c | 142 ++ arch/m68k/math-emu/fp_move.S | 244 +++ arch/m68k/math-emu/fp_movem.S | 368 ++++ arch/m68k/math-emu/fp_scan.S | 478 +++++ arch/m68k/math-emu/fp_trig.c | 183 ++ arch/m68k/math-emu/fp_trig.h | 32 + arch/m68k/math-emu/fp_util.S | 1456 ++++++++++++++ arch/m68k/math-emu/multi_arith.h | 822 ++++++++ arch/m68k/q40/README | 30 +- arch/m68k/q40/config.c | 27 +- arch/m68k/q40/q40ints.c | 179 +- drivers/block/floppy.c | 9 +- drivers/char/busmouse.c | 3 +- drivers/char/q40_keyb.c | 29 +- drivers/isdn/avmb1/b1.c | 1 - drivers/isdn/avmb1/b1isa.c | 1 - drivers/isdn/avmb1/b1pcmcia.c | 1 - drivers/isdn/avmb1/t1isa.c | 1 - drivers/isdn/divert/divert_init.c | 1 - drivers/isdn/divert/isdn_divert.c | 1 - drivers/isdn/divert/isdn_divert.h | 1 - drivers/isdn/eicon/eicon.h | 1 + drivers/isdn/eicon/eicon_idi.c | 1 + drivers/isdn/eicon/eicon_idi.h | 1 + drivers/isdn/eicon/eicon_io.c | 1 + drivers/isdn/eicon/eicon_isa.c | 1 + drivers/isdn/eicon/eicon_pci.c | 1 + drivers/isdn/hisax/bkm_a4t.c | 1 + drivers/isdn/hisax/bkm_a8.c | 1 + drivers/isdn/hisax/callc.c | 1 + drivers/isdn/hisax/elsa_ser.c | 1 + drivers/isdn/hisax/gazel.c | 1 + drivers/isdn/hisax/hfc_pci.c | 1 + drivers/isdn/hisax/isdnl1.c | 1 - drivers/isdn/hisax/isurf.c | 1 - drivers/isdn/isdn_bsdcomp.c | 1 - drivers/isdn/isdn_tty.h | 1 + drivers/isdn/isdn_ttyfax.c | 1 - drivers/net/a2065.c | 2 +- drivers/net/apne.c | 3 +- drivers/net/ariadne.c | 2 +- drivers/net/ariadne2.c | 4 +- drivers/net/hydra.c | 3 +- drivers/parport/parport_mfc3.c | 2 +- drivers/pnp/Config.in | 8 + drivers/pnp/Makefile | 8 + drivers/pnp/isapnp.c | 2159 +++++++++++++++++++++ drivers/pnp/isapnp_proc.c | 945 +++++++++ drivers/scsi/53c7xx.c | 2 +- drivers/scsi/oktagon_esp.c | 2 - drivers/scsi/scsi_error.c | 6 +- drivers/sound/lowlevel/awe_compat-fbsd.h | 172 -- drivers/sound/lowlevel/awe_compat-linux.h | 248 --- drivers/usb/audio.c | 29 +- drivers/usb/hub.c | 14 +- drivers/usb/mouse.c | 14 +- drivers/usb/proc_usb.c | 12 +- drivers/usb/uhci.c | 66 +- drivers/usb/uhci.h | 7 + drivers/usb/usb-debug.c | 33 +- drivers/usb/usb.c | 167 +- drivers/usb/usb.h | 28 +- drivers/video/clgenfb.c | 1 + drivers/video/offb.c | 2 +- drivers/video/tgafb.c | 6 +- drivers/video/vga.h | 1 + drivers/video/vgacon.c | 1 + fs/partitions/Config.in | 6 + fs/partitions/amiga.c | 43 +- include/asm-alpha/compiler.h | 4 +- include/asm-alpha/core_apecs.h | 108 +- include/asm-alpha/core_cia.h | 141 +- include/asm-alpha/core_lca.h | 98 +- include/asm-alpha/core_mcpcia.h | 331 ++-- include/asm-alpha/core_polaris.h | 151 +- include/asm-alpha/core_pyxis.h | 367 +++- include/asm-alpha/core_t2.h | 147 +- include/asm-alpha/core_tsunami.h | 127 +- include/asm-alpha/io.h | 99 +- include/asm-alpha/jensen.h | 13 +- include/asm-alpha/machvec.h | 3 +- include/asm-alpha/smp.h | 31 +- include/asm-alpha/vga.h | 20 +- include/asm-m68k/fpu.h | 2 + include/asm-m68k/irq.h | 6 +- include/asm-m68k/math-emu.h | 300 +++ include/asm-m68k/setup.h | 10 + include/linux/cyclomx.h | 1 + include/linux/ioport.h | 17 + include/linux/isapnp.h | 178 ++ include/linux/isdn.h | 3 - include/linux/isdn_ppp.h | 1 + include/linux/isdnif.h | 1 + include/linux/pci.h | 58 +- include/linux/sched.h | 2 + init/main.c | 7 + kernel/printk.c | 2 +- mm/vmscan.c | 5 +- net/netsyms.c | 2 + 126 files changed, 11637 insertions(+), 1534 deletions(-) create mode 100644 Documentation/isapnp.txt create mode 100644 arch/m68k/math-emu/Makefile create mode 100644 arch/m68k/math-emu/fp_arith.c create mode 100644 arch/m68k/math-emu/fp_arith.h create mode 100644 arch/m68k/math-emu/fp_cond.S create mode 100644 arch/m68k/math-emu/fp_decode.h create mode 100644 arch/m68k/math-emu/fp_emu.h create mode 100644 arch/m68k/math-emu/fp_entry.S create mode 100644 arch/m68k/math-emu/fp_log.c create mode 100644 arch/m68k/math-emu/fp_move.S create mode 100644 arch/m68k/math-emu/fp_movem.S create mode 100644 arch/m68k/math-emu/fp_scan.S create mode 100644 arch/m68k/math-emu/fp_trig.c create mode 100644 arch/m68k/math-emu/fp_trig.h create mode 100644 arch/m68k/math-emu/fp_util.S create mode 100644 arch/m68k/math-emu/multi_arith.h create mode 100644 drivers/pnp/isapnp.c create mode 100644 drivers/pnp/isapnp_proc.c delete mode 100644 drivers/sound/lowlevel/awe_compat-fbsd.h delete mode 100644 drivers/sound/lowlevel/awe_compat-linux.h create mode 100644 include/asm-m68k/math-emu.h create mode 100644 include/linux/isapnp.h diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 6c63d56dd17c..11846eec1ce9 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -10663,6 +10663,33 @@ CONFIG_M68060 If you anticipate running this kernel on a computer with a MC68060 processor, say Y. Otherwise, say N. +Math emulation support +CONFIG_M68KFPU_EMU + At some point in the future, this will cause floating-point math + instructions to be emulated by the kernel on machines that lack a + floating-point math coprocessor. Thrill-seekers and chronically + sleep-deprived psychotic hacker types can say Y now, everyone else + should probably wait a while. + +Math emulation only kernel +CONFIG_M68KFPU_EMU_ONLY + This option prevents any floating-point instructions from being + compiled into the kernel, thereby the kernel doesn't save any + floating point context anymore during task switches, so this + kernel will only be usable on machines without a floating-point + math coprocessor. This makes the kernel a bit faster as no tests + needs to be executed whether a floating-point instruction in the + kernel should be executed or not. + +Math emulation extra precision +CONFIG_M68KFPU_EMU_EXTRAPREC + The fpu uses normally a few bit more during calculations for + correct rounding, the emulator can (often) do the same but this + extra calculation can cost quite some time, so you can disable + it here. The emulator will then "only" calculate with a 64 bit + mantissa and round slightly incorrect, what is more then enough + for normal usage. + Advanced processor options CONFIG_ADVANCED_CPU This gives you access to some advanced options for the CPU. The diff --git a/Documentation/isapnp.txt b/Documentation/isapnp.txt new file mode 100644 index 000000000000..57912607c35c --- /dev/null +++ b/Documentation/isapnp.txt @@ -0,0 +1,147 @@ +ISA Plug & Play support by Jaroslav Kysela +========================================================= + +Interface /proc/isapnp +====================== + +Read commands: +-------------- + +No comment.. + +Write commands: +--------------- + +With the write interface you can simply activate or modify the configuration +for ISA Plug & Play devices. It is mainly useable for drivers which don't +use the ISA Plug & Play kernel support yet. + +card - select PnP device by vendor identification +csn - select PnP device by CSN +dev - select logical device +auto - run autoconfigure +activate - activate logical device +deactivate - deactivate logical device +port - set port 0-7 to value +irq - set IRQ 0-1 to value +dma - set DMA 0-1 to value +memory - set memory 0-3 to value +poke - poke configuration byte to selected register +pokew - poke configuration word to selected register +poked - poke configuration dword to selected register + +Explanation: + - variable begins with zero + - variable begins with one + - is in form 'PNP0000' + - is in form 'PNP0000' + +Example: + +cat > /proc/isapnp <prepare() only initialize the resource +members in the device structure. This structure contains all resources +set to auto configuration values after the initialization. The driver for +ISA PnP device may modify (or not) some resources to skip auto configuration +for the given resource. + +The function isapnp_configure does: + - resources which have the auto configuration value are configured + - the auto configuration is created using ISA PnP resource map + - the function writes configuration to ISA PnP configuration registers + - the function returns to the caller actual used resources + +Example (game port initialization) +================================== + +/*** initialization ***/ + + struct pci_dev *dev; + + /* find the first game port, use standard PnP IDs */ + dev = isapnp_find_dev(NULL, + ISAPNP_VENDOR('P','N','P'), + ISAPNP_FUNCTION(0xb02f), + NULL); + if (!dev) + return -ENODEV; + if (dev->prepare(dev)<0) + return -EAGAIN; + if (!dev->ro) { + /* override resource */ + if (user_port != USER_PORT_AUTO_VALUE) + dev->resource[0].start = user_port; + } + if (dev->activate(dev)<0) { + printk("isapnp configure failed (out of resources?)\n"); + return -ENOMEM; + } + user_port = dev->resource[0].start; /* get real port */ + +/*** deactivation ***/ + + /* to deactivate use: */ + if (dev) + dev->deactivate(dev); diff --git a/MAINTAINERS b/MAINTAINERS index 7ab5c19b4bca..0b7a55d323ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -377,7 +377,7 @@ S: Maintained HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series P: Jaroslav Kysela -M: perex@jcu.cz +M: perex@suse.cz S: Maintained IBM MCA SCSI SUBSYSTEM DRIVER @@ -434,6 +434,11 @@ L: linux-irda@list.uit.no W: http://www.cs.uit.no/linux-irda/ S: Maintained +ISAPNP +P: Jaroslav Kysela +M: perex@suse.cz +S: Maintained + ISDN SUBSYSTEM P: Fritz Elfert M: fritz@wuemaus.franken.de diff --git a/arch/alpha/kernel/machvec.h b/arch/alpha/kernel/machvec.h index 54fc1f914294..ff25662060fd 100644 --- a/arch/alpha/kernel/machvec.h +++ b/arch/alpha/kernel/machvec.h @@ -71,7 +71,8 @@ mv_writew: CAT(low1,_writew), \ mv_writel: CAT(low1,_writel), \ mv_writeq: CAT(low1,_writeq), \ - mv_dense_mem: CAT(low2,_dense_mem) + mv_ioremap: CAT(low2,_ioremap), \ + mv_is_ioaddr: CAT(low2,_is_ioaddr) #define IO(UP,low1,low2) \ IO_LITE(UP,low1,low2), \ diff --git a/arch/alpha/lib/io.c b/arch/alpha/lib/io.c index 71ba9d4bf6b0..4e6839bddcdb 100644 --- a/arch/alpha/lib/io.c +++ b/arch/alpha/lib/io.c @@ -37,45 +37,96 @@ void _outl(unsigned int b, unsigned long addr) __outl(b, addr); } +unsigned long ___raw_readb(unsigned long addr) +{ + return __readb(addr); +} + +unsigned long ___raw_readw(unsigned long addr) +{ + return __readw(addr); +} + +unsigned long ___raw_readl(unsigned long addr) +{ + return __readl(addr); +} + +unsigned long ___raw_readq(unsigned long addr) +{ + return __readq(addr); +} unsigned long _readb(unsigned long addr) { - return __readb(addr); + unsigned long r = __readb(addr); + mb(); + return r; } unsigned long _readw(unsigned long addr) { - return __readw(addr); + unsigned long r = __readw(addr); + mb(); + return r; } unsigned long _readl(unsigned long addr) { - return __readl(addr); + unsigned long r = __readl(addr); + mb(); + return r; } unsigned long _readq(unsigned long addr) { - return __readq(addr); + unsigned long r = __readq(addr); + mb(); + return r; +} + +void ___raw_writeb(unsigned char b, unsigned long addr) +{ + __writeb(b, addr); +} + +void ___raw_writeb(unsigned short b, unsigned long addr) +{ + __writew(b, addr); +} + +void ___raw_writel(unsigned int b, unsigned long addr) +{ + __writel(b, addr); +} + +void ___raw_writeq(unsigned long b, unsigned long addr) +{ + __writeq(b, addr); } void _writeb(unsigned char b, unsigned long addr) { __writeb(b, addr); + mb(); } void _writew(unsigned short b, unsigned long addr) { __writew(b, addr); + mb(); } void _writel(unsigned int b, unsigned long addr) { __writel(b, addr); + mb(); } void _writeq(unsigned long b, unsigned long addr) { __writeq(b, addr); + mb(); } /* @@ -363,7 +414,7 @@ void _memcpy_fromio(void * to, unsigned long from, long count) if (count >= 8 && ((long)to & 7) == (from & 7)) { count -= 8; do { - *(u64 *)to = readq(from); + *(u64 *)to = __raw_readq(from); count -= 8; to += 8; from += 8; @@ -374,7 +425,7 @@ void _memcpy_fromio(void * to, unsigned long from, long count) if (count >= 4 && ((long)to & 3) == (from & 3)) { count -= 4; do { - *(u32 *)to = readl(from); + *(u32 *)to = __raw_readl(from); count -= 4; to += 4; from += 4; @@ -385,7 +436,7 @@ void _memcpy_fromio(void * to, unsigned long from, long count) if (count >= 2 && ((long)to & 1) == (from & 1)) { count -= 2; do { - *(u16 *)to = readw(from); + *(u16 *)to = __raw_readw(from); count -= 2; to += 2; from += 2; @@ -394,7 +445,7 @@ void _memcpy_fromio(void * to, unsigned long from, long count) } while (count > 0) { - *(u8 *) to = readb(from); + *(u8 *) to = __raw_readb(from); count--; to++; from++; @@ -414,7 +465,7 @@ void _memcpy_toio(unsigned long to, const void * from, long count) if (count >= 8 && (to & 7) == ((long)from & 7)) { count -= 8; do { - writeq(*(const u64 *)from, to); + __raw_writeq(*(const u64 *)from, to); count -= 8; to += 8; from += 8; @@ -425,7 +476,7 @@ void _memcpy_toio(unsigned long to, const void * from, long count) if (count >= 4 && (to & 3) == ((long)from & 3)) { count -= 4; do { - writel(*(const u32 *)from, to); + __raw_writel(*(const u32 *)from, to); count -= 4; to += 4; from += 4; @@ -436,7 +487,7 @@ void _memcpy_toio(unsigned long to, const void * from, long count) if (count >= 2 && (to & 1) == ((long)from & 1)) { count -= 2; do { - writew(*(const u16 *)from, to); + __raw_writeb(*(const u16 *)from, to); count -= 2; to += 2; from += 2; @@ -445,11 +496,12 @@ void _memcpy_toio(unsigned long to, const void * from, long count) } while (count > 0) { - writeb(*(const u8 *) from, to); + __raw_writeb(*(const u8 *) from, to); count--; to++; from++; } + mb(); } /* @@ -459,21 +511,21 @@ void _memset_c_io(unsigned long to, unsigned long c, long count) { /* Handle any initial odd byte */ if (count > 0 && (to & 1)) { - writeb(c, to); + __raw_writeb(c, to); to++; count--; } /* Handle any initial odd halfword */ if (count >= 2 && (to & 2)) { - writew(c, to); + __raw_writeb(c, to); to += 2; count -= 2; } /* Handle any initial odd word */ if (count >= 4 && (to & 4)) { - writel(c, to); + __raw_writel(c, to); to += 4; count -= 4; } @@ -483,7 +535,7 @@ void _memset_c_io(unsigned long to, unsigned long c, long count) count -= 8; if (count >= 0) { do { - writeq(c, to); + __raw_writeq(c, to); to += 8; count -= 8; } while (count >= 0); @@ -492,20 +544,21 @@ void _memset_c_io(unsigned long to, unsigned long c, long count) /* The tail is word-aligned if we still have count >= 4 */ if (count >= 4) { - writel(c, to); + __raw_writel(c, to); to += 4; count -= 4; } /* The tail is half-word aligned if we have count >= 2 */ if (count >= 2) { - writew(c, to); + __raw_writeb(c, to); to += 2; count -= 2; } /* And finally, one last byte.. */ if (count) { - writeb(c, to); + __raw_writeb(c, to); } + mb(); } diff --git a/arch/i386/defconfig b/arch/i386/defconfig index f8494ca16d95..cbc76fa92c8d 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -69,6 +69,12 @@ CONFIG_BINFMT_MISC=y # CONFIG_I2O_SCSI is not set # CONFIG_I2O_PROC is not set +# +# Plug and Play configuration +# +CONFIG_PNP=y +CONFIG_ISAPNP=y + # # Block devices # diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index a94fa0a160e6..2d51e51a364b 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -93,7 +93,7 @@ extern int rd_image_start; /* starting block # of image */ #endif extern int root_mountflags; -extern int _etext, _edata, _end; +extern int _text, _etext, _edata, _end; extern unsigned long cpu_hz; /* @@ -251,7 +251,7 @@ visws_get_board_type_and_rev(void) static char command_line[COMMAND_LINE_SIZE] = { 0, }; char saved_command_line[COMMAND_LINE_SIZE]; -struct resource standard_resources[] = { +struct resource standard_io_resources[] = { { "dma1", 0x00, 0x1f }, { "pic1", 0x20, 0x3f }, { "timer", 0x40, 0x5f }, @@ -262,14 +262,90 @@ struct resource standard_resources[] = { { "fpu", 0xf0, 0xff } }; -/* For demonstration purposes only.. */ -#define keyboard_resources (standard_resources+3) -struct resource kbd_status_resource = { "status", 0x60, 0x60 }; +#define STANDARD_IO_RESOURCES (sizeof(standard_io_resources)/sizeof(struct resource)) + +/* System RAM - interrupted by the 640kB-1M hole */ +#define code_resource (ram_resources[3]) +#define data_resource (ram_resources[4]) +static struct resource ram_resources[] = { + { "System RAM", 0x000000, 0x09ffff, IORESOURCE_BUSY }, + { "System RAM", 0x100000, 0x100000, IORESOURCE_BUSY }, + { "Video RAM area", 0x0a0000, 0x0bffff }, + { "Kernel code", 0x100000, 0 }, + { "Kernel data", 0, 0 } +}; + +/* System ROM resources */ +#define MAXROMS 6 +static struct resource rom_resources[MAXROMS] = { + { "System ROM", 0xF0000, 0xFFFFF, IORESOURCE_BUSY }, + { "Video ROM", 0xc0000, 0xc7fff } +}; + +#define romsignature(x) (*(unsigned short *)(x) == 0xaa55) + +static void __init probe_roms(void) +{ + int roms = 1; + unsigned long base; + unsigned char *romstart; + + request_resource(&iomem_resource, rom_resources+0); + + /* Video ROM is standard at C000:0000 - C7FF:0000, check signature */ + for (base = 0xC0000; base < 0xE0000; base += 2048) { + romstart = bus_to_virt(base); + if (!romsignature(romstart)) + continue; + request_resource(&iomem_resource, rom_resources + roms); + roms++; + break; + } + + /* Extension roms at C800:0000 - DFFF:0000 */ + for (base = 0xC8000; base < 0xE0000; base += 2048) { + unsigned long length; -#define STANDARD_RESOURCES (sizeof(standard_resources)/sizeof(struct resource)) + romstart = bus_to_virt(base); + if (!romsignature(romstart)) + continue; + length = romstart[2] * 512; + if (length) { + unsigned int i; + unsigned char chksum; + + chksum = 0; + for (i = 0; i < length; i++) + chksum += romstart[i]; + + /* Good checksum? */ + if (!chksum) { + rom_resources[roms].start = base; + rom_resources[roms].end = base + length - 1; + rom_resources[roms].name = "Extension ROM"; + + request_resource(&iomem_resource, rom_resources + roms); + roms++; + if (roms >= MAXROMS) + return; + } + } + } + + /* Final check for motherboard extension rom at E000:0000 */ + base = 0xE0000; + romstart = bus_to_virt(base); + + if (romsignature(romstart)) { + rom_resources[roms].start = base; + rom_resources[roms].end = base + 65535; + rom_resources[roms].name = "Extension ROM"; -__initfunc(void setup_arch(char **cmdline_p, - unsigned long * memory_start_p, unsigned long * memory_end_p)) + request_resource(&iomem_resource, rom_resources + roms); + } +} + +void __init setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p) { unsigned long memory_start, memory_end; char c = ' ', *to = command_line, *from = COMMAND_LINE; @@ -301,6 +377,8 @@ __initfunc(void setup_arch(char **cmdline_p, } #endif + ram_resources[1].end = memory_end-1; + memory_end &= PAGE_MASK; #ifdef CONFIG_BLK_DEV_RAM rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; @@ -310,11 +388,16 @@ __initfunc(void setup_arch(char **cmdline_p, if (!MOUNT_ROOT_RDONLY) root_mountflags &= ~MS_RDONLY; memory_start = (unsigned long) &_end; - init_mm.start_code = PAGE_OFFSET; + init_mm.start_code = (unsigned long) &_text; init_mm.end_code = (unsigned long) &_etext; init_mm.end_data = (unsigned long) &_edata; init_mm.brk = (unsigned long) &_end; + code_resource.start = virt_to_bus(&_text); + code_resource.end = virt_to_bus(&_etext)-1; + data_resource.start = virt_to_bus(&_etext); + data_resource.end = virt_to_bus(&_edata)-1; + /* Save unparsed command line copy for /proc/cmdline */ memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; @@ -339,6 +422,8 @@ __initfunc(void setup_arch(char **cmdline_p, memory_end = memory_end << 20; from++; } + if (memory_end > ram_resources[1].end) + ram_resources[1].end = memory_end-1; } } c = *(from++); @@ -351,6 +436,14 @@ __initfunc(void setup_arch(char **cmdline_p, *to = '\0'; *cmdline_p = command_line; + /* Request the standard RAM and ROM resources - they eat up PCI memory space */ + request_resource(&iomem_resource, ram_resources+0); + request_resource(&iomem_resource, ram_resources+1); + request_resource(&iomem_resource, ram_resources+2); + request_resource(ram_resources+1, &code_resource); + request_resource(ram_resources+1, &data_resource); + probe_roms(); + #define VMALLOC_RESERVE (128 << 20) /* 128MB for vmalloc and initrd */ #define MAXMEM ((unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE)) @@ -386,9 +479,8 @@ __initfunc(void setup_arch(char **cmdline_p, #endif /* request I/O space for devices used on all i[345]86 PCs */ - for (i = 0; i < STANDARD_RESOURCES; i++) - request_resource(&ioport_resource, standard_resources+i); - request_resource(keyboard_resources, &kbd_status_resource); + for (i = 0; i < STANDARD_IO_RESOURCES; i++) + request_resource(&ioport_resource, standard_io_resources+i); #ifdef CONFIG_VT #if defined(CONFIG_VGA_CONSOLE) diff --git a/arch/m68k/Makefile b/arch/m68k/Makefile index 3a5957b095e2..bc914b9b0e60 100644 --- a/arch/m68k/Makefile +++ b/arch/m68k/Makefile @@ -116,6 +116,11 @@ CORE_FILES := $(CORE_FILES) arch/m68k/ifpsp060/ifpsp.o SUBDIRS := $(SUBDIRS) arch/m68k/ifpsp060 endif +ifdef CONFIG_M68KFPU_EMU +CORE_FILES := $(CORE_FILES) arch/m68k/math-emu/mathemu.o +SUBDIRS := $(SUBDIRS) arch/m68k/math-emu +endif + lilo: vmlinux if [ -f $(INSTALL_PATH)/vmlinux ]; then mv -f $(INSTALL_PATH)/vmlinux $(INSTALL_PATH)/vmlinux.old; fi if [ -f $(INSTALL_PATH)/System.map ]; then mv -f $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi diff --git a/arch/m68k/amiga/amiints.c b/arch/m68k/amiga/amiints.c index 4fb886456850..57a92b99c522 100644 --- a/arch/m68k/amiga/amiints.c +++ b/arch/m68k/amiga/amiints.c @@ -19,6 +19,20 @@ * called again. * The whole interrupt handling for CIAs is moved to cia.c * /Roman Zippel + * + * 07/08/99: rewamp of the interrupt handling - we now have two types of + * interrupts, normal and fast handlers, fast handlers being + * marked with SA_INTERRUPT and runs with all other interrupts + * disabled. Normal interrupts disable their own source but + * run with all other interrupt sources enabled. + * PORTS and EXTER interrupts are always shared even if the + * drivers do not explicitly mark this when calling + * request_irq which they really should do. + * This is similar to the way interrupts are handled on all + * other architectures and makes a ton of sense besides + * having the advantage of making it easier to share + * drivers. + * /Jes */ #include @@ -70,7 +84,7 @@ static void ami_badint(int irq, void *dev_id, struct pt_regs *fp) * the amiga IRQ handling routines. */ -__initfunc(void amiga_init_IRQ(void)) +void __init amiga_init_IRQ(void) { int i; @@ -81,7 +95,7 @@ __initfunc(void amiga_init_IRQ(void)) } else { ami_irq_list[i] = new_irq_node(); ami_irq_list[i]->handler = ami_badint; - ami_irq_list[i]->flags = IRQ_FLG_STD; + ami_irq_list[i]->flags = 0; ami_irq_list[i]->dev_id = NULL; ami_irq_list[i]->devname = NULL; ami_irq_list[i]->next = NULL; @@ -117,19 +131,18 @@ static inline void amiga_insert_irq(irq_node_t **list, irq_node_t *node) cur = *list; - if (node->flags & IRQ_FLG_FAST) { - node->flags &= ~IRQ_FLG_SLOW; - while (cur && cur->flags & IRQ_FLG_FAST) { - list = &cur->next; - cur = cur->next; - } - } else if (node->flags & IRQ_FLG_SLOW) { - while (cur) { + if (node->flags & SA_INTERRUPT) { + if (node->flags & SA_SHIRQ) + return; + /* + * There should never be more than one + */ + while (cur && cur->flags & SA_INTERRUPT) { list = &cur->next; cur = cur->next; } } else { - while (cur && !(cur->flags & IRQ_FLG_SLOW)) { + while (cur) { list = &cur->next; cur = cur->next; } @@ -168,13 +181,15 @@ static inline void amiga_delete_irq(irq_node_t **list, void *dev_id) * If the addition was successful, it returns 0. */ -int amiga_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), +int amiga_request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void *dev_id) { irq_node_t *node; if (irq >= AMI_IRQS) { - printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__, irq, devname); + printk ("%s: Unknown IRQ %d from %s\n", __FUNCTION__, + irq, devname); return -ENXIO; } @@ -190,6 +205,11 @@ int amiga_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_r return cia_request_irq(&ciaa_base, irq - IRQ_AMIGA_CIAA, handler, flags, devname, dev_id); + /* + * IRQ_AMIGA_PORTS & IRQ_AMIGA_EXTER defaults to shared, + * we could add a check here for the SA_SHIRQ flag but all drivers + * should be aware of sharing anyway. + */ if (ami_servers[irq]) { if (!(node = new_irq_node())) return -ENOMEM; @@ -200,18 +220,6 @@ int amiga_request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_r node->next = NULL; amiga_insert_irq(&ami_irq_list[irq], node); } else { - if (!(ami_irq_list[irq]->flags & IRQ_FLG_STD)) { - if (ami_irq_list[irq]->flags & IRQ_FLG_LOCK) { - printk("%s: IRQ %d from %s is not replaceable\n", - __FUNCTION__, irq, ami_irq_list[irq]->devname); - return -EBUSY; - } - if (!(flags & IRQ_FLG_REPLACE)) { - printk("%s: %s can't replace IRQ %d from %s\n", - __FUNCTION__, devname, irq, ami_irq_list[irq]->devname); - return -EBUSY; - } - } ami_irq_list[irq]->handler = handler; ami_irq_list[irq]->flags = flags; ami_irq_list[irq]->dev_id = dev_id; @@ -255,7 +263,7 @@ void amiga_free_irq(unsigned int irq, void *dev_id) printk("%s: removing probably wrong IRQ %d from %s\n", __FUNCTION__, irq, ami_irq_list[irq]->devname); ami_irq_list[irq]->handler = ami_badint; - ami_irq_list[irq]->flags = IRQ_FLG_STD; + ami_irq_list[irq]->flags = 0; ami_irq_list[irq]->dev_id = NULL; ami_irq_list[irq]->devname = NULL; custom.intena = ami_intena_vals[irq]; @@ -345,37 +353,58 @@ inline void amiga_do_irq(int irq, struct pt_regs *fp) void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server) { irq_node_t *node, *slow_nodes; - unsigned short flags; + unsigned short flags, intena; kstat.irqs[0][SYS_IRQS + irq]++; if (server->count++) server->reentrance = 1; - /* serve first fast and normal handlers */ - for (node = ami_irq_list[irq]; - node && (!(node->flags & IRQ_FLG_SLOW)); - node = node->next) - node->handler(irq, node->dev_id, fp); - custom.intreq = ami_intena_vals[irq]; + + intena = ami_intena_vals[irq]; + custom.intreq = intena; + + /* serve first fast handlers - there can only be one of these */ + node = ami_irq_list[irq]; + + /* + * Timer interrupts show up like this + */ if (!node) { server->count--; return; } + + if (node && (node->flags & SA_INTERRUPT)) { + save_flags(flags); + cli(); + node->handler(irq, node->dev_id, fp); + restore_flags(flags); + + server->count--; + return; + } + + /* + * Disable the interrupt source in question and reenable all + * other interrupts. No interrupt handler should ever touch + * the intena flags directly! + */ + custom.intena = intena; save_flags(flags); - restore_flags((flags & ~0x0700) | (fp->sr & 0x0700)); - /* if slow handlers exists, serve them now */ + sti(); + slow_nodes = node; for (;;) { for (; node; node = node->next) node->handler(irq, node->dev_id, fp); - /* if reentrance occurred, serve slow handlers again */ - custom.intena = ami_intena_vals[irq]; + if (!server->reentrance) { server->count--; - custom.intena = IF_SETCLR | ami_intena_vals[irq]; + restore_flags(flags); + custom.intena = IF_SETCLR | intena; return; } + server->reentrance = 0; - custom.intena = IF_SETCLR | ami_intena_vals[irq]; node = slow_nodes; } } @@ -493,24 +522,13 @@ int amiga_get_irq_list(char *buf) for (i = 0; i < AMI_STD_IRQS; i++) { if (!(node = ami_irq_list[i])) continue; - if (node->flags & IRQ_FLG_STD) - continue; len += sprintf(buf+len, "ami %2d: %10u ", i, kstat.irqs[0][SYS_IRQS + i]); do { - if (ami_servers[i]) { - if (node->flags & IRQ_FLG_FAST) - len += sprintf(buf+len, "F "); - else if (node->flags & IRQ_FLG_SLOW) - len += sprintf(buf+len, "S "); - else - len += sprintf(buf+len, " "); - } else { - if (node->flags & IRQ_FLG_LOCK) - len += sprintf(buf+len, "L "); - else - len += sprintf(buf+len, " "); - } + if (node->flags & SA_INTERRUPT) + len += sprintf(buf+len, "F "); + else + len += sprintf(buf+len, " "); len += sprintf(buf+len, "%s\n", node->devname); if ((node = node->next)) len += sprintf(buf+len, " "); diff --git a/arch/m68k/amiga/cia.c b/arch/m68k/amiga/cia.c index 8f27234b486d..cda8986994aa 100644 --- a/arch/m68k/amiga/cia.c +++ b/arch/m68k/amiga/cia.c @@ -94,20 +94,6 @@ int cia_request_irq(struct ciabase *base, unsigned int irq, { u_char mask; - if (!(base->irq_list[irq].flags & IRQ_FLG_STD)) { - if (base->irq_list[irq].flags & IRQ_FLG_LOCK) { - printk("%s: IRQ %i from %s is not replaceable\n", - __FUNCTION__, base->cia_irq + irq, - base->irq_list[irq].devname); - return -EBUSY; - } - if (!(flags & IRQ_FLG_REPLACE)) { - printk("%s: %s can't replace IRQ %i from %s\n", __FUNCTION__, - devname, base->cia_irq + irq, - base->irq_list[irq].devname); - return -EBUSY; - } - } base->irq_list[irq].handler = handler; base->irq_list[irq].flags = flags; base->irq_list[irq].dev_id = dev_id; @@ -128,7 +114,7 @@ void cia_free_irq(struct ciabase *base, unsigned int irq, void *dev_id) base->irq_list[irq].devname); base->irq_list[irq].handler = NULL; - base->irq_list[irq].flags = IRQ_FLG_STD; + base->irq_list[irq].flags = 0; cia_able_irq(base, 1 << irq); } @@ -153,14 +139,14 @@ static void cia_handler(int irq, void *dev_id, struct pt_regs *fp) amiga_do_irq_list(base->server_irq, fp, &base->server); } -__initfunc(void cia_init_IRQ(struct ciabase *base)) +void __init cia_init_IRQ(struct ciabase *base) { int i; /* init isr handlers */ for (i = 0; i < CIA_IRQS; i++) { base->irq_list[i].handler = NULL; - base->irq_list[i].flags = IRQ_FLG_STD; + base->irq_list[i].flags = 0; } /* clear any pending interrupt and turn off all interrupts */ @@ -168,7 +154,7 @@ __initfunc(void cia_init_IRQ(struct ciabase *base)) cia_able_irq(base, CIA_ICR_ALL); /* install CIA handler */ - request_irq(base->handler_irq, cia_handler, IRQ_FLG_LOCK, base->name, base); + request_irq(base->handler_irq, cia_handler, 0, base->name, base); custom.intena = IF_SETCLR | base->int_mask; } @@ -179,15 +165,10 @@ int cia_get_irq_list(struct ciabase *base, char *buf) j = base->cia_irq; for (i = 0; i < CIA_IRQS; i++) { - if (!(base->irq_list[i].flags & IRQ_FLG_STD)) { - len += sprintf(buf+len, "cia %2d: %10d ", j + i, - kstat.irqs[0][SYS_IRQS + j + i]); - if (base->irq_list[i].flags & IRQ_FLG_LOCK) - len += sprintf(buf+len, "L "); - else - len += sprintf(buf+len, " "); - len += sprintf(buf+len, "%s\n", base->irq_list[i].devname); - } + len += sprintf(buf+len, "cia %2d: %10d ", j + i, + kstat.irqs[0][SYS_IRQS + j + i]); + len += sprintf(buf+len, " "); + len += sprintf(buf+len, "%s\n", base->irq_list[i].devname); } return len; } diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c index 37b5bde3c4eb..95eaa76f38c5 100644 --- a/arch/m68k/amiga/config.c +++ b/arch/m68k/amiga/config.c @@ -173,7 +173,7 @@ int amiga_parse_bootinfo(const struct bi_record *record) * Identify builtin hardware */ -__initfunc(static void amiga_identify(void)) +static void __init amiga_identify(void) { /* Fill in some default values, if necessary */ if (amiga_eclock == 0) @@ -334,7 +334,7 @@ __initfunc(static void amiga_identify(void)) * Setup the Amiga configuration info */ -__initfunc(void config_amiga(void)) +void __init config_amiga(void) { amiga_debug_init(); amiga_identify(); @@ -446,8 +446,8 @@ __initfunc(void config_amiga(void)) static unsigned short jiffy_ticks; -__initfunc(static void amiga_sched_init(void (*timer_routine)(int, void *, - struct pt_regs *))) +static void __init amiga_sched_init(void (*timer_routine)(int, void *, + struct pt_regs *)) { jiffy_ticks = (amiga_eclock+HZ/2)/HZ; @@ -460,8 +460,7 @@ __initfunc(static void amiga_sched_init(void (*timer_routine)(int, void *, * Please don't change this to use ciaa, as it interferes with the * SCSI code. We'll have to take a look at this later */ - request_irq(IRQ_AMIGA_CIAB_TA, timer_routine, IRQ_FLG_LOCK, - "timer", NULL); + request_irq(IRQ_AMIGA_CIAB_TA, timer_routine, 0, "timer", NULL); /* start timer */ ciab.cra |= 0x11; } @@ -880,7 +879,7 @@ void amiga_serial_gets(struct console *co, char *s, int len) } #endif -__initfunc(static void amiga_debug_init(void)) +static void __init amiga_debug_init(void) { if (!strcmp( m68k_debug_device, "ser" )) { /* no initialization required (?) */ diff --git a/arch/m68k/config.in b/arch/m68k/config.in index f1143d34f7f1..51a3cebc13e2 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -58,6 +58,13 @@ bool '68020 support' CONFIG_M68020 bool '68030 support' CONFIG_M68030 bool '68040 support' CONFIG_M68040 bool '68060 support' CONFIG_M68060 +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'Math emulation support' CONFIG_M68KFPU_EMU + if [ "$CONFIG_M68KFPU_EMU" = "y" ]; then + bool 'Math emulation extra precision' CONFIG_M68KFPU_EMU_EXTRAPREC + bool 'Math emulation only kernel' CONFIG_M68KFPU_EMU_ONLY + fi +fi bool 'Advanced configuration options' CONFIG_ADVANCED if [ "$CONFIG_ADVANCED" = "y" ]; then bool 'Use read-modify-write instructions' CONFIG_RMW_INSNS diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index 2bdab4be75e8..1e8e7fd2f168 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -164,6 +164,36 @@ do_delayed_trace: addql #4,%sp jra 5b + +#if 0 +#if CONFIG_AMIGA +SYMBOL_NAME_LABEL(ami_inthandler) + addql #1,SYMBOL_NAME(local_irq_count) + SAVE_ALL_INT + GET_CURRENT(%d0) + + bfextu %sp@(PT_VECTOR){#4,#12},%d0 + movel %d0,%a0 + addql #1,%a0@(SYMBOL_NAME(kstat)+STAT_IRQ-VECOFF(VEC_SPUR)) + movel %a0@(SYMBOL_NAME(autoirq_list)-VECOFF(VEC_SPUR)),%a0 + +| amiga vector int handler get the req mask instead of irq vector + lea CUSTOMBASE,%a1 + movew %a1@(C_INTREQR),%d0 + andw %a1@(C_INTENAR),%d0 + +| prepare stack (push frame pointer, dev_id & req mask) + pea %sp@ + movel %a0@(IRQ_DEVID),%sp@- + movel %d0,%sp@- + pea %pc@(SYMBOL_NAME(ret_from_interrupt):w) + jbra @(IRQ_HANDLER,%a0)@(0) + +ENTRY(nmi_handler) + rte +#endif +#endif + /* ** This is the main interrupt handler, responsible for calling process_int() */ @@ -183,7 +213,7 @@ SYMBOL_NAME_LABEL(inthandler) jbeq 1f jbsr SYMBOL_NAME(floppy_hardint) jbra 3f -1: +1: #endif jbsr SYMBOL_NAME(process_int)| process the IRQ 3: addql #8,%sp | pop parameters off stack @@ -191,7 +221,7 @@ SYMBOL_NAME_LABEL(inthandler) SYMBOL_NAME_LABEL(ret_from_interrupt) subql #1,SYMBOL_NAME(local_irq_count) jeq 1f -2: +2: RESTORE_ALL 1: #if 1 @@ -295,6 +325,11 @@ SYMBOL_NAME_LABEL(resume) movel %sp,%a0@(TASK_THREAD+THREAD_KSP) /* save floating point context */ +#ifndef CONFIG_M68KFPU_EMU_ONLY +#ifdef CONFIG_M68KFPU_EMU + tstl SYMBOL_NAME(m68k_fputype) + jeq 3f +#endif fsave %a0@(TASK_THREAD+THREAD_FPSTATE) #if defined(CONFIG_M68060) @@ -316,6 +351,7 @@ SYMBOL_NAME_LABEL(resume) 2: fmovemx %fp0-%fp7,%a0@(TASK_THREAD+THREAD_FPREG) fmoveml %fpcr/%fpsr/%fpiar,%a0@(TASK_THREAD+THREAD_FPCNTL) 3: +#endif /* CONFIG_M68KFPU_EMU_ONLY */ /* Return previous task in %d1 */ movel %curptr,%d1 @@ -323,7 +359,11 @@ SYMBOL_NAME_LABEL(resume) movel %a1,%curptr /* restore floating point context */ - +#ifndef CONFIG_M68KFPU_EMU_ONLY +#ifdef CONFIG_M68KFPU_EMU + tstl SYMBOL_NAME(m68k_fputype) + jeq 4f +#endif #if defined(CONFIG_M68060) #if !defined(CPU_M68060_ONLY) btst #3,SYMBOL_NAME(m68k_cputype)+3 @@ -343,6 +383,8 @@ SYMBOL_NAME_LABEL(resume) 2: fmovemx %a1@(TASK_THREAD+THREAD_FPREG),%fp0-%fp7 fmoveml %a1@(TASK_THREAD+THREAD_FPCNTL),%fpcr/%fpsr/%fpiar 3: frestore %a1@(TASK_THREAD+THREAD_FPSTATE) +4: +#endif /* CONFIG_M68KFPU_EMU_ONLY */ /* restore the kernel stack pointer */ movel %a1@(TASK_THREAD+THREAD_KSP),%sp diff --git a/arch/m68k/kernel/head.S b/arch/m68k/kernel/head.S index 361b10cb663a..49285b86d46a 100644 --- a/arch/m68k/kernel/head.S +++ b/arch/m68k/kernel/head.S @@ -503,10 +503,18 @@ func_define putn,1 .macro puts string #if defined(CONSOLE) || defined(SERIAL_DEBUG) +/* The __INITDATA stuff is a no-op when ftrace or kgdb are turned on */ +#if defined(CONFIG_FTRACE) || defined(CONFIG_KGDB) + bra 1f +#endif __INITDATA .Lstr\@: .string "\string" __FINIT +#if defined(CONFIG_FTRACE) || defined(CONFIG_KGDB) + .align 2 +1: +#endif pea %pc@(.Lstr\@) func_call puts addql #4,%sp diff --git a/arch/m68k/kernel/ints.c b/arch/m68k/kernel/ints.c index 31931a920ebc..9dc6427e6320 100644 --- a/arch/m68k/kernel/ints.c +++ b/arch/m68k/kernel/ints.c @@ -86,14 +86,14 @@ void (*mach_free_irq) (unsigned int, void *) = dummy_free_irq; * the IRQ handling routines. */ -__initfunc(void init_IRQ(void)) +void __init init_IRQ(void) { int i; for (i = 0; i < SYS_IRQS; i++) { if (mach_default_handler) irq_list[i].handler = (*mach_default_handler)[i]; - irq_list[i].flags = IRQ_FLG_STD; + irq_list[i].flags = 0; irq_list[i].dev_id = NULL; irq_list[i].devname = default_names[i]; } @@ -144,6 +144,7 @@ int sys_request_irq(unsigned int irq, return -ENXIO; } +#if 0 if (!(irq_list[irq].flags & IRQ_FLG_STD)) { if (irq_list[irq].flags & IRQ_FLG_LOCK) { printk("%s: IRQ %d from %s is not replaceable\n", @@ -156,6 +157,8 @@ int sys_request_irq(unsigned int irq, return -EBUSY; } } +#endif + irq_list[irq].handler = handler; irq_list[irq].flags = flags; irq_list[irq].dev_id = dev_id; @@ -175,7 +178,7 @@ void sys_free_irq(unsigned int irq, void *dev_id) __FUNCTION__, irq, irq_list[irq].devname); irq_list[irq].handler = (*mach_default_handler)[irq]; - irq_list[irq].flags = IRQ_FLG_STD; + irq_list[irq].flags = 0; irq_list[irq].dev_id = NULL; irq_list[irq].devname = default_names[irq]; } @@ -250,9 +253,6 @@ int get_irq_list(char *buf) for (i = 0; i < SYS_IRQS; i++) { len += sprintf(buf+len, "auto %2d: %10u ", i, i ? kstat.irqs[0][i] : num_spurious); - if (irq_list[i].flags & IRQ_FLG_LOCK) - len += sprintf(buf+len, "L "); - else len += sprintf(buf+len, " "); len += sprintf(buf+len, "%s\n", irq_list[i].devname); } diff --git a/arch/m68k/kernel/m68k_defs.h b/arch/m68k/kernel/m68k_defs.h index 4926a6dadac1..374e56e27529 100644 --- a/arch/m68k/kernel/m68k_defs.h +++ b/arch/m68k/kernel/m68k_defs.h @@ -7,19 +7,29 @@ #define TASK_FLAGS 4 #define TASK_SIGPENDING 8 #define TASK_NEEDRESCHED 20 -#define TASK_TSS 470 -#define TASK_MM 622 -#define TSS_KSP 0 -#define TSS_USP 4 -#define TSS_SR 8 -#define TSS_FS 10 -#define TSS_CRP 12 -#define TSS_ESP0 20 -#define TSS_FPREG 24 -#define TSS_FPCNTL 120 -#define TSS_FPSTATE 132 +#define TASK_THREAD 482 +#define TASK_MM 634 +#define TASK_ACTIVE_MM 638 +#define THREAD_KSP 0 +#define THREAD_USP 4 +#define THREAD_SR 8 +#define THREAD_FS 10 +#define THREAD_CRP 12 +#define THREAD_ESP0 20 +#define THREAD_FPREG 24 +#define THREAD_FPCNTL 120 +#define THREAD_FPSTATE 132 #define PT_D0 32 #define PT_ORIG_D0 36 +#define PT_D1 0 +#define PT_D2 4 +#define PT_D3 8 +#define PT_D4 12 +#define PT_D5 16 +#define PT_A0 20 +#define PT_A1 24 +#define PT_A2 28 +#define PT_PC 46 #define PT_SR 44 #define PT_VECTOR 50 #define IRQ_HANDLER 0 @@ -35,6 +45,10 @@ #define FBCON_FONT_DESC_HEIGHT 12 #define FBCON_FONT_DESC_DATA 16 #define FBCON_FONT_DESC_PREF 20 +#define SIGSEGV 11 +#define SEGV_MAPERR 1 +#define SIGTRAP 5 +#define TRAP_TRACE 2 #define CUSTOMBASE -2132807680 #define C_INTENAR 28 #define C_INTREQR 30 diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index ff4819b9aa37..b676d5e2a0fb 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -58,7 +58,7 @@ static void default_idle(void) { while(1) { if (!current->need_resched) -#ifdef MACH_ATARI_ONLY +#if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES) /* block out HSYNC on the atari (falcon) */ __asm__("stop #0x2200" : : : "cc"); #else @@ -161,9 +161,10 @@ void flush_thread(void) unsigned long zero = 0; set_fs(USER_DS); current->thread.fs = __USER_DS; - asm volatile (".chip 68k/68881\n\t" - "frestore %0@\n\t" - ".chip 68k" : : "a" (&zero)); + if (!FPU_IS_EMU) + asm volatile (".chip 68k/68881\n\t" + "frestore %0@\n\t" + ".chip 68k" : : "a" (&zero)); } /* @@ -224,16 +225,18 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, */ p->thread.fs = get_fs().seg; - /* Copy the current fpu state */ - asm volatile ("fsave %0" : : "m" (p->thread.fpstate[0]) : "memory"); - - if (!CPU_IS_060 ? p->thread.fpstate[0] : p->thread.fpstate[2]) - asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t" - "fmoveml %/fpiar/%/fpcr/%/fpsr,%1" - : : "m" (p->thread.fp[0]), "m" (p->thread.fpcntl[0]) - : "memory"); - /* Restore the state in case the fpu was busy */ - asm volatile ("frestore %0" : : "m" (p->thread.fpstate[0])); + if (!FPU_IS_EMU) { + /* Copy the current fpu state */ + asm volatile ("fsave %0" : : "m" (p->thread.fpstate[0]) : "memory"); + + if (!CPU_IS_060 ? p->thread.fpstate[0] : p->thread.fpstate[2]) + asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t" + "fmoveml %/fpiar/%/fpcr/%/fpsr,%1" + : : "m" (p->thread.fp[0]), "m" (p->thread.fpcntl[0]) + : "memory"); + /* Restore the state in case the fpu was busy */ + asm volatile ("frestore %0" : : "m" (p->thread.fpstate[0])); + } return 0; } @@ -244,18 +247,32 @@ int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu) { char fpustate[216]; - /* First dump the fpu context to avoid protocol violation. */ - asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory"); - if (!CPU_IS_060 ? !fpustate[0] : !fpustate[2]) - return 0; + if (FPU_IS_EMU) { + int i; + + memcpy(fpu->fpcntl, current->thread.fpcntl, 12); + memcpy(fpu->fpregs, current->thread.fp, 96); + /* Convert internal fpu reg representation + * into long double format + */ + for (i = 0; i < 24; i += 3) + fpu->fpregs[i] = ((fpu->fpregs[i] & 0xffff0000) << 15) | + ((fpu->fpregs[i] & 0x0000ffff) << 16); + return 1; + } + + /* First dump the fpu context to avoid protocol violation. */ + asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory"); + if (!CPU_IS_060 ? !fpustate[0] : !fpustate[2]) + return 0; - asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0" + asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0" :: "m" (fpu->fpcntl[0]) : "memory"); - asm volatile ("fmovemx %/fp0-%/fp7,%0" + asm volatile ("fmovemx %/fp0-%/fp7,%0" :: "m" (fpu->fpregs[0]) : "memory"); - return 1; + return 1; } /* diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index 9d72d90b5264..7db64849dd29 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -384,10 +385,17 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) tmp = get_reg(child, addr); if (addr == PT_SR) tmp >>= 16; - } - else if (addr >= 21 && addr < 49) + } else if (addr >= 21 && addr < 49) { tmp = child->thread.fp[addr - 21]; - else +#ifdef CONFIG_M68KFPU_EMU + /* Convert internal fpu reg representation + * into long double format + */ + if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) + tmp = ((tmp & 0xffff0000) << 15) | + ((tmp & 0x0000ffff) << 16); +#endif + } else goto out; ret = put_user(tmp,(unsigned long *) data); goto out; @@ -423,6 +431,16 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) } if (addr >= 21 && addr < 48) { +#ifdef CONFIG_M68KFPU_EMU + /* Convert long double format + * into internal fpu reg representation + */ + if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) { + data = (unsigned long)data << 15; + data = (data & 0xffff0000) | + ((data & 0x0000ffff) >> 1); + } +#endif child->thread.fp[addr - 21] = data; ret = 0; } diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index d6302cb28c73..f35dd94c521c 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -14,6 +14,10 @@ * 68060 fixes by Jesper Skov * * 1997-12-01 Modified for POSIX.1b signals by Andreas Schwab + * + * mathemu support by Roman Zippel + * (Note: fpstate in the signal context is completly ignored for the emulator + * and the internal floating point format is put on stack) */ /* @@ -190,6 +194,13 @@ static inline int restore_fpu_state(struct sigcontext *sc) { int err = 1; + if (FPU_IS_EMU) { + /* restore registers */ + memcpy(current->thread.fpcntl, sc->sc_fpcntl, 12); + memcpy(current->thread.fp, sc->sc_fpregs, 24); + return 0; + } + if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) { /* Verify the frame format. */ if (!CPU_IS_060 && (sc->sc_fpstate[0] != fpu_version)) @@ -242,6 +253,18 @@ static inline int rt_restore_fpu_state(struct ucontext *uc) fpregset_t fpregs; int err = 1; + if (FPU_IS_EMU) { + /* restore fpu control register */ + if (__copy_from_user(current->thread.fpcntl, + &uc->uc_mcontext.fpregs.f_pcr, 12)) + goto out; + /* restore all other fpu register */ + if (__copy_from_user(current->thread.fp, + uc->uc_mcontext.fpregs.f_fpregs, 96)) + goto out; + return 0; + } + if (__get_user(*(long *)fpstate, (long *)&uc->uc_fpstate)) goto out; if (CPU_IS_060 ? fpstate[2] : fpstate[0]) { @@ -539,6 +562,13 @@ badframe: static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs) { + if (FPU_IS_EMU) { + /* save registers */ + memcpy(sc->sc_fpcntl, current->thread.fpcntl, 12); + memcpy(sc->sc_fpregs, current->thread.fp, 24); + return; + } + __asm__ volatile (".chip 68k/68881\n\t" "fsave %0\n\t" ".chip 68k" @@ -570,6 +600,16 @@ static inline int rt_save_fpu_state(struct ucontext *uc, struct pt_regs *regs) int context_size = CPU_IS_060 ? 8 : 0; int err = 0; + if (FPU_IS_EMU) { + /* save fpu control register */ + err |= copy_to_user(&uc->uc_mcontext.fpregs.f_pcr, + current->thread.fpcntl, 12); + /* save all other fpu register */ + err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs, + current->thread.fp, 96); + return err; + } + __asm__ volatile (".chip 68k/68881\n\t" "fsave %0\n\t" ".chip 68k" diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index f895ad5e73d2..151b6384e602 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -47,6 +47,7 @@ asmlinkage void buserr(void); asmlinkage void trap(void); asmlinkage void inthandler(void); asmlinkage void nmihandler(void); +asmlinkage void fpu_emu(void); e_vector vectors[256] = { 0, 0, buserr, trap, trap, trap, trap, trap, @@ -65,12 +66,12 @@ asm(".text\n" __ALIGN_STR "\n" SYMBOL_NAME_STR(nmihandler) ": rte"); -__initfunc(void base_trap_init(void)) +void __init base_trap_init(void) { /* setup the exception vector table */ __asm__ volatile ("movec %0,%%vbr" : : "r" ((void*)vectors)); - if (CPU_IS_040) { + if (CPU_IS_040 && !FPU_IS_EMU) { /* set up FPSP entry points */ asmlinkage void dz_vec(void) asm ("dz"); asmlinkage void inex_vec(void) asm ("inex"); @@ -93,6 +94,12 @@ __initfunc(void base_trap_init(void)) vectors[VEC_FPUNSUP] = unsupp_vec; } if (CPU_IS_060) { + /* set up ISP entry points */ + asmlinkage void unimp_vec(void) asm ("_060_isp_unimp"); + + vectors[VEC_UNIMPII] = unimp_vec; + } + if (CPU_IS_060 && !FPU_IS_EMU) { /* set up IFPSP entry points */ asmlinkage void snan_vec(void) asm ("_060_fpsp_snan"); asmlinkage void operr_vec(void) asm ("_060_fpsp_operr"); @@ -104,8 +111,6 @@ __initfunc(void base_trap_init(void)) asmlinkage void unsupp_vec(void) asm ("_060_fpsp_unsupp"); asmlinkage void effadd_vec(void) asm ("_060_fpsp_effadd"); - asmlinkage void unimp_vec(void) asm ("_060_isp_unimp"); - vectors[VEC_FPNAN] = snan_vec; vectors[VEC_FPOE] = operr_vec; vectors[VEC_FPOVER] = ovfl_vec; @@ -115,14 +120,10 @@ __initfunc(void base_trap_init(void)) vectors[VEC_LINE11] = fline_vec; vectors[VEC_FPUNSUP] = unsupp_vec; vectors[VEC_UNIMPEA] = effadd_vec; - - /* set up ISP entry points */ - - vectors[VEC_UNIMPII] = unimp_vec; } } -__initfunc(void trap_init (void)) +void __init trap_init (void) { int i; @@ -133,18 +134,17 @@ __initfunc(void trap_init (void)) for (i = 64; i < 256; i++) vectors[i] = inthandler; +#ifdef CONFIG_M68KFPU_EMU + if (FPU_IS_EMU) + vectors[VEC_LINE11] = fpu_emu; +#endif + /* if running on an amiga, make the NMI interrupt do nothing */ if (MACH_IS_AMIGA) { vectors[VEC_INT7] = nmihandler; } } -void set_evector(int vecnum, void (*handler)(void)) -{ - if (vecnum >= 0 && vecnum <= 256) - vectors[vecnum] = handler; -} - static inline void console_verbose(void) { @@ -152,6 +152,7 @@ static inline void console_verbose(void) console_loglevel = 15; } + static char *vec_names[] = { "RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR", "ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc", @@ -180,7 +181,6 @@ static char *space_names[] = { }; - void die_if_kernel(char *,struct pt_regs *,int); asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long error_code); @@ -507,44 +507,6 @@ static inline void bus_error030 (struct frame *fp) else asm volatile ("ploadr %1,%0@" : /* no outputs */ : "a" (addr), "d" (ssw)); - -#if 0 - /* If this was a data fault due to an invalid page and a - prefetch is pending on the same page, simulate it (but - only if the page is now valid). Otherwise we'll get an - weird insn access. */ - if ((ssw & RB) && (mmusr & MMU_I)) - { - unsigned long iaddr; - - if ((fp->ptregs.format) == 0xB) - iaddr = fp->un.fmtb.baddr; - else - iaddr = fp->ptregs.pc + 4; - if (((addr ^ iaddr) & PAGE_MASK) == 0) - { - /* We only need to check the ATC as the entry has - already been set up above. */ - asm volatile ("ptestr #1,%1@,#0\n\t" - "pmove %/psr,%0@" - : : "a" (&temp), "a" (iaddr)); - mmusr = temp; -#ifdef DEBUG - printk ("prefetch iaddr=%#lx ssw=%#x mmusr=%#x\n", - iaddr, ssw, mmusr); -#endif - if (!(mmusr & MMU_I)) - { - unsigned short insn; - asm volatile ("movesw %1@,%0" - : "=r" (insn) - : "a" (iaddr)); - fp->un.fmtb.isb = insn; - fp->un.fmtb.ssw &= ~RB; - } - } - } -#endif } /* Now handle the instruction fault. */ @@ -598,43 +560,6 @@ static inline void bus_error030 (struct frame *fp) die_if_kernel("Oops",&fp->ptregs,mmusr); force_sig(SIGSEGV, current); return; - } else { -#if 0 /* stale ATC entry?? Ignore it */ - -#ifdef DEBUG - static volatile long tlong; -#endif - - printk ("weird insn access at %#lx from pc %#lx (ssw is %#x)\n", - addr, fp->ptregs.pc, ssw); - asm volatile ("ptestr #1,%1@,#0\n\t" - "pmove %/psr,%0@" - : /* no outputs */ - : "a" (&temp), "a" (addr)); - mmusr = temp; - - printk ("level 0 mmusr is %#x\n", mmusr); -#ifdef DEBUG - if (m68k_cputype & CPU_68030) { - asm volatile ("pmove %/tt0,%0@" - : /* no outputs */ - : "a" (&tlong)); - printk ("tt0 is %#lx, ", tlong); - asm volatile ("pmove %/tt1,%0@" - : /* no outputs */ - : "a" (&tlong)); - printk ("tt1 is %#lx\n", tlong); - } - -#endif - -#if DEBUG - printk("Unknown SIGSEGV - 3\n"); -#endif - die_if_kernel("Oops",&fp->ptregs,mmusr); - force_sig(SIGSEGV, current); - return; -#endif } create_atc_entry: @@ -990,3 +915,16 @@ asmlinkage void fpsp040_die(void) { do_exit(SIGSEGV); } + +#ifdef CONFIG_M68KFPU_EMU +asmlinkage void fpemu_signal(int signal, int code, void *addr) +{ + siginfo_t info; + + info.si_signo = signal; + info.si_errno = 0; + info.si_code = code; + info.si_addr = addr; + force_sig_info(signal, &info, current); +} +#endif diff --git a/arch/m68k/math-emu/Makefile b/arch/m68k/math-emu/Makefile new file mode 100644 index 000000000000..fe379cb2c9dd --- /dev/null +++ b/arch/m68k/math-emu/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.o: + $(CC) $(EXTRA_CFLAGS) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +#EXTRA_CFLAGS=-DFPU_EMU_DEBUG + +O_TARGET := mathemu.o +O_OBJS := fp_entry.o fp_scan.o fp_util.o fp_move.o fp_movem.o \ + fp_cond.o fp_arith.o fp_log.o fp_trig.o + +include $(TOPDIR)/Rules.make diff --git a/arch/m68k/math-emu/fp_arith.c b/arch/m68k/math-emu/fp_arith.c new file mode 100644 index 000000000000..c494b1da9c2e --- /dev/null +++ b/arch/m68k/math-emu/fp_arith.c @@ -0,0 +1,700 @@ +/* + + fp_arith.c: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998-1999 David Huggins-Daines. + + Somewhat based on the AlphaLinux floating point emulator, by David + Mosberger-Tang. + + You may copy, modify, and redistribute this file under the terms of + the GNU General Public License, version 2, or any later version, at + your convenience. + */ + +#include "fp_emu.h" +#include "multi_arith.h" +#include "fp_arith.h" + +const struct fp_ext fp_QNaN = +{ + 0, 0, 0x7fff, { ~0 } +}; + +const struct fp_ext fp_Inf = +{ + 0, 0, 0x7fff, { 0 } +}; + +/* let's start with the easy ones */ + +struct fp_ext * +fp_fabs(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fabs\n"); + + fp_monadic_check(dest, src); + + dest->sign = 0; + + return dest; +} + +struct fp_ext * +fp_fneg(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fneg\n"); + + fp_monadic_check(dest, src); + + dest->sign = !dest->sign; + + return dest; +} + +/* Now, the slightly harder ones */ + +/* fp_fadd: Implements the kernel of the FADD, FSADD, FDADD, FSUB, + FDSUB, and FCMP instructions. */ + +struct fp_ext * +fp_fadd(struct fp_ext *dest, struct fp_ext *src) +{ + int diff; + + dprint(PINSTR, "fadd\n"); + + fp_dyadic_check(dest, src); + + if (IS_INF(dest)) { + /* infinity - infinity == NaN */ + if (IS_INF(src) && (src->sign != dest->sign)) + fp_set_nan(dest); + return dest; + } + if (IS_INF(src)) { + fp_copy_ext(dest, src); + return dest; + } + + if (IS_ZERO(dest)) { + if (IS_ZERO(src)) { + if (src->sign != dest->sign) { + if (FPDATA->rnd == FPCR_ROUND_RM) + dest->sign = 1; + else + dest->sign = 0; + } + } else + fp_copy_ext(dest, src); + return dest; + } + + dest->lowmant = src->lowmant = 0; + + if ((diff = dest->exp - src->exp) > 0) + fp_denormalize(src, diff); + else if ((diff = -diff) > 0) + fp_denormalize(dest, diff); + + if (dest->sign == src->sign) { + if (fp_addmant(dest, src)) + if (!fp_addcarry(dest)) + return dest; + } else { + if (dest->mant.m64 < src->mant.m64) { + fp_submant(dest, src, dest); + dest->sign = !dest->sign; + } else + fp_submant(dest, dest, src); + } + + return dest; +} + +/* fp_fsub: Implementes the kernel of the FSUB, FSSUB, and FDSUB + instructions. + + Remember that the arguments are in assembler-syntax order! */ + +struct fp_ext * +fp_fsub(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fsub "); + + src->sign = !src->sign; + return fp_fadd(dest, src); +} + + +struct fp_ext * +fp_fcmp(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fcmp "); + + FPDATA->temp[1] = *dest; + src->sign = !src->sign; + return fp_fadd(&FPDATA->temp[1], src); +} + +struct fp_ext * +fp_ftst(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "ftst\n"); + + (void)dest; + + return src; +} + +struct fp_ext * +fp_fmul(struct fp_ext *dest, struct fp_ext *src) +{ + union fp_mant128 temp; + int exp; + + dprint(PINSTR, "fmul\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + if (IS_ZERO(src)) + fp_set_nan(dest); + return dest; + } + if (IS_INF(src)) { + if (IS_ZERO(dest)) + fp_set_nan(dest); + else + fp_copy_ext(dest, src); + return dest; + } + + /* Of course, as we all know, zero * anything = zero. You may + not have known that it might be a positive or negative + zero... */ + if (IS_ZERO(dest) || IS_ZERO(src)) { + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + exp = dest->exp + src->exp - 0x3ffe; + + /* shift up the mantissa for denormalized numbers, + so that the highest bit is set, this makes the + shift of the result below easier */ + if ((long)dest->mant.m32[0] >= 0) + exp -= fp_overnormalize(dest); + if ((long)src->mant.m32[0] >= 0) + exp -= fp_overnormalize(src); + + /* now, do a 64-bit multiply with expansion */ + fp_multiplymant(&temp, dest, src); + + /* normalize it back to 64 bits and stuff it back into the + destination struct */ + if ((long)temp.m32[0] > 0) { + exp--; + fp_putmant128(dest, &temp, 1); + } else + fp_putmant128(dest, &temp, 0); + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +/* fp_fdiv: Implements the "kernel" of the FDIV, FSDIV, FDDIV and + FSGLDIV instructions. + + Note that the order of the operands is counter-intuitive: instead + of src / dest, the result is actually dest / src. */ + +struct fp_ext * +fp_fdiv(struct fp_ext *dest, struct fp_ext *src) +{ + union fp_mant128 temp; + int exp; + + dprint(PINSTR, "fdiv\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + /* infinity / infinity = NaN (quiet, as always) */ + if (IS_INF(src)) + fp_set_nan(dest); + /* infinity / anything else = infinity (with approprate sign) */ + return dest; + } + if (IS_INF(src)) { + /* anything / infinity = zero (with appropriate sign) */ + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + /* zeroes */ + if (IS_ZERO(dest)) { + /* zero / zero = NaN */ + if (IS_ZERO(src)) + fp_set_nan(dest); + /* zero / anything else = zero */ + return dest; + } + if (IS_ZERO(src)) { + /* anything / zero = infinity (with appropriate sign) */ + fp_set_sr(FPSR_EXC_DZ); + dest->exp = 0x7fff; + dest->mant.m64 = 0; + + return dest; + } + + exp = dest->exp - src->exp + 0x3fff; + + /* shift up the mantissa for denormalized numbers, + so that the highest bit is set, this makes lots + of things below easier */ + if ((long)dest->mant.m32[0] >= 0) + exp -= fp_overnormalize(dest); + if ((long)src->mant.m32[0] >= 0) + exp -= fp_overnormalize(src); + + /* now, do the 64-bit divide */ + fp_dividemant(&temp, dest, src); + + /* normalize it back to 64 bits and stuff it back into the + destination struct */ + if (!temp.m32[0]) { + exp--; + fp_putmant128(dest, &temp, 32); + } else + fp_putmant128(dest, &temp, 31); + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +struct fp_ext * +fp_fsglmul(struct fp_ext *dest, struct fp_ext *src) +{ + int exp; + + dprint(PINSTR, "fsglmul\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + if (IS_ZERO(src)) + fp_set_nan(dest); + return dest; + } + if (IS_INF(src)) { + if (IS_ZERO(dest)) + fp_set_nan(dest); + else + fp_copy_ext(dest, src); + return dest; + } + + /* Of course, as we all know, zero * anything = zero. You may + not have known that it might be a positive or negative + zero... */ + if (IS_ZERO(dest) || IS_ZERO(src)) { + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + exp = dest->exp + src->exp - 0x3ffe; + + /* do a 32-bit multiply */ + fp_mul64(dest->mant.m32[0], dest->mant.m32[1], + dest->mant.m32[0] & 0xffffff00, + src->mant.m32[0] & 0xffffff00); + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +struct fp_ext * +fp_fsgldiv(struct fp_ext *dest, struct fp_ext *src) +{ + int exp; + unsigned long quot, rem; + + dprint(PINSTR, "fsgldiv\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + /* infinity / infinity = NaN (quiet, as always) */ + if (IS_INF(src)) + fp_set_nan(dest); + /* infinity / anything else = infinity (with approprate sign) */ + return dest; + } + if (IS_INF(src)) { + /* anything / infinity = zero (with appropriate sign) */ + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + /* zeroes */ + if (IS_ZERO(dest)) { + /* zero / zero = NaN */ + if (IS_ZERO(src)) + fp_set_nan(dest); + /* zero / anything else = zero */ + return dest; + } + if (IS_ZERO(src)) { + /* anything / zero = infinity (with appropriate sign) */ + fp_set_sr(FPSR_EXC_DZ); + dest->exp = 0x7fff; + dest->mant.m64 = 0; + + return dest; + } + + exp = dest->exp - src->exp + 0x3fff; + + dest->mant.m32[0] &= 0xffffff00; + src->mant.m32[0] &= 0xffffff00; + + /* do the 32-bit divide */ + if (dest->mant.m32[0] >= src->mant.m32[0]) { + fp_sub64(dest->mant, src->mant); + fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); + dest->mant.m32[0] = 0x80000000 | (quot >> 1); + dest->mant.m32[1] = (quot & 1) | rem; /* only for rounding */ + } else { + fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); + dest->mant.m32[0] = quot; + dest->mant.m32[1] = rem; /* only for rounding */ + exp--; + } + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +/* fp_roundint: Internal rounding function for use by several of these + emulated instructions. + + This one rounds off the fractional part using the rounding mode + specified. */ + +static void fp_roundint(struct fp_ext *dest, int mode) +{ + union fp_mant64 oldmant; + unsigned long mask; + + if (!fp_normalize_ext(dest)) + return; + + /* infinities and zeroes */ + if (IS_INF(dest) || IS_ZERO(dest)) + return; + + /* first truncate the lower bits */ + oldmant = dest->mant; + switch (dest->exp) { + case 0 ... 0x3ffe: + dest->mant.m64 = 0; + break; + case 0x3fff ... 0x401e: + dest->mant.m32[0] &= 0xffffffffU << (0x401e - dest->exp); + dest->mant.m32[1] = 0; + if (oldmant.m64 == dest->mant.m64) + return; + break; + case 0x401f ... 0x403e: + dest->mant.m32[1] &= 0xffffffffU << (0x403e - dest->exp); + if (oldmant.m32[1] == dest->mant.m32[1]) + return; + break; + default: + return; + } + fp_set_sr(FPSR_EXC_INEX2); + + /* We might want to normalize upwards here... however, since + we know that this is only called on the output of fp_fdiv, + or with the input to fp_fint or fp_fintrz, and the inputs + to all these functions are either normal or denormalized + (no subnormals allowed!), there's really no need. + + In the case of fp_fdiv, observe that 0x80000000 / 0xffff = + 0xffff8000, and the same holds for 128-bit / 64-bit. (i.e. the + smallest possible normal dividend and the largest possible normal + divisor will still produce a normal quotient, therefore, (normal + << 64) / normal is normal in all cases) */ + + switch (mode) { + case FPCR_ROUND_RN: + switch (dest->exp) { + case 0 ... 0x3ffd: + return; + case 0x3ffe: + /* As noted above, the input is always normal, so the + guard bit (bit 63) is always set. therefore, the + only case in which we will NOT round to 1.0 is when + the input is exactly 0.5. */ + if (oldmant.m64 == (1ULL << 63)) + return; + break; + case 0x3fff ... 0x401d: + mask = 1 << (0x401d - dest->exp); + if (!(oldmant.m32[0] & mask)) + return; + if (oldmant.m32[0] & (mask << 1)) + break; + if (!(oldmant.m32[0] << (dest->exp - 0x3ffd)) && + !oldmant.m32[1]) + return; + break; + case 0x401e: + if (!(oldmant.m32[1] >= 0)) + return; + if (oldmant.m32[0] & 1) + break; + if (!(oldmant.m32[1] << 1)) + return; + break; + case 0x401f ... 0x403d: + mask = 1 << (0x403d - dest->exp); + if (!(oldmant.m32[1] & mask)) + return; + if (oldmant.m32[1] & (mask << 1)) + break; + if (!(oldmant.m32[1] << (dest->exp - 0x401d))) + return; + break; + default: + return; + } + break; + case FPCR_ROUND_RZ: + return; + default: + if (dest->sign ^ (mode - FPCR_ROUND_RM)) + break; + return; + } + + switch (dest->exp) { + case 0 ... 0x3ffe: + dest->exp = 0x3fff; + dest->mant.m64 = 1ULL << 63; + break; + case 0x3fff ... 0x401e: + mask = 1 << (0x401e - dest->exp); + if (dest->mant.m32[0] += mask) + break; + dest->mant.m32[0] = 0x80000000; + dest->exp++; + break; + case 0x401f ... 0x403e: + mask = 1 << (0x403e - dest->exp); + if (dest->mant.m32[1] += mask) + break; + if (dest->mant.m32[0] += 1) + break; + dest->mant.m32[0] = 0x80000000; + dest->exp++; + break; + } +} + +/* modrem_kernel: Implementation of the FREM and FMOD instructions + (which are exactly the same, except for the rounding used on the + intermediate value) */ + +static struct fp_ext * +modrem_kernel(struct fp_ext *dest, struct fp_ext *src, int mode) +{ + struct fp_ext tmp; + + fp_dyadic_check(dest, src); + + /* Infinities and zeros */ + if (IS_INF(dest) || IS_ZERO(src)) { + fp_set_nan(dest); + return dest; + } + if (IS_ZERO(dest) || IS_INF(src)) + return dest; + + /* FIXME: there is almost certainly a smarter way to do this */ + fp_copy_ext(&tmp, dest); + fp_fdiv(&tmp, src); /* NOTE: src might be modified */ + fp_roundint(&tmp, mode); + fp_fmul(&tmp, src); + fp_fsub(dest, &tmp); + + /* set the quotient byte */ + fp_set_quotient((dest->mant.m64 & 0x7f) | (dest->sign << 7)); + return dest; +} + +/* fp_fmod: Implements the kernel of the FMOD instruction. + + Again, the argument order is backwards. The result, as defined in + the Motorola manuals, is: + + fmod(src,dest) = (dest - (src * floor(dest / src))) */ + +struct fp_ext * +fp_fmod(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fmod\n"); + return modrem_kernel(dest, src, FPCR_ROUND_RZ); +} + +/* fp_frem: Implements the kernel of the FREM instruction. + + frem(src,dest) = (dest - (src * round(dest / src))) + */ + +struct fp_ext * +fp_frem(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "frem\n"); + return modrem_kernel(dest, src, FPCR_ROUND_RN); +} + +struct fp_ext * +fp_fint(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fint\n"); + + fp_copy_ext(dest, src); + + fp_roundint(dest, FPDATA->rnd); + + return dest; +} + +struct fp_ext * +fp_fintrz(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fintrz\n"); + + fp_copy_ext(dest, src); + + fp_roundint(dest, FPCR_ROUND_RZ); + + return dest; +} + +struct fp_ext * +fp_fscale(struct fp_ext *dest, struct fp_ext *src) +{ + int scale, oldround; + + dprint(PINSTR, "fscale\n"); + + fp_dyadic_check(dest, src); + + /* Infinities */ + if (IS_INF(src)) { + fp_set_nan(dest); + return dest; + } + if (IS_INF(dest)) + return dest; + + /* zeroes */ + if (IS_ZERO(src) || IS_ZERO(dest)) + return dest; + + /* Source exponent out of range */ + if (src->exp >= 0x400c) { + fp_set_ovrflw(dest); + return dest; + } + + /* src must be rounded with round to zero. */ + oldround = FPDATA->rnd; + FPDATA->rnd = FPCR_ROUND_RZ; + scale = fp_conv_ext2long(src); + FPDATA->rnd = oldround; + + /* new exponent */ + scale += dest->exp; + + if (scale >= 0x7fff) { + fp_set_ovrflw(dest); + } else if (scale <= 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -scale); + } else + dest->exp = scale; + + return dest; +} + diff --git a/arch/m68k/math-emu/fp_arith.h b/arch/m68k/math-emu/fp_arith.h new file mode 100644 index 000000000000..2cc3f846c393 --- /dev/null +++ b/arch/m68k/math-emu/fp_arith.h @@ -0,0 +1,52 @@ +/* + + fp_arith.h: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998 David Huggins-Daines. + + Somewhat based on the AlphaLinux floating point emulator, by David + Mosberger-Tang. + + You may copy, modify, and redistribute this file under the terms of + the GNU General Public License, version 2, or any later version, at + your convenience. + + */ + +#ifndef FP_ARITH_H +#define FP_ARITH_H + +/* easy ones */ +struct fp_ext * +fp_fabs(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fneg(struct fp_ext *dest, struct fp_ext *src); + +/* straightforward arithmetic */ +struct fp_ext * +fp_fadd(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fsub(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fcmp(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_ftst(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fmul(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fdiv(struct fp_ext *dest, struct fp_ext *src); + +/* ones that do rounding and integer conversions */ +struct fp_ext * +fp_fmod(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_frem(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fint(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fintrz(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fscale(struct fp_ext *dest, struct fp_ext *src); + +#endif /* FP_ARITH__H */ diff --git a/arch/m68k/math-emu/fp_cond.S b/arch/m68k/math-emu/fp_cond.S new file mode 100644 index 000000000000..d9981d6a833b --- /dev/null +++ b/arch/m68k/math-emu/fp_cond.S @@ -0,0 +1,334 @@ +/* + * fp_cond.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + + .globl fp_fscc, fp_fbccw, fp_fbccl + +#ifdef FPU_EMU_DEBUG +fp_fnop: + printf PDECODE,"fnop\n" + jra fp_end +#else +#define fp_fnop fp_end +#endif + +fp_fbccw: + tst.w %d2 + jeq fp_fnop + printf PDECODE,"fbccw " + fp_get_pc %a0 + lea (-2,%a0,%d2.w),%a0 + jra 1f + +fp_fbccl: + printf PDECODE,"fbccl " + fp_get_pc %a0 + move.l %d2,%d0 + swap %d0 + fp_get_instr_word %d0,fp_err_ua1 + lea (-2,%a0,%d0.l),%a0 +1: printf PDECODE,"%x",1,%a0 + move.l %d2,%d0 + swap %d0 + jsr fp_compute_cond + tst.l %d0 + jeq 1f + fp_put_pc %a0,1 +1: printf PDECODE,"\n" + jra fp_end + +fp_fdbcc: + printf PDECODE,"fdbcc " + fp_get_pc %a1 | calculate new pc + fp_get_instr_word %d0,fp_err_ua1 + add.w %d0,%a1 + fp_decode_addr_reg + printf PDECODE,"d%d,%x\n",2,%d0,%a1 + swap %d1 | test condition in %d1 + tst.w %d1 + jne 2f + move.l %d0,%d1 + jsr fp_get_data_reg + subq.w #1,%d0 + jcs 1f + fp_put_pc %a1,1 +1: jsr fp_put_data_reg +2: jra fp_end + +| set flags for decode macros for fs +do_fscc=1 +do_no_pc_mode=1 + +fp_fscc: + printf PDECODE,"fscc " + move.l %d2,%d0 + jsr fp_compute_cond + move.w %d0,%d1 + swap %d1 + + | decode addressing mode + fp_decode_addr_mode + + .long fp_data, fp_fdbcc + .long fp_indirect, fp_postinc + .long fp_predecr, fp_disp16 + .long fp_extmode0, fp_extmode1 + + | addressing mode: data register direct +fp_data: + fp_mode_data_direct + move.w %d0,%d1 | save register nr + jsr fp_get_data_reg + swap %d1 + move.b %d1,%d0 + swap %d1 + jsr fp_put_data_reg + printf PDECODE,"\n" + jra fp_end + +fp_indirect: + fp_mode_addr_indirect + jra fp_do_scc + +fp_postinc: + fp_mode_addr_indirect_postinc + jra fp_do_scc + +fp_predecr: + fp_mode_addr_indirect_predec + jra fp_do_scc + +fp_disp16: + fp_mode_addr_indirect_disp16 + jra fp_do_scc + +fp_extmode0: + fp_mode_addr_indirect_extmode0 + jra fp_do_scc + +fp_extmode1: + bfextu %d2{#13,#3},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_absolute_short, fp_absolute_long + .long fp_ill, fp_ill | NOTE: jump here to ftrap.x + .long fp_ill, fp_ill + .long fp_ill, fp_ill + +fp_absolute_short: + fp_mode_abs_short + jra fp_do_scc + +fp_absolute_long: + fp_mode_abs_long +| jra fp_do_scc + +fp_do_scc: + swap %d1 + putuser.b %d1,(%a0),fp_err_ua1,%a0 + printf PDECODE,"\n" + jra fp_end + + +#define tst_NAN btst #24,%d1 +#define tst_Z btst #26,%d1 +#define tst_N btst #27,%d1 + +fp_compute_cond: + move.l (FPD_FPSR,FPDATA),%d1 + btst #4,%d0 + jeq 1f + tst_NAN + jeq 1f + bset #15,%d1 + bset #7,%d1 + move.l %d1,(FPD_FPSR,FPDATA) +1: and.w #0xf,%d0 + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_f , fp_eq , fp_ogt, fp_oge + .long fp_olt, fp_ole, fp_ogl, fp_or + .long fp_un , fp_ueq, fp_ugt, fp_uge + .long fp_ult, fp_ule, fp_ne , fp_t + +fp_f: + moveq #0,%d0 + rts + +fp_eq: + moveq #0,%d0 + tst_Z + jeq 1f + moveq #-1,%d0 +1: rts + +fp_ogt: + moveq #0,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jne 1f + moveq #-1,%d0 +1: rts + +fp_oge: + moveq #-1,%d0 + tst_Z + jne 2f + tst_NAN + jne 1f + tst_N + jeq 2f +1: moveq #0,%d0 +2: rts + +fp_olt: + moveq #0,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jeq 1f + moveq #-1,%d0 +1: rts + +fp_ole: + moveq #-1,%d0 + tst_Z + jne 2f + tst_NAN + jne 1f + tst_N + jne 2f +1: moveq #0,%d0 +2: rts + +fp_ogl: + moveq #0,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + moveq #-1,%d0 +1: rts + +fp_or: + moveq #0,%d0 + tst_NAN + jne 1f + moveq #-1,%d0 +1: rts + +fp_un: + moveq #0,%d0 + tst_NAN + jeq 1f + moveq #-1,%d0 + rts + +fp_ueq: + moveq #-1,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + moveq #0,%d0 +1: rts + +fp_ugt: + moveq #-1,%d0 + tst_NAN + jne 2f + tst_N + jne 1f + tst_Z + jeq 2f +1: moveq #0,%d0 +2: rts + +fp_uge: + moveq #-1,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jeq 1f + moveq #0,%d0 +1: rts + +fp_ult: + moveq #-1,%d0 + tst_NAN + jne 2f + tst_Z + jne 1f + tst_N + jne 2f +1: moveq #0,%d0 +2: rts + +fp_ule: + moveq #-1,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jne 1f + moveq #0,%d0 +1: rts + +fp_ne: + moveq #0,%d0 + tst_Z + jne 1f + moveq #-1,%d0 +1: rts + +fp_t: + moveq #-1,%d0 + rts diff --git a/arch/m68k/math-emu/fp_decode.h b/arch/m68k/math-emu/fp_decode.h new file mode 100644 index 000000000000..233269415ba4 --- /dev/null +++ b/arch/m68k/math-emu/fp_decode.h @@ -0,0 +1,417 @@ +/* + * fp_decode.h + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FP_DECODE_H +#define _FP_DECODE_H + +/* These macros do the dirty work of the instr decoding, several variables + * can be defined in the source file to modify the work of these macros, + * currently the following variables are used: + * ... + * The register usage: + * d0 - will contain source operand for data direct mode, + * otherwise scratch register + * d1 - upper 16bit are reserved for caller + * lower 16bit may contain further arguments, + * is destroyed during decoding + * d2 - contains first two instruction words, + * first word will be used for extension word + * a0 - will point to source/dest operand for any indirect mode + * otherwise scratch register + * a1 - scratch register + * a2 - base addr to the task structure + * + * the current implementation doesn't check for every disallowed + * addressing mode (e.g. pc relative modes as destination), as long + * as it only means a new addressing mode, which should not appear + * in a program and that doesn't crash the emulation, I think it's + * not a problem to allow these modes. + */ + +do_fmovem=0 +do_fmovem_cr=0 +do_no_pc_mode=0 +do_fscc=0 + +| first decoding of the instr type +| this seperates the conditional instr +.macro fp_decode_cond_instr_type + bfextu %d2{#8,#2},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "f","fscc/fdbcc" +| .long "fbccw","fbccl" +.endm + +| second decoding of the instr type +| this seperates most move instr +.macro fp_decode_move_instr_type + bfextu %d2{#16,#3},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "f fpx,fpx","invalid instr" +| .long "f ,fpx","fmove fpx," +| .long "fmovem ,fpcr","fmovem ,fpx" +| .long "fmovem fpcr,","fmovem fpx," +.endm + +| extract the source specifier, specifies +| either source fp register or data format +.macro fp_decode_sourcespec + bfextu %d2{#19,#3},%d0 +.endm + +| decode destination format for fmove reg,ea +.macro fp_decode_dest_format + bfextu %d2{#19,#3},%d0 +.endm + +| decode source register for fmove reg,ea +.macro fp_decode_src_reg + bfextu %d2{#22,#3},%d0 +.endm + +| extract the addressing mode +| it depends on the instr which of the modes is valid +.macro fp_decode_addr_mode + bfextu %d2{#10,#3},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "data register direct","addr register direct" +| .long "addr register indirect" +| .long "addr register indirect postincrement" +| .long "addr register indirect predecrement" +| .long "addr register + index16" +| .long "extension mode1","extension mode2" +.endm + +| extract the register for the addressing mode +.macro fp_decode_addr_reg + bfextu %d2{#13,#3},%d0 +.endm + +| decode the 8bit diplacement from the brief extension word +.macro fp_decode_disp8 + move.b %d2,%d0 + ext.w %d0 +.endm + +| decode the index of the brief/full extension word +.macro fp_decode_index + bfextu %d2{#17,#3},%d0 | get the register nr + btst #15,%d2 | test for data/addr register + jne 1\@f + printf PDECODE,"d%d",1,%d0 + jsr fp_get_data_reg + jra 2\@f +1\@: printf PDECODE,"a%d",1,%d0 + jsr fp_get_addr_reg + move.l %a0,%d0 +2\@: +debug lea "'l'.w,%a0" + btst #11,%d2 | 16/32 bit size? + jne 3\@f +debug lea "'w'.w,%a0" + ext.l %d0 +3\@: printf PDECODE,":%c",1,%a0 + move.w %d2,%d1 | scale factor + rol.w #7,%d1 + and.w #3,%d1 +debug move.l "%d1,-(%sp)" +debug ext.l "%d1" + printf PDECODE,":%d",1,%d1 +debug move.l "(%sp)+,%d1" + lsl.l %d1,%d0 +.endm + +| decode the base displacement size +.macro fp_decode_basedisp + bfextu %d2{#26,#2},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "reserved","null displacement" +| .long "word displacement","long displacement" +.endm + +.macro fp_decode_outerdisp + bfextu %d2{#30,#2},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "no memory indirect action/reserved","null outer displacement" +| .long "word outer displacement","long outer displacement" +.endm + +| get the extension word and test for brief or full extension type +.macro fp_get_test_extword label + fp_get_instr_word %d2,fp_err_ua1 + btst #8,%d2 + jne \label +.endm + + +| test if %pc is the base register for the indirect addr mode +.macro fp_test_basereg_d16 label + btst #20,%d2 + jeq \label +.endm + +| test if %pc is the base register for one of the extended modes +.macro fp_test_basereg_ext label + btst #19,%d2 + jeq \label +.endm + +.macro fp_test_suppr_index label + btst #6,%d2 + jne \label +.endm + + +| addressing mode: data register direct +.macro fp_mode_data_direct + fp_decode_addr_reg + printf PDECODE,"d%d",1,%d0 +.endm + +| addressing mode: address register indirect +.macro fp_mode_addr_indirect + fp_decode_addr_reg + printf PDECODE,"(a%d)",1,%d0 + jsr fp_get_addr_reg +.endm + +| adjust stack for byte moves from/to stack +.macro fp_test_sp_byte_move + .if !do_fmovem + .if do_fscc + move.w #6,%d1 + .endif + cmp.w #7,%d0 + jne 1\@f + .if !do_fscc + cmp.w #6,%d1 + jne 1\@f + .endif + move.w #4,%d1 +1\@: + .endif +.endm + +| addressing mode: address register indirect with postincrement +.macro fp_mode_addr_indirect_postinc + fp_decode_addr_reg + printf PDECODE,"(a%d)+",1,%d0 + fp_test_sp_byte_move + jsr fp_get_addr_reg + move.l %a0,%a1 | save addr + .if do_fmovem + lea (%a0,%d1.w*4),%a0 + .if !do_fmovem_cr + lea (%a0,%d1.w*8),%a0 + .endif + .else + add.w (fp_datasize,%d1.w*2),%a0 + .endif + jsr fp_put_addr_reg + move.l %a1,%a0 +.endm + +| addressing mode: address register indirect with predecrement +.macro fp_mode_addr_indirect_predec + fp_decode_addr_reg + printf PDECODE,"-(a%d)",1,%d0 + fp_test_sp_byte_move + jsr fp_get_addr_reg + .if do_fmovem + .if !do_fmovem_cr + lea (-12,%a0),%a1 | setup to addr of 1st reg to move + neg.w %d1 + lea (%a0,%d1.w*4),%a0 + add.w %d1,%d1 + lea (%a0,%d1.w*4),%a0 + jsr fp_put_addr_reg + move.l %a1,%a0 + .else + neg.w %d1 + lea (%a0,%d1.w*4),%a0 + jsr fp_put_addr_reg + .endif + .else + sub.w (fp_datasize,%d1.w*2),%a0 + jsr fp_put_addr_reg + .endif +.endm + +| addressing mode: address register/programm counter indirect +| with 16bit displacement +.macro fp_mode_addr_indirect_disp16 + .if !do_no_pc_mode + fp_test_basereg_d16 1f + printf PDECODE,"pc" + fp_get_pc %a0 + jra 2f + .endif +1: fp_decode_addr_reg + printf PDECODE,"a%d",1,%d0 + jsr fp_get_addr_reg +2: fp_get_instr_word %a1,fp_err_ua1 + printf PDECODE,"@(%x)",1,%a1 + add.l %a1,%a0 +.endm + +| perform preindex (if I/IS == 0xx and xx != 00) +.macro fp_do_preindex + moveq #3,%d0 + and.w %d2,%d0 + jeq 1f + btst #2,%d2 + jne 1f + printf PDECODE,")@(" + getuser.l (%a1),%a1,fp_err_ua1,%a1 +debug jra "2f" +1: printf PDECODE,"," +2: +.endm + +| perform postindex (if I/IS == 1xx) +.macro fp_do_postindex + btst #2,%d2 + jeq 1f + printf PDECODE,")@(" + getuser.l (%a1),%a1,fp_err_ua1,%a1 +debug jra "2f" +1: printf PDECODE,"," +2: +.endm + +| all other indirect addressing modes will finally end up here +.macro fp_mode_addr_indirect_extmode0 + .if !do_no_pc_mode + fp_test_basereg_ext 1f + printf PDECODE,"pc" + fp_get_pc %a0 + jra 2f + .endif +1: fp_decode_addr_reg + printf PDECODE,"a%d",1,%d0 + jsr fp_get_addr_reg +2: move.l %a0,%a1 + swap %d2 + fp_get_test_extword 3f + | addressing mode: address register/programm counter indirect + | with index and 8bit displacement + fp_decode_disp8 +debug ext.l "%d0" + printf PDECODE,"@(%x,",1,%d0 + add.w %d0,%a1 + fp_decode_index + add.l %d0,%a1 + printf PDECODE,")" + jra 9f +3: | addressing mode: address register/programm counter memory indirect + | with base and/or outer displacement + btst #7,%d2 | base register suppressed? + jeq 1f + printf PDECODE,"!" + sub.l %a1,%a1 +1: printf PDECODE,"@(" + fp_decode_basedisp + + .long fp_ill,1f + .long 2f,3f + +#ifdef FPU_EMU_DEBUG +1: printf PDECODE,"0" | null base displacement + jra 1f +#endif +2: fp_get_instr_word %a0,fp_err_ua1 | 16bit base displacement + printf PDECODE,"%x:w",1,%a0 + jra 4f +3: fp_get_instr_long %a0,fp_err_ua1 | 32bit base displacement + printf PDECODE,"%x:l",1,%a0 +4: add.l %a0,%a1 +1: + fp_do_postindex + fp_test_suppr_index 1f + fp_decode_index + add.l %d0,%a1 +1: fp_do_preindex + + fp_decode_outerdisp + + .long 5f,1f + .long 2f,3f + +#ifdef FPU_EMU_DEBUG +1: printf PDECODE,"0" | null outer displacement + jra 1f +#endif +2: fp_get_instr_word %a0,fp_err_ua1 | 16bit outer displacement + printf PDECODE,"%x:w",1,%a0 + jra 4f +3: fp_get_instr_long %a0,fp_err_ua1 | 32bit outer displacement + printf PDECODE,"%x:l",1,%a0 +4: add.l %a0,%a1 +1: +5: printf PDECODE,")" +9: move.l %a1,%a0 + swap %d2 +.endm + +| get the absolute short address from user space +.macro fp_mode_abs_short + fp_get_instr_word %a0,fp_err_ua1 + printf PDECODE,"%x.w",1,%a0 +.endm + +| get the absolute long address from user space +.macro fp_mode_abs_long + fp_get_instr_long %a0,fp_err_ua1 + printf PDECODE,"%x.l",1,%a0 +.endm + +#endif /* _FP_DECODE_H */ diff --git a/arch/m68k/math-emu/fp_emu.h b/arch/m68k/math-emu/fp_emu.h new file mode 100644 index 000000000000..9344b939b09c --- /dev/null +++ b/arch/m68k/math-emu/fp_emu.h @@ -0,0 +1,137 @@ +/* + * fp_emu.h + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FP_EMU_H +#define _FP_EMU_H + +#ifndef __ASSEMBLY__ + +#include + +#define IS_INF(a) ((a)->exp == 0x7fff) +#define IS_ZERO(a) ((a)->mant.m64 == 0) + + +#define fp_set_sr(bit) ({ \ + FPDATA->fpsr |= 1 << (bit); \ +}) + +#define fp_set_quotient(quotient) ({ \ + FPDATA->fpsr &= 0xff00ffff; \ + FPDATA->fpsr |= ((quotient) & 0xff) << 16; \ +}) + +/* linkage for several useful functions */ + +/* Normalize the extended struct, return 0 for a NaN */ +#define fp_normalize_ext(fpreg) ({ \ + register struct fp_ext *reg asm ("a0") = fpreg; \ + register int res asm ("d0"); \ + \ + asm volatile ("jsr fp_conv_ext2ext" \ + : "=d" (res) : "a" (reg) \ + : "a1", "d1", "d2", "memory"); \ + res; \ +}) + +#define fp_copy_ext(dest, src) ({ \ + *dest = *src; \ +}) + +#define fp_monadic_check(dest, src) ({ \ + fp_copy_ext(dest, src); \ + if (!fp_normalize_ext(dest)) \ + return dest; \ +}) + +#define fp_dyadic_check(dest, src) ({ \ + if (!fp_normalize_ext(dest)) \ + return dest; \ + if (!fp_normalize_ext(src)) { \ + fp_copy_ext(dest, src); \ + return dest; \ + } \ +}) + +extern const struct fp_ext fp_QNaN; +extern const struct fp_ext fp_Inf; + +#define fp_set_nan(dest) ({ \ + fp_set_sr(FPSR_EXC_OPERR); \ + *dest = fp_QNaN; \ +}) + +/* TODO check rounding mode? */ +#define fp_set_ovrflw(dest) ({ \ + fp_set_sr(FPSR_EXC_OVFL); \ + dest->exp = 0x7fff; \ + dest->mant.m64 = 0; \ +}) + +#define fp_conv_ext2long(src) ({ \ + register struct fp_ext *__src asm ("a0") = src; \ + register int __res asm ("d0"); \ + \ + asm volatile ("jsr fp_conv_ext2long" \ + : "=d" (__res) : "a" (__src) \ + : "a1", "d1", "d2", "memory"); \ + __res; \ +}) + +#else /* __ASSEMBLY__ */ + +#include "../kernel/m68k_defs.h" +#include + +/* + * set, reset or clear a bit in the fp status register + */ +.macro fp_set_sr bit + bset #(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA) +.endm + +.macro fp_clr_sr bit + bclr #(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA) +.endm + +.macro fp_tst_sr bit + btst #(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA) +.endm + +#endif /* __ASSEMBLY__ */ + +#endif /* _FP_EMU_H */ diff --git a/arch/m68k/math-emu/fp_entry.S b/arch/m68k/math-emu/fp_entry.S new file mode 100644 index 000000000000..55088e8e8f54 --- /dev/null +++ b/arch/m68k/math-emu/fp_entry.S @@ -0,0 +1,325 @@ +/* + * fp_emu.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "fp_emu.h" + + .globl SYMBOL_NAME(fpu_emu) + .globl fp_debugprint + .globl fp_err_ua1,fp_err_ua2 + + .text +SYMBOL_NAME_LABEL(fpu_emu) + SAVE_ALL_INT + GET_CURRENT(%d0) + +#if defined(CPU_M68020_OR_M68030) && defined(CPU_M68040_OR_M68060) + tst.l SYMBOL_NAME(m68k_is040or060) + jeq 1f +#endif +#if defined(CPU_M68040_OR_M68060) + move.l (FPS_PC2,%sp),(FPS_PC,%sp) +#endif +1: + | emulate the instruction + jsr fp_scan + +#if defined(CONFIG_M68060) +#if !defined(CPU_M68060_ONLY) + btst #3,SYMBOL_NAME(m68k_cputype)+3 + jeq 1f +#endif + btst #7,(FPS_SR,%sp) + jne fp_sendtrace060 +#endif +1: + | emulation successful? + tst.l %d0 + jeq SYMBOL_NAME(ret_from_exception) + + | send some signal to program here + + jra SYMBOL_NAME(ret_from_exception) + + | we jump here after an access error while trying to access + | user space, we correct stackpointer and send a SIGSEGV to + | the user process +fp_err_ua2: + addq.l #4,%sp +fp_err_ua1: + addq.l #4,%sp + move.l %a0,-(%sp) + pea SEGV_MAPERR + pea SIGSEGV + jsr SYMBOL_NAME(fpemu_signal) + add.w #12,%sp + jra SYMBOL_NAME(ret_from_exception) + +#if defined(CONFIG_M68060) + | send a trace signal if we are debugged + | it does not really belong here, but... +fp_sendtrace060: + move.l (FPS_PC,%sp),-(%sp) + pea TRAP_TRACE + pea SIGTRAP + jsr SYMBOL_NAME(fpemu_signal) + add.w #12,%sp + jra SYMBOL_NAME(ret_from_exception) +#endif + + .globl fp_get_data_reg, fp_put_data_reg + .globl fp_get_addr_reg, fp_put_addr_reg + + | Entry points to get/put a register. Some of them can be get/put + | directly, others are on the stack, as we read/write the stack + | directly here, these function may only be called from within + | instruction decoding, otherwise the stack pointer is incorrect + | and the stack gets corrupted. +fp_get_data_reg: + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_get_d0, fp_get_d1 + .long fp_get_d2, fp_get_d3 + .long fp_get_d4, fp_get_d5 + .long fp_get_d6, fp_get_d7 + +fp_get_d0: + move.l (PT_D0+8,%sp),%d0 + printf PREGISTER,"{d0->%08x}",1,%d0 + rts + +fp_get_d1: + move.l (PT_D1+8,%sp),%d0 + printf PREGISTER,"{d1->%08x}",1,%d0 + rts + +fp_get_d2: + move.l (PT_D2+8,%sp),%d0 + printf PREGISTER,"{d2->%08x}",1,%d0 + rts + +fp_get_d3: + move.l %d3,%d0 + printf PREGISTER,"{d3->%08x}",1,%d0 + rts + +fp_get_d4: + move.l %d4,%d0 + printf PREGISTER,"{d4->%08x}",1,%d0 + rts + +fp_get_d5: + move.l %d5,%d0 + printf PREGISTER,"{d5->%08x}",1,%d0 + rts + +fp_get_d6: + move.l %d6,%d0 + printf PREGISTER,"{d6->%08x}",1,%d0 + rts + +fp_get_d7: + move.l %d7,%d0 + printf PREGISTER,"{d7->%08x}",1,%d0 + rts + +fp_put_data_reg: + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: + .long fp_put_d0, fp_put_d1 + .long fp_put_d2, fp_put_d3 + .long fp_put_d4, fp_put_d5 + .long fp_put_d6, fp_put_d7 + +fp_put_d0: + printf PREGISTER,"{d0<-%08x}",1,%d0 + move.l %d0,(PT_D0+8,%sp) + rts + +fp_put_d1: + printf PREGISTER,"{d1<-%08x}",1,%d0 + move.l %d0,(PT_D1+8,%sp) + rts + +fp_put_d2: + printf PREGISTER,"{d2<-%08x}",1,%d0 + move.l %d0,(PT_D2+8,%sp) + rts + +fp_put_d3: + printf PREGISTER,"{d3<-%08x}",1,%d0 +| move.l %d0,%d3 + move.l %d0,(PT_D3+8,%sp) + rts + +fp_put_d4: + printf PREGISTER,"{d4<-%08x}",1,%d0 +| move.l %d0,%d4 + move.l %d0,(PT_D4+8,%sp) + rts + +fp_put_d5: + printf PREGISTER,"{d5<-%08x}",1,%d0 +| move.l %d0,%d5 + move.l %d0,(PT_D5+8,%sp) + rts + +fp_put_d6: + printf PREGISTER,"{d6<-%08x}",1,%d0 + move.l %d0,%d6 + rts + +fp_put_d7: + printf PREGISTER,"{d7<-%08x}",1,%d0 + move.l %d0,%d7 + rts + +fp_get_addr_reg: + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_get_a0, fp_get_a1 + .long fp_get_a2, fp_get_a3 + .long fp_get_a4, fp_get_a5 + .long fp_get_a6, fp_get_a7 + +fp_get_a0: + move.l (PT_A0+8,%sp),%a0 + printf PREGISTER,"{a0->%08x}",1,%a0 + rts + +fp_get_a1: + move.l (PT_A1+8,%sp),%a0 + printf PREGISTER,"{a1->%08x}",1,%a0 + rts + +fp_get_a2: + move.l (PT_A2+8,%sp),%a0 + printf PREGISTER,"{a2->%08x}",1,%a0 + rts + +fp_get_a3: + move.l %a3,%a0 + printf PREGISTER,"{a3->%08x}",1,%a0 + rts + +fp_get_a4: + move.l %a4,%a0 + printf PREGISTER,"{a4->%08x}",1,%a0 + rts + +fp_get_a5: + move.l %a5,%a0 + printf PREGISTER,"{a5->%08x}",1,%a0 + rts + +fp_get_a6: + move.l %a6,%a0 + printf PREGISTER,"{a6->%08x}",1,%a0 + rts + +fp_get_a7: + move.l %usp,%a0 + printf PREGISTER,"{a7->%08x}",1,%a0 + rts + +fp_put_addr_reg: + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_put_a0, fp_put_a1 + .long fp_put_a2, fp_put_a3 + .long fp_put_a4, fp_put_a5 + .long fp_put_a6, fp_put_a7 + +fp_put_a0: + printf PREGISTER,"{a0<-%08x}",1,%a0 + move.l %a0,(PT_A0+8,%sp) + rts + +fp_put_a1: + printf PREGISTER,"{a1<-%08x}",1,%a0 + move.l %a0,(PT_A1+8,%sp) + rts + +fp_put_a2: + printf PREGISTER,"{a2<-%08x}",1,%a0 + move.l %a0,(PT_A2+8,%sp) + rts + +fp_put_a3: + printf PREGISTER,"{a3<-%08x}",1,%a0 + move.l %a0,%a3 + rts + +fp_put_a4: + printf PREGISTER,"{a4<-%08x}",1,%a0 + move.l %a0,%a4 + rts + +fp_put_a5: + printf PREGISTER,"{a5<-%08x}",1,%a0 + move.l %a0,%a5 + rts + +fp_put_a6: + printf PREGISTER,"{a6<-%08x}",1,%a0 + move.l %a0,%a6 + rts + +fp_put_a7: + printf PREGISTER,"{a7<-%08x}",1,%a0 + move.l %a0,%usp + rts + + .data + .align 4 + +fp_debugprint: +| .long PMDECODE + .long PMINSTR+PMDECODE+PMCONV+PMNORM +| .long PMCONV+PMNORM+PMINSTR +| .long 0 diff --git a/arch/m68k/math-emu/fp_log.c b/arch/m68k/math-emu/fp_log.c new file mode 100644 index 000000000000..8e1c96420ec0 --- /dev/null +++ b/arch/m68k/math-emu/fp_log.c @@ -0,0 +1,142 @@ +/* + + fp_trig.c: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998-1999 David Huggins-Daines / Roman Zippel. + + I hereby give permission, free of charge, to copy, modify, and + redistribute this software, in source or binary form, provided that + the above copyright notice and the following disclaimer are included + in all such copies. + + THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL + OR IMPLIED. + +*/ + +#include "fp_emu.h" + +struct fp_ext * +fp_fsqrt(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsqrt\n"); + + fp_monadic_check(dest, src); + + if (IS_ZERO(dest)) + return dest; + + if (dest->sign) { + fp_set_nan(dest); + return dest; + } + if (IS_INF(dest)) + return dest; + + return dest; +} + +struct fp_ext * +fp_fetoxm1(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fetoxm1\n"); + + fp_monadic_check(dest, src); + + if (IS_ZERO(dest)) + return dest; + + return dest; +} + +struct fp_ext * +fp_fetox(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fetox\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftwotox(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftwotox\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftentox(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftentox\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flogn(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flogn\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flognp1(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flognp1\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flog10(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flog10\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flog2(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flog2\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fgetexp(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fgetexp\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fgetman(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fgetman\n"); + + fp_monadic_check(dest, src); + + return dest; +} + diff --git a/arch/m68k/math-emu/fp_move.S b/arch/m68k/math-emu/fp_move.S new file mode 100644 index 000000000000..45fb02bfe934 --- /dev/null +++ b/arch/m68k/math-emu/fp_move.S @@ -0,0 +1,244 @@ +/* + * fp_move.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + +do_no_pc_mode=1 + + .globl fp_fmove_fp2mem + +fp_fmove_fp2mem: + clr.b (2+FPD_FPSR,FPDATA) + fp_decode_dest_format + move.w %d0,%d1 | store data size twice in %d1 + swap %d1 | one can be trashed below + move.w %d0,%d1 +#ifdef FPU_EMU_DEBUG + lea 0f,%a0 + clr.l %d0 + move.b (%a0,%d1.w),%d0 + printf PDECODE,"fmove.%c ",1,%d0 + fp_decode_src_reg + printf PDECODE,"fp%d,",1,%d0 + + .data +0: .byte 'l','s','x','p','w','d','b','p' + .previous +#endif + + | encode addressing mode for dest + fp_decode_addr_mode + + .long fp_data, fp_ill + .long fp_indirect, fp_postinc + .long fp_predecr, fp_disp16 + .long fp_extmode0, fp_extmode1 + + | addressing mode: data register direct +fp_data: + fp_mode_data_direct + move.w %d0,%d1 + fp_decode_src_reg + fp_get_fp_reg + lea (FPD_TEMPFP1,FPDATA),%a1 + move.l (%a0)+,(%a1)+ + move.l (%a0)+,(%a1)+ + move.l (%a0),(%a1) + lea (-8,%a1),%a0 + swap %d1 + move.l %d1,%d2 + printf PDECODE,"\n" + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: + .long fp_data_long, fp_data_single + .long fp_ill, fp_ill + .long fp_data_word, fp_ill + .long fp_data_byte, fp_ill + +fp_data_byte: + jsr fp_normalize_ext + jsr fp_conv_ext2byte + move.l %d0,%d1 + swap %d2 + move.w %d2,%d0 + jsr fp_get_data_reg + move.b %d1,%d0 + move.w %d2,%d1 + jsr fp_put_data_reg + jra fp_final + +fp_data_word: + jsr fp_normalize_ext + jsr fp_conv_ext2short + move.l %d0,%d1 + swap %d2 + move.w %d2,%d0 + jsr fp_get_data_reg + move.w %d1,%d0 + move.l %d2,%d1 + jsr fp_put_data_reg + jra fp_final + +fp_data_long: + jsr fp_normalize_ext + jsr fp_conv_ext2long + swap %d2 + move.w %d2,%d1 + jsr fp_put_data_reg + jra fp_final + +fp_data_single: + jsr fp_normalize_ext + jsr fp_conv_ext2single + swap %d2 + move.w %d2,%d1 + jsr fp_put_data_reg + jra fp_final + + | addressing mode: address register indirect +fp_indirect: + fp_mode_addr_indirect + jra fp_putdest + + | addressing mode: address register indirect with postincrement +fp_postinc: + fp_mode_addr_indirect_postinc + jra fp_putdest + + | addressing mode: address register indirect with predecrement +fp_predecr: + fp_mode_addr_indirect_predec + jra fp_putdest + + | addressing mode: address register indirect with 16bit displacement +fp_disp16: + fp_mode_addr_indirect_disp16 + jra fp_putdest + +fp_extmode0: + fp_mode_addr_indirect_extmode0 + jra fp_putdest + +fp_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_abs_short, fp_abs_long + .long fp_ill, fp_ill + .long fp_ill, fp_ill + .long fp_ill, fp_ill + +fp_abs_short: + fp_mode_abs_short + jra fp_putdest + +fp_abs_long: + fp_mode_abs_long + jra fp_putdest + +fp_putdest: + move.l %a0,%a1 + fp_decode_src_reg + move.l %d1,%d2 | save size + fp_get_fp_reg + printf PDECODE,"\n" + addq.l #8,%a0 + move.l (%a0),-(%sp) + move.l -(%a0),-(%sp) + move.l -(%a0),-(%sp) + move.l %sp,%a0 + jsr fp_normalize_ext + + swap %d2 + jmp ([0f:w,%pc,%d2.w*4]) + + .align 4 +0: + .long fp_format_long, fp_format_single + .long fp_format_extended, fp_format_packed + .long fp_format_word, fp_format_double + .long fp_format_byte, fp_format_packed + +fp_format_long: + jsr fp_conv_ext2long + putuser.l %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_single: + jsr fp_conv_ext2single + putuser.l %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_extended: + move.l (%a0)+,%d0 + lsl.w #1,%d0 + lsl.l #7,%d0 + lsl.l #8,%d0 + putuser.l %d0,(%a1)+,fp_err_ua1,%a1 + move.l (%a0)+,%d0 + putuser.l %d0,(%a1)+,fp_err_ua1,%a1 + move.l (%a0),%d0 + putuser.l %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_packed: + /* not supported yet */ + lea (12,%sp),%sp + jra fp_ill + +fp_format_word: + jsr fp_conv_ext2short + putuser.w %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_double: + jsr fp_conv_ext2double + jra fp_finish_move + +fp_format_byte: + jsr fp_conv_ext2byte + putuser.b %d0,(%a1),fp_err_ua1,%a1 +| jra fp_finish_move + +fp_finish_move: + lea (12,%sp),%sp + jra fp_final diff --git a/arch/m68k/math-emu/fp_movem.S b/arch/m68k/math-emu/fp_movem.S new file mode 100644 index 000000000000..01058b33bd91 --- /dev/null +++ b/arch/m68k/math-emu/fp_movem.S @@ -0,0 +1,368 @@ +/* + * fp_movem.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + +| set flags for decode macros for fmovem +do_fmovem=1 + + .globl fp_fmovem_fp, fp_fmovem_cr + +| %d1 contains the mask and count of the register list +| for other register usage see fp_decode.h + +fp_fmovem_fp: + printf PDECODE,"fmovem.x " + | get register list and count them + btst #11,%d2 + jne 1f + bfextu %d2{#24,#8},%d0 | static register list + jra 2f +1: bfextu %d2{#25,#3},%d0 | dynamic register list + jsr fp_get_data_reg +2: move.l %d0,%d1 + swap %d1 + jra 2f +1: addq.w #1,%d1 | count the # of registers in +2: lsr.b #1,%d0 | register list and keep it in %d1 + jcs 1b + jne 2b + printf PDECODE,"#%08x",1,%d1 +#ifdef FPU_EMU_DEBUG + btst #12,%d2 + jne 1f + printf PDECODE,"-" | decremental move + jra 2f +1: printf PDECODE,"+" | incremental move +2: btst #13,%d2 + jeq 1f + printf PDECODE,"->" | fpu -> cpu + jra 2f +1: printf PDECODE,"<-" | fpu <- cpu +2: +#endif + + | decode address mode + fp_decode_addr_mode + + .long fp_ill, fp_ill + .long fpr_indirect, fpr_postinc + .long fpr_predecr, fpr_disp16 + .long fpr_extmode0, fpr_extmode1 + + | addressing mode: address register indirect +fpr_indirect: + fp_mode_addr_indirect + jra fpr_do_movem + + | addressing mode: address register indirect with postincrement +fpr_postinc: + fp_mode_addr_indirect_postinc + jra fpr_do_movem + +fpr_predecr: + fp_mode_addr_indirect_predec + jra fpr_do_movem + + | addressing mode: address register/programm counter indirect + | with 16bit displacement +fpr_disp16: + fp_mode_addr_indirect_disp16 + jra fpr_do_movem + +fpr_extmode0: + fp_mode_addr_indirect_extmode0 + jra fpr_do_movem + +fpr_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fpr_absolute_short, fpr_absolute_long + .long fpr_disp16, fpr_extmode0 + .long fp_ill, fp_ill + .long fp_ill, fp_ill + +fpr_absolute_short: + fp_mode_abs_short + jra fpr_do_movem + +fpr_absolute_long: + fp_mode_abs_long +| jra fpr_do_movem + +fpr_do_movem: + swap %d1 | get fpu register list + lea (FPD_FPREG,FPDATA),%a1 + moveq #12,%d0 + btst #12,%d2 + jne 1f + lea (-12,%a1,%d0*8),%a1 + neg.l %d0 +1: btst #13,%d2 + jne 4f + | move register from memory into fpu + jra 3f +1: printf PMOVEM,"(%p>%p)",2,%a0,%a1 + getuser.l (%a0)+,%d2,fp_err_ua1,%a0 + lsr.l #8,%d2 + lsr.l #7,%d2 + lsr.w #1,%d2 + move.l %d2,(%a1)+ + getuser.l (%a0)+,%d2,fp_err_ua1,%a0 + move.l %d2,(%a1)+ + getuser.l (%a0),%d2,fp_err_ua1,%a0 + move.l %d2,(%a1) + subq.l #8,%a0 + subq.l #8,%a1 + add.l %d0,%a0 +2: add.l %d0,%a1 +3: lsl.b #1,%d1 + jcs 1b + jne 2b + jra 5f + | move register from fpu into memory +1: printf PMOVEM,"(%p>%p)",2,%a1,%a0 + move.l (%a1)+,%d2 + lsl.w #1,%d2 + lsl.l #7,%d2 + lsl.l #8,%d2 + putuser.l %d2,(%a0)+,fp_err_ua1,%a0 + move.l (%a1)+,%d2 + putuser.l %d2,(%a0)+,fp_err_ua1,%a0 + move.l (%a1),%d2 + putuser.l %d2,(%a0),fp_err_ua1,%a0 + subq.l #8,%a1 + subq.l #8,%a0 + add.l %d0,%a0 +2: add.l %d0,%a1 +4: lsl.b #1,%d1 + jcs 1b + jne 2b +5: + printf PDECODE,"\n" +#if 0 + lea (FPD_FPREG,FPDATA),%a0 + printf PMOVEM,"fp:" + printx PMOVEM,%a0@(0) + printx PMOVEM,%a0@(12) + printf PMOVEM,"\n " + printx PMOVEM,%a0@(24) + printx PMOVEM,%a0@(36) + printf PMOVEM,"\n " + printx PMOVEM,%a0@(48) + printx PMOVEM,%a0@(60) + printf PMOVEM,"\n " + printx PMOVEM,%a0@(72) + printx PMOVEM,%a0@(84) + printf PMOVEM,"\n" +#endif + jra fp_end + +| set flags for decode macros for fmovem control register +do_fmovem=1 +do_fmovem_cr=1 + +fp_fmovem_cr: + printf PDECODE,"fmovem.cr " + | get register list and count them + bfextu %d2{#19,#3},%d0 + move.l %d0,%d1 + swap %d1 + jra 2f +1: addq.w #1,%d1 +2: lsr.l #1,%d0 + jcs 1b + jne 2b + printf PDECODE,"#%08x",1,%d1 +#ifdef FPU_EMU_DEBUG + btst #13,%d2 + jeq 1f + printf PDECODE,"->" | fpu -> cpu + jra 2f +1: printf PDECODE,"<-" | fpu <- cpu +2: +#endif + + | decode address mode + fp_decode_addr_mode + + .long fpc_data, fpc_addr + .long fpc_indirect, fpc_postinc + .long fpc_predecr, fpc_disp16 + .long fpc_extmode0, fpc_extmode1 + +fpc_data: + fp_mode_data_direct + move.w %d0,%d1 + bfffo %d2{#19,#3},%d0 + sub.w #19,%d0 + lea (FPD_FPCR,FPDATA,%d0.w*4),%a1 + btst #13,%d2 + jne 1f + move.w %d1,%d0 + jsr fp_get_data_reg + move.l %d0,(%a1) + jra fpc_movem_fin +1: move.l (%a1),%d0 + jsr fp_put_data_reg + jra fpc_movem_fin + +fpc_addr: + fp_decode_addr_reg + printf PDECODE,"a%d",1,%d0 + btst #13,%d2 + jne 1f + jsr fp_get_addr_reg + move.l %a0,(FPD_FPIAR,FPDATA) + jra fpc_movem_fin +1: move.l (FPD_FPIAR,FPDATA),%a0 + jsr fp_put_addr_reg + jra fpc_movem_fin + +fpc_indirect: + fp_mode_addr_indirect + jra fpc_do_movem + +fpc_postinc: + fp_mode_addr_indirect_postinc + jra fpc_do_movem + +fpc_predecr: + fp_mode_addr_indirect_predec + jra fpc_do_movem + +fpc_disp16: + fp_mode_addr_indirect_disp16 + jra fpc_do_movem + +fpc_extmode0: + fp_mode_addr_indirect_extmode0 + jra fpc_do_movem + +fpc_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fpc_absolute_short, fpc_absolute_long + .long fpc_disp16, fpc_extmode0 + .long fpc_immediate, fp_ill + .long fp_ill, fp_ill + +fpc_absolute_short: + fp_mode_abs_short + jra fpc_do_movem + +fpc_absolute_long: + fp_mode_abs_long + jra fpc_do_movem + +fpc_immediate: + fp_get_pc %a0 + lea (%a0,%d1.w*4),%a1 + fp_put_pc %a1 + printf PDECODE,"#imm" +| jra fpc_do_movem +#if 0 + swap %d1 + lsl.l #5,%d1 + lea (FPD_FPCR,FPDATA),%a0 + jra 3f +1: move.l %d0,(%a0) +2: addq.l #4,%a0 +3: lsl.b #1,%d1 + jcs 1b + jne 2b + jra fpc_movem_fin +#endif + +fpc_do_movem: + swap %d1 | get fpu register list + lsl.l #5,%d1 + lea (FPD_FPCR,FPDATA),%a1 +1: btst #13,%d2 + jne 4f + + | move register from memory into fpu + jra 3f +1: printf PMOVEM,"(%p>%p)",2,%a0,%a1 + getuser.l (%a0)+,%d0,fp_err_ua1,%a0 + move.l %d0,(%a1) +2: addq.l #4,%a1 +3: lsl.b #1,%d1 + jcs 1b + jne 2b + jra fpc_movem_fin + + | move register from fpu into memory +1: printf PMOVEM,"(%p>%p)",2,%a1,%a0 + move.l (%a1),%d0 + putuser.l %d0,(%a0)+,fp_err_ua1,%a0 +2: addq.l #4,%a1 +4: lsl.b #1,%d1 + jcs 1b + jne 2b + +fpc_movem_fin: + and.l #0x0000fff0,(FPD_FPCR,FPDATA) + and.l #0x0ffffff8,(FPD_FPSR,FPDATA) + move.l (FPD_FPCR,FPDATA),%d0 + lsr.l #4,%d0 + moveq #3,%d1 + and.l %d0,%d1 + move.w %d1,(FPD_RND,FPDATA) + lsr.l #2,%d0 + moveq #3,%d1 + and.l %d0,%d1 + move.w %d1,(FPD_PREC,FPDATA) + printf PDECODE,"\n" +#if 0 + printf PMOVEM,"fpcr : %08x\n",1,FPDATA@(FPD_FPCR) + printf PMOVEM,"fpsr : %08x\n",1,FPDATA@(FPD_FPSR) + printf PMOVEM,"fpiar: %08x\n",1,FPDATA@(FPD_FPIAR) + clr.l %d0 + move.w (FPD_PREC,FPDATA),%d0 + printf PMOVEM,"prec : %04x\n",1,%d0 + move.w (FPD_RND,FPDATA),%d0 + printf PMOVEM,"rnd : %04x\n",1,%d0 +#endif + jra fp_end diff --git a/arch/m68k/math-emu/fp_scan.S b/arch/m68k/math-emu/fp_scan.S new file mode 100644 index 000000000000..4f404914c3b5 --- /dev/null +++ b/arch/m68k/math-emu/fp_scan.S @@ -0,0 +1,478 @@ +/* + * fp_scan.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + + .globl fp_scan, fp_datasize + + .data + +| %d2 - first two instr words +| %d1 - operand size + +/* operand formats are: + + Long = 0, i.e. fmove.l + Single, i.e. fmove.s + Extended, i.e. fmove.x + Packed-BCD, i.e. fmove.p + Word, i.e. fmove.w + Double, i.e. fmove.d +*/ + + .text + +| On entry: +| FPDATA - base of emulated FPU registers + +fp_scan: +| normal fpu instruction? (this excludes fsave/frestore) + fp_get_pc %a0 + printf PDECODE,"%08x: ",1,%a0 + getuser.b (%a0),%d0,fp_err_ua1,%a0 +#if 1 + cmp.b #0xf2,%d0 | cpid = 1 +#else + cmp.b #0xfc,%d0 | cpid = 6 +#endif + jne fp_nonstd +| first two instruction words are kept in %d2 + getuser.l (%a0)+,%d2,fp_err_ua1,%a0 + fp_put_pc %a0 +fp_decode_cond: | seperate conditional instr + fp_decode_cond_instr_type + + .long fp_decode_move, fp_fscc + .long fp_fbccw, fp_fbccl + +fp_decode_move: | seperate move instr + fp_decode_move_instr_type + + .long fp_fgen_fp, fp_ill + .long fp_fgen_ea, fp_fmove_fp2mem + .long fp_fmovem_cr, fp_fmovem_cr + .long fp_fmovem_fp, fp_fmovem_fp + +| now all arithmetic instr and a few move instr are left +fp_fgen_fp: | source is a fpu register + clr.b (FPD_FPSR+2,FPDATA) | clear the exception byte + fp_decode_sourcespec + printf PDECODE,"f.x fp%d",1,%d0 + fp_get_fp_reg + lea (FPD_TEMPFP1,FPDATA),%a1 | copy src into a temp location + move.l (%a0)+,(%a1)+ + move.l (%a0)+,(%a1)+ + move.l (%a0),(%a1) + lea (-8,%a1),%a0 + jra fp_getdest + +fp_fgen_ea: | source is + clr.b (FPD_FPSR+2,FPDATA) | clear the exception byte + | sort out fmovecr, keep data size in %d1 + fp_decode_sourcespec + cmp.w #7,%d0 + jeq fp_fmovecr + move.w %d0,%d1 | store data size twice in %d1 + swap %d1 | one can be trashed below + move.w %d0,%d1 +#ifdef FPU_EMU_DEBUG + lea 0f,%a0 + clr.l %d0 + move.b (%a0,%d1.w),%d0 + printf PDECODE,"f.%c ",1,%d0 + + .data +0: .byte 'l','s','x','p','w','d','b',0 + .previous +#endif + +/* + fp_getsource, fp_getdest + + basically, we end up with a pointer to the source operand in + %a1, and a pointer to the destination operand in %a0. both + are, of course, 96-bit extended floating point numbers. +*/ + +fp_getsource: + | decode addressing mode for source + fp_decode_addr_mode + + .long fp_data, fp_ill + .long fp_indirect, fp_postinc + .long fp_predecr, fp_disp16 + .long fp_extmode0, fp_extmode1 + + | addressing mode: data register direct +fp_data: + fp_mode_data_direct + jsr fp_get_data_reg + lea (FPD_TEMPFP1,FPDATA),%a0 + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: + .long fp_data_long, fp_data_single + .long fp_ill, fp_ill + .long fp_data_word, fp_ill + .long fp_data_byte, fp_ill + + | data types that fit in an integer data register +fp_data_byte: + extb.l %d0 + jra fp_data_long + +fp_data_word: + ext.l %d0 + +fp_data_long: + jsr fp_conv_long2ext + jra fp_getdest + +fp_data_single: + jsr fp_conv_single2ext + jra fp_getdest + + | addressing mode: address register indirect +fp_indirect: + fp_mode_addr_indirect + jra fp_fetchsource + + | addressing mode: address register indirect with postincrement +fp_postinc: + fp_mode_addr_indirect_postinc + jra fp_fetchsource + + | addressing mode: address register indirect with predecrement +fp_predecr: + fp_mode_addr_indirect_predec + jra fp_fetchsource + + | addressing mode: address register/programm counter indirect + | with 16bit displacement +fp_disp16: + fp_mode_addr_indirect_disp16 + jra fp_fetchsource + + | all other indirect addressing modes will finally end up here +fp_extmode0: + fp_mode_addr_indirect_extmode0 + jra fp_fetchsource + +| all pc relative addressing modes and immediate/absolute modes end up here +| the first ones are sent to fp_extmode0 or fp_disp16 +| and only the latter are handled here +fp_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_abs_short, fp_abs_long + .long fp_disp16, fp_extmode0 + .long fp_immediate, fp_ill + .long fp_ill, fp_ill + + | addressing mode: absolute short +fp_abs_short: + fp_mode_abs_short + jra fp_fetchsource + + | addressing mode: absolute long +fp_abs_long: + fp_mode_abs_long + jra fp_fetchsource + + | addressing mode: immediate data +fp_immediate: + printf PDECODE,"#" + fp_get_pc %a0 + move.w (fp_datasize,%d1.w*2),%d0 + addq.w #1,%d0 + and.w #-2,%d0 +#ifdef FPU_EMU_DEBUG + movem.l %d0/%d1,-(%sp) + movel %a0,%a1 + clr.l %d1 + jra 2f +1: getuser.b (%a1)+,%d1,fp_err_ua1,%a1 + printf PDECODE,"%02x",1,%d1 +2: dbra %d0,1b + movem.l (%sp)+,%d0/%d1 +#endif + lea (%a0,%d0.w),%a1 + fp_put_pc %a1 +| jra fp_fetchsource + +fp_fetchsource: + move.l %a0,%a1 + swap %d1 + lea (FPD_TEMPFP1,FPDATA),%a0 + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: .long fp_long, fp_single + .long fp_ext, fp_pack + .long fp_word, fp_double + .long fp_byte, fp_ill + +fp_long: + getuser.l (%a1),%d0,fp_err_ua1,%a1 + jsr fp_conv_long2ext + jra fp_getdest + +fp_single: + getuser.l (%a1),%d0,fp_err_ua1,%a1 + jsr fp_conv_single2ext + jra fp_getdest + +fp_ext: + getuser.l (%a1)+,%d0,fp_err_ua1,%a1 + lsr.l #8,%d0 + lsr.l #7,%d0 + lsr.w #1,%d0 + move.l %d0,(%a0)+ + getuser.l (%a1)+,%d0,fp_err_ua1,%a1 + move.l %d0,(%a0)+ + getuser.l (%a1),%d0,fp_err_ua1,%a1 + move.l %d0,(%a0) + subq.l #8,%a0 + jra fp_getdest + +fp_pack: + /* not supported yet */ + jra fp_ill + +fp_word: + getuser.w (%a1),%d0,fp_err_ua1,%a1 + ext.l %d0 + jsr fp_conv_long2ext + jra fp_getdest + +fp_double: + jsr fp_conv_double2ext + jra fp_getdest + +fp_byte: + getuser.b (%a1),%d0,fp_err_ua1,%a1 + extb.l %d0 + jsr fp_conv_long2ext +| jra fp_getdest + +fp_getdest: + move.l %a0,%a1 + bfextu %d2{#22,#3},%d0 + printf PDECODE,",fp%d\n",1,%d0 + fp_get_fp_reg + movem.l %a0/%a1,-(%sp) + pea fp_finalrounding + bfextu %d2{#25,#7},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_fmove_mem2fp, fp_fint, fp_fsinh, fp_fintrz + .long fp_fsqrt, fp_ill, fp_flognp1, fp_ill + .long fp_fetoxm1, fp_ftanh, fp_fatan, fp_ill + .long fp_fasin, fp_fatanh, fp_fsin, fp_ftan + .long fp_fetox, fp_ftwotox, fp_ftentox, fp_ill + .long fp_flogn, fp_flog10, fp_flog2, fp_ill + .long fp_fabs, fp_fcosh, fp_fneg, fp_ill + .long fp_facos, fp_fcos, fp_fgetexp, fp_fgetman + .long fp_fdiv, fp_fmod, fp_fadd, fp_fmul + .long fpa_fsgldiv, fp_frem, fp_fscale, fpa_fsglmul + .long fp_fsub, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_fsincos0, fp_fsincos1, fp_fsincos2, fp_fsincos3 + .long fp_fsincos4, fp_fsincos5, fp_fsincos6, fp_fsincos7 + .long fp_fcmp, fp_ill, fp_ftst, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_fsmove, fp_fssqrt, fp_ill, fp_ill + .long fp_fdmove, fp_fdsqrt, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_fsabs, fp_ill, fp_fsneg, fp_ill + .long fp_fdabs, fp_ill, fp_fdneg, fp_ill + .long fp_fsdiv, fp_ill, fp_fsadd, fp_fsmul + .long fp_fddiv, fp_ill, fp_fdadd, fp_fdmul + .long fp_fssub, fp_ill, fp_ill, fp_ill + .long fp_fdsub, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + + | Instructions follow + + | Move an (emulated) ROM constant +fp_fmovecr: + bfextu %d2{#27,#5},%d0 + printf PINSTR,"fp_fmovecr #%d",1,%d0 + move.l %d0,%d1 + add.l %d0,%d0 + add.l %d1,%d0 + lea (fp_constants,%d0*4),%a0 + move.l #0x801cc0ff,%d0 + addq.l #1,%d1 + lsl.l %d1,%d0 + jcc 1f + fp_set_sr FPSR_EXC_INEX2 | INEX2 exception +1: moveq #-128,%d0 | continue with fmove + and.l %d0,%d2 + jra fp_getdest + + .data + .align 4 +fp_constants: + .long 0x00004000,0xc90fdaa2,0x2168c235 | pi + .extend 0,0,0,0,0,0,0,0,0,0 + .long 0x00003ffd,0x9a209a84,0xfbcff798 | log10(2) + .long 0x00004000,0xadf85458,0xa2bb4a9a | e + .long 0x00003fff,0xb8aa3b29,0x5c17f0bc | log2(e) + .long 0x00003ffd,0xde5bd8a9,0x37287195 | log10(e) + .long 0x00000000,0x00000000,0x00000000 | 0.0 + .long 0x00003ffe,0xb17217f7,0xd1cf79ac | 1n(2) + .long 0x00004000,0x935d8ddd,0xaaa8ac17 | 1n(10) + | read this as "1.0 * 2^0" - note the high bit in the mantissa + .long 0x00003fff,0x80000000,0x00000000 | 10^0 + .long 0x00004002,0xa0000000,0x00000000 | 10^1 + .long 0x00004005,0xc8000000,0x00000000 | 10^2 + .long 0x0000400c,0x9c400000,0x00000000 | 10^4 + .long 0x00004019,0xbebc2000,0x00000000 | 10^8 + .long 0x00004034,0x8e1bc9bf,0x04000000 | 10^16 + .long 0x00004069,0x9dc5ada8,0x2b70b59e | 10^32 + .long 0x000040d3,0xc2781f49,0xffcfa6d5 | 10^64 + .long 0x000041a8,0x93ba47c9,0x80e98ce0 | 10^128 + .long 0x00004351,0xaa7eebfb,0x9df9de8e | 10^256 + .long 0x000046a3,0xe319a0ae,0xa60e91c7 | 10^512 + .long 0x00004d48,0xc9767586,0x81750c17 | 10^1024 + .long 0x00005a92,0x9e8b3b5d,0xc53d5de5 | 10^2048 + .long 0x00007525,0xc4605202,0x8a20979b | 10^4096 + .previous + +fp_fmove_mem2fp: + printf PINSTR,"fmove %p,%p\n",2,%a0,%a1 + move.l (%a1)+,(%a0)+ + move.l (%a1)+,(%a0)+ + move.l (%a1),(%a0) + subq.l #8,%a0 + rts + +fpa_fsglmul: + move.l #fp_finalrounding_single_fast,(%sp) + jra fp_fsglmul + +fpa_fsgldiv: + move.l #fp_finalrounding_single_fast,(%sp) + jra fp_fsgldiv + +.macro fp_dosingleprec instr + printf PINSTR,"single " + move.l #fp_finalrounding_single,(%sp) + jra \instr +.endm + +.macro fp_dodoubleprec instr + printf PINSTR,"double " + move.l #fp_finalrounding_double,(%sp) + jra \instr +.endm + +fp_fsmove: + fp_dosingleprec fp_fmove_mem2fp + +fp_fssqrt: + fp_dosingleprec fp_fsqrt + +fp_fdmove: + fp_dodoubleprec fp_fmove_mem2fp + +fp_fdsqrt: + fp_dodoubleprec fp_fsqrt + +fp_fsabs: + fp_dosingleprec fp_fabs + +fp_fsneg: + fp_dosingleprec fp_fneg + +fp_fdabs: + fp_dodoubleprec fp_fabs + +fp_fdneg: + fp_dodoubleprec fp_fneg + +fp_fsdiv: + fp_dosingleprec fp_fdiv + +fp_fsadd: + fp_dosingleprec fp_fadd + +fp_fsmul: + fp_dosingleprec fp_fmul + +fp_fddiv: + fp_dodoubleprec fp_fdiv + +fp_fdadd: + fp_dodoubleprec fp_fadd + +fp_fdmul: + fp_dodoubleprec fp_fmul + +fp_fssub: + fp_dosingleprec fp_fsub + +fp_fdsub: + fp_dodoubleprec fp_fsub + +fp_nonstd: + fp_get_pc %a0 + getuser.l (%a0),%d0,fp_err_ua1,%a0 + printf ,"nonstd ((%08x)=%08x)\n",2,%a0,%d0 + moveq #-1,%d0 + rts + + .data + .align 4 + + | data sizes corresponding to the operand formats +fp_datasize: + .word 4, 4, 12, 12, 2, 8, 1, 0 diff --git a/arch/m68k/math-emu/fp_trig.c b/arch/m68k/math-emu/fp_trig.c new file mode 100644 index 000000000000..6361d0784df2 --- /dev/null +++ b/arch/m68k/math-emu/fp_trig.c @@ -0,0 +1,183 @@ +/* + + fp_trig.c: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998-1999 David Huggins-Daines / Roman Zippel. + + I hereby give permission, free of charge, to copy, modify, and + redistribute this software, in source or binary form, provided that + the above copyright notice and the following disclaimer are included + in all such copies. + + THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL + OR IMPLIED. + +*/ + +#include "fp_emu.h" +#include "fp_trig.h" + +struct fp_ext * +fp_fsin(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsin\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fcos(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fcos\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftan(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftan\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fasin(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fasin\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_facos(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("facos\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fatan(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fatan\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fsinh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsinh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fcosh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fcosh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftanh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftanh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fatanh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fatanh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fsincos0(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos0\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos1(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos1\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos2(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos2\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos3(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos3\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos4(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos4\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos5(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos5\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos6(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos6\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos7(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos7\n"); + + return dest; +} diff --git a/arch/m68k/math-emu/fp_trig.h b/arch/m68k/math-emu/fp_trig.h new file mode 100644 index 000000000000..af8b247e9c98 --- /dev/null +++ b/arch/m68k/math-emu/fp_trig.h @@ -0,0 +1,32 @@ +/* + + fp_trig.h: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998 David Huggins-Daines. + + I hereby give permission, free of charge, to copy, modify, and + redistribute this software, in source or binary form, provided that + the above copyright notice and the following disclaimer are included + in all such copies. + + THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL + OR IMPLIED. + +*/ + +#ifndef FP_TRIG_H +#define FP_TRIG_H + +#include "fp_emu.h" + +/* floating point trigonometric instructions: + + the arguments to these are in the "internal" extended format, that + is, an "exploded" version of the 96-bit extended fp format used by + the 68881. + + they return a status code, which should end up in %d0, if all goes + well. */ + +#endif /* FP_TRIG__H */ diff --git a/arch/m68k/math-emu/fp_util.S b/arch/m68k/math-emu/fp_util.S new file mode 100644 index 000000000000..eeea8691d2db --- /dev/null +++ b/arch/m68k/math-emu/fp_util.S @@ -0,0 +1,1456 @@ +/* + * fp_util.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "fp_emu.h" + +/* + * Here are lots of conversion and normalization functions mainly + * used by fp_scan.S + * Note that these functions are optimized for "normal" numbers, + * these are handled first and exit as fast as possible, this is + * especially important for fp_normalize_ext/fp_conv_ext2ext, as + * it's called very often. + * The register usage is optimized for fp_scan.S and which register + * is currently at that time unused, be careful if you want change + * something here. %d0 and %d1 is always usable, sometimes %d2 (or + * only the lower half) most function have to return the %a0 + * unmodified, so that the caller can immediatly reuse it. + */ + + .globl fp_ill, fp_end + + | exits from fp_scan: + | illegal instruction +fp_ill: + printf ,"fp_illegal\n" + rts + | completed instruction +fp_end: + tst.l (TASK_MM-8,%a2) + jmi 1f + tst.l (TASK_MM-4,%a2) + jmi 1f + tst.l (TASK_MM,%a2) + jpl 2f +1: printf ,"oops:%p,%p,%p\n",3,%a2@(TASK_MM-8),%a2@(TASK_MM-4),%a2@(TASK_MM) +2: clr.l %d0 + rts + + .globl fp_conv_long2ext, fp_conv_single2ext + .globl fp_conv_double2ext, fp_conv_ext2ext + .globl fp_normalize_ext, fp_normalize_double + .globl fp_normalize_single, fp_normalize_single_fast + .globl fp_conv_ext2double, fp_conv_ext2single + .globl fp_conv_ext2long, fp_conv_ext2short + .globl fp_conv_ext2byte + .globl fp_finalrounding_single, fp_finalrounding_single_fast + .globl fp_finalrounding_double + .globl fp_finalrounding, fp_finaltest, fp_final + +/* + * First several conversion functions from a source operand + * into the extended format. Note, that only fp_conv_ext2ext + * normalizes the number and is always called after the other + * conversion functions, which only move the information into + * fp_ext structure. + */ + + | fp_conv_long2ext: + | + | args: %d0 = source (32-bit long) + | %a0 = destination (ptr to struct fp_ext) + +fp_conv_long2ext: + printf PCONV,"l2e: %p -> %p(",2,%d0,%a0 + clr.l %d1 | sign defaults to zero + tst.l %d0 + jeq fp_l2e_zero | is source zero? + jpl 1f | positive? + moveq #1,%d1 + neg.l %d0 +1: swap %d1 + move.w #0x3fff+31,%d1 + move.l %d1,(%a0)+ | set sign / exp + move.l %d0,(%a0)+ | set mantissa + clr.l (%a0) + subq.l #8,%a0 | restore %a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | source is zero +fp_l2e_zero: + clr.l (%a0)+ + clr.l (%a0)+ + clr.l (%a0) + subq.l #8,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + + | fp_conv_single2ext + | args: %d0 = source (single-precision fp value) + | %a0 = dest (struct fp_ext *) + +fp_conv_single2ext: + printf PCONV,"s2e: %p -> %p(",2,%d0,%a0 + move.l %d0,%d1 + lsl.l #8,%d0 | shift mantissa + lsr.l #8,%d1 | exponent / sign + lsr.l #7,%d1 + lsr.w #8,%d1 + jeq fp_s2e_small | zero / denormal? + cmp.w #0xff,%d1 | NaN / Inf? + jeq fp_s2e_large + bset #31,%d0 | set explizit bit + add.w #0x3fff-0x7f,%d1 | re-bias the exponent. +9: move.l %d1,(%a0)+ | fp_ext.sign, fp_ext.exp + move.l %d0,(%a0)+ | high lword of fp_ext.mant + clr.l (%a0) | low lword = 0 + subq.l #8,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | zeros and denormalized +fp_s2e_small: + | exponent is zero, so explizit bit is already zero too + tst.l %d0 + jeq 9b + move.w #0x4000-0x7f,%d1 + jra 9b + | infinities and NAN +fp_s2e_large: + bclr #31,%d0 | clear explizit bit + move.w #0x7fff,%d1 + jra 9b + +fp_conv_double2ext: +#ifdef FPU_EMU_DEBUG + getuser.l %a1@(0),%d0,fp_err_ua2,%a1 + getuser.l %a1@(4),%d1,fp_err_ua2,%a1 + printf PCONV,"d2e: %p%p -> %p(",3,%d0,%d1,%a0 +#endif + getuser.l (%a1)+,%d0,fp_err_ua2,%a1 + move.l %d0,%d1 + lsl.l #8,%d0 | shift high mantissa + lsl.l #3,%d0 + lsr.l #8,%d1 | exponent / sign + lsr.l #7,%d1 + lsr.w #5,%d1 + jeq fp_d2e_small | zero / denormal? + cmp.w #0x7ff,%d1 | NaN / Inf? + jeq fp_d2e_large + bset #31,%d0 | set explizit bit + add.w #0x3fff-0x3ff,%d1 | re-bias the exponent. +9: move.l %d1,(%a0)+ | fp_ext.sign, fp_ext.exp + move.l %d0,(%a0)+ + getuser.l (%a1)+,%d0,fp_err_ua2,%a1 + move.l %d0,%d1 + lsl.l #8,%d0 + lsl.l #3,%d0 + move.l %d0,(%a0) + moveq #21,%d0 + lsr.l %d0,%d1 + or.l %d1,-(%a0) + subq.l #4,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | zeros and denormalized +fp_d2e_small: + | exponent is zero, so explizit bit is already zero too + tst.l %d0 + jeq 9b + move.w #0x4000-0x3ff,%d1 + jra 9b + | infinities and NAN +fp_d2e_large: + bclr #31,%d0 | clear explizit bit + move.w #0x7fff,%d1 + jra 9b + + | fp_conv_ext2ext: + | originally used to get longdouble from userspace, now it's + | called before arithmetic operations to make sure the number + | is normalized [maybe rename it?]. + | args: %a0 = dest (struct fp_ext *) + | returns 0 in %d0 for a NaN, otherwise 1 + +fp_conv_ext2ext: + printf PCONV,"e2e: %p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,"), " + move.l (%a0)+,%d0 + cmp.w #0x7fff,%d0 | Inf / NaN? + jeq fp_e2e_large + move.l (%a0),%d0 + jpl fp_e2e_small | zero / denorm? + | The high bit is set, so normalization is irrelevant. +fp_e2e_checkround: + subq.l #4,%a0 +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + move.b (%a0),%d0 + jne fp_e2e_round +#endif + printf PCONV,"%p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + moveq #1,%d0 + rts +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC +fp_e2e_round: + fp_set_sr FPSR_EXC_INEX2 + clr.b (%a0) + move.w (FPD_RND,FPDATA),%d2 + jne fp_e2e_roundother | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #0,(11,%a0) | test lsb bit + jne fp_e2e_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_e2e_doroundup: + addq.l #1,(8,%a0) + jcc 9f + addq.l #1,(4,%a0) + jcc 9f + move.w #0x8000,(4,%a0) + addq.w #1,(2,%a0) +9: printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_e2e_roundother: + subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 1f | %d2 > 2, round to +infinity + tst.b (1,%a0) | to -inf + jne fp_e2e_doroundup | negative, round to infinity + jra 9b | positive, round to zero +1: tst.b (1,%a0) | to +inf + jeq fp_e2e_doroundup | positive, round to infinity + jra 9b | negative, round to zero +#endif + | zeros and subnormals: + | try to normalize these anyway. +fp_e2e_small: + jne fp_e2e_small1 | high lword zero? + move.l (4,%a0),%d0 + jne fp_e2e_small2 +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + jne fp_e2e_small3 +#endif + | Genuine zero. + clr.w -(%a0) + subq.l #2,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + moveq #1,%d0 + rts + | definitely subnormal, need to shift all 64 bits +fp_e2e_small1: + bfffo %d0{#0,#32},%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + move.w %d1,%d2 + jeq fp_e2e_checkround + | fancy 64-bit double-shift begins here + lsl.l %d2,%d0 + move.l %d0,(%a0)+ + move.l (%a0),%d0 + move.l %d0,%d1 + lsl.l %d2,%d0 + move.l %d0,(%a0) + neg.w %d2 + and.w #0x1f,%d2 + lsr.l %d2,%d1 + or.l %d1,-(%a0) +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC +fp_e2e_extra1: + clr.l %d0 + move.b (-4,%a0),%d0 + neg.w %d2 + add.w #24,%d2 + jcc 1f + clr.b (-4,%a0) + lsl.l %d2,%d0 + or.l %d0,(4,%a0) + jra fp_e2e_checkround +1: addq.w #8,%d2 + lsl.l %d2,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + or.l %d0,(4,%a0) +#endif + jra fp_e2e_checkround + | pathologically small subnormal +fp_e2e_small2: + bfffo %d0{#0,#32},%d1 + add.w #32,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Beyond pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_e2e_checkround + clr.l (4,%a0) + sub.w #32,%d2 + jcs 1f + lsl.l %d1,%d0 | lower lword needs only to be shifted + move.l %d0,(%a0) | into the higher lword +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + clr.b (-4,%a0) + neg.w %d1 + add.w #32,%d1 + bfins %d0,(%a0){%d1,#8} +#endif + jra fp_e2e_checkround +1: neg.w %d1 | lower lword is splitted between + bfins %d0,(%a0){%d1,#32} | higher and lower lword +#ifndef CONFIG_M68KFPU_EMU_EXTRAPREC + jra fp_e2e_checkround +#else + move.w %d1,%d2 + jra fp_e2e_extra1 + | These are extremely small numbers, that will mostly end up as zero + | anyway, so this is only important for correct rounding. +fp_e2e_small3: + bfffo %d0{#24,#8},%d1 + add.w #40,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_e2e_checkround + cmp.w #8,%d1 + jcs 2f +1: clr.b (-4,%a0) + sub.w #64,%d1 + jcs 1f + add.w #24,%d1 + lsl.l %d1,%d0 + move.l %d0,(%a0) + jra fp_e2e_checkround +1: neg.w %d1 + bfins %d0,(%a0){%d1,#8} + jra fp_e2e_checkround +2: lsl.l %d1,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + move.b %d0,(7,%a0) + jra fp_e2e_checkround +#endif +1: move.l %d0,%d1 | lower lword is splitted between + lsl.l %d2,%d0 | higher and lower lword + move.l %d0,(%a0) + move.l %d1,%d0 + neg.w %d2 + add.w #32,%d2 + lsr.l %d2,%d0 + move.l %d0,-(%a0) + jra fp_e2e_checkround + | Infinities and NaNs +fp_e2e_large: + move.l (%a0)+,%d0 + jne 3f +1: tst.l (%a0) + jne 4f + moveq #1,%d0 +2: subq.l #8,%a0 + printf PCONV,"%p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | we have maybe a NaN, shift off the highest bit +3: lsl.l #1,%d0 + jeq 1b + | we have a NaN, clear the return value +4: clrl %d0 + jra 2b + + +/* + * Normalization functions. Call these on the output of general + * FP operators, and before any conversion into the destination + * formats. fp_normalize_ext has always to be called first, the + * following conversion functions expect an already normalized + * number. + */ + + | fp_normalize_ext: + | normalize an extended in extended (unpacked) format, basically + | it does the same as fp_conv_ext2ext, additionally it also does + | the necessary postprocessing checks. + | args: %a0 (struct fp_ext *) + | NOTE: it does _not_ modify %a0/%a1 and the upper word of %d2 + +fp_normalize_ext: + printf PNORM,"ne: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,"), " + move.l (%a0)+,%d0 + cmp.w #0x7fff,%d0 | Inf / NaN? + jeq fp_ne_large + move.l (%a0),%d0 + jpl fp_ne_small | zero / denorm? + | The high bit is set, so normalization is irrelevant. +fp_ne_checkround: + subq.l #4,%a0 +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + move.b (%a0),%d0 + jne fp_ne_round +#endif + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC +fp_ne_round: + fp_set_sr FPSR_EXC_INEX2 + clr.b (%a0) + move.w (FPD_RND,FPDATA),%d2 + jne fp_ne_roundother | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #0,(11,%a0) | test lsb bit + jne fp_ne_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_ne_doroundup: + addq.l #1,(8,%a0) + jcc 9f + addq.l #1,(4,%a0) + jcc 9f + addq.w #1,(2,%a0) + move.w #0x8000,(4,%a0) +9: printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_ne_roundother: + subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 1f | %d2 > 2, round to +infinity + tst.b (1,%a0) | to -inf + jne fp_ne_doroundup | negative, round to infinity + jra 9b | positive, round to zero +1: tst.b (1,%a0) | to +inf + jeq fp_ne_doroundup | positive, round to infinity + jra 9b | negative, round to zero +#endif + | Zeros and subnormal numbers + | These are probably merely subnormal, rather than "denormalized" + | numbers, so we will try to make them normal again. +fp_ne_small: + jne fp_ne_small1 | high lword zero? + move.l (4,%a0),%d0 + jne fp_ne_small2 +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + jne fp_ne_small3 +#endif + | Genuine zero. + clr.w -(%a0) + subq.l #2,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | Subnormal. +fp_ne_small1: + bfffo %d0{#0,#32},%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 + fp_set_sr FPSR_EXC_UNFL +1: move.w %d2,(%a0)+ + move.w %d1,%d2 + jeq fp_ne_checkround + | This is exactly the same 64-bit double shift as seen above. + lsl.l %d2,%d0 + move.l %d0,(%a0)+ + move.l (%a0),%d0 + move.l %d0,%d1 + lsl.l %d2,%d0 + move.l %d0,(%a0) + neg.w %d2 + and.w #0x1f,%d2 + lsr.l %d2,%d1 + or.l %d1,-(%a0) +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC +fp_ne_extra1: + clr.l %d0 + move.b (-4,%a0),%d0 + neg.w %d2 + add.w #24,%d2 + jcc 1f + clr.b (-4,%a0) + lsl.l %d2,%d0 + or.l %d0,(4,%a0) + jra fp_ne_checkround +1: addq.w #8,%d2 + lsl.l %d2,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + or.l %d0,(4,%a0) +#endif + jra fp_ne_checkround + | May or may not be subnormal, if so, only 32 bits to shift. +fp_ne_small2: + bfffo %d0{#0,#32},%d1 + add.w #32,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Beyond pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 + fp_set_sr FPSR_EXC_UNFL +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_ne_checkround + clr.l (4,%a0) + sub.w #32,%d1 + jcs 1f + lsl.l %d1,%d0 | lower lword needs only to be shifted + move.l %d0,(%a0) | into the higher lword +#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + clr.b (-4,%a0) + neg.w %d1 + add.w #32,%d1 + bfins %d0,(%a0){%d1,#8} +#endif + jra fp_ne_checkround +1: neg.w %d1 | lower lword is splitted between + bfins %d0,(%a0){%d1,#32} | higher and lower lword +#ifndef CONFIG_M68KFPU_EMU_EXTRAPREC + jra fp_ne_checkround +#else + move.w %d1,%d2 + jra fp_ne_extra1 + | These are extremely small numbers, that will mostly end up as zero + | anyway, so this is only important for correct rounding. +fp_ne_small3: + bfffo %d0{#24,#8},%d1 + add.w #40,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_ne_checkround + cmp.w #8,%d1 + jcs 2f +1: clr.b (-4,%a0) + sub.w #64,%d1 + jcs 1f + add.w #24,%d1 + lsl.l %d1,%d0 + move.l %d0,(%a0) + jra fp_ne_checkround +1: neg.w %d1 + bfins %d0,(%a0){%d1,#8} + jra fp_ne_checkround +2: lsl.l %d1,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + move.b %d0,(7,%a0) + jra fp_ne_checkround +#endif + | Infinities and NaNs, again, same as above. +fp_ne_large: + move.l (%a0)+,%d0 + jne 3f +1: tst.l (%a0) + jne 4f +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | we have maybe a NaN, shift off the highest bit +3: move.l %d0,%d1 + lsl.l #1,%d1 + jne 4f + clr.l (-4,%a0) + jra 1b + | we have a NaN, test if it is signaling +4: bset #30,%d0 + jne 2b + fp_set_sr FPSR_EXC_SNAN + move.l %d0,(-4,%a0) + jra 2b + + | these next two do rounding as per the IEEE standard. + | values for the rounding modes appear to be: + | 0: Round to nearest + | 1: Round to zero + | 2: Round to -Infinity + | 3: Round to +Infinity + | both functions expect that fp_normalize was already + | called (and extended argument is already normalized + | as far as possible), these are used if there is different + | rounding precision is selected and before converting + | into single/double + + | fp_normalize_double: + | normalize an extended with double (52-bit) precision + | args: %a0 (struct fp_ext *) + +fp_normalize_double: + printf PNORM,"nd: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,"), " + move.l (%a0)+,%d2 + tst.w %d2 + jeq fp_nd_zero | zero / denormalized + cmp.w #0x7fff,%d2 + jeq fp_nd_huge | NaN / infinitive. + sub.w #0x4000-0x3ff,%d2 | will the exponent fit? + jcs fp_nd_small | too small. + cmp.w #0x7fe,%d2 + jcc fp_nd_large | too big. + addq.l #4,%a0 + move.l (%a0),%d0 | low lword of mantissa + | now, round off the low 11 bits. +fp_nd_round: + moveq #21,%d1 + lsl.l %d1,%d0 | keep 11 low bits. + jne fp_nd_checkround | Are they non-zero? + | nothing to do here +9: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | Be careful with the X bit! It contains the lsb + | from the shift above, it is needed for round to nearest. +fp_nd_checkround: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + and.w #0xf800,(2,%a0) | clear bits 0-10 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.l %d0 | test guard bit + jpl 9b | zero is closer + | here we test the X bit by adding it to %d2 + clr.w %d2 | first set z bit, addx only clears it + addx.w %d2,%d2 | test lsb bit + | IEEE754-specified "round to even" behaviour. If the guard + | bit is set, then the number is odd, so rounding works like + | in grade-school arithmetic (i.e. 1.5 rounds to 2.0) + | Otherwise, an equal distance rounds towards zero, so as not + | to produce an odd number. This is strange, but it is what + | the standard says. + jne fp_nd_doroundup | round to infinity + lsl.l #1,%d0 | check low bits + jeq 9b | round to zero +fp_nd_doroundup: + | round (the mantissa, that is) towards infinity + add.l #0x800,(%a0) + jcc 9b | no overflow, good. + addq.l #1,-(%a0) | extend to high lword + jcc 1f | no overflow, good. + | Yow! we have managed to overflow the mantissa. Since this + | only happens when %d1 was 0xfffff800, it is now zero, so + | reset the high bit, and increment the exponent. + move.w #0x8000,(%a0) + addq.w #1,-(%a0) + cmp.w #0x43ff,(%a0)+ | exponent now overflown? + jeq fp_nd_large | yes, so make it infinity. +1: subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + | Round to +Inf or -Inf. High word of %d2 contains the + | sign of the number, by the way. + swap %d2 | to -inf + tst.b %d2 + jne fp_nd_doroundup | negative, round to infinity + jra 9b | positive, round to zero +3: swap %d2 | to +inf + tst.b %d2 + jeq fp_nd_doroundup | positive, round to infinity + jra 9b | negative, round to zero + | Exponent underflow. Try to make a denormal, and set it to + | the smallest possible fraction if this fails. +fp_nd_small: + fp_set_sr FPSR_EXC_UNFL | set UNFL bit + move.w #0x3c01,(-2,%a0) | 2**-1022 + neg.w %d2 | degree of underflow + cmp.w #32,%d2 | single or double shift? + jcc 1f + | Again, another 64-bit double shift. + move.l (%a0),%d0 + move.l %d0,%d1 + lsr.l %d2,%d0 + move.l %d0,(%a0)+ + move.l (%a0),%d0 + lsr.l %d2,%d0 + neg.w %d2 + add.w #32,%d2 + lsl.l %d2,%d1 + or.l %d1,%d0 + move.l (%a0),%d1 + move.l %d0,(%a0) + | Check to see if we shifted off any significant bits + lsl.l %d2,%d1 + jeq fp_nd_round | Nope, round. + bset #0,%d0 | Yes, so set the "sticky bit". + jra fp_nd_round | Now, round. + | Another 64-bit single shift and store +1: sub.w #32,%d2 + cmp.w #32,%d2 | Do we really need to shift? + jcc 2f | No, the number is too small. + move.l (%a0),%d0 + clr.l (%a0)+ + move.l %d0,%d1 + lsr.l %d2,%d0 + neg.w %d2 + add.w #32,%d2 + | Again, check to see if we shifted off any significant bits. + tst.l (%a0) + jeq 1f + bset #0,%d0 | Sticky bit. +1: move.l %d0,(%a0) + lsl.l %d2,%d1 + jeq fp_nd_round + bset #0,%d0 + jra fp_nd_round + | Sorry, the number is just too small. +2: clr.l (%a0)+ + clr.l (%a0) + moveq #1,%d0 | Smallest possible fraction, + jra fp_nd_round | round as desired. + | zero and denormalized +fp_nd_zero: + tst.l (%a0)+ + jne 1f + tst.l (%a0) + jne 1f + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts | zero. nothing to do. + | These are not merely subnormal numbers, but true denormals, + | i.e. pathologically small (exponent is 2**-16383) numbers. + | It is clearly impossible for even a normal extended number + | with that exponent to fit into double precision, so just + | write these ones off as "too darn small". +1: fp_set_sr FPSR_EXC_UNFL | Set UNFL bit + clr.l (%a0) + clr.l -(%a0) + move.w #0x3c01,-(%a0) | i.e. 2**-1022 + addq.l #6,%a0 + moveq #1,%d0 + jra fp_nd_round | round. + | Exponent overflow. Just call it infinity. +fp_nd_large: + move.w #0x7ff,%d0 + and.w (6,%a0),%d0 + jeq 1f + fp_set_sr FPSR_EXC_INEX2 +1: fp_set_sr FPSR_EXC_OVFL + move.w (FPD_RND,FPDATA),%d2 + jne 3f | %d2 = 0 round to nearest +1: move.w #0x7fff,(-2,%a0) + clr.l (%a0)+ + clr.l (%a0) +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +3: subq.w #2,%d2 + jcs 5f | %d2 < 2, round to zero + jhi 4f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne 1b + jra 5f +4: tst.b (-3,%a0) | to +inf + jeq 1b +5: move.w #0x43fe,(-2,%a0) + moveq #-1,%d0 + move.l %d0,(%a0)+ + move.w #0xf800,%d0 + move.l %d0,(%a0) + jra 2b + | Infinities or NaNs +fp_nd_huge: + subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + + | fp_normalize_single: + | normalize an extended with single (23-bit) precision + | args: %a0 (struct fp_ext *) + +fp_normalize_single: + printf PNORM,"ns: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,") " + addq.l #2,%a0 + move.w (%a0)+,%d2 + jeq fp_ns_zero | zero / denormalized + cmp.w #0x7fff,%d2 + jeq fp_ns_huge | NaN / infinitive. + sub.w #0x4000-0x7f,%d2 | will the exponent fit? + jcs fp_ns_small | too small. + cmp.w #0xfe,%d2 + jcc fp_ns_large | too big. + move.l (%a0)+,%d0 | get high lword of mantissa +fp_ns_round: + tst.l (%a0) | check the low lword + jeq 1f + | Set a sticky bit if it is non-zero. This should only + | affect the rounding in what would otherwise be equal- + | distance situations, which is what we want it to do. + bset #0,%d0 +1: clr.l (%a0) | zap it from memory. + | now, round off the low 8 bits of the hi lword. + tst.b %d0 | 8 low bits. + jne fp_ns_checkround | Are they non-zero? + | nothing to do here + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_ns_checkround: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + clr.b -(%a0) | clear low byte of high lword + subq.l #3,%a0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #8,%d0 | test lsb bit + | round to even behaviour, see above. + jne fp_ns_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_ns_doroundup: + | round (the mantissa, that is) towards infinity + add.l #0x100,(%a0) + jcc 9f | no overflow, good. + | Overflow. This means that the %d1 was 0xffffff00, so it + | is now zero. We will set the mantissa to reflect this, and + | increment the exponent (checking for overflow there too) + move.w #0x8000,(%a0) + addq.w #1,-(%a0) + cmp.w #0x407f,(%a0)+ | exponent now overflown? + jeq fp_ns_large | yes, so make it infinity. +9: subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | check nondefault rounding modes +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne fp_ns_doroundup | negative, round to infinity + jra 9b | positive, round to zero +3: tst.b (-3,%a0) | to +inf + jeq fp_ns_doroundup | positive, round to infinity + jra 9b | negative, round to zero + | Exponent underflow. Try to make a denormal, and set it to + | the smallest possible fraction if this fails. +fp_ns_small: + fp_set_sr FPSR_EXC_UNFL | set UNFL bit + move.w #0x3f81,(-2,%a0) | 2**-126 + neg.w %d2 | degree of underflow + cmp.w #32,%d2 | single or double shift? + jcc 2f + | a 32-bit shift. + move.l (%a0),%d0 + move.l %d0,%d1 + lsr.l %d2,%d0 + move.l %d0,(%a0)+ + | Check to see if we shifted off any significant bits. + neg.w %d2 + add.w #32,%d2 + lsl.l %d2,%d1 + jeq 1f + bset #0,%d0 | Sticky bit. + | Check the lower lword +1: tst.l (%a0) + jeq fp_ns_round + clr (%a0) + bset #0,%d0 | Sticky bit. + jra fp_ns_round + | Sorry, the number is just too small. +2: clr.l (%a0)+ + clr.l (%a0) + moveq #1,%d0 | Smallest possible fraction, + jra fp_ns_round | round as desired. + | Exponent overflow. Just call it infinity. +fp_ns_large: + tst.b (3,%a0) + jeq 1f + fp_set_sr FPSR_EXC_INEX2 +1: fp_set_sr FPSR_EXC_OVFL + move.w (FPD_RND,FPDATA),%d2 + jne 3f | %d2 = 0 round to nearest +1: move.w #0x7fff,(-2,%a0) + clr.l (%a0)+ + clr.l (%a0) +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +3: subq.w #2,%d2 + jcs 5f | %d2 < 2, round to zero + jhi 4f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne 1b + jra 5f +4: tst.b (-3,%a0) | to +inf + jeq 1b +5: move.w #0x407e,(-2,%a0) + move.l #0xffffff00,(%a0)+ + clr.l (%a0) + jra 2b + | zero and denormalized +fp_ns_zero: + tst.l (%a0)+ + jne 1f + tst.l (%a0) + jne 1f + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts | zero. nothing to do. + | These are not merely subnormal numbers, but true denormals, + | i.e. pathologically small (exponent is 2**-16383) numbers. + | It is clearly impossible for even a normal extended number + | with that exponent to fit into single precision, so just + | write these ones off as "too darn small". +1: fp_set_sr FPSR_EXC_UNFL | Set UNFL bit + clr.l (%a0) + clr.l -(%a0) + move.w #0x3f81,-(%a0) | i.e. 2**-126 + addq.l #6,%a0 + moveq #1,%d0 + jra fp_ns_round | round. + | Infinities or NaNs +fp_ns_huge: + subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + + | fp_normalize_single_fast: + | normalize an extended with single (23-bit) precision + | this is only used by fsgldiv/fsgdlmul, where the + | operand is not completly normalized. + | args: %a0 (struct fp_ext *) + +fp_normalize_single_fast: + printf PNORM,"nsf: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,") " + addq.l #2,%a0 + move.w (%a0)+,%d2 + cmp.w #0x7fff,%d2 + jeq fp_nsf_huge | NaN / infinitive. + move.l (%a0)+,%d0 | get high lword of mantissa +fp_nsf_round: + tst.l (%a0) | check the low lword + jeq 1f + | Set a sticky bit if it is non-zero. This should only + | affect the rounding in what would otherwise be equal- + | distance situations, which is what we want it to do. + bset #0,%d0 +1: clr.l (%a0) | zap it from memory. + | now, round off the low 8 bits of the hi lword. + tst.b %d0 | 8 low bits. + jne fp_nsf_checkround | Are they non-zero? + | nothing to do here + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_nsf_checkround: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + clr.b -(%a0) | clear low byte of high lword + subq.l #3,%a0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #8,%d0 | test lsb bit + | round to even behaviour, see above. + jne fp_nsf_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_nsf_doroundup: + | round (the mantissa, that is) towards infinity + add.l #0x100,(%a0) + jcc 9f | no overflow, good. + | Overflow. This means that the %d1 was 0xffffff00, so it + | is now zero. We will set the mantissa to reflect this, and + | increment the exponent (checking for overflow there too) + move.w #0x8000,(%a0) + addq.w #1,-(%a0) + cmp.w #0x407f,(%a0)+ | exponent now overflown? + jeq fp_nsf_large | yes, so make it infinity. +9: subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | check nondefault rounding modes +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne fp_nsf_doroundup | negative, round to infinity + jra 9b | positive, round to zero +3: tst.b (-3,%a0) | to +inf + jeq fp_nsf_doroundup | positive, round to infinity + jra 9b | negative, round to zero + | Exponent overflow. Just call it infinity. +fp_nsf_large: + tst.b (3,%a0) + jeq 1f + fp_set_sr FPSR_EXC_INEX2 +1: fp_set_sr FPSR_EXC_OVFL + move.w (FPD_RND,FPDATA),%d2 + jne 3f | %d2 = 0 round to nearest +1: move.w #0x7fff,(-2,%a0) + clr.l (%a0)+ + clr.l (%a0) +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +3: subq.w #2,%d2 + jcs 5f | %d2 < 2, round to zero + jhi 4f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne 1b + jra 5f +4: tst.b (-3,%a0) | to +inf + jeq 1b +5: move.w #0x407e,(-2,%a0) + move.l #0xffffff00,(%a0)+ + clr.l (%a0) + jra 2b + | Infinities or NaNs +fp_nsf_huge: + subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + + | conv_ext2int (macro): + | Generates a subroutine that converts an extended value to an + | integer of a given size, again, with the appropriate type of + | rounding. + + | Macro arguments: + | s: size, as given in an assembly instruction. + | b: number of bits in that size. + + | Subroutine arguments: + | %a0: source (struct fp_ext *) + + | Returns the integer in %d0 (like it should) + +.macro conv_ext2int s,b + .set inf,(1<<(\b-1))-1 | i.e. MAXINT + printf PCONV,"e2i%d: %p(",2,#\b,%a0 + printx PCONV,%a0@ + printf PCONV,") " + addq.l #2,%a0 + move.w (%a0)+,%d2 | exponent + jeq fp_e2i_zero\b | zero / denorm (== 0, here) + cmp.w #0x7fff,%d2 + jeq fp_e2i_huge\b | Inf / NaN + sub.w #0x3ffe,%d2 + jcs fp_e2i_small\b + cmp.w #\b,%d2 + jhi fp_e2i_large\b + move.l (%a0),%d0 + move.l %d0,%d1 + lsl.l %d2,%d1 + jne fp_e2i_round\b + tst.l (4,%a0) + jne fp_e2i_round\b + neg.w %d2 + add.w #32,%d2 + lsr.l %d2,%d0 +9: tst.w (-4,%a0) + jne 1f + tst.\s %d0 + jmi fp_e2i_large\b + printf PCONV,"-> %p\n",1,%d0 + rts +1: neg.\s %d0 + jeq 1f + jpl fp_e2i_large\b +1: printf PCONV,"-> %p\n",1,%d0 + rts +fp_e2i_round\b: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + neg.w %d2 + add.w #32,%d2 + .if \b>16 + jeq 5f + .endif + lsr.l %d2,%d0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.l %d1 | test guard bit + jpl 9b | zero is closer + btst %d2,%d0 | test lsb bit (%d2 still 0) + jne fp_e2i_doroundup\b + lsl.l #1,%d1 | check low bits + jne fp_e2i_doroundup\b + tst.l (4,%a0) + jeq 9b +fp_e2i_doroundup\b: + addq.l #1,%d0 + jra 9b + | check nondefault rounding modes +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + tst.w (-4,%a0) | to -inf + jne fp_e2i_doroundup\b | negative, round to infinity + jra 9b | positive, round to zero +3: tst.w (-4,%a0) | to +inf + jeq fp_e2i_doroundup\b | positive, round to infinity + jra 9b | negative, round to zero + | we are only want -2**127 get correctly rounded here, + | since the guard bit is in the lower lword. + | everything else ends up anyway as overflow. + .if \b>16 +5: move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2b | %d2 == 0, round to nearest + move.l (4,%a0),%d1 | test guard bit + jpl 9b | zero is closer + lsl.l #1,%d1 | check low bits + jne fp_e2i_doroundup\b + jra 9b + .endif +fp_e2i_zero\b: + clr.l %d0 + tst.l (%a0)+ + jne 1f + tst.l (%a0) + jeq 3f +1: subq.l #4,%a0 + fp_clr_sr FPSR_EXC_UNFL | fp_normalize_ext has set this bit +fp_e2i_small\b: + fp_set_sr FPSR_EXC_INEX2 + clr.l %d0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + subq.w #2,%d2 + jcs 3f | %d2 < 2, round to nearest/zero + jhi 2f | %d2 > 2, round to +infinity + tst.w (-4,%a0) | to -inf + jeq 3f + subq.\s #1,%d0 + jra 3f +2: tst.w (-4,%a0) | to +inf + jne 3f + addq.\s #1,%d0 +3: printf PCONV,"-> %p\n",1,%d0 + rts +fp_e2i_large\b: + fp_set_sr FPSR_EXC_OPERR + move.\s #inf,%d0 + tst.w (-4,%a0) + jeq 1f + addq.\s #1,%d0 +1: printf PCONV,"-> %p\n",1,%d0 + rts +fp_e2i_huge\b: + move.\s (%a0),%d0 + tst.l (%a0) + jne 1f + tst.l (%a0) + jeq fp_e2i_large\b + | fp_normalize_ext has set this bit already + | and made the number nonsignaling +1: fp_tst_sr FPSR_EXC_SNAN + jne 1f + fp_set_sr FPSR_EXC_OPERR +1: printf PCONV,"-> %p\n",1,%d0 + rts +.endm + +fp_conv_ext2long: + conv_ext2int l,32 + +fp_conv_ext2short: + conv_ext2int w,16 + +fp_conv_ext2byte: + conv_ext2int b,8 + +fp_conv_ext2double: + jsr fp_normalize_double + printf PCONV,"e2d: %p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,"), " + move.l (%a0)+,%d2 + cmp.w #0x7fff,%d2 + jne 1f + move.w #0x7ff,%d2 + move.l (%a0)+,%d0 + jra 2f +1: sub.w #0x3fff-0x3ff,%d2 + move.l (%a0)+,%d0 + jmi 2f + clr.w %d2 +2: lsl.w #5,%d2 + lsl.l #7,%d2 + lsl.l #8,%d2 + move.l %d0,%d1 + lsl.l #1,%d0 + lsr.l #4,%d0 + lsr.l #8,%d0 + or.l %d2,%d0 + putuser.l %d0,(%a1)+,fp_err_ua2,%a1 + moveq #21,%d0 + lsl.l %d0,%d1 + move.l (%a0),%d0 + lsr.l #4,%d0 + lsr.l #7,%d0 + or.l %d1,%d0 + putuser.l %d0,(%a1),fp_err_ua2,%a1 +#ifdef FPU_EMU_DEBUG + getuser.l %a1@(-4),%d0,fp_err_ua2,%a1 + getuser.l %a1@(0),%d1,fp_err_ua2,%a1 + printf PCONV,"%p(%08x%08x)\n",3,%a1,%d0,%d1 +#endif + rts + +fp_conv_ext2single: + jsr fp_normalize_single + printf PCONV,"e2s: %p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,"), " + move.l (%a0)+,%d1 + cmp.w #0x7fff,%d1 + jne 1f + move.w #0xff,%d1 + move.l (%a0)+,%d0 + jra 2f +1: sub.w #0x3fff-0x7f,%d1 + move.l (%a0)+,%d0 + jmi 2f + clr.w %d1 +2: lsl.w #8,%d1 + lsl.l #7,%d1 + lsl.l #8,%d1 + bclr #31,%d0 + lsr.l #8,%d0 + or.l %d1,%d0 + printf PCONV,"%08x\n",1,%d0 + rts + + | special return addresses for instr that + | encode the rounding precision in the opcode + | (e.g. fsmove,fdmove) + +fp_finalrounding_single: + addq.l #8,%sp + jsr fp_normalize_ext + jsr fp_normalize_single + jra fp_finaltest + +fp_finalrounding_single_fast: + addq.l #8,%sp + jsr fp_normalize_ext + jsr fp_normalize_single_fast + jra fp_finaltest + +fp_finalrounding_double: + addq.l #8,%sp + jsr fp_normalize_ext + jsr fp_normalize_double + jra fp_finaltest + + | fp_finaltest: + | set the emulated status register based on the outcome of an + | emulated instruction. + +fp_finalrounding: + addq.l #8,%sp +| printf ,"f: %p\n",1,%a0 + jsr fp_normalize_ext + move.w (FPD_PREC,FPDATA),%d0 + subq.w #1,%d0 + jcs fp_finaltest + jne 1f + jsr fp_normalize_single + jra 2f +1: jsr fp_normalize_double +2:| printf ,"f: %p\n",1,%a0 +fp_finaltest: + | First, we do some of the obvious tests for the exception + | status byte and condition code bytes of fp_sr here, so that + | they do not have to be handled individually by every + | emulated instruction. + clr.l %d0 + addq.l #1,%a0 + tst.b (%a0)+ | sign + jeq 1f + bset #FPSR_CC_NEG-24,%d0 | N bit +1: cmp.w #0x7fff,(%a0)+ | exponent + jeq 2f + | test for zero + moveq #FPSR_CC_Z-24,%d1 + tst.l (%a0)+ + jne 9f + tst.l (%a0) + jne 9f + jra 8f + | infinitiv and NAN +2: moveq #FPSR_CC_NAN-24,%d1 + move.l (%a0)+,%d2 + lsl.l #1,%d2 | ignore high bit + jne 8f + tst.l (%a0) + jne 8f + moveq #FPSR_CC_INF-24,%d1 +8: bset %d1,%d0 +9: move.b %d0,(FPD_FPSR+0,FPDATA) | set condition test result + | move instructions enter here + | Here, we test things in the exception status byte, and set + | other things in the accrued exception byte accordingly. + | Emulated instructions can set various things in the former, + | as defined in fp_emu.h. +fp_final: + move.l (FPD_FPSR,FPDATA),%d0 +#if 0 + btst #FPSR_EXC_SNAN,%d0 | EXC_SNAN + jne 1f + btst #FPSR_EXC_OPERR,%d0 | EXC_OPERR + jeq 2f +1: bset #FPSR_AEXC_IOP,%d0 | set IOP bit +2: btst #FPSR_EXC_OVFL,%d0 | EXC_OVFL + jeq 1f + bset #FPSR_AEXC_OVFL,%d0 | set OVFL bit +1: btst #FPSR_EXC_UNFL,%d0 | EXC_UNFL + jeq 1f + btst #FPSR_EXC_INEX2,%d0 | EXC_INEX2 + jeq 1f + bset #FPSR_AEXC_UNFL,%d0 | set UNFL bit +1: btst #FPSR_EXC_DZ,%d0 | EXC_INEX1 + jeq 1f + bset #FPSR_AEXC_DZ,%d0 | set DZ bit +1: btst #FPSR_EXC_OVFL,%d0 | EXC_OVFL + jne 1f + btst #FPSR_EXC_INEX2,%d0 | EXC_INEX2 + jne 1f + btst #FPSR_EXC_INEX1,%d0 | EXC_INEX1 + jeq 2f +1: bset #FPSR_AEXC_INEX,%d0 | set INEX bit +2: move.l %d0,(FPD_FPSR,FPDATA) +#else + | same as above, greatly optimized, but untested (yet) + move.l %d0,%d2 + lsr.l #5,%d0 + move.l %d0,%d1 + lsr.l #4,%d1 + or.l %d0,%d1 + and.b #0x08,%d1 + move.l %d2,%d0 + lsr.l #6,%d0 + or.l %d1,%d0 + move.l %d2,%d1 + lsr.l #4,%d1 + or.b #0xdf,%d1 + and.b %d1,%d0 + move.l %d2,%d1 + lsr.l #7,%d1 + and.b #0x80,%d1 + or.b %d1,%d0 + and.b #0xf8,%d0 + or.b %d0,%d2 + move.l %d2,(FPD_FPSR,FPDATA) +#endif + move.b (FPD_FPSR+2,FPDATA),%d0 + and.b (FPD_FPCR+2,FPDATA),%d0 + jeq 1f + printf ,"send signal!!!\n" +1: jra fp_end diff --git a/arch/m68k/math-emu/multi_arith.h b/arch/m68k/math-emu/multi_arith.h new file mode 100644 index 000000000000..2b9b9e2e1b27 --- /dev/null +++ b/arch/m68k/math-emu/multi_arith.h @@ -0,0 +1,822 @@ +/* multi_arith.h: multi-precision integer arithmetic functions, needed + to do extended-precision floating point. + + (c) 1998 David Huggins-Daines. + + Somewhat based on arch/alpha/math-emu/ieee-math.c, which is (c) + David Mosberger-Tang. + + You may copy, modify, and redistribute this file under the terms of + the GNU General Public License, version 2, or any later version, at + your convenience. */ + +/* Note: + + These are not general multi-precision math routines. Rather, they + implement the subset of integer arithmetic that we need in order to + multiply, divide, and normalize 128-bit unsigned mantissae. */ + +#ifndef MULTI_ARITH_H +#define MULTI_ARITH_H + +#if 0 /* old code... */ + +/* Unsigned only, because we don't need signs to multiply and divide. */ +typedef unsigned int int128[4]; + +/* Word order */ +enum { + MSW128, + NMSW128, + NLSW128, + LSW128 +}; + +/* big-endian */ +#define LO_WORD(ll) (((unsigned int *) &ll)[1]) +#define HI_WORD(ll) (((unsigned int *) &ll)[0]) + +/* Convenience functions to stuff various integer values into int128s */ + +extern inline void zero128(int128 a) +{ + a[LSW128] = a[NLSW128] = a[NMSW128] = a[MSW128] = 0; +} + +/* Human-readable word order in the arguments */ +extern inline void set128(unsigned int i3, + unsigned int i2, + unsigned int i1, + unsigned int i0, + int128 a) +{ + a[LSW128] = i0; + a[NLSW128] = i1; + a[NMSW128] = i2; + a[MSW128] = i3; +} + +/* Convenience functions (for testing as well) */ +extern inline void int64_to_128(unsigned long long src, + int128 dest) +{ + dest[LSW128] = (unsigned int) src; + dest[NLSW128] = src >> 32; + dest[NMSW128] = dest[MSW128] = 0; +} + +extern inline void int128_to_64(const int128 src, + unsigned long long *dest) +{ + *dest = src[LSW128] | (long long) src[NLSW128] << 32; +} + +extern inline void put_i128(const int128 a) +{ + printk("%08x %08x %08x %08x\n", a[MSW128], a[NMSW128], + a[NLSW128], a[LSW128]); +} + +/* Internal shifters: + + Note that these are only good for 0 < count < 32. + */ + +extern inline void _lsl128(unsigned int count, int128 a) +{ + a[MSW128] = (a[MSW128] << count) | (a[NMSW128] >> (32 - count)); + a[NMSW128] = (a[NMSW128] << count) | (a[NLSW128] >> (32 - count)); + a[NLSW128] = (a[NLSW128] << count) | (a[LSW128] >> (32 - count)); + a[LSW128] <<= count; +} + +extern inline void _lsr128(unsigned int count, int128 a) +{ + a[LSW128] = (a[LSW128] >> count) | (a[NLSW128] << (32 - count)); + a[NLSW128] = (a[NLSW128] >> count) | (a[NMSW128] << (32 - count)); + a[NMSW128] = (a[NMSW128] >> count) | (a[MSW128] << (32 - count)); + a[MSW128] >>= count; +} + +/* Should be faster, one would hope */ + +extern inline void lslone128(int128 a) +{ + asm volatile ("lsl.l #1,%0\n" + "roxl.l #1,%1\n" + "roxl.l #1,%2\n" + "roxl.l #1,%3\n" + : + "=d" (a[LSW128]), + "=d"(a[NLSW128]), + "=d"(a[NMSW128]), + "=d"(a[MSW128]) + : + "0"(a[LSW128]), + "1"(a[NLSW128]), + "2"(a[NMSW128]), + "3"(a[MSW128])); +} + +extern inline void lsrone128(int128 a) +{ + asm volatile ("lsr.l #1,%0\n" + "roxr.l #1,%1\n" + "roxr.l #1,%2\n" + "roxr.l #1,%3\n" + : + "=d" (a[MSW128]), + "=d"(a[NMSW128]), + "=d"(a[NLSW128]), + "=d"(a[LSW128]) + : + "0"(a[MSW128]), + "1"(a[NMSW128]), + "2"(a[NLSW128]), + "3"(a[LSW128])); +} + +/* Generalized 128-bit shifters: + + These bit-shift to a multiple of 32, then move whole longwords. */ + +extern inline void lsl128(unsigned int count, int128 a) +{ + int wordcount, i; + + if (count % 32) + _lsl128(count % 32, a); + + if (0 == (wordcount = count / 32)) + return; + + /* argh, gak, endian-sensitive */ + for (i = 0; i < 4 - wordcount; i++) { + a[i] = a[i + wordcount]; + } + for (i = 3; i >= 4 - wordcount; --i) { + a[i] = 0; + } +} + +extern inline void lsr128(unsigned int count, int128 a) +{ + int wordcount, i; + + if (count % 32) + _lsr128(count % 32, a); + + if (0 == (wordcount = count / 32)) + return; + + for (i = 3; i >= wordcount; --i) { + a[i] = a[i - wordcount]; + } + for (i = 0; i < wordcount; i++) { + a[i] = 0; + } +} + +extern inline int orl128(int a, int128 b) +{ + b[LSW128] |= a; +} + +extern inline int btsthi128(const int128 a) +{ + return a[MSW128] & 0x80000000; +} + +/* test bits (numbered from 0 = LSB) up to and including "top" */ +extern inline int bftestlo128(int top, const int128 a) +{ + int r = 0; + + if (top > 31) + r |= a[LSW128]; + if (top > 63) + r |= a[NLSW128]; + if (top > 95) + r |= a[NMSW128]; + + r |= a[3 - (top / 32)] & ((1 << (top % 32 + 1)) - 1); + + return (r != 0); +} + +/* Aargh. We need these because GCC is broken */ +/* FIXME: do them in assembly, for goodness' sake! */ +extern inline void mask64(int pos, unsigned long long *mask) +{ + *mask = 0; + + if (pos < 32) { + LO_WORD(*mask) = (1 << pos) - 1; + return; + } + LO_WORD(*mask) = -1; + HI_WORD(*mask) = (1 << (pos - 32)) - 1; +} + +extern inline void bset64(int pos, unsigned long long *dest) +{ + /* This conditional will be optimized away. Thanks, GCC! */ + if (pos < 32) + asm volatile ("bset %1,%0":"=m" + (LO_WORD(*dest)):"id"(pos)); + else + asm volatile ("bset %1,%0":"=m" + (HI_WORD(*dest)):"id"(pos - 32)); +} + +extern inline int btst64(int pos, unsigned long long dest) +{ + if (pos < 32) + return (0 != (LO_WORD(dest) & (1 << pos))); + else + return (0 != (HI_WORD(dest) & (1 << (pos - 32)))); +} + +extern inline void lsl64(int count, unsigned long long *dest) +{ + if (count < 32) { + HI_WORD(*dest) = (HI_WORD(*dest) << count) + | (LO_WORD(*dest) >> count); + LO_WORD(*dest) <<= count; + return; + } + count -= 32; + HI_WORD(*dest) = LO_WORD(*dest) << count; + LO_WORD(*dest) = 0; +} + +extern inline void lsr64(int count, unsigned long long *dest) +{ + if (count < 32) { + LO_WORD(*dest) = (LO_WORD(*dest) >> count) + | (HI_WORD(*dest) << (32 - count)); + HI_WORD(*dest) >>= count; + return; + } + count -= 32; + LO_WORD(*dest) = HI_WORD(*dest) >> count; + HI_WORD(*dest) = 0; +} +#endif + +extern inline void fp_denormalize(struct fp_ext *reg, unsigned int cnt) +{ + reg->exp += cnt; + + switch (cnt) { + case 0 ... 8: + reg->lowmant = reg->mant.m32[1] << (8 - cnt); + reg->mant.m32[1] = (reg->mant.m32[1] >> cnt) | + (reg->mant.m32[0] << (32 - cnt)); + reg->mant.m32[0] = reg->mant.m32[0] >> cnt; + break; + case 9 ... 32: + reg->lowmant = reg->mant.m32[1] >> (cnt - 8); + if (reg->mant.m32[1] << (40 - cnt)) + reg->lowmant |= 1; + reg->mant.m32[1] = (reg->mant.m32[1] >> cnt) | + (reg->mant.m32[0] << (32 - cnt)); + reg->mant.m32[0] = reg->mant.m32[0] >> cnt; + break; + case 33 ... 39: + asm volatile ("bfextu %1{%2,#8},%0" : "=d" (reg->lowmant) + : "m" (reg->mant.m32[0]), "d" (64 - cnt)); + if (reg->mant.m32[1] << (40 - cnt)) + reg->lowmant |= 1; + reg->mant.m32[1] = reg->mant.m32[0] >> (cnt - 32); + reg->mant.m32[0] = 0; + break; + case 40 ... 71: + reg->lowmant = reg->mant.m32[0] >> (cnt - 40); + if ((reg->mant.m32[0] << (72 - cnt)) || reg->mant.m32[1]) + reg->lowmant |= 1; + reg->mant.m32[1] = reg->mant.m32[0] >> (cnt - 32); + reg->mant.m32[0] = 0; + break; + default: + reg->lowmant = reg->mant.m32[0] || reg->mant.m32[1]; + reg->mant.m32[0] = 0; + reg->mant.m32[1] = 0; + break; + } +} + +extern inline int fp_overnormalize(struct fp_ext *reg) +{ + int shift; + + if (reg->mant.m32[0]) { + asm ("bfffo %1{#0,#32},%0" : "=d" (shift) : "dm" (reg->mant.m32[0])); + reg->mant.m32[0] = (reg->mant.m32[0] << shift) | (reg->mant.m32[1] >> (32 - shift)); + reg->mant.m32[1] = (reg->mant.m32[1] << shift); + } else { + asm ("bfffo %1{#0,#32},%0" : "=d" (shift) : "dm" (reg->mant.m32[1])); + reg->mant.m32[0] = (reg->mant.m32[1] << shift); + reg->mant.m32[1] = 0; + shift += 32; + } + + return shift; +} + +extern inline int fp_addmant(struct fp_ext *dest, struct fp_ext *src) +{ + int carry; + + /* we assume here, gcc only insert move and a clr instr */ + asm volatile ("add.b %1,%0" : "=d,=g" (dest->lowmant) + : "g,d" (src->lowmant), "0,0" (dest->lowmant)); + asm volatile ("addx.l %1,%0" : "=d" (dest->mant.m32[1]) + : "d" (src->mant.m32[1]), "0" (dest->mant.m32[1])); + asm volatile ("addx.l %1,%0" : "=d" (dest->mant.m32[0]) + : "d" (src->mant.m32[0]), "0" (dest->mant.m32[0])); + asm volatile ("addx.l %0,%0" : "=d" (carry) : "0" (0)); + + return carry; +} + +extern inline int fp_addcarry(struct fp_ext *reg) +{ + if (++reg->exp == 0x7fff) { + if (reg->mant.m64) + fp_set_sr(FPSR_EXC_INEX2); + reg->mant.m64 = 0; + fp_set_sr(FPSR_EXC_OVFL); + return 0; + } + reg->lowmant = (reg->mant.m32[1] << 7) | (reg->lowmant ? 1 : 0); + reg->mant.m32[1] = (reg->mant.m32[1] >> 1) | + (reg->mant.m32[0] << 31); + reg->mant.m32[0] = (reg->mant.m32[0] >> 1) | 0x80000000; + + return 1; +} + +extern inline void fp_submant(struct fp_ext *dest, struct fp_ext *src1, struct fp_ext *src2) +{ + /* we assume here, gcc only insert move and a clr instr */ + asm volatile ("sub.b %1,%0" : "=d,=g" (dest->lowmant) + : "g,d" (src2->lowmant), "0,0" (src1->lowmant)); + asm volatile ("subx.l %1,%0" : "=d" (dest->mant.m32[1]) + : "d" (src2->mant.m32[1]), "0" (src1->mant.m32[1])); + asm volatile ("subx.l %1,%0" : "=d" (dest->mant.m32[0]) + : "d" (src2->mant.m32[0]), "0" (src1->mant.m32[0])); +} + +#define fp_mul64(desth, destl, src1, src2) ({ \ + asm ("mulu.l %2,%1:%0" : "=d" (destl), "=d" (desth) \ + : "g" (src1), "0" (src2)); \ +}) +#define fp_div64(quot, rem, srch, srcl, div) \ + asm ("divu.l %2,%1:%0" : "=d" (quot), "=d" (rem) \ + : "dm" (div), "1" (srch), "0" (srcl)) +#define fp_add64(dest1, dest2, src1, src2) ({ \ + asm ("add.l %1,%0" : "=d,=dm" (dest2) \ + : "dm,d" (src2), "0,0" (dest2)); \ + asm ("addx.l %1,%0" : "=d" (dest1) \ + : "d" (src1), "0" (dest1)); \ +}) +#define fp_addx96(dest, src) ({ \ + /* we assume here, gcc only insert move and a clr instr */ \ + asm volatile ("add.l %1,%0" : "=d,=g" (dest->m32[2]) \ + : "g,d" (temp.m32[1]), "0,0" (dest->m32[2])); \ + asm volatile ("addx.l %1,%0" : "=d" (dest->m32[1]) \ + : "d" (temp.m32[0]), "0" (dest->m32[1])); \ + asm volatile ("addx.l %1,%0" : "=d" (dest->m32[0]) \ + : "d" (0), "0" (dest->m32[0])); \ +}) +#define fp_sub64(dest, src) ({ \ + asm ("sub.l %1,%0" : "=d,=dm" (dest.m32[1]) \ + : "dm,d" (src.m32[1]), "0,0" (dest.m32[1])); \ + asm ("subx.l %1,%0" : "=d" (dest.m32[0]) \ + : "d" (src.m32[0]), "0" (dest.m32[0])); \ +}) +#define fp_sub96c(dest, srch, srcm, srcl) ({ \ + char carry; \ + asm ("sub.l %1,%0" : "=d,=dm" (dest.m32[2]) \ + : "dm,d" (srcl), "0,0" (dest.m32[2])); \ + asm ("subx.l %1,%0" : "=d" (dest.m32[1]) \ + : "d" (srcm), "0" (dest.m32[1])); \ + asm ("subx.l %2,%1; scs %0" : "=d" (carry), "=d" (dest.m32[0]) \ + : "d" (srch), "1" (dest.m32[0])); \ + carry; \ +}) + +extern inline void fp_multiplymant(union fp_mant128 *dest, struct fp_ext *src1, struct fp_ext *src2) +{ + union fp_mant64 temp; + + fp_mul64(dest->m32[0], dest->m32[1], src1->mant.m32[0], src2->mant.m32[0]); + fp_mul64(dest->m32[2], dest->m32[3], src1->mant.m32[1], src2->mant.m32[1]); + + fp_mul64(temp.m32[0], temp.m32[1], src1->mant.m32[0], src2->mant.m32[1]); + fp_addx96(dest, temp); + + fp_mul64(temp.m32[0], temp.m32[1], src1->mant.m32[1], src2->mant.m32[0]); + fp_addx96(dest, temp); +} + +extern inline void fp_dividemant(union fp_mant128 *dest, struct fp_ext *src, struct fp_ext *div) +{ + union fp_mant128 tmp; + union fp_mant64 tmp64; + unsigned long *mantp = dest->m32; + unsigned long fix, rem, first, dummy; + int i; + + /* the algorithm below requires dest to be smaller than div, + but both have the high bit set */ + if (src->mant.m64 >= div->mant.m64) { + fp_sub64(src->mant, div->mant); + *mantp = 1; + } else + *mantp = 0; + mantp++; + + /* basic idea behind this algorithm: we can't divide two 64bit numbers + (AB/CD) directly, but we can calculate AB/C0, but this means this + quotient is off by C0/CD, so we have to multiply the first result + to fix the result, after that we have nearly the correct result + and only a few corrections are needed. */ + + /* C0/CD can be precalculated, but it's an 64bit division again, but + we can make it a bit easier, by dividing first through C so we get + 10/1D and now only a single shift and the value fits into 32bit. */ + fix = 0x80000000; + dummy = div->mant.m32[1] / div->mant.m32[0] + 1; + dummy = (dummy >> 1) | fix; + fp_div64(fix, dummy, fix, 0, dummy); + fix--; + + for (i = 0; i < 3; i++, mantp++) { + if (src->mant.m32[0] == div->mant.m32[0]) { + fp_div64(first, rem, 0, src->mant.m32[1], div->mant.m32[0]); + + fp_mul64(*mantp, dummy, first, fix); + *mantp += fix; + } else { + fp_div64(first, rem, src->mant.m32[0], src->mant.m32[1], div->mant.m32[0]); + + fp_mul64(*mantp, dummy, first, fix); + } + + fp_mul64(tmp.m32[0], tmp.m32[1], div->mant.m32[0], first - *mantp); + fp_add64(tmp.m32[0], tmp.m32[1], 0, rem); + tmp.m32[2] = 0; + + fp_mul64(tmp64.m32[0], tmp64.m32[1], *mantp, div->mant.m32[1]); + fp_sub96c(tmp, 0, tmp64.m32[0], tmp64.m32[1]); + + src->mant.m32[0] = tmp.m32[1]; + src->mant.m32[1] = tmp.m32[2]; + + while (!fp_sub96c(tmp, 0, div->mant.m32[0], div->mant.m32[1])) { + src->mant.m32[0] = tmp.m32[1]; + src->mant.m32[1] = tmp.m32[2]; + *mantp += 1; + } + } +} + +#if 0 +extern inline unsigned int fp_fls128(union fp_mant128 *src) +{ + unsigned long data; + unsigned int res, off; + + if ((data = src->m32[0])) + off = 0; + else if ((data = src->m32[1])) + off = 32; + else if ((data = src->m32[2])) + off = 64; + else if ((data = src->m32[3])) + off = 96; + else + return 128; + + asm ("bfffo %1{#0,#32},%0" : "=d" (res) : "dm" (data)); + return res + off; +} + +extern inline void fp_shiftmant128(union fp_mant128 *src, int shift) +{ + unsigned long sticky; + + switch (shift) { + case 0: + return; + case 1: + asm volatile ("lsl.l #1,%0" + : "=d" (src->m32[3]) : "0" (src->m32[3])); + asm volatile ("roxl.l #1,%0" + : "=d" (src->m32[2]) : "0" (src->m32[2])); + asm volatile ("roxl.l #1,%0" + : "=d" (src->m32[1]) : "0" (src->m32[1])); + asm volatile ("roxl.l #1,%0" + : "=d" (src->m32[0]) : "0" (src->m32[0])); + return; + case 2 ... 31: + src->m32[0] = (src->m32[0] << shift) | (src->m32[1] >> (32 - shift)); + src->m32[1] = (src->m32[1] << shift) | (src->m32[2] >> (32 - shift)); + src->m32[2] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift)); + src->m32[3] = (src->m32[3] << shift); + return; + case 32 ... 63: + shift -= 32; + src->m32[0] = (src->m32[1] << shift) | (src->m32[2] >> (32 - shift)); + src->m32[1] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift)); + src->m32[2] = (src->m32[3] << shift); + src->m32[3] = 0; + return; + case 64 ... 95: + shift -= 64; + src->m32[0] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift)); + src->m32[1] = (src->m32[3] << shift); + src->m32[2] = src->m32[3] = 0; + return; + case 96 ... 127: + shift -= 96; + src->m32[0] = (src->m32[3] << shift); + src->m32[1] = src->m32[2] = src->m32[3] = 0; + return; + case -31 ... -1: + shift = -shift; + sticky = 0; + if (src->m32[3] << (32 - shift)) + sticky = 1; + src->m32[3] = (src->m32[3] >> shift) | (src->m32[2] << (32 - shift)) | sticky; + src->m32[2] = (src->m32[2] >> shift) | (src->m32[1] << (32 - shift)); + src->m32[1] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)); + src->m32[0] = (src->m32[0] >> shift); + return; + case -63 ... -32: + shift = -shift - 32; + sticky = 0; + if ((src->m32[2] << (32 - shift)) || src->m32[3]) + sticky = 1; + src->m32[3] = (src->m32[2] >> shift) | (src->m32[1] << (32 - shift)) | sticky; + src->m32[2] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)); + src->m32[1] = (src->m32[0] >> shift); + src->m32[0] = 0; + return; + case -95 ... -64: + shift = -shift - 64; + sticky = 0; + if ((src->m32[1] << (32 - shift)) || src->m32[2] || src->m32[3]) + sticky = 1; + src->m32[3] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)) | sticky; + src->m32[2] = (src->m32[0] >> shift); + src->m32[1] = src->m32[0] = 0; + return; + case -127 ... -96: + shift = -shift - 96; + sticky = 0; + if ((src->m32[0] << (32 - shift)) || src->m32[1] || src->m32[2] || src->m32[3]) + sticky = 1; + src->m32[3] = (src->m32[0] >> shift) | sticky; + src->m32[2] = src->m32[1] = src->m32[0] = 0; + return; + } + + if (shift < 0 && (src->m32[0] || src->m32[1] || src->m32[2] || src->m32[3])) + src->m32[3] = 1; + else + src->m32[3] = 0; + src->m32[2] = 0; + src->m32[1] = 0; + src->m32[0] = 0; +} +#endif + +extern inline void fp_putmant128(struct fp_ext *dest, union fp_mant128 *src, int shift) +{ + unsigned long tmp; + + switch (shift) { + case 0: + dest->mant.m64 = src->m64[0]; + dest->lowmant = src->m32[2] >> 24; + if (src->m32[3] || (src->m32[2] << 8)) + dest->lowmant |= 1; + break; + case 1: + asm volatile ("lsl.l #1,%0" + : "=d" (tmp) : "0" (src->m32[2])); + asm volatile ("roxl.l #1,%0" + : "=d" (dest->mant.m32[1]) : "0" (src->m32[1])); + asm volatile ("roxl.l #1,%0" + : "=d" (dest->mant.m32[0]) : "0" (src->m32[0])); + dest->lowmant = tmp >> 24; + if (src->m32[3] || (tmp << 8)) + dest->lowmant |= 1; + break; + case 31: + asm volatile ("lsr.l #1,%1; roxr.l #1,%0" + : "=d" (dest->mant.m32[0]) + : "d" (src->m32[0]), "0" (src->m32[1])); + asm volatile ("roxr.l #1,%0" + : "=d" (dest->mant.m32[1]) : "0" (src->m32[2])); + asm volatile ("roxr.l #1,%0" + : "=d" (tmp) : "0" (src->m32[3])); + dest->lowmant = tmp >> 24; + if (src->m32[3] << 7) + dest->lowmant |= 1; + break; + case 32: + dest->mant.m32[0] = src->m32[1]; + dest->mant.m32[1] = src->m32[2]; + dest->lowmant = src->m32[3] >> 24; + if (src->m32[3] << 8) + dest->lowmant |= 1; + break; + } +} + +#if 0 /* old code... */ +extern inline int fls(unsigned int a) +{ + int r; + + asm volatile ("bfffo %1{#0,#32},%0" + : "=d" (r) : "md" (a)); + return r; +} + +/* fls = "find last set" (cf. ffs(3)) */ +extern inline int fls128(const int128 a) +{ + if (a[MSW128]) + return fls(a[MSW128]); + if (a[NMSW128]) + return fls(a[NMSW128]) + 32; + /* XXX: it probably never gets beyond this point in actual + use, but that's indicative of a more general problem in the + algorithm (i.e. as per the actual 68881 implementation, we + really only need at most 67 bits of precision [plus + overflow]) so I'm not going to fix it. */ + if (a[NLSW128]) + return fls(a[NLSW128]) + 64; + if (a[LSW128]) + return fls(a[LSW128]) + 96; + else + return -1; +} + +extern inline int zerop128(const int128 a) +{ + return !(a[LSW128] | a[NLSW128] | a[NMSW128] | a[MSW128]); +} + +extern inline int nonzerop128(const int128 a) +{ + return (a[LSW128] | a[NLSW128] | a[NMSW128] | a[MSW128]); +} + +/* Addition and subtraction */ +/* Do these in "pure" assembly, because "extended" asm is unmanageable + here */ +extern inline void add128(const int128 a, int128 b) +{ + /* rotating carry flags */ + unsigned int carry[2]; + + carry[0] = a[LSW128] > (0xffffffff - b[LSW128]); + b[LSW128] += a[LSW128]; + + carry[1] = a[NLSW128] > (0xffffffff - b[NLSW128] - carry[0]); + b[NLSW128] = a[NLSW128] + b[NLSW128] + carry[0]; + + carry[0] = a[NMSW128] > (0xffffffff - b[NMSW128] - carry[1]); + b[NMSW128] = a[NMSW128] + b[NMSW128] + carry[1]; + + b[MSW128] = a[MSW128] + b[MSW128] + carry[0]; +} + +/* Note: assembler semantics: "b -= a" */ +extern inline void sub128(const int128 a, int128 b) +{ + /* rotating borrow flags */ + unsigned int borrow[2]; + + borrow[0] = b[LSW128] < a[LSW128]; + b[LSW128] -= a[LSW128]; + + borrow[1] = b[NLSW128] < a[NLSW128] + borrow[0]; + b[NLSW128] = b[NLSW128] - a[NLSW128] - borrow[0]; + + borrow[0] = b[NMSW128] < a[NMSW128] + borrow[1]; + b[NMSW128] = b[NMSW128] - a[NMSW128] - borrow[1]; + + b[MSW128] = b[MSW128] - a[MSW128] - borrow[0]; +} + +/* Poor man's 64-bit expanding multiply */ +extern inline void mul64(unsigned long long a, + unsigned long long b, + int128 c) +{ + unsigned long long acc; + int128 acc128; + + zero128(acc128); + zero128(c); + + /* first the low words */ + if (LO_WORD(a) && LO_WORD(b)) { + acc = (long long) LO_WORD(a) * LO_WORD(b); + c[NLSW128] = HI_WORD(acc); + c[LSW128] = LO_WORD(acc); + } + /* Next the high words */ + if (HI_WORD(a) && HI_WORD(b)) { + acc = (long long) HI_WORD(a) * HI_WORD(b); + c[MSW128] = HI_WORD(acc); + c[NMSW128] = LO_WORD(acc); + } + /* The middle words */ + if (LO_WORD(a) && HI_WORD(b)) { + acc = (long long) LO_WORD(a) * HI_WORD(b); + acc128[NMSW128] = HI_WORD(acc); + acc128[NLSW128] = LO_WORD(acc); + add128(acc128, c); + } + /* The first and last words */ + if (HI_WORD(a) && LO_WORD(b)) { + acc = (long long) HI_WORD(a) * LO_WORD(b); + acc128[NMSW128] = HI_WORD(acc); + acc128[NLSW128] = LO_WORD(acc); + add128(acc128, c); + } +} + +/* Note: unsigned */ +extern inline int cmp128(int128 a, int128 b) +{ + if (a[MSW128] < b[MSW128]) + return -1; + if (a[MSW128] > b[MSW128]) + return 1; + if (a[NMSW128] < b[NMSW128]) + return -1; + if (a[NMSW128] > b[NMSW128]) + return 1; + if (a[NLSW128] < b[NLSW128]) + return -1; + if (a[NLSW128] > b[NLSW128]) + return 1; + + return (signed) a[LSW128] - b[LSW128]; +} + +inline void div128(int128 a, int128 b, int128 c) +{ + int128 mask; + + /* Algorithm: + + Shift the divisor until it's at least as big as the + dividend, keeping track of the position to which we've + shifted it, i.e. the power of 2 which we've multiplied it + by. + + Then, for this power of 2 (the mask), and every one smaller + than it, subtract the mask from the dividend and add it to + the quotient until the dividend is smaller than the raised + divisor. At this point, divide the dividend and the mask + by 2 (i.e. shift one place to the right). Lather, rinse, + and repeat, until there are no more powers of 2 left. */ + + /* FIXME: needless to say, there's room for improvement here too. */ + + /* Shift up */ + /* XXX: since it just has to be "at least as big", we can + probably eliminate this horribly wasteful loop. I will + have to prove this first, though */ + set128(0, 0, 0, 1, mask); + while (cmp128(b, a) < 0 && !btsthi128(b)) { + lslone128(b); + lslone128(mask); + } + + /* Shift down */ + zero128(c); + do { + if (cmp128(a, b) >= 0) { + sub128(b, a); + add128(mask, c); + } + lsrone128(mask); + lsrone128(b); + } while (nonzerop128(mask)); + + /* The remainder is in a... */ +} +#endif + +#endif /* MULTI_ARITH_H */ diff --git a/arch/m68k/q40/README b/arch/m68k/q40/README index 5433796cbc91..60134d9de5bb 100644 --- a/arch/m68k/q40/README +++ b/arch/m68k/q40/README @@ -7,7 +7,7 @@ available from this place and ftp.uni-erlangen.de/linux/680x0/q40/ and mirrors. Hints to documentation usually refer to the linux source tree in -/usr/src/linux unless URL given. +/usr/src/linux/Documentation unless URL given. It seems IRQ unmasking can't be safely done on a Q40. Autoprobing is not yet implemented - do not try it! (See below) @@ -18,8 +18,8 @@ particular device drivers. The floppy imposes a very high interrupt load on the CPU, approx 30K/s. When something blocks interrupts (HD) it will loose some of them, so far this is not known to have caused any data loss. On hihgly loaded systems -it can make the floppy very slow. Other Q40 OS' simply poll the floppy -for this reason - something that can't be done in Linux. +it can make the floppy very slow or practicaly stop. Other Q40 OS' simply +poll the floppy for this reason - something that can't be done in Linux. Only possible cure is getting a 82072 contoler with fifo instead of the 8272A @@ -48,7 +48,7 @@ Debugging Upon startup the kernel will usually output "ABCQGHIJ" into the SRAM, preceded by the booter signature. This is a trace just in case something went wrong during earliest setup stages. -*Changed* to preserve SRAM contents by default, this is only done when +**Changed** to preserve SRAM contents by default, this is only done when requested - SRAM must start with '%LX$' signature to do this. '-d' option to 'lxx' loader enables this. @@ -92,12 +92,22 @@ Interrupts ========== q40 master chip handles only level triggered interrupts :-(( -further limitation is no disabling etc. Unless someone finds -some ingenious clue this means autoprobing will never work. -Parallel port interrupts cause most trouble.. - -IRQ sharing is not yet implemented. - +further limitation is no disabling etc. There is NO WAY to remove +an ISA irq request other than serve the HW specific control register, +the ISA irq lines are connected straight to the CPU ipl1 pin.. + +IRQ sharing is not yet implemented but this should be only a minor +problem.. + +Linux has some requirements wrt interrupt architecture, these are +to my knowledge: + (a) interrupt handler must not be reentered even when sti() is called + (b) working enable/disable_irq + +Luckily these requirements are only important for drivers shared +with other architectures - ide,serial,parallel, ethernet.. +q40ints.c now contains a trivial hack for (a), however (b) could +be only solved by driver-specific code Keyboard ======== diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c index 889936243955..178bef18c724 100644 --- a/arch/m68k/q40/config.c +++ b/arch/m68k/q40/config.c @@ -1,12 +1,12 @@ /* * arch/m68k/q40/config.c * + * Copyright (C) 1999 Richard Zidlicky + * * originally based on: * * linux/bvme/config.c * - * Copyright (C) 1993 Hamish Macdonald - * * 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 * for more details. @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -134,12 +135,11 @@ int q40_kbdrate (struct kbd_repeat *k) void q40_reset() { - printk ("\n\n*******************************************\n" - "Called q40_reset : press the RESET button!! \n"); - printk( "*******************************************\n"); - - while(1) - ; + printk ("\n\n*******************************************\n" + "Called q40_reset : press the RESET button!! \n" + "*******************************************\n"); + + while(1) ; } static void q40_get_model(char *model) @@ -232,6 +232,9 @@ void q40_mksound(unsigned int hz, unsigned int ticks) } static void (*q40_timer_routine)(int, void *, struct pt_regs *); +static short rtc_oldsecs=0; +unsigned rtc_irq_flags=0; +unsigned rtc_irq_ctrl=0; static void q40_timer_int (int irq, void *dev_id, struct pt_regs *fp) { @@ -250,6 +253,14 @@ static void q40_timer_int (int irq, void *dev_id, struct pt_regs *fp) *DAC_LEFT=sval; *DAC_RIGHT=sval; } +#ifdef CONFIG_Q40RTC + if (rtc_irq_ctrl && (rtc_oldsecs != RTC_SECS)) + { + rtc_oldsecs = RTC_SECS; + rtc_irq_flags = RTC_UIE; + rtc_interrupt(); + } +#endif if (ql_ticks) return; #endif q40_timer_routine(irq, dev_id, fp); diff --git a/arch/m68k/q40/q40ints.c b/arch/m68k/q40/q40ints.c index e9b5a5b1f037..c6625aad8b09 100644 --- a/arch/m68k/q40/q40ints.c +++ b/arch/m68k/q40/q40ints.c @@ -29,7 +29,7 @@ * 3,4,5,6,7,10,11,14,15 : ISA dev IRQs * 16-31: reserved * 32 : keyboard int - * 33 : frame int (50 Hz periodic timer) + * 33 : frame int (50/200 Hz periodic timer) * 34 : sample int (10/20 KHz periodic timer) * */ @@ -45,20 +45,21 @@ extern void (*q40_sys_default_handler[]) (int, void *, struct pt_regs *); /* ad static void q40_defhand (int irq, void *dev_id, struct pt_regs *fp); static void sys_default_handler(int lev, void *dev_id, struct pt_regs *regs); -/* - * This should ideally be 4 elements only, for speed. - */ #define DEVNAME_SIZE 24 -static struct { +static struct q40_irq_node { void (*handler)(int, void *, struct pt_regs *); unsigned long flags; void *dev_id; + /* struct q40_irq_node *next;*/ char devname[DEVNAME_SIZE]; unsigned count; + unsigned short state; } irq_tab[Q40_IRQ_MAX+1]; +short unsigned q40_ablecount[Q40_IRQ_MAX+1]; + /* * void q40_init_IRQ (void) * @@ -78,8 +79,11 @@ void q40_init_IRQ (void) irq_tab[i].handler = q40_defhand; irq_tab[i].flags = IRQ_FLG_STD; irq_tab[i].dev_id = NULL; + /* irq_tab[i].next = NULL;*/ irq_tab[i].devname[0] = 0; irq_tab[i].count = 0; + irq_tab[i].state =0; + q40_ablecount[i]=0; /* all enabled */ } /* setup handler for ISA ints */ @@ -108,23 +112,20 @@ int q40_request_irq(unsigned int irq, } /* test for ISA ints not implemented by HW */ - if (irq<15) + switch (irq) { - switch (irq){ - case 1: case 2: case 8: case 9: - case 12: case 13: - printk("%s: ISA IRQ %d from %s not implemented by HW\n", __FUNCTION__, irq, devname); - return -ENXIO; - default: - } + case 1: case 2: case 8: case 9: + case 12: case 13: + printk("%s: ISA IRQ %d from %s not implemented by HW\n", __FUNCTION__, irq, devname); + return -ENXIO; + case 11: + printk("warning IRQ 10 and 11 not distinguishable\n"); + irq=10; + default: } if (irqsr;*/ + fp->sr = (fp->sr & (~0x700))+0x200; + disabled=1; + return; + } + irq_tab[irq].state |= IRQ_INPROGRESS; irq_tab[irq].handler(irq,irq_tab[irq].dev_id,fp); + + /* naively enable everything, if that fails than */ + /* this function will be reentered immediately thus */ + /* getting another chance to disable the IRQ */ + + irq_tab[irq].state &= ~IRQ_INPROGRESS; + if ( disabled ) + { + /*printk("reenabling irq %d\n",irq); */ + fp->sr = (fp->sr & (~0x700)); /*saved_mask; */ + disabled=0; + } + else if ( fp->sr &0x200 ) + printk("exiting irq handler: fp->sr &0x200 !!\n"); + return; } } @@ -279,7 +312,17 @@ void q40_irq2_handler (int vec, void *devname, struct pt_regs *fp) if (irq_tab[irq].handler == q40_defhand ) continue; /* ignore uninited INTs :-( */ + /* the INPROGRESS stuff should be completely useless*/ + /* for internal ints, nevertheless test it..*/ + if ( irq_tab[irq].state & IRQ_INPROGRESS ) + { + /*disable_irq(irq); + return;*/ + printk("rentering handler for IRQ %d !!\n",irq); + } irq_tab[irq].handler(irq,irq_tab[irq].dev_id,fp); + irq_tab[irq].state &= ~IRQ_INPROGRESS; + /*enable_irq(irq);*/ /* better not try luck !*/ return; } } @@ -327,19 +370,59 @@ static void sys_default_handler(int lev, void *dev_id, struct pt_regs *regs) sys_default_handler,sys_default_handler,sys_default_handler,sys_default_handler }; +int irq_disabled=0; void q40_enable_irq (unsigned int irq) { + /* enable ISA iqs */ + if ( irq>=0 && irq<=15 ) /* the moderately bad case */ + master_outb(1,EXT_ENABLE_REG); +#if 0 + unsigned long flags; + int i; + + if (irq>=10 && irq <= 15) + { + if ( !(--q40_ablecount[irq])) + for (i=10,irq_disabled=0; i<=15; i++) + { + irq_disabled |= (q40_ablecount[irq] !=0); + } + if ( !irq_disabled ) + { + save_flags(flags); + restore_flags(flags & (~0x700)); + } + } +#endif } void q40_disable_irq (unsigned int irq) { + /* disable ISA iqs : only do something if the driver has been + * verified to be Q40 "compatible" - right now only IDE + * Any driver should not attempt to sleep accross disable_irq !! + */ + + if ( irq>=10 && irq<=15 ) /* the moderately bad case */ + master_outb(0,EXT_ENABLE_REG); +#if 0 + unsigned long flags; + + if (irq>=10 && irq <= 15) + { + save_flags(flags); + restore_flags(flags | 0x200 ); + irq_disabled=1; + q40_ablecount[irq]++; + } +#endif } unsigned long q40_probe_irq_on (void) { - printk("sorry, irq probing not yet implemented - reconfigure the driver to avoid this\n"); - return 0; + printk("irq probing not working - reconfigure the driver to avoid this\n"); + return -1; } int q40_probe_irq_off (unsigned long irqs) { diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 0116527dc1f1..9ce38e92150a 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4068,8 +4068,7 @@ static struct param_table { { "no_unexpected_interrupts", 0, &print_unex, 0, 0 }, { "L40SX", 0, &print_unex, 0, 0 } }; -#define FLOPPY_SETUP -void __init floppy_setup(char *str) +static int __init floppy_setup(char *str) { int i; int param; @@ -4091,7 +4090,7 @@ void __init floppy_setup(char *str) DPRINT("%s=%d\n", str, param); *config_params[i].var = param; } - return; + return 1; } } } @@ -4105,6 +4104,7 @@ void __init floppy_setup(char *str) } else DPRINT("botched floppy option\n"); DPRINT("Read linux/drivers/block/README.fd\n"); + return 1; } static int have_no_fdc= -EIO; @@ -4463,6 +4463,9 @@ MODULE_SUPPORTED_DEVICE("fd"); #endif #else + +__setup ("floppy=", floppy_setup); + /* eject the boot floppy (if we need the drive for a different root floppy) */ /* This should only be called at boot time when we're sure that there's no * resource contention. */ diff --git a/drivers/char/busmouse.c b/drivers/char/busmouse.c index e3bbf306edc0..e9eb6da3aa4d 100644 --- a/drivers/char/busmouse.c +++ b/drivers/char/busmouse.c @@ -380,7 +380,7 @@ register_busmouse(struct busmouse *ops) if (busmouse_data[msedev]) return -EBUSY; - mse = kmalloc(GFP_KERNEL, sizeof(*mse)); + mse = kmalloc(sizeof(*mse), GFP_KERNEL); if (!mse) return -ENOMEM; @@ -465,6 +465,7 @@ bus_mouse_init(void)) return 0; } +EXPORT_SYMBOL(busmouse_add_movementbuttons); EXPORT_SYMBOL(busmouse_add_movement); EXPORT_SYMBOL(busmouse_add_buttons); EXPORT_SYMBOL(register_busmouse); diff --git a/drivers/char/q40_keyb.c b/drivers/char/q40_keyb.c index ea9d7438f3b7..a4b966aac5b4 100644 --- a/drivers/char/q40_keyb.c +++ b/drivers/char/q40_keyb.c @@ -240,32 +240,15 @@ int q40kbd_getkeycode(unsigned int scancode) -int q40kbd_pretranslate(unsigned char scancode, char raw_mode) -{ - if (scancode == 0xff) { - /* in scancode mode 1, my ESC key generates 0xff */ - /* the calculator keys on a FOCUS 9000 generate 0xff */ -#ifndef KBD_IS_FOCUS_9000 -#ifdef KBD_REPORT_ERR - if (!raw_mode) - printk(KERN_DEBUG "Keyboard error\n"); -#endif -#endif - prev_scancode = 0; - return 0; - } - if (scancode == 0xe0 || scancode == 0xe1) { +int q40kbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + if (scancode == 0xe0 || scancode == 0xe1) { prev_scancode = scancode; return 0; } - return 1; -} -int q40kbd_translate(unsigned char scancode, unsigned char *keycode, - char raw_mode) -{ - /*printk("translate ...\n");*/ if (prev_scancode) { /* * usually it will be 0xe0, but a Pause key generates @@ -376,7 +359,7 @@ static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (qcode == 0xe0) { qprev=0xe0; - handle_scancode(qprev); + handle_scancode(qprev , 1); goto exit; } @@ -394,7 +377,7 @@ static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (scancode==0xff) /* SySrq */ scancode=SYSRQ_KEY; - handle_scancode(scancode | (keyup ? 0200 : 0)); + handle_scancode(scancode, ! keyup ); keyup=0; mark_bh(KEYBOARD_BH); diff --git a/drivers/isdn/avmb1/b1.c b/drivers/isdn/avmb1/b1.c index 620a2aa6e336..1b7f07bc46f1 100644 --- a/drivers/isdn/avmb1/b1.c +++ b/drivers/isdn/avmb1/b1.c @@ -47,7 +47,6 @@ * */ -#include #include #include #include diff --git a/drivers/isdn/avmb1/b1isa.c b/drivers/isdn/avmb1/b1isa.c index 9b4c47d43671..d5562fb3801e 100644 --- a/drivers/isdn/avmb1/b1isa.c +++ b/drivers/isdn/avmb1/b1isa.c @@ -34,7 +34,6 @@ * */ -#include #include #include #include diff --git a/drivers/isdn/avmb1/b1pcmcia.c b/drivers/isdn/avmb1/b1pcmcia.c index ff7629cb0f71..9af06268abff 100644 --- a/drivers/isdn/avmb1/b1pcmcia.c +++ b/drivers/isdn/avmb1/b1pcmcia.c @@ -34,7 +34,6 @@ * */ -#include #include #include #include diff --git a/drivers/isdn/avmb1/t1isa.c b/drivers/isdn/avmb1/t1isa.c index eb8c5714918e..73fd8d24728c 100644 --- a/drivers/isdn/avmb1/t1isa.c +++ b/drivers/isdn/avmb1/t1isa.c @@ -38,7 +38,6 @@ * */ -#include #include #include #include diff --git a/drivers/isdn/divert/divert_init.c b/drivers/isdn/divert/divert_init.c index 7ee924993b01..ec58f43c0008 100644 --- a/drivers/isdn/divert/divert_init.c +++ b/drivers/isdn/divert/divert_init.c @@ -31,7 +31,6 @@ * */ -#include #include #include #include "isdn_divert.h" diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c index 7454aa0958be..6650d47f4a12 100644 --- a/drivers/isdn/divert/isdn_divert.c +++ b/drivers/isdn/divert/isdn_divert.c @@ -29,7 +29,6 @@ -#include #define __NO_VERSION__ #include #include diff --git a/drivers/isdn/divert/isdn_divert.h b/drivers/isdn/divert/isdn_divert.h index 7a2b5cf08a8a..336187837b55 100644 --- a/drivers/isdn/divert/isdn_divert.h +++ b/drivers/isdn/divert/isdn_divert.h @@ -29,7 +29,6 @@ -#include #include #include diff --git a/drivers/isdn/eicon/eicon.h b/drivers/isdn/eicon/eicon.h index 88f408cd148f..444732e7b6fa 100644 --- a/drivers/isdn/eicon/eicon.h +++ b/drivers/isdn/eicon/eicon.h @@ -186,6 +186,7 @@ typedef struct { #ifdef __KERNEL__ /* Kernel includes */ +#include #include #include #include diff --git a/drivers/isdn/eicon/eicon_idi.c b/drivers/isdn/eicon/eicon_idi.c index 412eb46ba9d1..c10b014cdf26 100644 --- a/drivers/isdn/eicon/eicon_idi.c +++ b/drivers/isdn/eicon/eicon_idi.c @@ -70,6 +70,7 @@ * */ +#include #define __NO_VERSION__ #include "eicon.h" #include "eicon_idi.h" diff --git a/drivers/isdn/eicon/eicon_idi.h b/drivers/isdn/eicon/eicon_idi.h index 9a1da1751303..ca989cf9d83e 100644 --- a/drivers/isdn/eicon/eicon_idi.h +++ b/drivers/isdn/eicon/eicon_idi.h @@ -54,6 +54,7 @@ #ifndef IDI_H #define IDI_H +#include #define ASSIGN 0x01 #define REMOVE 0xff diff --git a/drivers/isdn/eicon/eicon_io.c b/drivers/isdn/eicon/eicon_io.c index 79ce41eed87c..058afa229c0a 100644 --- a/drivers/isdn/eicon/eicon_io.c +++ b/drivers/isdn/eicon/eicon_io.c @@ -36,6 +36,7 @@ */ +#include #include "eicon.h" void diff --git a/drivers/isdn/eicon/eicon_isa.c b/drivers/isdn/eicon/eicon_isa.c index 924d5620f686..ba2dd0f852c1 100644 --- a/drivers/isdn/eicon/eicon_isa.c +++ b/drivers/isdn/eicon/eicon_isa.c @@ -50,6 +50,7 @@ * */ +#include #include "eicon.h" #include "eicon_isa.h" diff --git a/drivers/isdn/eicon/eicon_pci.c b/drivers/isdn/eicon/eicon_pci.c index a5611394c3aa..6bb4c9a30904 100644 --- a/drivers/isdn/eicon/eicon_pci.c +++ b/drivers/isdn/eicon/eicon_pci.c @@ -64,6 +64,7 @@ * */ +#include #include #include "eicon.h" diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c index e97c8e9bf01e..adf6b73b2012 100644 --- a/drivers/isdn/hisax/bkm_a4t.c +++ b/drivers/isdn/hisax/bkm_a4t.c @@ -28,6 +28,7 @@ #define __NO_VERSION__ +#include #include "hisax.h" #include "isac.h" #include "hscx.h" diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c index 0e014ebc06e1..0defb74b59dd 100644 --- a/drivers/isdn/hisax/bkm_a8.c +++ b/drivers/isdn/hisax/bkm_a8.c @@ -27,6 +27,7 @@ */ #define __NO_VERSION__ +#include #include "hisax.h" #include "isac.h" #include "ipac.h" diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c index 6284a4cf38ca..6caf0341fe71 100644 --- a/drivers/isdn/hisax/callc.c +++ b/drivers/isdn/hisax/callc.c @@ -126,6 +126,7 @@ * */ +#include #define __NO_VERSION__ #include "hisax.h" #include "../avmb1/capicmd.h" /* this should be moved in a common place */ diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c index a1016fd4b40f..e4b35e502900 100644 --- a/drivers/isdn/hisax/elsa_ser.c +++ b/drivers/isdn/hisax/elsa_ser.c @@ -1,3 +1,4 @@ +#include #include #include diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c index ea27f28b230f..199bf2b609c8 100644 --- a/drivers/isdn/hisax/gazel.c +++ b/drivers/isdn/hisax/gazel.c @@ -23,6 +23,7 @@ * Initial revision * */ +#include #define __NO_VERSION__ #include "hisax.h" #include "isac.h" diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index 2d972f3fe625..837c957bed80 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -67,6 +67,7 @@ * */ +#include #define __NO_VERSION__ #include "hisax.h" #include "hfc_pci.h" diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c index 68813448084a..9ed3106727cb 100644 --- a/drivers/isdn/hisax/isdnl1.c +++ b/drivers/isdn/hisax/isdnl1.c @@ -132,7 +132,6 @@ const char *l1_revision = "$Revision: 2.34 $"; #define __NO_VERSION__ -#include #include "hisax.h" #include "isdnl1.h" diff --git a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c index a5d251b71382..a81bf07605e2 100644 --- a/drivers/isdn/hisax/isurf.c +++ b/drivers/isdn/hisax/isurf.c @@ -17,7 +17,6 @@ */ #define __NO_VERSION__ -#include #include "hisax.h" #include "isac.h" #include "isar.h" diff --git a/drivers/isdn/isdn_bsdcomp.c b/drivers/isdn/isdn_bsdcomp.c index 92012c8e5c7b..e5cc0ea30be5 100644 --- a/drivers/isdn/isdn_bsdcomp.c +++ b/drivers/isdn/isdn_bsdcomp.c @@ -64,7 +64,6 @@ #include #include #include -#include /* to get the struct task_struct */ #include /* used in new tty drivers */ #include /* used in new tty drivers */ diff --git a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h index 4fe9568ca9e2..62e723eafbed 100644 --- a/drivers/isdn/isdn_tty.h +++ b/drivers/isdn/isdn_tty.h @@ -86,6 +86,7 @@ * */ +#include #define DLE 0x10 #define ETX 0x03 diff --git a/drivers/isdn/isdn_ttyfax.c b/drivers/isdn/isdn_ttyfax.c index fc17b5b830ac..cc7c14fc6a6d 100644 --- a/drivers/isdn/isdn_ttyfax.c +++ b/drivers/isdn/isdn_ttyfax.c @@ -33,7 +33,6 @@ #undef ISDN_TTY_FAX_CMD_DEBUG #define __NO_VERSION__ -#include #include #include #include "isdn_common.h" diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c index 348b2e790452..6699709e40ff 100644 --- a/drivers/net/a2065.c +++ b/drivers/net/a2065.c @@ -499,7 +499,7 @@ static int lance_open (struct device *dev) last_dev = dev; /* Install the Interrupt handler */ - if (request_irq(IRQ_AMIGA_PORTS, lance_interrupt, 0, + if (request_irq(IRQ_AMIGA_PORTS, lance_interrupt, SA_SHIRQ, "a2065 Ethernet", dev)) return -EAGAIN; diff --git a/drivers/net/apne.c b/drivers/net/apne.c index 28ac93605072..fd8f1e577d3d 100644 --- a/drivers/net/apne.c +++ b/drivers/net/apne.c @@ -294,7 +294,8 @@ static int __init apne_probe1(struct device *dev, int ioaddr) dev->base_addr = ioaddr; /* Install the Interrupt handler */ - if (request_irq(IRQ_AMIGA_PORTS, apne_interrupt, 0, "apne Ethernet", dev)) + if (request_irq(IRQ_AMIGA_PORTS, apne_interrupt, SA_SHIRQ, + "apne Ethernet", dev)) return -EAGAIN; diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c index e08455cf83f1..7b396c6933c6 100644 --- a/drivers/net/ariadne.c +++ b/drivers/net/ariadne.c @@ -293,7 +293,7 @@ static int ariadne_open(struct device *dev) dev->interrupt = 0; dev->start = 1; - if (request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, 0, + if (request_irq(IRQ_AMIGA_PORTS, ariadne_interrupt, SA_SHIRQ, "Ariadne Ethernet", dev)) return(-EAGAIN); diff --git a/drivers/net/ariadne2.c b/drivers/net/ariadne2.c index 83ed4359b866..9dcffecc0516 100644 --- a/drivers/net/ariadne2.c +++ b/drivers/net/ariadne2.c @@ -180,8 +180,8 @@ static int __init ariadne2_init(struct device *dev, unsigned int key, dev->irq = IRQ_AMIGA_PORTS; /* Install the Interrupt handler */ - if (request_irq(IRQ_AMIGA_PORTS, ei_interrupt, 0, "AriadNE2 Ethernet", - dev)) + if (request_irq(IRQ_AMIGA_PORTS, ei_interrupt, SA_SHIRQ, + "AriadNE2 Ethernet", dev)) return -EAGAIN; /* Allocate dev->priv and fill in 8390 specific dev fields. */ diff --git a/drivers/net/hydra.c b/drivers/net/hydra.c index 5ced788bbacb..79649cfbc533 100644 --- a/drivers/net/hydra.c +++ b/drivers/net/hydra.c @@ -282,7 +282,8 @@ static int hydra_open(struct device *dev) dev->interrupt = 0; dev->start = 1; - if(request_irq(IRQ_AMIGA_PORTS, hydra_interrupt, 0, "Hydra Ethernet", dev)) + if(request_irq(IRQ_AMIGA_PORTS, hydra_interrupt, SA_SHIRQ, + "Hydra Ethernet", dev)) return(-EAGAIN); MOD_INC_USE_COUNT; diff --git a/drivers/parport/parport_mfc3.c b/drivers/parport/parport_mfc3.c index 351585bc8d08..1a0da9bca44f 100644 --- a/drivers/parport/parport_mfc3.c +++ b/drivers/parport/parport_mfc3.c @@ -346,7 +346,7 @@ __initfunc(int parport_mfc3_init(void)) if (p->irq != PARPORT_IRQ_NONE) if (use_cnt++ == 0) - if (request_irq(IRQ_AMIGA_PORTS, mfc3_interrupt, 0, p->name, &pp_mfc3_ops)) + if (request_irq(IRQ_AMIGA_PORTS, mfc3_interrupt, SA_SHIRQ, p->name, &pp_mfc3_ops)) use_cnt--; if (parport_probe_hook) diff --git a/drivers/pnp/Config.in b/drivers/pnp/Config.in index 0aa5aa426b34..a350c45266f0 100644 --- a/drivers/pnp/Config.in +++ b/drivers/pnp/Config.in @@ -1,3 +1,11 @@ # # Plug and Play configuration # +mainmenu_option next_comment +comment 'Plug and Play configuration' + +tristate 'Plug and Play support' CONFIG_PNP + +dep_tristate 'ISA Plug and Play support' CONFIG_ISAPNP $CONFIG_PNP + +endmenu diff --git a/drivers/pnp/Makefile b/drivers/pnp/Makefile index a801f19925c4..f12b5686d1d5 100644 --- a/drivers/pnp/Makefile +++ b/drivers/pnp/Makefile @@ -18,4 +18,12 @@ LX_OBJS := MI_OBJS := MIX_OBJS := +ifeq ($(CONFIG_ISAPNP),y) + LX_OBJS += isapnp.o +else + ifeq ($(CONFIG_ISAPNP),m) + MX_OBJS += isapnp.o + endif +endif + include $(TOPDIR)/Rules.make diff --git a/drivers/pnp/isapnp.c b/drivers/pnp/isapnp.c new file mode 100644 index 000000000000..c8b5cef4c12f --- /dev/null +++ b/drivers/pnp/isapnp.c @@ -0,0 +1,2159 @@ +/* + * ISA Plug & Play support + * Copyright (c) by Jaroslav Kysela + * + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PROC_FS +#include "isapnp_proc.c" +#endif + +#if 0 +#define ISAPNP_REGION_OK +#endif +#if 0 +#define ISAPNP_DEBUG +#endif + +struct resource *pidxr_res = NULL; +struct resource *pnpwrp_res = NULL; +struct resource *isapnp_rdp_res = NULL; + +int isapnp_disable = 0; /* Disable ISA PnP */ +int isapnp_rdp = 0; /* Read Data Port */ +int isapnp_reset = 0; /* reset all PnP cards (deactivate) */ +int isapnp_skip_pci_scan = 0; /* skip PCI resource scanning */ +int isapnp_verbose = 1; /* verbose mode */ +int isapnp_reserve_irq[16] = { [0 ... 15] = -1 }; /* reserve (don't use) some IRQ */ +int isapnp_reserve_dma[8] = { [0 ... 7] = -1 }; /* reserve (don't use) some DMA */ +int isapnp_reserve_io[16] = { [0 ... 15] = -1 }; /* reserve (don't use) some I/O region */ +int isapnp_reserve_mem[16] = { [0 ... 15] = -1 }; /* reserve (don't use) some memory region */ + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Generic ISA Plug & Play support"); +MODULE_PARM(isapnp_disable, "i"); +MODULE_PARM_DESC(isapnp_disable, "ISA Plug & Play disable"); +MODULE_PARM(isapnp_rdp, "i"); +MODULE_PARM_DESC(isapnp_rdp, "ISA Plug & Play read data port"); +MODULE_PARM(isapnp_reset, "i"); +MODULE_PARM_DESC(isapnp_reset, "ISA Plug & Play reset all cards"); +MODULE_PARM(isapnp_skip_pci_scan, "i"); +MODULE_PARM_DESC(isapnp_skip_pci_scan, "ISA Plug & Play skip PCI resource scanning"); +MODULE_PARM(isapnp_verbose, "i"); +MODULE_PARM_DESC(isapnp_verbose, "ISA Plug & Play verbose mode"); +MODULE_PARM(isapnp_reserve_irq, "1-16i"); +MODULE_PARM_DESC(isapnp_reserve_irq, "ISA Plug & Play - reserve IRQ line(s)"); +MODULE_PARM(isapnp_reserve_dma, "1-8i"); +MODULE_PARM_DESC(isapnp_reserve_dma, "ISA Plug & Play - reserve DMA channel(s)"); +MODULE_PARM(isapnp_reserve_io, "1-16i"); +MODULE_PARM_DESC(isapnp_reserve_io, "ISA Plug & Play - reserve I/O region(s) - port,size"); +MODULE_PARM(isapnp_reserve_mem, "1-16i"); +MODULE_PARM_DESC(isapnp_reserve_mem, "ISA Plug & Play - reserve memory region(s) - address,size"); + +#define _PIDXR 0x279 +#define _PNPWRP 0xa79 + +/* short tags */ +#define _STAG_PNPVERNO 0x01 +#define _STAG_LOGDEVID 0x02 +#define _STAG_COMPATDEVID 0x03 +#define _STAG_IRQ 0x04 +#define _STAG_DMA 0x05 +#define _STAG_STARTDEP 0x06 +#define _STAG_ENDDEP 0x07 +#define _STAG_IOPORT 0x08 +#define _STAG_FIXEDIO 0x09 +#define _STAG_VENDOR 0x0e +#define _STAG_END 0x0f +/* long tags */ +#define _LTAG_MEMRANGE 0x81 +#define _LTAG_ANSISTR 0x82 +#define _LTAG_UNICODESTR 0x83 +#define _LTAG_VENDOR 0x84 +#define _LTAG_MEM32RANGE 0x85 +#define _LTAG_FIXEDMEM32RANGE 0x86 + +struct pci_bus *isapnp_cards = NULL; /* ISA PnP cards */ +struct pci_dev *isapnp_devices = NULL; /* ISA PnP devices */ +static struct pci_dev *isapnp_last_device = NULL; +static unsigned char isapnp_checksum_value; +static DECLARE_MUTEX(isapnp_cfg_mutex); +static int isapnp_detected = 0; + +/* some prototypes */ + +static int isapnp_config_prepare(struct pci_dev *dev); +static int isapnp_config_activate(struct pci_dev *dev); +static int isapnp_config_deactivate(struct pci_dev *dev); + +static int isapnp_debug = 0; + +static inline void write_data(unsigned char x) +{ +// if (isapnp_debug) printk("",x); + outb(x, _PNPWRP); +} + +static inline void write_address(unsigned char x) +{ +// if (isapnp_debug) printk("",x); + outb(x, _PIDXR); + udelay(10); +} + +static inline unsigned char read_data(void) +{ + unsigned char val = inb(isapnp_rdp); +// if (isapnp_debug) printk("",val); + return val; +} + +unsigned char isapnp_read_byte(unsigned char idx) +{ + write_address(idx); + return read_data(); +} + +unsigned short isapnp_read_word(unsigned char idx) +{ + unsigned short val; + + val = isapnp_read_byte(idx); + val = (val << 8) + isapnp_read_byte(idx+1); + return val; +} + +unsigned int isapnp_read_dword(unsigned char idx) +{ + unsigned int val; + + val = isapnp_read_byte(idx); + val = (val << 8) + isapnp_read_byte(idx+1); + val = (val << 8) + isapnp_read_byte(idx+2); + val = (val << 8) + isapnp_read_byte(idx+3); + return val; +} + +void isapnp_write_byte(unsigned char idx, unsigned char val) +{ + write_address(idx); + write_data(val); +} + +void isapnp_write_word(unsigned char idx, unsigned short val) +{ + isapnp_write_byte(idx, val >> 8); + isapnp_write_byte(idx+1, val); +} + +void isapnp_write_dword(unsigned char idx, unsigned int val) +{ + isapnp_write_byte(idx, val >> 24); + isapnp_write_byte(idx+1, val >> 16); + isapnp_write_byte(idx+2, val >> 8); + isapnp_write_byte(idx+3, val); +} + +static void *isapnp_alloc(long size) +{ + void *result; + + result = kmalloc(size, GFP_KERNEL); + if (!result) + return NULL; + memset(result, 0, size); + return result; +} + +static void isapnp_key(void) +{ + unsigned char code = 0x6a, msb; + int i; + + mdelay(1); + write_address(0x00); + write_address(0x00); + + write_address(code); + + for (i = 1; i < 32; i++) { + msb = ((code & 0x01) ^ ((code & 0x02) >> 1)) << 7; + code = (code >> 1) | msb; + write_address(code); + } +} + +/* place all pnp cards in wait-for-key state */ +static void isapnp_wait(void) +{ + isapnp_write_byte(0x02, 0x02); +} + +void isapnp_wake(unsigned char csn) +{ + isapnp_write_byte(0x03, csn); +} + +void isapnp_device(unsigned char logdev) +{ + isapnp_write_byte(0x07, logdev); +} + +void isapnp_activate(unsigned char logdev) +{ + isapnp_device(logdev); + isapnp_write_byte(ISAPNP_CFG_ACTIVATE, 1); + udelay(250); +} + +void isapnp_deactivate(unsigned char logdev) +{ + isapnp_device(logdev); + isapnp_write_byte(ISAPNP_CFG_ACTIVATE, 0); +} + +static void __init isapnp_peek(unsigned char *data, int bytes) +{ + int i, j; + unsigned char d; + + for (i = 1; i <= bytes; i++) { + for (j = 0; j < 10; j++) { + d = isapnp_read_byte(0x05); + if (d & 1) + break; + udelay(10); + } + if (!(d & 1)) { + *data++ = 0xff; + continue; + } + d = isapnp_read_byte(0x04); /* PRESDI */ + isapnp_checksum_value += d; + if (data != NULL) + *data++ = d; + } +} + +#define RDP_STEP 32 /* minimum is 4 */ + +static int isapnp_next_rdp(void) +{ + int rdp = isapnp_rdp; + while (rdp <= 0x3ff) { + if (!check_region(rdp, 1)) { + isapnp_rdp = rdp; + return 0; + } + rdp += RDP_STEP; + } + return -1; +} + +/* Set read port address */ +static inline void isapnp_set_rdp(void) +{ + isapnp_write_byte(0x00, isapnp_rdp >> 2); + udelay(100); +} + + +static int __init isapnp_isolate_rdp_select(void) +{ + isapnp_wait(); + isapnp_key(); + + isapnp_write_byte(0x02, 0x04); /* Control: reset CSN */ + mdelay(2); + + isapnp_wait(); + isapnp_key(); + isapnp_wake(0x00); + + if (isapnp_next_rdp() < 0) { + isapnp_wait(); + return -1; + } + + isapnp_set_rdp(); + udelay(1000); + write_address(0x01); + udelay(1000); + return 0; +} + +/* + * Isolate (assign uniqued CSN) to all ISA PnP devices. + */ + +static int __init isapnp_isolate(void) +{ + unsigned char checksum = 0x6a; + unsigned char chksum = 0x00; + unsigned char bit = 0x00; + int data; + int csn = 0; + int i; + int iteration = 1; + + isapnp_rdp = 0x213; + if (isapnp_isolate_rdp_select() < 0) + return -1; + + while (1) { + for (i = 1; i <= 64; i++) { + data = read_data() << 8; + udelay(250); + data = data | read_data(); + udelay(250); + if (data == 0x55aa) + bit = 0x01; + checksum = ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit) << 7) | (checksum >> 1); + bit = 0x00; + } + for (i = 65; i <= 72; i++) { + data = read_data() << 8; + udelay(250); + data = data | read_data(); + udelay(250); + if (data == 0x55aa) + chksum |= (1 << (i - 65)); + } + if (checksum != 0x00 && checksum == chksum) { + csn++; + + isapnp_write_byte(0x06, csn); + udelay(250); + iteration++; + isapnp_wake(0x00); + write_address(0x01); + goto __next; + } + if (iteration == 1) { + isapnp_rdp += RDP_STEP; +isapnp_debug = 0; + if (isapnp_isolate_rdp_select() < 0) + return -1; + } else if (iteration > 1) { + break; + } + __next: + checksum = 0x6a; + chksum = 0x00; + bit = 0x00; + } + isapnp_wait(); + return csn; +} + +/* + * Read one tag from stream. + */ + +static int __init isapnp_read_tag(unsigned char *type, unsigned short *size) +{ + unsigned char tag, tmp[2]; + + isapnp_peek(&tag, 1); + if (tag == 0) /* invalid tag */ + return -1; + if (tag & 0x80) { /* large item */ + *type = tag; + isapnp_peek(tmp, 2); + *size = (tmp[1] << 8) | tmp[0]; + } else { + *type = (tag >> 3) & 0x0f; + *size = tag & 0x07; + } +#if 0 + printk("tag = 0x%x, type = 0x%x, size = %i\n", tag, *type, *size); +#endif + if (type == 0) /* wrong type */ + return -1; + if (*type == 0xff && *size == 0xffff) /* probably invalid data */ + return -1; + return 0; +} + +/* + * Skip specified number of bytes from stream. + */ + +static void __init isapnp_skip_bytes(int count) +{ + isapnp_peek(NULL, count); +} + +/* + * Parse logical device tag. + */ + +static struct pci_dev * __init isapnp_parse_device(struct pci_bus *card, int size, int number) +{ + unsigned char tmp[6]; + struct pci_dev *dev; + + isapnp_peek(tmp, size); + dev = isapnp_alloc(sizeof(struct pci_dev)); + if (!dev) + return NULL; + dev->devfn = number; + dev->vendor = (tmp[1] << 8) | tmp[0]; + dev->device = (tmp[3] << 8) | tmp[2]; + dev->regs = tmp[4]; + dev->bus = card; + if (size > 5) + dev->regs |= tmp[5] << 8; + dev->prepare = isapnp_config_prepare; + dev->activate = isapnp_config_activate; + dev->deactivate = isapnp_config_deactivate; + return dev; +} + +/* + * Build new resources structure + */ + +static struct isapnp_resources * __init isapnp_build_resources(struct pci_dev *dev, int dependent) +{ + struct isapnp_resources *res, *ptr, *ptra; + + res = isapnp_alloc(sizeof(struct isapnp_resources)); + if (!res) + return NULL; + res->dev = dev; + ptr = (struct isapnp_resources *)dev->sysdata; + while (ptr && ptr->next) + ptr = ptr->next; + if (ptr && ptr->dependent && dependent) { /* add to another list */ + ptra = ptr->alt; + while (ptra && ptra->alt) + ptra = ptra->alt; + if (!ptra) + ptr->alt = res; + else + ptra->alt = res; + } else { + if (!ptr) + dev->sysdata = res; + else + ptr->next = res; + } + if (dependent) { + res->priority = dependent & 0xff; + if (res->priority > ISAPNP_RES_PRIORITY_FUNCTIONAL) + res->priority = ISAPNP_RES_PRIORITY_INVALID; + res->dependent = 1; + } else { + res->priority = ISAPNP_RES_PRIORITY_PREFERRED; + res->dependent = 0; + } + return res; +} + +/* + * Add IRQ resource to resources list. + */ + +static void __init isapnp_add_irq_resource(struct pci_dev *dev, + struct isapnp_resources **res, + int dependent, int size) +{ + unsigned char tmp[3]; + struct isapnp_irq *irq, *ptr; + + isapnp_peek(tmp, size); + irq = isapnp_alloc(sizeof(struct isapnp_irq)); + if (!irq) + return; + if (*res == NULL) { + *res = isapnp_build_resources(dev, dependent); + if (*res == NULL) { + kfree(irq); + return; + } + } + irq->map = (tmp[1] << 8) | tmp[0]; + if (size > 2) + irq->flags = tmp[2]; + else + irq->flags = DEVICE_IRQ_FLAG_HIGHEDGE; + irq->res = *res; + ptr = (*res)->irq; + while (ptr && ptr->next) + ptr = ptr->next; + if (ptr) + ptr->next = irq; + else + (*res)->irq = irq; +} + +/* + * Add DMA resource to resources list. + */ + +static void __init isapnp_add_dma_resource(struct pci_dev *dev, + struct isapnp_resources **res, + int dependent, int size) +{ + unsigned char tmp[2]; + struct isapnp_dma *dma, *ptr; + + isapnp_peek(tmp, size); + dma = isapnp_alloc(sizeof(struct isapnp_dma)); + if (!dma) + return; + if (*res == NULL) { + *res = isapnp_build_resources(dev, dependent); + if (*res == NULL) { + kfree(dma); + return; + } + } + dma->map = tmp[0]; + dma->type = tmp[1] & 3; + dma->flags = (tmp[1] >> 2) & 7; + dma->speed = (tmp[1] >> 6) & 3; + dma->res = *res; + ptr = (*res)->dma; + while (ptr && ptr->next) + ptr = ptr->next; + if (ptr) + ptr->next = dma; + else + (*res)->dma = dma; +} + +/* + * Add port resource to resources list. + */ + +static void __init isapnp_add_port_resource(struct pci_dev *dev, + struct isapnp_resources **res, + int dependent, int size) +{ + unsigned char tmp[7]; + struct isapnp_port *port, *ptr; + + isapnp_peek(tmp, size); + port = isapnp_alloc(sizeof(struct isapnp_port)); + if (!port) + return; + if (*res == NULL) { + *res = isapnp_build_resources(dev, dependent); + if (*res == NULL) { + kfree(port); + return; + } + } + port->min = (tmp[2] << 8) | tmp[1]; + port->max = (tmp[4] << 8) | tmp[3]; + port->align = tmp[5]; + port->size = tmp[6]; + port->flags = tmp[0] ? ISAPNP_PORT_FLAG_16BITADDR : 0; + port->res = *res; + ptr = (*res)->port; + while (ptr && ptr->next) + ptr = ptr->next; + if (ptr) + ptr->next = port; + else + (*res)->port = port; +} + +/* + * Add fixed port resource to resources list. + */ + +static void __init isapnp_add_fixed_port_resource(struct pci_dev *dev, + struct isapnp_resources **res, + int dependent, int size) +{ + unsigned char tmp[3]; + struct isapnp_port *port, *ptr; + + isapnp_peek(tmp, size); + port = isapnp_alloc(sizeof(struct isapnp_port)); + if (!port) + return; + if (*res == NULL) { + *res = isapnp_build_resources(dev, dependent); + if (*res == NULL) { + kfree(port); + return; + } + } + port->min = port->max = (tmp[1] << 8) | tmp[0]; + port->size = tmp[2]; + port->align = 0; + port->flags = ISAPNP_PORT_FLAG_FIXED; + port->res = *res; + ptr = (*res)->port; + while (ptr && ptr->next) + ptr = ptr->next; + if (ptr) + ptr->next = port; + else + (*res)->port = port; +} + +/* + * Add memory resource to resources list. + */ + +static void __init isapnp_add_mem_resource(struct pci_dev *dev, + struct isapnp_resources **res, + int dependent, int size) +{ + unsigned char tmp[9]; + struct isapnp_mem *mem, *ptr; + + isapnp_peek(tmp, size); + mem = isapnp_alloc(sizeof(struct isapnp_mem)); + if (!mem) + return; + if (*res == NULL) { + *res = isapnp_build_resources(dev, dependent); + if (*res == NULL) { + kfree(mem); + return; + } + } + mem->min = ((tmp[2] << 8) | tmp[1]) << 8; + mem->max = ((tmp[4] << 8) | tmp[3]) << 8; + mem->align = (tmp[6] << 8) | tmp[5]; + mem->size = ((tmp[8] << 8) | tmp[7]) << 8; + mem->flags = tmp[0] & 7; + mem->flags |= (tmp[0] >> 2) & 0x18; + mem->type = (tmp[0] >> 3) & 3; + mem->res = *res; + ptr = (*res)->mem; + while (ptr && ptr->next) + ptr = ptr->next; + if (ptr) + ptr->next = mem; + else + (*res)->mem = mem; +} + +/* + * Add 32-bit memory resource to resources list. + */ + +static void __init isapnp_add_mem32_resource(struct pci_dev *dev, + struct isapnp_resources **res, + int dependent, int size) +{ + unsigned char tmp[17]; + struct isapnp_mem32 *mem32, *ptr; + + isapnp_peek(tmp, size); + mem32 = isapnp_alloc(sizeof(struct isapnp_mem32)); + if (!mem32) + return; + if (*res == NULL) { + *res = isapnp_build_resources(dev, dependent); + if (*res == NULL) { + kfree(mem32); + return; + } + } + memcpy(mem32->data, tmp, 17); + mem32->res = *res; + ptr = (*res)->mem32; + while (ptr && ptr->next) + ptr = ptr->next; + if (ptr) + ptr->next = mem32; + else + (*res)->mem32 = mem32; +} + +/* + * Add 32-bit fixed memory resource to resources list. + */ + +static void __init isapnp_add_fixed_mem32_resource(struct pci_dev *dev, + struct isapnp_resources **res, + int dependent, int size) +{ + unsigned char tmp[17]; + struct isapnp_mem32 *mem32, *ptr; + + isapnp_peek(tmp, size); + mem32 = isapnp_alloc(sizeof(struct isapnp_mem32)); + if (!mem32) + return; + if (*res == NULL) { + *res = isapnp_build_resources(dev, dependent); + if (*res == NULL) { + kfree(mem32); + return; + } + } + memcpy(mem32->data, tmp, 17); + mem32->res = *res; + ptr = (*res)->mem32; + while (ptr && ptr->next) + ptr = ptr->next; + if (ptr) + ptr->next = mem32; + else + (*res)->mem32 = mem32; +} + +/* + * Parse resource map for logical device. + */ + +static int __init isapnp_create_device(struct pci_bus *card, + unsigned short size) +{ + int number = 0, skip = 0, dependent = 0, compat = 0; + unsigned char type, tmp[17]; + struct pci_dev *dev, *prev_dev; + struct isapnp_resources *res = NULL; + + if ((dev = isapnp_parse_device(card, size, number++)) == NULL) + return 1; + card->devices = dev; + if (isapnp_last_device) { + isapnp_last_device->next = dev; + isapnp_last_device = dev; + } else { + isapnp_devices = isapnp_last_device = dev; + } + while (1) { + if (isapnp_read_tag(&type, &size)<0) + return 1; + if (skip && type != _STAG_LOGDEVID && type != _STAG_END) + goto __skip; + switch (type) { + case _STAG_LOGDEVID: + if (size >= 5 && size <= 6) { + prev_dev = dev; + isapnp_config_prepare(dev); + if ((dev = isapnp_parse_device(card, size, number++)) == NULL) + return 1; + prev_dev->sibling = dev; + isapnp_last_device->next = dev; + isapnp_last_device = dev; + size = 0; + skip = 0; + } else { + skip = 1; + } + res = NULL; + dependent = 0; + compat = 0; + break; + case _STAG_COMPATDEVID: + if (size == 4 && compat < DEVICE_COUNT_COMPATIBLE) { + isapnp_peek(tmp, 4); + dev->vendor_compatible[compat] = (tmp[1] << 8) | tmp[0]; + dev->device_compatible[compat] = (tmp[3] << 8) | tmp[2]; + compat++; + size = 0; + } + break; + case _STAG_IRQ: + if (size < 2 || size > 3) + goto __skip; + isapnp_add_irq_resource(dev, &res, dependent, size); + size = 0; + break; + case _STAG_DMA: + if (size != 2) + goto __skip; + isapnp_add_dma_resource(dev, &res, dependent, size); + size = 0; + break; + case _STAG_STARTDEP: + if (size > 1) + goto __skip; + res = NULL; + dependent = 0x100 | ISAPNP_RES_PRIORITY_ACCEPTABLE; + if (size > 0) { + isapnp_peek(tmp, size); + dependent = 0x100 | tmp[0]; + size = 0; + } + break; + case _STAG_ENDDEP: + if (size != 0) + goto __skip; + res = NULL; + dependent = 0; + break; + case _STAG_IOPORT: + if (size != 7) + goto __skip; + isapnp_add_port_resource(dev, &res, dependent, size); + size = 0; + break; + case _STAG_FIXEDIO: + if (size != 3) + goto __skip; + isapnp_add_fixed_port_resource(dev, &res, dependent, size); + size = 0; + break; + case _STAG_VENDOR: + break; + case _LTAG_MEMRANGE: + if (size != 9) + goto __skip; + isapnp_add_mem_resource(dev, &res, dependent, size); + size = 0; + break; + case _LTAG_ANSISTR: + if (dev->name[0] == '\0') { + unsigned short size1 = size > 47 ? 47 : size; + isapnp_peek(dev->name, size1); + dev->name[size1] = '\0'; + size -= size1; + } + break; + case _LTAG_UNICODESTR: + /* silently ignore */ + /* who use unicode for hardware identification? */ + break; + case _LTAG_VENDOR: + break; + case _LTAG_MEM32RANGE: + if (size != 17) + goto __skip; + isapnp_add_mem32_resource(dev, &res, dependent, size); + size = 0; + break; + case _LTAG_FIXEDMEM32RANGE: + if (size != 17) + goto __skip; + isapnp_add_fixed_mem32_resource(dev, &res, dependent, size); + size = 0; + break; + case _STAG_END: + if (size > 0) + isapnp_skip_bytes(size); + return 1; + default: + printk("isapnp: unexpected or unknown tag type 0x%x for logical device %i (device %i), ignored\n", type, dev->devfn, card->number); + } + __skip: + if (size > 0) + isapnp_skip_bytes(size); + } + isapnp_config_prepare(dev); + return 0; +} + +/* + * Parse resource map for ISA PnP card. + */ + +static void __init isapnp_parse_resource_map(struct pci_bus *card) +{ + unsigned char type, tmp[17]; + unsigned short size; + + while (1) { + if (isapnp_read_tag(&type, &size)<0) + return; + switch (type) { + case _STAG_PNPVERNO: + if (size != 2) + goto __skip; + isapnp_peek(tmp, 2); + card->pnpver = tmp[0]; + card->productver = tmp[1]; + size = 0; + break; + case _STAG_LOGDEVID: + if (size >= 5 && size <= 6) { + if (isapnp_create_device(card, size)==1) + return; + size = 0; + } + break; + case _STAG_VENDOR: + break; + case _LTAG_ANSISTR: + if (card->name[0] == '\0') { + unsigned short size1 = size > 47 ? 47 : size; + isapnp_peek(card->name, size1); + card->name[size1] = '\0'; + size -= size1; + } + break; + case _LTAG_UNICODESTR: + /* silently ignore */ + /* who use unicode for hardware identification? */ + break; + case _LTAG_VENDOR: + break; + case _STAG_END: + if (size > 0) + isapnp_skip_bytes(size); + return; + default: + printk("isapnp: unexpected or unknown tag type 0x%x for device %i, ignored\n", type, card->number); + } + __skip: + if (size > 0) + isapnp_skip_bytes(size); + } +} + +/* + * Compute ISA PnP checksum for first eight bytes. + */ + +static unsigned char __init isapnp_checksum(unsigned char *data) +{ + int i, j; + unsigned char checksum = 0x6a, bit, b; + + for (i = 0; i < 8; i++) { + b = data[i]; + for (j = 0; j < 8; j++) { + bit = 0; + if (b & (1 << j)) + bit = 1; + checksum = ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit) << 7) | (checksum >> 1); + } + } + return checksum; +} + +/* + * Build device list for all present ISA PnP devices. + */ + +static int __init isapnp_build_device_list(void) +{ + int csn; + unsigned char header[9], checksum; + struct pci_bus *card, *prev = NULL; + + isapnp_wait(); + isapnp_key(); + for (csn = 1; csn <= 10; csn++) { + isapnp_wake(csn); + isapnp_peek(header, 9); + checksum = isapnp_checksum(header); +#if 0 + printk("vendor: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + header[0], header[1], header[2], header[3], + header[4], header[5], header[6], header[7], header[8]); + printk("checksum = 0x%x\n", checksum); +#endif + if (checksum == 0x00 || checksum != header[8]) /* not valid CSN */ + continue; + if ((card = isapnp_alloc(sizeof(struct pci_bus))) == NULL) + continue; + card->number = csn; + card->vendor = (header[1] << 8) | header[0]; + card->device = (header[3] << 8) | header[2]; + card->serial = (header[7] << 24) | (header[6] << 16) | (header[5] << 8) | header[4]; + isapnp_checksum_value = 0x00; + isapnp_parse_resource_map(card); + if (isapnp_checksum_value != 0x00) + printk("isapnp: checksum for device %i is not valid (0x%x)\n", csn, isapnp_checksum_value); + card->checksum = isapnp_checksum_value; + if (!isapnp_cards) + isapnp_cards = card; + else + prev->next = card; + prev = card; + } + return 0; +} + +/* + * Basic configuration routines. + */ + +int isapnp_present(void) +{ + if (isapnp_devices) + return 1; + return 0; +} + +int isapnp_cfg_begin(int csn, int logdev) +{ + if (csn < 1 || csn > 10 || logdev > 10) + return -EINVAL; + MOD_INC_USE_COUNT; + down(&isapnp_cfg_mutex); + isapnp_wait(); + isapnp_key(); + isapnp_wake(csn); +#if 1 /* to avoid malfunction when isapnptools is used */ + isapnp_set_rdp(); + udelay(1000); /* delay 1000us */ + write_address(0x01); + udelay(1000); /* delay 1000us */ +#endif + if (logdev >= 0) + isapnp_device(logdev); + return 0; +} + +int isapnp_cfg_end(void) +{ + isapnp_wait(); + up(&isapnp_cfg_mutex); + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Resource manager. + */ + +static struct isapnp_port *isapnp_find_port(struct pci_dev *dev, int index) +{ + struct isapnp_resources *res; + struct isapnp_port *port; + + if (!dev || index < 0 || index > 7) + return NULL; + for (res = (struct isapnp_resources *)dev->sysdata; res; res = res->next) { + for (port = res->port; port; port = port->next) { + if (!index) + return port; + index--; + } + } + return NULL; +} + +struct isapnp_irq *isapnp_find_irq(struct pci_dev *dev, int index) +{ + struct isapnp_resources *res, *resa; + struct isapnp_irq *irq; + int index1, index2, index3; + + if (!dev || index < 0 || index > 7) + return NULL; + for (res = (struct isapnp_resources *)dev->sysdata; res; res = res->next) { + index3 = 0; + for (resa = res; resa; resa = resa->alt) { + index1 = index; + index2 = 0; + for (irq = resa->irq; irq; irq = irq->next) { + if (!index1) + return irq; + index1--; + index2++; + } + if (index3 < index2) + index3 = index2; + } + index -= index3; + } + return NULL; +} + +struct isapnp_dma *isapnp_find_dma(struct pci_dev *dev, int index) +{ + struct isapnp_resources *res; + struct isapnp_dma *dma; + + if (!dev || index < 0 || index > 7) + return NULL; + for (res = (struct isapnp_resources *)dev->sysdata; res; res = res->next) { + for (dma = res->dma; dma; dma = dma->next) { + if (!index) + return dma; + index--; + } + } + return NULL; +} + +struct isapnp_mem *isapnp_find_mem(struct pci_dev *dev, int index) +{ + struct isapnp_resources *res; + struct isapnp_mem *mem; + + if (!dev || index < 0 || index > 7) + return NULL; + for (res = (struct isapnp_resources *)dev->sysdata; res; res = res->next) { + for (mem = res->mem; mem; mem = mem->next) { + if (!index) + return mem; + index--; + } + } + return NULL; +} + +struct isapnp_mem32 *isapnp_find_mem32(struct pci_dev *dev, int index) +{ + struct isapnp_resources *res; + struct isapnp_mem32 *mem32; + + if (!dev || index < 0 || index > 7) + return NULL; + for (res = (struct isapnp_resources *)dev->sysdata; res; res = res->next) { + for (mem32 = res->mem32; mem32; mem32 = mem32->next) { + if (!index) + return mem32; + index--; + } + } + return NULL; +} + +/* + * Device manager. + */ + +struct pci_bus *isapnp_find_card(unsigned short vendor, + unsigned short device, + struct pci_bus *from) +{ + struct pci_bus *card; + + if (from == NULL) { + from = isapnp_cards; + } else { + from = from->next; + } + for (card = from; card; card = card->next) { + if (card->vendor == vendor && card->device == device) + return card; + } + return NULL; +} + +struct pci_dev *isapnp_find_dev(struct pci_bus *card, + unsigned short vendor, + unsigned short function, + struct pci_dev *from) +{ + struct pci_dev *dev; + int idx; + + if (card == NULL) { /* look for a logical device from all cards */ + if (from == NULL) { + from = isapnp_devices; + } else { + from = from->next; + } + for (dev = from; dev; dev = dev->next) { + if (dev->vendor == vendor && dev->device == function) + return dev; + for (idx = 0; idx < DEVICE_COUNT_COMPATIBLE; idx++) + if (dev->vendor_compatible[idx] == vendor && + dev->device_compatible[idx] == function) + return dev; + } + } else { + if (from == NULL) { + from = card->devices; + } else { + from = from->next; + } + if (from->bus != card) /* something is wrong */ + return NULL; + for (dev = from; dev; dev = dev->sibling) { + if (dev->vendor == vendor && dev->device == function) + return dev; + for (idx = 0; idx < DEVICE_COUNT_COMPATIBLE; idx++) + if (dev->vendor_compatible[idx] == vendor && + dev->device_compatible[idx] == function) + return dev; + } + } + return NULL; +} + +static int isapnp_config_prepare(struct pci_dev *dev) +{ + struct isapnp_resources *res, *resa; + struct isapnp_port *port; + struct isapnp_irq *irq; + struct isapnp_dma *dma; + struct isapnp_mem *mem; + int port_count, port_count1; + int irq_count, irq_count1; + int dma_count, dma_count1; + int mem_count, mem_count1; + int idx; + + if (dev == NULL) + return -EINVAL; + if (dev->active || dev->ro) + return -EBUSY; + dev->irq = dev->irq2 = DEVICE_IRQ_NOTSET; + dev->irq_flags = dev->irq2_flags = 0; + for (idx = 0; idx < DEVICE_COUNT_DMA; idx++) { + dev->dma[idx] = DEVICE_DMA_NOTSET; + dev->dma_type[idx] = DEVICE_DMA_TYPE_8AND16BIT; + dev->dma_flags[idx] = 0; + dev->dma_speed[idx] = DEVICE_DMA_SPEED_COMPATIBLE; + } + for (idx = 0; idx < DEVICE_COUNT_RESOURCE; idx++) { + dev->resource[idx].name = NULL; + dev->resource[idx].start = DEVICE_IO_NOTSET; + dev->resource[idx].end = 0; + dev->resource[idx].fixed = 0; + dev->resource[idx].bits = 12; + dev->resource[idx].hw_flags = 0; + dev->resource[idx].type = DEVICE_IO_TYPE_8AND16BIT; + } + port_count = irq_count = dma_count = mem_count = 0; + for (res = (struct isapnp_resources *)dev->sysdata; res; res = res->next) { + port_count1 = irq_count1 = dma_count1 = mem_count1 = 0; + for (resa = res; resa; resa = resa->alt) { + for (port = resa->port, idx = 0; port; port = port->next, idx++) { + if (dev->resource[port_count + idx].start == DEVICE_IO_NOTSET) { + dev->resource[port_count + idx].start = DEVICE_IO_AUTO; + dev->resource[port_count + idx].end = port->size; + dev->resource[port_count + idx].bits = port->flags & ISAPNP_PORT_FLAG_16BITADDR ? 16 : 12; + dev->resource[port_count + idx].fixed = port->flags & ISAPNP_PORT_FLAG_FIXED ? 1 : 0; + } + } + if (port_count1 < idx) + port_count1 = idx; + for (irq = resa->irq, idx = 0; irq; irq = irq->next, idx++) { + if (irq_count + idx == 0) { + if (dev->irq == DEVICE_IRQ_NOTSET) { + dev->irq = DEVICE_IRQ_AUTO; + dev->irq_flags = irq->flags; + } + } else if (irq_count + idx == 1) { + if (dev->irq2 == DEVICE_IRQ_NOTSET) { + dev->irq2 = DEVICE_IRQ_AUTO; + dev->irq2_flags = irq->flags; + } + } + + } + if (irq_count1 < idx) + irq_count1 = idx; + for (dma = resa->dma, idx = 0; dma; dma = dma->next, idx++) + if (dev->dma[idx] == DEVICE_DMA_NOTSET) { + dev->dma[idx] = DEVICE_DMA_AUTO; + dev->dma_type[idx] = dma->type; + dev->dma_flags[idx] = dma->flags; + dev->dma_speed[idx] = dma->speed; + } + if (dma_count1 < idx) + dma_count1 = idx; + for (mem = resa->mem, idx = 0; mem; mem = mem->next, idx++) + if (dev->resource[mem_count + idx + 8].start == DEVICE_IO_AUTO) { + dev->resource[mem_count + idx].start = DEVICE_IO_AUTO; + dev->resource[mem_count + idx].end = mem->size; + dev->resource[mem_count + idx].bits = 24; + dev->resource[mem_count + idx].fixed = 0; + dev->resource[mem_count + idx].hw_flags = mem->flags; + dev->resource[mem_count + idx].type = mem->type; + } + if (mem_count1 < idx) + mem_count1 = idx; + } + port_count += port_count1; + irq_count += irq_count1; + dma_count += dma_count1; + mem_count += mem_count1; + } + return 0; +} + +struct isapnp_cfgtmp { + struct isapnp_port *port[8]; + struct isapnp_irq *irq[2]; + struct isapnp_dma *dma[2]; + struct isapnp_mem *mem[4]; + struct pci_dev *request; + struct pci_dev result; +}; + +static int isapnp_alternative_switch(struct isapnp_cfgtmp *cfg, + struct isapnp_resources *from, + struct isapnp_resources *to) +{ + int tmp, tmp1; + struct isapnp_port *port; + struct isapnp_irq *irq; + struct isapnp_dma *dma; + struct isapnp_mem *mem; + + if (!cfg) + return -EINVAL; + /* process port settings */ + for (tmp = 0; tmp < 8; tmp++) { + if (cfg->request->resource[tmp].start != DEVICE_IO_AUTO) + continue; /* don't touch */ + port = cfg->port[tmp]; + if (!port) { + cfg->port[tmp] = port = isapnp_find_port(cfg->request, tmp); + if (!port) + return -EINVAL; + } + if (from && port->res == from) { + while (port->res != to) { + if (!port->res->alt) + return -EINVAL; + port = port->res->alt->port; + for (tmp1 = tmp; tmp1 > 0 && port; tmp1--) + port = port->next; + cfg->port[tmp] = port; + cfg->result.resource[tmp].start = DEVICE_IO_AUTO; + if (!port) + return -ENOENT; + } + } + } + /* process irq settings */ + for (tmp = 0; tmp < 2; tmp++) { + if (tmp == 0) { + if (cfg->request->irq != DEVICE_IRQ_AUTO) + continue; /* don't touch */ + } else { + if (cfg->request->irq2 != DEVICE_IRQ_AUTO) + continue; /* don't touch */ + } + irq = cfg->irq[tmp]; + if (!irq) { + cfg->irq[tmp] = irq = isapnp_find_irq(cfg->request, tmp); + if (!irq) + return -EINVAL; + } + if (from && irq->res == from) { + while (irq->res != to) { + if (!irq->res->alt) + return -EINVAL; + irq = irq->res->alt->irq; + for (tmp1 = tmp; tmp1 > 0 && irq; tmp1--) + irq = irq->next; + cfg->irq[tmp] = irq; + if (tmp == 0) { + cfg->result.irq = DEVICE_IRQ_AUTO; + } else { + cfg->result.irq2 = DEVICE_IRQ_AUTO; + } + if (!irq) + return -ENOENT; + } + } + } + /* process dma settings */ + for (tmp = 0; tmp < 2; tmp++) { + if (cfg->request->dma[tmp] != DEVICE_DMA_AUTO) + continue; /* don't touch */ + dma = cfg->dma[tmp]; + if (!dma) { + cfg->dma[tmp] = dma = isapnp_find_dma(cfg->request, tmp); + if (!dma) + return -EINVAL; + } + if (from && dma->res == from) { + while (dma->res != to) { + if (!dma->res->alt) + return -EINVAL; + dma = dma->res->alt->dma; + for (tmp1 = tmp; tmp1 > 0 && dma; tmp1--) + dma = dma->next; + cfg->dma[tmp] = dma; + cfg->result.dma[tmp] = DEVICE_DMA_AUTO; + if (!dma) + return -ENOENT; + } + } + } + /* process memory settings */ + for (tmp = 0; tmp < 4; tmp++) { + if (cfg->request->resource[tmp + 8].start != DEVICE_IO_AUTO) + continue; /* don't touch */ + mem = cfg->mem[tmp]; + if (!mem) { + cfg->mem[tmp] = mem = isapnp_find_mem(cfg->request, tmp); + if (!mem) + return -EINVAL; + } + if (from && mem->res == from) { + while (mem->res != to) { + if (!mem->res->alt) + return -EINVAL; + mem = mem->res->alt->mem; + for (tmp1 = tmp; tmp1 > 0 && mem; tmp1--) + mem = mem->next; + cfg->mem[tmp] = mem; + cfg->result.resource[tmp + 8].start = DEVICE_IO_AUTO; + if (!mem) + return -ENOENT; + } + } + } + return 0; +} + +static int isapnp_check_port(struct isapnp_cfgtmp *cfg, int port, int size, int idx) +{ + int i, tmp, rport, rsize; + struct isapnp_port *xport; + struct pci_dev *dev; + + if (check_region(port, size)) + return 1; + for (i = 0; i < 8; i++) { + rport = isapnp_reserve_io[i << 1]; + rsize = isapnp_reserve_io[(i << 1) + 1]; + if (port >= rport && port < rport + rsize) + return 1; + if (port + size > rport && port + size < (rport + rsize) - 1) + return 1; + } + for (dev = isapnp_devices; dev; dev = dev->next) { + if (dev->active) { + for (tmp = 0; tmp < 8; tmp++) { + if (dev->resource[tmp].start != DEVICE_IO_NOTSET) { + rport = dev->resource[tmp].start; + rsize = (dev->resource[tmp].end - rport) + 1; + if (port >= rport && port < rport + rsize) + return 1; + if (port + size > rport && port + size < (rport + rsize) - 1) + return 1; + } + } + } + } + for (i = 0; i < 8; i++) { + if (i == idx) + continue; + tmp = cfg->request->resource[i].start; + if (tmp == DEVICE_IO_NOTSET) + continue; + if (tmp == DEVICE_IO_AUTO) { /* auto */ + xport = cfg->port[i]; + if (!xport) + return 1; + tmp = cfg->result.resource[i].start; + if (tmp == DEVICE_IO_AUTO) + continue; + if (tmp + xport->size >= port && tmp <= port + xport->size) + return 1; + continue; + } + if (port == tmp) + return 1; + xport = isapnp_find_port(cfg->request, i); + if (!xport) + return 1; + if (tmp + xport->size >= port && tmp <= port + xport->size) + return 1; + } + return 0; +} + +static int isapnp_valid_port(struct isapnp_cfgtmp *cfg, int idx) +{ + int err; + unsigned long *value; + struct isapnp_port *port; + + if (!cfg || idx < 0 || idx > 7) + return -EINVAL; + if (cfg->result.resource[idx].start != DEVICE_IO_AUTO) /* don't touch */ + return 0; + __again: + port = cfg->port[idx]; + if (!port) + return -EINVAL; + value = &cfg->result.resource[idx].start; + if (*value == DEVICE_IO_AUTO) { + if (!isapnp_check_port(cfg, *value = port->min, port->size, idx)) + return 0; + } + do { + *value += port->align; + if (*value > port->max || !port->align) { + if (port->res && port->res->alt) { + if ((err = isapnp_alternative_switch(cfg, port->res, port->res->alt))<0) + return err; + goto __again; + } + return -ENOENT; + } + } while (isapnp_check_port(cfg, *value, port->size, idx)); + return 0; +} + +static void isapnp_test_handler(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int isapnp_check_interrupt(struct isapnp_cfgtmp *cfg, int irq, int idx) +{ + int i; + struct pci_dev *dev; + + if (irq < 0 || irq > 15) + return 1; + for (i = 0; i < 16; i++) { + if (isapnp_reserve_irq[i] == irq) + return 1; + } + for (dev = isapnp_devices; dev; dev = dev->next) { + if (dev->active) { + if (dev->irq == irq || dev->irq2 == irq) + return 1; + } + } + if (request_irq(irq, isapnp_test_handler, SA_INTERRUPT, "isapnp", NULL)) + return 1; + free_irq(irq, NULL); + if (idx != 0) { + if (cfg->result.irq != DEVICE_IRQ_AUTO && + cfg->result.irq != DEVICE_IRQ_NOTSET) + if (cfg->result.irq == irq) + return 1; + } + if (idx != 1) { + if (cfg->result.irq2 != DEVICE_IRQ_AUTO && + cfg->result.irq2 != DEVICE_IRQ_NOTSET) + if (cfg->result.irq2 == irq) + return 1; + } + return 0; +} + +static int isapnp_valid_irq(struct isapnp_cfgtmp *cfg, int idx) +{ + /* IRQ priority: table is good for i386 */ + static unsigned short xtab[16] = { + 5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2 + }; + int err, i; + unsigned int *value; + struct isapnp_irq *irq; + + if (!cfg || idx < 0 || idx > 1) + return -EINVAL; + if (idx == 0) { + if (cfg->result.irq != DEVICE_IRQ_AUTO) /* don't touch */ + return 0; + } else { + if (cfg->result.irq2 != DEVICE_IRQ_AUTO) /* don't touch */ + return 0; + } + __again: + irq = cfg->irq[idx]; + if (!irq) + return -EINVAL; + if (idx == 0) { + value = &cfg->result.irq; + } else { + value = &cfg->result.irq2; + } + if (*value == DEVICE_IRQ_AUTO) { + for (i = 0; i < 16 && !(irq->map & (1<= 16) + return -ENOENT; + if (!isapnp_check_interrupt(cfg, *value = xtab[i], idx)) + return 0; + } + do { + for (i = 0; i < 16 && xtab[i] != *value; i++); + for (i++; i < 16 && !(irq->map & (1<= 16) { + if (irq->res && irq->res->alt) { + if ((err = isapnp_alternative_switch(cfg, irq->res, irq->res->alt))<0) + return err; + goto __again; + } + return -ENOENT; + } else { + *value = xtab[i]; + } + } while (isapnp_check_interrupt(cfg, *value, idx)); + return 0; +} + +static int isapnp_check_dma(struct isapnp_cfgtmp *cfg, int dma, int idx) +{ + int i; + struct pci_dev *dev; + + if (dma < 0 || dma == 4 || dma > 7) + return 1; + for (i = 0; i < 8; i++) { + if (isapnp_reserve_dma[i] == dma) + return 1; + } + for (dev = isapnp_devices; dev; dev = dev->next) { + if (dev->active) { + if (dev->dma[0] == dma || dev->dma[1] == dma) + return 1; + } + } + if (request_dma(dma, "isapnp")) + return 1; + free_dma(dma); + for (i = 0; i < 2; i++) { + if (i == idx) + continue; + if (cfg->result.dma[i] == DEVICE_DMA_NOTSET || + cfg->result.dma[i] == DEVICE_DMA_AUTO) + continue; + if (cfg->result.dma[i] == dma) + return 1; + } + return 0; +} + +static int isapnp_valid_dma(struct isapnp_cfgtmp *cfg, int idx) +{ + int err, i; + unsigned char *value; + struct isapnp_dma *dma; + + if (!cfg || idx < 0 || idx > 1) + return -EINVAL; + if (cfg->result.dma[idx] != DEVICE_DMA_AUTO) /* don't touch */ + return 0; + __again: + dma = cfg->dma[idx]; + if (!dma) + return -EINVAL; + value = &cfg->result.dma[idx]; + if (*value == DEVICE_DMA_AUTO) { + for (i = 0; i < 8 && !(dma->map & (1<= 8) + return -ENOENT; + if (!isapnp_check_dma(cfg, *value = i, idx)) + return 0; + } + do { + for (i = *value + 1; i < 8 && !(dma->map & (1<= 8) { + if (dma->res && dma->res->alt) { + if ((err = isapnp_alternative_switch(cfg, dma->res, dma->res->alt))<0) + return err; + goto __again; + } + return -ENOENT; + } else { + *value = i; + } + } while (isapnp_check_dma(cfg, *value, idx)); + return 0; +} + +static int isapnp_check_mem(struct isapnp_cfgtmp *cfg, unsigned int addr, unsigned int size, int idx) +{ + int i, tmp; + unsigned int raddr, rsize; + struct pci_dev *dev; + struct isapnp_mem *xmem; + + for (i = 0; i < 8; i++) { + raddr = (unsigned int)isapnp_reserve_mem[i << 1]; + rsize = (unsigned int)isapnp_reserve_mem[(i << 1) + 1]; + if (addr >= raddr && addr < raddr + rsize) + return 1; + if (addr + size > raddr && addr + size < (raddr + rsize) - 1) + return 1; + if (__check_region(&iomem_resource, addr, size)) + return 1; + } + for (dev = isapnp_devices; dev; dev = dev->next) { + if (dev->active) { + for (tmp = 0; tmp < 4; tmp++) { + if (dev->resource[tmp].start != DEVICE_IO_NOTSET) { + raddr = dev->resource[tmp + 8].start; + rsize = (dev->resource[tmp + 8].end - raddr) + 1; + if (addr >= raddr && addr < raddr + rsize) + return 1; + if (addr + size > raddr && addr + size < (raddr + rsize) - 1) + return 1; + } + } + } + } + for (i = 0; i < 4; i++) { + if (i == idx) + continue; + tmp = cfg->request->resource[i + 8].start; + if (tmp == DEVICE_IO_NOTSET) + continue; + if (tmp == DEVICE_IO_AUTO) { /* auto */ + xmem = cfg->mem[i]; + if (!xmem) + return 1; + tmp = cfg->result.resource[i + 8].start; + if (tmp == DEVICE_IO_AUTO) + continue; + if (tmp + xmem->size >= addr && tmp <= addr + xmem->size) + return 1; + continue; + } + if (addr == tmp) + return 1; + xmem = isapnp_find_mem(cfg->request, i); + if (!xmem) + return 1; + if (tmp + xmem->size >= addr && tmp <= addr + xmem->size) + return 1; + } + return 0; +} + +static int isapnp_valid_mem(struct isapnp_cfgtmp *cfg, int idx) +{ + int err; + unsigned long *value; + struct isapnp_mem *mem; + + if (!cfg || idx < 0 || idx > 3) + return -EINVAL; + if (cfg->result.resource[idx + 8].start != DEVICE_IO_AUTO) /* don't touch */ + return 0; + __again: + mem = cfg->mem[idx]; + if (!mem) + return -EINVAL; + value = &cfg->result.resource[idx].start; + if (*value == DEVICE_IO_AUTO) { + *value = mem->min; + if (!isapnp_check_mem(cfg, *value, mem->size, idx)) + return 0; + } + do { + *value += mem->align; + if (*value >= 8 || !mem->align) { + if (mem->res && mem->res->alt) { + if ((err = isapnp_alternative_switch(cfg, mem->res, mem->res->alt))<0) + return err; + goto __again; + } + return -ENOENT; + } + } while (isapnp_check_mem(cfg, *value, mem->size, idx)); + return 0; +} + +static int isapnp_check_valid(struct isapnp_cfgtmp *cfg) +{ + int tmp; + + for (tmp = 0; tmp < 8; tmp++) + if (cfg->result.resource[tmp].start == DEVICE_IO_AUTO) + return -EAGAIN; + if (cfg->result.irq == DEVICE_IRQ_AUTO) + return -EAGAIN; + if (cfg->result.irq2 == DEVICE_IRQ_AUTO) + return -EAGAIN; + for (tmp = 0; tmp < 2; tmp++) + if (cfg->result.dma[tmp] == DEVICE_DMA_AUTO) + return -EAGAIN; + for (tmp = 0; tmp < 4; tmp++) + if (cfg->result.resource[tmp + 1].start == DEVICE_IO_AUTO) + return -EAGAIN; + return 0; +} + +static int isapnp_config_activate(struct pci_dev *dev) +{ + struct isapnp_cfgtmp cfg; + int tmp, fauto, err; + + if (!dev) + return -EINVAL; + if (dev->active) + return -EBUSY; + memset(&cfg, 0, sizeof(cfg)); + cfg.request = dev; + memcpy(&cfg.result, dev, sizeof(struct pci_dev)); + /* check if all values are set, otherwise try auto-configuration */ + for (tmp = fauto = 0; !fauto && tmp < 8; tmp++) { + if (dev->resource[tmp].start == DEVICE_IO_AUTO) + fauto++; + } + if (dev->irq == DEVICE_IRQ_AUTO) + fauto++; + if (dev->irq2 == DEVICE_IRQ_AUTO) + fauto++; + for (tmp = 0; !fauto && tmp < 2; tmp++) { + if (dev->dma[tmp] == DEVICE_DMA_AUTO) + fauto++; + } + for (tmp = 0; !fauto && tmp < 4; tmp++) { + if (dev->resource[tmp + 8].start == DEVICE_IO_AUTO) + fauto++; + } + if (!fauto) + goto __skip_auto; + /* set variables to initial values */ + if ((err = isapnp_alternative_switch(&cfg, NULL, NULL))<0) + return err; + /* find first valid configuration */ + fauto = 0; + do { + for (tmp = 0; tmp < 8 && cfg.result.resource[tmp].start != DEVICE_IO_NOTSET; tmp++) + if ((err = isapnp_valid_port(&cfg, tmp))<0) + return err; + if (cfg.result.irq != DEVICE_IRQ_NOTSET) + if ((err = isapnp_valid_irq(&cfg, 0))<0) + return err; + if (cfg.result.irq2 != DEVICE_IRQ_NOTSET) + if ((err = isapnp_valid_irq(&cfg, 1))<0) + return err; + for (tmp = 0; tmp < 2 && tmp < cfg.result.dma[tmp] != DEVICE_DMA_NOTSET; tmp++) + if ((err = isapnp_valid_dma(&cfg, tmp))<0) + return err; + for (tmp = 0; tmp < 4 && tmp < cfg.result.resource[tmp + 8].start != DEVICE_IO_NOTSET; tmp++) + if ((err = isapnp_valid_mem(&cfg, tmp))<0) + return err; + } while (isapnp_check_valid(&cfg)<0 && fauto++ < 20); + if (fauto >= 20) + return -EAGAIN; + __skip_auto: + /* we have valid configuration, try configure hardware */ + isapnp_cfg_begin(dev->bus->number, dev->devfn); + dev->active = 1; + dev->irq = cfg.result.irq; + dev->irq2 = cfg.result.irq2; + dev->dma[0] = cfg.result.dma[0]; + dev->dma[1] = cfg.result.dma[1]; + for (tmp = 0; tmp < 12; tmp++) { + dev->resource[tmp].start = cfg.result.resource[tmp].start; + if (cfg.result.resource[tmp].start != DEVICE_IO_NOTSET && + cfg.result.resource[tmp].end != DEVICE_IO_AUTO) + dev->resource[tmp].end += cfg.result.resource[tmp].start; + } + for (tmp = 0; tmp < 8 && dev->resource[tmp].start != DEVICE_IO_NOTSET; tmp++) + isapnp_write_word(ISAPNP_CFG_PORT+(tmp<<1), dev->resource[tmp].start); + if (dev->irq != DEVICE_IRQ_NOTSET) { + if (dev->irq == 2) + dev->irq = 9; + isapnp_write_byte(ISAPNP_CFG_IRQ+(0<<1), dev->irq); + } + if (dev->irq2 != DEVICE_IRQ_NOTSET) { + if (dev->irq2 == 2) + dev->irq2 = 9; + isapnp_write_byte(ISAPNP_CFG_IRQ+(1<<1), dev->irq2); + } + for (tmp = 0; tmp < 2 && dev->dma[tmp] != DEVICE_DMA_NOTSET; tmp++) + isapnp_write_byte(ISAPNP_CFG_DMA+tmp, dev->dma[tmp]); + for (tmp = 0; tmp < 4 && dev->resource[tmp].start != DEVICE_IO_NOTSET; tmp++) + isapnp_write_word(ISAPNP_CFG_MEM+(tmp<<2), (dev->resource[tmp + 8].start >> 8) & 0xffff); + isapnp_activate(dev->devfn); + isapnp_cfg_end(); + return 0; +} + +static int isapnp_config_deactivate(struct pci_dev *dev) +{ + if (!dev || !dev->active) + return -EINVAL; + isapnp_cfg_begin(dev->bus->number, dev->devfn); + isapnp_deactivate(dev->devfn); + dev->activate = 0; + isapnp_cfg_end(); + return 0; +} + +/* + * Inititialization. + */ + +#ifdef MODULE + +static void isapnp_free_port(struct isapnp_port *port) +{ + struct isapnp_port *next; + + while (port) { + next = port->next; + kfree(port); + port = next; + } +} + +static void isapnp_free_irq(struct isapnp_irq *irq) +{ + struct isapnp_irq *next; + + while (irq) { + next = irq->next; + kfree(irq); + irq = next; + } +} + +static void isapnp_free_dma(struct isapnp_dma *dma) +{ + struct isapnp_dma *next; + + while (dma) { + next = dma->next; + kfree(dma); + dma = next; + } +} + +static void isapnp_free_mem(struct isapnp_mem *mem) +{ + struct isapnp_mem *next; + + while (mem) { + next = mem->next; + kfree(mem); + mem = next; + } +} + +static void isapnp_free_mem32(struct isapnp_mem32 *mem32) +{ + struct isapnp_mem32 *next; + + while (mem32) { + next = mem32->next; + kfree(mem32); + mem32 = next; + } +} + +static void isapnp_free_resources(struct isapnp_resources *resources, int alt) +{ + struct isapnp_resources *next; + + while (resources) { + next = alt ? resources->alt : resources->next; + isapnp_free_port(resources->port); + isapnp_free_irq(resources->irq); + isapnp_free_dma(resources->dma); + isapnp_free_mem(resources->mem); + isapnp_free_mem32(resources->mem32); + if (!alt && resources->alt) + isapnp_free_resources(resources->alt, 1); + kfree(resources); + resources = next; + } +} + +static void isapnp_free_device(struct pci_dev *dev) +{ + struct pci_dev *next; + + while (dev) { + next = dev->next; + isapnp_free_resources((struct isapnp_resources *)dev->sysdata, 0); + kfree(dev); + dev = next; + } +} + +#endif /* MODULE */ + +static void isapnp_free_all_resources(void) +{ +#ifdef MODULE + struct pci_bus *card, *cardnext; +#endif + +#ifdef ISAPNP_REGION_OK + release_resource(pidxr_res); +#endif + release_resource(pnpwrp_res); + if (isapnp_rdp >= 0x203 && isapnp_rdp <= 0x3ff) + release_resource(isapnp_rdp_res); +#ifdef MODULE + for (card = isapnp_cards; card; card = cardnext) { + cardnext = card->next; + isapnp_free_device(card->devices); + kfree(card); + } +#ifdef CONFIG_PROC_FS + isapnp_proc_done(); +#endif +#endif +} + +static int __init isapnp_do_reserve_irq(int irq) +{ + int i; + + if (irq < 0 || irq > 15) + return -EINVAL; + for (i = 0; i < 16; i++) { + if (isapnp_reserve_irq[i] == irq) + return 0; + } + for (i = 0; i < 16; i++) { + if (isapnp_reserve_irq[i] < 0) { + isapnp_reserve_irq[i] = irq; +#ifdef ISAPNP_DEBUG + printk("IRQ %i is reserved now.\n", irq); +#endif + return 0; + } + } + return -ENOMEM; +} + +#ifdef CONFIG_PCI + +static void __init isapnp_pci_init(void) +{ + int devfn; + struct pci_dev *dev; + + for (devfn = 0; devfn < 255; devfn++) { + dev = pci_find_slot(0, devfn); + if (dev != NULL) + break; + } + if (dev == NULL) + return; + while (dev) { +#ifdef ISAPNP_DEBUG + printk("PCI: reserved IRQ: %i\n", dev->irq); +#endif + if (dev->irq > 0) + isapnp_do_reserve_irq(dev->irq); + dev = dev->next; + } +} + +#endif /* CONFIG_PCI */ + +EXPORT_SYMBOL(isapnp_present); +EXPORT_SYMBOL(isapnp_cfg_begin); +EXPORT_SYMBOL(isapnp_cfg_end); +EXPORT_SYMBOL(isapnp_read_byte); +EXPORT_SYMBOL(isapnp_read_word); +EXPORT_SYMBOL(isapnp_read_dword); +EXPORT_SYMBOL(isapnp_write_byte); +EXPORT_SYMBOL(isapnp_write_word); +EXPORT_SYMBOL(isapnp_write_dword); +EXPORT_SYMBOL(isapnp_wake); +EXPORT_SYMBOL(isapnp_device); +EXPORT_SYMBOL(isapnp_activate); +EXPORT_SYMBOL(isapnp_deactivate); +EXPORT_SYMBOL(isapnp_find_card); +EXPORT_SYMBOL(isapnp_find_dev); + +int __init isapnp_init(void) +{ + int cards; + struct pci_bus *card; + struct pci_dev *dev; + + if (isapnp_disable) { + isapnp_detected = 0; + printk("isapnp: ISA Plug & Play support disabled\n"); + return 0; + } +#ifdef ISAPNP_REGION_OK + pidxr_res=request_region(_PIDXR, 1, "isapnp index"); + if(!pidxr_res) { + printk("isapnp: Index Register 0x%x already used\n", _PIDXR); + return -EBUSY; + } +#endif + pnpwrp_res=request_region(_PNPWRP, 1, "isapnp write"); + if(!pnpwrp_res) { + printk("isapnp: Write Data Register 0x%x already used\n", _PNPWRP); + return -EBUSY; + } + if (isapnp_rdp >= 0x203 && isapnp_rdp <= 0x3ff) { + isapnp_rdp_res=request_region(isapnp_rdp, 1, "isapnp read"); + if(!isapnp_rdp_res) { + printk("isapnp: Read Data Register 0x%x already used\n", isapnp_rdp); + return -EBUSY; + } + } + isapnp_detected = 1; + if (isapnp_rdp < 0x203 || isapnp_rdp > 0x3ff) { + cards = isapnp_isolate(); + if (cards < 0 || + (isapnp_rdp < 0x203 || isapnp_rdp > 0x3ff)) { + isapnp_free_all_resources(); + isapnp_detected = 0; + printk("isapnp: No Plug & Play device found\n"); + return 0; + } + isapnp_rdp_res=request_region(isapnp_rdp, 1, "isapnp read"); + } + isapnp_build_device_list(); + cards = 0; + for (card = isapnp_cards; card; card = card->next) + cards++; + if (isapnp_verbose) { + for (card = isapnp_cards; card; card = card->next) { + printk( "isapnp: Card '%s'\n", card->name[0]?card->name:"Unknown"); + if (isapnp_verbose < 2) + continue; + for (dev = card->devices; dev; dev = dev->next) + printk("isapnp: Device '%s'\n", dev->name[0]?card->name:"Unknown"); + } + } + if (cards) { + printk("isapnp: %i Plug & Play card%s detected total\n", cards, cards>1?"s":""); + } else { + printk("isapnp: No Plug & Play card found\n"); + } +#ifdef CONFIG_PCI + if (!isapnp_skip_pci_scan) + isapnp_pci_init(); +#endif +#ifdef CONFIG_PROC_FS + isapnp_proc_init(); +#endif + return 0; +} + +#ifdef MODULE + +int init_module(void) +{ + return isapnp_init(); +} + +void cleanup_module(void) +{ + if (isapnp_detected) + isapnp_free_all_resources(); +} + +#endif diff --git a/drivers/pnp/isapnp_proc.c b/drivers/pnp/isapnp_proc.c new file mode 100644 index 000000000000..9c6f0be2a9c2 --- /dev/null +++ b/drivers/pnp/isapnp_proc.c @@ -0,0 +1,945 @@ +/* + * ISA Plug & Play support + * Copyright (c) by Jaroslav Kysela + * + * + * 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. + * + */ + +static void *isapnp_alloc(long size); +struct pci_bus *isapnp_cards; +struct pci_dev *isapnp_devices; + +struct isapnp_info_buffer { + char *buffer; /* pointer to begin of buffer */ + char *curr; /* current position in buffer */ + unsigned long size; /* current size */ + unsigned long len; /* total length of buffer */ + int stop; /* stop flag */ + int error; /* error code */ +}; + +typedef struct isapnp_info_buffer isapnp_info_buffer_t; + +static struct proc_dir_entry *isapnp_proc_entry = NULL; + +static void isapnp_info_read(isapnp_info_buffer_t *buffer); +static void isapnp_info_write(isapnp_info_buffer_t *buffer); + +int isapnp_printf(isapnp_info_buffer_t * buffer, char *fmt,...) +{ + va_list args; + int res; + char sbuffer[512]; + + if (buffer->stop || buffer->error) + return 0; + va_start(args, fmt); + res = vsprintf(sbuffer, fmt, args); + va_end(args); + if (buffer->size + res >= buffer->len) { + buffer->stop = 1; + return 0; + } + strcpy(buffer->curr, sbuffer); + buffer->curr += res; + buffer->size += res; + return res; +} + +static loff_t isapnp_info_entry_lseek(struct file *file, loff_t offset, int orig) +{ + switch (orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + return file->f_pos; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + return file->f_pos; + case 2: /* SEEK_END */ + default: + return -EINVAL; + } + return -ENXIO; +} + +static ssize_t isapnp_info_entry_read(struct file *file, char *buffer, + size_t count, loff_t * offset) +{ + isapnp_info_buffer_t *buf; + long size = 0, size1; + int mode; + + mode = file->f_flags & O_ACCMODE; + if (mode != O_RDONLY) + return -EINVAL; + buf = (isapnp_info_buffer_t *) file->private_data; + if (!buf) + return -EIO; + if (file->f_pos >= buf->size) + return 0; + size = buf->size < count ? buf->size : count; + size1 = buf->size - file->f_pos; + if (size1 < size) + size = size1; + if (copy_to_user(buffer, buf->buffer + file->f_pos, size)) + return -EFAULT; + file->f_pos += size; + return size; +} + +static ssize_t isapnp_info_entry_write(struct file *file, const char *buffer, + size_t count, loff_t * offset) +{ + isapnp_info_buffer_t *buf; + long size = 0, size1; + int mode; + + mode = file->f_flags & O_ACCMODE; + if (mode != O_WRONLY) + return -EINVAL; + buf = (isapnp_info_buffer_t *) file->private_data; + if (!buf) + return -EIO; + if (file->f_pos < 0) + return -EINVAL; + if (file->f_pos >= buf->len) + return -ENOMEM; + size = buf->len < count ? buf->len : count; + size1 = buf->len - file->f_pos; + if (size1 < size) + size = size1; + if (copy_from_user(buf->buffer + file->f_pos, buffer, size)) + return -EFAULT; + if (buf->size < file->f_pos + size) + buf->size = file->f_pos + size; + file->f_pos += size; + return size; +} + +static int isapnp_info_entry_open(struct inode *inode, struct file *file) +{ + isapnp_info_buffer_t *buffer; + int mode; + + mode = file->f_flags & O_ACCMODE; + if (mode != O_RDONLY && mode != O_WRONLY) + return -EINVAL; + buffer = (isapnp_info_buffer_t *) + isapnp_alloc(sizeof(isapnp_info_buffer_t)); + if (!buffer) + return -ENOMEM; + buffer->len = 4 * PAGE_SIZE; + buffer->buffer = vmalloc(buffer->len); + if (!buffer->buffer) { + kfree(buffer); + return -ENOMEM; + } + buffer->curr = buffer->buffer; + file->private_data = buffer; + MOD_INC_USE_COUNT; + if (mode == O_RDONLY) + isapnp_info_read(buffer); + return 0; +} + +static int isapnp_info_entry_release(struct inode *inode, struct file *file) +{ + isapnp_info_buffer_t *buffer; + int mode; + + if ((buffer = (isapnp_info_buffer_t *) file->private_data) == NULL) + return -EINVAL; + mode = file->f_flags & O_ACCMODE; + if (mode == O_WRONLY) + isapnp_info_write(buffer); + vfree(buffer->buffer); + kfree(buffer); + MOD_DEC_USE_COUNT; + return 0; +} + +static unsigned int isapnp_info_entry_poll(struct file *file, poll_table * wait) +{ + if (!file->private_data) + return 0; + return POLLIN | POLLRDNORM; +} + +static int isapnp_info_entry_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return -EINVAL; +} + +static int isapnp_info_entry_mmap(struct file *file, struct vm_area_struct *vma) +{ + return -ENXIO; +} + +static struct file_operations isapnp_info_entry_operations = +{ + isapnp_info_entry_lseek, /* lseek */ + isapnp_info_entry_read, /* read */ + isapnp_info_entry_write, /* write */ + NULL, /* readdir */ + isapnp_info_entry_poll, /* poll */ + isapnp_info_entry_ioctl, /* ioctl - default */ + isapnp_info_entry_mmap, /* mmap */ + isapnp_info_entry_open, /* open */ + NULL, /* flush */ + isapnp_info_entry_release, /* release */ + NULL, /* can't fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +static struct inode_operations isapnp_info_entry_inode_operations = +{ + &isapnp_info_entry_operations, /* default sound info directory file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +__initfunc(static int isapnp_proc_init(void)) +{ + struct proc_dir_entry *p; + + isapnp_proc_entry = NULL; + p = create_proc_entry("isapnp", S_IFREG | S_IRUGO | S_IWUSR, &proc_root); + if (!p) + return -ENOMEM; + p->ops = &isapnp_info_entry_inode_operations; + isapnp_proc_entry = p; + return 0; +} + +#ifdef MODULE +static int isapnp_proc_done(void) +{ + if (isapnp_proc_entry) + proc_unregister(&proc_root, isapnp_proc_entry->low_ino); + return 0; +} +#endif /* MODULE */ + +/* + * + */ + +static void isapnp_print_devid(isapnp_info_buffer_t *buffer, unsigned short vendor, unsigned short device) +{ + char tmp[8]; + + sprintf(tmp, "%c%c%c%x%x%x%x", + 'A' + ((vendor >> 2) & 0x3f) - 1, + 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, + 'A' + ((vendor >> 8) & 0x1f) - 1, + (device >> 4) & 0x0f, + device & 0x0f, + (device >> 12) & 0x0f, + (device >> 8) & 0x0f); + isapnp_printf(buffer, tmp); +} + +static void isapnp_print_compatible(isapnp_info_buffer_t *buffer, struct pci_dev *dev) +{ + int idx; + + for (idx = 0; idx < DEVICE_COUNT_COMPATIBLE; idx++) { + if (dev->vendor_compatible[idx] == 0) + continue; + isapnp_printf(buffer, " Compatible device "); + isapnp_print_devid(buffer, + dev->vendor_compatible[idx], + dev->device_compatible[idx]); + isapnp_printf(buffer, "\n"); + } +} + +static void isapnp_print_port(isapnp_info_buffer_t *buffer, char *space, struct isapnp_port *port) +{ + isapnp_printf(buffer, "%sPort 0x%x-0x%x, align 0x%x, size 0x%x, %i-bit address decoding\n", + space, port->min, port->max, port->align ? (port->align-1) : 0, port->size, + port->flags & ISAPNP_PORT_FLAG_16BITADDR ? 16 : 10); +} + +static void isapnp_print_irq(isapnp_info_buffer_t *buffer, char *space, struct isapnp_irq *irq) +{ + int first = 1, i; + + isapnp_printf(buffer, "%sIRQ ", space); + for (i = 0; i < 16; i++) + if (irq->map & (1<map) + isapnp_printf(buffer, ""); + if (irq->flags & DEVICE_IRQ_FLAG_HIGHEDGE) + isapnp_printf(buffer, " High-Edge"); + if (irq->flags & DEVICE_IRQ_FLAG_LOWEDGE) + isapnp_printf(buffer, " Low-Edge"); + if (irq->flags & DEVICE_IRQ_FLAG_HIGHLEVEL) + isapnp_printf(buffer, " High-Level"); + if (irq->flags & DEVICE_IRQ_FLAG_LOWLEVEL) + isapnp_printf(buffer, " Low-Level"); + isapnp_printf(buffer, "\n"); +} + +static void isapnp_print_dma(isapnp_info_buffer_t *buffer, char *space, struct isapnp_dma *dma) +{ + int first = 1, i; + char *s; + + isapnp_printf(buffer, "%sDMA ", space); + for (i = 0; i < 8; i++) + if (dma->map & (1<map) + isapnp_printf(buffer, ""); + switch (dma->type) { + case DEVICE_DMA_TYPE_8BIT: + s = "8-bit"; + break; + case DEVICE_DMA_TYPE_8AND16BIT: + s = "8-bit&16-bit"; + break; + default: + s = "16-bit"; + } + isapnp_printf(buffer, " %s", s); + if (dma->flags & DEVICE_DMA_FLAG_MASTER) + isapnp_printf(buffer, " master"); + if (dma->flags & DEVICE_DMA_FLAG_BYTE) + isapnp_printf(buffer, " byte-count"); + if (dma->flags & DEVICE_DMA_FLAG_WORD) + isapnp_printf(buffer, " word-count"); + switch (dma->speed) { + case DEVICE_DMA_SPEED_TYPEA: + s = "type-A"; + break; + case DEVICE_DMA_SPEED_TYPEB: + s = "type-B"; + break; + case DEVICE_DMA_SPEED_TYPEF: + s = "type-F"; + break; + default: + s = "compatible"; + break; + } + isapnp_printf(buffer, " %s\n", s); +} + +static void isapnp_print_mem(isapnp_info_buffer_t *buffer, char *space, struct isapnp_mem *mem) +{ + char *s; + + isapnp_printf(buffer, "%sMemory 0x%x-0x%x, align 0x%x, size 0x%x", + space, mem->min, mem->max, mem->align, mem->size); + if (mem->flags & DEVICE_IO_FLAG_WRITEABLE) + isapnp_printf(buffer, ", writeable"); + if (mem->flags & DEVICE_IO_FLAG_CACHEABLE) + isapnp_printf(buffer, ", cacheable"); + if (mem->flags & DEVICE_IO_FLAG_RANGELENGTH) + isapnp_printf(buffer, ", range-length"); + if (mem->flags & DEVICE_IO_FLAG_SHADOWABLE) + isapnp_printf(buffer, ", shadowable"); + if (mem->flags & DEVICE_IO_FLAG_EXPANSIONROM) + isapnp_printf(buffer, ", expansion ROM"); + switch (mem->type) { + case DEVICE_IO_TYPE_8BIT: + s = "8-bit"; + break; + case DEVICE_IO_TYPE_8AND16BIT: + s = "8-bit&16-bit"; + break; + default: + s = "16-bit"; + } + isapnp_printf(buffer, ", %s\n", s); +} + +static void isapnp_print_mem32(isapnp_info_buffer_t *buffer, char *space, struct isapnp_mem32 *mem32) +{ + int first = 1, i; + + isapnp_printf(buffer, "%s32-bit memory ", space); + for (i = 0; i < 17; i++) { + if (first) { + first = 0; + } else { + isapnp_printf(buffer, ":"); + } + isapnp_printf(buffer, "%02x", mem32->data[i]); + } +} + +static void isapnp_print_resources(isapnp_info_buffer_t *buffer, char *space, struct isapnp_resources *res) +{ + char *s; + struct isapnp_port *port; + struct isapnp_irq *irq; + struct isapnp_dma *dma; + struct isapnp_mem *mem; + struct isapnp_mem32 *mem32; + + switch (res->priority) { + case ISAPNP_RES_PRIORITY_PREFERRED: + s = "preferred"; + break; + case ISAPNP_RES_PRIORITY_ACCEPTABLE: + s = "acceptable"; + break; + case ISAPNP_RES_PRIORITY_FUNCTIONAL: + s = "functional"; + break; + default: + s = "invalid"; + } + isapnp_printf(buffer, "%sPriority %s\n", space, s); + for (port = res->port; port; port = port->next) + isapnp_print_port(buffer, space, port); + for (irq = res->irq; irq; irq = irq->next) + isapnp_print_irq(buffer, space, irq); + for (dma = res->dma; dma; dma = dma->next) + isapnp_print_dma(buffer, space, dma); + for (mem = res->mem; mem; mem = mem->next) + isapnp_print_mem(buffer, space, mem); + for (mem32 = res->mem32; mem32; mem32 = mem32->next) + isapnp_print_mem32(buffer, space, mem32); +} + +static void isapnp_print_configuration(isapnp_info_buffer_t *buffer, struct pci_dev *dev) +{ + int i, tmp, next; + char *space = " "; + + isapnp_cfg_begin(dev->bus->number, dev->devfn); + isapnp_printf(buffer, "%sDevice is %sactive\n", + space, isapnp_read_byte(ISAPNP_CFG_ACTIVATE)?"":"not "); + for (i = next = 0; i < 8; i++) { + tmp = isapnp_read_word(ISAPNP_CFG_PORT + (i << 1)); + if (!tmp) + continue; + if (!next) { + isapnp_printf(buffer, "%sActive port ", space); + next = 1; + } + isapnp_printf(buffer, "%s0x%x", i > 0 ? "," : "", tmp); + } + if (next) + isapnp_printf(buffer, "\n"); + for (i = next = 0; i < 2; i++) { + tmp = isapnp_read_word(ISAPNP_CFG_IRQ + (i << 1)); + if (!(tmp >> 8)) + continue; + if (!next) { + isapnp_printf(buffer, "%sActive IRQ ", space); + next = 1; + } + isapnp_printf(buffer, "%s%i", i > 0 ? "," : "", tmp >> 8); + if (tmp & 0xff) + isapnp_printf(buffer, " [0x%x]", tmp & 0xff); + } + if (next) + isapnp_printf(buffer, "\n"); + for (i = next = 0; i < 2; i++) { + tmp = isapnp_read_byte(ISAPNP_CFG_DMA + i); + if (tmp == 4) + continue; + if (!next) { + isapnp_printf(buffer, "%sActive DMA ", space); + next = 1; + } + isapnp_printf(buffer, "%s%i", i > 0 ? "," : "", tmp); + } + if (next) + isapnp_printf(buffer, "\n"); + for (i = next = 0; i < 4; i++) { + tmp = isapnp_read_dword(ISAPNP_CFG_MEM + (i << 3)); + if (!tmp) + continue; + if (!next) { + isapnp_printf(buffer, "%sActive memory ", space); + next = 1; + } + isapnp_printf(buffer, "%s0x%x", i > 0 ? "," : "", tmp); + } + if (next) + isapnp_printf(buffer, "\n"); + isapnp_cfg_end(); +} + +static void isapnp_print_device(isapnp_info_buffer_t *buffer, struct pci_dev *dev) +{ + int block, block1; + char *space = " "; + struct isapnp_resources *res, *resa; + + if (!dev) + return; + isapnp_printf(buffer, " Logical device %i '", dev->devfn); + isapnp_print_devid(buffer, dev->vendor, dev->device); + isapnp_printf(buffer, ":%s'", dev->name[0]?dev->name:"Unknown"); + isapnp_printf(buffer, "\n"); +#if 0 + isapnp_cfg_begin(dev->bus->number, dev->devfn); + for (block = 0; block < 128; block++) + if ((block % 16) == 15) + isapnp_printf(buffer, "%02x\n", isapnp_read_byte(block)); + else + isapnp_printf(buffer, "%02x:", isapnp_read_byte(block)); + isapnp_cfg_end(); +#endif + if (dev->regs) + isapnp_printf(buffer, "%sSupported registers 0x%x\n", space, dev->regs); + isapnp_print_compatible(buffer, dev); + isapnp_print_configuration(buffer, dev); + for (res = (struct isapnp_resources *)dev->sysdata, block = 0; res; res = res->next, block++) { + isapnp_printf(buffer, "%sResources %i\n", space, block); + isapnp_print_resources(buffer, " ", res); + for (resa = res->alt, block1 = 1; resa; resa = resa->alt, block1++) { + isapnp_printf(buffer, "%s Alternate resources %i:%i\n", space, block, block1); + isapnp_print_resources(buffer, " ", resa); + } + } +} + +/* + * Main read routine + */ + +static void isapnp_info_read(isapnp_info_buffer_t *buffer) +{ + struct pci_bus *card; + struct pci_dev *dev; + + for (card = isapnp_cards; card; card = card->next) { + isapnp_printf(buffer, "Card %i '", card->number); + isapnp_print_devid(buffer, card->vendor, card->device); + isapnp_printf(buffer, ":%s'", card->name[0]?card->name:"Unknown"); + if (card->pnpver) + isapnp_printf(buffer, " PnP version %x.%x", card->pnpver >> 4, card->pnpver & 0x0f); + if (card->productver) + isapnp_printf(buffer, " Product version %x.%x", card->productver >> 4, card->productver & 0x0f); + isapnp_printf(buffer,"\n"); + for (dev = card->devices; dev; dev = dev->sibling) + isapnp_print_device(buffer, dev); + } +} + +/* + * + */ + +static struct pci_bus *isapnp_info_card; +static struct pci_dev *isapnp_info_device; + +static char *isapnp_get_str(char *dest, char *src, int len) +{ + int c; + + while (*src == ' ' || *src == '\t') + src++; + if (*src == '"' || *src == '\'') { + c = *src++; + while (--len > 0 && *src && *src != c) { + *dest++ = *src++; + } + if (*src == c) + src++; + } else { + while (--len > 0 && *src && *src != ' ' && *src != '\t') { + *dest++ = *src++; + } + } + *dest = 0; + while (*src == ' ' || *src == '\t') + src++; + return src; +} + +static unsigned char isapnp_get_hex(unsigned char c) +{ + if (c >= '0' || c <= '9') + return c - '0'; + if (c >= 'a' || c <= 'f') + return (c - 'a') + 10; + if (c >= 'A' || c <= 'F') + return (c - 'A') + 10; + return 0; +} + +static unsigned int isapnp_parse_id(const char *id) +{ + if (strlen(id) != 7) { + printk("isapnp: wrong PnP ID\n"); + return 0; + } + return (ISAPNP_VENDOR(id[0], id[1], id[2])<<16) | + (isapnp_get_hex(id[3])<<4) | + (isapnp_get_hex(id[4])<<0) | + (isapnp_get_hex(id[5])<<12) | + (isapnp_get_hex(id[6])<<8); +} + +static int isapnp_set_card(char *line) +{ + int idx, idx1; + unsigned int id; + char index[16], value[32]; + + isapnp_info_card = NULL; + line = isapnp_get_str(index, line, sizeof(index)); + isapnp_get_str(value, line, sizeof(value)); + idx = idx1 = simple_strtoul(index, NULL, 0); + id = isapnp_parse_id(value); + isapnp_info_card = isapnp_find_card(id >> 16, id & 0xffff, NULL); + while (isapnp_info_card && idx1-- > 0) + isapnp_info_card = isapnp_find_card(id >> 16, id & 0xffff, isapnp_info_card); + if (isapnp_info_card == NULL) { + printk("isapnp: card '%s' order %i not found\n", value, idx); + return 1; + } + if (isapnp_cfg_begin(isapnp_info_card->number, -1)<0) { + printk("isapnp: configuration start sequence for device '%s' failed\n", value); + isapnp_info_card = NULL; + return 1; + } + return 0; +} + +static int isapnp_select_csn(char *line) +{ + int csn; + char index[16], value[32]; + + isapnp_info_device = NULL; + isapnp_get_str(index, line, sizeof(index)); + csn = simple_strtoul(index, NULL, 0); + for (isapnp_info_card = isapnp_cards; isapnp_info_card; isapnp_info_card = isapnp_info_card->next) + if (isapnp_info_card->number == csn) + break; + if (isapnp_info_card == NULL) { + printk("isapnp: cannot find CSN %i\n", csn); + return 1; + } + if (isapnp_cfg_begin(isapnp_info_card->number, -1)<0) { + printk("isapnp: configuration start sequence for device '%s' failed\n", value); + isapnp_info_card = NULL; + return 1; + } + return 0; +} + +static int isapnp_set_device(char *line) +{ + int idx, idx1; + unsigned int id; + char index[16], value[32]; + + line = isapnp_get_str(index, line, sizeof(index)); + isapnp_get_str(value, line, sizeof(value)); + idx = idx1 = simple_strtoul(index, NULL, 0); + id = isapnp_parse_id(value); + isapnp_info_device = isapnp_find_dev(isapnp_info_card, id >> 16, id & 0xffff, NULL); + while (isapnp_info_device && idx-- > 0) + isapnp_info_device = isapnp_find_dev(isapnp_info_card, id >> 16, id & 0xffff, isapnp_info_device); + if (isapnp_info_device == NULL) { + printk("isapnp: device '%s' order %i not found\n", value, idx); + return 1; + } + isapnp_device(isapnp_info_device->devfn); + return 0; +} + +static int isapnp_autoconfigure(void) +{ + if (isapnp_info_device == NULL) { + printk("isapnp: device is not set\n"); + return 0; + } + if (isapnp_info_device->active) + isapnp_info_device->deactivate(isapnp_info_device); + if (isapnp_info_device->prepare(isapnp_info_device) < 0) { + printk("isapnp: cannot prepare device for the activation"); + return 0; + } + if (isapnp_info_device->activate(isapnp_info_device) < 0) { + printk("isapnp: cannot activate device"); + return 0; + } + return 0; +} + +static int isapnp_set_port(char *line) +{ + int idx, port; + char index[16], value[32]; + + line = isapnp_get_str(index, line, sizeof(index)); + isapnp_get_str(value, line, sizeof(value)); + idx = simple_strtoul(index, NULL, 0); + port = simple_strtoul(value, NULL, 0); + if (idx < 0 || idx > 7) { + printk("isapnp: wrong port index %i\n", idx); + return 1; + } + if (port < 0 || port > 0xffff) { + printk("isapnp: wrong port value 0x%x\n", port); + return 1; + } + isapnp_write_word(ISAPNP_CFG_PORT + (idx << 1), port); + if (isapnp_info_device->resource[idx].start == DEVICE_IO_NOTSET) + return 0; + if (isapnp_info_device->resource[idx].start == DEVICE_IO_AUTO) { + isapnp_info_device->resource[idx].start = port; + isapnp_info_device->resource[idx].end += port - 1; + } else { + isapnp_info_device->resource[idx].end -= isapnp_info_device->resource[idx].start; + isapnp_info_device->resource[idx].start = port; + isapnp_info_device->resource[idx].end += port; + } + return 0; +} + +static int isapnp_set_irq(char *line) +{ + int idx, irq; + char index[16], value[32]; + + line = isapnp_get_str(index, line, sizeof(index)); + isapnp_get_str(value, line, sizeof(value)); + idx = simple_strtoul(index, NULL, 0); + irq = simple_strtoul(value, NULL, 0); + if (idx < 0 || idx > 1) { + printk("isapnp: wrong IRQ index %i\n", idx); + return 1; + } + if (irq == 2) + irq = 9; + if (irq < 0 || irq > 15) { + printk("isapnp: wrong IRQ value %i\n", irq); + return 1; + } + isapnp_write_byte(ISAPNP_CFG_IRQ + (idx << 1), irq); + if (idx == 0) { + if (isapnp_info_device->irq == DEVICE_IRQ_NOTSET) + return 0; + isapnp_info_device->irq = irq; + } else { + if (isapnp_info_device->irq2 == DEVICE_IRQ_NOTSET) + return 0; + isapnp_info_device->irq2 = irq; + } + return 0; +} + +static int isapnp_set_dma(char *line) +{ + int idx, dma; + char index[16], value[32]; + + line = isapnp_get_str(index, line, sizeof(index)); + isapnp_get_str(value, line, sizeof(value)); + idx = simple_strtoul(index, NULL, 0); + dma = simple_strtoul(value, NULL, 0); + if (idx < 0 || idx > 1) { + printk("isapnp: wrong DMA index %i\n", idx); + return 1; + } + if (dma < 0 || dma > 7) { + printk("isapnp: wrong DMA value %i\n", dma); + return 1; + } + isapnp_write_byte(ISAPNP_CFG_DMA + idx, dma); + if (isapnp_info_device->dma[idx] == DEVICE_DMA_NOTSET) + return 0; + isapnp_info_device->dma[idx] = dma; + return 0; +} + +static int isapnp_set_mem(char *line) +{ + int idx; + unsigned int mem; + char index[16], value[32]; + + line = isapnp_get_str(index, line, sizeof(index)); + isapnp_get_str(value, line, sizeof(value)); + idx = simple_strtoul(index, NULL, 0); + mem = simple_strtoul(value, NULL, 0); + if (idx < 0 || idx > 3) { + printk("isapnp: wrong memory index %i\n", idx); + return 1; + } + mem >>= 8; + isapnp_write_word(ISAPNP_CFG_MEM + (idx<<2), mem & 0xffff); + if (isapnp_info_device->resource[idx + 8].start == DEVICE_IO_NOTSET) + return 0; + if (isapnp_info_device->resource[idx + 8].start == DEVICE_IO_AUTO) { + isapnp_info_device->resource[idx + 8].start = mem & ~0x00ffff00; + isapnp_info_device->resource[idx + 8].end += (mem & ~0x00ffff00) - 1; + } else { + isapnp_info_device->resource[idx + 8].end -= isapnp_info_device->resource[idx + 8].start; + isapnp_info_device->resource[idx + 8].start = mem & ~0x00ffff00; + isapnp_info_device->resource[idx + 8].end += mem & ~0x00ffff00; + } + return 0; +} + +static int isapnp_poke(char *line, int what) +{ + int reg; + unsigned int val; + char index[16], value[32]; + + line = isapnp_get_str(index, line, sizeof(index)); + isapnp_get_str(value, line, sizeof(value)); + reg = simple_strtoul(index, NULL, 0); + val = simple_strtoul(value, NULL, 0); + if (reg < 0 || reg > 127) { + printk("isapnp: wrong register %i\n", reg); + return 1; + } + switch (what) { + case 1: + isapnp_write_word(reg, val); + break; + case 2: + isapnp_write_dword(reg, val); + break; + default: + isapnp_write_byte(reg, val); + break; + } + return 0; +} + +static int isapnp_decode_line(char *line) +{ + char cmd[32]; + + line = isapnp_get_str(cmd, line, sizeof(cmd)); + if (!strcmp(cmd, "card")) + return isapnp_set_card(line); + if (!strcmp(cmd, "csn")) + return isapnp_select_csn(line); + if (!isapnp_info_card) { + printk("isapnp: card is not selected\n"); + return 1; + } + if (!strncmp(cmd, "dev", 3)) + return isapnp_set_device(line); + if (!isapnp_info_device) { + printk("isapnp: device is not selected\n"); + return 1; + } + if (!strncmp(cmd, "auto", 4)) + return isapnp_autoconfigure(); + if (!strncmp(cmd, "act", 3)) { + isapnp_activate(isapnp_info_device->devfn); + isapnp_info_device->active = 1; + return 0; + } + if (!strncmp(cmd, "deact", 5)) { + isapnp_deactivate(isapnp_info_device->devfn); + isapnp_info_device->active = 0; + return 0; + } + if (!strcmp(cmd, "port")) + return isapnp_set_port(line); + if (!strcmp(cmd, "irq")) + return isapnp_set_irq(line); + if (!strcmp(cmd, "dma")) + return isapnp_set_dma(line); + if (!strncmp(cmd, "mem", 3)) + return isapnp_set_mem(line); + if (!strcmp(cmd, "poke")) + return isapnp_poke(line, 0); + if (!strcmp(cmd, "pokew")) + return isapnp_poke(line, 1); + if (!strcmp(cmd, "poked")) + return isapnp_poke(line, 2); + printk("isapnp: wrong command '%s'\n", cmd); + return 1; +} + +/* + * Main write routine + */ + +static void isapnp_info_write(isapnp_info_buffer_t *buffer) +{ + int c, idx, idx1 = 0; + char line[128]; + + if (buffer->size <= 0) + return; + isapnp_info_card = NULL; + isapnp_info_device = NULL; + for (idx = 0; idx < buffer->size; idx++) { + c = buffer->buffer[idx]; + if (c == '\n') { + line[idx1] = '\0'; + if (line[0] != '#') { + if (isapnp_decode_line(line)) + goto __end; + } + idx1 = 0; + continue; + } + if (idx1 >= sizeof(line)-1) { + printk("isapnp: line too long, aborting\n"); + return; + } + line[idx1++] = c; + } + __end: + if (isapnp_info_card) + isapnp_cfg_end(); +} diff --git a/drivers/scsi/53c7xx.c b/drivers/scsi/53c7xx.c index f35145d21825..a0a009071c69 100644 --- a/drivers/scsi/53c7xx.c +++ b/drivers/scsi/53c7xx.c @@ -1074,7 +1074,7 @@ NCR53c7x0_init (struct Scsi_Host *host) { NCR53c7x0_driver_init (host); - if (request_irq(host->irq, NCR53c7x0_intr, 0, "53c7xx", host)) + if (request_irq(host->irq, NCR53c7x0_intr, SA_SHIRQ, "53c7xx", host)) { printk("scsi%d : IRQ%d not free, detaching\n", host->host_no, host->irq); diff --git a/drivers/scsi/oktagon_esp.c b/drivers/scsi/oktagon_esp.c index 4223226a7b4d..ecfbb8778862 100644 --- a/drivers/scsi/oktagon_esp.c +++ b/drivers/scsi/oktagon_esp.c @@ -39,8 +39,6 @@ #include #include -#include - #ifdef USE_BOTTOM_HALF #include #include diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index d7150954872b..755d48bb9eba 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -35,7 +35,11 @@ #include "hosts.h" #include "constants.h" -#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) +#ifdef MODULE + #define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) +#else + #define SHUTDOWN_SIGS (0UL) +#endif #ifdef DEBUG #define SENSE_TIMEOUT SCSI_TIMEOUT diff --git a/drivers/sound/lowlevel/awe_compat-fbsd.h b/drivers/sound/lowlevel/awe_compat-fbsd.h deleted file mode 100644 index 8ebe5cab4ba7..000000000000 --- a/drivers/sound/lowlevel/awe_compat-fbsd.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * sound/awe_compat.h - * - * Compat defines for the AWE32/SB32/AWE64 wave table synth driver. - * version 0.4.3; Nov. 1, 1998 - * - * Copyright (C) 1996-1998 Takashi Iwai - * - * 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. - */ - -#ifndef AWE_COMPAT_H_DEF -#define AWE_COMPAT_H_DEF - -/*================================================================ - * version check - *================================================================*/ - -/* FreeBSD version check */ -#include - -#define AWE_OBSOLETE_VOXWARE -#if __FreeBSD__ >= 2 -# include -# if __FreeBSD_version >= 300000 -# undef AWE_OBSOLETE_VOXWARE -# endif -#endif -#ifdef __linux__ -# include -#endif - - -/*================================================================ - * INCLUDE OTHER HEADER FILES - *================================================================*/ - -/* reading configuration of sound driver */ - -#include -#ifdef AWE_OBSOLETE_VOXWARE - -#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AWE32) -#define CONFIG_AWE32_SYNTH -#endif - -#else /* AWE_OBSOLETE_VOXWARE */ - -#ifdef HAS_LOWLEVEL_H -#include "lowlevel.h" -#endif - -#if defined(CONFIGURE_SOUNDCARD) && defined(CONFIG_AWE32) -# define CONFIG_AWE32_SYNTH -#endif - -#endif /* AWE_OBSOLETE_VOXWARE */ - - -/*================================================================ - * include AWE header files - *================================================================*/ - -#if defined(CONFIG_AWE32_SYNTH) || defined(CONFIG_AWE32_SYNTH_MODULE) - -#include -#include -#include - -#ifdef AWE_HAS_GUS_COMPATIBILITY -/* include finetune table */ -#ifdef AWE_OBSOLETE_VOXWARE -# define SEQUENCER_C -#endif -#include -#include -#endif /* AWE_HAS_GUS_COMPATIBILITY */ - - -/*---------------------------------------------------------------- - * compatibility macros for AWE32 driver - *----------------------------------------------------------------*/ - -/* redefine following macros */ -#undef IOCTL_IN -#undef IOCTL_OUT -#undef OUTW -#undef COPY_FROM_USER -#undef COPY_TO_USER -#undef GET_BYTE_FROM_USER -#undef GET_SHORT_FROM_USER -#undef IOCTL_TO_USER - -/* inline is not checked yet.. maybe it'll work */ -#define INLINE /*inline*/ - -#define KERN_WARNING /**/ - -/*---------------------------------------------------------------- - * memory management for freebsd - *----------------------------------------------------------------*/ - -/* i/o requests; nothing */ -#define awe_check_port() 0 /* always false */ -#define awe_request_region() /* nothing */ -#define awe_release_region() /* nothing */ - -#define AWE_DYNAMIC_BUFFER - -#define my_malloc_init(ptr) /* nothing */ -#define my_malloc_memptr() 0 -#define my_malloc(size) malloc(size, M_TEMP, M_WAITOK) -#define my_free(ptr) if (ptr) {free(ptr, M_TEMP);} - -#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;} - -/*---------------------------------------------------------------- - * i/o interfaces for freebsd - *----------------------------------------------------------------*/ - -/* according to linux rule; the arguments are swapped */ -#define OUTW(data,addr) outw(addr, data) - -#define COPY_FROM_USER(target,source,offs,count) \ - uiomove(((caddr_t)(target)),(count),((struct uio *)(source))) -#define COPY_TO_USER(target,source,offs,count) \ - uiomove(((caddr_t)(source)),(count),((struct uio *)(target))) -#define GET_BYTE_FROM_USER(target,addr,offs) \ - uiomove(((char*)&(target)), 1, ((struct uio *)(addr))) -#define GET_SHORT_FROM_USER(target,addr,offs) \ - uiomove(((char*)&(target)), 2, ((struct uio *)(addr))) -#define IOCTL_TO_USER(target,offs,source,count) \ - memcpy(&((target)[offs]), (source), (count)) -#define IO_WRITE_CHECK(cmd) (cmd & IOC_IN) -#define IOCTL_IN(arg) (*(int*)(arg)) -#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val)) -#define BZERO(target,len) bzero((caddr_t)target, len) -#define MEMCPY(dst,src,len) bcopy((caddr_t)src, (caddr_t)dst, len) - -#ifndef AWE_OBSOLETE_VOXWARE -# define printk printf -# define RET_ERROR(err) -err -#endif - - -/* old style device tables (not modulized) */ -#define sound_alloc_synthdev() \ - (num_synths >= MAX_SYNTH_DEV ? -1 : num_synths++) -#define sound_alloc_mixerdev() \ - (num_mixers >= MAX_MIXER_DEV ? -1 : num_mixers++) -#define sound_alloc_mididev() \ - (num_midis >= MAX_MIXER_DEV ? -1 : num_midis++) -#define sound_unload_synthdev(dev) /**/ -#define sound_unload_mixerdev(dev) /**/ -#define sound_unload_mididev(dev) /**/ - - -#endif /* CONFIG_AWE32_SYNTH */ - -#endif /* AWE_COMPAT_H_DEF */ diff --git a/drivers/sound/lowlevel/awe_compat-linux.h b/drivers/sound/lowlevel/awe_compat-linux.h deleted file mode 100644 index 6ab482ac48b3..000000000000 --- a/drivers/sound/lowlevel/awe_compat-linux.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * sound/awe_compat.h - * - * Compat defines for the AWE32/SB32/AWE64 wave table synth driver. - * version 0.4.3; Oct. 1, 1998 - * - * Copyright (C) 1996-1998 Takashi Iwai - * - * 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. - */ - -#ifndef AWE_COMPAT_H_DEF -#define AWE_COMPAT_H_DEF - -/*================================================================ - * version check - *================================================================*/ - -#include "awe_config.h" - -#define ASC_LINUX_VERSION(V,P,S) (((V) * 65536) + ((P) * 256) + (S)) - -#ifndef LINUX_VERSION_CODE -#include -#endif - -/* linux version check */ -#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,0,0) -#define AWE_OBSOLETE_VOXWARE -#endif - -#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0) -#define AWE_NEW_KERNEL_INTERFACE -#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,80) -#define AWE_MODULE_SUPPORT -#endif -#endif - -#ifdef AWE_OBSOLETE_VOXWARE -#include "soundvers.h" -#else -#include "../soundvers.h" -#endif - -#if defined(SOUND_INTERNAL_VERSION) && SOUND_INTERNAL_VERSION >= 0x30803 -/* OSS/Free-3.8 */ -#define AWE_NO_PATCHMGR -#define AWE_OSS38 -#define HAS_LOWLEVEL_H -#endif - -/*================================================================ - * INCLUDE OTHER HEADER FILES - *================================================================*/ - -/* set up module */ - -#if defined(AWE_MODULE_SUPPORT) && defined(MODULE) -#include -#include -#include -#include "../soundmodule.h" -#endif - - -/* reading configuration of sound driver */ - -#ifdef AWE_OBSOLETE_VOXWARE - -#include "sound_config.h" -#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AWE32) -#define CONFIG_AWE32_SYNTH -#endif - -#else /* AWE_OBSOLETE_VOXWARE */ - -#ifdef HAS_LOWLEVEL_H -#include "lowlevel.h" -#endif - -#include "../sound_config.h" - -#endif /* AWE_OBSOLETE_VOXWARE */ - - -/*================================================================ - * include AWE header files - *================================================================*/ - -#if defined(CONFIG_AWE32_SYNTH) || defined(CONFIG_AWE32_SYNTH_MODULE) - -#include "awe_hw.h" -#include "awe_version.h" -#include - -#ifdef AWE_HAS_GUS_COMPATIBILITY -/* include finetune table */ -#ifdef AWE_OBSOLETE_VOXWARE -# include "tuning.h" -#else -# include "../tuning.h" -#endif -#include -#endif /* AWE_HAS_GUS_COMPATIBILITY */ - - -/*---------------------------------------------------------------- - * compatibility macros for AWE32 driver - *----------------------------------------------------------------*/ - -/* redefine following macros */ -#undef IOCTL_IN -#undef IOCTL_OUT -#undef OUTW -#undef COPY_FROM_USER -#undef COPY_TO_USER -#undef GET_BYTE_FROM_USER -#undef GET_SHORT_FROM_USER -#undef IOCTL_TO_USER - -/* use inline prefix */ -#define INLINE inline - -/*---------------------------------------------------------------- - * memory management for linux - *----------------------------------------------------------------*/ - -#ifdef AWE_OBSOLETE_VOXWARE -/* old type linux system */ - -/* i/o requests; nothing */ -#define awe_check_port() 0 /* always false */ -#define awe_request_region() /* nothing */ -#define awe_release_region() /* nothing */ - -static int _mem_start; /* memory pointer for permanent buffers */ - -#define my_malloc_init(memptr) _mem_start = (memptr) -#define my_malloc_memptr() _mem_start -#define my_free(ptr) /* do nothing */ - -/* allocate buffer only once */ -#define INIT_TABLE(buffer,index,nums,type) {\ -PERMANENT_MALLOC(buffer, char*, size, _mem_start); index = (nums);\ -} - -#else - -#define AWE_DYNAMIC_BUFFER - -#define my_malloc_init(ptr) /* nothing */ -#define my_malloc_memptr() 0 -#define my_malloc(size) vmalloc(size) -#define my_free(ptr) if (ptr) {vfree(ptr);} - -/* do not allocate buffer at beginning */ -#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;} - -/* old type macro */ -#define RET_ERROR(err) -err - -#endif - -/*---------------------------------------------------------------- - * i/o interfaces for linux - *----------------------------------------------------------------*/ - -#define OUTW(data,addr) outw(data, addr) - -#ifdef AWE_NEW_KERNEL_INTERFACE -#define COPY_FROM_USER(target,source,offs,count) \ - copy_from_user(target, (source)+(offs), count) -#define GET_BYTE_FROM_USER(target,addr,offs) \ - get_user(target, (unsigned char*)&((addr)[offs])) -#define GET_SHORT_FROM_USER(target,addr,offs) \ - get_user(target, (unsigned short*)&((addr)[offs])) -#ifdef AWE_OSS38 -#define IOCTL_TO_USER(target,offs,source,count) \ - memcpy(target, (source)+(offs), count) -#define IO_WRITE_CHECK(cmd) (_SIOC_DIR(cmd) & _IOC_WRITE) -#else -#define IOCTL_TO_USER(target,offs,source,count) \ - copy_to_user(target, (source)+(offs), count) -#define IO_WRITE_CHECK(cmd) (_IOC_DIR(cmd) & _IOC_WRITE) -#endif /* AWE_OSS38 */ -#define COPY_TO_USER IOCTL_TO_USER -#define IOCTL_IN(arg) (*(int*)(arg)) -#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val)) - -#else /* old type i/o */ -#define COPY_FROM_USER(target,source,offs,count) \ - memcpy_fromfs(target, (source)+(offs), (count)) -#define GET_BYTE_FROM_USER(target,addr,offs) \ - *((char *)&(target)) = get_fs_byte((addr)+(offs)) -#define GET_SHORT_FROM_USER(target,addr,offs) \ - *((short *)&(target)) = get_fs_word((addr)+(offs)) -#ifdef AWE_OSS38 -#define IOCTL_TO_USER(target,offs,source,count) \ - memcpy(target, (source)+(offs), count) -#define COPY_TO_USER(target,offs,source,count) \ - memcpy_tofs(target, (source)+(offs), (count)) -#define IOCTL_IN(arg) (*(int*)(arg)) -#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val)) -#define IO_WRITE_CHECK(cmd) (_SIOC_DIR(cmd) & _IOC_WRITE) -#else /* AWE_OSS38 */ -#define IOCTL_TO_USER(target,offs,source,count) \ - memcpy_tofs(target, (source)+(offs), (count)) -#define COPY_TO_USER IOCTL_TO_USER -#define IOCTL_IN(arg) get_fs_long((long *)(arg)) -#define IOCTL_OUT(arg,ret) snd_ioctl_return((int *)arg, ret) -#define IO_WRITE_CHECK(cmd) (cmd & IOC_IN) -#endif /* AWE_OSS38 */ - -#endif /* AWE_NEW_KERNEL_INTERFACE */ - -#define BZERO(target,len) memset(target, 0, len) -#define MEMCPY(dst,src,len) memcpy(dst, src, len) - -/* old style device tables (not modulized) */ -#ifndef AWE_MODULE_SUPPORT - -#define sound_alloc_synthdev() \ - (num_synths >= MAX_SYNTH_DEV ? -1 : num_synths++) -#define sound_alloc_mixerdev() \ - (num_mixers >= MAX_MIXER_DEV ? -1 : num_mixers++) -#define sound_alloc_mididev() \ - (num_midis >= MAX_MIXER_DEV ? -1 : num_midis++) -#define sound_unload_synthdev(dev) /**/ -#define sound_unload_mixerdev(dev) /**/ -#define sound_unload_mididev(dev) /**/ - -#endif /* AWE_MODULE_SUPPORT */ - -#endif /* CONFIG_AWE32_SYNTH */ - -#endif /* AWE_COMPAT_H_DEF */ diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 6039a8b2102e..704ec051f503 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -6,6 +6,7 @@ #include #include "usb.h" +#define AUDIO_DEBUG 1 static int usb_audio_probe(struct usb_device *dev); static void usb_audio_disconnect(struct usb_device *dev); @@ -35,23 +36,23 @@ static int usb_audio_irq(int state, void *buffer, int len, void *dev_id) static int usb_audio_probe(struct usb_device *dev) { - struct usb_interface_descriptor *interface; + struct usb_interface_descriptor *intf_desc; struct usb_endpoint_descriptor *endpoint; struct usb_audio *aud; + int bEndpointAddress = 0; int i; int na=0; - interface = &dev->config[0].altsetting[0].interface[0]; for (i=0; iconfig[0].bNumInterfaces; i++) { - endpoint = &interface->endpoint[i]; + intf_desc = &dev->config->interface[i].altsetting[0]; - if(interface->bInterfaceClass != 1) + if(intf_desc->bInterfaceClass != 1) continue; printk(KERN_INFO "USB audio device detected.\n"); - switch(interface->bInterfaceSubClass) { + switch(intf_desc->bInterfaceSubClass) { case 0x01: printk(KERN_INFO "audio: Control device.\n"); break; @@ -74,8 +75,6 @@ static int usb_audio_probe(struct usb_device *dev) aud->dev = dev; dev->private = aud; - endpoint = &interface->endpoint[0]; - // if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { // printk (KERN_INFO " Failed usb_set_configuration: Audio\n"); // break; @@ -83,11 +82,11 @@ static int usb_audio_probe(struct usb_device *dev) // usb_set_protocol(dev, 0); // usb_set_idle(dev, 0, 0); - usb_request_irq(dev, - usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), - usb_audio_irq, - endpoint->bInterval, - aud); +// usb_request_irq(dev, +// usb_rcvctrlpipe(dev, bEndpointAddress), +// usb_audio_irq, +// endpoint->bInterval, +// aud); list_add(&aud->list, &usb_audio_list); @@ -123,10 +122,16 @@ int usb_audio_init(void) void usb_audio_interface(struct usb_interface_descriptor *interface, u8 *data) { +#ifdef AUDIO_DEBUG + printk(KERN_DEBUG "usb_audio_interface.\n"); +#endif } void usb_audio_endpoint(struct usb_endpoint_descriptor *interface, u8 *data) { +#ifdef AUDIO_DEBUG + printk(KERN_DEBUG "usb_audio_interface.\n"); +#endif } #ifdef MODULE diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 64116d483d22..072d769014b9 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -199,7 +199,7 @@ static void usb_hub_configure(struct usb_hub *hub) static int hub_probe(struct usb_device *dev) { - struct usb_interface_descriptor *interface; + struct usb_interface_descriptor *intf_desc; struct usb_endpoint_descriptor *endpoint; struct usb_hub *hub; unsigned long flags; @@ -212,20 +212,20 @@ static int hub_probe(struct usb_device *dev) if (dev->config[0].bNumInterfaces != 1) return -1; - interface = &dev->config[0].altsetting[0].interface[0]; + intf_desc = &dev->config[0].interface[0].altsetting[0]; /* Is it a hub? */ - if (interface->bInterfaceClass != 9) + if (intf_desc->bInterfaceClass != 9) return -1; - if ((interface->bInterfaceSubClass != 0) && - (interface->bInterfaceSubClass != 1)) + if ((intf_desc->bInterfaceSubClass != 0) && + (intf_desc->bInterfaceSubClass != 1)) return -1; /* Multiple endpoints? What kind of mutant ninja-hub is this? */ - if (interface->bNumEndpoints != 1) + if (intf_desc->bNumEndpoints != 1) return -1; - endpoint = &interface->endpoint[0]; + endpoint = &intf_desc->endpoint[0]; /* Output endpoint? Curiousier and curiousier.. */ if (!(endpoint->bEndpointAddress & USB_DIR_IN)) diff --git a/drivers/usb/mouse.c b/drivers/usb/mouse.c index 44ab90986265..60d454048282 100644 --- a/drivers/usb/mouse.c +++ b/drivers/usb/mouse.c @@ -256,7 +256,7 @@ static struct miscdevice usb_mouse = { static int mouse_probe(struct usb_device *dev) { - struct usb_interface_descriptor *interface; + struct usb_interface_descriptor *intf_desc; struct usb_endpoint_descriptor *endpoint; struct mouse_state *mouse = &static_mouse_state; @@ -269,19 +269,19 @@ static int mouse_probe(struct usb_device *dev) return -1; /* Is it a mouse interface? */ - interface = &dev->config[0].altsetting[0].interface[0]; - if (interface->bInterfaceClass != 3) + intf_desc = &dev->config[0].interface[0].altsetting[0]; + if (intf_desc->bInterfaceClass != 3) return -1; - if (interface->bInterfaceSubClass != 1) + if (intf_desc->bInterfaceSubClass != 1) return -1; - if (interface->bInterfaceProtocol != 2) + if (intf_desc->bInterfaceProtocol != 2) return -1; /* Multiple endpoints? What kind of mutant ninja-mouse is this? */ - if (interface->bNumEndpoints != 1) + if (intf_desc->bNumEndpoints != 1) return -1; - endpoint = &interface->endpoint[0]; + endpoint = &intf_desc->endpoint[0]; /* Output endpoint? Curiousier and curiousier.. */ if (!(endpoint->bEndpointAddress & 0x80)) diff --git a/drivers/usb/proc_usb.c b/drivers/usb/proc_usb.c index 20cb728ba8a6..565fb3923b7d 100644 --- a/drivers/usb/proc_usb.c +++ b/drivers/usb/proc_usb.c @@ -213,7 +213,7 @@ static int usb_dump_config (const struct usb_config_descriptor *config, const int active, char *buf, int *len) { int i, j; - struct usb_alternate_setting *as; + struct usb_interface *intf; if (!config) { /* getting these some in 2.3.7; none in 2.3.6 */ *len += sprintf (buf + *len, "(null Cfg. desc.)\n"); @@ -223,13 +223,13 @@ static int usb_dump_config (const struct usb_config_descriptor *config, if (usb_dump_config_descriptor (config, active, buf, len) < 0) return -1; - for (i = 0; i < config->num_altsetting; i++) { - as = config->altsetting + i; - if ((as) == NULL) + for (i = 0; i < config->bNumInterfaces; i++) { + intf = config->interface + i; + if ((intf) == NULL) break; - for (j = 0; j < config->bNumInterfaces; j++) - if (usb_dump_interface (as->interface + j, buf, len) < 0) + for (j = 0; j < intf->num_altsetting; j++) + if (usb_dump_interface (intf->altsetting + j, buf, len) < 0) return -1; } diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 56441efb7cf1..6e53155506da 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -63,7 +63,10 @@ static LIST_HEAD(uhci_list); #define UHCI_DEBUG /* - * Map status to standard result codes + * Map status to standard result codes. + * + * is ((td->status >> 16) & 0xff) [a.k.a. uhci_status_bits(td->status)] + * is True for output TDs and False for input TDs. */ static int uhci_map_status(int status, int dir_out) { @@ -109,20 +112,19 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned /* locate the first failing td, if any */ do { - status = (tmp->status >> 16) & 0xff; + status = uhci_status_bits(tmp->status); if (status) { /* must reset the toggle on first error */ if (uhci_debug) { printk(KERN_DEBUG "Set toggle from %x rval %ld\n", (unsigned int)tmp, rval ? *rval : 0); } - usb_settoggle(dev->usb, usb_pipeendpoint(tmp->info), - usb_pipeout(tmp->info) ^ 1, - (tmp->info >> 19) & 1); + usb_settoggle(dev->usb, uhci_endpoint(tmp->info), + uhci_packetout(tmp->info), uhci_toggle(tmp->info)); break; } else { if (rval) - *rval += (tmp->status & 0x3ff) + 1; + *rval += uhci_actual_length(tmp->status); } if ((tmp->link & UHCI_PTR_TERM) || (tmp->link & UHCI_PTR_QH)) @@ -154,8 +156,8 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned if (status & 0x40) { /* endpoint has stalled - mark it halted */ - usb_endpoint_halt(dev->usb, usb_pipeendpoint(tmp->info), - usb_pipeout(tmp->info) ^ 1); + usb_endpoint_halt(dev->usb, uhci_endpoint(tmp->info), + uhci_packetout(tmp->info)); return USB_ST_STALL; } @@ -164,7 +166,7 @@ static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned if (!rval) return USB_ST_DATAUNDERRUN; } - return uhci_map_status(status, usb_pipeout(tmp->info) ^ 1); + return uhci_map_status(status, uhci_packetout(tmp->info)); } /* @@ -434,7 +436,7 @@ static void *uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb td->link = UHCI_PTR_TERM; /* Terminate */ td->status = status; /* In */ td->info = destination | ((usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)) - 1) << 21) | - (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << 19); + (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << TD_TOKEN_TOGGLE); td->buffer = virt_to_bus(dev->data); td->qh = qh; td->dev = dev; @@ -507,14 +509,14 @@ static int uhci_compress_isochronous(struct usb_device *usb_dev, void *_isodesc) for (i = 0; i < isodesc->num; i++) { struct uhci_td *td = &isodesc->td[i]; char *cdata = uhci_ptr_to_virt(td->buffer); - int n = (td->status + 1) & 0x7FF; + int n = uhci_actual_length(td->status); if ((cdata != data) && (n)) memmove(data, cdata, n); #ifdef UHCI_DEBUG /* Debugging */ - if ((td->status >> 16) & 0xFF) + if (uhci_status_bits(td->status)) printk(KERN_DEBUG "error: %d %X\n", i, (td->status >> 16)); #endif @@ -790,8 +792,8 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre if (!td) return -ENOMEM; - /* The "pipe" thing contains the destination in bits 8--18, 0x2D is SETUP */ - destination = (pipe & PIPE_DEVEP_MASK) | 0x2D; + /* The "pipe" thing contains the destination in bits 8--18. */ + destination = (pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; /* 3 errors */ status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_SPD | (3 << 27); @@ -805,11 +807,11 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre /* * If direction is "send", change the frame from SETUP (0x2D) - * to OUT (0xE1). Else change it from SETUP to IN (0x69) + * to OUT (0xE1). Else change it from SETUP to IN (0x69). */ - destination ^= (0x2D ^ 0x69); /* SETUP -> IN */ + destination ^= (USB_PID_SETUP ^ USB_PID_IN); /* SETUP -> IN */ if (usb_pipeout(pipe)) - destination ^= (0xE1 ^ 0x69); /* IN -> OUT */ + destination ^= (USB_PID_OUT ^ USB_PID_IN); /* IN -> OUT */ prevtd = td; td = uhci_td_alloc(dev); @@ -829,7 +831,7 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre pktsze = maxsze; /* Alternate Data0/1 (start with Data1) */ - destination ^= 1 << 19; + destination ^= 1 << TD_TOKEN_TOGGLE; td->status = status; /* Status */ td->info = destination | ((pktsze - 1) << 21); /* pktsze bytes of data */ @@ -849,8 +851,8 @@ static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, devre /* * Build the final TD for control status */ - destination ^= (0xE1 ^ 0x69); /* OUT -> IN */ - destination |= 1 << 19; /* End in Data1 */ + destination ^= (USB_PID_OUT ^ USB_PID_IN); /* OUT -> IN */ + destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ td->status = status | TD_CTRL_IOC; /* no limit on errors on final packet */ td->info = destination | (UHCI_NULL_DATA_SIZE << 21); /* 0 bytes of data */ @@ -966,10 +968,10 @@ static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da int maxsze = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)); if (usb_endpoint_halted(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) && - usb_clear_halt(usb_dev, usb_pipeendpoint(pipe) | (pipe & 0x80))) + usb_clear_halt(usb_dev, usb_pipeendpoint(pipe) | (pipe & USB_DIR_IN))) return USB_ST_STALL; - /* The "pipe" thing contains the destination in bits 8--18 */ + /* The "pipe" thing contains the destination in bits 8--18. */ destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); /* 3 errors */ @@ -993,7 +995,7 @@ static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *da td->status = status; /* Status */ td->info = destination | ((pktsze-1) << 21) | (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), - usb_pipeout(pipe)) << 19); /* pktsze bytes of data */ + usb_pipeout(pipe)) << TD_TOKEN_TOGGLE); /* pktsze bytes of data */ td->buffer = virt_to_bus(data); td->backptr = &prevtd->link; @@ -1053,7 +1055,7 @@ static void * uhci_request_bulk(struct usb_device *usb_dev, unsigned int pipe, u unsigned long destination, status; int maxsze = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe)); - /* The "pipe" thing contains the destination in bits 8--18, 0x69 is IN */ + /* The "pipe" thing contains the destination in bits 8--18. */ destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid(pipe); /* Infinite errors is 0 */ @@ -1071,7 +1073,7 @@ static void * uhci_request_bulk(struct usb_device *usb_dev, unsigned int pipe, u td->status = status; /* Status */ td->info = destination | ((pktsze-1) << 21) | - (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << 19); /* pktsze bytes of data */ + (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)) << TD_TOKEN_TOGGLE); /* pktsze bytes of data */ td->buffer = virt_to_bus(data); td->backptr = &prevtd->link; td->qh = bulk_qh; @@ -1338,7 +1340,7 @@ static void uhci_interrupt_notify(struct uhci *uhci) list_del(&td->irq_list); INIT_LIST_HEAD(&td->irq_list); - if (td->completed(uhci_map_status(status, usb_pipeout(td->info) ^ 1), + if (td->completed(uhci_map_status(uhci_status_bits(status), uhci_packetout(td->info)), bus_to_virt(td->buffer), -1, td->dev_id)) { list_add(&td->irq_list, &uhci->interrupt_list); @@ -1346,10 +1348,10 @@ static void uhci_interrupt_notify(struct uhci *uhci) if (!(td->status & TD_CTRL_IOS)) { struct usb_device *usb_dev = td->dev->usb; - usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), usb_pipeout(td->info) ^ 1); - td->info &= ~(1 << 19); /* clear data toggle */ - td->info |= usb_gettoggle(usb_dev, usb_pipeendpoint(td->info), - usb_pipeout(td->info) ^ 1) << 19; /* toggle between data0 and data1 */ + usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info)); + td->info &= ~(1 << TD_TOKEN_TOGGLE); /* clear data toggle */ + td->info |= usb_gettoggle(usb_dev, uhci_endpoint(td->info), + uhci_packetout(td->info)) << TD_TOKEN_TOGGLE; /* toggle between data0 and data1 */ td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; /* The HC removes it, so re-add it */ uhci_insert_td_in_qh(td->qh, td); @@ -1359,7 +1361,7 @@ static void uhci_interrupt_notify(struct uhci *uhci) /* marked for removal */ td->flags &= ~UHCI_TD_REMOVE; - usb_dotoggle(usb_dev, usb_pipeendpoint(td->info), usb_pipeout(td->info) ^ 1); + usb_dotoggle(usb_dev, uhci_endpoint(td->info), uhci_packetout(td->info)); uhci_remove_qh(td->qh->skel, td->qh); uhci_qh_free(td->qh); uhci_td_free(td); @@ -1436,7 +1438,7 @@ static void uhci_init_ticktd(struct uhci *uhci) /* Don't clobber the frame */ td->link = uhci->fl->frame[0]; td->status = TD_CTRL_IOC; - td->info = (15 << 21) | 0x7f69; /* (ignored) input packet, 16 bytes, device 127 */ + td->info = (15 << 21) | (0x7f << 8) | USB_PID_IN; /* (ignored) input packet, 16 bytes, device 127 */ td->buffer = 0; td->qh = NULL; diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h index 51457fb5663c..1432b6909fe6 100644 --- a/drivers/usb/uhci.h +++ b/drivers/usb/uhci.h @@ -91,6 +91,13 @@ struct uhci_framelist { #define TD_CTRL_NAK (1 << 19) /* NAK Received */ #define TD_CTRL_CRCTIME (1 << 18) /* CTC/Time Out Error */ #define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7ff /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) + +#define uhci_status_bits(ctrl_sts) ((ctrl_sts >> 16) & 0xff) +#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ #define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS) diff --git a/drivers/usb/usb-debug.c b/drivers/usb/usb-debug.c index e656cc6c4c8e..36010f24c6a1 100644 --- a/drivers/usb/usb-debug.c +++ b/drivers/usb/usb-debug.c @@ -13,28 +13,28 @@ static void usb_show_endpoint(struct usb_endpoint_descriptor *endpoint) usb_show_endpoint_descriptor(endpoint); } -static void usb_show_interface(struct usb_interface_descriptor *interface) +static void usb_show_interface(struct usb_interface_descriptor *altsetting) { int i; - usb_show_interface_descriptor(interface); - for (i = 0 ; i < interface->bNumEndpoints; i++) - usb_show_endpoint(interface->endpoint + i); + usb_show_interface_descriptor(altsetting); + for (i = 0 ; i < altsetting->bNumEndpoints; i++) + usb_show_endpoint(altsetting->endpoint + i); } static void usb_show_config(struct usb_config_descriptor *config) { int i, j; - struct usb_alternate_setting *as; + struct usb_interface *intf; usb_show_config_descriptor(config); - for (i = 0; i < config->num_altsetting; i++) { - as = config->altsetting + i; - if ((as) == NULL) + for (i = 0; i < config->bNumInterfaces; i++) { + intf = config->interface + i; + if ((intf) == NULL) break; - printk("\n Alternate Setting: %d\n", i); - for (j = 0 ; j < config->bNumInterfaces; j++) - usb_show_interface(as->interface + j); + printk("\n Interface: %d\n", i); + for (j = 0 ; j < intf->num_altsetting; j++) + usb_show_interface(intf->altsetting + j); } } @@ -99,7 +99,7 @@ void usb_show_config_descriptor(struct usb_config_descriptor * desc) void usb_show_interface_descriptor(struct usb_interface_descriptor * desc) { - printk(" Interface:\n"); + printk(" Alternate Setting: %2d\n", desc->bAlternateSetting); printk(" bLength = %4d%s\n", desc->bLength, desc->bLength == USB_DT_INTERFACE_SIZE ? "" : " (!!!)"); printk(" bDescriptorType = %02x\n", desc->bDescriptorType); @@ -113,10 +113,13 @@ void usb_show_interface_descriptor(struct usb_interface_descriptor * desc) void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor * desc) { + char *bLengthCommentString = (USB_DT_AUCLSTEP_SIZE == desc->bLength) ? + " (!Audio)" : " (!!!)"; + char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" }; printk(" Endpoint:\n"); printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == USB_DT_ENDPOINT_SIZE ? "" : " (!!!)"); + desc->bLength == USB_DT_ENDPOINT_SIZE ? "" : bLengthCommentString); printk(" bDescriptorType = %02x\n", desc->bDescriptorType); printk(" bEndpointAddress = %02x (%s)\n", desc->bEndpointAddress, (desc->bEndpointAddress & 0x80) ? "in" : "out"); @@ -124,6 +127,10 @@ void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor * desc) EndpointType[3 & desc->bmAttributes]); printk(" wMaxPacketSize = %04x\n", desc->wMaxPacketSize); printk(" bInterval = %02x\n", desc->bInterval); + if (USB_DT_AUCLSTEP_SIZE == desc->bLength) { + printk(" bRefresh = %04x\n", desc->bRefresh); + printk(" bSynchAddress = %02x\n", desc->bSynchAddress); + } } void usb_show_hub_descriptor(struct usb_hub_descriptor * desc) diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index a2359d041542..982afa36d96b 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -329,7 +329,7 @@ static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descri return parsed;// + ptr[parsed]; } -static int usb_parse_interface(struct usb_device *dev, struct usb_interface_descriptor *interface, unsigned char *ptr, int len) +static int usb_parse_altsetting(struct usb_device *dev, struct usb_interface_descriptor *altsetting, unsigned char *ptr, int len) { int i; int parsed = usb_expect_descriptor(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE); @@ -338,35 +338,35 @@ static int usb_parse_interface(struct usb_device *dev, struct usb_interface_desc if (parsed < 0) return parsed; - memcpy(interface, ptr + parsed, *ptr); + memcpy(altsetting, ptr + parsed, *ptr); len -= ptr[parsed]; parsed += ptr[parsed]; while((i=usb_check_descriptor(ptr+parsed, len, 0x24)) >= 0) { - usb_audio_interface(interface, ptr+parsed+i); + usb_audio_interface(altsetting, ptr+parsed+i); len -= ptr[parsed+i]; parsed += ptr[parsed+i]; } - if (interface->bNumEndpoints > USB_MAXENDPOINTS) { + if (altsetting->bNumEndpoints > USB_MAXENDPOINTS) { printk(KERN_WARNING "usb: too many endpoints.\n"); return -1; } - interface->endpoint = (struct usb_endpoint_descriptor *) - kmalloc(interface->bNumEndpoints * sizeof(struct usb_endpoint_descriptor), GFP_KERNEL); - if (!interface->endpoint) { + altsetting->endpoint = (struct usb_endpoint_descriptor *) + kmalloc(altsetting->bNumEndpoints * sizeof(struct usb_endpoint_descriptor), GFP_KERNEL); + if (!altsetting->endpoint) { printk(KERN_WARNING "usb: out of memory.\n"); return -1; } - memset(interface->endpoint, 0, interface->bNumEndpoints*sizeof(struct usb_endpoint_descriptor)); + memset(altsetting->endpoint, 0, altsetting->bNumEndpoints*sizeof(struct usb_endpoint_descriptor)); - for (i = 0; i < interface->bNumEndpoints; i++) { + for (i = 0; i < altsetting->bNumEndpoints; i++) { // if(((USB_DT_HID << 8) | 9) == *(unsigned short*)(ptr + parsed)) { // parsed += 9; /* skip over the HID descriptor for now */ // len -= 9; // } - retval = usb_parse_endpoint(dev, interface->endpoint + i, ptr + parsed, len); + retval = usb_parse_endpoint(dev, altsetting->endpoint + i, ptr + parsed, len); if (retval < 0) return retval; parsed += retval; @@ -377,10 +377,13 @@ static int usb_parse_interface(struct usb_device *dev, struct usb_interface_desc static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor *config, unsigned char *ptr, int len) { - int i, j; + int i; int retval; - struct usb_alternate_setting *as; + struct usb_interface *intf; + struct usb_interface_descriptor as; /* This is needing for copying. */ int parsed = usb_expect_descriptor(ptr, len, USB_DT_CONFIG, 9); + unsigned short bNumInterfaces; + unsigned short bIntfaceNum = 0, bAltSetting = 0; if (parsed < 0) return parsed; @@ -389,65 +392,65 @@ static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor len -= *ptr; parsed += *ptr; le16_to_cpus(&config->wTotalLength); + bNumInterfaces = config->bNumInterfaces; - if (config->bNumInterfaces > USB_MAXINTERFACES) { + if (bNumInterfaces > USB_MAXINTERFACES) { printk(KERN_WARNING "usb: too many interfaces.\n"); return -1; - } - config->altsetting = (struct usb_alternate_setting *) - kmalloc(USB_MAXALTSETTING * sizeof(struct usb_alternate_setting), GFP_KERNEL); - if (!config->altsetting) { + config->interface = (struct usb_interface *) + kmalloc(bNumInterfaces * sizeof(struct usb_interface), GFP_KERNEL); + if (!config->interface) { printk(KERN_WARNING "usb: out of memory.\n"); return -1; } - config->act_altsetting = 0; - config->num_altsetting = 1; - - config->altsetting->interface = (struct usb_interface_descriptor *) - kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL); - if (!config->altsetting->interface) { - printk(KERN_WARNING "usb: out of memory.\n"); - return -1; + memset(config->interface, + 0, (bNumInterfaces) * sizeof(struct usb_interface)); + + for (i = 0; i < bNumInterfaces; i++) { + intf = (config->interface) +i; + /* We have at least one interface */ + intf->num_altsetting = 1; + intf->altsetting = (struct usb_interface_descriptor*) + kmalloc((USB_MAXALTSETTING +1) * sizeof(struct usb_interface_descriptor), GFP_KERNEL); + if (!config->interface[i].altsetting) { + printk(KERN_WARNING "usb: out of memory.\n"); + return -1; + } + memset(intf->altsetting, + 0, (USB_MAXALTSETTING+1) * sizeof(struct usb_interface_descriptor)); } - memset(config->altsetting->interface, - 0, config->bNumInterfaces*sizeof(struct usb_interface_descriptor)); - - for (i = 0; i < config->bNumInterfaces; i++) { - retval = usb_parse_interface(dev, config->altsetting->interface + i, ptr + parsed, len); + + /* Ok, we now have allocated the necessary structures, now decide + * where to put the parsed interface descriptors - sort by + * bAltSetting and bInterfaceNumber. + */ + while (len > 0) { + retval = usb_parse_altsetting(dev, &as, ptr + parsed, len); if (retval < 0) return parsed; // HACK -// return retval; + // return retval; parsed += retval; len -= retval; - } - - printk("parsed = %d len = %d\n", parsed, len); - - // now parse for additional alternate settings - for (j = 1; j < USB_MAXALTSETTING; j++) { - retval = usb_expect_descriptor(ptr + parsed, len, USB_DT_INTERFACE, 9); - if (retval) - break; - config->num_altsetting++; - as = config->altsetting + j; - as->interface = (struct usb_interface_descriptor *) - kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL); - if (!as->interface) { - printk(KERN_WARNING "usb: out of memory.\n"); + bIntfaceNum = as.bInterfaceNumber; + if (bIntfaceNum > config->bNumInterfaces) { + printk(KERN_WARNING "usb: bInterfaceNumber %2u exceeding config->bNumINterfaces.\n", bIntfaceNum); + return -1; + } + bAltSetting = as.bAlternateSetting; + if (bAltSetting > USB_MAXALTSETTING) { + printk(KERN_WARNING "usb: illegal bAlternateSetting %2u.\n", bAltSetting); return -1; } - memset(as->interface, 0, config->bNumInterfaces * sizeof(struct usb_interface_descriptor)); - for (i = 0; i < config->bNumInterfaces; i++) { - retval = usb_parse_interface(dev, as->interface + i, - ptr + parsed, len); - if (retval < 0) - return parsed; - parsed += retval; - len -= retval; + if (bAltSetting > config->interface[bIntfaceNum].num_altsetting) { + config->interface[bIntfaceNum].num_altsetting = bAltSetting +1; } + memcpy( &(config->interface[bIntfaceNum].altsetting[bAltSetting]), + &as, sizeof(struct usb_interface_descriptor)); } + + printk("parsed = %d len = %d\n", parsed, len); return parsed; } @@ -484,29 +487,29 @@ void usb_destroy_configuration(struct usb_device *dev) { int c, a, i; struct usb_config_descriptor *cf; - struct usb_alternate_setting *as; - struct usb_interface_descriptor *ifp; + struct usb_interface *intf; + struct usb_interface_descriptor *as; if (!dev->config) return; for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { cf = &dev->config[c]; - if (!cf->altsetting) + if (!cf->interface) break; - for (a = 0; a < cf->num_altsetting; a++) { - as = &cf->altsetting[a]; - if (as->interface == NULL) + for (a = 0; a < cf->bNumInterfaces; a++) { + intf = &cf->interface[a]; + if (intf->altsetting == NULL) break; - for(i=0;ibNumInterfaces;i++) { - ifp = &as->interface[i]; - if(ifp->endpoint==NULL) + for(i=0; i <= USB_MAXALTSETTING ;i++) { + as = &intf->altsetting[i]; + if(as->endpoint==NULL) break; - kfree(ifp->endpoint); + kfree(as->endpoint); } - kfree(as->interface); + kfree(intf->altsetting); } - kfree(cf->altsetting); + kfree(cf->interface); } kfree(dev->config); @@ -700,22 +703,24 @@ int usb_set_idle(struct usb_device *dev, int duration, int report_id) static void usb_set_maxpacket(struct usb_device *dev) { - int i; - int act_as = dev->actconfig->act_altsetting; - struct usb_alternate_setting *as = dev->actconfig->altsetting + act_as; + int i, j; + struct usb_interface *intf; for (i=0; iactconfig->bNumInterfaces; i++) { - struct usb_interface_descriptor *ip = &as->interface[i]; - struct usb_endpoint_descriptor *ep = ip->endpoint; - int e; - - for (e=0; ebNumEndpoints; e++) { - if (usb_endpoint_out(ep[e].bEndpointAddress)) - dev->epmaxpacketout[ep[e].bEndpointAddress & 0x0f] = - ep[e].wMaxPacketSize; - else - dev->epmaxpacketin [ep[e].bEndpointAddress & 0x0f] = - ep[e].wMaxPacketSize; + intf = (dev->actconfig->interface) +i; + for (j = 0; j < intf->num_altsetting; j++) { + struct usb_interface_descriptor *as = intf->altsetting +j; + struct usb_endpoint_descriptor *ep = as->endpoint; + int e; + + for (e=0; ebNumEndpoints; e++) { + if (usb_endpoint_out(ep[e].bEndpointAddress)) + dev->epmaxpacketout[ep[e].bEndpointAddress & 0x0f] = + ep[e].wMaxPacketSize; + else + dev->epmaxpacketin[ep[e].bEndpointAddress & 0x0f] = + ep[e].wMaxPacketSize; + } } } } @@ -781,7 +786,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) return -1; dev->ifnum = interface; - dev->actconfig->act_altsetting = alternate; + dev->actconfig->interface[interface].act_altsetting = alternate; usb_set_maxpacket(dev); return 0; } diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index b73d73f25a5c..871f003f77ab 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -61,6 +61,7 @@ typedef struct { #define USB_DT_CONFIG_SIZE 9 #define USB_DT_INTERFACE_SIZE 9 #define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_AUCLSTEP_SIZE 9 /* Audio Classes are special */ #define USB_DT_HUB_NONVAR_SIZE 7 /* @@ -167,7 +168,7 @@ struct usb_devmap { */ #define USB_MAXCONFIG 8 -#define USB_MAXALTSETTING 5 +#define USB_MAXALTSETTING 6 #define USB_MAXINTERFACES 32 #define USB_MAXENDPOINTS 32 @@ -196,6 +197,8 @@ struct usb_endpoint_descriptor { __u8 bmAttributes; __u16 wMaxPacketSize; __u8 bInterval; + __u8 bRefresh; + __u8 bSynchAddress; void *audio; }; @@ -215,9 +218,10 @@ struct usb_interface_descriptor { void *audio; }; -/* hack for alternate settings */ -struct usb_alternate_setting { - struct usb_interface_descriptor *interface; +struct usb_interface { + struct usb_interface_descriptor *altsetting; + int act_altsetting; /* active alternate setting */ + int num_altsetting; /* number of alternate settings */ }; /* Configuration descriptor information.. */ @@ -230,9 +234,7 @@ struct usb_config_descriptor { __u8 iConfiguration; __u8 bmAttributes; __u8 MaxPower; - int act_altsetting; /* active alternate setting */ - int num_altsetting; /* number of alternate settings */ - struct usb_alternate_setting *altsetting; + struct usb_interface *interface; }; /* String descriptor */ @@ -398,7 +400,7 @@ extern int usb_compress_isochronous (struct usb_device *usb_dev, void *_isodesc) * unsigned int. The encoding is: * * - max size: bits 0-1 (00 = 8, 01 = 16, 10 = 32, 11 = 64) - * - direction: bit 7 (0 = Host-to-Device, 1 = Device-to-Host) + * - direction: bit 7 (0 = Host-to-Device [Out], 1 = Device-to-Host [In]) * - device: bits 8-14 * - endpoint: bits 15-18 * - Data0/1: bit 19 @@ -414,7 +416,7 @@ extern int usb_compress_isochronous (struct usb_device *usb_dev, void *_isodesc) #define usb_maxpacket(dev, pipe, out) (out \ ? (dev)->epmaxpacketout[usb_pipeendpoint(pipe)] \ : (dev)->epmaxpacketin [usb_pipeendpoint(pipe)] ) -#define usb_packetid(pipe) (((pipe) & 0x80) ? 0x69 : 0xE1) +#define usb_packetid(pipe) (((pipe) & USB_DIR_IN) ? USB_PID_IN : USB_PID_OUT) #define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1) #define usb_pipein(pipe) (((pipe) >> 7) & 1) @@ -454,13 +456,13 @@ static inline unsigned int __default_pipe(struct usb_device *dev) /* Create various pipes... */ #define usb_sndctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint)) -#define usb_rcvctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint) | 0x80) +#define usb_rcvctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) #define usb_sndisocpipe(dev,endpoint) ((0 << 30) | __create_pipe(dev,endpoint)) -#define usb_rcvisocpipe(dev,endpoint) ((0 << 30) | __create_pipe(dev,endpoint) | 0x80) +#define usb_rcvisocpipe(dev,endpoint) ((0 << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) #define usb_sndbulkpipe(dev,endpoint) ((3 << 30) | __create_pipe(dev,endpoint)) -#define usb_rcvbulkpipe(dev,endpoint) ((3 << 30) | __create_pipe(dev,endpoint) | 0x80) +#define usb_rcvbulkpipe(dev,endpoint) ((3 << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) #define usb_snddefctrl(dev) ((2 << 30) | __default_pipe(dev)) -#define usb_rcvdefctrl(dev) ((2 << 30) | __default_pipe(dev) | 0x80) +#define usb_rcvdefctrl(dev) ((2 << 30) | __default_pipe(dev) | USB_DIR_IN) /* * Send and receive control messages.. diff --git a/drivers/video/clgenfb.c b/drivers/video/clgenfb.c index 9930eb89fcf3..9c19c4c30147 100644 --- a/drivers/video/clgenfb.c +++ b/drivers/video/clgenfb.c @@ -33,6 +33,7 @@ #define CLGEN_VERSION "1.9.3" +#include #include #include #include diff --git a/drivers/video/offb.c b/drivers/video/offb.c index 7956b25c9743..9ca7f6313dd3 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -447,7 +447,7 @@ static int __init offb_init_driver(struct device_node *dp) #endif /* CONFIG_FB_PLATINUM */ #ifdef CONFIG_FB_CLGEN if ((!strncmp(dp->name, "MacPicasso",10) || - (!strncmp(dp->name, "54m30",10)) { + (!strncmp(dp->name, "54m30",5)) { clgen_of_init(dp); return 1; } diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index 6308bf840860..09ead177c0cd 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -325,9 +325,9 @@ static int tgafb_encode_fix(struct fb_fix_screeninfo *fix, const void *fb_par, fix->visual = FB_VISUAL_TRUECOLOR; fix->line_length = par->xres * (par->bits_per_pixel >> 3); - fix->smem_start = (char *)__pa(fb_info.tga_fb_base + dense_mem(fb_info.tga_fb_base)); + fix->smem_start = ioremap(fb_info.tga_fb_base); fix->smem_len = fix->line_length * par->yres; - fix->mmio_start = (char *)__pa(fb_info.tga_regs_base); + fix->mmio_start = ioremap(fb_info.tga_regs_base); fix->mmio_len = 0x1000; /* Is this sufficient? */ fix->xpanstep = fix->ypanstep = fix->ywrapstep = 0; fix->accel = FB_ACCEL_DEC_TGA; @@ -934,7 +934,7 @@ static int tgafb_blank(int blank, struct fb_info_gen *info) static void tgafb_set_disp(const void *fb_par, struct display *disp, struct fb_info_gen *info) { - disp->screen_base = (char *)fb_info.tga_fb_base + dense_mem(fb_info.tga_fb_base); + disp->screen_base = ioremap(fb_info.tga_fb_base); switch (fb_info.tga_type) { #ifdef FBCON_HAS_CFB8 case 0: /* 8-plane */ diff --git a/drivers/video/vga.h b/drivers/video/vga.h index a92fde108eca..044673301ec2 100644 --- a/drivers/video/vga.h +++ b/drivers/video/vga.h @@ -17,6 +17,7 @@ #ifndef __linux_video_vga_h__ #define __linux_video_vga_h__ +#include #include #include #ifndef CONFIG_AMIGA diff --git a/drivers/video/vgacon.c b/drivers/video/vgacon.c index dc82db05b0de..9b0e6a9dea63 100644 --- a/drivers/video/vgacon.c +++ b/drivers/video/vgacon.c @@ -277,6 +277,7 @@ static const char __init *vgacon_startup(void) vga_video_font_height = 8; } } + vga_vram_base = VGA_MAP_MEM(vga_vram_base); vga_vram_end = VGA_MAP_MEM(vga_vram_end); diff --git a/fs/partitions/Config.in b/fs/partitions/Config.in index ec30732e9b65..5f0c6557b163 100644 --- a/fs/partitions/Config.in +++ b/fs/partitions/Config.in @@ -7,6 +7,9 @@ if [ "$CONFIG_PARTITION_ADVANCED" = "n" ]; then if [ "$ARCH" = "alpha" ]; then define_bool CONFIG_OSF_PARTITION y fi + if [ "$CONFIG_AMIGA" = "y" ]; then + define_bool CONFIG_AMIGA_PARTITION y + fi if [ "$CONFIG_ARM" = "y" ]; then if [ "$CONFIG_ARCH_ACORN" = "y" ]; then define_bool CONFIG_ACORN_PARTITION y @@ -17,6 +20,9 @@ if [ "$CONFIG_PARTITION_ADVANCED" = "n" ]; then define_bool CONFIG_ACORN_PARTITION_RISCIX y fi fi + if [ "$CONFIG_ATARI" = "y" ]; then + define_bool CONFIG_ATARI_PARTITION y + fi bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL if [ "$ARCH" = "sparc" -o "$CONFIG_SMD_DISKLABEL" = "y" ]; then define_bool CONFIG_SUN_PARTITION y diff --git a/fs/partitions/amiga.c b/fs/partitions/amiga.c index 229e514d6fee..aa8605315508 100644 --- a/fs/partitions/amiga.c +++ b/fs/partitions/amiga.c @@ -20,15 +20,13 @@ #include "check.h" #include "amiga.h" -static int current_minor; - static __inline__ u32 checksum_block(u32 *m, int size) { u32 sum = 0; while (size--) - sum += htonl(*m++); + sum += be32_to_cpu(*m++); return sum; } @@ -48,6 +46,9 @@ amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int old_blocksize = get_ptable_blocksize(dev); blocksize = get_hardsect_size(dev); + if (blocksize < 512) + blocksize = 512; + set_blocksize(dev,blocksize); res = 0; @@ -57,15 +58,15 @@ amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int kdevname(dev),blk); goto rdb_done; } - if (*(u32 *)bh->b_data == htonl(IDNAME_RIGIDDISK)) { + if (*(u32 *)bh->b_data == cpu_to_be32(IDNAME_RIGIDDISK)) { rdb = (struct RigidDiskBlock *)bh->b_data; - if (checksum_block((u32 *)bh->b_data,htonl(rdb->rdb_SummedLongs) & 0x7F)) { + if (checksum_block((u32 *)bh->b_data,be32_to_cpu(rdb->rdb_SummedLongs) & 0x7F)) { /* Try again with 0xdc..0xdf zeroed, Windows might have * trashed it. */ *(u32 *)(&bh->b_data[0xdc]) = 0; if (checksum_block((u32 *)bh->b_data, - htonl(rdb->rdb_SummedLongs) & 0x7F)) { + be32_to_cpu(rdb->rdb_SummedLongs) & 0x7F)) { brelse(bh); printk("Dev %s: RDB in block %d has bad checksum\n", kdevname(dev),blk); @@ -75,7 +76,7 @@ amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int "ignored in checksum calculation\n",blk); } printk(" RDSK"); - blk = htonl(rdb->rdb_PartitionList); + blk = be32_to_cpu(rdb->rdb_PartitionList); brelse(bh); for (part = 1; blk > 0 && part <= 16; part++) { if (!(bh = bread(dev,blk,blocksize))) { @@ -84,29 +85,30 @@ amiga_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int goto rdb_done; } pb = (struct PartitionBlock *)bh->b_data; - blk = htonl(pb->pb_Next); - if (pb->pb_ID == htonl(IDNAME_PARTITION) && checksum_block( - (u32 *)pb,htonl(pb->pb_SummedLongs) & 0x7F) == 0 ) { + blk = be32_to_cpu(pb->pb_Next); + if (pb->pb_ID == cpu_to_be32(IDNAME_PARTITION) && checksum_block( + (u32 *)pb,be32_to_cpu(pb->pb_SummedLongs) & 0x7F) == 0 ) { /* Tell Kernel about it */ - if (!(nr_sects = (htonl(pb->pb_Environment[10]) + 1 - - htonl(pb->pb_Environment[9])) * - htonl(pb->pb_Environment[3]) * - htonl(pb->pb_Environment[5]))) { + if (!(nr_sects = (be32_to_cpu(pb->pb_Environment[10]) + 1 - + be32_to_cpu(pb->pb_Environment[9])) * + be32_to_cpu(pb->pb_Environment[3]) * + be32_to_cpu(pb->pb_Environment[5]))) { brelse(bh); continue; - } - start_sect = htonl(pb->pb_Environment[9]) * - htonl(pb->pb_Environment[3]) * - htonl(pb->pb_Environment[5]); - add_gd_partition(hd,current_minor,start_sect,nr_sects); - current_minor++; + } + start_sect = be32_to_cpu(pb->pb_Environment[9]) * + be32_to_cpu(pb->pb_Environment[3]) * + be32_to_cpu(pb->pb_Environment[5]); + add_gd_partition(hd,first_part_minor,start_sect,nr_sects); + first_part_minor++; res = 1; } brelse(bh); } printk("\n"); + break; } else brelse(bh); @@ -116,4 +118,3 @@ rdb_done: set_blocksize(dev,old_blocksize); return res; } - diff --git a/include/asm-alpha/compiler.h b/include/asm-alpha/compiler.h index 5656ce953c2c..04e76b1d1f93 100644 --- a/include/asm-alpha/compiler.h +++ b/include/asm-alpha/compiler.h @@ -13,7 +13,7 @@ * EGCS (of varying versions) does a good job of using insxl and extxl. */ -#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91 +#if 0 && (__GNUC__ > 2 || __GNUC_MINOR__ >= 91) #define __kernel_insbl(val, shift) \ (((unsigned long)(val) & 0xfful) << ((shift) * 8)) #define __kernel_inswl(val, shift) \ @@ -29,7 +29,7 @@ __kir; }) #endif -#if __GNUC__ > 2 || __GNUC_MINOR__ >= 92 +#if 0 && (__GNUC__ > 2 || __GNUC_MINOR__ >= 92) #define __kernel_extbl(val, shift) (((val) >> (((shift) & 7) * 8)) & 0xfful) #define __kernel_extwl(val, shift) (((val) >> (((shift) & 7) * 8)) & 0xfffful) #else diff --git a/include/asm-alpha/core_apecs.h b/include/asm-alpha/core_apecs.h index 3346346f9cb6..d105281cb048 100644 --- a/include/asm-alpha/core_apecs.h +++ b/include/asm-alpha/core_apecs.h @@ -426,12 +426,12 @@ static inline void * apecs_xl_bus_to_virt(unsigned long address) * detect null "pointers" (the NCR driver is much simpler if * NULL pointers are preserved). */ - if (address < APECS_XL_DMA_WIN1_BASE) - return 0; - else if (address < (APECS_XL_DMA_WIN1_BASE + APECS_XL_DMA_WIN1_SIZE)) - address -= APECS_XL_DMA_WIN1_BASE; + if (address < APECS_XL_DMA_WIN1_BASE) + return 0; + else if (address < (APECS_XL_DMA_WIN1_BASE + APECS_XL_DMA_WIN1_SIZE)) + address -= APECS_XL_DMA_WIN1_BASE; else /* should be more checking here, maybe? */ - address -= APECS_XL_DMA_WIN2_BASE; + address -= APECS_XL_DMA_WIN2_BASE; return phys_to_virt(address); } @@ -501,6 +501,15 @@ __EXTERN_INLINE unsigned long apecs_readb(unsigned long addr) { unsigned long result, msb; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "apecs: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += APECS_DENSE_MEM; + } +#endif + + addr -= APECS_DENSE_MEM; if (addr >= (1UL << 24)) { msb = addr & 0xf8000000; addr -= msb; @@ -514,6 +523,15 @@ __EXTERN_INLINE unsigned long apecs_readw(unsigned long addr) { unsigned long result, msb; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "apecs: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += APECS_DENSE_MEM; + } +#endif + + addr -= APECS_DENSE_MEM; if (addr >= (1UL << 24)) { msb = addr & 0xf8000000; addr -= msb; @@ -525,18 +543,43 @@ __EXTERN_INLINE unsigned long apecs_readw(unsigned long addr) __EXTERN_INLINE unsigned long apecs_readl(unsigned long addr) { - return *(vuip) (addr + APECS_DENSE_MEM); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "apecs: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += APECS_DENSE_MEM; + } +#endif + + return *(vuip)addr; } __EXTERN_INLINE unsigned long apecs_readq(unsigned long addr) { - return *(vulp) (addr + APECS_DENSE_MEM); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "apecs: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += APECS_DENSE_MEM; + } +#endif + + return *(vulp)addr; } __EXTERN_INLINE void apecs_writeb(unsigned char b, unsigned long addr) { unsigned long msb; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "apecs: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += APECS_DENSE_MEM; + } +#endif + + addr -= APECS_DENSE_MEM; if (addr >= (1UL << 24)) { msb = addr & 0xf8000000; addr -= msb; @@ -549,6 +592,15 @@ __EXTERN_INLINE void apecs_writew(unsigned short b, unsigned long addr) { unsigned long msb; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "apecs: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += APECS_DENSE_MEM; + } +#endif + + addr -= APECS_DENSE_MEM; if (addr >= (1UL << 24)) { msb = addr & 0xf8000000; addr -= msb; @@ -559,19 +611,38 @@ __EXTERN_INLINE void apecs_writew(unsigned short b, unsigned long addr) __EXTERN_INLINE void apecs_writel(unsigned int b, unsigned long addr) { - *(vuip) (addr + APECS_DENSE_MEM) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "apecs: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += APECS_DENSE_MEM; + } +#endif + + *(vuip)addr = b; } __EXTERN_INLINE void apecs_writeq(unsigned long b, unsigned long addr) { - *(vulp) (addr + APECS_DENSE_MEM) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "apecs: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += APECS_DENSE_MEM; + } +#endif + + *(vulp)addr = b; } -/* Find the DENSE memory area for a given bus address. */ +__EXTERN_INLINE unsigned long apecs_ioremap(unsigned long addr) +{ + return APECS_DENSE_MEM + addr; +} -__EXTERN_INLINE unsigned long apecs_dense_mem(unsigned long addr) +__EXTERN_INLINE int apecs_is_ioaddr(unsigned long addr) { - return APECS_DENSE_MEM; + return addr >= IDENT_ADDR + 0x100000000UL; } #undef vip @@ -602,7 +673,8 @@ __EXTERN_INLINE unsigned long apecs_dense_mem(unsigned long addr) #define __writew apecs_writew #define __writel apecs_writel #define __writeq apecs_writeq -#define dense_mem apecs_dense_mem +#define __ioremap apecs_ioremap +#define __is_ioaddr apecs_is_ioaddr #define inb(port) \ (__builtin_constant_p((port))?__inb(port):_inb(port)) @@ -610,10 +682,12 @@ __EXTERN_INLINE unsigned long apecs_dense_mem(unsigned long addr) #define outb(x, port) \ (__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port))) -#define readl(a) __readl((unsigned long)(a)) -#define readq(a) __readq((unsigned long)(a)) -#define writel(v,a) __writel((v),(unsigned long)(a)) -#define writeq(v,a) __writeq((v),(unsigned long)(a)) +#if !__DEBUG_IOREMAP +#define __raw_readl(a) __readl((unsigned long)(a)) +#define __raw_readq(a) __readq((unsigned long)(a)) +#define __raw_writel(v,a) __writel((v),(unsigned long)(a)) +#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a)) +#endif #endif /* __WANT_IO_DEF */ diff --git a/include/asm-alpha/core_cia.h b/include/asm-alpha/core_cia.h index c4d4658a23bb..cd4cceadb837 100644 --- a/include/asm-alpha/core_cia.h +++ b/include/asm-alpha/core_cia.h @@ -39,17 +39,15 @@ /* CIA ADDRESS BIT DEFINITIONS * - * 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| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |0|0|0| - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | \_/ \_/ - * | | | - * +-- IO space, not cached. Byte Enable --+ | - * Transfer Length --+ - * - * + * 3333 3333 3322 2222 2222 1111 1111 11 + * 9876 5432 1098 7654 3210 9876 5432 1098 7654 3210 + * ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- + * 1 000 + * ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- + * | |\| + * | Byte Enable --+ | + * | Transfer Length --+ + * +-- IO space, not cached * * Byte Transfer * Enable Length Transfer Byte Address @@ -404,6 +402,15 @@ __EXTERN_INLINE unsigned long cia_srm_base(unsigned long addr) { unsigned long mask, base; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "cia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += CIA_DENSE_MEM; + } +#endif + + addr -= CIA_DENSE_MEM; if (addr >= alpha_mv.sm_base_r1 && addr <= alpha_mv.sm_base_r1 + CIA_MEM_R1_MASK) { mask = CIA_MEM_R1_MASK; @@ -422,7 +429,8 @@ __EXTERN_INLINE unsigned long cia_srm_base(unsigned long addr) else { #if 0 - printk("cia: address 0x%lx not covered by HAE\n", addr); + printk(KERN_CRIT "cia: address 0x%lx not covered by HAE\n", + addr); #endif return 0; } @@ -456,8 +464,9 @@ __EXTERN_INLINE unsigned long cia_srm_readw(unsigned long addr) __EXTERN_INLINE void cia_srm_writeb(unsigned char b, unsigned long addr) { - unsigned long work = cia_srm_base(addr), w; - if (work) { + unsigned long work, w; + + if ((work = cia_srm_base(addr)) != 0) { work += 0x00; /* add transfer length */ w = __kernel_insbl(b, addr & 3); *(vuip) work = w; @@ -466,8 +475,10 @@ __EXTERN_INLINE void cia_srm_writeb(unsigned char b, unsigned long addr) __EXTERN_INLINE void cia_srm_writew(unsigned short b, unsigned long addr) { - unsigned long work = cia_srm_base(addr), w; - if (work) { + unsigned long work, w; + + addr -= CIA_DENSE_MEM; + if ((work = cia_srm_base(addr)) != 0) { work += 0x08; /* add transfer length */ w = __kernel_inswl(b, addr & 3); *(vuip) work = w; @@ -478,6 +489,15 @@ __EXTERN_INLINE unsigned long cia_readb(unsigned long addr) { unsigned long result, msb; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "cia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + /* Note that CIA_DENSE_MEM has no bits not masked in these + operations, so we don't have to subtract it back out. */ msb = addr & 0xE0000000; addr &= CIA_MEM_R1_MASK; set_hae(msb); @@ -490,6 +510,15 @@ __EXTERN_INLINE unsigned long cia_readw(unsigned long addr) { unsigned long result, msb; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "cia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + /* Note that CIA_DENSE_MEM has no bits not masked in these + operations, so we don't have to subtract it back out. */ msb = addr & 0xE0000000; addr &= CIA_MEM_R1_MASK; set_hae(msb); @@ -500,8 +529,17 @@ __EXTERN_INLINE unsigned long cia_readw(unsigned long addr) __EXTERN_INLINE void cia_writeb(unsigned char b, unsigned long addr) { - unsigned long msb, w; + unsigned long msb, w; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "cia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + /* Note that CIA_DENSE_MEM has no bits not masked in these + operations, so we don't have to subtract it back out. */ msb = addr & 0xE0000000; addr &= CIA_MEM_R1_MASK; set_hae(msb); @@ -512,8 +550,17 @@ __EXTERN_INLINE void cia_writeb(unsigned char b, unsigned long addr) __EXTERN_INLINE void cia_writew(unsigned short b, unsigned long addr) { - unsigned long msb, w; + unsigned long msb, w; + +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "cia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + /* Note that CIA_DENSE_MEM has no bits not masked in these + operations, so we don't have to subtract it back out. */ msb = addr & 0xE0000000; addr &= CIA_MEM_R1_MASK; set_hae(msb); @@ -524,29 +571,60 @@ __EXTERN_INLINE void cia_writew(unsigned short b, unsigned long addr) __EXTERN_INLINE unsigned long cia_readl(unsigned long addr) { - return *(vuip) (addr + CIA_DENSE_MEM); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "cia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + return *(vuip)addr; } __EXTERN_INLINE unsigned long cia_readq(unsigned long addr) { - return *(vulp) (addr + CIA_DENSE_MEM); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "cia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + return *(vulp)addr; } __EXTERN_INLINE void cia_writel(unsigned int b, unsigned long addr) { - *(vuip) (addr + CIA_DENSE_MEM) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "cia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + *(vuip)addr = b; } __EXTERN_INLINE void cia_writeq(unsigned long b, unsigned long addr) { - *(vulp) (addr + CIA_DENSE_MEM) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "cia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + *(vulp)addr = b; } -/* Find the DENSE memory area for a given bus address. */ +__EXTERN_INLINE unsigned long cia_ioremap(unsigned long addr) +{ + return CIA_DENSE_MEM + addr; +} -__EXTERN_INLINE unsigned long cia_dense_mem(unsigned long addr) +__EXTERN_INLINE int cia_is_ioaddr(unsigned long addr) { - return CIA_DENSE_MEM; + return addr >= IDENT_ADDR + 0x8000000000UL; } #undef vip @@ -580,7 +658,8 @@ __EXTERN_INLINE unsigned long cia_dense_mem(unsigned long addr) #define __readq cia_readq #define __writel cia_writel #define __writeq cia_writeq -#define dense_mem cia_dense_mem +#define __ioremap cia_ioremap +#define __is_ioaddr cia_is_ioaddr #define inb(port) \ (__builtin_constant_p((port))?__inb(port):_inb(port)) @@ -595,10 +674,12 @@ __EXTERN_INLINE unsigned long cia_dense_mem(unsigned long addr) #define inl(port) __inl(port) #define outl(x,port) __outl((x),(port)) -#define readl(a) __readl((unsigned long)(a)) -#define readq(a) __readq((unsigned long)(a)) -#define writel(v,a) __writel((v),(unsigned long)(a)) -#define writeq(v,a) __writeq((v),(unsigned long)(a)) +#if !__DEBUG_IOREMAP +#define __raw_readl(a) __readl((unsigned long)(a)) +#define __raw_readq(a) __readq((unsigned long)(a)) +#define __raw_writel(v,a) __writel((v),(unsigned long)(a)) +#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a)) +#endif #endif /* __WANT_IO_DEF */ diff --git a/include/asm-alpha/core_lca.h b/include/asm-alpha/core_lca.h index 63f25892459d..aaa272db6c5e 100644 --- a/include/asm-alpha/core_lca.h +++ b/include/asm-alpha/core_lca.h @@ -305,6 +305,15 @@ __EXTERN_INLINE unsigned long lca_readb(unsigned long addr) { unsigned long result, msb; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "lca: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += LCA_DENSE_MEM; + } +#endif + + addr -= LCA_DENSE_MEM; if (addr >= (1UL << 24)) { msb = addr & 0xf8000000; addr -= msb; @@ -318,6 +327,15 @@ __EXTERN_INLINE unsigned long lca_readw(unsigned long addr) { unsigned long result, msb; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "lca: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += LCA_DENSE_MEM; + } +#endif + + addr -= LCA_DENSE_MEM; if (addr >= (1UL << 24)) { msb = addr & 0xf8000000; addr -= msb; @@ -329,12 +347,28 @@ __EXTERN_INLINE unsigned long lca_readw(unsigned long addr) __EXTERN_INLINE unsigned long lca_readl(unsigned long addr) { - return *(vuip) (addr + LCA_DENSE_MEM); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "lca: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += LCA_DENSE_MEM; + } +#endif + + return *(vuip)addr; } __EXTERN_INLINE unsigned long lca_readq(unsigned long addr) { - return *(vulp) (addr + LCA_DENSE_MEM); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "lca: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += LCA_DENSE_MEM; + } +#endif + + return *(vulp)addr; } __EXTERN_INLINE void lca_writeb(unsigned char b, unsigned long addr) @@ -342,6 +376,15 @@ __EXTERN_INLINE void lca_writeb(unsigned char b, unsigned long addr) unsigned long msb; unsigned long w; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "lca: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += LCA_DENSE_MEM; + } +#endif + + addr -= LCA_DENSE_MEM; if (addr >= (1UL << 24)) { msb = addr & 0xf8000000; addr -= msb; @@ -356,6 +399,15 @@ __EXTERN_INLINE void lca_writew(unsigned short b, unsigned long addr) unsigned long msb; unsigned long w; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "lca: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += LCA_DENSE_MEM; + } +#endif + + addr -= LCA_DENSE_MEM; if (addr >= (1UL << 24)) { msb = addr & 0xf8000000; addr -= msb; @@ -367,19 +419,38 @@ __EXTERN_INLINE void lca_writew(unsigned short b, unsigned long addr) __EXTERN_INLINE void lca_writel(unsigned int b, unsigned long addr) { - *(vuip) (addr + LCA_DENSE_MEM) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "lca: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += LCA_DENSE_MEM; + } +#endif + + *(vuip)addr = b; } __EXTERN_INLINE void lca_writeq(unsigned long b, unsigned long addr) { - *(vulp) (addr + LCA_DENSE_MEM) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "lca: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += LCA_DENSE_MEM; + } +#endif + + *(vulp)addr = b; } -/* Find the DENSE memory area for a given bus address. */ +__EXTERN_INLINE unsigned long lca_ioremap(unsigned long addr) +{ + return LCA_DENSE_MEM + addr; +} -__EXTERN_INLINE unsigned long lca_dense_mem(unsigned long addr) +__EXTERN_INLINE int lca_is_ioaddr(unsigned long addr) { - return LCA_DENSE_MEM; + return addr >= IDENT_ADDR + 0x100000000UL; } #undef vip @@ -404,7 +475,8 @@ __EXTERN_INLINE unsigned long lca_dense_mem(unsigned long addr) #define __readq lca_readq #define __writel lca_writel #define __writeq lca_writeq -#define dense_mem lca_dense_mem +#define __ioremap lca_ioremap +#define __is_ioaddr lca_is_ioaddr #define inb(port) \ (__builtin_constant_p((port))?__inb(port):_inb(port)) @@ -412,10 +484,12 @@ __EXTERN_INLINE unsigned long lca_dense_mem(unsigned long addr) #define outb(x, port) \ (__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port))) -#define readl(a) __readl((unsigned long)(a)) -#define readq(a) __readq((unsigned long)(a)) -#define writel(v,a) __writel((v),(unsigned long)(a)) -#define writeq(v,a) __writeq((v),(unsigned long)(a)) +#if !__DEBUG_IOREMAP +#define __raw_readl(a) __readl((unsigned long)(a)) +#define __raw_readq(a) __readq((unsigned long)(a)) +#define __raw_writel(v,a) __writel((v),(unsigned long)(a)) +#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a)) +#endif #endif /* __WANT_IO_DEF */ diff --git a/include/asm-alpha/core_mcpcia.h b/include/asm-alpha/core_mcpcia.h index 84eab12d8230..9afaf0cc77cc 100644 --- a/include/asm-alpha/core_mcpcia.h +++ b/include/asm-alpha/core_mcpcia.h @@ -37,17 +37,15 @@ /* MCPCIA ADDRESS BIT DEFINITIONS * - * 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| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |0|0|0| - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | \_/ \_/ - * | | | - * +-- IO space, not cached. Byte Enable --+ | - * Transfer Length --+ - * - * + * 3333 3333 3322 2222 2222 1111 1111 11 + * 9876 5432 1098 7654 3210 9876 5432 1098 7654 3210 + * ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- + * 1 000 + * ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- + * | |\| + * | Byte Enable --+ | + * | Transfer Length --+ + * +-- IO space, not cached * * Byte Transfer * Enable Length Transfer Byte Address @@ -171,8 +169,8 @@ * Data structure for handling MCPCIA machine checks: */ struct el_MCPCIA_uncorrected_frame_mcheck { - struct el_common header; - struct el_common_EV5_uncorrectable_mcheck procdata; + struct el_common header; + struct el_common_EV5_uncorrectable_mcheck procdata; }; @@ -215,41 +213,6 @@ __EXTERN_INLINE void * mcpcia_bus_to_virt(unsigned long address) #define vuip volatile unsigned int * #define vulp volatile unsigned long * -#if 0 /* BWIO */ -__EXTERN_INLINE unsigned int mcpcia_bw_inb(unsigned long addr) -{ - return __kernel_ldbu(*(vucp)(addr+MCPCIA_BW_IO)); -} - -__EXTERN_INLINE void mcpcia_bw_outb(unsigned char b, unsigned long addr) -{ - __kernel_stb(b, *(vucp)(addr+MCPCIA_BW_IO)); - mb(); -} - -__EXTERN_INLINE unsigned int mcpcia_bw_inw(unsigned long addr) -{ - return __kernel_ldwu(*(vusp)(addr+MCPCIA_BW_IO)); -} - -__EXTERN_INLINE void mcpcia_bw_outw(unsigned short b, unsigned long addr) -{ - __kernel_stw(b, *(vusp)(addr+MCPCIA_BW_IO)); - mb(); -} - -__EXTERN_INLINE unsigned int mcpcia_bw_inl(unsigned long addr) -{ - return *(vuip)(addr+MCPCIA_BW_IO); -} - -__EXTERN_INLINE void mcpcia_bw_outl(unsigned int b, unsigned long addr) -{ - *(vuip)(addr+MCPCIA_BW_IO) = b; - mb(); -} -#endif - __EXTERN_INLINE unsigned int mcpcia_inb(unsigned long in_addr) { unsigned long addr = in_addr & 0xffffffffUL; @@ -307,8 +270,8 @@ __EXTERN_INLINE void mcpcia_outl(unsigned int b, unsigned long in_addr) /* * Memory functions. 64-bit and 32-bit accesses are done through * dense memory space, everything else through sparse space. - * - * For reading and writing 8 and 16 bit quantities we need to + * + * For reading and writing 8 and 16 bit quantities we need to * go through one of the three sparse address mapping regions * and use the HAE_MEM CSR to provide some bits of the address. * The following few routines use only sparse address region 1 @@ -317,10 +280,10 @@ __EXTERN_INLINE void mcpcia_outl(unsigned int b, unsigned long in_addr) * See p 6-17 of the specification but it looks something like this: * * 21164 Address: - * + * * 3 2 1 * 9876543210987654321098765432109876543210 - * 1ZZZZ0.PCI.QW.Address............BBLL + * 1ZZZZ0.PCI.QW.Address............BBLL * * ZZ = SBZ * BB = Byte offset @@ -333,56 +296,34 @@ __EXTERN_INLINE void mcpcia_outl(unsigned int b, unsigned long in_addr) * HHH....PCI.QW.Address........ 00 * * HHH = 31:29 HAE_MEM CSR - * + * */ -#if 0 /* BWIO */ -__EXTERN_INLINE unsigned long mcpcia_bw_readb(unsigned long addr) -{ - return __kernel_ldbu(*(vucp)(addr+MCPCIA_BW_MEM)); -} - -__EXTERN_INLINE unsigned long mcpcia_bw_readw(unsigned long addr) -{ - return __kernel_ldbw(*(vusp)(addr+MCPCIA_BW_MEM)); -} - -__EXTERN_INLINE unsigned long mcpcia_bw_readl(unsigned long addr) -{ - return *(vuip)(addr + MCPCIA_BW_MEM); -} - -__EXTERN_INLINE unsigned long mcpcia_bw_readq(unsigned long addr) +__EXTERN_INLINE unsigned long mcpcia_ioremap(unsigned long in_addr) { - return *(vulp)(addr + MCPCIA_BW_MEM); -} - -__EXTERN_INLINE void mcpcia_bw_writeb(unsigned char b, unsigned long addr) -{ - __kernel_stb(b, *(vucp)(addr+MCPCIA_BW_MEM)); -} - -__EXTERN_INLINE void mcpcia_bw_writew(unsigned short b, unsigned long addr) -{ - __kernel_stw(b, *(vusp)(addr+MCPCIA_BW_MEM)); -} - -__EXTERN_INLINE void mcpcia_bw_writel(unsigned int b, unsigned long addr) -{ - *(vuip)(addr+MCPCIA_BW_MEM) = b; + unsigned long addr = in_addr & 0xffffffffUL; + unsigned long hose = (in_addr >> 32) & 3; + return addr + MCPCIA_DENSE(hose); } -__EXTERN_INLINE void mcpcia_bw_writeq(unsigned long b, unsigned long addr) +__EXTERN_INLINE int mcpcia_is_ioaddr(unsigned long addr) { - *(vulp)(addr+MCPCIA_BW_MEM) = b; + return addr >= IDENT_ADDR + 0x8000000000UL; } -#endif __EXTERN_INLINE unsigned long mcpcia_srm_base(unsigned long addr) { unsigned long mask, base; unsigned long hose = (addr >> 32) & 3; +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "mcpcia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + addr &= 0xfffffffful; if (addr >= alpha_mv.sm_base_r1 && addr <= alpha_mv.sm_base_r1 + MCPCIA_MEM_MASK) { mask = MCPCIA_MEM_MASK; @@ -407,7 +348,7 @@ __EXTERN_INLINE unsigned long mcpcia_srm_readb(unsigned long addr) return 0xff; work += 0x00; /* add transfer length */ - result = *(vip) work; + result = *(vip)work; return __kernel_extbl(result, addr & 3); } @@ -425,28 +366,39 @@ __EXTERN_INLINE unsigned long mcpcia_srm_readw(unsigned long addr) __EXTERN_INLINE void mcpcia_srm_writeb(unsigned char b, unsigned long addr) { - unsigned long work = mcpcia_srm_base(addr); + unsigned long w, work = mcpcia_srm_base(addr); if (work) { work += 0x00; /* add transfer length */ - *(vuip) work = b * 0x01010101; + w = __kernel_insbl(b, addr & 3); + *(vuip)work = w; } } __EXTERN_INLINE void mcpcia_srm_writew(unsigned short b, unsigned long addr) { - unsigned long work = mcpcia_srm_base(addr); + unsigned long w, work = mcpcia_srm_base(addr); if (work) { work += 0x08; /* add transfer length */ - *(vuip) work = b * 0x00010001; + w = __kernel_inswl(b, addr & 3); + *(vuip)work = w; } } __EXTERN_INLINE unsigned long mcpcia_readb(unsigned long in_addr) { + /* Note that MCPCIA_DENSE(hose) has no bits not masked here, and + that the hose calculation is still correct. */ unsigned long addr = in_addr & 0xffffffffUL; unsigned long hose = (in_addr >> 32) & 3; unsigned long result, msb, work, temp; +#if __DEBUG_IOREMAP + if (in_addr <= 0x1000000000) { + printk(KERN_CRIT "mcpcia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + msb = addr & ~MCPCIA_MEM_MASK; temp = addr & MCPCIA_MEM_MASK; set_hae(msb); @@ -458,12 +410,21 @@ __EXTERN_INLINE unsigned long mcpcia_readb(unsigned long in_addr) __EXTERN_INLINE unsigned long mcpcia_readw(unsigned long in_addr) { + /* Note that MCPCIA_DENSE(hose) has no bits not masked here, and + that the hose calculation is still correct. */ unsigned long addr = in_addr & 0xffffffffUL; unsigned long hose = (in_addr >> 32) & 3; unsigned long result, msb, work, temp; +#if __DEBUG_IOREMAP + if (in_addr <= 0x1000000000) { + printk(KERN_CRIT "mcpcia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + msb = addr & ~MCPCIA_MEM_MASK; - temp = addr & MCPCIA_MEM_MASK ; + temp = addr & MCPCIA_MEM_MASK; set_hae(msb); work = ((temp << 5) + MCPCIA_SPARSE(hose) + 0x08); @@ -473,63 +434,100 @@ __EXTERN_INLINE unsigned long mcpcia_readw(unsigned long in_addr) __EXTERN_INLINE void mcpcia_writeb(unsigned char b, unsigned long in_addr) { + /* Note that MCPCIA_DENSE(hose) has no bits not masked here, and + that the hose calculation is still correct. */ unsigned long addr = in_addr & 0xffffffffUL; unsigned long hose = (in_addr >> 32) & 3; - unsigned long msb; + unsigned long msb, w; + +#if __DEBUG_IOREMAP + if (in_addr <= 0x1000000000) { + printk(KERN_CRIT "mcpcia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif msb = addr & ~MCPCIA_MEM_MASK; addr &= MCPCIA_MEM_MASK; set_hae(msb); - *(vuip) ((addr << 5) + MCPCIA_SPARSE(hose) + 0x00) = b * 0x01010101; + w = __kernel_insbl(b, in_addr & 3); + *(vuip) ((addr << 5) + MCPCIA_SPARSE(hose) + 0x00) = w; } __EXTERN_INLINE void mcpcia_writew(unsigned short b, unsigned long in_addr) { + /* Note that MCPCIA_DENSE(hose) has no bits not masked here, and + that the hose calculation is still correct. */ unsigned long addr = in_addr & 0xffffffffUL; unsigned long hose = (in_addr >> 32) & 3; - unsigned long msb ; + unsigned long msb, w; + +#if __DEBUG_IOREMAP + if (in_addr <= 0x1000000000) { + printk(KERN_CRIT "mcpcia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif - msb = addr & ~MCPCIA_MEM_MASK ; - addr &= MCPCIA_MEM_MASK ; + msb = addr & ~MCPCIA_MEM_MASK; + addr &= MCPCIA_MEM_MASK; set_hae(msb); - *(vuip) ((addr << 5) + MCPCIA_SPARSE(hose) + 0x08) = b * 0x00010001; + w = __kernel_inswl(b, in_addr & 3); + *(vuip) ((addr << 5) + MCPCIA_SPARSE(hose) + 0x08) = w; } -__EXTERN_INLINE unsigned long mcpcia_readl(unsigned long in_addr) +__EXTERN_INLINE unsigned long mcpcia_readl(unsigned long addr) { - unsigned long addr = in_addr & 0xffffffffUL; - unsigned long hose = (in_addr >> 32) & 3; - return *(vuip) (addr + MCPCIA_DENSE(hose)); -} +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "mcpcia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = mcpcia_ioremap(addr); + } +#endif -__EXTERN_INLINE unsigned long mcpcia_readq(unsigned long in_addr) -{ - unsigned long addr = in_addr & 0xffffffffUL; - unsigned long hose = (in_addr >> 32) & 3; - return *(vulp) (addr + MCPCIA_DENSE(hose)); + return *(vuip)addr; } -__EXTERN_INLINE void mcpcia_writel(unsigned int b, unsigned long in_addr) +__EXTERN_INLINE unsigned long mcpcia_readq(unsigned long addr) { - unsigned long addr = in_addr & 0xffffffffUL; - unsigned long hose = (in_addr >> 32) & 3; - *(vuip) (addr + MCPCIA_DENSE(hose)) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "mcpcia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = mcpcia_ioremap(addr); + } +#endif + + return *(vulp)addr; } -__EXTERN_INLINE void mcpcia_writeq(unsigned long b, unsigned long in_addr) +__EXTERN_INLINE void mcpcia_writel(unsigned int b, unsigned long addr) { - unsigned long addr = in_addr & 0xffffffffUL; - unsigned long hose = (in_addr >> 32) & 3; - *(vulp) (addr + MCPCIA_DENSE(hose)) = b; -} +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "mcpcia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = mcpcia_ioremap(addr); + } +#endif -/* Find the DENSE memory area for a given bus address. */ + *(vuip)addr = b; +} -__EXTERN_INLINE unsigned long mcpcia_dense_mem(unsigned long addr) +__EXTERN_INLINE void mcpcia_writeq(unsigned long b, unsigned long addr) { - return MCPCIA_DENSE((addr >> 32) & 3); +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "mcpcia: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = mcpcia_ioremap(addr); + } +#endif + + *(vulp)addr = b; } #undef vucp @@ -543,69 +541,42 @@ __EXTERN_INLINE unsigned long mcpcia_dense_mem(unsigned long addr) #define virt_to_bus mcpcia_virt_to_bus #define bus_to_virt mcpcia_bus_to_virt -#if 0 /* BWIO */ -# define __inb mcpcia_bw_inb -# define __inw mcpcia_bw_inw -# define __inl mcpcia_bw_inl -# define __outb mcpcia_bw_outb -# define __outw mcpcia_bw_outw -# define __outl mcpcia_bw_outl -# define __readb mcpcia_bw_readb -# define __readw mcpcia_bw_readw -# define __writeb mcpcia_bw_writeb -# define __writew mcpcia_bw_writew -# define __readl mcpcia_bw_readl -# define __readq mcpcia_bw_readq -# define __writel mcpcia_bw_writel -# define __writeq mcpcia_bw_writeq -#else -# define __inb mcpcia_inb -# define __inw mcpcia_inw -# define __inl mcpcia_inl -# define __outb mcpcia_outb -# define __outw mcpcia_outw -# define __outl mcpcia_outl -# ifdef CONFIG_ALPHA_SRM_SETUP -# define __readb mcpcia_srm_readb -# define __readw mcpcia_srm_readw -# define __writeb mcpcia_srm_writeb -# define __writew mcpcia_srm_writew -# else -# define __readb mcpcia_readb -# define __readw mcpcia_readw -# define __writeb mcpcia_writeb -# define __writew mcpcia_writew -# endif -# define __readl mcpcia_readl -# define __readq mcpcia_readq -# define __writel mcpcia_writel -# define __writeq mcpcia_writeq -#endif /* BWIO */ - -#define dense_mem mcpcia_dense_mem - -#if 0 /* BWIO */ -# define inb(port) __inb((port)) -# define inw(port) __inw((port)) -# define inl(port) __inl((port)) -# define outb(x, port) __outb((x),(port)) -# define outw(x, port) __outw((x),(port)) -# define outl(x, port) __outl((x),(port)) -# define readb(addr) __readb((addr)) -# define readw(addr) __readw((addr)) -# define writeb(b, addr) __writeb((b),(addr)) -# define writew(b, addr) __writew((b),(addr)) +#define __inb mcpcia_inb +#define __inw mcpcia_inw +#define __inl mcpcia_inl +#define __outb mcpcia_outb +#define __outw mcpcia_outw +#define __outl mcpcia_outl +#ifdef CONFIG_ALPHA_SRM_SETUP +# define __readb mcpcia_srm_readb +# define __readw mcpcia_srm_readw +# define __writeb mcpcia_srm_writeb +# define __writew mcpcia_srm_writew #else +# define __readb mcpcia_readb +# define __readw mcpcia_readw +# define __writeb mcpcia_writeb +# define __writew mcpcia_writew +#endif +#define __readl mcpcia_readl +#define __readq mcpcia_readq +#define __writel mcpcia_writel +#define __writeq mcpcia_writeq + +#define __ioremap mcpcia_ioremap +#define __is_ioaddr mcpcia_is_ioaddr + # define inb(port) \ (__builtin_constant_p((port))?__inb(port):_inb(port)) # define outb(x, port) \ (__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port))) -#endif /* BWIO */ -#define readl(a) __readl((unsigned long)(a)) -#define readq(a) __readq((unsigned long)(a)) -#define writel(v,a) __writel((v),(unsigned long)(a)) -#define writeq(v,a) __writeq((v),(unsigned long)(a)) +#if !__DEBUG_IOREMAP +#define __raw_readl(a) __readl((unsigned long)(a)) +#define __raw_readq(a) __readq((unsigned long)(a)) +#define __raw_writel(v,a) __writel((v),(unsigned long)(a)) +#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a)) +#endif #endif /* __WANT_IO_DEF */ diff --git a/include/asm-alpha/core_polaris.h b/include/asm-alpha/core_polaris.h index 8caf50622bc2..75e42e8a2a22 100644 --- a/include/asm-alpha/core_polaris.h +++ b/include/asm-alpha/core_polaris.h @@ -19,23 +19,23 @@ */ /* Polaris memory regions */ -#define POLARIS_SPARSE_MEM_BASE (IDENT_ADDR + 0xf800000000) -#define POLARIS_DENSE_MEM_BASE (IDENT_ADDR + 0xf900000000) -#define POLARIS_SPARSE_IO_BASE (IDENT_ADDR + 0xf980000000) -#define POLARIS_SPARSE_CONFIG_BASE (IDENT_ADDR + 0xf9c0000000) -#define POLARIS_IACK_BASE (IDENT_ADDR + 0xf9f8000000) -#define POLARIS_DENSE_IO_BASE (IDENT_ADDR + 0xf9fc000000) -#define POLARIS_DENSE_CONFIG_BASE (IDENT_ADDR + 0xf9fe000000) +#define POLARIS_SPARSE_MEM_BASE (IDENT_ADDR + 0xf800000000) +#define POLARIS_DENSE_MEM_BASE (IDENT_ADDR + 0xf900000000) +#define POLARIS_SPARSE_IO_BASE (IDENT_ADDR + 0xf980000000) +#define POLARIS_SPARSE_CONFIG_BASE (IDENT_ADDR + 0xf9c0000000) +#define POLARIS_IACK_BASE (IDENT_ADDR + 0xf9f8000000) +#define POLARIS_DENSE_IO_BASE (IDENT_ADDR + 0xf9fc000000) +#define POLARIS_DENSE_CONFIG_BASE (IDENT_ADDR + 0xf9fe000000) -#define POLARIS_IACK_SC POLARIS_IACK_BASE +#define POLARIS_IACK_SC POLARIS_IACK_BASE /* The Polaris command/status registers live in PCI Config space for * bus 0/device 0. As such, they may be bytes, words, or doublewords. */ -#define POLARIS_W_VENID (POLARIS_DENSE_CONFIG_BASE) -#define POLARIS_W_DEVID (POLARIS_DENSE_CONFIG_BASE+2) -#define POLARIS_W_CMD (POLARIS_DENSE_CONFIG_BASE+4) -#define POLARIS_W_STATUS (POLARIS_DENSE_CONFIG_BASE+6) +#define POLARIS_W_VENID (POLARIS_DENSE_CONFIG_BASE) +#define POLARIS_W_DEVID (POLARIS_DENSE_CONFIG_BASE+2) +#define POLARIS_W_CMD (POLARIS_DENSE_CONFIG_BASE+4) +#define POLARIS_W_STATUS (POLARIS_DENSE_CONFIG_BASE+6) /* No HAE address. Polaris has no concept of an HAE, since it * supports transfers of all sizes in dense space. @@ -62,7 +62,7 @@ struct el_POLARIS_sysdata_mcheck { u_long psc_pcictl2; }; - #ifdef __KERNEL__ +#ifdef __KERNEL__ #ifndef __EXTERN_INLINE #define __EXTERN_INLINE extern inline @@ -96,7 +96,7 @@ __EXTERN_INLINE void * polaris_bus_to_virt(unsigned long address) __EXTERN_INLINE unsigned int polaris_inb(unsigned long addr) { - return __kernel_ldbu(*(vucp)(addr + POLARIS_DENSE_IO_BASE)); + return __kernel_ldbu(*(vucp)(addr + POLARIS_DENSE_IO_BASE)); } __EXTERN_INLINE void polaris_outb(unsigned char b, unsigned long addr) @@ -107,7 +107,7 @@ __EXTERN_INLINE void polaris_outb(unsigned char b, unsigned long addr) __EXTERN_INLINE unsigned int polaris_inw(unsigned long addr) { - return __kernel_ldwu(*(vusp)(addr + POLARIS_DENSE_IO_BASE)); + return __kernel_ldwu(*(vusp)(addr + POLARIS_DENSE_IO_BASE)); } __EXTERN_INLINE void polaris_outw(unsigned short b, unsigned long addr) @@ -118,13 +118,13 @@ __EXTERN_INLINE void polaris_outw(unsigned short b, unsigned long addr) __EXTERN_INLINE unsigned int polaris_inl(unsigned long addr) { - return *(vuip)(addr + POLARIS_DENSE_IO_BASE); + return *(vuip)(addr + POLARIS_DENSE_IO_BASE); } __EXTERN_INLINE void polaris_outl(unsigned int b, unsigned long addr) { - *(vuip)(addr + POLARIS_DENSE_IO_BASE) = b; - mb(); + *(vuip)(addr + POLARIS_DENSE_IO_BASE) = b; + mb(); } /* @@ -136,53 +136,116 @@ __EXTERN_INLINE void polaris_outl(unsigned int b, unsigned long addr) __EXTERN_INLINE unsigned long polaris_readb(unsigned long addr) { - return __kernel_ldbu(*(vucp)(addr + POLARIS_DENSE_MEM_BASE)); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "polaris: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += POLARIS_DENSE_MEM_BASE; + } +#endif + + return __kernel_ldbu(*(vucp)addr); } __EXTERN_INLINE unsigned long polaris_readw(unsigned long addr) { - return __kernel_ldwu(*(vusp)(addr + POLARIS_DENSE_MEM_BASE)); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "polaris: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += POLARIS_DENSE_MEM_BASE; + } +#endif + + return __kernel_ldwu(*(vusp)addr); } __EXTERN_INLINE unsigned long polaris_readl(unsigned long addr) { - return *(vuip)(addr + POLARIS_DENSE_MEM_BASE); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "polaris: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += POLARIS_DENSE_MEM_BASE; + } +#endif + + return *(vuip)addr; } __EXTERN_INLINE unsigned long polaris_readq(unsigned long addr) { - return *(vulp)(addr + POLARIS_DENSE_MEM_BASE); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "polaris: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += POLARIS_DENSE_MEM_BASE; + } +#endif + + return *(vulp)addr; } __EXTERN_INLINE void polaris_writeb(unsigned char b, unsigned long addr) { - __kernel_stb(b, *(vucp)(addr + POLARIS_DENSE_MEM_BASE)); - mb(); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "polaris: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += POLARIS_DENSE_MEM_BASE; + } +#endif + + __kernel_stb(b, *(vucp)addr); } __EXTERN_INLINE void polaris_writew(unsigned short b, unsigned long addr) { - __kernel_stw(b, *(vusp)(addr + POLARIS_DENSE_MEM_BASE)); - mb(); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "polaris: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += POLARIS_DENSE_MEM_BASE; + } +#endif + + __kernel_stw(b, *(vusp)addr); } __EXTERN_INLINE void polaris_writel(unsigned int b, unsigned long addr) { - *(vuip)(addr + POLARIS_DENSE_MEM_BASE) = b; - mb(); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "polaris: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += POLARIS_DENSE_MEM_BASE; + } +#endif + + *(vuip)addr = b; } __EXTERN_INLINE void polaris_writeq(unsigned long b, unsigned long addr) { - *(vulp)(addr + POLARIS_DENSE_MEM_BASE) = b; - mb(); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "polaris: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += POLARIS_DENSE_MEM_BASE; + } +#endif + + *(vulp)addr = b; } -/* Find the DENSE memory area for a given bus address. */ +__EXTERN_INLINE unsigned long polaris_ioremap(unsigned long addr) +{ + return POLARIS_DENSE_MEM_BASE + addr; +} -__EXTERN_INLINE unsigned long polaris_dense_mem(unsigned long addr) +__EXTERN_INLINE int polaris_is_ioaddr(unsigned long addr) { - return POLARIS_DENSE_MEM_BASE; + return addr >= IDENT_ADDR + 0x8000000000UL; } #undef vucp @@ -209,7 +272,8 @@ __EXTERN_INLINE unsigned long polaris_dense_mem(unsigned long addr) #define __readq polaris_readq #define __writel polaris_writel #define __writeq polaris_writeq -#define dense_mem polaris_dense_mem +#define __ioremap polaris_ioremap +#define __is_ioaddr polaris_is_ioaddr #define inb(port) __inb((port)) #define inw(port) __inw((port)) @@ -219,15 +283,16 @@ __EXTERN_INLINE unsigned long polaris_dense_mem(unsigned long addr) #define outw(v, port) __outw((v),(port)) #define outl(v, port) __outl((v),(port)) -#define readb(a) __readb((unsigned long)(a)) -#define readw(a) __readw((unsigned long)(a)) -#define readl(a) __readl((unsigned long)(a)) -#define readq(a) __readq((unsigned long)(a)) - -#define writeb(v,a) __writeb((v),(unsigned long)(a)) -#define writew(v,a) __writew((v),(unsigned long)(a)) -#define writel(v,a) __writel((v),(unsigned long)(a)) -#define writeq(v,a) __writeq((v),(unsigned long)(a)) +#if !__DEBUG_IOREMAP +#define __raw_readb(a) __readb((unsigned long)(a)) +#define __raw_readw(a) __readw((unsigned long)(a)) +#define __raw_readl(a) __readl((unsigned long)(a)) +#define __raw_readq(a) __readq((unsigned long)(a)) +#define __raw_writeb(v,a) __writeb((v),(unsigned long)(a)) +#define __raw_writeb(v,a) __writew((v),(unsigned long)(a)) +#define __raw_writel(v,a) __writel((v),(unsigned long)(a)) +#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a)) +#endif #endif /* __WANT_IO_DEF */ diff --git a/include/asm-alpha/core_pyxis.h b/include/asm-alpha/core_pyxis.h index 7ca0d1514569..3ae7cbe17822 100644 --- a/include/asm-alpha/core_pyxis.h +++ b/include/asm-alpha/core_pyxis.h @@ -34,19 +34,17 @@ **------------------------------------------------------------------------*/ -/* CIA ADDRESS BIT DEFINITIONS - * - * 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| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |0|0|0| - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | \_/ \_/ - * | | | - * +-- IO space, not cached. Byte Enable --+ | - * Transfer Length --+ - * +/* PYXIS ADDRESS BIT DEFINITIONS * + * 3333 3333 3322 2222 2222 1111 1111 11 + * 9876 5432 1098 7654 3210 9876 5432 1098 7654 3210 + * ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- + * 1 000 + * ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- + * | |\| + * | Byte Enable --+ | + * | Transfer Length --+ + * +-- IO space, not cached * * Byte Transfer * Enable Length Transfer Byte Address @@ -213,47 +211,47 @@ * Data structure for handling PYXIS machine checks: */ struct el_PYXIS_sysdata_mcheck { - u_long coma_gcr; - u_long coma_edsr; - u_long coma_ter; - u_long coma_elar; - u_long coma_ehar; - u_long coma_ldlr; - u_long coma_ldhr; - u_long coma_base0; - u_long coma_base1; - u_long coma_base2; - u_long coma_cnfg0; - u_long coma_cnfg1; - u_long coma_cnfg2; - u_long epic_dcsr; - u_long epic_pear; - u_long epic_sear; - u_long epic_tbr1; - u_long epic_tbr2; - u_long epic_pbr1; - u_long epic_pbr2; - u_long epic_pmr1; - u_long epic_pmr2; - u_long epic_harx1; - u_long epic_harx2; - u_long epic_pmlt; - u_long epic_tag0; - u_long epic_tag1; - u_long epic_tag2; - u_long epic_tag3; - u_long epic_tag4; - u_long epic_tag5; - u_long epic_tag6; - u_long epic_tag7; - u_long epic_data0; - u_long epic_data1; - u_long epic_data2; - u_long epic_data3; - u_long epic_data4; - u_long epic_data5; - u_long epic_data6; - u_long epic_data7; + u_long coma_gcr; + u_long coma_edsr; + u_long coma_ter; + u_long coma_elar; + u_long coma_ehar; + u_long coma_ldlr; + u_long coma_ldhr; + u_long coma_base0; + u_long coma_base1; + u_long coma_base2; + u_long coma_cnfg0; + u_long coma_cnfg1; + u_long coma_cnfg2; + u_long epic_dcsr; + u_long epic_pear; + u_long epic_sear; + u_long epic_tbr1; + u_long epic_tbr2; + u_long epic_pbr1; + u_long epic_pbr2; + u_long epic_pmr1; + u_long epic_pmr2; + u_long epic_harx1; + u_long epic_harx2; + u_long epic_pmlt; + u_long epic_tag0; + u_long epic_tag1; + u_long epic_tag2; + u_long epic_tag3; + u_long epic_tag4; + u_long epic_tag5; + u_long epic_tag6; + u_long epic_tag7; + u_long epic_data0; + u_long epic_data1; + u_long epic_data2; + u_long epic_data3; + u_long epic_data4; + u_long epic_data5; + u_long epic_data6; + u_long epic_data7; }; @@ -373,8 +371,8 @@ __EXTERN_INLINE void pyxis_outl(unsigned int b, unsigned long addr) /* * Memory functions. 64-bit and 32-bit accesses are done through * dense memory space, everything else through sparse space. - * - * For reading and writing 8 and 16 bit quantities we need to + * + * For reading and writing 8 and 16 bit quantities we need to * go through one of the three sparse address mapping regions * and use the HAE_MEM CSR to provide some bits of the address. * The following few routines use only sparse address region 1 @@ -383,10 +381,10 @@ __EXTERN_INLINE void pyxis_outl(unsigned int b, unsigned long addr) * See p 6-17 of the specification but it looks something like this: * * 21164 Address: - * - * 3 2 1 + * + * 3 2 1 * 9876543210987654321098765432109876543210 - * 1ZZZZ0.PCI.QW.Address............BBLL + * 1ZZZZ0.PCI.QW.Address............BBLL * * ZZ = SBZ * BB = Byte offset @@ -394,60 +392,136 @@ __EXTERN_INLINE void pyxis_outl(unsigned int b, unsigned long addr) * * PCI Address: * - * 3 2 1 + * 3 2 1 * 10987654321098765432109876543210 * HHH....PCI.QW.Address........ 00 * * HHH = 31:29 HAE_MEM CSR - * + * */ __EXTERN_INLINE unsigned long pyxis_bw_readb(unsigned long addr) { - return __kernel_ldbu(*(vucp)(addr+PYXIS_BW_MEM)); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_BW_MEM; + } +#endif + + return __kernel_ldbu(*(vucp)addr); } __EXTERN_INLINE unsigned long pyxis_bw_readw(unsigned long addr) { - return __kernel_ldwu(*(vusp)(addr+PYXIS_BW_MEM)); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_BW_MEM; + } +#endif + + return __kernel_ldwu(*(vusp)addr); } __EXTERN_INLINE unsigned long pyxis_bw_readl(unsigned long addr) { - return *(vuip)(addr+PYXIS_BW_MEM); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_BW_MEM; + } +#endif + + return *(vuip)addr; } __EXTERN_INLINE unsigned long pyxis_bw_readq(unsigned long addr) { - return *(vulp)(addr+PYXIS_BW_MEM); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_BW_MEM; + } +#endif + + return *(vulp)addr; } __EXTERN_INLINE void pyxis_bw_writeb(unsigned char b, unsigned long addr) { - __kernel_stb(b, *(vucp)(addr+PYXIS_BW_MEM)); - mb(); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_BW_MEM; + } +#endif + + __kernel_stb(b, *(vucp)addr); } __EXTERN_INLINE void pyxis_bw_writew(unsigned short b, unsigned long addr) { - __kernel_stw(b, *(vusp)(addr+PYXIS_BW_MEM)); - mb(); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_BW_MEM; + } +#endif + + __kernel_stw(b, *(vusp)addr); } __EXTERN_INLINE void pyxis_bw_writel(unsigned int b, unsigned long addr) { - *(vuip)(addr+PYXIS_BW_MEM) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_BW_MEM; + } +#endif + + *(vuip)addr = b; } __EXTERN_INLINE void pyxis_bw_writeq(unsigned long b, unsigned long addr) { - *(vulp)(addr+PYXIS_BW_MEM) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_BW_MEM; + } +#endif + + *(vulp)addr = b; +} + +__EXTERN_INLINE unsigned long pyxis_bw_ioremap(unsigned long addr) +{ + return PYXIS_BW_MEM + addr; } __EXTERN_INLINE unsigned long pyxis_srm_base(unsigned long addr) { unsigned long mask, base; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_BW_MEM; + } +#endif + + addr -= PYXIS_BW_MEM; if (addr >= alpha_mv.sm_base_r1 && addr <= alpha_mv.sm_base_r1 + PYXIS_MEM_R1_MASK) { mask = PYXIS_MEM_R1_MASK; @@ -500,19 +574,21 @@ __EXTERN_INLINE unsigned long pyxis_srm_readw(unsigned long addr) __EXTERN_INLINE void pyxis_srm_writeb(unsigned char b, unsigned long addr) { - unsigned long work = pyxis_srm_base(addr); + unsigned long w, work = pyxis_srm_base(addr); if (work) { work += 0x00; /* add transfer length */ - *(vuip) work = b * 0x01010101; + w = __kernel_insbl(b, addr & 3); + *(vuip)work = w; } } __EXTERN_INLINE void pyxis_srm_writew(unsigned short b, unsigned long addr) { - unsigned long work = pyxis_srm_base(addr); + unsigned long w, work = pyxis_srm_base(addr); if (work) { work += 0x08; /* add transfer length */ - *(vuip) work = b * 0x00010001; + w = __kernel_inswl(b, addr & 3); + *(vuip)work = w; } } @@ -520,8 +596,17 @@ __EXTERN_INLINE unsigned long pyxis_readb(unsigned long addr) { unsigned long result, msb, work, temp; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + /* Note that PYXIS_DENSE_MEM has no bits not masked in these + operations, so we don't have to subtract it back out. */ msb = addr & 0xE0000000UL; - temp = addr & PYXIS_MEM_R1_MASK ; + temp = addr & PYXIS_MEM_R1_MASK; set_hae(msb); work = ((temp << 5) + PYXIS_SPARSE_MEM + 0x00); @@ -533,8 +618,17 @@ __EXTERN_INLINE unsigned long pyxis_readw(unsigned long addr) { unsigned long result, msb, work, temp; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + /* Note that PYXIS_DENSE_MEM has no bits not masked in these + operations, so we don't have to subtract it back out. */ msb = addr & 0xE0000000UL; - temp = addr & PYXIS_MEM_R1_MASK ; + temp = addr & PYXIS_MEM_R1_MASK; set_hae(msb); work = ((temp << 5) + PYXIS_SPARSE_MEM + 0x08); @@ -544,51 +638,106 @@ __EXTERN_INLINE unsigned long pyxis_readw(unsigned long addr) __EXTERN_INLINE void pyxis_writeb(unsigned char b, unsigned long addr) { - unsigned long msb ; + unsigned long msb, w; - msb = addr & 0xE0000000 ; - addr &= PYXIS_MEM_R1_MASK ; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + /* Note that PYXIS_DENSE_MEM has no bits not masked in these + operations, so we don't have to subtract it back out. */ + msb = addr & 0xE0000000; + addr &= PYXIS_MEM_R1_MASK; set_hae(msb); - *(vuip) ((addr << 5) + PYXIS_SPARSE_MEM + 0x00) = b * 0x01010101; + w = __kernel_insbl(b, addr & 3); + *(vuip) ((addr << 5) + PYXIS_SPARSE_MEM + 0x00) = w; } __EXTERN_INLINE void pyxis_writew(unsigned short b, unsigned long addr) { - unsigned long msb ; + unsigned long msb, w; - msb = addr & 0xE0000000 ; - addr &= PYXIS_MEM_R1_MASK ; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + } +#endif + + /* Note that PYXIS_DENSE_MEM has no bits not masked in these + operations, so we don't have to subtract it back out. */ + msb = addr & 0xE0000000; + addr &= PYXIS_MEM_R1_MASK; set_hae(msb); - *(vuip) ((addr << 5) + PYXIS_SPARSE_MEM + 0x08) = b * 0x00010001; + w = __kernel_inswl(b, addr & 3); + *(vuip) ((addr << 5) + PYXIS_SPARSE_MEM + 0x08) = w; } __EXTERN_INLINE unsigned long pyxis_readl(unsigned long addr) { - return *(vuip) (addr + PYXIS_DENSE_MEM); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_DENSE_MEM; + } +#endif + + return *(vuip)addr; } __EXTERN_INLINE unsigned long pyxis_readq(unsigned long addr) { - return *(vulp) (addr + PYXIS_DENSE_MEM); +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_DENSE_MEM; + } +#endif + + return *(vulp)addr; } __EXTERN_INLINE void pyxis_writel(unsigned int b, unsigned long addr) { - *(vuip) (addr + PYXIS_DENSE_MEM) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_DENSE_MEM; + } +#endif + + *(vuip)addr = b; } __EXTERN_INLINE void pyxis_writeq(unsigned long b, unsigned long addr) { - *(vulp) (addr + PYXIS_DENSE_MEM) = b; +#if __DEBUG_IOREMAP + if (addr <= 0x100000000) { + printk(KERN_CRIT "pyxis: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr += PYXIS_DENSE_MEM; + } +#endif + + *(vulp)addr = b; } -/* Find the DENSE memory area for a given bus address. */ +__EXTERN_INLINE unsigned long pyxis_ioremap(unsigned long addr) +{ + return PYXIS_DENSE_MEM + addr; +} -__EXTERN_INLINE unsigned long pyxis_dense_mem(unsigned long addr) +__EXTERN_INLINE int pyxis_is_ioaddr(unsigned long addr) { - return PYXIS_DENSE_MEM; + return addr >= IDENT_ADDR + 0x8000000000UL; } #undef vucp @@ -617,6 +766,7 @@ __EXTERN_INLINE unsigned long pyxis_dense_mem(unsigned long addr) # define __readq pyxis_bw_readq # define __writel pyxis_bw_writel # define __writeq pyxis_bw_writeq +# define __ioremap pyxis_bw_ioremap #else # define __inb pyxis_inb # define __inw pyxis_inw @@ -639,21 +789,24 @@ __EXTERN_INLINE unsigned long pyxis_dense_mem(unsigned long addr) # define __readq pyxis_readq # define __writel pyxis_writel # define __writeq pyxis_writeq +# define __ioremap pyxis_ioremap #endif /* BWIO */ -#define dense_mem pyxis_dense_mem +#define __is_ioaddr pyxis_is_ioaddr #if defined(BWIO_ENABLED) && !defined(CONFIG_ALPHA_RUFFIAN) -# define inb(port) __inb((port)) -# define inw(port) __inw((port)) -# define inl(port) __inl((port)) -# define outb(x, port) __outb((x),(port)) -# define outw(x, port) __outw((x),(port)) -# define outl(x, port) __outl((x),(port)) -# define readb(addr) __readb((addr)) -# define readw(addr) __readw((addr)) -# define writeb(b, addr) __writeb((b),(addr)) -# define writew(b, addr) __writew((b),(addr)) +# define inb(port) __inb((port)) +# define inw(port) __inw((port)) +# define inl(port) __inl((port)) +# define outb(x, port) __outb((x),(port)) +# define outw(x, port) __outw((x),(port)) +# define outl(x, port) __outl((x),(port)) +# if !__DEBUG_IOREMAP +# define __raw_readb(addr) __readb((addr)) +# define __raw_readw(addr) __readw((addr)) +# define __raw_writeb(b, addr) __writeb((b),(addr)) +# define __raw_writeb(b, addr) __writew((b),(addr)) +# endif #else # define inb(port) \ (__builtin_constant_p((port))?__inb(port):_inb(port)) @@ -661,10 +814,12 @@ __EXTERN_INLINE unsigned long pyxis_dense_mem(unsigned long addr) (__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port))) #endif /* BWIO */ -#define readl(a) __readl((unsigned long)(a)) -#define readq(a) __readq((unsigned long)(a)) -#define writel(v,a) __writel((v),(unsigned long)(a)) -#define writeq(v,a) __writeq((v),(unsigned long)(a)) +#if !__DEBUG_IOREMAP +#define __raw_readl(a) __readl((unsigned long)(a)) +#define __raw_readq(a) __readq((unsigned long)(a)) +#define __raw_writel(v,a) __writel((v),(unsigned long)(a)) +#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a)) +#endif #endif /* __WANT_IO_DEF */ diff --git a/include/asm-alpha/core_t2.h b/include/asm-alpha/core_t2.h index fdb0f82fe4f1..0767d9d0e3e8 100644 --- a/include/asm-alpha/core_t2.h +++ b/include/asm-alpha/core_t2.h @@ -78,33 +78,33 @@ 3.8fff.ffff * * +--------------+ 3 8000 0000 - * | CPU 0 CSRs | + * | CPU 0 CSRs | * +--------------+ 3 8100 0000 - * | CPU 1 CSRs | + * | CPU 1 CSRs | * +--------------+ 3 8200 0000 - * | CPU 2 CSRs | + * | CPU 2 CSRs | * +--------------+ 3 8300 0000 - * | CPU 3 CSRs | + * | CPU 3 CSRs | * +--------------+ 3 8400 0000 - * | CPU Reserved | + * | CPU Reserved | * +--------------+ 3 8700 0000 - * | Mem Reserved | + * | Mem Reserved | * +--------------+ 3 8800 0000 - * | Mem 0 CSRs | + * | Mem 0 CSRs | * +--------------+ 3 8900 0000 - * | Mem 1 CSRs | + * | Mem 1 CSRs | * +--------------+ 3 8a00 0000 - * | Mem 2 CSRs | + * | Mem 2 CSRs | * +--------------+ 3 8b00 0000 - * | Mem 3 CSRs | - * +--------------+ 3 8c00 0000 - * | Mem Reserved | - * +--------------+ 3 8e00 0000 - * | PCI Bridge | - * +--------------+ 3 8f00 0000 - * | Expansion IO | - * +--------------+ 3 9000 0000 - * + * | Mem 3 CSRs | + * +--------------+ 3 8c00 0000 + * | Mem Reserved | + * +--------------+ 3 8e00 0000 + * | PCI Bridge | + * +--------------+ 3 8f00 0000 + * | Expansion IO | + * +--------------+ 3 9000 0000 + * * */ #define T2_CPU0_BASE (IDENT_ADDR + GAMMA_BIAS + 0x380000000L) @@ -176,7 +176,7 @@ struct el_t2_procdata_mcheck { unsigned long elfmc_bc_tag; /* Backup Cache Tag Probe Results. */ }; -/* +/* * Sable processor specific Machine Check Data segment. */ @@ -184,7 +184,7 @@ struct el_t2_logout_header { unsigned int elfl_size; /* size in bytes of logout area. */ int elfl_sbz1:31; /* Should be zero. */ char elfl_retry:1; /* Retry flag. */ - unsigned int elfl_procoffset; /* Processor-specific offset. */ + unsigned int elfl_procoffset; /* Processor-specific offset. */ unsigned int elfl_sysoffset; /* Offset of system-specific. */ unsigned int elfl_error_type; /* PAL error type code. */ unsigned int elfl_frame_rev; /* PAL Frame revision. */ @@ -233,7 +233,7 @@ struct el_t2_data_memory { */ struct el_t2_data_other_cpu { short elco_cpuid; /* CPU ID */ - short elco_res02[3]; + short elco_res02[3]; unsigned long elco_bcc; /* CSR 0 */ unsigned long elco_bcce; /* CSR 1 */ unsigned long elco_bccea; /* CSR 2 */ @@ -256,7 +256,7 @@ struct el_t2_data_other_cpu { * Sable other CPU error frame - sable pfms section 3.44 */ struct el_t2_data_t2{ - struct el_t2_frame_header elct_hdr; /* ID$T2-FRAME */ + struct el_t2_frame_header elct_hdr; /* ID$T2-FRAME */ unsigned long elct_iocsr; /* IO Control and Status Register */ unsigned long elct_cerr1; /* Cbus Error Register 1 */ unsigned long elct_cerr2; /* Cbus Error Register 2 */ @@ -294,31 +294,31 @@ struct el_t2_data_corrected { unsigned long elcpb_bc_tag; }; -/* +/* * Sable error log data structure * Note there are 4 memory slots on sable (see t2.h) */ struct el_t2_frame_mcheck { - struct el_t2_frame_header elfmc_header; /* ID$P-FRAME_MCHECK */ + struct el_t2_frame_header elfmc_header; /* ID$P-FRAME_MCHECK */ struct el_t2_logout_header elfmc_hdr; struct el_t2_procdata_mcheck elfmc_procdata; struct el_t2_sysdata_mcheck elfmc_sysdata; struct el_t2_data_t2 elfmc_t2data; - struct el_t2_data_memory elfmc_memdata[4]; - struct el_t2_frame_header elfmc_footer; /* empty */ + struct el_t2_data_memory elfmc_memdata[4]; + struct el_t2_frame_header elfmc_footer; /* empty */ }; -/* +/* * Sable error log data structures on memory errors */ struct el_t2_frame_corrected { - struct el_t2_frame_header elfcc_header; /* ID$P-BC-COR */ + struct el_t2_frame_header elfcc_header; /* ID$P-BC-COR */ struct el_t2_logout_header elfcc_hdr; - struct el_t2_data_corrected elfcc_procdata; + struct el_t2_data_corrected elfcc_procdata; /* struct el_t2_data_t2 elfcc_t2data; */ /* struct el_t2_data_memory elfcc_memdata[4]; */ - struct el_t2_frame_header elfcc_footer; /* empty */ + struct el_t2_frame_header elfcc_footer; /* empty */ }; @@ -398,10 +398,9 @@ __EXTERN_INLINE void t2_outl(unsigned int b, unsigned long addr) /* - * Memory functions. 64-bit and 32-bit accesses are done through - * dense memory space, everything else through sparse space. - * - * For reading and writing 8 and 16 bit quantities we need to + * Memory functions. + * + * For reading and writing 8 and 16 bit quantities we need to * go through one of the three sparse address mapping regions * and use the HAE_MEM CSR to provide some bits of the address. * The following few routines use only sparse address region 1 @@ -410,10 +409,10 @@ __EXTERN_INLINE void t2_outl(unsigned int b, unsigned long addr) * See p 6-17 of the specification but it looks something like this: * * 21164 Address: - * - * 3 2 1 + * + * 3 2 1 * 9876543210987654321098765432109876543210 - * 1ZZZZ0.PCI.QW.Address............BBLL + * 1ZZZZ0.PCI.QW.Address............BBLL * * ZZ = SBZ * BB = Byte offset @@ -421,12 +420,12 @@ __EXTERN_INLINE void t2_outl(unsigned int b, unsigned long addr) * * PCI Address: * - * 3 2 1 + * 3 2 1 * 10987654321098765432109876543210 * HHH....PCI.QW.Address........ 00 * * HHH = 31:29 HAE_MEM CSR - * + * */ __EXTERN_INLINE unsigned long t2_srm_base(unsigned long addr) @@ -495,19 +494,21 @@ __EXTERN_INLINE unsigned long t2_srm_readq(unsigned long addr) __EXTERN_INLINE void t2_srm_writeb(unsigned char b, unsigned long addr) { - unsigned long work = t2_srm_base(addr); + unsigned long w, work = t2_srm_base(addr); if (work) { work += 0x00; /* add transfer length */ - *(vuip) work = b * 0x01010101; + w = __kernel_insbl(b, addr & 3); + *(vuip) work = w; } } __EXTERN_INLINE void t2_srm_writew(unsigned short b, unsigned long addr) { - unsigned long work = t2_srm_base(addr); + unsigned long w, work = t2_srm_base(addr); if (work) { work += 0x08; /* add transfer length */ - *(vuip) work = b * 0x00010001; + w = __kernel_inswl(b, addr & 3); + *(vuip) work = w; } } @@ -537,11 +538,11 @@ __EXTERN_INLINE unsigned long t2_readb(unsigned long addr) { unsigned long result, msb; - msb = addr & 0xE0000000 ; - addr &= T2_MEM_R1_MASK ; + msb = addr & 0xE0000000; + addr &= T2_MEM_R1_MASK; set_hae(msb); - result = *(vip) ((addr << 5) + T2_SPARSE_MEM + 0x00) ; + result = *(vip) ((addr << 5) + T2_SPARSE_MEM + 0x00); return __kernel_extbl(result, addr & 3); } @@ -549,8 +550,8 @@ __EXTERN_INLINE unsigned long t2_readw(unsigned long addr) { unsigned long result, msb; - msb = addr & 0xE0000000 ; - addr &= T2_MEM_R1_MASK ; + msb = addr & 0xE0000000; + addr &= T2_MEM_R1_MASK; set_hae(msb); result = *(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x08); @@ -562,8 +563,8 @@ __EXTERN_INLINE unsigned long t2_readl(unsigned long addr) { unsigned long msb; - msb = addr & 0xE0000000 ; - addr &= T2_MEM_R1_MASK ; + msb = addr & 0xE0000000; + addr &= T2_MEM_R1_MASK; set_hae(msb); return *(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x18); @@ -573,8 +574,8 @@ __EXTERN_INLINE unsigned long t2_readq(unsigned long addr) { unsigned long r0, r1, work, msb; - msb = addr & 0xE0000000 ; - addr &= T2_MEM_R1_MASK ; + msb = addr & 0xE0000000; + addr &= T2_MEM_R1_MASK; set_hae(msb); work = (addr << 5) + T2_SPARSE_MEM + 0x18; @@ -585,33 +586,35 @@ __EXTERN_INLINE unsigned long t2_readq(unsigned long addr) __EXTERN_INLINE void t2_writeb(unsigned char b, unsigned long addr) { - unsigned long msb ; + unsigned long msb, w; - msb = addr & 0xE0000000 ; - addr &= T2_MEM_R1_MASK ; + msb = addr & 0xE0000000; + addr &= T2_MEM_R1_MASK; set_hae(msb); - *(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x00) = b * 0x01010101; + w = __kernel_insbl(b, addr & 3); + *(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x00) = w; } __EXTERN_INLINE void t2_writew(unsigned short b, unsigned long addr) { - unsigned long msb ; + unsigned long msb, w; - msb = addr & 0xE0000000 ; - addr &= T2_MEM_R1_MASK ; + msb = addr & 0xE0000000; + addr &= T2_MEM_R1_MASK; set_hae(msb); - *(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x08) = b * 0x00010001; + w = __kernel_inswl(b, addr & 3); + *(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x08) = w; } /* On SABLE with T2, we must use SPARSE memory even for 32-bit access. */ __EXTERN_INLINE void t2_writel(unsigned int b, unsigned long addr) { - unsigned long msb ; + unsigned long msb; - msb = addr & 0xE0000000 ; - addr &= T2_MEM_R1_MASK ; + msb = addr & 0xE0000000; + addr &= T2_MEM_R1_MASK; set_hae(msb); *(vuip) ((addr << 5) + T2_SPARSE_MEM + 0x18) = b; @@ -619,10 +622,10 @@ __EXTERN_INLINE void t2_writel(unsigned int b, unsigned long addr) __EXTERN_INLINE void t2_writeq(unsigned long b, unsigned long addr) { - unsigned long msb, work; + unsigned long msb, work; - msb = addr & 0xE0000000 ; - addr &= T2_MEM_R1_MASK ; + msb = addr & 0xE0000000; + addr &= T2_MEM_R1_MASK; set_hae(msb); work = (addr << 5) + T2_SPARSE_MEM + 0x18; @@ -630,11 +633,14 @@ __EXTERN_INLINE void t2_writeq(unsigned long b, unsigned long addr) *(vuip)(work + (4 << 5)) = b >> 32; } -/* Find the DENSE memory area for a given bus address. */ +__EXTERN_INLINE unsigned long t2_ioremap(unsigned long addr) +{ + return addr; +} -__EXTERN_INLINE unsigned long t2_dense_mem(unsigned long addr) +__EXTERN_INLINE int t2_is_ioaddr(unsigned long addr) { - return T2_DENSE_MEM; + return (long)addr >= 0; } #undef vip @@ -671,7 +677,8 @@ __EXTERN_INLINE unsigned long t2_dense_mem(unsigned long addr) #define __writeq t2_writeq #endif -#define dense_mem t2_dense_mem +#define __ioremap t2_ioremap +#define __is_ioaddr t2_is_ioaddr #define inb(port) \ (__builtin_constant_p((port))?__inb(port):_inb(port)) diff --git a/include/asm-alpha/core_tsunami.h b/include/asm-alpha/core_tsunami.h index 28a869bb007a..664043b49d69 100644 --- a/include/asm-alpha/core_tsunami.h +++ b/include/asm-alpha/core_tsunami.h @@ -158,7 +158,7 @@ union TPchipPERROR { unsigned perror_v_rsvd2 : 1; unsigned perror_v_cmd : 4; unsigned perror_v_syn : 8; - } perror_r_bits; + } perror_r_bits; int perror_q_whole [2]; }; @@ -178,7 +178,7 @@ union TPchipWSBA { unsigned wsba_v_rsvd1 : 17; unsigned wsba_v_addr : 12; unsigned wsba_v_rsvd2 : 32; - } wsba_r_bits; + } wsba_r_bits; int wsba_q_whole [2]; }; @@ -272,7 +272,7 @@ union TPchipPERRMASK { unsigned perrmask_v_cre : 1; unsigned perrmask_v_rsvd1 : 20; unsigned perrmask_v_rsvd2 : 32; - } perrmask_r_bits; + } perrmask_r_bits; int perrmask_q_whole [2]; }; @@ -369,55 +369,118 @@ __EXTERN_INLINE void tsunami_outl(unsigned int b, unsigned long addr) * Memory functions. all accesses are done through linear space. */ +__EXTERN_INLINE unsigned long tsunami_ioremap(unsigned long addr) +{ + return XADDR + TSUNAMI_MEM(XHOSE); +} + +__EXTERN_INLINE int tsunami_is_ioaddr(unsigned long addr) +{ + return addr >= IDENT_ADDR+TS_BIAS; +} + __EXTERN_INLINE unsigned long tsunami_readb(unsigned long addr) { - return __kernel_ldbu(*(vucp)(XADDR + TSUNAMI_MEM(XHOSE))); +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "tsunami: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = tsunami_ioremap(addr); + } +#endif + + return __kernel_ldbu(*(vucp)addr); } __EXTERN_INLINE unsigned long tsunami_readw(unsigned long addr) { - return __kernel_ldwu(*(vusp)(XADDR + TSUNAMI_MEM(XHOSE))); +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "tsunami: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = tsunami_ioremap(addr); + } +#endif + + return __kernel_ldwu(*(vusp)addr); } __EXTERN_INLINE unsigned long tsunami_readl(unsigned long addr) { - return *(vuip)(XADDR + TSUNAMI_MEM(XHOSE)); +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "tsunami: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = tsunami_ioremap(addr); + } +#endif + + return *(vuip)addr; } __EXTERN_INLINE unsigned long tsunami_readq(unsigned long addr) { - return *(vulp)(XADDR + TSUNAMI_MEM(XHOSE)); +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "tsunami: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = tsunami_ioremap(addr); + } +#endif + + return *(vulp)addr; } __EXTERN_INLINE void tsunami_writeb(unsigned char b, unsigned long addr) { - __kernel_stb(b, *(vucp)(XADDR + TSUNAMI_MEM(XHOSE))); - mb(); +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "tsunami: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = tsunami_ioremap(addr); + } +#endif + + __kernel_stb(b, *(vucp)addr); } __EXTERN_INLINE void tsunami_writew(unsigned short b, unsigned long addr) { - __kernel_stw(b, *(vusp)(XADDR + TSUNAMI_MEM(XHOSE))); - mb(); +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "tsunami: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = tsunami_ioremap(addr); + } +#endif + + __kernel_stw(b, *(vusp)addr); } __EXTERN_INLINE void tsunami_writel(unsigned int b, unsigned long addr) { - *(vuip)(XADDR + TSUNAMI_MEM(XHOSE)) = b; - mb(); +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "tsunami: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = tsunami_ioremap(addr); + } +#endif + + *(vuip)addr = b; } __EXTERN_INLINE void tsunami_writeq(unsigned long b, unsigned long addr) { - *(vulp)(XADDR + TSUNAMI_MEM(XHOSE)) = b; - mb(); -} - -/* Find the DENSE memory area for a given bus address. */ +#if __DEBUG_IOREMAP + if (addr <= 0x1000000000) { + printk(KERN_CRIT "tsunami: 0x%lx not ioremapped (%p)\n", + addr, __builtin_return_address(0)); + addr = tsunami_ioremap(addr); + } +#endif -__EXTERN_INLINE unsigned long tsunami_dense_mem(unsigned long addr) -{ - return TSUNAMI_MEM(XHOSE); + *(vulp)addr = b; } #undef vucp @@ -447,7 +510,8 @@ __EXTERN_INLINE unsigned long tsunami_dense_mem(unsigned long addr) #define __readq tsunami_readq #define __writel tsunami_writel #define __writeq tsunami_writeq -#define dense_mem tsunami_dense_mem +#define __ioremap tsunami_ioremap +#define __is_ioaddr tsunami_is_ioaddr #define inb(port) __inb((port)) #define inw(port) __inw((port)) @@ -457,15 +521,16 @@ __EXTERN_INLINE unsigned long tsunami_dense_mem(unsigned long addr) #define outw(v, port) __outw((v),(port)) #define outl(v, port) __outl((v),(port)) -#define readb(a) __readb((unsigned long)(a)) -#define readw(a) __readw((unsigned long)(a)) -#define readl(a) __readl((unsigned long)(a)) -#define readq(a) __readq((unsigned long)(a)) - -#define writeb(v,a) __writeb((v),(unsigned long)(a)) -#define writew(v,a) __writew((v),(unsigned long)(a)) -#define writel(v,a) __writel((v),(unsigned long)(a)) -#define writeq(v,a) __writeq((v),(unsigned long)(a)) +#if !__DEBUG_IOREMAP +#define __raw_readb(a) __readb((unsigned long)(a)) +#define __raw_readw(a) __readw((unsigned long)(a)) +#define __raw_readl(a) __readl((unsigned long)(a)) +#define __raw_readq(a) __readq((unsigned long)(a)) +#define __raw_writeb(v,a) __writeb((v),(unsigned long)(a)) +#define __raw_writeb(v,a) __writew((v),(unsigned long)(a)) +#define __raw_writel(v,a) __writel((v),(unsigned long)(a)) +#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a)) +#endif #endif /* __WANT_IO_DEF */ diff --git a/include/asm-alpha/io.h b/include/asm-alpha/io.h index 671e125751e6..24cd808892ae 100644 --- a/include/asm-alpha/io.h +++ b/include/asm-alpha/io.h @@ -1,7 +1,10 @@ #ifndef __ALPHA_IO_H #define __ALPHA_IO_H +#define __DEBUG_IOREMAP 1 + #include +#include #include /* We don't use IO slowdowns on the Alpha, but.. */ @@ -103,6 +106,9 @@ extern void _sethae (unsigned long addr); /* cached version */ # define __writel(v,a) alpha_mv.mv_writel((v),(unsigned long)(a)) # define __writeq(v,a) alpha_mv.mv_writeq((v),(unsigned long)(a)) +# define __ioremap(a) alpha_mv.mv_ioremap(a) +# define __is_ioaddr(a) alpha_mv.mv_is_ioaddr(a) + # define inb __inb # define inw __inw # define inl __inl @@ -110,16 +116,14 @@ extern void _sethae (unsigned long addr); /* cached version */ # define outw __outw # define outl __outl -# define readb __readb -# define readw __readw -# define readl __readl -# define readq __readq -# define writeb __writeb -# define writew __writew -# define writel __writel -# define writeq __writeq - -# define dense_mem(a) alpha_mv.mv_dense_mem(a) +# define __raw_readb __readb +# define __raw_readw __readw +# define __raw_readl __readl +# define __raw_readq __readq +# define __raw_writeb __writeb +# define __raw_writeb __writew +# define __raw_writel __writel +# define __raw_writeq __writeq #else @@ -244,21 +248,85 @@ extern void writel(unsigned int b, unsigned long addr); #ifdef __KERNEL__ /* - * The "address" in IO memory space is not clearly either an integer or a - * pointer. We will accept both, thus the casts. + * On Alpha, we have the whole of I/O space mapped at all times, but + * at odd and sometimes discontinuous addresses. Note that the + * discontinuities are all across busses, so we need not care for that + * for any one device. * - * On the alpha, we have the whole physical address space mapped at all - * times, so "ioremap()" and "iounmap()" do not need to do anything. + * Map the I/O space address into the kernel's virtual address space. */ static inline void * ioremap(unsigned long offset, unsigned long size) { - return (void *) offset; + return (void *) __ioremap(offset); } static inline void iounmap(void *addr) { } +/* Indirect back to the macros provided. */ + +extern unsigned long ___raw_readb(unsigned long addr); +extern unsigned long ___raw_readw(unsigned long addr); +extern unsigned long ___raw_readl(unsigned long addr); +extern unsigned long ___raw_readq(unsigned long addr); +extern void ___raw_writeb(unsigned char b, unsigned long addr); +extern void ___raw_writeb(unsigned short b, unsigned long addr); +extern void ___raw_writel(unsigned int b, unsigned long addr); +extern void ___raw_writeq(unsigned long b, unsigned long addr); + +#ifdef __raw_readb +# define readb(a) ({ unsigned long r_ = __raw_readb(a); mb(); r_; }) +#endif +#ifdef __raw_readw +# define readw(a) ({ unsigned long r_ = __raw_readw(a); mb(); r_; }) +#endif +#ifdef __raw_readl +# define readl(a) ({ unsigned long r_ = __raw_readl(a); mb(); r_; }) +#endif +#ifdef __raw_readq +# define readq(a) ({ unsigned long r_ = __raw_readq(a); mb(); r_; }) +#endif + +#ifdef __raw_writeb +# define writeb(v,a) ({ __raw_writeb((v),(a)); mb(); }) +#endif +#ifdef __raw_writeb +# define writew(v,a) ({ __raw_writeb((v),(a)); mb(); }) +#endif +#ifdef __raw_writel +# define writel(v,a) ({ __raw_writel((v),(a)); mb(); }) +#endif +#ifdef __raw_writeq +# define writeq(v,a) ({ __raw_writeq((v),(a)); mb(); }) +#endif + +#ifndef __raw_readb +# define __raw_readb(a) ___raw_readb((unsigned long)(a)) +#endif +#ifndef __raw_readw +# define __raw_readw(a) ___raw_readw((unsigned long)(a)) +#endif +#ifndef __raw_readl +# define __raw_readl(a) ___raw_readl((unsigned long)(a)) +#endif +#ifndef __raw_readq +# define __raw_readq(a) ___raw_readq((unsigned long)(a)) +#endif + +#ifndef __raw_writeb +# define __raw_writeb(v,a) ___raw_writeb((v),(unsigned long)(a)) +#endif +#ifndef __raw_writeb +# define __raw_writeb(v,a) ___raw_writeb((v),(unsigned long)(a)) +#endif +#ifndef __raw_writel +# define __raw_writel(v,a) ___raw_writel((v),(unsigned long)(a)) +#endif +#ifndef __raw_writeq +# define __raw_writeq(v,a) ___raw_writeq((v),(unsigned long)(a)) +#endif + #ifndef readb # define readb(a) _readb((unsigned long)(a)) #endif @@ -271,6 +339,7 @@ static inline void iounmap(void *addr) #ifndef readq # define readq(a) _readq((unsigned long)(a)) #endif + #ifndef writeb # define writeb(v,a) _writeb((v),(unsigned long)(a)) #endif diff --git a/include/asm-alpha/jensen.h b/include/asm-alpha/jensen.h index 874511e94eb6..826dfc54d4dd 100644 --- a/include/asm-alpha/jensen.h +++ b/include/asm-alpha/jensen.h @@ -292,12 +292,14 @@ __EXTERN_INLINE void jensen_writeq(unsigned long b, unsigned long addr) *(vuip) (addr + (4 << 7)) = b >> 32; } -/* Find the DENSE memory area for a given bus address. - Whee, there is none. */ +__EXTERN_INLINE unsigned long jensen_ioremap(unsigned long addr) +{ + return addr; +} -__EXTERN_INLINE unsigned long jensen_dense_mem(unsigned long addr) +__EXTERN_INLINE int jensen_is_ioaddr(unsigned long addr) { - return 0; + return (long)addr >= 0; } #undef vuip @@ -320,7 +322,8 @@ __EXTERN_INLINE unsigned long jensen_dense_mem(unsigned long addr) #define __readq jensen_readq #define __writel jensen_writel #define __writeq jensen_writeq -#define dense_mem jensen_dense_mem +#define __ioremap jensen_ioremap +#define __is_ioaddr jensen_is_ioaddr /* * The above have so much overhead that it probably doesn't make diff --git a/include/asm-alpha/machvec.h b/include/asm-alpha/machvec.h index c8a65b4be86b..4d66386b4aa9 100644 --- a/include/asm-alpha/machvec.h +++ b/include/asm-alpha/machvec.h @@ -56,7 +56,8 @@ struct alpha_machine_vector void (*mv_writel)(unsigned int, unsigned long); void (*mv_writeq)(unsigned long, unsigned long); - unsigned long (*mv_dense_mem)(unsigned long); + unsigned long (*mv_ioremap)(unsigned long); + int (*mv_is_ioaddr)(unsigned long); int (*hose_read_config_byte)(u8, u8, u8, u8 *value, struct linux_hose_info *); diff --git a/include/asm-alpha/smp.h b/include/asm-alpha/smp.h index feb0f1e8581c..499363b1f99f 100644 --- a/include/asm-alpha/smp.h +++ b/include/asm-alpha/smp.h @@ -1,10 +1,25 @@ #ifndef __ASM_SMP_H #define __ASM_SMP_H +#include + +/* HACK: Cabrio WHAMI return value is bogus if more than 8 bits used.. :-( */ + +static __inline__ unsigned char +__hard_smp_processor_id(void) +{ + register unsigned char __r0 __asm__("$0"); + __asm__ __volatile__( + "call_pal %1 #whami" + : "=r"(__r0) + :"i" (PAL_whami) + : "$1", "$22", "$23", "$24", "$25"); + return __r0; +} + #ifdef __SMP__ #include -#include struct cpuinfo_alpha { unsigned long loops_per_sec; @@ -38,20 +53,6 @@ extern int __cpu_logical_map[NR_CPUS]; #endif /* __SMP__ */ -/* HACK: Cabrio WHAMI return value is bogus if more than 8 bits used.. :-( */ - -static __inline__ unsigned char -__hard_smp_processor_id(void) -{ - register unsigned char __r0 __asm__("$0"); - __asm__ __volatile__( - "call_pal %1 #whami" - : "=r"(__r0) - :"i" (PAL_whami) - : "$1", "$22", "$23", "$24", "$25"); - return __r0; -} - #define NO_PROC_ID (-1) #endif diff --git a/include/asm-alpha/vga.h b/include/asm-alpha/vga.h index 7e66f50d3664..1bfc05344ede 100644 --- a/include/asm-alpha/vga.h +++ b/include/asm-alpha/vga.h @@ -15,26 +15,26 @@ extern inline void scr_writew(u16 val, u16 *addr) { - if ((long) addr < 0) - *addr = val; - else + if (__is_ioaddr((unsigned long) addr)) writew(val, (unsigned long) addr); + else + *addr = val; } extern inline u16 scr_readw(const u16 *addr) { - if ((long) addr < 0) - return *addr; - else + if (__is_ioaddr((unsigned long) addr)) return readw((unsigned long) addr); + else + return *addr; } extern inline void scr_memsetw(u16 *s, u16 c, unsigned int count) { - if ((long)s < 0) - memsetw(s, c, count); - else + if (__is_ioaddr((unsigned long) s)) memsetw_io(s, c, count); + else + memsetw(s, c, count); } extern inline void scr_memcpyw_from(u16 *d, const u16 *s, unsigned int count) @@ -51,6 +51,6 @@ extern inline void scr_memcpyw_to(u16 *d, const u16 *s, unsigned int count) #define vga_readb readb #define vga_writeb writeb -#define VGA_MAP_MEM(x) (x) +#define VGA_MAP_MEM(x) ((unsigned long) ioremap((x), 0)) #endif diff --git a/include/asm-m68k/fpu.h b/include/asm-m68k/fpu.h index 717ecfa7fed8..3bcf85065c19 100644 --- a/include/asm-m68k/fpu.h +++ b/include/asm-m68k/fpu.h @@ -11,6 +11,8 @@ #define FPSTATESIZE (216/sizeof(unsigned char)) #elif defined(CONFIG_M68040) #define FPSTATESIZE (96/sizeof(unsigned char)) +#elif defined(CONFIG_M68KFPU_EMU) +#define FPSTATESIZE (28/sizeof(unsigned char)) #elif defined(CONFIG_M68060) #define FPSTATESIZE (12/sizeof(unsigned char)) #else diff --git a/include/asm-m68k/irq.h b/include/asm-m68k/irq.h index 1614ecd0bc05..0a62d30ef96a 100644 --- a/include/asm-m68k/irq.h +++ b/include/asm-m68k/irq.h @@ -79,13 +79,17 @@ extern int sys_request_irq(unsigned int, extern void sys_free_irq(unsigned int, void *); /* - * various flags for request_irq() + * various flags for request_irq() - the Amiga now uses the standard + * mechanism like all other architectures - SA_INTERRUPT and SA_SHIRQ + * are your friends. */ +#ifndef CONFIG_AMIGA #define IRQ_FLG_LOCK (0x0001) /* handler is not replaceable */ #define IRQ_FLG_REPLACE (0x0002) /* replace existing handler */ #define IRQ_FLG_FAST (0x0004) #define IRQ_FLG_SLOW (0x0008) #define IRQ_FLG_STD (0x8000) /* internally used */ +#endif /* * This structure is used to chain together the ISRs for a particular diff --git a/include/asm-m68k/math-emu.h b/include/asm-m68k/math-emu.h new file mode 100644 index 000000000000..a09dc54f2ecc --- /dev/null +++ b/include/asm-m68k/math-emu.h @@ -0,0 +1,300 @@ +#ifndef _ASM_M68K_SETUP_H +#define _ASM_M68K_SETUP_H + +#include +#include + +/* Status Register bits */ + +/* accrued exception bits */ +#define FPSR_AEXC_INEX 3 +#define FPSR_AEXC_DZ 4 +#define FPSR_AEXC_UNFL 5 +#define FPSR_AEXC_OVFL 6 +#define FPSR_AEXC_IOP 7 + +/* exception status bits */ +#define FPSR_EXC_INEX1 8 +#define FPSR_EXC_INEX2 9 +#define FPSR_EXC_DZ 10 +#define FPSR_EXC_UNFL 11 +#define FPSR_EXC_OVFL 12 +#define FPSR_EXC_OPERR 13 +#define FPSR_EXC_SNAN 14 +#define FPSR_EXC_BSUN 15 + +/* quotient byte, assumes big-endian, of course */ +#define FPSR_QUOTIENT(fpsr) (*((signed char *) &(fpsr) + 1)) + +/* condition code bits */ +#define FPSR_CC_NAN 24 +#define FPSR_CC_INF 25 +#define FPSR_CC_Z 26 +#define FPSR_CC_NEG 27 + + +/* Control register bits */ + +/* rounding mode */ +#define FPCR_ROUND_RN 0 /* round to nearest/even */ +#define FPCR_ROUND_RZ 1 /* round to zero */ +#define FPCR_ROUND_RM 2 /* minus infinity */ +#define FPCR_ROUND_RP 3 /* plus infinity */ + +/* rounding precision */ +#define FPCR_PRECISION_X 0 /* long double */ +#define FPCR_PRECISION_S 1 /* double */ +#define FPCR_PRECISION_D 2 /* float */ + + +/* Flags to select the debugging output */ +#define PDECODE 0 +#define PEXECUTE 1 +#define PCONV 2 +#define PNORM 3 +#define PREGISTER 4 +#define PINSTR 5 +#define PUNIMPL 6 +#define PMOVEM 7 + +#define PMDECODE (1< +#include + +union fp_mant64 { + unsigned long long m64; + unsigned long m32[2]; +}; + +union fp_mant128 { + unsigned long long m64[2]; + unsigned long m32[4]; +}; + +/* internal representation of extended fp numbers */ +struct fp_ext { + unsigned char lowmant; + unsigned char sign; + unsigned short exp; + union fp_mant64 mant; +}; + +/* C representation of FPU registers */ +/* NOTE: if you change this, you have to change the assembler offsets + below and the size in , too */ +struct fp_data { + struct fp_ext fpreg[8]; + unsigned int fpcr; + unsigned int fpsr; + unsigned int fpiar; + unsigned short prec; + unsigned short rnd; + struct fp_ext temp[2]; +}; + +#if FPU_EMU_DEBUG +extern unsigned int fp_debugprint; + +#define dprint(bit, fmt, args...) ({ \ + if (fp_debugprint & (1 << (bit))) \ + printk(fmt, ## args); \ +}) +#else +#define dprint(bit, fmt, args...) +#endif + +#define uprint(str) ({ \ + static int __count = 3; \ + \ + if (__count > 0) { \ + printk("You just hit an unimplemented " \ + "fpu instruction (%s)\n", str); \ + printk("Please report this to ....\n"); \ + __count--; \ + } \ +}) + +#define FPDATA ((struct fp_data *)current->thread.fp) + +#else /* __ASSEMBLY__ */ + +#define FPDATA %a2 + +/* offsets from the base register to the floating point data in the task struct */ +#define FPD_FPREG (TASK_THREAD+THREAD_FPREG+0) +#define FPD_FPCR (TASK_THREAD+THREAD_FPREG+96) +#define FPD_FPSR (TASK_THREAD+THREAD_FPREG+100) +#define FPD_FPIAR (TASK_THREAD+THREAD_FPREG+104) +#define FPD_PREC (TASK_THREAD+THREAD_FPREG+108) +#define FPD_RND (TASK_THREAD+THREAD_FPREG+110) +#define FPD_TEMPFP1 (TASK_THREAD+THREAD_FPREG+112) +#define FPD_TEMPFP2 (TASK_THREAD+THREAD_FPREG+124) +#define FPD_SIZEOF (TASK_THREAD+THREAD_FPREG+136) + +/* offsets on the stack to access saved registers, + * these are only used during instruction decoding + * where we always know how deep we're on the stack. + */ +#define FPS_DO (PT_D0) +#define FPS_D1 (PT_D1) +#define FPS_D2 (PT_D2) +#define FPS_A0 (PT_A0) +#define FPS_A1 (PT_A1) +#define FPS_A2 (PT_A2) +#define FPS_SR (PT_SR) +#define FPS_PC (PT_PC) +#define FPS_EA (PT_PC+6) +#define FPS_PC2 (PT_PC+10) + +.macro fp_get_fp_reg + lea (FPD_FPREG,FPDATA,%d0.w*4),%a0 + lea (%a0,%d0.w*8),%a0 +.endm + +/* Macros used to get/put the current program counter. + * 020/030 use a different stack frame then 040/060, for the + * 040/060 the return pc points already to the next location, + * so this only needs to be modified for jump instructions. + */ +.macro fp_get_pc dest + move.l (FPS_PC+4,%sp),\dest +.endm + +.macro fp_put_pc src,jump=0 + move.l \src,(FPS_PC+4,%sp) +.endm + +.macro fp_get_instr_data f,s,dest,label + getuser \f,%sp@(FPS_PC+4)@(0),\dest,\label,%sp@(FPS_PC+4) + addq.l #\s,%sp@(FPS_PC+4) +.endm + +.macro fp_get_instr_word dest,label,addr + fp_get_instr_data w,2,\dest,\label,\addr +.endm + +.macro fp_get_instr_long dest,label,addr + fp_get_instr_data l,4,\dest,\label,\addr +.endm + +/* These macros are used to read from/write to user space + * on error we jump to the fixup section, load the fault + * address into %a0 and jump to the exit. + * (derived from ) + */ +.macro getuser size,src,dest,label,addr +| printf ,"[\size<%08x]",1,\addr +.Lu1\@: moves\size \src,\dest + + .section .fixup,"ax" + .even +.Lu2\@: move.l \addr,%a0 + jra \label + .previous + + .section __ex_table,"a" + .align 4 + .long .Lu1\@,.Lu2\@ + .previous +.endm + +.macro putuser size,src,dest,label,addr +| printf ,"[\size>%08x]",1,\addr +.Lu1\@: moves\size \src,\dest +.Lu2\@: + + .section .fixup,"ax" + .even +.Lu3\@: move.l \addr,%a0 + jra \label + .previous + + .section __ex_table,"a" + .align 4 + .long .Lu1\@,.Lu3\@ + .long .Lu2\@,.Lu3\@ + .previous +.endm + + +.macro movestack nr,arg1,arg2,arg3,arg4,arg5 + .if \nr + movestack (\nr-1),\arg2,\arg3,\arg4,\arg5 + move.l \arg1,-(%sp) + .endif +.endm + +.macro printf bit=-1,string,nr=0,arg1,arg2,arg3,arg4,arg5 +#ifdef FPU_EMU_DEBUG + .data +.Lpdata\@: + .string "\string" + .previous + + movem.l %d0/%d1/%a0/%a1,-(%sp) + .if \bit+1 +#if 0 + moveq #\bit,%d0 + andw #7,%d0 + btst %d0,fp_debugprint+((31-\bit)/8) +#else + btst #\bit,fp_debugprint+((31-\bit)/8) +#endif + jeq .Lpskip\@ + .endif + movestack \nr,\arg1,\arg2,\arg3,\arg4,\arg5 + pea .Lpdata\@ + jsr printk + lea ((\nr+1)*4,%sp),%sp +.Lpskip\@: + movem.l (%sp)+,%d0/%d1/%a0/%a1 +#endif +.endm + +.macro printx bit,fp +#ifdef FPU_EMU_DEBUG + movem.l %d0/%a0,-(%sp) + lea \fp,%a0 +#if 0 + moveq #'+',%d0 + tst.w (%a0) + jeq .Lx1\@ + moveq #'-',%d0 +.Lx1\@: printf \bit," %c",1,%d0 + move.l (4,%a0),%d0 + bclr #31,%d0 + jne .Lx2\@ + printf \bit,"0." + jra .Lx3\@ +.Lx2\@: printf \bit,"1." +.Lx3\@: printf \bit,"%08x%08x",2,%d0,%a0@(8) + move.w (2,%a0),%d0 + ext.l %d0 + printf \bit,"E%04x",1,%d0 +#else + printf \bit," %08x%08x%08x",3,%a0@,%a0@(4),%a0@(8) +#endif + movem.l (%sp)+,%d0/%a0 +#endif +.endm + +.macro debug instr,args +#ifdef FPU_EMU_DEBUG + \instr \args +#endif +.endm + + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_M68K_SETUP_H */ diff --git a/include/asm-m68k/setup.h b/include/asm-m68k/setup.h index d857208fe291..5b6b8c73b92e 100644 --- a/include/asm-m68k/setup.h +++ b/include/asm-m68k/setup.h @@ -315,6 +315,16 @@ extern int m68k_is040or060; #define CPU_TYPE (m68k_cputype) +#ifdef CONFIG_M68KFPU_EMU +# ifdef CONFIG_M68KFPU_EMU_ONLY +# define FPU_IS_EMU (1) +# else +# define FPU_IS_EMU (!m68k_fputype) +# endif +#else +# define FPU_IS_EMU (0) +#endif + /* * Miscellaneous diff --git a/include/linux/cyclomx.h b/include/linux/cyclomx.h index 5c04c73e1399..fa6ca3b0d39c 100644 --- a/include/linux/cyclomx.h +++ b/include/linux/cyclomx.h @@ -21,6 +21,7 @@ #ifndef _CYCLOMX_H #define _CYCLOMX_H +#include #include #include diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 28309dc8b1e3..b22c43a0b523 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -8,6 +8,19 @@ #ifndef _LINUX_IOPORT_H #define _LINUX_IOPORT_H +#define DEVICE_IO_NOTSET (~0) +#define DEVICE_IO_AUTO ((~0)-1) + +#define DEVICE_IO_FLAG_WRITEABLE (1<<0) +#define DEVICE_IO_FLAG_CACHEABLE (1<<1) +#define DEVICE_IO_FLAG_RANGELENGTH (1<<2) +#define DEVICE_IO_FLAG_SHADOWABLE (1<<4) +#define DEVICE_IO_FLAG_EXPANSIONROM (1<<5) + +#define DEVICE_IO_TYPE_8BIT 0 +#define DEVICE_IO_TYPE_16BIT 1 +#define DEVICE_IO_TYPE_8AND16BIT 2 + /* * Resources are tree-like, allowing * nesting etc.. @@ -16,6 +29,10 @@ struct resource { const char *name; unsigned long start, end; unsigned long flags; + unsigned char bits; /* decoded bits */ + unsigned char fixed; /* fixed range */ + unsigned short hw_flags; /* hardware flags */ + unsigned short type; /* region type */ struct resource *parent, *sibling, *child; }; diff --git a/include/linux/isapnp.h b/include/linux/isapnp.h new file mode 100644 index 000000000000..9aab523090a6 --- /dev/null +++ b/include/linux/isapnp.h @@ -0,0 +1,178 @@ +/* + * ISA Plug & Play support + * Copyright (c) by Jaroslav Kysela + * + * + * 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. + * + */ + +#ifndef LINUX_ISAPNP_H +#define LINUX_ISAPNP_H + +#include + +/* + * Configuration registers (TODO: change by specification) + */ + +#define ISAPNP_CFG_ACTIVATE 0x30 /* byte */ +#define ISAPNP_CFG_MEM 0x40 /* 4 * dword */ +#define ISAPNP_CFG_PORT 0x60 /* 8 * word */ +#define ISAPNP_CFG_IRQ 0x70 /* 2 * word */ +#define ISAPNP_CFG_DMA 0x74 /* 2 * byte */ + +/* + * + */ + +#define ISAPNP_VENDOR(a,b,c) (((((a)-'A'+1)&0x3f)<<2)|\ + ((((b)-'A'+1)&0x18)>>3)|((((b)-'A'+1)&7)<<13)|\ + ((((c)-'A'+1)&0x1f)<<8)) +#define ISAPNP_DEVICE(x) ((((x)&0xf000)>>8)|\ + (((x)&0x0f00)>>8)|\ + (((x)&0x00f0)<<8)|\ + (((x)&0x000f)<<8)) +#define ISAPNP_FUNCTION(x) ISAPNP_DEVICE(x) + +/* + * + */ + +#ifdef __KERNEL__ + +#include + +#define ISAPNP_PORT_FLAG_16BITADDR (1<<0) +#define ISAPNP_PORT_FLAG_FIXED (1<<1) + +struct isapnp_port { + unsigned short min; /* min base number */ + unsigned short max; /* max base number */ + unsigned char align; /* align boundary */ + unsigned char size; /* size of range */ + unsigned short flags; /* port flags */ + struct isapnp_resources *res; /* parent */ + struct isapnp_port *next; /* next port */ +}; + +struct isapnp_irq { + unsigned short map; /* bitmaks for IRQ lines */ + unsigned short flags; /* IRQ flags */ + struct isapnp_resources *res; /* parent */ + struct isapnp_irq *next; /* next IRQ */ +}; + +struct isapnp_dma { + unsigned char map; /* bitmask for DMA channels */ + unsigned char type; /* DMA type */ + unsigned char flags; /* DMA flags */ + unsigned char speed; /* DMA speed */ + struct isapnp_resources *res; /* parent */ + struct isapnp_dma *next; /* next port */ +}; + +struct isapnp_mem { + unsigned int min; /* min base number */ + unsigned int max; /* max base number */ + unsigned int align; /* align boundary */ + unsigned int size; /* size of range */ + unsigned short flags; /* memory flags */ + unsigned short type; /* memory type */ + struct isapnp_resources *res; /* parent */ + struct isapnp_mem *next; /* next memory resource */ +}; + +struct isapnp_mem32 { + /* TODO */ + unsigned char data[17]; + struct isapnp_resources *res; /* parent */ + struct isapnp_mem32 *next; /* next 32-bit memory resource */ +}; + +#define ISAPNP_RES_PRIORITY_PREFERRED 0 +#define ISAPNP_RES_PRIORITY_ACCEPTABLE 1 +#define ISAPNP_RES_PRIORITY_FUNCTIONAL 2 +#define ISAPNP_RES_PRIORITY_INVALID 65535 + +struct isapnp_resources { + unsigned short priority; /* priority */ + unsigned short dependent; /* dependent resources */ + struct isapnp_port *port; /* first port */ + struct isapnp_irq *irq; /* first IRQ */ + struct isapnp_dma *dma; /* first DMA */ + struct isapnp_mem *mem; /* first memory resource */ + struct isapnp_mem32 *mem32; /* first 32-bit memory */ + struct pci_dev *dev; /* parent */ + struct isapnp_resources *alt; /* alternative resource (aka dependent resources) */ + struct isapnp_resources *next; /* next resource */ +}; + +#ifdef CONFIG_ISAPNP + +/* lowlevel configuration */ +int isapnp_present(void); +int isapnp_cfg_begin(int csn, int device); +int isapnp_cfg_end(void); +unsigned char isapnp_read_byte(unsigned char idx); +unsigned short isapnp_read_word(unsigned char idx); +unsigned int isapnp_read_dword(unsigned char idx); +void isapnp_write_byte(unsigned char idx, unsigned char val); +void isapnp_write_word(unsigned char idx, unsigned short val); +void isapnp_write_dword(unsigned char idx, unsigned int val); +void isapnp_wake(unsigned char csn); +void isapnp_device(unsigned char device); +void isapnp_activate(unsigned char device); +void isapnp_deactivate(unsigned char device); +/* manager */ +struct pci_bus *isapnp_find_card(unsigned short vendor, + unsigned short device, + struct pci_bus *from); +struct pci_dev *isapnp_find_dev(struct pci_bus *card, + unsigned short vendor, + unsigned short function, + struct pci_dev *from); +/* init/main.c */ +int isapnp_init(void); + +#else /* !CONFIG_ISAPNP */ + +/* lowlevel configuration */ +extern inline int isapnp_present(void) { return 0; } +extern inline int isapnp_cfg_begin(int csn, int device) { return -ENODEV; } +extern inline int isapnp_cfg_end(void) { return -ENODEV; } +extern inline unsigned char isapnp_read_byte(unsigned char idx) { return 0xff; } +extern inline unsigned short isapnp_read_word(unsigned char idx) { return 0xffff; } +extern inline unsigned int isapnp_read_dword(unsigned char idx) { return 0xffffffff; } +extern inline void isapnp_write_byte(unsigned char idx, unsigned char val) { ; } +extern inline void isapnp_write_word(unsigned char idx, unsigned short val) { ; } +extern inline void isapnp_write_dword(unsigned char idx, unsigned int val) { ; } +extern void isapnp_wake(unsigned char csn) { ; } +extern void isapnp_device(unsigned char device) { ; } +extern void isapnp_activate(unsigned char device) { ; } +extern void isapnp_deactivate(unsigned char device) { ; } +/* manager */ +extern struct pci_bus *isapnp_find_card(unsigned short vendor, + unsigned short device, + struct pci_bus *from) { return NULL; } +extern struct pci_dev *isapnp_find_dev(struct pci_bus *card, + unsigned short vendor, + unsigned short function, + struct pci_dev *from) { return NULL; } + +#endif /* CONFIG_ISAPNP */ + +#endif /* __KERNEL__ */ +#endif /* LINUX_ISAPNP_H */ diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 853f236f6fef..79190775fa0a 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -393,9 +393,6 @@ typedef struct { #ifdef __KERNEL__ -#ifndef STANDALONE -#include -#endif #include #include #include diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h index a62101d0f778..9ca7dc7cde32 100644 --- a/include/linux/isdn_ppp.h +++ b/include/linux/isdn_ppp.h @@ -3,6 +3,7 @@ #ifndef _LINUX_ISDN_PPP_H #define _LINUX_ISDN_PPP_H +#include #include #define CALLTYPE_INCOMING 0x1 diff --git a/include/linux/isdnif.h b/include/linux/isdnif.h index aeb177ab3956..35b66bba836d 100644 --- a/include/linux/isdnif.h +++ b/include/linux/isdnif.h @@ -134,6 +134,7 @@ #ifndef isdnif_h #define isdnif_h +#include #include /* diff --git a/include/linux/pci.h b/include/linux/pci.h index e6f96a396f56..e9111e6bd21b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1208,11 +1208,41 @@ #include #include +#define DEVICE_COUNT_COMPATIBLE 4 +#define DEVICE_COUNT_DMA 2 +#define DEVICE_COUNT_RESOURCE 12 + +#define DEVICE_IRQ_NOTSET 0xffffffff +#define DEVICE_IRQ_AUTO 0xfffffffe +#define DEVICE_DMA_NOTSET 0xff +#define DEVICE_DMA_AUTO 0xfe + +#define DEVICE_IRQ_FLAG_HIGHEDGE (1<<0) +#define DEVICE_IRQ_FLAG_LOWEDGE (1<<1) +#define DEVICE_IRQ_FLAG_HIGHLEVEL (1<<2) +#define DEVICE_IRQ_FLAG_LOWLEVEL (1<<3) + +#define DEVICE_DMA_TYPE_8BIT 0 +#define DEVICE_DMA_TYPE_8AND16BIT 1 +#define DEVICE_DMA_TYPE_16BIT 2 + +#define DEVICE_DMA_FLAG_MASTER (1<<0) +#define DEVICE_DMA_FLAG_BYTE (1<<1) +#define DEVICE_DMA_FLAG_WORD (1<<2) + +#define DEVICE_DMA_SPEED_COMPATIBLE 0 +#define DEVICE_DMA_SPEED_TYPEA 1 +#define DEVICE_DMA_SPEED_TYPEB 2 +#define DEVICE_DMA_SPEED_TYPEF 3 + /* * There is one pci_dev structure for each slot-number/function-number * combination: */ struct pci_dev { + int active; /* device is active */ + int ro; /* Read/Only */ + struct pci_bus *bus; /* bus this device is on */ struct pci_dev *sibling; /* next device on this bus */ struct pci_dev *next; /* chain of all devices */ @@ -1227,6 +1257,12 @@ struct pci_dev { unsigned int hdr_type; /* PCI header type */ unsigned int master : 1; /* set if device is master capable */ + unsigned short regs; + + /* device is compatible with these IDs */ + unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE]; + unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE]; + char name[48]; /* @@ -1241,12 +1277,23 @@ struct pci_dev { * cannot generate interrupts at all. */ unsigned int irq; /* irq generated by this device */ + unsigned short irq_flags; /* irq type */ + unsigned int irq2; + unsigned short irq2_flags; + unsigned char dma[DEVICE_COUNT_DMA]; + unsigned char dma_type[DEVICE_COUNT_DMA]; + unsigned char dma_flags[DEVICE_COUNT_DMA]; + unsigned char dma_speed[DEVICE_COUNT_DMA]; /* Base registers for this device, can be adjusted by * pcibios_fixup() as necessary. */ - struct resource resource[6]; + struct resource resource[DEVICE_COUNT_RESOURCE]; unsigned long rom_address; + + int (*prepare)(struct pci_dev *dev); + int (*activate)(struct pci_dev *dev); + int (*deactivate)(struct pci_dev *dev); }; struct pci_bus { @@ -1264,6 +1311,15 @@ struct pci_bus { unsigned char primary; /* number of primary bridge */ unsigned char secondary; /* number of secondary bridge */ unsigned char subordinate; /* max number of subordinate buses */ + + char name[48]; + unsigned short vendor; + unsigned short device; + unsigned int serial; /* serial number */ + unsigned char pnpver; /* Plug & Play version */ + unsigned char productver; /* product version */ + unsigned char checksum; /* if zero - checksum passed */ + unsigned char pad1; }; extern struct pci_bus pci_root; /* root bus */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 065b91c94aff..26bf3ed71a49 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -716,6 +716,7 @@ do { \ add_wait_queue(&wq, &__wait); \ for (;;) { \ current->state = TASK_UNINTERRUPTIBLE; \ + mb(); \ if (condition) \ break; \ schedule(); \ @@ -739,6 +740,7 @@ do { \ add_wait_queue(&wq, &__wait); \ for (;;) { \ current->state = TASK_INTERRUPTIBLE; \ + mb(); \ if (condition) \ break; \ if (!signal_pending(current)) { \ diff --git a/init/main.c b/init/main.c index cf5013ddf366..4f3df4c74fb3 100644 --- a/init/main.c +++ b/init/main.c @@ -52,6 +52,10 @@ extern void nubus_init(void); #endif +#ifdef CONFIG_ISAPNP +#include +#endif + /* * Versions of gcc older than that listed below may actually compile * and link okay, but the end product can have subtle run time bugs. @@ -632,6 +636,9 @@ static void __init do_basic_setup(void) #ifdef CONFIG_MAC nubus_init(); #endif +#ifdef CONFIG_ISAPNP + isapnp_init(); +#endif /* Networking initialization needs a process context */ sock_init(); diff --git a/kernel/printk.c b/kernel/printk.c index ed438f2d96e8..09fb686280e9 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -118,7 +118,7 @@ __setup("console=", console_setup); */ int do_syslog(int type, char * buf, int len) { - unsigned long i, j, count, flags; + unsigned long i, j, count; int do_clear = 0; char c; int error = -EPERM; diff --git a/mm/vmscan.c b/mm/vmscan.c index 6ea7e979152b..6178c6abfe3c 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -50,10 +50,7 @@ static int try_to_swap_out(struct vm_area_struct* vma, unsigned long address, pt if (pte_val(pte) != pte_val(*page_table)) goto out_failed_unlock; - /* - * Dont be too eager to get aging right if - * memory is dangerously low. - */ + /* Don't look at this pte if it's been accessed recently. */ if (pte_young(pte)) { /* * Transfer the "accessed" bit from the page diff --git a/net/netsyms.c b/net/netsyms.c index f96167bc86ca..b5abf8660e08 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -178,7 +178,9 @@ EXPORT_SYMBOL(neigh_sysctl_register); #endif EXPORT_SYMBOL(pneigh_lookup); EXPORT_SYMBOL(pneigh_enqueue); +EXPORT_SYMBOL(neigh_create); EXPORT_SYMBOL(neigh_destroy); +EXPORT_SYMBOL(neigh_lookup); EXPORT_SYMBOL(neigh_parms_alloc); EXPORT_SYMBOL(neigh_parms_release); EXPORT_SYMBOL(neigh_rand_reach_time); -- 2.39.5