+++ /dev/null
- ARM Linux 2.1.99
- ================
-
- Since this is a development kernel, it will not be as stable as the 2.0
- series, and can cause very nasty problems (eg, trashing your hard disk).
- When running one of these kernels, I advise you to back up the complete
- contents of all your hard disks.
-
-
-Contributors
-------------
-
- Here is a list of people actively working on the project (If you
- wish to be added to the list, please email me):
-
- Name: Russell King
- Mail: linux@arm.uk.linux.org
- Desc: Original developer of ARM Linux, project co-ordinator.
-
- Name: Dave Gilbert
- Mail: linux@treblig.org
- Desc: A3/4/5xx floppy and hard disk code maintainer.
-
- Name: Philip Blundell
- Mail: Philip.Blundell@pobox.com
- Desc: Architecture and processor selection during make config.
-
-
-Todo list
----------
-
- This is the list of changes to be done (roughly prioritised):
-
- * fully test new MEMC translation code
- * fully test new AcornSCSI driver.
- * reply to email ;)
-
-
- Notes
- =====
-
-Compilation of kernel
----------------------
-
- In order to compile ARM Linux, you will need a compiler capable of
- generating ARM ELF code with GNU extensions. GCC-2.7.2.2 is good.
-
- To build ARM Linux natively, you shouldn't have to alter the ARCH = line in
- the top level Makefile. However, if you don't have the ARM Linux ELF tools
- installed as default, then you should change the CROSS_COMPILE line as
- detailed below.
-
- If you wish to cross-compile, then alter the following lines in the top
- level make file:
-
- ARCH = <whatever>
- with
- ARCH = arm
-
- and
-
- CROSS_COMPILE=
- to
- CROSS_COMPILE=<your-path-to-your-compiler-without-gcc>
- eg.
- CROSS_COMPILE=/usr/bin/arm-unknown-linuxelf-
-
- Do a 'make config', followed by 'make dep', and finally 'make all' to
- build the kernel (vmlinux). A compressed image can be built by doing
- a 'make zImage' instead of 'make all'.
-
-
-Bug reports etc.
-----------------
-
- Please send patches, bug reports and code for the ARM Linux project
- to linux@arm.uk.linux.org. Patches will not be included into future
- kernels unless they come to me (or the relevant person concerned).
-
- When sending bug reports, please ensure that they contain all relevant
- information, eg. the kernel messages that were printed before/during
- the problem, what you were doing, etc.
-
- For patches, please include some explanation as to what the patch does
- and why (if relevant).
-
-
-Modules
--------
-
- Although modularisation is supported (and required for the FP emulator),
- each module on an arm2/arm250/arm3 machine when is loaded will take
- memory up to the next 32k boundary due to the size of the pages. Hence is
- modularisation on these machines really worth it?
-
- However, arm6 and up machines allow modules to take multiples of 4k, and
- as such Acorn RiscPCs and other architectures using these processors can
- make good use of modularisation.
-
-
-ADFS Image files
-----------------
-
- You can access image files on your ADFS partitions by mounting the ADFS
- partition, and then using the loopback device driver. You must have
- losetup installed.
-
- Please note that the PCEmulator DOS partitions have a partition table at
- the start, and as such, you will have to give '-o offset' to losetup.
-
-
-Kernel initialisation abort codes
----------------------------------
-
- When the kernel is unable to boot, it will if possible display a colour
- at the top of the screen. The colours have the following significance
- when run in a 16 colour mode with the default palette:
-
- Stripes of white, red, yellow, and green:
- Kernel does not support the processor architecture detected.
-
-
-Request to developers
----------------------
-
- When writing device drivers which include a separate assembler file, please
- include it in with the C file, and not the arch/arm/lib directory. This
- allows the driver to be compiled as a loadable module without requiring
- half the code to be compiled into the kernel image.
-
- In general, try to avoid using assembler unless it is really necessary. It
- makes drivers far less easy to port to other hardware.
-
-
-ST506 hard drives
------------------
-
- The ST506 hard drive controllers seem to be working fine (if a little
- slowly). At the moment they will only work off the controllers on an
- A4x0's motherboard, but for it to work off a Podule just requires
- someone with a podule to add the addresses for the IRQ mask and the
- HDC base to the source.
-
- As of 31/3/96 it works with two drives (you should get the ADFS
- *configure hard drive set to 2). I've got an internal 20 MB and a great
- big external 5.25" FH 64 MB drive (who could ever want more :-) ).
-
- I've just got 240 K/s off it (a dd with bs=128k); that's about half of what
- RiscOS gets, but it's a heck of a lot better than the 50 K/s I was getting
- last week :-)
-
- Known bug: Drive data errors can cause a hang; including cases where
- the controller has fixed the error using ECC. (Possibly ONLY
- in that case...hmm).
-
-
-1772 Floppy
------------
- This also seems to work OK, but hasn't been stressed much lately. It
- hasn't got any code for disc change detection in there at the moment which
- could be a bit of a problem! Suggestions on the correct way to do this
- are welcome.
-
-
-Kernel entry (head-armv.S)
---------------------------
- The initial entry into the kernel made via head-armv.S uses architecture
- independent code. The architecture is selected by the value of 'r1' on
- entry, which must be kept unique. You can register a new architecture
- by mailing the following details to rmk@arm.uk.linux.org. Please give
- the mail a subject of 'Register new architecture':
-
- Name: <name of your architecture>
- ARCHDIR: <name of include/asm-arm/arch-* directory>
- Description:
- <description of your architecture>
-
- Please follow this format - it is an automated system. You should
- receive a reply the next day.
----
-Russell King (03/05/1998)
CONFIG_HOST_FOOTBRIDGE
The 21285 Footbridge chip can operate in either `host mode' or
`add-in' mode. Say Y if your 21285 is in host mode, and therefore
- is the configuration master, otherwise say N.
+ is the configuration master, otherwise say N. This must not be
+ set to 'Y' if the card is used in 'add-in' mode.
MFM harddisk support
CONFIG_BLK_DEV_MFM
"What should you avoid when writing PCI drivers"
- by Martin Mares <mj@atrey.karlin.mff.cuni.cz> on 13-Feb-1998
+ by Martin Mares <mj@atrey.karlin.mff.cuni.cz> on 17-Jun-1999
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For class-based search, use pci_find_class(CLASS_ID, dev).
+ You can use the constant PCI_ANY_ID as a wildcard replacement for
+VENDOR_ID or DEVICE_ID. This allows searching for any device from a
+specific vendor, for example.
+
In case you want to do some complex matching, look at pci_devices -- it's
a linked list of pci_dev structures for all PCI devices in the system.
# License. See the file "COPYING" in the main directory of this archive
# for more details.
#
-# Copyright (C) 1995, 1996 by Russell King
+# Copyright (C) 1995-1999 by Russell King
CFLAGS_PROC :=
ASFLAGS_PROC :=
-# All processors get `-mshort-load-bytes' for now, to work around alignment
-# problems. This is more of a hack that just happens to work than a real fix
-# but it will do for now.
+# GCC 2.7 uses different options to later compilers; sort out which we have
+CONFIG_GCC_NEW := $(shell if $(CC) --version 2>&1 | grep '^2\.7' > /dev/null; then echo n; else echo y; fi)
+
+# Hack to get around RiscPC with StrongARM optimistaion
+# problem - force ARM710 optimisation for now.
+ifeq ($(CONFIG_GCC_NEW),y)
+ ifeq ($(CONFIG_ARCH_RPC),y)
+ ifeq ($(CONFIG_CPU_SA110),y)
+ CONFIG_CPU_SA110 := n
+ CONFIG_CPU_ARM7 := y
+ endif
+ endif
+endif
ifeq ($(CONFIG_CPU_26),y)
PROCESSOR = armo
TEXTADDR = 0x02080000
ZTEXTADDR = 0x01800000
ZRELADDR = 0x02080000
- ifeq ($(CONFIG_BINUTILS_NEW),y)
+ ifeq ($(CONFIG_GCC_NEW),y)
CFLAGS_PROC += -mapcs-26 -mshort-load-bytes
ifeq ($(CONFIG_CPU_ARM2),y)
CFLAGS_PROC += -mcpu=arm2
ifeq ($(CONFIG_CPU_32),y)
PROCESSOR = armv
TEXTADDR = 0xC0008000
- ifeq ($(CONFIG_BINUTILS_NEW),y)
+ ifeq ($(CONFIG_GCC_NEW),y)
CFLAGS_PROC += -mapcs-32 -mshort-load-bytes
ifeq ($(CONFIG_CPU_ARM6),y)
CFLAGS_PROC += -mcpu=arm6
# Processor Architecture
# CFLAGS_PROC - processor dependent CFLAGS
-# PROCESSOR - processor type
-# TEXTADDR - Uncompressed kernel link text address
-# ZTEXTADDR - Compressed kernel link text address
-# ZRELADDR - Compressed kernel relocating address (point at which uncompressed kernel is loaded).
+# PROCESSOR - processor type
+# TEXTADDR - Uncompressed kernel link text address
+# ZTEXTADDR - Compressed kernel link text address
+# ZRELADDR - Compressed kernel relocating address
+# (point at which uncompressed kernel is loaded).
#
COMPRESSED_HEAD = head.o
ifeq ($(CONFIG_ARCH_A5K),y)
MACHINE = a5k
ARCHDIR = arc
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
endif
ifeq ($(CONFIG_ARCH_ARC),y)
MACHINE = arc
ARCHDIR = arc
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
endif
ifeq ($(CONFIG_ARCH_RPC),y)
MACHINE = rpc
ARCHDIR = rpc
-COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o
ZTEXTADDR = 0x10008000
ZRELADDR = 0x10008000
endif
ZRELADDR = 0x00008000
endif
-ifeq ($(CONFIG_ARCH_EBSA285),y)
-MACHINE = ebsa285
+ifeq ($(CONFIG_FOOTBRIDGE),y)
+MACHINE = footbridge
ARCHDIR = ebsa285
ZTEXTADDR = 0x00008000
ZRELADDR = 0x00008000
endif
+ifeq ($(CONFIG_ARCH_CO285),y)
+TEXTADDR = 0x60008000
+endif
+
ifeq ($(CONFIG_ARCH_NEXUSPCI),y)
MACHINE = nexuspci
ARCHDIR = nexuspci
COMPRESSED_HEAD = head-nexuspci.o
endif
-ifeq ($(CONFIG_ARCH_VNC),y)
-TEXTADDR = 0xC000C000
-MACHINE = vnc
-ARCHDIR = vnc
-endif
-
-ifeq ($(CONFIG_ARCH_TBOX),y)
-MACHINE = tbox
-ARCHDIR = tbox
-ZTEXTADDR = 0x80008000
-ZRELDIR = 0x80008000
-endif
-
PERL = perl
-ifeq ($(CONFIG_BINUTILS_NEW),y)
-LD = $(CROSS_COMPILE)ld -m elf32arm
-else
-LD = $(CROSS_COMPILE)ld -m elf_arm
-endif
+LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
OBJDUMP = $(CROSS_COMPILE)objdump
CPP = $(CC) -E
ARCHCC := $(word 1,$(CC))
GCCLIB := `$(CC) $(CFLAGS_PROC) --print-libgcc-file-name`
-#GCCARCH := -B/usr/bin/arm-linuxelf-
HOSTCFLAGS := $(CFLAGS:-fomit-frame-pointer=)
ifeq ($(CONFIG_FRAME_POINTER),y)
CFLAGS := $(CFLAGS:-fomit-frame-pointer=)
LINKFLAGS = -T $(TOPDIR)/arch/arm/vmlinux-$(PROCESSOR).lds -e stext -Ttext $(TEXTADDR)
ZLINKFLAGS = -Ttext $(ZTEXTADDR)
-SUBDIRS := $(SUBDIRS:drivers=arch/arm/drivers) arch/arm/lib arch/arm/kernel arch/arm/mm
-HEAD := arch/arm/kernel/head-$(PROCESSOR).o arch/arm/kernel/init_task.o
+# If we're intending to debug the kernel, make sure it has line number
+# information. This gets stripped out when building (z)Image so it doesn't
+# add anything to the footprint of the running kernel.
+ifeq ($(CONFIG_DEBUG_INFO),y)
+CFLAGS += -g
+endif
+
+HEAD := arch/arm/kernel/head-$(PROCESSOR).o \
+ arch/arm/kernel/init_task.o
+SUBDIRS := arch/arm/special $(SUBDIRS) arch/arm/lib arch/arm/kernel \
+ arch/arm/mm arch/arm/nwfpe
CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES)
LIBS := arch/arm/lib/lib.a $(LIBS) $(GCCLIB)
-
-BLOCK_DRIVERS := drivers/block/block.a
-CDROM_DRIVERS := drivers/cdrom/cdrom.a
-ifeq ($(CONFIG_FB),y)
-CHAR_DRIVERS := arch/arm/drivers/char1/char1.a drivers/char/char.a arch/arm/drivers/char1/char1.a
-else
-ifeq ($(CONFIG_VGA_CONSOLE),y)
-CHAR_DRIVERS := arch/arm/drivers/char1/char1.a drivers/char/char.a arch/arm/drivers/char1/char1.a
-else
-CHAR_DRIVERS := arch/arm/drivers/char/char.a
-endif
-endif
-MISC_DRIVERS := drivers/misc/misc.a
-NET_DRIVERS := drivers/net/net.a
-PARIDE_DRIVERS := drivers/block/paride/paride.a
-PCI_DRIVERS := drivers/pci/pci.a
-SCSI_DRIVERS := drivers/scsi/scsi.a
-SOUND_DRIVERS := drivers/sound/sound.a
-VIDEO_DRIVERS := drivers/video/video.a
-PNP_DRIVERS := drivers/pnp/pnp.a
+DRIVERS += arch/arm/special/special.a
ifeq ($(CONFIG_ARCH_ACORN),y)
-BLOCK_DRIVERS += drivers/acorn/block/acorn-block.a
-CHAR_DRIVERS += drivers/acorn/char/acorn-char.a
-NET_DRIVERS += drivers/acorn/net/acorn-net.a drivers/net/net.a
-SCSI_DRIVERS += drivers/acorn/scsi/acorn-scsi.a
+SUBDIRS += drivers/acorn/block drivers/acorn/char drivers/acorn/net \
+ drivers/acorn/scsi
+DRIVERS += drivers/acorn/block/acorn-block.a \
+ drivers/acorn/char/acorn-char.a \
+ drivers/acorn/net/acorn-net.a \
+ drivers/acorn/scsi/acorn-scsi.a
endif
-DRIVERS := $(BLOCK_DRIVERS) $(CHAR_DRIVERS) $(MISC_DRIVERS) $(NET_DRIVERS)
-
-ifeq ($(CONFIG_FB),y)
-DRIVERS := $(DRIVERS) $(VIDEO_DRIVERS)
-else
-ifeq ($(CONFIG_VGA_CONSOLE),y)
-DRIVERS := $(DRIVERS) $(VIDEO_DRIVERS)
-endif
-endif
-ifeq ($(CONFIG_SCSI),y)
-DRIVERS := $(DRIVERS) $(SCSI_DRIVERS)
-endif
-ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),)
-DRIVERS := $(DRIVERS) $(CDROM_DRIVERS)
-endif
-ifdef CONFIG_PCI
-DRIVERS := $(DRIVERS) $(PCI_DRIVERS)
-endif
-ifeq ($(CONFIG_SOUND),y)
-DRIVERS := $(DRIVERS) $(SOUND_DRIVERS)
-endif
-ifeq ($(CONFIG_PARIDE),y)
-DRIVERS := $(DRIVERS) $(PARIDE_DRIVERS)
-endif
-ifdef CONFIG_PNP
-DRIVERS := $(DRIVERS) $(PNP_DRIVERS)
+ifeq ($(CONFIG_NWFPE),y)
+DRIVERS += arch/arm/nwfpe/math-emu.a
endif
+MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
+
symlinks::
$(RM) include/asm-arm/arch include/asm-arm/proc
(cd include/asm-arm; ln -sf arch-$(ARCHDIR) arch; ln -sf proc-$(PROCESSOR) proc)
-# Once we've finished integrating the sources, the @$(MAKE) will disappear
-archmrproper:
- rm -f include/asm-arm/arch include/asm-arm/proc
- @$(MAKE) -C arch/$(ARCH)/drivers mrproper
-
arch/arm/kernel: dummy
$(MAKE) linuxsubdirs SUBDIRS=arch/arm/kernel
arch/arm/lib: dummy
$(MAKE) linuxsubdirs SUBDIRS=arch/arm/lib
-MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
-
-zImage: vmlinux
- @$(MAKEBOOT) zImage
+zImage zinstall Image install: vmlinux
+ @$(MAKEBOOT) $@
-zinstall: vmlinux
- @$(MAKEBOOT) zinstall
+# Once we've finished integrating the sources, the @$(MAKE) will disappear
+archmrproper:
+ rm -f include/asm-arm/arch include/asm-arm/proc
+ @$(MAKE) -C arch/$(ARCH)/special mrproper
-Image: vmlinux
- @$(MAKEBOOT) Image
+archclean:
+ @$(MAKEBOOT) clean
+ $(RM) arch/arm/lib/constants.h
-install: vmlinux
- @$(MAKEBOOT) install
+archdep:
+ @$(MAKEBOOT) dep
# My testing targets (that short circuit a few dependencies)
zImg:; @$(MAKEBOOT) zImage
i:; @$(MAKEBOOT) install
zi:; @$(MAKEBOOT) zinstall
-archclean:
- @$(MAKEBOOT) clean
- $(RM) arch/arm/lib/constants.h
+a5k_config:
+ $(RM) arch/arm/defconfig
+ cp arch/arm/def-configs/a5k arch/arm/defconfig
+
+ebsa110_config:
+ $(RM) arch/arm/defconfig
+ cp arch/arm/def-configs/ebsa110 arch/arm/defconfig
+
+footbridge_config:
+ $(RM) arch/arm/defconfig
+ cp arch/arm/def-configs/footbridge arch/arm/defconfig
+
+rpc_config:
+ $(RM) arch/arm/defconfig
+ cp arch/arm/def-configs/rpc arch/arm/defconfig
-archdep:
- @$(MAKEBOOT) dep
-sed -e /^MACHINE..*=/s,= .*,= rpc,;/^PROCESSOR..*=/s,= .*,= armv, linux/arch/arm/Makefile.normal
OBJS =$(HEAD) misc.o $(COMPRESSED_EXTRA)
CFLAGS =-O2 -DSTDC_HEADERS $(CFLAGS_PROC)
ARFLAGS =rc
+FONTC =$(TOPDIR)/drivers/video/font_acorn_8x8.c
+
+ifeq ($(CONFIG_ARCH_ACORN),y)
+OBJS += ll_char_wr.o font.o
+endif
all: vmlinux
-vmlinux: piggy.o $(OBJS)
+vmlinux: $(OBJS) piggy.o
$(LD) $(ZLINKFLAGS) -o vmlinux $(OBJS) piggy.o
$(HEAD): $(HEAD:.o=.S)
$(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-arm -T $$tmppiggy.lnk; \
rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk;
+font.o: $(FONTC)
+ $(CC) -Dstatic= -c -o $@ $(FONTC)
+
clean:; rm -f vmlinux core
--- /dev/null
+/*
+ * linux/arch/arm/lib/ll_char_wr.S
+ *
+ * Copyright (C) 1995, 1996 Russell King.
+ *
+ * Speedups & 1bpp code (C) 1996 Philip Blundell & Russell King.
+ *
+ * 10-04-96 RMK Various cleanups & reduced register usage.
+ * 08-04-98 RMK Shifts re-ordered
+ */
+
+@ Regs: [] = corruptible
+@ {} = used
+@ () = do not use
+#define __ASSEMBLY__
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+ .text
+
+#define BOLD 0x01
+#define ITALIC 0x02
+#define UNDERLINE 0x04
+#define FLASH 0x08
+#define INVERSE 0x10
+
+LC0: .word SYMBOL_NAME(bytes_per_char_h)
+ .word SYMBOL_NAME(video_size_row)
+ .word SYMBOL_NAME(acorndata_8x8)
+ .word SYMBOL_NAME(con_charconvtable)
+
+ENTRY(ll_write_char)
+ stmfd sp!, {r4 - r7, lr}
+@
+@ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc)
+@
+ eor ip, r1, #UNDERLINE << 9
+/*
+ * calculate colours
+ */
+ tst r1, #INVERSE << 9
+ moveq r2, r1, lsr #16
+ moveq r3, r1, lsr #24
+ movne r2, r1, lsr #24
+ movne r3, r1, lsr #16
+ and r3, r3, #255
+ and r2, r2, #255
+/*
+ * calculate offset into character table
+ */
+ mov r1, r1, lsl #23
+ mov r1, r1, lsr #20
+/*
+ * calculate offset required for each row [maybe I should make this an argument to this fn.
+ * Have to see what the register usage is like in the calling routines.
+ */
+ adr r4, LC0
+ ldmia r4, {r4, r5, r6, lr}
+ ldr r4, [r4]
+ ldr r5, [r5]
+/*
+ * Go to resolution-dependent routine...
+ */
+ cmp r4, #4
+ blt Lrow1bpp
+ eor r2, r3, r2 @ Create eor mask to change colour from bg
+ orr r3, r3, r3, lsl #8 @ to fg.
+ orr r3, r3, r3, lsl #16
+ add r0, r0, r5, lsl #3 @ Move to bottom of character
+ add r1, r1, #7
+ ldrb r7, [r6, r1]
+ tst ip, #UNDERLINE << 9
+ eoreq r7, r7, #255
+ teq r4, #8
+ beq Lrow8bpplp
+@
+@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
+@
+ orr r3, r3, r3, lsl #4
+Lrow4bpplp: ldr r7, [lr, r7, lsl #2]
+ mul r7, r2, r7
+ tst r1, #7 @ avoid using r7 directly after
+ eor ip, r3, r7
+ str ip, [r0, -r5]!
+ LOADREGS(eqfd, sp!, {r4 - r7, pc})
+ sub r1, r1, #1
+ ldrb r7, [r6, r1]
+ ldr r7, [lr, r7, lsl #2]
+ mul r7, r2, r7
+ tst r1, #7 @ avoid using r7 directly after
+ eor ip, r3, r7
+ str ip, [r0, -r5]!
+ subne r1, r1, #1
+ ldrneb r7, [r6, r1]
+ bne Lrow4bpplp
+ LOADREGS(fd, sp!, {r4 - r7, pc})
+
+@
+@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
+@
+Lrow8bpplp: mov ip, r7, lsr #4
+ ldr ip, [lr, ip, lsl #2]
+ mul r4, r2, ip
+ and ip, r7, #15 @ avoid r4
+ ldr ip, [lr, ip, lsl #2] @ avoid r4
+ mul ip, r2, ip @ avoid r4
+ eor r4, r3, r4 @ avoid ip
+ tst r1, #7 @ avoid ip
+ sub r0, r0, r5 @ avoid ip
+ eor ip, r3, ip
+ stmia r0, {r4, ip}
+ LOADREGS(eqfd, sp!, {r4 - r7, pc})
+ sub r1, r1, #1
+ ldrb r7, [r6, r1]
+ mov ip, r7, lsr #4
+ ldr ip, [lr, ip, lsl #2]
+ mul r4, r2, ip
+ and ip, r7, #15 @ avoid r4
+ ldr ip, [lr, ip, lsl #2] @ avoid r4
+ mul ip, r2, ip @ avoid r4
+ eor r4, r3, r4 @ avoid ip
+ tst r1, #7 @ avoid ip
+ sub r0, r0, r5 @ avoid ip
+ eor ip, r3, ip
+ stmia r0, {r4, ip}
+ subne r1, r1, #1
+ ldrneb r7, [r6, r1]
+ bne Lrow8bpplp
+ LOADREGS(fd, sp!, {r4 - r7, pc})
+
+@
+@ Smashable regs: {r0 - r3}, [r4], {r5, r6}, [r7], (r8 - fp), [ip], (sp), [lr], (pc)
+@
+Lrow1bpp: add r6, r6, r1
+ ldmia r6, {r4, r7}
+ tst ip, #INVERSE << 9
+ mvnne r4, r4
+ mvnne r7, r7
+ strb r4, [r0], r5
+ mov r4, r4, lsr #8
+ strb r4, [r0], r5
+ mov r4, r4, lsr #8
+ strb r4, [r0], r5
+ mov r4, r4, lsr #8
+ strb r4, [r0], r5
+ strb r7, [r0], r5
+ mov r7, r7, lsr #8
+ strb r7, [r0], r5
+ mov r7, r7, lsr #8
+ strb r7, [r0], r5
+ mov r7, r7, lsr #8
+ tst ip, #UNDERLINE << 9
+ mvneq r7, r7
+ strb r7, [r0], r5
+ LOADREGS(fd, sp!, {r4 - r7, pc})
+
+ .bss
+ENTRY(con_charconvtable)
+ .space 1024
A5000 CONFIG_ARCH_A5K \
RiscPC CONFIG_ARCH_RPC \
EBSA-110 CONFIG_ARCH_EBSA110 \
- EBSA-285 CONFIG_ARCH_EBSA285 \
- NexusPCI CONFIG_ARCH_NEXUSPCI \
- Corel-VNC CONFIG_ARCH_VNC \
- Tbox CONFIG_ARCH_TBOX" RiscPC
+ FootBridge-based CONFIG_FOOTBRIDGE" RiscPC
+
+if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
+ bool 'FootBridge in HOST mode' CONFIG_HOST_FOOTBRIDGE
+ if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
+ define_bool CONFIG_ADDIN_FOOTBRIDGE n
+ else
+ define_bool CONFIG_ADDIN_FOOTBRIDGE y
+ fi
+fi
+
+if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
+ bool ' Include support for Intel EBSA285' CONFIG_ARCH_EBSA285
+ bool ' Include support for Chalice CATS boards' CONFIG_CATS
+ bool ' Include support for Corel NetWinder' CONFIG_ARCH_NETWINDER
+fi
-if [ "$CONFIG_ARCH_EBSA285" = "y" ]; then
- bool ' Include support for CATS boards' CONFIG_CATS
+if [ "$CONFIG_ADDIN_FOOTBRIDGE" = "y" ]; then
+ # If we get any other footbridge-based plug-in boards, then
+ # add your architecture options here
+ define_bool CONFIG_ARCH_CO285 y
fi
# Select various configuration options depending on the machine type
# Easy check for Acorn-style architectures
-
if [ "$CONFIG_ARCH_ARC" = "y" -o \
"$CONFIG_ARCH_A5K" = "y" -o \
"$CONFIG_ARCH_RPC" = "y" ]; then
define_bool CONFIG_ARCH_ACORN n
fi
-if [ "$CONFIG_ARCH_TBOX" = "y" ]; then
- define_bool CONFIG_BUS_I2C y
-fi
+#if [ "$CONFIG_ARCH_TBOX" = "y" ]; then
+# define_bool CONFIG_BUS_I2C y
+#fi
# These machines always have PCI
-
if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o \
- "$CONFIG_ARCH_VNC" = "y" ]; then
+ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
define_bool CONFIG_PCI y
fi
-if [ "$CONFIG_ARCH_EBSA285" = "y" ]; then
- bool "PCI support" CONFIG_PCI
-fi
# These machines have ISA-DMA
if [ "$CONFIG_CATS" = "y" -o \
- "$CONFIG_ARCH_VNC" = "y" ]; then
+ "$CONFIG_ARCH_NETWINDER" = "y" ]; then
define_bool CONFIG_ISA_DMA y
else
define_bool CONFIG_ISA_DMA n
# Figure out whether this system uses 26-bit or 32-bit CPUs. Nobody has
# ever built a machine that can take both, and now that ARM3 is obsolete
# nobody is likely to either.
-
if [ "$CONFIG_ARCH_ARC" = "y" -o \
"$CONFIG_ARCH_A5K" = "y" ]; then
define_bool CONFIG_CPU_32 n
# Now allow the user to choose a more precise CPU. This is only used to set
# the flags we pass to GCC, not in any code.
-
choice 'Optimise for CPU' \
"ARM2 CONFIG_CPU_ARM2 \
ARM3 CONFIG_CPU_ARM3 \
SA110 CONFIG_CPU_SA110" ARM6
if [ "$CONFIG_CPU_26" = "y" ]; then
-
# For 26-bit CPUs, the page size changes with the amount of physical RAM!
# The default is 4MB but if the user has less they have to own up to it here.
-
choice 'Physical memory size' \
"4MB+ CONFIG_PAGESIZE_32 \
- 2MB CONFIG_PAGESIZE_16 \
- 1MB/512K CONFIG_PAGESIZE_8" 4MB+
+ 2MB CONFIG_PAGESIZE_16" 4MB+
fi
endmenu
mainmenu_option next_comment
comment 'Code maturity level options'
bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
-bool 'Use new compilation options (for GCC 2.8)' CONFIG_BINUTILS_NEW
-bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER
+if [ "$CONFIG_CPU_32" = "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then
+ bool 'Enable kernel-mode alignment trap handler (EXPERIMENTAL)' CONFIG_ALIGNMENT_TRAP
+fi
+bool 'Split text into discardable sections' CONFIG_TEXT_SECTIONS
endmenu
mainmenu_option next_comment
bool 'System V IPC' CONFIG_SYSVIPC
bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
bool 'Sysctl support' CONFIG_SYSCTL
+tristate 'Math emulation' CONFIG_NWFPE
tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
+if [ "$CONFIG_CPU_32" = "y" ]; then
+ tristate 'RISC OS personality' CONFIG_ARTHUR
+fi
tristate 'Parallel port support' CONFIG_PARPORT
if [ "$CONFIG_PARPORT" != "n" ]; then
- dep_tristate ' Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT
+ if [ "$CONFIG_ARCH_ARC" = "y" ]; then
+ dep_tristate ' Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT
+ fi
dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
# If exactly one hardware type is selected then parport will optimise away
# support for loading any others. Defeat this if the user is keen.
fi
fi
fi
-if [ "$CONFIG_ARCH_EBSA285" = "y" -o \
- "$CONFIG_ARCH_EBSA110" = "y" -o \
- "$CONFIG_ARCH_VNC" = "y" ]; then
+if [ "$CONFIG_ARCH_EBSA110" = "y" -o \
+ "$CONFIG_ARCH_NETWINDER" = "y" -o \
+ "$CONFIG_CATS" = "y" ]; then
string 'Initial kernel command string' CONFIG_CMDLINE
fi
+if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \
+ "$CONFIG_ARCH_EBSA110" = "y" -o \
+ "$CONFIG_ARCH_EBSA285" = "y" -o \
+ "$CONFIG_ARCH_CO285" = "y" ]; then
+ bool 'Timer and CPU usage LEDs' CONFIG_LEDS
+ if [ "$CONFIG_LEDS" = "y" ]; then
+ if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \
+ "$CONFIG_ARCH_EBSA285" = "y" -o \
+ "$CONFIG_ARCH_CO285" = "y" ]; then
+ bool ' Timer LED' CONFIG_LEDS_TIMER
+ bool ' CPU usage LED' CONFIG_LEDS_CPU
+ fi
+ fi
+fi
endmenu
+source drivers/i2o/Config.in
+
source drivers/pnp/Config.in
source drivers/block/Config.in
source drivers/acorn/block/Config.in
fi
-if [ "$CONFIG_VGA_CONSOLE" = "n" -a "$CONFIG_FB" = "n" ]; then
- source arch/arm/drivers/char/Config.in
-else
- source drivers/char/Config.in
-fi
+source drivers/char/Config.in
if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
- source drivers/acorn/char/Config.in
+ if [ "$CONFIG_MOUSE" = "y" ]; then
+ if [ "$CONFIG_ARCH_RPC" != "y" ]; then
+ define_bool CONFIG_KBDMOUSE y
+ else
+ define_bool CONFIG_RPCMOUSE y
+ fi
+ fi
fi
+source drivers/usb/Config.in
+
if [ "$CONFIG_VT" = "y" ]; then
mainmenu_option next_comment
comment 'Console drivers'
if [ "$CONFIG_NET" = "y" ]; then
source net/Config.in
-fi
-if [ "$CONFIG_NET" = "y" ]; then
+ source net/ax25/Config.in
+
+ source net/irda/Config.in
+
mainmenu_option next_comment
comment 'Network device support'
endmenu
fi
+# mainmenu_option next_comment
+# comment 'ISDN subsystem'
+#
+# tristate 'ISDN support' CONFIG_ISDN
+# if [ "$CONFIG_ISDN" != "n" ]; then
+# source drivers/isdn/Config.in
+# fi
+# endmenu
+
mainmenu_option next_comment
comment 'SCSI support'
endmenu
fi
-# mainmenu_option next_comment
-# comment 'ISDN subsystem'
-#
-# tristate 'ISDN support' CONFIG_ISDN
-# if [ "$CONFIG_ISDN" != "n" ]; then
-# source drivers/isdn/Config.in
-# fi
-# endmenu
-
source fs/Config.in
mainmenu_option next_comment
comment 'Kernel hacking'
-bool 'Debug kernel errors' CONFIG_DEBUG_ERRORS
+bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER
+bool 'Verbose kernel error messages' CONFIG_DEBUG_ERRORS
+bool 'Verbose user fault messages' CONFIG_DEBUG_USER
+bool 'Include debugging information in kernel binary' CONFIG_DEBUG_INFO
#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_CPU_26" = "y" ]; then
+ bool 'Disable pgtable cache (EXPERIMENTAL)' CONFIG_NO_PGT_CACHE
+ fi
+
+ # These options are only for real kernel hackers
+ # who want to get their hands dirty.
+ bool 'Kernel low-level debugging functions' CONFIG_DEBUG_LL
+ if [ "$CONFIG_DEBUG_LL" = "y" ]; then
+ if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
+ bool 'Kernel low-level debugging messages via DC21285 port' CONFIG_DEBUG_DC21285_PORT
+ fi
+ fi
+fi
endmenu
#
CONFIG_ARM=y
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_EBSA110 is not set
+CONFIG_FOOTBRIDGE=y
+CONFIG_HOST_FOOTBRIDGE=y
+# CONFIG_ADDIN_FOOTBRIDGE is not set
+CONFIG_ARCH_EBSA285=y
+# CONFIG_CATS is not set
+CONFIG_ARCH_NETWINDER=y
+# CONFIG_ARCH_ACORN is not set
+CONFIG_PCI=y
+CONFIG_ISA_DMA=y
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+# CONFIG_CPU_ARM2 is not set
+# CONFIG_CPU_ARM3 is not set
+# CONFIG_CPU_ARM6 is not set
+# CONFIG_CPU_ARM7 is not set
+CONFIG_CPU_SA110=y
+
#
# Code maturity level options
#
CONFIG_EXPERIMENTAL=y
+# CONFIG_ALIGNMENT_TRAP is not set
+# CONFIG_TEXT_SECTIONS is not set
#
# Loadable module support
#
CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
+# CONFIG_MODVERSIONS is not set
CONFIG_KMOD=y
#
# General setup
#
-# CONFIG_ARCH_ARC is not set
-# CONFIG_ARCH_A5K is not set
-CONFIG_ARCH_RPC=y
-# CONFIG_ARCH_EBSA110 is not set
-# CONFIG_ARCH_NEXUSPCI is not set
-CONFIG_ARCH_ACORN=y
-# CONFIG_PCI is not set
-# CONFIG_CPU_ARM2 is not set
-# CONFIG_CPU_ARM3 is not set
-# CONFIG_CPU_ARM6 is not set
-CONFIG_CPU_SA110=y
-CONFIG_FRAME_POINTER=y
-# CONFIG_BINUTILS_NEW is not set
-CONFIG_DEBUG_ERRORS=y
CONFIG_NET=y
CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_SYSCTL=y
+CONFIG_NWFPE=y
CONFIG_BINFMT_AOUT=y
-CONFIG_BINFMT_ELF=m
-# CONFIG_BINFMT_JAVA is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
CONFIG_PARPORT=y
CONFIG_PARPORT_PC=y
+CONFIG_CMDLINE="root=/dev/hda2 ro mem=32M parport=0x378,7 ide0=autotune"
+CONFIG_LEDS=y
+CONFIG_LEDS_TIMER=y
+# CONFIG_LEDS_CPU is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
#
-# Floppy, IDE, and other block devices
+# Block devices
#
-CONFIG_BLK_DEV_FD=y
+# CONFIG_BLK_DEV_FD is not set
CONFIG_BLK_DEV_IDE=y
#
#
# CONFIG_BLK_DEV_HD_IDE is not set
CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDECD is not set
# CONFIG_BLK_DEV_IDETAPE is not set
# CONFIG_BLK_DEV_IDEFLOPPY is not set
# CONFIG_BLK_DEV_IDESCSI is not set
-# CONFIG_BLK_DEV_IDE_PCMCIA is not set
-CONFIG_BLK_DEV_IDE_CARDS=y
-CONFIG_BLK_DEV_IDE_ICSIDE=y
-# CONFIG_BLK_DEV_IDE_RAPIDE is not set
-# CONFIG_BLK_DEV_XD is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_BLK_DEV_OFFBOARD=y
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_VIA82C586 is not set
+# CONFIG_BLK_DEV_CMD646 is not set
+CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_IDE_CHIPSETS is not set
#
# Additional Block Devices
#
CONFIG_BLK_DEV_LOOP=m
-# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_STRIPED=m
+CONFIG_MD_MIRRORING=m
+CONFIG_MD_RAID5=m
CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BLK_DEV_XD is not set
CONFIG_PARIDE_PARPORT=y
-# CONFIG_PARIDE is not set
-CONFIG_BLK_DEV_PART=y
+CONFIG_PARIDE=m
+
+#
+# Parallel IDE high-level drivers
+#
+CONFIG_PARIDE_PD=m
+CONFIG_PARIDE_PCD=m
+CONFIG_PARIDE_PF=m
+CONFIG_PARIDE_PT=m
+CONFIG_PARIDE_PG=m
+
+#
+# Parallel IDE protocol modules
+#
+CONFIG_PARIDE_ATEN=m
+CONFIG_PARIDE_BPCK=m
+CONFIG_PARIDE_COMM=m
+CONFIG_PARIDE_DSTR=m
+CONFIG_PARIDE_FIT2=m
+CONFIG_PARIDE_FIT3=m
+CONFIG_PARIDE_EPAT=m
+CONFIG_PARIDE_EPIA=m
+CONFIG_PARIDE_FRIQ=m
+CONFIG_PARIDE_FRPW=m
+CONFIG_PARIDE_KBIC=m
+CONFIG_PARIDE_KTTI=m
+CONFIG_PARIDE_ON20=m
+CONFIG_PARIDE_ON26=m
# CONFIG_BLK_DEV_HD is not set
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_UNIX98_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_PRINTER_READBACK=y
+CONFIG_MOUSE=y
+
+#
+# Mice
+#
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+# CONFIG_QIC02_TAPE is not set
+CONFIG_WATCHDOG=y
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+# CONFIG_WDT is not set
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+CONFIG_DS1620=y
+CONFIG_NWBUTTON=y
+CONFIG_NWBUTTON_REBOOT=y
+CONFIG_NWFLASH=m
+# CONFIG_NVRAM is not set
+CONFIG_RTC=y
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Joystick support
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_DTLK is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
+#
+# Console drivers
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_PM2 is not set
+CONFIG_FB_CYBER2000=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+# CONFIG_FBCON_MFB is not set
+# CONFIG_FBCON_CFB2 is not set
+# CONFIG_FBCON_CFB4 is not set
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+# CONFIG_FBCON_CFB32 is not set
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+CONFIG_FBCON_VGA=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+CONFIG_FONT_ACORN_8x8=y
+
#
# Networking options
#
-# CONFIG_PACKET is not set
+CONFIG_PACKET=y
# CONFIG_NETLINK is not set
# CONFIG_FIREWALL is not set
# CONFIG_FILTER is not set
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
-# CONFIG_IP_PNP is not set
-# CONFIG_IP_ACCT is not set
-# CONFIG_IP_MASQUERADE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
# CONFIG_IP_ROUTER is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
-# CONFIG_IP_ALIAS is not set
+CONFIG_IP_ALIAS=y
# CONFIG_SYN_COOKIES is not set
#
# (it is safe to leave these untouched)
#
# CONFIG_INET_RARP is not set
-CONFIG_IP_NOSR=y
-# CONFIG_SKB_LARGE is not set
+CONFIG_SKB_LARGE=y
# CONFIG_IPV6 is not set
#
# CONFIG_LAPB is not set
# CONFIG_BRIDGE is not set
# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_NET_FASTROUTE is not set
# CONFIG_NET_HW_FLOWCONTROL is not set
# CONFIG_CPU_IS_SLOW is not set
+
+#
+# QoS and/or fair queueing
+#
# CONFIG_NET_SCHED is not set
-# CONFIG_NET_PROFILE is not set
#
-# SCSI support
+# Amateur Radio support
#
-CONFIG_SCSI=y
+# CONFIG_HAMRADIO is not set
#
-# SCSI support type (disk, tape, CD-ROM)
+# IrDA subsystem support
#
-CONFIG_BLK_DEV_SD=y
-# CONFIG_CHR_DEV_ST is not set
-CONFIG_BLK_DEV_SR=y
-# CONFIG_BLK_DEV_SR_VENDOR is not set
-# CONFIG_CHR_DEV_SG is not set
+# CONFIG_IRDA is not set
#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+# Network device support
#
-# CONFIG_SCSI_MULTI_LUN is not set
-CONFIG_SCSI_CONSTANTS=y
-CONFIG_SCSI_LOGGING=y
+CONFIG_NETDEVICES=y
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_ARM_AM79C961A is not set
+CONFIG_NET_VENDOR_3COM=y
+# CONFIG_EL1 is not set
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_EL16 is not set
+# CONFIG_EL3 is not set
+# CONFIG_3C515 is not set
+CONFIG_VORTEX=y
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_RTL8139 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_EISA=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_DE4X5 is not set
+CONFIG_DEC_ELCP=m
+# CONFIG_DGRS is not set
+# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_NE3210 is not set
+CONFIG_NE2K_PCI=y
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_ES3210 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_ZNET is not set
+# CONFIG_NET_POCKET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_DLCI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
#
-# SCSI low-level drivers
+# CCP compressors for PPP are only built as modules.
#
-CONFIG_SCSI_ACORNSCSI_3=m
-CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE=y
-CONFIG_SCSI_ACORNSCSI_SYNC=y
-CONFIG_SCSI_CUMANA_2=m
-CONFIG_SCSI_POWERTECSCSI=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+# CONFIG_NET_RADIO is not set
+# CONFIG_TR is not set
+# CONFIG_SHAPER is not set
+# CONFIG_HOSTESS_SV11 is not set
+# CONFIG_COSA is not set
+# CONFIG_RCPCI is not set
#
-# The following drives are not fully supported
+# SCSI support
#
-CONFIG_SCSI_CUMANA_1=m
-CONFIG_SCSI_OAK1=m
-CONFIG_SCSI_PPA=m
-CONFIG_SCSI_PPA_HAVE_PEDANTIC=2
+# CONFIG_SCSI is not set
#
-# Network device support
+# Sound
#
-CONFIG_NETDEVICES=y
-# CONFIG_DUMMY is not set
-# CONFIG_EQUALIZER is not set
-CONFIG_PPP=m
+CONFIG_SOUND=m
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_PAS is not set
+CONFIG_SOUND_SB=m
+CONFIG_SOUND_ADLIB=m
+# CONFIG_SOUND_GUS is not set
+# CONFIG_SOUND_MPU401 is not set
+# CONFIG_SOUND_PSS is not set
+# CONFIG_SOUND_MSS is not set
+# CONFIG_SOUND_SSCAPE is not set
+# CONFIG_SOUND_TRIX is not set
+# CONFIG_SOUND_MAD16 is not set
+# CONFIG_SOUND_WAVEFRONT is not set
+# CONFIG_SOUND_CS4232 is not set
+# CONFIG_SOUND_OPL3SA2 is not set
+# CONFIG_SOUND_MAUI is not set
+# CONFIG_SOUND_SGALAXY is not set
+# CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_OPL3SA1 is not set
+# CONFIG_SOUND_SOFTOSS is not set
+# CONFIG_SOUND_YM3812 is not set
+# CONFIG_SOUND_VMIDI is not set
+# CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_VIDC is not set
+CONFIG_SOUND_WAVEARTIST=m
+CONFIG_WAVEARTIST_BASE=250
+CONFIG_WAVEARTIST_IRQ=12
+CONFIG_WAVEARTIST_DMA=3
+CONFIG_WAVEARTIST_DMA2=7
#
-# CCP compressors for PPP are only built as modules.
+# Additional low level sound drivers
#
-# CONFIG_SLIP is not set
-CONFIG_ETHER1=m
-CONFIG_ETHER3=m
-CONFIG_ETHERH=m
+# CONFIG_LOWLEVEL_SOUND is not set
#
# Filesystems
#
# CONFIG_QUOTA is not set
-# CONFIG_MINIX_FS is not set
-CONFIG_EXT2_FS=y
-CONFIG_ISO9660_FS=y
-CONFIG_JOLIET=y
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
+# CONFIG_AUTOFS_FS is not set
+CONFIG_ADFS_FS=y
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
# CONFIG_UMSDOS_FS is not set
-CONFIG_VFAT_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
CONFIG_NFS_FS=y
-CONFIG_NFSD=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=m
+# CONFIG_NFSD_SUN is not set
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
-# CONFIG_CODA_FS is not set
# CONFIG_SMB_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_NTFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_HFS_FS is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_AUTOFS_FS is not set
-# CONFIG_UFS_FS is not set
-CONFIG_ADFS_FS=y
-CONFIG_ADFS_FS=y
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_OSF_PARTITION is not set
# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+CONFIG_ACORN_PARTITION=y
+CONFIG_ACORN_PARTITION_ADFS=y
+# CONFIG_ACORN_PARTITION_ICS is not set
+# CONFIG_ACORN_PARTITION_POWERTEC is not set
+# CONFIG_ACORN_PARTITION_RISCIX is not set
CONFIG_NLS=y
#
# Native Language Support
#
-# CONFIG_NLS_CODEPAGE_437 is not set
+CONFIG_NLS_CODEPAGE_437=m
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
-# CONFIG_NLS_CODEPAGE_850 is not set
-# CONFIG_NLS_CODEPAGE_852 is not set
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
# CONFIG_NLS_CODEPAGE_855 is not set
# CONFIG_NLS_CODEPAGE_857 is not set
# CONFIG_NLS_CODEPAGE_860 is not set
# CONFIG_NLS_CODEPAGE_866 is not set
# CONFIG_NLS_CODEPAGE_869 is not set
# CONFIG_NLS_CODEPAGE_874 is not set
-# CONFIG_NLS_ISO8859_1 is not set
-# CONFIG_NLS_ISO8859_2 is not set
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_ISO8859_4 is not set
# CONFIG_NLS_ISO8859_5 is not set
# CONFIG_NLS_ISO8859_7 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_ISO8859_9 is not set
+CONFIG_NLS_ISO8859_15=m
# CONFIG_NLS_KOI8_R is not set
-#
-# Character devices
-#
-CONFIG_VT=y
-CONFIG_VT_CONSOLE=y
-CONFIG_SERIAL=y
-# CONFIG_SERIAL_CONSOLE is not set
-# CONFIG_SERIAL_EXTENDED is not set
-CONFIG_ATOMWIDE_SERIAL=y
-CONFIG_DUALSP_SERIAL=y
-CONFIG_MOUSE=y
-CONFIG_PRINTER=m
-CONFIG_PRINTER_READBACK=y
-# CONFIG_UMISC is not set
-# CONFIG_WATCHDOG is not set
-CONFIG_RPCMOUSE=y
-
-#
-# Sound
-#
-CONFIG_SOUND=m
-CONFIG_VIDC=y
-CONFIG_AUDIO=y
-DSP_BUFFSIZE=65536
-
#
# Kernel hacking
#
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_INFO is not set
CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_LL is not set
ENTRY_OBJ = entry-$(PROCESSOR).o
O_TARGET := kernel.o
-O_OBJS := $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o setup.o \
+O_OBJS := $(ENTRY_OBJ) irq.o process.o ptrace.o setup.o \
signal.o sys_arm.o time.o traps.o
-DMA_OBJS_arc = dma-arc.o
-DMA_OBJS_a5k = dma-a5k.o
-DMA_OBJS_rpc = dma-rpc.o
-DMA_OBJS_ebsa110 = dma-dummy.o
-DMA_OBJS_ebsa285 = dma-ebsa285.o
-DMA_OBJS_nexuspci =
-DMA_OBJS_vnc = dma-vnc.o
-
-O_OBJS_arc = ecard.o iic.o fiq.o oldlatches.o
-O_OBJS_a5k = ecard.o iic.o fiq.o
-O_OBJS_rpc = ecard.o iic.o fiq.o
-O_OBJS_ebsa110 = leds-ebsa110.o
-O_OBJS_ebsa285 = leds-ebsa285.o hw-ebsa285.o
-O_OBJS_nexuspci =
-O_OBJS_vnc = leds-ebsa285.o hw-vnc.o
+ifeq ($(CONFIG_ISA_DMA),y)
+ ISA_DMA_OBJS += dma-isa.o
+endif
+
+O_OBJS_arc = dma-arc.o iic.o fiq.o oldlatches.o
+O_OBJS_a5k = dma-a5k.o iic.o fiq.o
+O_OBJS_rpc = dma-rpc.o iic.o fiq.o
+O_OBJS_ebsa110 = dma-dummy.o
+O_OBJS_footbridge = dma-footbridge.o $(ISA_DMA_OBJS)
+O_OBJS_nexuspci = dma-dummy.o
+
+OX_OBJS_arc = dma.o
+OX_OBJS_a5k = dma.o
+OX_OBJS_rpc = dma.o
+OX_OBJS_ebsa110 =
+OX_OBJS_footbridge= dma.o hw-footbridge.o
+OX_OBJS_nexuspci =
all: lib kernel.o $(HEAD_OBJ) init_task.o
+O_OBJS += $(O_OBJS_$(MACHINE))
+
ifeq ($(CONFIG_MODULES),y)
OX_OBJS = armksyms.o
-else
- O_OBJS += armksyms.o
+endif
+
+ifeq ($(CONFIG_ARCH_ACORN),y)
+ OX_OBJS += ecard.o
endif
ifeq ($(MACHINE),nexuspci)
endif
endif
-ifneq ($(DMA_OBJS_$(MACHINE)),)
- OX_OBJS += dma.o
- O_OBJS += $(DMA_OBJS_$(MACHINE))
- ifeq ($(CONFIG_ISA_DMA),y)
- O_OBJS += dma-isa.o
- endif
+ifdef CONFIG_LEDS
+ OX_OBJS += leds-$(MACHINE).o
+endif
+
+ifeq ($(CONFIG_MODULES),y)
+ OX_OBJS += $(OX_OBJS_$(MACHINE))
else
- O_OBJS += dma-dummy.o
+ O_OBJS += $(OX_OBJS_$(MACHINE))
endif
-O_OBJS += $(O_OBJS_$(MACHINE))
+ifeq ($(CONFIG_ARTHUR),y)
+ O_OBJS += arthur.o
+else
+ ifeq ($(CONFIG_ARTHUR),m)
+ M_OBJS += arthur.o
+ endif
+endif
$(HEAD_OBJ): $(HEAD_OBJ:.o=.S)
$(CC) -D__ASSEMBLY__ -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@
lib:
$(MAKE) -C ../lib constants.h
+
+# Spell out some dependencies that `make dep' doesn't spot
+entry-armv.o: calls.S
+entry-armo.o: calls.S
#include <linux/mman.h>
#include <linux/pci.h>
#include <linux/delay.h>
+#include <linux/in6.h>
-#include <asm/ecard.h>
#include <asm/elf.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/pgtable.h>
+#include <asm/semaphore.h>
#include <asm/system.h>
#include <asm/uaccess.h>
+#include <asm/checksum.h>
extern void dump_thread(struct pt_regs *, struct user *);
extern int dump_fpu(struct pt_regs *, struct user_fp_struct *);
extern void inswb(unsigned int port, void *to, int len);
extern void outswb(unsigned int port, const void *to, int len);
+extern unsigned int local_bh_count[NR_CPUS];
+extern unsigned int local_irq_count[NR_CPUS];
+
+extern void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);
+extern void iounmap(void *addr);
+
+extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
+
+/*
+ * syscalls
+ */
+extern int sys_write(int, const char *, int);
+extern int sys_read(int, char *, int);
+extern int sys_lseek(int, off_t, int);
+extern int sys_open(const char *, int, int);
+extern int sys_exit(int);
+extern int sys_wait4(int, int *, int, struct rusage *);
+
/*
* libgcc functions - functions that are used internally by the
* compiler... (prototypes are not correct though, but that
extern void __umoddi3(void);
extern void __umodsi3(void);
+extern void ret_from_exception(void);
+extern void fpundefinstr(void);
extern void fp_enter(void);
#define EXPORT_SYMBOL_ALIAS(sym,orig) \
const char __kstrtab_##sym##[] __attribute__((section(".kstrtab"))) = \
EXPORT_SYMBOL_ALIAS(fp_printk,printk);
EXPORT_SYMBOL_ALIAS(fp_send_sig,send_sig);
+#ifdef CONFIG_CPU_26
+EXPORT_SYMBOL(fpundefinstr);
+EXPORT_SYMBOL(ret_from_exception);
+#endif
+
/* platform dependent support */
EXPORT_SYMBOL(dump_thread);
EXPORT_SYMBOL(dump_fpu);
EXPORT_SYMBOL(udelay);
EXPORT_SYMBOL(xchg_str);
-
- /* expansion card support */
-#ifdef CONFIG_ARCH_ACORN
-EXPORT_SYMBOL(ecard_startfind);
-EXPORT_SYMBOL(ecard_find);
-EXPORT_SYMBOL(ecard_readchunk);
-EXPORT_SYMBOL(ecard_address);
+EXPORT_SYMBOL(local_bh_count);
+EXPORT_SYMBOL(local_irq_count);
+#ifdef CONFIG_CPU_32
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(iounmap);
#endif
+EXPORT_SYMBOL(kernel_thread);
EXPORT_SYMBOL(enable_irq);
EXPORT_SYMBOL(disable_irq);
/* processor dependencies */
EXPORT_SYMBOL(processor);
-EXPORT_SYMBOL(machine_type);
+EXPORT_SYMBOL(__machine_arch_type);
+
+ /* networking */
+EXPORT_SYMBOL(csum_partial_copy);
+EXPORT_SYMBOL(__csum_ipv6_magic);
/* io */
-EXPORT_SYMBOL(outswb);
+EXPORT_SYMBOL(outsb);
EXPORT_SYMBOL(outsw);
-EXPORT_SYMBOL(inswb);
+EXPORT_SYMBOL(outsl);
+EXPORT_SYMBOL(insb);
EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(insl);
+
+EXPORT_SYMBOL(_memcpy_fromio);
+EXPORT_SYMBOL(_memcpy_toio);
+EXPORT_SYMBOL(_memset_io);
/* address translation */
#ifndef __virt_to_phys__is_a_macro
EXPORT_SYMBOL(__bus_to_virt);
#endif
+#ifndef CONFIG_NO_PGT_CACHE
EXPORT_SYMBOL(quicklists);
+#endif
EXPORT_SYMBOL(__bad_pmd);
EXPORT_SYMBOL(__bad_pmd_kernel);
EXPORT_SYMBOL(armidlist);
EXPORT_SYMBOL(armidindex);
EXPORT_SYMBOL(elf_platform);
+
+ /* syscalls */
+EXPORT_SYMBOL(sys_write);
+EXPORT_SYMBOL(sys_read);
+EXPORT_SYMBOL(sys_lseek);
+EXPORT_SYMBOL(sys_open);
+EXPORT_SYMBOL(sys_exit);
+EXPORT_SYMBOL(sys_wait4);
+
+ /* semaphores */
+EXPORT_SYMBOL_NOVERS(__down_failed);
+EXPORT_SYMBOL_NOVERS(__down_interruptible_failed);
+EXPORT_SYMBOL_NOVERS(__up_wakeup);
+
--- /dev/null
+/*
+ * Arthur personality
+ * Copyright (C) 1998 Philip Blundell
+ */
+
+#include <linux/personality.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+
+#include <asm/ptrace.h>
+
+/* RISC OS doesn't have many signals, and a lot of those that it does
+ have don't map easily to any Linux equivalent. Never mind. */
+
+#define RISCOS_SIGABRT 1
+#define RISCOS_SIGFPE 2
+#define RISCOS_SIGILL 3
+#define RISCOS_SIGINT 4
+#define RISCOS_SIGSEGV 5
+#define RISCOS_SIGTERM 6
+#define RISCOS_SIGSTAK 7
+#define RISCOS_SIGUSR1 8
+#define RISCOS_SIGUSR2 9
+#define RISCOS_SIGOSERROR 10
+
+static unsigned long riscos_to_linux_signals[32] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31
+};
+
+static unsigned long linux_to_riscos_signals[32] = {
+ 0, -1, RISCOS_SIGINT, -1,
+ RISCOS_SIGILL, 5, RISCOS_SIGABRT, 7,
+ RISCOS_SIGFPE, 9, RISCOS_SIGUSR1, RISCOS_SIGSEGV,
+ RISCOS_SIGUSR2, 13, 14, RISCOS_SIGTERM,
+ 16, 17, 18, 19,
+ 20, 21, 22, 23,
+ 24, 25, 26, 27,
+ 28, 29, 30, 31
+};
+
+static void arthur_lcall7(int nr, struct pt_regs *regs)
+{
+ struct siginfo info;
+ info.si_signo = SIGSWI;
+ info.si_code = nr;
+ /* Bounce it to the emulator */
+ send_sig_info(SIGSWI, &info, current);
+}
+
+static struct exec_domain riscos_exec_domain = {
+ "Arthur", /* name */
+ (lcall7_func)arthur_lcall7,
+ PER_RISCOS, PER_RISCOS,
+ riscos_to_linux_signals,
+ linux_to_riscos_signals,
+#ifdef MODULE
+ &__this_module, /* No usage counter. */
+#else
+ NULL,
+#endif
+ NULL /* Nothing after this in the list. */
+};
+
+/*
+ * We could do with some locking to stop Arthur being removed while
+ * processes are using it.
+ */
+
+#ifdef MODULE
+int init_module(void)
+#else
+int initialise_arthur(void)
+#endif
+{
+ return register_exec_domain(&riscos_exec_domain);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+ unregister_exec_domain(&riscos_exec_domain);
+}
+#endif
.long SYMBOL_NAME(sys_lseek)
/* 20 */ .long SYMBOL_NAME(sys_getpid)
.long SYMBOL_NAME(sys_mount_wrapper)
- .long SYMBOL_NAME(sys_umount)
+ .long SYMBOL_NAME(sys_oldumount)
.long SYMBOL_NAME(sys_setuid)
.long SYMBOL_NAME(sys_getuid)
/* 25 */ .long SYMBOL_NAME(sys_stime)
.long SYMBOL_NAME(sys_geteuid)
/* 50 */ .long SYMBOL_NAME(sys_getegid)
.long SYMBOL_NAME(sys_acct)
- .long SYMBOL_NAME(sys_ni_syscall) /* was sys_phys */
+ .long SYMBOL_NAME(sys_umount)
.long SYMBOL_NAME(sys_ni_syscall) /* was sys_lock */
.long SYMBOL_NAME(sys_ioctl)
/* 55 */ .long SYMBOL_NAME(sys_fcntl)
.long SYMBOL_NAME(sys_ni_syscall) /* was sys_profil */
.long SYMBOL_NAME(sys_statfs)
/* 100 */ .long SYMBOL_NAME(sys_fstatfs)
- .long SYMBOL_NAME(sys_ni_syscall) /* .long _sys_ioperm */
+ .long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_socketcall)
.long SYMBOL_NAME(sys_syslog)
.long SYMBOL_NAME(sys_setitimer)
.long SYMBOL_NAME(sys_newlstat)
.long SYMBOL_NAME(sys_newfstat)
.long SYMBOL_NAME(sys_uname)
-/* 110 */ .long SYMBOL_NAME(sys_iopl)
+/* 110 */ .long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(sys_vhangup)
.long SYMBOL_NAME(sys_idle)
.long SYMBOL_NAME(sys_syscall) /* call a syscall */
.long SYMBOL_NAME(sys_capget)
/* 185 */ .long SYMBOL_NAME(sys_capset)
.long SYMBOL_NAME(sys_sigaltstack_wrapper)
+ .long SYMBOL_NAME(sys_sendfile)
+ .long SYMBOL_NAME(sys_ni_syscall)
+ .long SYMBOL_NAME(sys_ni_syscall)
+/* 190 */ .long SYMBOL_NAME(sys_vfork_wrapper)
.rept NR_syscalls-186
.long SYMBOL_NAME(sys_ni_syscall)
#include <linux/pci.h>
#include <linux/ptrace.h>
#include <linux/interrupt.h>
+#include <linux/mm.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/system.h>
+#include <asm/hardware.h>
-#define MAX_SLOTS 20
+#define MAX_SLOTS 21
extern void pcibios_fixup_ebsa285(struct pci_dev *dev);
extern void pcibios_init_ebsa285(void);
-extern void pcibios_fixup_vnc(struct pci_dev *dev);
-extern void pcibios_init_vnc(void);
int
pcibios_present(void)
int slot = PCI_SLOT(dev_fn);
if (slot < MAX_SLOTS)
- return 0xf8c00000 + (slot << 11) + (PCI_FUNC(dev_fn) << 8);
+ return PCICFG0_BASE + 0xc00000 +
+ (slot << 11) + (PCI_FUNC(dev_fn) << 8);
else
return 0;
} else
- return 0xf9000000 | (bus << 16) | (dev_fn << 8);
+ return PCICFG1_BASE | (bus << 16) | (dev_fn << 8);
}
int
struct pci_dev *dev;
for (dev = pci_devices; dev; dev = dev->next) {
- if (machine_is_ebsa285() || machine_is_cats())
- pcibios_fixup_ebsa285(dev);
- if (machine_is_netwinder())
- pcibios_fixup_vnc(dev);
+ pcibios_fixup_ebsa285(dev);
pcibios_write_config_byte(dev->bus->number, dev->devfn,
PCI_INTERRUPT_LINE, dev->irq);
dev->bus->number, dev->devfn,
dev->vendor, dev->device, dev->irq);
}
- if (machine_is_netwinder())
- hw_init();
+
+ hw_init();
}
__initfunc(void pcibios_init(void))
{
- if (machine_is_ebsa285() || machine_is_cats())
- pcibios_init_ebsa285();
- if (machine_is_netwinder())
- pcibios_init_vnc();
-
- printk("DEC21285 PCI revision %02X\n", *(unsigned char *)0xfe000008);
+ unsigned int mem_size = (unsigned int)high_memory - PAGE_OFFSET;
+ unsigned long cntl;
+
+ *CSR_SDRAMBASEMASK = (mem_size - 1) & 0x0ffc0000;
+ *CSR_SDRAMBASEOFFSET = 0;
+ *CSR_ROMBASEMASK = 0x80000000;
+ *CSR_CSRBASEMASK = 0;
+ *CSR_CSRBASEOFFSET = 0;
+ *CSR_PCIADDR_EXTN = 0;
+
+#ifdef CONFIG_HOST_FOOTBRIDGE
+ /*
+ * Against my better judgement, Philip Blundell still seems
+ * to be saying that we should initialise the PCI stuff here
+ * when the PCI_CFN bit is not set, dispite my comment below,
+ * which he decided to remove. If it is not set, then
+ * the card is in add-in mode, and we're in a machine where
+ * the bus is set up by 'others'.
+ *
+ * We should therefore not mess about with the mapping in
+ * anyway, and we should not be using the virt_to_bus functions
+ * that exist in the HOST architecture mode (since they assume
+ * a fixed mapping).
+ *
+ * Instead, you should be using ADDIN mode, which allows for
+ * this situation. This does assume that you have correctly
+ * initialised the PCI bus, which you must have done to get
+ * your PC booted.
+ *
+ * Unfortunately, he seems to be blind to this. I guess he'll
+ * also remove all this.
+ *
+ * And THIS COMMENT STAYS, even if this gets patched, thank
+ * you.
+ */
+
+ /*
+ * Map our SDRAM at a known address in PCI space, just in case
+ * the firmware had other ideas. Using a nonzero base is
+ * necessary, since some VGA cards forcefully use PCI addresses
+ * in the range 0x000a0000 to 0x000c0000. (eg, S3 cards).
+ *
+ * NOTE! If you need to chec the PCI_CFN bit in the SA110
+ * control register then you've configured the kernel wrong.
+ * If you're not using host mode, then DO NOT set
+ * CONFIG_HOST_FOOTBRIDGE, but use CONFIG_ADDIN_FOOTBRIDGE
+ * instead. In this case, you MUST supply some firmware
+ * to allow your PC to boot, plus we should not modify the
+ * mappings that the PC BIOS has set up for us.
+ */
+ *CSR_PCICACHELINESIZE = 0x00002008;
+ *CSR_PCICSRBASE = 0;
+ *CSR_PCICSRIOBASE = 0;
+ *CSR_PCISDRAMBASE = virt_to_bus((void *)PAGE_OFFSET);
+ *CSR_PCIROMBASE = 0;
+ *CSR_PCICMD = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_FAST_BACK |
+ PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY |
+ (1 << 31) | (1 << 29) | (1 << 28) | (1 << 24);
+#endif
+
+ /*
+ * Clear any existing errors - we aren't
+ * interested in historical data...
+ */
+ cntl = *CSR_SA110_CNTL & 0xffffde07;
+ *CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR;
+
+ pcibios_init_ebsa285();
+
+ printk(KERN_DEBUG"PCI: DEC21285 revision %02lX\n", *CSR_CLASSREV & 0xff);
}
__initfunc(void pcibios_fixup_bus(struct pci_bus *bus))
#include <asm/fiq.h>
#include <asm/io.h>
#include <asm/hardware.h>
-#include <asm/pgtable.h>
#include "dma.h"
if (channel != DMA_VIRTUAL_FLOPPY)
printk("arch_dma_count: invalid channel %d\n", channel);
else {
- extern int floppy_fiqresidual(void);
- return floppy_fiqresidual();
+ struct pt_regs regs;
+ get_fiq_regs(®s);
+ return regs.ARM_r9;
}
return 0;
}
if (channel != DMA_VIRTUAL_FLOPPY)
printk("arch_enable_dma: invalid channel %d\n", channel);
else {
+ struct pt_regs regs;
void *fiqhandler_start;
unsigned int fiqhandler_length;
extern void floppy_fiqsetup(unsigned long len, unsigned long addr,
return;
}
memcpy((void *)0x1c, fiqhandler_start, fiqhandler_length);
- flush_page_to_ram(0);
- floppy_fiqsetup(dma->buf.length, __bus_to_virt(dma->buf.address), (int)PCIO_FLOPPYDMABASE);
+ regs.ARM_r9 = dma->buf.length;
+ regs.ARM_r10 = __bus_to_virt(dma->buf.address);
+ regs.ARM_fp = (int)PCIO_FLOPPYDMABASE;
+ set_fiq_regs(®s);
enable_irq(dma->dma_irq);
}
}
}
}
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+ return 0;
+}
+
__initfunc(void arch_dma_init(dma_t *dma))
{
dma[DMA_VIRTUAL_FLOPPY].dma_irq = 64;
/*
* arch/arm/kernel/dma-arc.c
*
- * Copyright (C) 1998 Dave Gilbert / Russell King
+ * Copyright (C) 1998-1999 Dave Gilbert / Russell King
*
* DMA functions specific to Archimedes architecture
*/
+#include <linux/config.h>
#include <linux/sched.h>
#include <linux/init.h>
#include "dma.h"
-int arch_request_dma(dmach_t channel, dma_t *dma)
+int arch_request_dma(dmach_t channel, dma_t *dma, const char * dev_id)
{
if (channel == DMA_VIRTUAL_FLOPPY0 ||
channel == DMA_VIRTUAL_FLOPPY1)
void arch_free_dma(dmach_t channel, dma_t *dma)
{
- if (channel != DMA_VIRTUAL_FLOPPY0 &&
- channel != DMA_VIRTUAL_FLOPPY1)
- return 0;
- else
- return -EINVAL;
}
void arch_enable_dma(dmach_t channel, dma_t *dma)
{
switch (channel) {
+#ifdef CONFIG_BLK_DEV_FD
case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */
switch (dma->dma_mode) {
case DMA_MODE_READ: /* read */
restore_flags(flags);
}
break;
+#endif
}
}
+int arch_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+ switch (channel) {
+#ifdef CONFIG_BLK_DEV_FD
+ case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */
+ extern unsigned int fdc1772_bytestogo;
+
+ /* 10/1/1999 DAG - I presume its the number of bytes left? */
+ return fdc1772_bytestogo;
+ };
+ break;
+
+ case DMA_VIRTUAL_FLOPPY1: { /* Command completed */
+ /* 10/1/1999 DAG - Presume whether there is an outstanding command? */
+ extern unsigned int fdc1772_fdc_int_done;
+
+ return (fdc1772_fdc_int_done==0)?1:0; /* Explicit! If the int done is 0 then 1 int to go */
+ };
+ break;
+
+#endif
+
+ default:
+ printk("dma-arc.c:arch_get_dma_residue called with unknown/unconfigured DMA channel\n");
+ return 0;
+ };
+}
+
void arch_disable_dma(dmach_t channel, dma_t *dma)
{
if (channel != DMA_VIRTUAL_FLOPPY0 &&
disable_irq(dma->dma_irq);
}
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+ return 0;
+}
+
__initfunc(void arch_dma_init(dma_t *dma))
{
dma[DMA_VIRTUAL_FLOPPY0].dma_irq = 64;
#include <linux/errno.h>
#include <linux/init.h>
+#include <asm/spinlock.h>
+
+spinlock_t dma_spin_lock = SPIN_LOCK_UNLOCKED;
+
int request_dma(int channel, const char *device_id)
{
return -EINVAL;
+++ /dev/null
-/*
- * arch/arm/kernel/dma-ebsa285.c
- *
- * Copyright (C) 1998 Phil Blundell
- *
- * DMA functions specific to EBSA-285/CATS architectures
- *
- * Changelog:
- * 09/11/1998 RMK Split out ISA DMA functions to dma-isa.c
- */
-
-#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/mman.h>
-#include <linux/init.h>
-
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/hardware.h>
-
-#include "dma.h"
-#include "dma-isa.h"
-
-int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name)
-{
- switch (channel) {
- case 0:
- case 1: /* 21285 internal channels */
- return 0;
-
- case 2 ... 9:
- if (machine_is_cats())
- return isa_request_dma(channel - 2, dma, dev_name);
- }
-
- return -EINVAL;
-}
-
-void arch_free_dma(dmach_t channel, dma_t *dma)
-{
- /* nothing to do */
-}
-
-int arch_get_dma_residue(dmach_t channel, dma_t *dma)
-{
- int residue = 0;
-
- switch (channel) {
- case 0:
- case 1:
- break;
-#ifdef CONFIG_CATS
- case 2 ... 9:
- if (machine_is_cats())
- residue = isa_get_dma_residue(channel - 2);
-#endif
- }
- return residue;
-}
-
-void arch_enable_dma(dmach_t channel, dma_t *dma)
-{
- switch (channel) {
- case 0:
- case 1:
- /*
- * Not yet implemented
- */
- break;
-#ifdef CONFIG_CATS
- case 2 ... 9:
- if (machine_is_cats())
- isa_enable_dma(channel - 2, dma);
-#endif
- }
-}
-
-void arch_disable_dma(dmach_t channel, dma_t *dma)
-{
- switch (channel) {
- case 0:
- case 1:
- /*
- * Not yet implemented
- */
- break;
-#ifdef CONFIG_CATS
- case 2 ... 9:
- if (machine_is_cats())
- isa_disable_dma(channel - 2, dma);
-#endif
- }
-}
-
-__initfunc(void arch_dma_init(dma_t *dma))
-{
- /* Nothing to do */
-}
--- /dev/null
+/*
+ * arch/arm/kernel/dma-ebsa285.c
+ *
+ * Copyright (C) 1998 Phil Blundell
+ *
+ * DMA functions specific to EBSA-285/CATS architectures
+ *
+ * Changelog:
+ * 09-Nov-1998 RMK Split out ISA DMA functions to dma-isa.c
+ * 17-Mar-1999 RMK Allow any EBSA285-like architecture to have
+ * ISA DMA controllers.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/mman.h>
+#include <linux/init.h>
+
+#include <asm/page.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include "dma.h"
+#include "dma-isa.h"
+
+#ifdef CONFIG_ISA_DMA
+static int has_isa_dma;
+#else
+#define has_isa_dma 0
+#endif
+
+int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name)
+{
+ switch (channel) {
+ case _DC21285_DMA(0):
+ case _DC21285_DMA(1): /* 21285 internal channels */
+ return 0;
+
+ case _ISA_DMA(0) ... _ISA_DMA(7):
+ if (has_isa_dma)
+ return isa_request_dma(channel - _ISA_DMA(0), dma, dev_name);
+ }
+
+ return -EINVAL;
+}
+
+void arch_free_dma(dmach_t channel, dma_t *dma)
+{
+ /* nothing to do */
+}
+
+int arch_get_dma_residue(dmach_t channel, dma_t *dma)
+{
+ int residue = 0;
+
+ switch (channel) {
+ case _DC21285_DMA(0):
+ case _DC21285_DMA(1):
+ break;
+
+ case _ISA_DMA(0) ... _ISA_DMA(7):
+ if (has_isa_dma)
+ residue = isa_get_dma_residue(channel - _ISA_DMA(0), dma);
+ }
+ return residue;
+}
+
+void arch_enable_dma(dmach_t channel, dma_t *dma)
+{
+ switch (channel) {
+ case _DC21285_DMA(0):
+ case _DC21285_DMA(1):
+ /*
+ * Not yet implemented
+ */
+ break;
+
+ case _ISA_DMA(0) ... _ISA_DMA(7):
+ if (has_isa_dma)
+ isa_enable_dma(channel - _ISA_DMA(0), dma);
+ }
+}
+
+void arch_disable_dma(dmach_t channel, dma_t *dma)
+{
+ switch (channel) {
+ case _DC21285_DMA(0):
+ case _DC21285_DMA(1):
+ /*
+ * Not yet implemented
+ */
+ break;
+
+ case _ISA_DMA(0) ... _ISA_DMA(7):
+ if (has_isa_dma)
+ isa_disable_dma(channel - _ISA_DMA(0), dma);
+ }
+}
+
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns)
+{
+ return 0;
+}
+
+__initfunc(void arch_dma_init(dma_t *dma))
+{
+#ifdef CONFIG_ISA_DMA
+ has_isa_dma = isa_init_dma();
+#endif
+}
* Copyright (C) 1998 Phil Blundell
*/
#include <linux/sched.h>
+#include <linux/init.h>
#include <asm/dma.h>
#include <asm/io.h>
#include "dma.h"
#include "dma-isa.h"
+#define ISA_DMA_MODE_READ 0x44
+#define ISA_DMA_MODE_WRITE 0x48
+#define ISA_DMA_MODE_CASCADE 0xc0
+#define ISA_DMA_AUTOINIT 0x10
+
#define ISA_DMA_MASK 0
#define ISA_DMA_MODE 1
#define ISA_DMA_CLRFF 2
int isa_request_dma(int channel, dma_t *dma, const char *dev_name)
{
- if (channel != 4)
- return 0;
-
- return -EINVAL;
+ return 0;
}
void isa_free_dma(int channel, dma_t *dma)
unsigned int io_port = isa_dma_port[channel][ISA_DMA_COUNT];
int count;
- count = 1 + inb(io_port) + (inb(io_port) << 8);
+ count = 1 + inb(io_port);
+ count |= inb(io_port) << 8;
return channel < 4 ? count : (count << 1);
}
void isa_enable_dma(int channel, dma_t *dma)
{
- unsigned long address, length;
-
if (dma->invalid) {
+ unsigned long address, length;
+ unsigned int mode;
+
address = dma->buf.address;
length = dma->buf.length - 1;
- outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
outb(address >> 16, isa_dma_port[channel][ISA_DMA_PGLO]);
+ outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
if (channel >= 4) {
address >>= 1;
- length = (length >> 1) & 0xfe; /* why &0xfe? */
+ length >>= 1;
}
outb(0, isa_dma_port[channel][ISA_DMA_CLRFF]);
outb(length, isa_dma_port[channel][ISA_DMA_COUNT]);
outb(length >> 8, isa_dma_port[channel][ISA_DMA_COUNT]);
- outb(dma->dma_mode | (channel & 3), isa_dma_port[channel][ISA_DMA_MODE]);
+ mode = channel & 3;
- switch (dma->dma_mode) {
+ switch (dma->dma_mode & DMA_MODE_MASK) {
case DMA_MODE_READ:
+ mode |= ISA_DMA_MODE_READ;
dma_cache_inv(__bus_to_virt(dma->buf.address), dma->buf.length);
break;
case DMA_MODE_WRITE:
+ mode |= ISA_DMA_MODE_WRITE;
dma_cache_wback(__bus_to_virt(dma->buf.address), dma->buf.length);
break;
+
+ case DMA_MODE_CASCADE:
+ mode |= ISA_DMA_MODE_CASCADE;
+ break;
+
+ default:
+ break;
}
+
+ if (dma->dma_mode & DMA_AUTOINIT)
+ mode |= ISA_DMA_AUTOINIT;
+
+ outb(mode, isa_dma_port[channel][ISA_DMA_MODE]);
dma->invalid = 0;
}
outb(channel & 3, isa_dma_port[channel][ISA_DMA_MASK]);
{
outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]);
}
+
+__initfunc(int isa_init_dma(void))
+{
+ int dmac_found;
+
+ outb(0xff, 0x0d);
+ outb(0xff, 0xda);
+
+ outb(0x55, 0x00);
+ outb(0xaa, 0x00);
+
+ dmac_found = inb(0x00) == 0x55 && inb(0x00) == 0xaa;
+
+ if (dmac_found) {
+ int channel;
+
+ for (channel = 0; channel < 8; channel++)
+ isa_disable_dma(channel, NULL);
+
+ outb(0x40, 0x0b);
+ outb(0x41, 0x0b);
+ outb(0x42, 0x0b);
+ outb(0x43, 0x0b);
+
+ outb(0xc0, 0xd6);
+ outb(0x41, 0xd6);
+ outb(0x42, 0xd6);
+ outb(0x43, 0xd6);
+
+ outb(0, 0xd4);
+
+ outb(0x10, 0x08);
+ outb(0x10, 0xd0);
+
+ /*
+ * Is this correct? According to
+ * my documentation, it doesn't
+ * appear to be. It should be
+ * outb(0x3f, 0x40b); outb(0x3f, 0x4d6);
+ */
+ outb(0x30, 0x40b);
+ outb(0x31, 0x40b);
+ outb(0x32, 0x40b);
+ outb(0x33, 0x40b);
+ outb(0x31, 0x4d6);
+ outb(0x32, 0x4d6);
+ outb(0x33, 0x4d6);
+
+ request_dma(DMA_ISA_CASCADE, "cascade");
+ }
+
+ return dmac_found;
+}
*/
void isa_disable_dma(int channel, dma_t *dma);
+/*
+ * Initialise DMA
+ */
+int isa_init_dma(void);
#include <linux/init.h>
#include <asm/page.h>
-#include <asm/pgtable.h>
#include <asm/dma.h>
#include <asm/fiq.h>
#include <asm/io.h>
+#include <asm/iomd.h>
#include <asm/hardware.h>
#include <asm/uaccess.h>
break;
case DMA_VIRTUAL_FLOPPY: {
- extern int floppy_fiqresidual(void);
- residue = floppy_fiqresidual();
+ struct pt_regs regs;
+ get_fiq_regs(®s);
+ return regs.ARM_r9;
}
break;
}
set_fiq_handler(fiqhandler_start, fiqhandler_length);
set_fiq_regs(®s);
enable_irq(dma->dma_irq);
-
}
break;
}
}
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle)
+{
+ int tcr, speed;
+
+ if (cycle < 188)
+ speed = 3;
+ else if (cycle <= 250)
+ speed = 2;
+ else if (cycle < 438)
+ speed = 1;
+ else
+ speed = 0;
+
+ tcr = inb(IOMD_DMATCR);
+ speed &= 3;
+
+ switch (channel) {
+ case DMA_0:
+ tcr = (tcr & ~0x03) | speed;
+ break;
+
+ case DMA_1:
+ tcr = (tcr & ~0x0c) | (speed << 2);
+ break;
+
+ case DMA_2:
+ tcr = (tcr & ~0x30) | (speed << 4);
+ break;
+
+ case DMA_3:
+ tcr = (tcr & ~0xc0) | (speed << 6);
+ break;
+
+ default:
+ break;
+ }
+
+ outb(tcr, IOMD_DMATCR);
+}
+
__initfunc(void arch_dma_init(dma_t *dma))
{
outb(0, IOMD_IO0CR);
outb(0, IOMD_IO2CR);
outb(0, IOMD_IO3CR);
-// outb(0xf0, IOMD_DMATCR);
+ outb(0xa0, IOMD_DMATCR);
dma[0].dma_base = ioaddr(IOMD_IO0CURA);
dma[0].dma_irq = IRQ_DMA0;
+++ /dev/null
-/*
- * arch/arm/kernel/dma-vnc.c
- *
- * Copyright (C) 1998 Russell King
- */
-#include <linux/sched.h>
-#include <linux/malloc.h>
-#include <linux/mman.h>
-#include <linux/init.h>
-
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/hardware.h>
-
-#include "dma.h"
-#include "dma-isa.h"
-
-int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name)
-{
- if (channel < 8)
- return isa_request_dma(channel, dma, dev_name);
- return -EINVAL;
-}
-
-void arch_free_dma(dmach_t channel, dma_t *dma)
-{
- isa_free_dma(channel, dma);
-}
-
-int arch_get_dma_residue(dmach_t channel, dma_t *dma)
-{
- return isa_get_dma_residue(channel, dma);
-}
-
-void arch_enable_dma(dmach_t channel, dma_t *dma)
-{
- isa_enable_dma(channel, dma);
-}
-
-void arch_disable_dma(dmach_t channel, dma_t *dma)
-{
- isa_disable_dma(channel, dma);
-}
-
-__initfunc(void arch_dma_init(dma_t *dma))
-{
- /* Nothing to do */
-}
-
#include <linux/init.h>
#include <asm/page.h>
-#include <asm/pgtable.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/io.h>
printk (KERN_ERR "Trying to disable free DMA%d\n", channel);
}
+void set_dma_speed(dmach_t channel, int cycle_ns)
+{
+ dma_chan[channel].speed =
+ arch_set_dma_speed(channel, &dma_chan[channel], cycle_ns);
+}
+
int get_dma_residue(dmach_t channel)
{
return arch_get_dma_residue(channel, &dma_chan[channel]);
EXPORT_SYMBOL(set_dma_mode);
EXPORT_SYMBOL(get_dma_residue);
EXPORT_SYMBOL(set_dma_sg);
+EXPORT_SYMBOL(set_dma_speed);
__initfunc(void init_dma(void))
{
unsigned int active:1; /* Transfer active */
unsigned int invalid:1; /* Address/Count changed */
dmamode_t dma_mode; /* DMA mode */
+ int speed; /* DMA speed */
unsigned int lock; /* Device is allocated */
const char *device_id; /* Device name */
*/
int arch_get_dma_residue(dmach_t channel, dma_t *dma);
+/* Prototype: int arch_set_dma_speed(channel, dma, cycle)
+ * Purpose : Convert a cycle time to a register setting
+ * Params : channel - DMA channel number
+ * : dma - DMA structure for channel
+ * : cycle - cycle time in NS
+ * Returns : setting for 'dma->speed'
+ */
+int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle);
+
/* Prototype: void arch_dma_init(dma)
* Purpose : Initialise architecture specific DMA
* Params : dma - pointer to array of DMA structures
*
* Created from information from Acorns RiscOS3 PRMs
*
- * 08-Dec-1996 RMK Added code for the 9'th expansion card - the ether podule slot.
+ * 08-Dec-1996 RMK Added code for the 9'th expansion card - the ether
+ * podule slot.
* 06-May-1997 RMK Added blacklist for cards whose loader doesn't work.
- * 12-Sep-1997 RMK Created new handling of interrupt enables/disables - cards can
- * now register their own routine to control interrupts (recommended).
- * 29-Sep-1997 RMK Expansion card interrupt hardware not being re-enabled on reset from
- * Linux. (Caused cards not to respond under RiscOS without hard reset).
+ * 12-Sep-1997 RMK Created new handling of interrupt enables/disables
+ * - cards can now register their own routine to control
+ * interrupts (recommended).
+ * 29-Sep-1997 RMK Expansion card interrupt hardware not being re-enabled
+ * on reset from Linux. (Caused cards not to respond
+ * under RiscOS without hard reset).
* 15-Feb-1998 RMK Added DMA support
* 12-Sep-1998 RMK Added EASI support
+ * 10-Jan-1999 RMK Run loaders in a simulated RISC OS environment.
+ * 17-Apr-1999 RMK Support for EASI Type C cycles.
*/
#define ECARD_C
+#define __KERNEL_SYSCALLS__
#include <linux/config.h>
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
#include <linux/init.h>
-#include <asm/io.h>
-#include <asm/hardware.h>
+#include <asm/dma.h>
#include <asm/ecard.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/dma.h>
+#include <asm/pgtable.h>
#ifdef CONFIG_ARCH_ARC
#include <asm/arch/oldlatches.h>
#define oldlatch_init()
#endif
-#define BLACKLIST_NAME(m,p,s) { m, p, NULL, s }
-#define BLACKLIST_LOADER(m,p,l) { m, p, l, NULL }
-#define BLACKLIST_NOLOADER(m,p) { m, p, noloader, blacklisted_str }
-#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+enum req {
+ req_readbytes,
+ req_reset
+};
-extern unsigned long atomwide_serial_loader[], oak_scsi_loader[], noloader[];
-static const char blacklisted_str[] = "*loader s/w is not 32-bit compliant*";
+struct ecard_request {
+ enum req req;
+ ecard_t *ec;
+ unsigned int address;
+ unsigned int length;
+ unsigned int use_loader;
+ void *buffer;
+};
-static const struct expcard_blacklist {
+struct expcard_blacklist {
unsigned short manufacturer;
unsigned short product;
- const loader_t loader;
const char *type;
-} blacklist[] = {
-/* Cards without names */
- BLACKLIST_NAME(MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1"),
-
-/* Cards with corrected loader */
- BLACKLIST_LOADER(MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, atomwide_serial_loader),
- BLACKLIST_LOADER(MANU_OAK, PROD_OAK_SCSI, oak_scsi_loader),
+};
-/* Supported cards with broken loader */
- { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI, noloader, "AlSystems PowerTec SCSI" },
+static ecard_t *cards;
+static ecard_t *slot_to_expcard[MAX_ECARDS];
+static unsigned int ectcr;
+#ifdef HAS_EXPMASK
+static unsigned int have_expmask;
+#endif
-/* Unsupported cards with no loader */
- BLACKLIST_NOLOADER(MANU_MCS, PROD_MCS_CONNECT32)
+/* List of descriptions of cards which don't have an extended
+ * identification, or chunk directories containing a description.
+ */
+static const struct expcard_blacklist __init blacklist[] = {
+ { MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" }
};
+asmlinkage extern int
+ecard_loader_reset(volatile unsigned char *pa, loader_t loader);
+asmlinkage extern int
+ecard_loader_read(int off, volatile unsigned char *pa, loader_t loader);
extern int setup_arm_irq(int, struct irqaction *);
+extern void do_ecard_IRQ(int, struct pt_regs *);
+
+
+static void
+ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs);
+
+static struct irqaction irqexpansioncard = {
+ ecard_irq_noexpmask, SA_INTERRUPT, 0, "expansion cards", NULL, NULL
+};
+
+static inline unsigned short
+ecard_getu16(unsigned char *v)
+{
+ return v[0] | v[1] << 8;
+}
+static inline signed long
+ecard_gets24(unsigned char *v)
+{
+ return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
+}
+
+static inline ecard_t *
+slot_to_ecard(unsigned int slot)
+{
+ return slot < MAX_ECARDS ? slot_to_expcard[slot] : NULL;
+}
+
+/* ===================== Expansion card daemon ======================== */
/*
- * from linux/arch/arm/kernel/irq.c
+ * Since the loader programs on the expansion cards need to be run
+ * in a specific environment, create a separate task with this
+ * environment up, and pass requests to this task as and when we
+ * need to.
+ *
+ * This should allow 99% of loaders to be called from Linux.
+ *
+ * From a security standpoint, we trust the card vendors. This
+ * may be a misplaced trust.
*/
-extern void do_ecard_IRQ(int irq, struct pt_regs *);
+#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+#define POD_INT_ADDR(x) ((volatile unsigned char *)\
+ ((BUS_ADDR((x)) - IO_BASE) + IO_START))
-static ecard_t expcard[MAX_ECARDS];
-static signed char irqno_to_expcard[16];
-static unsigned int ecard_numcards, ecard_numirqcards;
-static unsigned int have_expmask;
+static void
+ecard_task_reset(struct ecard_request *req)
+{
+ if (req->ec == NULL) {
+ ecard_t *ec;
+
+ for (ec = cards; ec; ec = ec->next) {
+ printk(KERN_DEBUG "Resetting card %d\n",
+ ec->slot_no);
+
+ if (ec->loader)
+ ecard_loader_reset(POD_INT_ADDR(ec->podaddr),
+ ec->loader);
+ }
+ printk(KERN_DEBUG "All cards reset\n");
+ } else if (req->ec->loader)
+ ecard_loader_reset(POD_INT_ADDR(req->ec->podaddr),
+ req->ec->loader);
+}
+
+static void
+ecard_task_readbytes(struct ecard_request *req)
+{
+ unsigned char *buf = (unsigned char *)req->buffer;
+ volatile unsigned char *base_addr =
+ (volatile unsigned char *)POD_INT_ADDR(req->ec->podaddr);
+ unsigned int len = req->length;
+
+ if (req->ec->slot_no == 8) {
+ /*
+ * The card maintains an index which
+ * increments the address into a 4096-byte
+ * page on each access. We need to keep
+ * track of the counter.
+ */
+ static unsigned int index;
+ unsigned int offset, page;
+ unsigned char byte = 0; /* keep gcc quiet */
+
+ offset = req->address & 4095;
+ page = req->address >> 12;
+
+ if (page > 256)
+ return;
+
+ page *= 4;
+
+ if (offset == 0 || index > offset) {
+ /*
+ * We need to reset the index counter.
+ */
+ *base_addr = 0;
+ index = 0;
+ }
+
+ while (index <= offset) {
+ byte = base_addr[page];
+ index += 1;
+ }
+
+ while (len--) {
+ *buf++ = byte;
+ if (len) {
+ byte = base_addr[page];
+ index += 1;
+ }
+ }
+ } else {
+ unsigned int off = req->address;
+
+ if (!req->use_loader || !req->ec->loader) {
+ off *= 4;
+ while (len--) {
+ *buf++ = base_addr[off];
+ off += 4;
+ }
+ } else {
+ while(len--) {
+ /*
+ * The following is required by some
+ * expansion card loader programs.
+ */
+ *(unsigned long *)0x108 = 0;
+ *buf++ = ecard_loader_read(off++, base_addr,
+ req->ec->loader);
+ }
+ }
+ }
+
+}
+
+#ifdef CONFIG_CPU_32
+static pid_t ecard_pid;
+static wait_queue_head_t ecard_wait;
+static wait_queue_head_t ecard_done;
+static struct ecard_request *ecard_req;
+
+/*
+ * Set up the expansion card daemon's environment.
+ */
+static void
+ecard_init_task(void)
+{
+ /* We want to set up the page tables for the following mapping:
+ * Virtual Physical
+ * 0x03000000 0x03000000
+ * 0x03010000 unmapped
+ * 0x03210000 0x03210000
+ * 0x03400000 unmapped
+ * 0x08000000 0x08000000
+ * 0x10000000 unmapped
+ *
+ * FIXME: we don't follow this 100% yet.
+ */
+ pgd_t *src_pgd, *dst_pgd;
+ unsigned int dst_addr = IO_START;
+
+ src_pgd = pgd_offset(current->mm, IO_BASE);
+ dst_pgd = pgd_offset(current->mm, dst_addr);
+
+ while (dst_addr < IO_START + IO_SIZE) {
+ *dst_pgd++ = *src_pgd++;
+ dst_addr += PGDIR_SIZE;
+ }
+
+ flush_tlb_range(current->mm, IO_START, IO_START + IO_SIZE);
+
+ dst_addr = EASI_START;
+ src_pgd = pgd_offset(current->mm, EASI_BASE);
+ dst_pgd = pgd_offset(current->mm, dst_addr);
+
+ while (dst_addr < EASI_START + EASI_SIZE) {
+ *dst_pgd++ = *src_pgd++;
+ dst_addr += PGDIR_SIZE;
+ }
+
+ flush_tlb_range(current->mm, EASI_START, EASI_START + EASI_SIZE);
+}
+
+static int
+ecard_task(void * unused)
+{
+ current->session = 1;
+ current->pgrp = 1;
+
+ /*
+ * We don't want /any/ signals, not even SIGKILL
+ */
+ sigfillset(¤t->blocked);
+ sigemptyset(¤t->signal);
+
+ strcpy(current->comm, "kecardd");
+
+ /*
+ * Set up the environment
+ */
+ ecard_init_task();
+
+ while (1) {
+ struct ecard_request *req;
+
+ do {
+ req = xchg(&ecard_req, NULL);
+
+ if (req == NULL) {
+ sigemptyset(¤t->signal);
+ interruptible_sleep_on(&ecard_wait);
+ }
+ } while (req == NULL);
+
+ switch (req->req) {
+ case req_readbytes:
+ ecard_task_readbytes(req);
+ break;
+
+ case req_reset:
+ ecard_task_reset(req);
+ break;
+ }
+ wake_up(&ecard_done);
+ }
+}
+
+/*
+ * Wake the expansion card daemon to action our request.
+ *
+ * FIXME: The test here is not sufficient to detect if the
+ * kcardd is running.
+ */
+static inline void
+ecard_call(struct ecard_request *req)
+{
+ /*
+ * If we're called from task 0, or from an
+ * interrupt (will be keyboard interrupt),
+ * we forcefully set up the memory map, and
+ * call the loader. We can't schedule, or
+ * sleep for this call.
+ */
+ if ((current == task[0] || in_interrupt()) &&
+ req->req == req_reset && req->ec == NULL) {
+ ecard_init_task();
+ ecard_task_reset(req);
+ } else {
+ if (ecard_pid <= 0)
+ ecard_pid = kernel_thread(ecard_task, NULL, 0);
+
+ ecard_req = req;
+
+ wake_up(&ecard_wait);
+
+ sleep_on(&ecard_done);
+ }
+}
+#else
+/*
+ * On 26-bit processors, we don't need the kcardd thread to access the
+ * expansion card loaders. We do it directly.
+ */
+static inline void
+ecard_call(struct ecard_request *req)
+{
+ if (req->req == req_reset)
+ ecard_task_reset(req);
+ else
+ ecard_task_readbytes(req);
+}
+#endif
+
+/* ======================= Mid-level card control ===================== */
+/*
+ * This is called to reset the loaders for each expansion card on reboot.
+ *
+ * This is required to make sure that the card is in the correct state
+ * that RiscOS expects it to be.
+ */
+void
+ecard_reset(int slot)
+{
+ struct ecard_request req;
+
+ req.req = req_reset;
+
+ if (slot < 0)
+ req.ec = NULL;
+ else
+ req.ec = slot_to_ecard(slot);
+
+ ecard_call(&req);
+
+#ifdef HAS_EXPMASK
+ if (have_expmask && slot < 0) {
+ have_expmask |= ~0;
+ EXPMASK_ENABLE = have_expmask;
+ }
+#endif
+}
+
+static void
+ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
+{
+ struct ecard_request req;
+
+ req.req = req_readbytes;
+ req.ec = ec;
+ req.address = off;
+ req.length = len;
+ req.use_loader = useld;
+ req.buffer = addr;
+
+ ecard_call(&req);
+}
+
+int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
+{
+ struct ex_chunk_dir excd;
+ int index = 16;
+ int useld = 0;
+
+ if (!ec->cid.cd)
+ return 0;
+
+ while(1) {
+ ecard_readbytes(&excd, ec, index, 8, useld);
+ index += 8;
+ if (c_id(&excd) == 0) {
+ if (!useld && ec->loader) {
+ useld = 1;
+ index = 0;
+ continue;
+ }
+ return 0;
+ }
+ if (c_id(&excd) == 0xf0) { /* link */
+ index = c_start(&excd);
+ continue;
+ }
+ if (c_id(&excd) == 0x80) { /* loader */
+ if (!ec->loader) {
+ ec->loader = (loader_t)kmalloc(c_len(&excd),
+ GFP_KERNEL);
+ if (ec->loader)
+ ecard_readbytes(ec->loader, ec,
+ (int)c_start(&excd),
+ c_len(&excd), useld);
+ else
+ return 0;
+ }
+ continue;
+ }
+ if (c_id(&excd) == id && num-- == 0)
+ break;
+ }
+
+ if (c_id(&excd) & 0x80) {
+ switch (c_id(&excd) & 0x70) {
+ case 0x70:
+ ecard_readbytes((unsigned char *)excd.d.string, ec,
+ (int)c_start(&excd), c_len(&excd),
+ useld);
+ break;
+ case 0x00:
+ break;
+ }
+ }
+ cd->start_offset = c_start(&excd);
+ memcpy(cd->d.string, excd.d.string, 256);
+ return 1;
+}
+
+/* ======================= Interrupt control ============================ */
static void ecard_def_irq_enable(ecard_t *ec, int irqnr)
{
#endif
}
+static int ecard_def_irq_pending(ecard_t *ec)
+{
+ return !ec->irqmask || ec->irqaddr[0] & ec->irqmask;
+}
+
static void ecard_def_fiq_enable(ecard_t *ec, int fiqnr)
{
panic("ecard_def_fiq_enable called - impossible");
panic("ecard_def_fiq_disable called - impossible");
}
+static int ecard_def_fiq_pending(ecard_t *ec)
+{
+ return !ec->fiqmask || ec->fiqaddr[0] & ec->fiqmask;
+}
+
static expansioncard_ops_t ecard_default_ops = {
ecard_def_irq_enable,
ecard_def_irq_disable,
+ ecard_def_irq_pending,
ecard_def_fiq_enable,
- ecard_def_fiq_disable
+ ecard_def_fiq_disable,
+ ecard_def_fiq_pending
};
/*
*/
void ecard_enableirq(unsigned int irqnr)
{
- irqnr &= 7;
- if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) {
- ecard_t *ec = expcard + irqno_to_expcard[irqnr];
+ ecard_t *ec = slot_to_ecard(irqnr - 32);
+ if (ec) {
if (!ec->ops)
ec->ops = &ecard_default_ops;
void ecard_disableirq(unsigned int irqnr)
{
- irqnr &= 7;
- if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) {
- ecard_t *ec = expcard + irqno_to_expcard[irqnr];
+ ecard_t *ec = slot_to_ecard(irqnr - 32);
+ if (ec) {
if (!ec->ops)
ec->ops = &ecard_default_ops;
void ecard_enablefiq(unsigned int fiqnr)
{
- fiqnr &= 7;
- if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) {
- ecard_t *ec = expcard + irqno_to_expcard[fiqnr];
+ ecard_t *ec = slot_to_ecard(fiqnr);
+ if (ec) {
if (!ec->ops)
ec->ops = &ecard_default_ops;
void ecard_disablefiq(unsigned int fiqnr)
{
- fiqnr &= 7;
- if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) {
- ecard_t *ec = expcard + irqno_to_expcard[fiqnr];
+ ecard_t *ec = slot_to_ecard(fiqnr);
+ if (ec) {
if (!ec->ops)
ec->ops = &ecard_default_ops;
}
}
-static void ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs)
+static void
+ecard_dump_irq_state(ecard_t *ec)
{
- const int num_cards = ecard_numirqcards;
- int i, called = 0;
+ printk(" %d: %sclaimed, ",
+ ec->slot_no,
+ ec->claimed ? "" : "not ");
+
+ if (ec->ops && ec->ops->irqpending &&
+ ec->ops != &ecard_default_ops)
+ printk("irq %spending\n",
+ ec->ops->irqpending(ec) ? "" : "not ");
+ else
+ printk("irqaddr %p, mask = %02X, status = %02X\n",
+ ec->irqaddr, ec->irqmask, *ec->irqaddr);
+}
- for (i = 0; i < num_cards; i++) {
- if (expcard[i].claimed && expcard[i].irq &&
- (!expcard[i].irqmask ||
- expcard[i].irqaddr[0] & expcard[i].irqmask)) {
- do_ecard_IRQ(expcard[i].irq, regs);
- called ++;
+static void
+ecard_check_lockup(void)
+{
+ static int last, lockup;
+ ecard_t *ec;
+
+ /*
+ * If the timer interrupt has not run since the last million
+ * unrecognised expansion card interrupts, then there is
+ * something seriously wrong. Disable the expansion card
+ * interrupts so at least we can continue.
+ *
+ * Maybe we ought to start a timer to re-enable them some time
+ * later?
+ */
+ if (last == jiffies) {
+ lockup += 1;
+ if (lockup > 1000000) {
+ printk(KERN_ERR "\nInterrupt lockup detected - "
+ "disabling all expansion card interrupts\n");
+
+ disable_irq(IRQ_EXPANSIONCARD);
+
+ printk("Expansion card IRQ state:\n");
+
+ for (ec = cards; ec; ec = ec->next)
+ ecard_dump_irq_state(ec);
}
+ } else
+ lockup = 0;
+
+ /*
+ * If we did not recognise the source of this interrupt,
+ * warn the user, but don't flood the user with these messages.
+ */
+ if (!last || time_after(jiffies, last + 5*HZ)) {
+ last = jiffies;
+ printk(KERN_WARNING "Unrecognised interrupt from backplane\n");
}
- cli();
- if (called == 0) {
- static int last, lockup;
-
- if (last == jiffies) {
- lockup += 1;
- if (lockup > 1000000) {
- printk(KERN_ERR "\nInterrupt lockup detected - disabling expansion card IRQs\n");
- disable_irq(intr_no);
- printk("Expansion card IRQ state:\n");
- for (i = 0; i < num_cards; i++)
- printk(" %d: %sclaimed, irqaddr = %p, irqmask = %X, status=%X\n", expcard[i].irq - 32,
- expcard[i].claimed ? "" : "not", expcard[i].irqaddr, expcard[i].irqmask, *expcard[i].irqaddr);
- }
- } else
- lockup = 0;
+}
+
+static void
+ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs)
+{
+ ecard_t *ec;
+ int called = 0;
+
+ for (ec = cards; ec; ec = ec->next) {
+ int pending;
+
+ if (!ec->claimed || ec->irq == NO_IRQ || ec->slot_no == 8)
+ continue;
- if (!last || time_after(jiffies, last + 5*HZ)) {
- last = jiffies;
- printk(KERN_ERR "\nUnrecognised interrupt from backplane\n");
+ if (ec->ops && ec->ops->irqpending)
+ pending = ec->ops->irqpending(ec);
+ else
+ pending = ecard_default_ops.irqpending(ec);
+
+ if (pending) {
+ do_ecard_IRQ(ec->irq, regs);
+ called ++;
}
}
+ cli();
+
+ if (called == 0)
+ ecard_check_lockup();
}
#ifdef HAS_EXPMASK
0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00
};
-static void ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs)
+static void
+ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs)
{
const unsigned int statusmask = 15;
unsigned int status;
status = EXPMASK_STATUS & statusmask;
if (status) {
- unsigned int irqno;
+ unsigned int slot;
ecard_t *ec;
again:
- irqno = first_set[status];
- ec = expcard + irqno_to_expcard[irqno];
+ slot = first_set[status];
+ ec = slot_to_ecard(slot);
if (ec->claimed) {
unsigned int oldexpmask;
/*
- * this ugly code is so that we can operate a prioritorising system.
+ * this ugly code is so that we can operate a
+ * prioritorising system:
+ *
* Card 0 highest priority
* Card 1
* Card 2
* Card 3 lowest priority
+ *
* Serial cards should go in 0/1, ethernet/scsi in 2/3
* otherwise you will lose serial data at high speeds!
*/
oldexpmask = have_expmask;
- EXPMASK_ENABLE = (have_expmask &= priority_masks[irqno]);
+ EXPMASK_ENABLE = (have_expmask &= priority_masks[slot]);
sti();
do_ecard_IRQ(ec->irq, regs);
cli();
if (status)
goto again;
} else {
- printk(KERN_WARNING "card%d: interrupt from unclaimed card???\n", irqno);
- EXPMASK_ENABLE = (have_expmask &= ~(1 << irqno));
+ printk(KERN_WARNING "card%d: interrupt from unclaimed "
+ "card???\n", slot);
+ EXPMASK_ENABLE = (have_expmask &= ~(1 << slot));
}
} else
printk(KERN_WARNING "Wild interrupt from backplane (masks)\n");
}
-static int ecard_checkirqhw(void)
+__initfunc(static void
+ecard_probeirqhw(void))
{
+ ecard_t *ec;
int found;
EXPMASK_ENABLE = 0x00;
found = ((EXPMASK_STATUS & 15) == 0);
EXPMASK_ENABLE = 0xff;
- return found;
+ if (!found)
+ return;
+
+ printk(KERN_DEBUG "Expansion card interrupt "
+ "management hardware found\n");
+
+ irqexpansioncard.handler = ecard_irq_expmask;
+
+ /* for each card present, set a bit to '1' */
+ have_expmask = 0x80000000;
+
+ for (ec = cards; ec; ec = ec->next)
+ have_expmask |= 1 << ec->slot_no;
+
+ EXPMASK_ENABLE = have_expmask;
}
+#else
+#define ecard_probeirqhw()
+#endif
+
+#ifndef IO_EC_MEMC8_BASE
+#define IO_EC_MEMC8_BASE 0
#endif
-static void ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld)
+unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
{
- extern int ecard_loader_read(int off, volatile unsigned int pa, loader_t loader);
- unsigned char *a = (unsigned char *)addr;
+ unsigned long address = 0;
+ int slot = ec->slot_no;
- if (ec->slot_no == 8) {
- static unsigned int lowaddress;
- unsigned int laddr, haddr;
- unsigned char byte = 0; /* keep gcc quiet */
+ if (ec->slot_no == 8)
+ return IO_EC_MEMC8_BASE;
- laddr = off & 4095; /* number of bytes to read from offset + base addr */
- haddr = off >> 12; /* offset into card from base addr */
+ ectcr &= ~(1 << slot);
- if (haddr > 256)
- return;
+ switch (type) {
+ case ECARD_MEMC:
+ if (slot < 4)
+ address = IO_EC_MEMC_BASE + (slot << 12);
+ break;
- /*
- * If we require a low address or address 0, then reset, and start again...
- */
- if (!off || lowaddress > laddr) {
- outb(0, ec->podaddr);
- lowaddress = 0;
- }
- while (lowaddress <= laddr) {
- byte = inb(ec->podaddr + haddr);
- lowaddress += 1;
- }
- while (len--) {
- *a++ = byte;
- if (len) {
- byte = inb(ec->podaddr + haddr);
- lowaddress += 1;
- }
- }
- } else {
- if (!useld || !ec->loader) {
- while(len--)
- *a++ = inb(ec->podaddr + (off++));
- } else {
- while(len--) {
- *(unsigned long *)0x108 = 0; /* hack for some loaders!!! */
- *a++ = ecard_loader_read(off++, BUS_ADDR(ec->podaddr), ec->loader);
- }
- }
+ case ECARD_IOC:
+ if (slot < 4)
+ address = IO_EC_IOC_BASE + (slot << 12);
+#ifdef IO_EC_IOC4_BASE
+ else
+ address = IO_EC_IOC4_BASE + ((slot - 4) << 12);
+#endif
+ if (address)
+ address += speed << 17;
+ break;
+
+#ifdef IO_EC_EASI_BASE
+ case ECARD_EASI:
+ address = IO_EC_EASI_BASE + (slot << 22);
+ if (speed == ECARD_FAST)
+ ectcr |= 1 << slot;
+ break;
+#endif
}
+
+#ifdef IOMD_ECTCR
+ outb(ectcr, IOMD_ECTCR);
+#endif
+ return address;
}
+static const char *unknown = "*unknown*";
+
static int ecard_prints(char *buffer, ecard_t *ec)
{
char *start = buffer;
- buffer += sprintf(buffer, "\n %d: ", ec->slot_no);
+ buffer += sprintf(buffer, " %d: %s ", ec->slot_no,
+ ec->type == ECARD_EASI ? "EASI" : " ");
if (ec->cid.id == 0) {
struct in_chunk_dir incd;
buffer += sprintf(buffer, "[%04X:%04X] ",
ec->cid.manufacturer, ec->cid.product);
- if (!ec->card_desc && ec->cid.is && ec->cid.cd &&
- ecard_readchunk(&incd, ec, 0xf5, 0))
- ec->card_desc = incd.d.string;
+ if (!ec->card_desc && ec->cid.cd &&
+ ecard_readchunk(&incd, ec, 0xf5, 0)) {
+ ec->card_desc = kmalloc(strlen(incd.d.string)+1, GFP_KERNEL);
- if (!ec->card_desc)
- ec->card_desc = "*unknown*";
+ if (ec->card_desc)
+ strcpy(ec->card_desc, incd.d.string);
+ }
- buffer += sprintf(buffer, "%s", ec->card_desc);
+ buffer += sprintf(buffer, "%s\n", ec->card_desc ? ec->card_desc : "*unknown*");
} else
- buffer += sprintf(buffer, "Simple card %d", ec->cid.id);
+ buffer += sprintf(buffer, "Simple card %d\n", ec->cid.id);
return buffer - start;
}
-static inline unsigned short ecard_getu16(unsigned char *v)
+int get_ecard_dev_info(char *buf, char **start, off_t pos, int count, int wr)
{
- return v[0] | v[1] << 8;
+ ecard_t *ec = cards;
+ off_t at = 0;
+ int len, cnt;
+
+ cnt = 0;
+ while (ec && count > cnt) {
+ len = ecard_prints(buf, ec);
+ at += len;
+ if (at >= pos) {
+ if (!*start) {
+ *start = buf + (pos - (at - len));
+ cnt = at - pos;
+ } else
+ cnt += len;
+ buf += len;
+ }
+ ec = ec->next;
+ }
+ return (count > cnt) ? cnt : count;
}
-static inline signed long ecard_gets24(unsigned char *v)
+static struct proc_dir_entry proc_ecard_devices = {
+ PROC_BUS_ECARD_DEVICES, 7, "devices",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
+ get_ecard_dev_info
+};
+
+static struct proc_dir_entry *proc_bus_ecard_dir;
+
+static void ecard_proc_init(void)
{
- return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0);
+ proc_bus_ecard_dir = create_proc_entry("ecard", S_IFDIR, proc_bus);
+ proc_register(proc_bus_ecard_dir, &proc_ecard_devices);
}
/*
* If bit 1 of the first byte of the card is set, then the
* card does not exist.
*/
-__initfunc(static int ecard_probe(int card, int freeslot, card_type_t type))
+__initfunc(static int
+ecard_probe(int slot, card_type_t type))
{
- ecard_t *ec = expcard + freeslot;
+ ecard_t **ecp;
+ ecard_t *ec;
struct ex_ecid cid;
char buffer[200];
- int i;
+ int i, rc = -ENOMEM;
+
+ ec = kmalloc(sizeof(ecard_t), GFP_KERNEL);
+
+ if (!ec)
+ goto nodev;
- irqno_to_expcard[card] = -1;
+ memset(ec, 0, sizeof(ecard_t));
- ec->slot_no = card;
+ ec->slot_no = slot;
+ ec->type = type;
ec->irq = NO_IRQ;
ec->fiq = NO_IRQ;
ec->dma = NO_DMA;
ec->card_desc = NULL;
ec->ops = &ecard_default_ops;
+ rc = -ENODEV;
if ((ec->podaddr = ecard_address(ec, type, ECARD_SYNC)) == 0)
- return 0;
+ goto nodev;
cid.r_zero = 1;
ecard_readbytes(&cid, ec, 0, 16, 0);
if (cid.r_zero)
- return 0;
+ goto nodev;
- irqno_to_expcard[card] = freeslot;
-
- ec->type = type;
ec->cid.id = cid.r_id;
ec->cid.cd = cid.r_cd;
ec->cid.is = cid.r_is;
ec->cid.fiqmask = cid.r_fiqmask;
ec->cid.fiqoff = ecard_gets24(cid.r_fiqoff);
ec->fiqaddr =
- ec->irqaddr = (unsigned char *)BUS_ADDR(ec->podaddr);
+ ec->irqaddr = (unsigned char *)ioaddr(ec->podaddr);
- if (ec->cid.cd && ec->cid.is) {
+ if (ec->cid.is) {
ec->irqmask = ec->cid.irqmask;
ec->irqaddr += ec->cid.irqoff;
ec->fiqmask = ec->cid.fiqmask;
for (i = 0; i < sizeof(blacklist) / sizeof(*blacklist); i++)
if (blacklist[i].manufacturer == ec->cid.manufacturer &&
blacklist[i].product == ec->cid.product) {
- ec->loader = blacklist[i].loader;
ec->card_desc = blacklist[i].type;
break;
}
- ecard_prints(buffer, ec);
- printk("%s", buffer);
-
- ec->irq = 32 + card;
+ ec->irq = 32 + slot;
#ifdef IO_EC_MEMC8_BASE
- if (card == 8)
+ if (slot == 8)
ec->irq = 11;
#endif
#ifdef CONFIG_ARCH_RPC
/* On RiscPC, only first two slots have DMA capability */
- if (card < 2)
- ec->dma = 2 + card;
+ if (slot < 2)
+ ec->dma = 2 + slot;
#endif
#if 0 /* We don't support FIQs on expansion cards at the moment */
- ec->fiq = 96 + card;
+ ec->fiq = 96 + slot;
#endif
- return 1;
-}
+ rc = 0;
-/*
- * This is called to reset the loaders for each expansion card on reboot.
- *
- * This is required to make sure that the card is in the correct state
- * that RiscOS expects it to be.
- */
-void ecard_reset(int card)
-{
- extern int ecard_loader_reset(volatile unsigned int pa, loader_t loader);
+ for (ecp = &cards; *ecp; ecp = &(*ecp)->next);
- if (card >= ecard_numcards)
- return;
-
- if (card < 0) {
- for (card = 0; card < ecard_numcards; card++)
- if (expcard[card].loader)
- ecard_loader_reset(BUS_ADDR(expcard[card].podaddr),
- expcard[card].loader);
- } else
- if (expcard[card].loader)
- ecard_loader_reset(BUS_ADDR(expcard[card].podaddr),
- expcard[card].loader);
+ *ecp = ec;
-#ifdef HAS_EXPMASK
- if (have_expmask) {
- have_expmask |= ~0;
- EXPMASK_ENABLE = have_expmask;
+nodev:
+ if (rc && ec)
+ kfree(ec);
+ else {
+ slot_to_expcard[slot] = ec;
+
+ ecard_prints(buffer, ec);
+ printk("%s", buffer);
}
-#endif
+ return rc;
}
-static unsigned int ecard_startcard;
+static ecard_t *finding_pos;
void ecard_startfind(void)
{
- ecard_startcard = 0;
+ finding_pos = NULL;
}
ecard_t *ecard_find(int cid, const card_ids *cids)
{
- int card;
+ if (!finding_pos)
+ finding_pos = cards;
+ else
+ finding_pos = finding_pos->next;
+
+ for (; finding_pos; finding_pos = finding_pos->next) {
+ if (finding_pos->claimed)
+ continue;
- if (!cids) {
- for (card = ecard_startcard; card < ecard_numcards; card++)
- if (!expcard[card].claimed &&
- (expcard[card].cid.id ^ cid) == 0)
+ if (!cids) {
+ if ((finding_pos->cid.id ^ cid) == 0)
break;
- } else {
- for (card = ecard_startcard; card < ecard_numcards; card++) {
+ } else {
unsigned int manufacturer, product;
int i;
- if (expcard[card].claimed)
- continue;
-
- manufacturer = expcard[card].cid.manufacturer;
- product = expcard[card].cid.product;
+ manufacturer = finding_pos->cid.manufacturer;
+ product = finding_pos->cid.product;
for (i = 0; cids[i].manufacturer != 65535; i++)
if (manufacturer == cids[i].manufacturer &&
}
}
- ecard_startcard = card + 1;
-
- return card < ecard_numcards ? &expcard[card] : NULL;
+ return finding_pos;
}
-int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num)
+__initfunc(static void ecard_free_all(void))
{
- struct ex_chunk_dir excd;
- int index = 16;
- int useld = 0;
+ ecard_t *ec, *ecn;
- if (!ec->cid.is || !ec->cid.cd)
- return 0;
-
- while(1) {
- ecard_readbytes(&excd, ec, index, 8, useld);
- index += 8;
- if (c_id(&excd) == 0) {
- if (!useld && ec->loader) {
- useld = 1;
- index = 0;
- continue;
- }
- return 0;
- }
- if (c_id(&excd) == 0xf0) { /* link */
- index = c_start(&excd);
- continue;
- }
- if (c_id(&excd) == 0x80) { /* loader */
- if (!ec->loader) {
- ec->loader = (loader_t)kmalloc(c_len(&excd), GFP_KERNEL);
- ecard_readbytes(ec->loader, ec, (int)c_start(&excd), c_len(&excd), useld);
- }
- continue;
- }
- if (c_id(&excd) == id && num-- == 0)
- break;
- }
+ for (ec = cards; ec; ec = ecn) {
+ ecn = ec->next;
- if (c_id(&excd) & 0x80) {
- switch (c_id(&excd) & 0x70) {
- case 0x70:
- ecard_readbytes((unsigned char *)excd.d.string, ec,
- (int)c_start(&excd), c_len(&excd), useld);
- break;
- case 0x00:
- break;
- }
+ kfree(ec);
}
- cd->start_offset = c_start(&excd);
- memcpy(cd->d.string, excd.d.string, 256);
- return 1;
-}
-
-unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed)
-{
- switch (ec->slot_no) {
- case 0 ... 3:
- switch (type) {
- case ECARD_MEMC:
- return IO_EC_MEMC_BASE + (ec->slot_no << 12);
-
- case ECARD_IOC:
- return IO_EC_IOC_BASE + (speed << 17) + (ec->slot_no << 12);
-
-#ifdef IO_EC_EASI_BASE
- case ECARD_EASI:
- return IO_EC_EASI_BASE + (ec->slot_no << 22);
-#endif
- }
- break;
- case 4 ... 7:
- switch (type) {
-#ifdef IO_EC_IOC4_BASE
- case ECARD_IOC:
- return IO_EC_IOC4_BASE + (speed << 17) + ((ec->slot_no - 4) << 12);
-#endif
-#ifdef IO_EC_EASI_BASE
- case ECARD_EASI:
- return IO_EC_EASI_BASE + (ec->slot_no << 22);
-#endif
- default:
- break;
- }
- break;
+ cards = NULL;
-#ifdef IO_EC_MEMC8_BASE
- case 8:
- return IO_EC_MEMC8_BASE;
-#endif
- }
- return 0;
+ memset(slot_to_expcard, 0, sizeof(slot_to_expcard));
}
-static struct irqaction irqexpansioncard = {
- ecard_irq_noexpmask,
- SA_INTERRUPT,
- 0,
- "expansion cards",
- NULL,
- NULL
-};
-
/*
* Initialise the expansion card system.
* Locate all hardware - interrupt management and
*/
__initfunc(void ecard_init(void))
{
- int i, nc = 0;
+ int slot;
- memset(expcard, 0, sizeof(expcard));
+ oldlatch_init();
-#ifdef HAS_EXPMASK
- if (ecard_checkirqhw()) {
- printk(KERN_DEBUG "Expansion card interrupt management hardware found\n");
- irqexpansioncard.handler = ecard_irq_expmask;
- irqexpansioncard.flags |= SA_IRQNOMASK;
- have_expmask = -1;
- }
+#ifdef CONFIG_CPU_32
+ init_waitqueue_head(&ecard_wait);
+ init_waitqueue_head(&ecard_done);
#endif
- printk("Installed expansion cards:");
+ printk("Probing expansion cards: (does not imply support)\n");
- /*
- * First of all, probe all cards on the expansion card interrupt line
- */
- for (i = 0; i < 8; i++)
- if (ecard_probe(i, nc, ECARD_IOC) || ecard_probe(i, nc, ECARD_EASI))
- nc += 1;
- else
- have_expmask &= ~(1<<i);
-
- ecard_numirqcards = nc;
+ for (slot = 0; slot < 8; slot ++) {
+ if (ecard_probe(slot, ECARD_EASI) == -ENODEV)
+ ecard_probe(slot, ECARD_IOC);
+ }
- /* Now probe other cards with different interrupt lines
- */
#ifdef IO_EC_MEMC8_BASE
- if (ecard_probe(8, nc, ECARD_IOC))
- nc += 1;
+ ecard_probe(8, ECARD_IOC);
#endif
- printk("\n");
- ecard_numcards = nc;
+ ecard_probeirqhw();
- if (nc && setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) {
- printk("Could not allocate interrupt for expansion cards\n");
- return;
+ if (setup_arm_irq(IRQ_EXPANSIONCARD, &irqexpansioncard)) {
+ printk(KERN_ERR "Unable to claim IRQ%d for expansion cards\n",
+ IRQ_EXPANSIONCARD);
+ ecard_free_all();
}
-
-#ifdef HAS_EXPMASK
- if (nc && have_expmask)
- EXPMASK_ENABLE = have_expmask;
-#endif
- oldlatch_init();
+ ecard_proc_init();
}
+
+EXPORT_SYMBOL(ecard_startfind);
+EXPORT_SYMBOL(ecard_find);
+EXPORT_SYMBOL(ecard_readchunk);
+EXPORT_SYMBOL(ecard_address);
.macro restore_user_regs
ldmia sp, {r0 - lr}^
mov r0, r0
- add sp, sp, #15*4
- ldr lr, [sp], #8
+ ldr lr, [sp, #15*4]
+ add sp, sp, #15*4+8
movs pc, lr
.endm
str r0, [sp, #S_OLD_R0] ;\
mov fp, #0
-#define USER_RESTORE_ALL \
- ldmia sp, {r0 - lr}^ ;\
- mov r0, r0 ;\
- add sp, sp, #15*4 ;\
- ldr lr, [sp], #8 ;\
- movs pc, lr
-
#define SVC_RESTORE_ALL \
ldmfd sp, {r0 - pc}^
mov r0, r0
movs pc, lr
-Lfiqmsg: .ascii "*** Unexpeced FIQ\n\0"
+Lfiqmsg: .ascii "*** Unexpected FIQ\n\0"
.align
.LCfiq: .word __temp_fiq
and r4, r10, #255 @ get offset
and r6, r10, #0x000f0000
tst r10, #0x00800000 @ +/-
- rsbeq r4, r4, #0
ldr r5, [sp, r6, lsr #14] @ Load reg
+ rsbeq r4, r4, #0
add r5, r5, r4, lsl #2
str r5, [sp, r6, lsr #14] @ Save reg
b ret_from_exception
-wfs_mask_data: .word 0x0e200110 @ WFS
- .word 0x0fff0fff
+wfs_mask_data: .word 0x0e200110 @ WFS/RFS
+ .word 0x0fef0fff
.word 0x0d0d0100 @ LDF [sp]/STF [sp]
.word 0x0d0b0100 @ LDF [fp]/STF [fp]
.word 0x0f0f0f00
save_user_regs
teqp pc, #0x00000003 @ NOT a problem - doesnt change mode
mask_pc r0, lr @ Address of abort
- mov r1, #FAULT_CODE_PREFETCH|FAULT_CODE_USER @ Error code
- mov r2, sp @ Tasks registers
+ mov r1, sp @ Tasks registers
bl SYMBOL_NAME(do_PrefetchAbort)
teq r0, #0 @ If non-zero, we believe this abort..
bne ret_from_sys_call
adr lr, 1b
orr lr, lr, #0x08000003 @ Force SVC
bne do_IRQ
+ mov r4, #0
b ret_with_reschedule
irq_prio_table
and r0, r0, #15 << 2 @ Mask out reg.
teq r0, #15 << 2
ldr r0, [r3, r0] @ Get register
- biceq r0, r0, #PCMASK
mov r1, r4, lsl #20
+ biceq r0, r0, #PCMASK
tst r4, #1 << 23
addne r0, r0, r1, lsr #20
subeq r0, r0, r1, lsr #20
and r0, r0, #15 << 2 @ Mask out reg.
teq r0, #15 << 2
ldr r0, [r3, r0] @ Get register
- biceq r0, r0, #PCMASK
and r7, r4, #15
+ biceq r0, r0, #PCMASK
teq r7, #15 @ Check for PC
ldr r7, [r3, r7, lsl #2] @ Get Rm
- biceq r7, r7, #PCMASK
and r8, r4, #0x60 @ Get shift types
+ biceq r7, r7, #PCMASK
mov r9, r4, lsr #7 @ Get shift amount
and r9, r9, #31
teq r8, #0
and r0, r0, #15 << 2 @ Mask out reg.
teq r0, #15 << 2
ldr r0, [r3, r0] @ Get register
- biceq r0, r0, #PCMASK
mov r1, r4, lsl #24 @ Get offset
+ biceq r0, r0, #PCMASK
tst r4, #1 << 23
addne r0, r0, r1, lsr #24
subeq r0, r0, r1, lsr #24
#endif
b SYMBOL_NAME(do_DataAbort)
-#include "entry-common.S"
+/*
+ *=============================================================================
+ * Low-level interface code
+ *-----------------------------------------------------------------------------
+ * Trap initialisation
+ *-----------------------------------------------------------------------------
+ *
+ * Note - FIQ code has changed. The default is a couple of words in 0x1c, 0x20
+ * that call _unexp_fiq. Nowever, we now copy the FIQ routine to 0x1c (removes
+ * some excess cycles).
+ *
+ * What we need to put into 0-0x1c are branches to branch to the kernel.
+ */
- .data
+ .section ".text.init",#alloc,#execinstr
+
+.Ljump_addresses:
+ swi SYS_ERROR0
+ .word vector_undefinstr - 12
+ .word vector_swi - 16
+ .word vector_prefetch - 20
+ .word vector_data - 24
+ .word vector_addrexcptn - 28
+ .word vector_IRQ - 32
+ .word _unexp_fiq - 36
+ b . + 8
+/*
+ * initialise the trap system
+ */
+ENTRY(trap_init)
+ stmfd sp!, {r4 - r7, lr}
+ adr r1, .Ljump_addresses
+ ldmia r1, {r1 - r7, ip, lr}
+ orr r2, lr, r2, lsr #2
+ orr r3, lr, r3, lsr #2
+ orr r4, lr, r4, lsr #2
+ orr r5, lr, r5, lsr #2
+ orr r6, lr, r6, lsr #2
+ orr r7, lr, r7, lsr #2
+ orr ip, lr, ip, lsr #2
+ mov r0, #0
+ stmia r0, {r1 - r7, ip}
+ ldmfd sp!, {r4 - r7, pc}^
+
+ .text
+
+#include "entry-common.S"
-__temp_irq: .word 0 @ saved lr_irq
+ .bss
+__temp_irq: .space 4 @ saved lr_irq
__temp_fiq: .space 128
#define S_R1 4
#define S_R0 0
+#define OFF_CR_ALIGNMENT(x) cr_alignment - x
+
#ifdef IOC_BASE
/* IOC / IOMD based hardware */
+#include <asm/iomd.h>
+
.equ ioc_base_high, IOC_BASE & 0xff000000
.equ ioc_base_low, IOC_BASE & 0x00ff0000
.macro disable_fiq
.byte 6, 6, 6, 6, 2, 2, 2, 2, 3, 3, 6, 6, 2, 2, 2, 2
.endm
-#elif defined(CONFIG_ARCH_EBSA285)
+#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE)
+#include <asm/dec21285.h>
.macro disable_fiq
.endm
+ .equ irq_mask_pci_err_high, IRQ_MASK_PCI_ERR & 0xff000000
+ .equ irq_mask_pci_err_low, IRQ_MASK_PCI_ERR & 0x00ffffff
+ .equ dc21285_high, ARMCSR_BASE & 0xff000000
+ .equ dc21285_low, ARMCSR_BASE & 0x00ffffff
+
.macro get_irqnr_and_base, irqnr, irqstat, base
- mov r4, #0xfe000000
+ mov r4, #dc21285_high
+ .if dc21285_low
+ orr r4, r4, #dc21285_low
+ .endif
ldr \irqstat, [r4, #0x180] @ get interrupts
- mov \irqnr, #0
-1001: tst \irqstat, #1
- addeq \irqnr, \irqnr, #1
- moveq \irqstat, \irqstat, lsr #1
- tsteq \irqnr, #32
- beq 1001b
- teq \irqnr, #32
- .endm
- .macro irq_prio_table
- .endm
-
-#elif defined(CONFIG_ARCH_NEXUSPCI)
+ mov \irqnr, #IRQ_SDRAMPARITY
+ tst \irqstat, #IRQ_MASK_SDRAMPARITY
+ bne 1001f
- .macro disable_fiq
- .endm
+ tst \irqstat, #IRQ_MASK_UART_RX
+ movne \irqnr, #IRQ_CONRX
+ bne 1001f
- .macro get_irqnr_and_base, irqnr, irqstat, base
- ldr r4, =0xffe00000
- ldr \irqstat, [r4, #0x180] @ get interrupts
- mov \irqnr, #0
-1001: tst \irqstat, #1
- addeq \irqnr, \irqnr, #1
- moveq \irqstat, \irqstat, lsr #1
- tsteq \irqnr, #32
- beq 1001b
- teq \irqnr, #32
- .endm
+ tst \irqstat, #IRQ_MASK_DMA1
+ movne \irqnr, #IRQ_DMA1
+ bne 1001f
- .macro irq_prio_table
- .endm
+ tst \irqstat, #IRQ_MASK_DMA2
+ movne \irqnr, #IRQ_DMA2
+ bne 1001f
-#elif defined(CONFIG_ARCH_VNC)
+ tst \irqstat, #IRQ_MASK_IN0
+ movne \irqnr, #IRQ_IN0
+ bne 1001f
- .macro disable_fiq
- .endm
+ tst \irqstat, #IRQ_MASK_IN1
+ movne \irqnr, #IRQ_IN1
+ bne 1001f
- .equ pci_iack_high, PCI_IACK & 0xff000000
- .equ pci_iack_low, PCI_IACK & 0x00ff0000
+ tst \irqstat, #IRQ_MASK_IN2
+ movne \irqnr, #IRQ_IN2
+ bne 1001f
- .macro get_irqnr_and_base, irqnr, irqstat, base
- mov r4, #IO_BASE_ARM_CSR
- ldr \irqstat, [r4, #CSR_IRQ_STATUS] @ just show us the unmasked ones
+ tst \irqstat, #IRQ_MASK_IN3
+ movne \irqnr, #IRQ_IN3
+ bne 1001f
- @ run through hard priorities
- @ timer
- tst \irqstat, #IRQ_MASK_TIMER0
- movne \irqnr, #IRQ_TIMER0
+ tst \irqstat, #IRQ_MASK_PCI
+ movne \irqnr, #IRQ_PCI
bne 1001f
- @ ether10
- tst \irqstat, #IRQ_MASK_ETHER10
- movne \irqnr, #IRQ_ETHER10
+ tst \irqstat, #IRQ_MASK_I2OINPOST
+ movne \irqnr, #IRQ_I2OINPOST
bne 1001f
- @ ether100
- tst \irqstat, #IRQ_MASK_ETHER100
- movne \irqnr, #IRQ_ETHER100
+ tst \irqstat, #IRQ_MASK_TIMER1
+ movne \irqnr, #IRQ_TIMER1
bne 1001f
- @ video compressor
- tst \irqstat, #IRQ_MASK_VIDCOMP
- movne \irqnr, #IRQ_VIDCOMP
+ tst \irqstat, #IRQ_MASK_TIMER2
+ movne \irqnr, #IRQ_TIMER2
bne 1001f
- @ now try all the PIC sources
- @ determine whether we have an irq
- tst \irqstat, #IRQ_MASK_EXTERN_IRQ
- beq 1002f
- mov r4, #pci_iack_high
- orr r4, r4, #pci_iack_low
- ldrb \irqnr, [r4] @ get the IACK byte
- b 1001f
-
-1002: @ PCI errors
- tst \irqstat, #IRQ_MASK_PCI_ERR
- movne \irqnr, #IRQ_PCI_ERR
+ tst \irqstat, #IRQ_MASK_TIMER3
+ movne \irqnr, #IRQ_TIMER3
bne 1001f
- @ softint
- tst \irqstat, #IRQ_MASK_SOFTIRQ
- movne \irqnr, #IRQ_SOFTIRQ
+ tst \irqstat, #IRQ_MASK_UART_TX
+ movne \irqnr, #IRQ_CONTX
bne 1001f
- @ debug uart
- tst \irqstat, #IRQ_MASK_UART_DEBUG
- movne \irqnr, #IRQ_CONRX
+ tst \irqstat, #irq_mask_pci_err_high
+ tsteq \irqstat, #irq_mask_pci_err_low
+ movne \irqnr, #IRQ_PCI_ERR
bne 1001f
+1001:
+ .endm
+
+ .macro irq_prio_table
+ .endm
- @ watchdog
- tst \irqstat, #IRQ_MASK_WATCHDOG
- movne \irqnr, #IRQ_WATCHDOG
+#elif defined(CONFIG_ARCH_NEXUSPCI)
-1001: @ If Z is set, then we will not enter an interrupt
+ .macro disable_fiq
.endm
- .macro irq_prio_table
+ .macro get_irqnr_and_base, irqnr, irqstat, base
+ ldr r4, =0xffe00000
+ ldr \irqstat, [r4, #0x180] @ get interrupts
+ mov \irqnr, #0
+1001: tst \irqstat, #1
+ addeq \irqnr, \irqnr, #1
+ moveq \irqstat, \irqstat, lsr #1
+ tsteq \irqnr, #32
+ beq 1001b
+ teq \irqnr, #32
.endm
+ .macro irq_prio_table
+ .endm
#else
#error Unknown architecture
#endif
stmia sp, {r0 - r12} @ Calling r0 - r12
add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling sp, lr
- mov r7, r0
+ str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
- mov r5, lr
- stmia r8, {r5, r6, r7} @ Save calling PC, CPSR, OLD_R0
+ str r6, [r8, #4] @ Save CPSR
+ str r0, [r8, #8] @ Save OLD_R0
.endm
.macro restore_user_regs
- mrs r0, cpsr @ disable IRQs
- orr r0, r0, #I_BIT
- msr cpsr, r0
+ mrs r1, cpsr @ disable IRQs
+ orr r1, r1, #I_BIT
ldr r0, [sp, #S_PSR] @ Get calling cpsr
+ msr cpsr, r1
msr spsr, r0 @ save in spsr_svc
ldmia sp, {r0 - lr}^ @ Get calling r0 - lr
mov r0, r0
- add sp, sp, #S_PC
- ldr lr, [sp], #S_FRAME_SIZE - S_PC @ Get PC and jump over PC, PSR, OLD_R0
+ ldr lr, [sp, #S_PC] @ Get PC
+ add sp, sp, #S_FRAME_SIZE
movs pc, lr @ return & move spsr_svc into cpsr
.endm
msr cpsr, \temp
.endm
- .macro initialise_traps_extra
- mrs r0, cpsr
- bic r0, r0, #31
- orr r0, r0, #0xd3
- msr cpsr, r0
- .endm
-
-
-#ifndef __ARM_ARCH_4__
-.Larm700bug: str lr, [r8]
- ldr r0, [sp, #S_PSR] @ Get calling cpsr
- msr spsr, r0
- ldmia sp, {r0 - lr}^ @ Get calling r0 - lr
- mov r0, r0
- add sp, sp, #S_PC
- ldr lr, [sp], #S_FRAME_SIZE - S_PC @ Get PC and jump over PC, PSR, OLD_R0
- movs pc, lr
-#endif
-
.macro get_current_task, rd
mov \rd, sp, lsr #13
mov \rd, \rd, lsl #13
adr\cond \reg, \label
.endm
-/*=============================================================================
- * Address exception handler
- *-----------------------------------------------------------------------------
- * These aren't too critical.
- * (they're not supposed to happen, and won't happen in 32-bit mode).
- */
-
-vector_addrexcptn:
- b vector_addrexcptn
-
-/*=============================================================================
- * Undefined FIQs
- *-----------------------------------------------------------------------------
- * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
- * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
- * Basically to switch modes, we *HAVE* to clobber one register... brain
- * damage alert! I don't think that we can execute any code in here in any
- * other mode than FIQ... Ok you can switch to another mode, but you can't
- * get out of that mode without clobbering one register.
- */
-_unexp_fiq: disable_fiq
- subs pc, lr, #4
-
-/*=============================================================================
- * Interrupt entry dispatcher
- *-----------------------------------------------------------------------------
- * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
- */
-vector_IRQ: @
- @ save mode specific registers
- @
- ldr r13, .LCirq
- sub lr, lr, #4
- str lr, [r13] @ save lr_IRQ
- mrs lr, spsr
- str lr, [r13, #4] @ save spsr_IRQ
- @
- @ now branch to the relevent MODE handling routine
- @
- mrs sp, cpsr @ switch to SVC mode
- bic sp, sp, #31
- orr sp, sp, #0x13
- msr spsr, sp
- and lr, lr, #15
- cmp lr, #4
- addlts pc, pc, lr, lsl #2 @ Changes mode and branches
- b __irq_invalid @ 4 - 15
- b __irq_usr @ 0 (USR_26 / USR_32)
- b __irq_invalid @ 1 (FIQ_26 / FIQ_32)
- b __irq_invalid @ 2 (IRQ_26 / IRQ_32)
- b __irq_svc @ 3 (SVC_26 / SVC_32)
-/*
- *------------------------------------------------------------------------------------------------
- * Undef instr entry dispatcher - dispatches it to the correct handler for the processor mode
- *------------------------------------------------------------------------------------------------
- * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
- */
-.LCirq: .word __temp_irq
-.LCund: .word __temp_und
-.LCabt: .word __temp_abt
-
-vector_undefinstr:
- @
- @ save mode specific registers
- @
- ldr r13, [pc, #.LCund - . - 8]
- str lr, [r13]
- mrs lr, spsr
- str lr, [r13, #4]
- @
- @ now branch to the relevent MODE handling routine
- @
- mrs sp, cpsr
- bic sp, sp, #31
- orr sp, sp, #0x13
- msr spsr, sp
- and lr, lr, #15
- cmp lr, #4
- addlts pc, pc, lr, lsl #2 @ Changes mode and branches
- b __und_invalid @ 4 - 15
- b __und_usr @ 0 (USR_26 / USR_32)
- b __und_invalid @ 1 (FIQ_26 / FIQ_32)
- b __und_invalid @ 2 (IRQ_26 / IRQ_32)
- b __und_svc @ 3 (SVC_26 / SVC_32)
-/*
- *------------------------------------------------------------------------------------------------
- * Prefetch abort dispatcher - dispatches it to the correct handler for the processor mode
- *------------------------------------------------------------------------------------------------
- * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
- */
-vector_prefetch:
- @
- @ save mode specific registers
- @
- sub lr, lr, #4
- ldr r13, .LCabt
- str lr, [r13]
- mrs lr, spsr
- str lr, [r13, #4]
- @
- @ now branch to the relevent MODE handling routine
- @
- mrs sp, cpsr
- bic sp, sp, #31
- orr sp, sp, #0x13
- msr spsr, sp
- and lr, lr, #15
- adds pc, pc, lr, lsl #2 @ Changes mode and branches
- b __pabt_invalid @ 4 - 15
- b __pabt_usr @ 0 (USR_26 / USR_32)
- b __pabt_invalid @ 1 (FIQ_26 / FIQ_32)
- b __pabt_invalid @ 2 (IRQ_26 / IRQ_32)
- b __pabt_invalid @ 3 (SVC_26 / SVC_32)
/*
- *------------------------------------------------------------------------------------------------
- * Data abort dispatcher - dispatches it to the correct handler for the processor mode
- *------------------------------------------------------------------------------------------------
- * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ * Invalid mode handlers
*/
-vector_data: @
- @ save mode specific registers
- @
- sub lr, lr, #8
- ldr r13, .LCabt
- str lr, [r13]
- mrs lr, spsr
- str lr, [r13, #4]
- @
- @ now branch to the relevent MODE handling routine
- @
- mrs sp, cpsr
- bic sp, sp, #31
- orr sp, sp, #0x13
- msr spsr, sp
- and lr, lr, #15
- cmp lr, #4
- addlts pc, pc, lr, lsl #2 @ Changes mode & branches
- b __dabt_invalid @ 4 - 15
- b __dabt_usr @ 0 (USR_26 / USR_32)
- b __dabt_invalid @ 1 (FIQ_26 / FIQ_32)
- b __dabt_invalid @ 2 (IRQ_26 / IRQ_32)
- b __dabt_svc @ 3 (SVC_26 / SVC_32)
+__pabt_invalid: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
+ stmia sp, {r0 - lr} @ Save XXX r0 - lr
+ ldr r4, .LCabt
+ mov r1, #BAD_PREFETCH
+ b 1f
-/*=============================================================================
- * Prefetch abort handler
- *-----------------------------------------------------------------------------
- */
-pabtmsg: .ascii "Pabt: %08lX\n\0"
- .align
-__pabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
- stmia sp, {r0 - r12} @ Save r0 - r12
- add r8, sp, #S_PC
- stmdb r8, {sp, lr}^ @ Save sp_usr lr_usr
+__dabt_invalid: sub sp, sp, #S_FRAME_SIZE
+ stmia sp, {r0 - lr} @ Save SVC r0 - lr [lr *should* be intact]
ldr r4, .LCabt
- ldmia r4, {r5 - r7} @ Get USR pc, cpsr
- stmia r8, {r5 - r7} @ Save USR pc, cpsr, old_r0
+ mov r1, #BAD_DATA
+ b 1f
- mrs r7, cpsr @ Enable interrupts if they were
- bic r7, r7, #I_BIT @ previously
- msr cpsr, r7
- mov r0, r5 @ address (pc)
- mov r1, sp @ regs
- bl SYMBOL_NAME(do_PrefetchAbort) @ call abort handler
- teq r0, #0 @ Does this still apply???
- bne ret_from_exception @ Return from exception
-#ifdef DEBUG_UNDEF
- adr r0, t
- bl SYMBOL_NAME(printk)
-#endif
- mov r0, r5
- mov r1, sp
- and r2, r6, #31
- bl SYMBOL_NAME(do_undefinstr)
- ldr lr, [sp, #S_PSR] @ Get USR cpsr
- msr spsr, lr
- ldmia sp, {r0 - pc}^ @ Restore USR registers
+__irq_invalid: sub sp, sp, #S_FRAME_SIZE @ Allocate space on stack for frame
+ stmfd sp, {r0 - lr} @ Save r0 - lr
+ ldr r4, .LCirq
+ mov r1, #BAD_IRQ
+ b 1f
-__pabt_invalid: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
- stmia sp, {r0 - lr} @ Save XXX r0 - lr
- mov r7, r0 @ OLD R0
- ldr r4, .LCabt
- ldmia r4, {r5 - r7} @ Get XXX pc, cpsr
+__und_invalid: sub sp, sp, #S_FRAME_SIZE
+ stmia sp, {r0 - lr}
+ ldr r4, .LCund
+ mov r1, #BAD_UNDEFINSTR @ int reason
+
+1: mov fp, #0
+ ldmia r4, {r5 - r7} @ Get XXX pc, cpsr, old_r0
add r4, sp, #S_PC
stmia r4, {r5 - r7} @ Save XXX pc, cpsr, old_r0
- mov r0, sp @ Prefetch aborts are definitely *not*
- mov r1, #BAD_PREFETCH @ allowed in non-user modes. We cant
- and r2, r6, #31 @ recover from this problem.
+ mov r0, sp
+ and r2, r6, #31 @ int mode
b SYMBOL_NAME(bad_mode)
-#ifdef DEBUG_UNDEF
-t: .ascii "*** undef ***\r\n\0"
- .align
-#endif
-/*=============================================================================
- * Data abort handler code
- *-----------------------------------------------------------------------------
+wfs_mask_data: .word 0x0e200110 @ WFS/RFS
+ .word 0x0fef0fff
+ .word 0x0d0d0100 @ LDF [sp]/STF [sp]
+ .word 0x0d0b0100 @ LDF [fp]/STF [fp]
+ .word 0x0f0f0f00
+
+/* We get here if an undefined instruction happens and the floating
+ * point emulator is not present. If the offending instruction was
+ * a WFS, we just perform a normal return as if we had emulated the
+ * operation. This is a hack to allow some basic userland binaries
+ * to run so that the emulator module proper can be loaded. --philb
*/
-.LCprocfns: .word SYMBOL_NAME(processor)
+fpe_not_present:
+ adr r10, wfs_mask_data
+ ldmia r10, {r4, r5, r6, r7, r8}
+ ldr r10, [sp, #S_PC] @ Load PC
+ sub r10, r10, #-4
+ mask_pc r10, r10
+ ldrt r10, [r10] @ get instruction
+ and r5, r10, r5
+ teq r5, r4 @ Is it WFS?
+ moveq pc, r9
+ and r5, r10, r8
+ teq r5, r6 @ Is it LDF/STF on sp or fp?
+ teqne r5, r7
+ movne pc, lr
+ tst r10, #0x00200000 @ Does it have WB
+ moveq pc, r9
+ and r4, r10, #255 @ get offset
+ and r6, r10, #0x000f0000
+ tst r10, #0x00800000 @ +/-
+ ldr r5, [sp, r6, lsr #14] @ Load reg
+ rsbeq r4, r4, #0
+ add r5, r5, r4, lsl #2
+ str r5, [sp, r6, lsr #14] @ Save reg
+ mov pc, r9
-__dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
- stmia sp, {r0 - r12} @ save r0 - r12
- add r3, sp, #S_PC
- stmdb r3, {sp, lr}^
- ldr r0, .LCabt
- ldmia r0, {r0 - r2} @ Get USR pc, cpsr
- stmia r3, {r0 - r2} @ Save USR pc, cpsr, old_r0
- mov fp, #0
- mrs r2, cpsr @ Enable interrupts if they were
- bic r2, r2, #I_BIT @ previously
- msr cpsr, r2
- ldr r2, .LCprocfns
- mov lr, pc
- ldr pc, [r2, #8] @ call processor specific code
- mov r3, sp
- bl SYMBOL_NAME(do_DataAbort)
- b ret_from_sys_call
-
-__dabt_svc: sub sp, sp, #S_FRAME_SIZE
+/*
+ * SVC mode handlers
+ */
+ .align 5
+__dabt_svc: sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ save r0 - r12
ldr r2, .LCabt
add r0, sp, #S_FRAME_SIZE
+ ldmia r2, {r2 - r4} @ get pc, cpsr
add r5, sp, #S_SP
mov r1, lr
- ldmia r2, {r2 - r4} @ get pc, cpsr
stmia r5, {r0 - r4} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
tst r3, #I_BIT
mrseq r0, cpsr @ Enable interrupts if they were
msr spsr, r0
ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
-__dabt_invalid: sub sp, sp, #S_FRAME_SIZE
- stmia sp, {r0 - lr} @ Save SVC r0 - lr [lr *should* be intact]
- mov r7, r0
- ldr r4, .LCabt
- ldmia r4, {r5, r6} @ Get SVC pc, cpsr
- add r4, sp, #S_PC
- stmia r4, {r5, r6, r7} @ Save SVC pc, cpsr, old_r0
- mov r0, sp
- mov r1, #BAD_DATA
- and r2, r6, #31
- b SYMBOL_NAME(bad_mode)
-
-/*=============================================================================
- * Interrupt (IRQ) handler
- *-----------------------------------------------------------------------------
- */
-__irq_usr: sub sp, sp, #S_FRAME_SIZE
+ .align 5
+__irq_svc: sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ save r0 - r12
- add r8, sp, #S_PC
- stmdb r8, {sp, lr}^
- ldr r4, .LCirq
- ldmia r4, {r5 - r7} @ get saved PC, SPSR
- stmia r8, {r5 - r7} @ save pc, psr, old_r0
+ ldr r7, .LCirq
+ add r5, sp, #S_FRAME_SIZE
+ ldmia r7, {r7 - r9}
+ add r4, sp, #S_SP
+ mov r6, lr
+ stmia r4, {r5, r6, r7, r8, r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
1: get_irqnr_and_base r0, r6, r5
movne r1, sp
@
@
adrsvc ne, lr, 1b
bne do_IRQ
- b ret_with_reschedule
-
- irq_prio_table
+ ldr r0, [sp, #S_PSR]
+ msr spsr, r0
+ ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
-__irq_svc: sub sp, sp, #S_FRAME_SIZE
+ .align 5
+__und_svc: sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ save r0 - r12
+ ldr r7, .LCund
mov r6, lr
- ldr r7, .LCirq
ldmia r7, {r7 - r9}
add r5, sp, #S_FRAME_SIZE
add r4, sp, #S_SP
- stmia r4, {r5, r6, r7, r8, r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
+ stmia r4, {r5 - r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
+
+ adrsvc al, r9, 1f @ r9 = normal FP return
+ bl call_fpe @ lr = undefined instr return
+
+ mov r0, r5 @ unsigned long pc
+ mov r1, sp @ struct pt_regs *regs
+ bl SYMBOL_NAME(do_undefinstr)
+
+1: ldr lr, [sp, #S_PSR] @ Get SVC cpsr
+ msr spsr, lr
+ ldmia sp, {r0 - pc}^ @ Restore SVC registers
+
+ .align 5
+.LCirq: .word __temp_irq
+.LCund: .word __temp_und
+.LCabt: .word __temp_abt
+.LCprocfns: .word SYMBOL_NAME(processor)
+.LCfp: .word SYMBOL_NAME(fp_enter)
+#ifdef CONFIG_ALIGNMENT_TRAP
+.LCswi: .word SYMBOL_NAME(cr_alignment)
+#endif
+
+ irq_prio_table
+
+/*
+ * User mode handlers
+ */
+#ifdef DEBUG_UNDEF
+t: .ascii "Prefetch -> undefined instruction\n\0"
+ .align
+#endif
+ .align 5
+__dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
+ stmia sp, {r0 - r12} @ save r0 - r12
+ ldr r4, .LCabt
+ add r3, sp, #S_PC
+ ldmia r4, {r0 - r2} @ Get USR pc, cpsr
+ stmia r3, {r0 - r2} @ Save USR pc, cpsr, old_r0
+ stmdb r3, {sp, lr}^
+
+#ifdef CONFIG_ALIGNMENT_TRAP
+ ldr r7, [r4, #OFF_CR_ALIGNMENT(__temp_abt)]
+ mcr p15, 0, r7, c1, c0
+#endif
+
+ mov fp, #0
+ mrs r2, cpsr @ Enable interrupts if they were
+ bic r2, r2, #I_BIT @ previously
+ msr cpsr, r2
+ ldr r2, .LCprocfns
+ mov lr, pc
+ ldr pc, [r2, #8] @ call processor specific code
+ mov r3, sp
+ adrsvc al, lr, ret_from_sys_call
+ b SYMBOL_NAME(do_DataAbort)
+
+ .align 5
+__irq_usr: sub sp, sp, #S_FRAME_SIZE
+ stmia sp, {r0 - r12} @ save r0 - r12
+ ldr r4, .LCirq
+ add r8, sp, #S_PC
+ ldmia r4, {r5 - r7} @ get saved PC, SPSR
+ stmia r8, {r5 - r7} @ save pc, psr, old_r0
+ stmdb r8, {sp, lr}^
+
+#ifdef CONFIG_ALIGNMENT_TRAP
+ ldr r7, [r4, #OFF_CR_ALIGNMENT(__temp_irq)]
+ mcr p15, 0, r7, c1, c0
+#endif
+
+ mov fp, #0
1: get_irqnr_and_base r0, r6, r5
movne r1, sp
+ adrsvc ne, lr, 1b
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
- adrsvc ne, lr, 1b
bne do_IRQ
- ldr r0, [sp, #S_PSR]
- msr spsr, r0
- ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
-
-__irq_invalid: sub sp, sp, #S_FRAME_SIZE @ Allocate space on stack for frame
- stmfd sp, {r0 - lr} @ Save r0 - lr
- mov r7, #-1
- ldr r4, .LCirq
- ldmia r4, {r5, r6} @ get saved pc, psr
- add r4, sp, #S_PC
- stmia r4, {r5, r6, r7}
- mov fp, #0
- mov r0, sp
- mov r1, #BAD_IRQ
- b SYMBOL_NAME(bad_mode)
-
-/*=============================================================================
- * Undefined instruction handler
- *-----------------------------------------------------------------------------
- * Handles floating point instructions
- */
-.LC2: .word SYMBOL_NAME(fp_enter)
+ mov r4, #0
+ b ret_with_reschedule
+ .align 5
__und_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
stmia sp, {r0 - r12} @ Save r0 - r12
- add r8, sp, #S_PC
- stmdb r8, {sp, lr}^ @ Save user r0 - r12
ldr r4, .LCund
+ add r8, sp, #S_PC
ldmia r4, {r5 - r7}
stmia r8, {r5 - r7} @ Save USR pc, cpsr, old_r0
- mov fp, #0
+ stmdb r8, {sp, lr}^ @ Save user r0 - r12
+
+#ifdef CONFIG_ALIGNMENT_TRAP
+ ldr r7, [r4, #OFF_CR_ALIGNMENT(__temp_und)]
+ mcr p15, 0, r7, c1, c0
+#endif
- adrsvc al, r9, ret_from_exception @ r9 = normal FP return
+ mov fp, #0
+ adrsvc al, r9, ret_from_sys_call @ r9 = normal FP return
adrsvc al, lr, fpundefinstr @ lr = undefined instr return
-1: get_current_task r10
+call_fpe: get_current_task r10
mov r8, #1
strb r8, [r10, #TSK_USED_MATH] @ set current->used_math
+ ldr r4, .LCfp
add r10, r10, #TSS_FPESAVE @ r10 = workspace
- ldr r4, .LC2
ldr pc, [r4] @ Call FP module USR entry point
-__und_svc: sub sp, sp, #S_FRAME_SIZE
- stmia sp, {r0 - r12} @ save r0 - r12
- mov r6, lr
- ldr r7, .LCund
- ldmia r7, {r7 - r9}
- add r5, sp, #S_FRAME_SIZE
- add r4, sp, #S_SP
- stmia r4, {r5 - r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
-
- adrsvc al, r9, 3f @ r9 = normal FP return
- bl 1b @ lr = undefined instr return
-
- mov r0, r5 @ unsigned long pc
- mov r1, sp @ struct pt_regs *regs
- bl SYMBOL_NAME(do_undefinstr)
-
-3: ldr lr, [sp, #S_PSR] @ Get SVC cpsr
- msr spsr, lr
- ldmia sp, {r0 - pc}^ @ Restore SVC registers
-
fpundefinstr: mov r0, lr
mov r1, sp
mrs r4, cpsr @ Enable interrupts
bic r4, r4, #I_BIT
msr cpsr, r4
- adrsvc al, lr, ret_from_exception
+ adrsvc al, lr, ret_from_sys_call
b SYMBOL_NAME(do_undefinstr)
-__und_invalid: sub sp, sp, #S_FRAME_SIZE
- stmia sp, {r0 - lr}
- mov r7, r0
- ldr r4, .LCund
- ldmia r4, {r5, r6} @ Get UND/IRQ/FIQ/ABT pc, cpsr
- add r4, sp, #S_PC
- stmia r4, {r5, r6, r7} @ Save UND/IRQ/FIQ/ABT pc, cpsr, old_r0
- mov r0, sp @ struct pt_regs *regs
- mov r1, #BAD_UNDEFINSTR @ int reason
- and r2, r6, #31 @ int mode
- b SYMBOL_NAME(bad_mode) @ Does not ever return...
+ .align 5
+__pabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
+ stmia sp, {r0 - r12} @ Save r0 - r12
+ ldr r4, .LCabt
+ add r8, sp, #S_PC
+ ldmia r4, {r5 - r7} @ Get USR pc, cpsr
+ stmia r8, {r5 - r7} @ Save USR pc, cpsr, old_r0
+ stmdb r8, {sp, lr}^ @ Save sp_usr lr_usr
-/* We get here if an undefined instruction happens and the floating
- * point emulator is not present. If the offending instruction was
- * a WFS, we just perform a normal return as if we had emulated the
- * operation. This is a hack to allow some basic userland binaries
- * to run so that the emulator module proper can be loaded. --philb
- */
-fpe_not_present:
- adr r10, wfs_mask_data
- ldmia r10, {r4, r5, r6, r7, r8}
- ldr r10, [sp, #S_PC] @ Load PC
- sub r10, r10, #4
- mask_pc r10, r10
- ldrt r10, [r10] @ get instruction
- and r5, r10, r5
- teq r5, r4 @ Is it WFS?
- moveq pc, r9
- and r5, r10, r8
- teq r5, r6 @ Is it LDF/STF on sp or fp?
- teqne r5, r7
- movne pc, lr
- tst r10, #0x00200000 @ Does it have WB
- moveq pc, r9
- and r4, r10, #255 @ get offset
- and r6, r10, #0x000f0000
- tst r10, #0x00800000 @ +/-
- rsbeq r4, r4, #0
- ldr r5, [sp, r6, lsr #14] @ Load reg
- add r5, r5, r4, lsl #2
- str r5, [sp, r6, lsr #14] @ Save reg
- mov pc, r9
+#ifdef CONFIG_ALIGNMENT_TRAP
+ ldr r7, [r4, #OFF_CR_ALIGNMENT(__temp_abt)]
+ mcr p15, 0, r7, c1, c0
+#endif
-wfs_mask_data: .word 0x0e200110 @ WFS
- .word 0x0fff0fff
- .word 0x0d0d0100 @ LDF [sp]/STF [sp]
- .word 0x0d0b0100 @ LDF [fp]/STF [fp]
- .word 0x0f0f0f00
+ mov fp, #0
+ mrs r7, cpsr @ Enable interrupts if they were
+ bic r7, r7, #I_BIT @ previously
+ msr cpsr, r7
+ mov r0, r5 @ address (pc)
+ mov r1, sp @ regs
+ bl SYMBOL_NAME(do_PrefetchAbort) @ call abort handler
+ teq r0, #0 @ Does this still apply???
+ bne ret_from_sys_call @ Return from exception
+#ifdef DEBUG_UNDEF
+ adr r0, t
+ bl SYMBOL_NAME(printk)
+#endif
+ mov r0, r5
+ mov r1, sp
+ and r2, r6, #31
+ bl SYMBOL_NAME(do_undefinstr)
+ ldr lr, [sp, #S_PSR] @ Get USR cpsr
+ msr spsr, lr
+ ldmia sp, {r0 - pc}^ @ Restore USR registers
#include "entry-common.S"
+ .text
+
+#ifndef __ARM_ARCH_4__
+.Larm700bug: ldr r0, [sp, #S_PSR] @ Get calling cpsr
+ str lr, [r8]
+ msr spsr, r0
+ ldmia sp, {r0 - lr}^ @ Get calling r0 - lr
+ mov r0, r0
+ ldr lr, [sp, #S_PC] @ Get PC
+ add sp, sp, #S_FRAME_SIZE
+ movs pc, lr
+#endif
+
+ .section ".text.init",#alloc,#execinstr
+/*
+ * Vector stubs. NOTE that we only align 'vector_IRQ' to a cache line boundary,
+ * and we rely on each stub being exactly 48 (1.5 cache lines) in size. This
+ * means that we only ever load two cache lines for this code, or one if we're
+ * lucky. We also copy this code to 0x200 so that we can use branches in the
+ * vectors, rather than ldr's.
+ */
+ .align 5
+__stubs_start:
+/*
+ * Interrupt dispatcher
+ * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
+ */
+vector_IRQ: @
+ @ save mode specific registers
+ @
+ ldr r13, .LCsirq
+ sub lr, lr, #4
+ str lr, [r13] @ save lr_IRQ
+ mrs lr, spsr
+ str lr, [r13, #4] @ save spsr_IRQ
+ @
+ @ now branch to the relevent MODE handling routine
+ @
+ bic r13, lr, #63
+ orr r13, r13, #0x93
+ msr spsr, r13 @ switch to SVC_32 mode
+
+ and lr, lr, #15
+ adr r13, .LCtab_irq
+ ldr lr, [r13, lr, lsl #2]
+ movs pc, lr @ Changes mode and branches
+/*
+ * Data abort dispatcher - dispatches it to the correct handler for the processor mode
+ * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ */
+vector_data: @
+ @ save mode specific registers
+ @
+ ldr r13, .LCsabt
+ sub lr, lr, #8
+ str lr, [r13]
+ mrs lr, spsr
+ str lr, [r13, #4]
+ @
+ @ now branch to the relevent MODE handling routine
+ @
+ bic r13, lr, #63
+ orr r13, r13, #0x93
+ msr spsr, r13 @ switch to SVC_32 mode
+
+ and lr, lr, #15
+ adr r13, .LCtab_dabt
+ ldr lr, [r13, lr, lsl #2]
+ movs pc, lr @ Changes mode and branches
+
+/*
+ * Prefetch abort dispatcher - dispatches it to the correct handler for the processor mode
+ * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
+ */
+vector_prefetch:
+ @
+ @ save mode specific registers
+ @
+ ldr r13, .LCsabt
+ sub lr, lr, #4
+ str lr, [r13] @ save lr_ABT
+ mrs lr, spsr
+ str lr, [r13, #4] @ save spsr_ABT
+ @
+ @ now branch to the relevent MODE handling routine
+ @
+ bic r13, lr, #63
+ orr r13, r13, #0x93
+ msr spsr, r13 @ switch to SVC_32 mode
+
+ ands lr, lr, #15
+ ldreq lr, .LCtab_pabt
+ ldrne lr, .LCtab_pabt + 4
+ movs pc, lr
+
+/*
+ * Undef instr entry dispatcher - dispatches it to the correct handler for the processor mode
+ * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
+ */
+vector_undefinstr:
+ @
+ @ save mode specific registers
+ @
+ ldr r13, .LCsund
+ str lr, [r13] @ save lr_UND
+ mrs lr, spsr
+ str lr, [r13, #4] @ save spsr_UND
+ @
+ @ now branch to the relevent MODE handling routine
+ @
+ bic r13, lr, #63
+ orr r13, r13, #0x93
+ msr spsr, r13 @ switch to SVC_32 mode
+
+ and lr, lr, #15
+ adr r13, .LCtab_und
+ ldr lr, [r13, lr, lsl #2]
+ movs pc, lr @ Changes mode and branches
+
+/*=============================================================================
+ * Undefined FIQs
+ *-----------------------------------------------------------------------------
+ * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
+ * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
+ * Basically to switch modes, we *HAVE* to clobber one register... brain
+ * damage alert! I don't think that we can execute any code in here in any
+ * other mode than FIQ... Ok you can switch to another mode, but you can't
+ * get out of that mode without clobbering one register.
+ */
+vector_FIQ: disable_fiq
+ subs pc, lr, #4
+
+/*=============================================================================
+ * Address exception handler
+ *-----------------------------------------------------------------------------
+ * These aren't too critical.
+ * (they're not supposed to happen, and won't happen in 32-bit data mode).
+ */
+
+vector_addrexcptn:
+ b vector_addrexcptn
+
+/*
+ * We group all the following data together to optimise
+ * for CPUs with separate I & D caches.
+ */
+ .align 5
+
+.LCtab_irq: .word __irq_usr @ 0 (USR_26 / USR_32)
+ .word __irq_invalid @ 1 (FIQ_26 / FIQ_32)
+ .word __irq_invalid @ 2 (IRQ_26 / IRQ_32)
+ .word __irq_svc @ 3 (SVC_26 / SVC_32)
+ .word __irq_invalid @ 4
+ .word __irq_invalid @ 5
+ .word __irq_invalid @ 6
+ .word __irq_invalid @ 7
+ .word __irq_invalid @ 8
+ .word __irq_invalid @ 9
+ .word __irq_invalid @ a
+ .word __irq_invalid @ b
+ .word __irq_invalid @ c
+ .word __irq_invalid @ d
+ .word __irq_invalid @ e
+ .word __irq_invalid @ f
+
+.LCtab_und: .word __und_usr @ 0 (USR_26 / USR_32)
+ .word __und_invalid @ 1 (FIQ_26 / FIQ_32)
+ .word __und_invalid @ 2 (IRQ_26 / IRQ_32)
+ .word __und_svc @ 3 (SVC_26 / SVC_32)
+ .word __und_invalid @ 4
+ .word __und_invalid @ 5
+ .word __und_invalid @ 6
+ .word __und_invalid @ 7
+ .word __und_invalid @ 8
+ .word __und_invalid @ 9
+ .word __und_invalid @ a
+ .word __und_invalid @ b
+ .word __und_invalid @ c
+ .word __und_invalid @ d
+ .word __und_invalid @ e
+ .word __und_invalid @ f
+
+.LCtab_dabt: .word __dabt_usr @ 0 (USR_26 / USR_32)
+ .word __dabt_invalid @ 1 (FIQ_26 / FIQ_32)
+ .word __dabt_invalid @ 2 (IRQ_26 / IRQ_32)
+ .word __dabt_svc @ 3 (SVC_26 / SVC_32)
+ .word __dabt_invalid @ 4
+ .word __dabt_invalid @ 5
+ .word __dabt_invalid @ 6
+ .word __dabt_invalid @ 7
+ .word __dabt_invalid @ 8
+ .word __dabt_invalid @ 9
+ .word __dabt_invalid @ a
+ .word __dabt_invalid @ b
+ .word __dabt_invalid @ c
+ .word __dabt_invalid @ d
+ .word __dabt_invalid @ e
+ .word __dabt_invalid @ f
+
+.LCtab_pabt: .word __pabt_usr
+ .word __pabt_invalid
+
+.LCvswi: .word vector_swi
+
+.LCsirq: .word __temp_irq
+.LCsund: .word __temp_und
+.LCsabt: .word __temp_abt
+
+__stubs_end:
+
+ .equ __real_stubs_start, .LCvectors + 0x200
+
+.LCvectors: swi SYS_ERROR0
+ b __real_stubs_start + (vector_undefinstr - __stubs_start)
+ ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
+ b __real_stubs_start + (vector_prefetch - __stubs_start)
+ b __real_stubs_start + (vector_data - __stubs_start)
+ b __real_stubs_start + (vector_addrexcptn - __stubs_start)
+ b __real_stubs_start + (vector_IRQ - __stubs_start)
+ b __real_stubs_start + (vector_FIQ - __stubs_start)
+
+ENTRY(trap_init)
+ stmfd sp!, {r4 - r6, lr}
+
+ adr r1, .LCvectors @ set up the vectors
+ mov r0, #0
+ ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr}
+ stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr}
+
+ add r2, r0, #0x200
+ adr r0, __stubs_start @ copy stubs to 0x200
+ adr r1, __stubs_end
+1: ldr r3, [r0], #4
+ str r3, [r2], #4
+ cmp r0, r1
+ blt 1b
+ LOADREGS(fd, sp!, {r4 - r6, pc})
+
.data
+/*
+ * Do not reorder these, and do not insert extra data between...
+ */
+
__temp_irq: .word 0 @ saved lr_irq
.word 0 @ saved spsr_irq
.word -1 @ old_r0
__temp_abt: .word 0 @ Saved lr_abt
.word 0 @ Saved spsr_abt
.word -1 @ old_r0
+
+ .globl SYMBOL_NAME(cr_alignment)
+ .globl SYMBOL_NAME(cr_no_alignment)
+SYMBOL_NAME(cr_alignment):
+ .space 4
+SYMBOL_NAME(cr_no_alignment):
+ .space 4
/*============================================================================
* All exits to user mode from the kernel go through this code.
*/
-
-#include <linux/config.h>
-
.globl ret_from_sys_call
-ret_from_exception:
- adr r0, 1f
- ldmia r0, {r0, r1}
+ .align 5
+fast_syscall_return:
+ str r0, [sp, #S_R0 + 4] @ returned r0
+slow_syscall_return:
+ add sp, sp, #4
+ret_from_sys_call:
+ adr r0, bh_data
+ ldmia r0, {r0, r4}
ldr r0, [r0]
- ldr r1, [r1]
+ ldr r1, [r4]
tst r0, r1
blne SYMBOL_NAME(do_bottom_half)
-ret_from_intr: ldr r0, [sp, #S_PSR]
- tst r0, #3
- beq ret_with_reschedule
- b ret_from_all
+ret_with_reschedule:
+ get_current_task r1 @ check for scheduling
+ ldr r0, [r1, #TSK_NEED_RESCHED]
+ teq r0, #0
+ bne ret_reschedule
+ ldr r1, [r1, #TSK_SIGPENDING]
+ teq r1, #0 @ check for signals
+ bne ret_signal
+
+ret_from_all: restore_user_regs
ret_signal: mov r1, sp
adrsvc al, lr, ret_from_all
+ mov r2, r4
b SYMBOL_NAME(do_signal)
-2: bl SYMBOL_NAME(schedule)
+ret_reschedule: adrsvc al, lr, ret_with_reschedule
+ b SYMBOL_NAME(schedule)
-ret_from_sys_call:
- adr r0, 1f
+ .globl ret_from_exception
+ret_from_exception:
+ adr r0, bh_data
ldmia r0, {r0, r1}
ldr r0, [r0]
ldr r1, [r1]
+ mov r4, #0
tst r0, r1
- adrsvc ne, lr, ret_from_intr
- bne SYMBOL_NAME(do_bottom_half)
-
-ret_with_reschedule:
- get_current_task r1
- ldr r0, [r1, #TSK_NEED_RESCHED]
- teq r0, #0
- bne 2b
- ldr r1, [r1, #TSK_SIGPENDING]
- teq r1, #0
- bne ret_signal
-
-ret_from_all: restore_user_regs
+ blne SYMBOL_NAME(do_bottom_half)
+ ldr r0, [sp, #S_PSR]
+ tst r0, #3 @ returning to user mode?
+ beq ret_with_reschedule
+ b ret_from_all
-1: .word SYMBOL_NAME(bh_mask)
- .word SYMBOL_NAME(bh_active)
+#include "calls.S"
/*=============================================================================
* SWI handler
* too worried.
*/
-#include "calls.S"
-
+ .align 5
vector_swi: save_user_regs
- mov fp, #0
mask_pc lr, lr
- ldr r6, [lr, #-4]! @ get SWI instruction
+ mov fp, #0
+ ldr r6, [lr, #-4] @ get SWI instruction
arm700_bug_check r6, r7
+#ifdef CONFIG_ALIGNMENT_TRAP
+ ldr r7, .LCswi
+ ldr r7, [r7]
+ mcr p15, 0, r7, c1, c0
+#endif
enable_irqs r7
-
+
+ str r4, [sp, #-4]! @ new style: (r0 = arg1, r4 = arg5)
+ adrsvc al, lr, fast_syscall_return
+
bic r6, r6, #0xff000000 @ mask off SWI op-code
eor r6, r6, #OS_NUMBER<<20 @ check OS number
cmp r6, #NR_syscalls @ check upper syscall limit
bcs 2f
- get_current_task r5
- ldr ip, [r5, #TSK_FLAGS] @ check for syscall tracing
+ get_current_task r7
+ ldr ip, [r7, #TSK_FLAGS] @ check for syscall tracing
+ adr r5, SYMBOL_NAME(sys_call_table)
tst ip, #PF_TRACESYS
- bne 1f
+ ldreq pc, [r5, r6, lsl #2] @ call sys routine
- adr ip, SYMBOL_NAME(sys_call_table)
- str r4, [sp, #-4]! @ new style: (r0 = arg1, r5 = arg5)
- mov lr, pc
- ldr pc, [ip, r6, lsl #2] @ call sys routine
- add sp, sp, #4
- str r0, [sp, #S_R0] @ returned r0
- b ret_from_sys_call
-
-1: ldr r7, [sp, #S_IP] @ save old IP
+ ldr r7, [sp, #S_IP + 4] @ save old IP
mov r0, #0
- str r0, [sp, #S_IP] @ trace entry [IP = 0]
+ str r0, [sp, #S_IP + 4] @ trace entry [IP = 0]
bl SYMBOL_NAME(syscall_trace)
- str r7, [sp, #S_IP]
- ldmia sp, {r0 - r3} @ have to reload r0 - r3
- adr ip, SYMBOL_NAME(sys_call_table)
- str r4, [sp, #-4]! @ new style: (r0 = arg1, r5 = arg5)
+ str r7, [sp, #S_IP + 4]
+
+ ldmib sp, {r0 - r3} @ have to reload r0 - r3
mov lr, pc
- ldr pc, [ip, r6, lsl #2] @ call sys routine
- add sp, sp, #4
- str r0, [sp, #S_R0] @ returned r0
+ ldr pc, [r5, r6, lsl #2] @ call sys routine
+ str r0, [sp, #S_R0 + 4] @ returned r0
+
mov r0, #1
- str r0, [sp, #S_IP] @ trace exit [IP = 1]
+ str r0, [sp, #S_IP + 4] @ trace exit [IP = 1]
bl SYMBOL_NAME(syscall_trace)
- str r7, [sp, #S_IP]
- b ret_from_sys_call
+ str r7, [sp, #S_IP + 4]
+ b slow_syscall_return
-2: tst r6, #0x00f00000 @ is it a Unix SWI?
+2: add r1, sp, #4
+ tst r6, #0x00f00000 @ is it a Unix SWI?
bne 3f
- cmp r6, #(KSWI_SYS_BASE - KSWI_BASE)
- bcc 4f @ not private func
- bic r0, r6, #0x000f0000
- mov r1, sp
- bl SYMBOL_NAME(arm_syscall)
- b ret_from_sys_call
-
-3: eor r0, r6, #OS_NUMBER<<20 @ Put OS number back
- mov r1, sp
- bl SYMBOL_NAME(deferred)
- ldmfd sp, {r0 - r3}
- b ret_from_sys_call
-
-4: bl SYMBOL_NAME(sys_ni_syscall)
- str r0, [sp, #0] @ returned r0
- b ret_from_sys_call
+ subs r0, r6, #(KSWI_SYS_BASE - KSWI_BASE)
+ bcs SYMBOL_NAME(arm_syscall)
+ b SYMBOL_NAME(sys_ni_syscall) @ not private func
-@ r0 = syscall number
-@ r1 = syscall r0
-@ r5 = syscall r4
-@ ip = syscall table
-SYMBOL_NAME(sys_syscall):
- mov r6, r0
- eor r6, r6, #OS_NUMBER << 20
- cmp r6, #NR_syscalls @ check range
- movgt r0, #-ENOSYS
- movgt pc, lr
- add sp, sp, #4 @ take of the save of our r4
- ldmib sp, {r0 - r4} @ get our args
- str r4, [sp, #-4]! @ Put our arg on the stack
- ldr pc, [ip, r6, lsl #2]
+3: eor r0, r6, #OS_NUMBER <<20 @ Put OS number back
+ adrsvc al, lr, slow_syscall_return
+ b SYMBOL_NAME(deferred)
+
+ .align 5
+
+bh_data: .word SYMBOL_NAME(bh_mask)
+ .word SYMBOL_NAME(bh_active)
ENTRY(sys_call_table)
#include "calls.S"
/*============================================================================
* Special system call wrappers
*/
+@ r0 = syscall number
+@ r5 = syscall table
+SYMBOL_NAME(sys_syscall):
+ eor r6, r0, #OS_NUMBER << 20
+ cmp r6, #NR_syscalls @ check range
+ ldmleib sp, {r0 - r4} @ get our args
+ strle r4, [sp] @ Put our arg on the stack
+ ldrle pc, [r5, r6, lsl #2]
+ mov r0, #-ENOSYS
+ mov pc, lr
+
sys_fork_wrapper:
add r0, sp, #4
b SYMBOL_NAME(sys_fork)
+sys_vfork_wrapper:
+ add r0, sp, #4
+ b SYMBOL_NAME(sys_vfork)
+
sys_execve_wrapper:
add r3, sp, #4
b SYMBOL_NAME(sys_execve)
ldr r2, [sp, #4 + S_SP]
b do_sigaltstack
-/*
- *=============================================================================
- * Low-level interface code
- *-----------------------------------------------------------------------------
- * Trap initialisation
- *-----------------------------------------------------------------------------
- *
- * Note - FIQ code has changed. The default is a couple of words in 0x1c, 0x20
- * that call _unexp_fiq. Nowever, we now copy the FIQ routine to 0x1c (removes
- * some excess cycles).
- *
- * What we need to put into 0-0x1c are ldrs to branch to 0xC0000000
- * (the kernel).
- * 0x1c onwards is reserved for FIQ, so I think that I will allocate 0xe0 onwards for
- * the actual address to jump to.
- */
-
- .section ".text.init",#alloc,#execinstr
-
-#if defined(CONFIG_CPU_32)
-/*
- * these go into 0x00
- */
-.Lbranches: swi SYS_ERROR0
- ldr pc, .Lbranches + 0xe4
- ldr pc, .Lbranches + 0xe8
- ldr pc, .Lbranches + 0xec
- ldr pc, .Lbranches + 0xf0
- ldr pc, .Lbranches + 0xf4
- ldr pc, .Lbranches + 0xf8
- ldr pc, .Lbranches + 0xfc
-/*
- * this is put into 0xe4 and above
- */
-.Ljump_addresses:
- .word vector_undefinstr @ 0xe4
- .word vector_swi @ 0xe8
- .word vector_prefetch @ 0xec
- .word vector_data @ 0xf0
- .word vector_addrexcptn @ 0xf4
- .word vector_IRQ @ 0xf8
- .word _unexp_fiq @ 0xfc
-/*
- * initialise the trap system
- */
-ENTRY(trap_init)
- stmfd sp!, {r4 - r7, lr}
- initialise_traps_extra
- mov r0, #0xe4
- adr r1, .Ljump_addresses
- ldmia r1, {r1 - r7}
- stmia r0, {r1 - r7}
- mov r0, #0
- adr r1, .Lbranches
- ldmia r1, {r1 - r7}
- stmia r0, {r1 - r7}
- LOADREGS(fd, sp!, {r4 - r7, pc})
-#elif defined(CONFIG_CPU_26)
-.Ljump_addresses:
- swi SYS_ERROR0
- .word vector_undefinstr - 12
- .word vector_swi - 16
- .word vector_prefetch - 20
- .word vector_data - 24
- .word vector_addrexcptn - 28
- .word vector_IRQ - 32
- .word _unexp_fiq - 36
- b . + 8
-/*
- * initialise the trap system
- */
-ENTRY(trap_init)
- stmfd sp!, {r4 - r7, lr}
- adr r1, .Ljump_addresses
- ldmia r1, {r1 - r7, ip, lr}
- orr r2, lr, r2, lsr #2
- orr r3, lr, r3, lsr #2
- orr r4, lr, r4, lsr #2
- orr r5, lr, r5, lsr #2
- orr r6, lr, r6, lsr #2
- orr r7, lr, r7, lsr #2
- orr ip, lr, ip, lsr #2
- mov r0, #0
- stmia r0, {r1 - r7, ip}
- ldmfd sp!, {r4 - r7, pc}^
-#endif
-
- .previous
-
-/*============================================================================
- * FP support
- */
-
.data
ENTRY(fp_enter)
* linux/arch/arm/kernel/fiq.c
*
* Copyright (C) 1998 Russell King
+ * Copyright (C) 1998, 1999 Phil Blundell
+ *
* FIQ support written by Philip Blundell <philb@gnu.org>, 1998.
*
* FIQ support re-written by Russell King to be more generic
unprotect_page_0();
*(unsigned long *)FIQ_VECTOR = no_fiq_insn;
protect_page_0();
- __flush_entry_to_ram(FIQ_VECTOR);
+ flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + 4);
}
return 0;
memcpy((void *)FIQ_VECTOR, start, length);
protect_page_0();
-#ifdef CONFIG_CPU_32
- processor.u.armv3v4._flush_cache_area(FIQ_VECTOR, FIQ_VECTOR + length, 1);
-#endif
+ flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + length);
}
+/*
+ * Taking an interrupt in FIQ mode is death, so both these functions
+ * disable irqs for the duration.
+ */
void set_fiq_regs(struct pt_regs *regs)
{
- /* not yet -
- * this is temporary to get the floppy working
- * again on RiscPC. It *will* become more
- * generic.
- */
-#ifdef CONFIG_ARCH_ACORN
- extern void floppy_fiqsetup(unsigned long len, unsigned long addr,
- unsigned long port);
- floppy_fiqsetup(regs->ARM_r9, regs->ARM_r10, regs->ARM_fp);
+ register unsigned long tmp, tmp2;
+ __asm__ volatile (
+#ifdef CONFIG_CPU_26
+ "mov %0, pc
+ bic %1, %0, #0x3
+ orr %1, %1, #0x0c000001
+ teqp %1, #0 @ select FIQ mode
+ mov r0, r0
+ ldmia %2, {r8 - r14}
+ teqp %0, #0 @ return to SVC mode
+ mov r0, r0"
#endif
+#ifdef CONFIG_CPU_32
+ "mrs %0, cpsr
+ bic %1, %0, #0xf
+ orr %1, %1, #0xc1
+ msr cpsr, %1 @ select FIQ mode
+ mov r0, r0
+ ldmia %2, {r8 - r14}
+ msr cpsr, %0 @ return to SVC mode
+ mov r0, r0"
+#endif
+ : "=r" (tmp), "=r" (tmp2)
+ : "r" (®s->ARM_r8)
+ /* These registers aren't modified by the above code in a way
+ visible to the compiler, but we mark them as clobbers anyway
+ so that GCC won't put any of the input or output operands in
+ them. */
+ : "r8", "r9", "r10", "r11", "r12", "r13", "r14");
}
void get_fiq_regs(struct pt_regs *regs)
{
- /* not yet */
+ register unsigned long tmp, tmp2;
+ __asm__ volatile (
+#ifdef CONFIG_CPU_26
+ "mov %0, pc
+ bic %1, %0, #0x3
+ orr %1, %1, #0x0c000001
+ teqp %1, #0 @ select FIQ mode
+ mov r0, r0
+ stmia %2, {r8 - r14}
+ teqp %0, #0 @ return to SVC mode
+ mov r0, r0"
+#endif
+#ifdef CONFIG_CPU_32
+ "mrs %0, cpsr
+ bic %1, %0, #0xf
+ orr %1, %1, #0xc1
+ msr cpsr, %1 @ select FIQ mode
+ mov r0, r0
+ stmia %2, {r8 - r14}
+ msr cpsr, %0 @ return to SVC mode
+ mov r0, r0"
+#endif
+ : "=r" (tmp), "=r" (tmp2)
+ : "r" (®s->ARM_r8)
+ /* These registers aren't modified by the above code in a way
+ visible to the compiler, but we mark them as clobbers anyway
+ so that GCC won't put any of the input or output operands in
+ them. */
+ : "r8", "r9", "r10", "r11", "r12", "r13", "r14");
}
int claim_fiq(struct fiq_handler *f)
*/
#include <linux/config.h>
#include <linux/linkage.h>
+#include <asm/hardware.h>
+#include <asm/dec21285.h>
+
+ .globl SYMBOL_NAME(swapper_pg_dir)
+ .equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000
+
+ .section ".text.init",#alloc,#execinstr
+ENTRY(stext)
+ENTRY(_stext)
-#ifndef CONFIG_ARCH_VNC
#if (TEXTADDR & 0xffff) != 0x8000
#error TEXTADDR must start at 0xXXXX8000
#endif
-#else
- .text
+
+#ifdef CONFIG_ARCH_NETWINDER
mov r0, r0
mov r0, r0
mov r0, r0
mov r0, r0
mov r0, r0
mov r0, r0
+
+ adr r2, 1f
+ ldmdb r2, {r7, r8}
+ and r3, r2, #0x0000c000
+ teq r3, #0x00008000
+ beq __entry
+ bic r3, r2, #0xc000
+ orr r3, r3, #0x8000
+ mov r0, r3
+ mov r4, #32
+ sub r5, r8, r7
+ b 1f
+
+ .word _stext
+ .word _end
+
+1: ldmia r2!, {r6, r7, r8, r9}
+ stmia r3!, {r6, r7, r8, r9}
+ subs r4, r4, #16
+ bcs 1b
+ movs r4, r5
+ mov r5, #0
+ movne pc, r0
+
mov r0, #0
mov r1, #5
#endif
-#define DEBUG
-
- .globl SYMBOL_NAME(swapper_pg_dir)
- .equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000
-
- .text
/*
* Entry point and restart point. Entry *must* be called with r0 == 0,
* MMU off. Note! These should be unique!!! Please read Documentation/ARM-README
* r1 = 5 -> Corel Netwinder
* r1 = 6 -> CATS
* r1 = 7 -> tbox
+ * r1 = 8 -> SA110/21285 as co-processor
*/
-ENTRY(stext)
-ENTRY(_stext)
__entry: teq r0, #0 @ check for illegal entry...
bne .Lerror @ loop indefinitely
- cmp r1, #8 @ Unknown machine architecture
+ cmp r1, #9 @ Unknown machine architecture
bge .Lerror
-/* First thing to do is to get the page tables set up so that we can call the kernel
- * in the correct place. This is relocatable code...
+/* First thing to do is to get the page tables set up so that we can call
+ * the kernel in the correct place. This is relocatable code...
* - Read processor ID register (CP#15, CR0).
*/
mrc p15, 0, r9, c0, c0 @ get Processor ID
adr r4, .LCMachTypes
add r4, r4, r1, lsl #4
- ldmia r4, {r4, r5, r6}
+ ldmia r4, {r4, r5, r6, r7}
/*
* r4 = page dir in physical ram
* r5 = physical address of start of RAM
add r3, r3, #1 << 20
str r3, [r0], #4
add r3, r3, #1 << 20
-#ifdef DEBUG
+#ifdef CONFIG_DEBUG_LL
/* Map in IO space
* This allows debug messages to be output via a serial
* before/while paging_init.
*/
- add r0, r4, #0x3800
+ add r0, r4, r7
orr r3, r6, r8
add r2, r0, #0x0800
1: str r3, [r0], #4
add r3, r3, #1 << 20
teq r0, r2
bne 1b
-#ifdef CONFIG_ARCH_VNC
- add r0, r4, #0x3f00
- add r0, r0, #0x00f8
+#ifdef CONFIG_ARCH_NETWINDER
+ teq r1, #5
+ bne 1f
+ add r0, r4, #0x3fc0
mov r3, #0x7c000000
orr r3, r3, r8
str r3, [r0], #4
add r3, r3, #1 << 20
str r3, [r0], #4
+1:
#endif
#endif
#ifdef CONFIG_ARCH_RPC
.LCMachTypes: .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
.long 0 @ Address of RAM
.long 0xe0000000 @ I/O address
- .long 0
+ .long 0x3800
@ Acorn RiscPC
.long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x10000000
.long 0x10000000
.long 0x03000000
- .long 0
+ .long 0x3800
@ EBSIT ???
.long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000
.long 0
.long 0xe0000000
- .long 0
+ .long 0x3800
@ NexusPCI
.long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x40000000
.long 0x40000000
.long 0x10000000
- .long 0
+ .long 0x3800
@ DEC EBSA285
.long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
.long 0 @ Address of RAM
.long 0x24000000 @ I/O base address (0x42000000 -> 0xFE000000)
- .long 0
+ .long 0x3800
@ Corel VNC
.long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
.long 0 @ Address of RAM
.long 0x24000000 @ I/O base address (0x42000000 -> 0xfe000000)
- .long 0
+ .long 0x3800
@ CATS
.long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical)
.long 0 @ Address of RAM
.long 0x24000000 @ I/O base address (0x42000000 -> 0xfe000000)
- .long 0
+ .long 0x3800
@ tbox
.long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x80000000
.long 0x80000000 @ Address of RAM
.long 0x00400000 @ Uart
- .long 0
+ .long 0x3800
+
+ @ DEC EBSA285 as co-processor
+ .long 0x4000 @ Address of page tables (physical)
+ .long 0 @ Address of RAM
+ .long DC21285_ARMCSR_BASE @ Physical I/O base address
+ .long 0x7cf00000 >> 18 @ Virtual I/O base address
.LCProcTypes: @ ARM6 / 610
.long 0x41560600
mcr p15, 0, r4, c2, c0 @ load page table pointer
mov r0, #0x1f @ Domains 0, 1 = client
mcr p15, 0, r0, c3, c0 @ load domain access register
+#ifdef CONFIG_ALIGNMENT_TRAP
+ mov r0, #0x3f @ ....S..DPWCAM
+#else
mov r0, #0x3d @ ....S..DPWC.M
+#endif
orr r0, r0, #0x100
mov pc, lr
mcr p15, 0, r4, c2, c0 @ load page table pointer
mov r0, #0x1f @ Domains 0, 1 = client
mcr p15, 0, r0, c3, c0 @ load domain access register
+#ifdef CONFIG_ALIGNMENT_TRAP
+ mov r0, #0x7f @ ....S.LDPWCAM
+#else
mov r0, #0x7d @ ....S.LDPWC.M
+#endif
orr r0, r0, #0x100
mov pc, lr
mrc p15, 0, r0, c1, c0 @ get control register v4
bic r0, r0, #0x0e00
bic r0, r0, #0x0002
+#ifdef CONFIG_ALIGNMENT_TRAP
+ orr r0, r0, #0x003f @ I...S..DPWCAM
+#else
orr r0, r0, #0x003d @ I...S..DPWC.M
+#endif
orr r0, r0, #0x1100 @ v4 supports separate I cache
mov pc, lr
- .section ".text.init",#alloc,#execinstr
-
.Lsa_fastclock: mcr p15, 0, r4, c15, c1, 2 @ Enable clock switching
mov pc, lr
.LC0: .long SYMBOL_NAME(__entry)
- .long SYMBOL_NAME(machine_type)
+ .long SYMBOL_NAME(__machine_arch_type)
.long SYMBOL_NAME(__bss_start)
.long SYMBOL_NAME(processor_id)
.long SYMBOL_NAME(_end)
+ .long SYMBOL_NAME(cr_alignment)
.long SYMBOL_NAME(init_task_union)+8192
.align
.Lalready_done_mmap:
adr r4, .LC0
- ldmia r4, {r3, r4, r5, r6, r8, sp} @ Setup stack
+ ldmia r4, {r3, r4, r5, r6, r7, r8, sp} @ Setup stack
add r10, r10, r3 @ Add base back in
mov fp, #0
-1: cmp r5, r8 @ Clear BSS
+1: cmp r5, r7 @ Clear BSS
strcc fp, [r5],#4
bcc 1b
+ bic r2, r0, #2 @ Clear 'A' bit
+ stmia r8, {r0, r2} @ Save control register values
+
str r1, [r4] @ Save machine type
str r9, [r6] @ Save processor ID
mov lr, pc
b SYMBOL_NAME(start_kernel)
.text
-#ifdef DEBUG
+
+#ifdef CONFIG_DEBUG_LL
/*
* Some debugging routines (useful if you've got MM problems and
- * printk isn't working). For DEBUGGING ONLY!!!
+ * printk isn't working). For DEBUGGING ONLY!!! Do not leave
+ * references to these in a production kernel!
*/
#if defined(CONFIG_ARCH_RPC)
.macro addruart,rx
beq 1001b
.endm
-#elif defined(CONFIG_ARCH_EBSA285)
+#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE)
+#ifndef CONFIG_DEBUG_DC21285_PORT
+ /* For NetWinder debugging */
.macro addruart,rx
- mov \rx, #0xfe000000
+ mov \rx, #0xff000000
+ orr \rx, \rx, #0x000003f8
.endm
.macro senduart,rd,rx
- str \rd, [\rx, #0x160] @ UARTDR
+ strb \rd, [\rx]
.endm
.macro busyuart,rd,rx
-1001: ldr \rd, [\rx, #0x178] @ UARTFLG
- tst \rd, #1 << 3
- bne 1001b
+1002: ldrb \rd, [\rx, #0x5]
+ and \rd, \rd, #0x60
+ teq \rd, #0x60
+ bne 1002b
.endm
.macro waituart,rd,rx
+1001: ldrb \rd, [\rx, #0x6]
+ tst \rd, #0x10
+ beq 1001b
.endm
+#else
+ /* For EBSA285 debugging */
+ .equ dc21285_high, ARMCSR_BASE & 0xff000000
+ .equ dc21285_low, ARMCSR_BASE & 0x00ffffff
-#elif defined(CONFIG_ARCH_NEXUSPCI)
.macro addruart,rx
- ldr \rx, =0xfff00000
+ mov \rx, #dc21285_high
+ .if dc21285_low
+ orr \rx, \rx, #dc21285_low
+ .endif
.endm
.macro senduart,rd,rx
- str \rd, [\rx, #0xc]
+ str \rd, [\rx, #0x160] @ UARTDR
.endm
.macro busyuart,rd,rx
-1001: ldr \rd, [\rx, #0x4]
- tst \rd, #1 << 0
+1001: ldr \rd, [\rx, #0x178] @ UARTFLG
+ tst \rd, #1 << 3
bne 1001b
.endm
.macro waituart,rd,rx
.endm
-
-#elif defined(CONFIG_ARCH_VNC)
+#endif
+#elif defined(CONFIG_ARCH_NEXUSPCI)
.macro addruart,rx
- mov \rx, #0xff000000
- orr \rx, \rx, #0x00e00000
- orr \rx, \rx, #0x000003f8
+ ldr \rx, =0xfff00000
.endm
.macro senduart,rd,rx
- strb \rd, [\rx]
+ str \rd, [\rx, #0xc]
.endm
.macro busyuart,rd,rx
-1002: ldrb \rd, [\rx, #0x5]
- and \rd, \rd, #0x60
- teq \rd, #0x60
- bne 1002b
+1001: ldr \rd, [\rx, #0x4]
+ tst \rd, #1 << 0
+ bne 1001b
.endm
.macro waituart,rd,rx
-1001: ldrb \rd, [\rx, #0x6]
- tst \rd, #0x10
- beq 1001b
.endm
#else
#error Unknown architecture
mov r0, #0
b 1b
- .ltorg
-
.bss
hexbuf: .space 16
+++ /dev/null
-/*
- * arch/arm/kernel/hw-ebsa286.c
- *
- * EBSA285 hardware specific functions
- *
- * Copyright (C) 1998 Russell King, Phil Blundel
- */
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/ptrace.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-
-#include <asm/irq.h>
-#include <asm/system.h>
-
-extern int setup_arm_irq(int, struct irqaction *);
-
-extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set);
-extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr);
-extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq);
-
-static int irqmap_ebsa[] __initdata = { 9, 8, 18, 11 };
-static int irqmap_cats[] __initdata = { 18, 8, 9, 11 };
-
-__initfunc(static int ebsa_irqval(struct pci_dev *dev))
-{
- unsigned char pin;
-
- pcibios_read_config_byte(dev->bus->number,
- dev->devfn,
- PCI_INTERRUPT_PIN,
- &pin);
-
- return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3];
-}
-
-__initfunc(static int cats_irqval(struct pci_dev *dev))
-{
- if (dev->irq >= 128)
- return 32 + (dev->irq & 0x1f);
-
- switch (dev->irq) {
- case 1:
- case 2:
- case 3:
- case 4:
- return irqmap_cats[dev->irq - 1];
- case 0:
- return 0;
- }
-
- printk("PCI: device %02x:%02x has unknown irq line %x\n",
- dev->bus->number, dev->devfn, dev->irq);
- return 0;
-}
-
-__initfunc(void pcibios_fixup_ebsa285(struct pci_dev *dev))
-{
- char cmd;
-
- /* sort out the irq mapping for this device */
- switch (machine_type) {
- case MACH_TYPE_EBSA285:
- dev->irq = ebsa_irqval(dev);
- break;
- case MACH_TYPE_CATS:
- dev->irq = cats_irqval(dev);
- break;
- }
-
- /* Turn on bus mastering - boot loader doesn't
- * - perhaps it should! - dag
- */
- pci_read_config_byte(dev, PCI_COMMAND, &cmd);
- pci_write_config_byte(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
-}
-
-static void irq_pci_err(int irq, void *dev_id, struct pt_regs *regs)
-{
- const char *err = "unknown";
- unsigned long cmd = *(unsigned long *)0xfe000004 & 0xffff;
- unsigned long ctrl = *(unsigned long *)0xfe00013c & 0xffffde07;
- static unsigned long next_warn[7];
- int idx = 6;
-
- switch(irq) {
- case IRQ_PCIPARITY:
- *(unsigned long *)0xfe000004 = cmd | 1 << 31;
- idx = 0;
- err = "parity";
- break;
-
- case IRQ_PCITARGETABORT:
- *(unsigned long *)0xfe000004 = cmd | 1 << 28;
- idx = 1;
- err = "target abort";
- break;
-
- case IRQ_PCIMASTERABORT:
- *(unsigned long *)0xfe000004 = cmd | 1 << 29;
- idx = 2;
- err = "master abort";
- break;
-
- case IRQ_PCIDATAPARITY:
- *(unsigned long *)0xfe000004 = cmd | 1 << 24;
- idx = 3;
- err = "data parity";
- break;
-
- case IRQ_DISCARDTIMER:
- *(unsigned long *)0xfe00013c = ctrl | 1 << 8;
- idx = 4;
- err = "discard timer";
- break;
-
- case IRQ_SERR:
- *(unsigned long *)0xfe00013c = ctrl | 1 << 3;
- idx = 5;
- err = "system";
- break;
- }
- if (time_after_eq(jiffies, next_warn[idx])) {
- next_warn[idx] = jiffies + 3 * HZ / 100;
- printk(KERN_ERR "PCI %s error detected\n", err);
- }
-}
-
-static struct irqaction irq_pci_error = {
- irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL
-};
-
-__initfunc(void pcibios_init_ebsa285(void))
-{
- setup_arm_irq(IRQ_PCIPARITY, &irq_pci_error);
- setup_arm_irq(IRQ_PCITARGETABORT, &irq_pci_error);
- setup_arm_irq(IRQ_PCIMASTERABORT, &irq_pci_error);
- setup_arm_irq(IRQ_PCIDATAPARITY, &irq_pci_error);
- setup_arm_irq(IRQ_DISCARDTIMER, &irq_pci_error);
- setup_arm_irq(IRQ_SERR, &irq_pci_error);
-
- /*
- * Map our SDRAM at a known address in PCI space, just in case
- * the firmware had other ideas. Using a nonzero base is slightly
- * bizarre but apparently necessary to avoid problems with some
- * video cards.
- *
- * We should really only do this if the central function is enabled.
- */
- *(unsigned long *)0xfe000010 = 0;
- *(unsigned long *)0xfe000018 = 0xe0000000;
- *(unsigned long *)0xfe0000f8 = 0;
- *(unsigned long *)0xfe0000fc = 0;
- *(unsigned long *)0xfe000100 = 0x01fc0000;
- *(unsigned long *)0xfe000104 = 0;
- *(unsigned long *)0xfe000108 = 0x80000000;
- *(unsigned long *)0xfe000004 = 0x17;
-}
--- /dev/null
+/*
+ * arch/arm/kernel/hw-footbridge.c
+ *
+ * Footbridge-dependent machine fixup
+ *
+ * Copyright (C) 1998, 1999 Russell King, Phil Blundell
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/smp.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/dec21285.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+#define IRDA_IO_BASE 0x180
+#define ETHER10_IO_BASE 0x301
+#define GP1_IO_BASE 0x338
+#define GP2_IO_BASE 0x33a
+#define DEC21143_IO_BASE 0x401
+#define DEC21143_MEM_BASE 0x00800000
+#define CYBER2000_MEM_BASE 0x01000000
+
+int have_isa_bridge;
+
+extern int setup_arm_irq(int, struct irqaction *);
+extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set);
+extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr);
+extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq);
+extern void (*kd_mksound)(unsigned int hz, unsigned int ticks);
+
+#ifdef CONFIG_PCI
+
+static int irqmap_ebsa[] __initdata = { IRQ_IN1, IRQ_IN0, IRQ_PCI, IRQ_IN3 };
+
+__initfunc(static int ebsa_irqval(struct pci_dev *dev))
+{
+ unsigned char pin;
+
+ pcibios_read_config_byte(dev->bus->number,
+ dev->devfn,
+ PCI_INTERRUPT_PIN,
+ &pin);
+
+ return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3];
+}
+
+#ifdef CONFIG_CATS
+static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 };
+
+__initfunc(static int cats_irqval(struct pci_dev *dev))
+{
+ if (dev->irq >= 128)
+ return 16 + (dev->irq & 0x1f);
+
+ switch (dev->irq) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return irqmap_cats[dev->irq - 1];
+ case 0:
+ return 0;
+ }
+
+ printk("PCI: device %02x:%02x has unknown irq line %x\n",
+ dev->bus->number, dev->devfn, dev->irq);
+ return 0;
+}
+#endif
+
+__initfunc(void pcibios_fixup_ebsa285(struct pci_dev *dev))
+{
+ /* Latency timer of 32 */
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 32);
+
+ /* 32-byte cache line size */
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8);
+
+ /* Set SysErr enable, Parity enable */
+ pci_set_cmd(dev, 0, PCI_COMMAND_FAST_BACK | PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+
+ /* If this device is an ISA bridge, set the
+ * have_isa_bridge flag. We will then go looking
+ * for things like keyboard, etc
+ */
+ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA ||
+ (dev->class >> 8) == PCI_CLASS_BRIDGE_EISA)
+ have_isa_bridge = !0;
+
+ /* sort out the irq mapping for this device */
+ switch (machine_arch_type) {
+ case MACH_TYPE_EBSA285:
+ dev->irq = ebsa_irqval(dev);
+ /* Turn on bus mastering - boot loader doesn't
+ * - perhaps it should! - dag
+ */
+ pci_set_cmd(dev, 0, PCI_COMMAND_MASTER);
+ break;
+
+#ifdef CONFIG_CATS
+ case MACH_TYPE_CATS:
+ dev->irq = cats_irqval(dev);
+ /* Turn on bus mastering - boot loader doesn't
+ * - perhaps it should! - dag
+ */
+ pci_set_cmd(dev, 0, PCI_COMMAND_MASTER);
+ break;
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
+ case MACH_TYPE_NETWINDER:
+ /* disable ROM */
+ pci_write_config_dword(dev, PCI_ROM_ADDRESS, 0);
+
+#define DEV(v,d) ((v)<<16|(d))
+ switch (DEV(dev->vendor, dev->device)) {
+ /* Ether 100 */
+ case DEV(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142):
+ pci_set_base_addr(dev, 0, DEC21143_IO_BASE);
+ pci_set_base_addr(dev, 1, DEC21143_MEM_BASE);
+ pci_set_cmd(dev, 0, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
+ /* Put the chip to sleep in case the driver isn't loaded */
+ pci_write_config_dword(dev, 0x40, 0x80000000);
+ dev->irq = IRQ_NETWINDER_ETHER100;
+ break;
+
+ /* Ether 10 */
+ case DEV(PCI_VENDOR_ID_WINBOND2,0x5a5a):
+ pci_set_base_addr(dev, 0, ETHER10_IO_BASE);
+ pci_set_cmd(dev, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, PCI_COMMAND_IO);
+ dev->irq = IRQ_NETWINDER_ETHER10;
+ break;
+
+ /* ISA bridge */
+ case DEV(PCI_VENDOR_ID_WINBOND,PCI_DEVICE_ID_WINBOND_83C553):
+ pci_set_base_addr(dev, 0, 0);
+ pci_set_cmd(dev, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, PCI_COMMAND_IO);
+ /*
+ * Enable all memory requests from ISA to be channeled to PCI
+ */
+ pci_write_config_byte(dev, 0x48, 255);
+ /*
+ * Disable ping-pong (as per errata)
+ */
+ pci_write_config_byte(dev, 0x42, 0);
+ /*
+ * Enable PCI packet retry
+ */
+ pci_write_config_byte(dev, 0x40, 0x22);
+ /*
+ * Do not use PCI CPU park enable, park on
+ * last master, disable GAT bit
+ */
+ pci_write_config_byte(dev, 0x83, 0x02);
+ /*
+ * Default rotating priorities
+ */
+ pci_write_config_byte(dev, 0x80, 0xe0);
+ /*
+ * Rotate bank 4
+ */
+ pci_write_config_byte(dev, 0x81, 0x01);
+ break;
+
+ /* IDE */
+ case DEV(PCI_VENDOR_ID_WINBOND,PCI_DEVICE_ID_WINBOND_82C105):
+ pci_set_base_addr(dev, 0, 0x1f1);
+ pci_set_base_addr(dev, 1, 0x3f5);
+ pci_set_base_addr(dev, 2, 0x171);
+ pci_set_base_addr(dev, 3, 0x375);
+ pci_set_base_addr(dev, 4, 0xe801);
+ pci_set_cmd(dev, PCI_COMMAND_MEMORY, PCI_COMMAND_MASTER | PCI_COMMAND_IO);
+ dev->irq = IRQ_ISA_HARDDISK1;
+ break;
+
+ /* VGA */
+ case DEV(PCI_VENDOR_ID_INTERG,0x2000):
+ pci_set_base_addr(dev, 0, CYBER2000_MEM_BASE);
+ pci_set_cmd(dev, PCI_COMMAND_MASTER, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ dev->irq = IRQ_NETWINDER_VGA;
+ break;
+ }
+#endif
+ }
+}
+
+static inline void
+report_pci_dev_error(void)
+{
+ struct pci_dev *dev;
+
+ for (dev = pci_devices; dev; dev = dev->next) {
+ unsigned short status;
+
+ pci_read_config_word(dev, PCI_STATUS, &status);
+ if (status & 0xf900) {
+ printk(KERN_DEBUG "PCI: [%04X:%04X] status = %X\n",
+ dev->vendor, dev->device, status);
+
+ pci_write_config_word(dev, PCI_STATUS, status & 0xf900);
+ }
+ }
+}
+#else
+#define report_pci_dev_error()
+#endif
+
+/*
+ * Warn on PCI errors. Please report any occurances!
+ */
+static void
+irq_pci_err(int irq, void *dev_id, struct pt_regs *regs)
+{
+ static unsigned long next_warn;
+ unsigned long cmd = *CSR_PCICMD & 0x0000ffff;
+ unsigned long ctrl = (*CSR_SA110_CNTL) & 0xffffde07;
+ unsigned long irqstatus = *CSR_IRQ_RAWSTATUS;
+ int warn = time_after_eq(jiffies, next_warn);
+
+ ctrl |= SA110_CNTL_DISCARDTIMER;
+
+ if (warn) {
+ next_warn = jiffies + 3 * HZ / 100;
+ printk(KERN_DEBUG "PCI: ");
+ }
+
+ if (irqstatus & (1 << 31)) {
+ if (warn)
+ printk("parity error ");
+ cmd |= 1 << 31;
+ }
+
+ if (irqstatus & (1 << 30)) {
+ if (warn)
+ printk("target abort ");
+ cmd |= 1 << 28;
+ }
+
+ if (irqstatus & (1 << 29)) {
+ if (warn)
+ printk("master abort ");
+ cmd |= 1 << 29;
+ }
+
+ if (irqstatus & (1 << 28)) {
+ if (warn)
+ printk("data parity error ");
+ cmd |= 1 << 24;
+ }
+
+ if (irqstatus & (1 << 27)) {
+ if (warn)
+ printk("discard timer expired ");
+ ctrl &= ~SA110_CNTL_DISCARDTIMER;
+ }
+
+ if (irqstatus & (1 << 23)) {
+ if (warn)
+ printk("system error ");
+ ctrl |= SA110_CNTL_RXSERR;
+ }
+
+ if (warn)
+ printk("pc=[<%08lX>]\n", instruction_pointer(regs));
+
+ report_pci_dev_error();
+
+ *CSR_PCICMD = cmd;
+ *CSR_SA110_CNTL = ctrl;
+}
+
+static struct irqaction irq_pci_error = {
+ irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL
+};
+
+__initfunc(void pcibios_init_ebsa285(void))
+{
+ setup_arm_irq(IRQ_PCI_ERR, &irq_pci_error);
+}
+
+/*
+ * Netwinder stuff
+ */
+#ifdef CONFIG_ARCH_NETWINDER
+
+/*
+ * Winbond WB83977F accessibility stuff
+ */
+static inline void wb977_open(void)
+{
+ outb(0x87, 0x370);
+ outb(0x87, 0x370);
+}
+
+static inline void wb977_close(void)
+{
+ outb(0xaa, 0x370);
+}
+
+static inline void wb977_wb(int reg, int val)
+{
+ outb(reg, 0x370);
+ outb(val, 0x371);
+}
+
+static inline void wb977_ww(int reg, int val)
+{
+ outb(reg, 0x370);
+ outb(val >> 8, 0x371);
+ outb(reg + 1, 0x370);
+ outb(val, 0x371);
+}
+
+#define wb977_device_select(dev) wb977_wb(0x07, dev)
+#define wb977_device_disable() wb977_wb(0x30, 0x00)
+#define wb977_device_enable() wb977_wb(0x30, 0x01)
+
+/*
+ * This is a lock for accessing ports GP1_IO_BASE and GP2_IO_BASE
+ */
+spinlock_t __netwinder_data gpio_lock = SPIN_LOCK_UNLOCKED;
+
+static unsigned int __netwinder_data current_gpio_op = 0;
+static unsigned int __netwinder_data current_gpio_io = 0;
+static unsigned int __netwinder_data current_cpld = 0;
+
+void __netwinder_text gpio_modify_op(int mask, int set)
+{
+ unsigned int new_gpio, changed;
+
+ new_gpio = (current_gpio_op & ~mask) | set;
+ changed = new_gpio ^ current_gpio_op;
+ current_gpio_op = new_gpio;
+
+ if (changed & 0xff)
+ outb(new_gpio, GP1_IO_BASE);
+ if (changed & 0xff00)
+ outb(new_gpio >> 8, GP2_IO_BASE);
+}
+
+static inline void __gpio_modify_io(int mask, int in)
+{
+ unsigned int new_gpio, changed;
+ int port;
+
+ new_gpio = (current_gpio_io & ~mask) | in;
+ changed = new_gpio ^ current_gpio_io;
+ current_gpio_io = new_gpio;
+
+ changed >>= 1;
+ new_gpio >>= 1;
+
+ wb977_device_select(7);
+
+ for (port = 0xe1; changed && port < 0xe8; changed >>= 1) {
+ wb977_wb(port, new_gpio & 1);
+
+ port += 1;
+ new_gpio >>= 1;
+ }
+
+ wb977_device_select(8);
+
+ for (port = 0xe8; changed && port < 0xec; changed >>= 1) {
+ wb977_wb(port, new_gpio & 1);
+
+ port += 1;
+ new_gpio >>= 1;
+ }
+}
+
+void __netwinder_text gpio_modify_io(int mask, int in)
+{
+ /* Open up the SuperIO chip */
+ wb977_open();
+
+ __gpio_modify_io(mask, in);
+
+ /* Close up the EFER gate */
+ wb977_close();
+}
+
+int __netwinder_text gpio_read(void)
+{
+ return inb(GP1_IO_BASE) | inb(GP2_IO_BASE) << 8;
+}
+
+/*
+ * Initialise the Winbond W83977F global registers
+ */
+static inline void wb977_init_global(void)
+{
+ /*
+ * Enable R/W config registers
+ */
+ wb977_wb(0x26, 0x40);
+
+ /*
+ * Power down FDC (not used)
+ */
+ wb977_wb(0x22, 0xfe);
+
+ /*
+ * GP12, GP11, CIRRX, IRRXH, GP10
+ */
+ wb977_wb(0x2a, 0xc1);
+
+ /*
+ * GP23, GP22, GP21, GP20, GP13
+ */
+ wb977_wb(0x2b, 0x6b);
+
+ /*
+ * GP17, GP16, GP15, GP14
+ */
+ wb977_wb(0x2c, 0x55);
+}
+
+/*
+ * Initialise the Winbond W83977F printer port
+ */
+static inline void wb977_init_printer(void)
+{
+ wb977_device_select(1);
+
+ /*
+ * mode 1 == EPP
+ */
+ wb977_wb(0xf0, 0x01);
+}
+
+/*
+ * Initialise the Winbond W83977F keyboard controller
+ */
+static inline void wb977_init_keyboard(void)
+{
+ wb977_device_select(5);
+
+ /*
+ * Keyboard controller address
+ */
+ wb977_ww(0x60, 0x0060);
+ wb977_ww(0x62, 0x0064);
+
+ /*
+ * Keyboard IRQ 1, active high, edge trigger
+ */
+ wb977_wb(0x70, 1);
+ wb977_wb(0x71, 0x02);
+
+ /*
+ * Mouse IRQ 5, active high, edge trigger
+ */
+ wb977_wb(0x72, 5);
+ wb977_wb(0x73, 0x02);
+
+ /*
+ * KBC 8MHz
+ */
+ wb977_wb(0xf0, 0x40);
+
+ /*
+ * Enable device
+ */
+ wb977_device_enable();
+}
+
+/*
+ * Initialise the Winbond W83977F Infra-Red device
+ */
+static inline void wb977_init_irda(void)
+{
+ wb977_device_select(6);
+
+ /*
+ * IR base address
+ */
+ wb977_ww(0x60, IRDA_IO_BASE);
+
+ /*
+ * IRDA IRQ 6, active high, edge trigger
+ */
+ wb977_wb(0x70, 6);
+ wb977_wb(0x71, 0x02);
+
+ /*
+ * RX DMA - ISA DMA 0
+ */
+ wb977_wb(0x74, 0x00);
+
+ /*
+ * TX DMA - Disable Tx DMA
+ */
+ wb977_wb(0x75, 0x04);
+
+ /*
+ * Append CRC, Enable bank selection
+ */
+ wb977_wb(0xf0, 0x03);
+
+ /*
+ * Enable device
+ */
+ wb977_device_enable();
+}
+
+/*
+ * Initialise Winbond W83977F general purpose IO
+ */
+static inline void wb977_init_gpio(void)
+{
+ unsigned long flags;
+
+ /*
+ * Set up initial I/O definitions
+ */
+ current_gpio_io = -1;
+ __gpio_modify_io(-1, GPIO_DONE | GPIO_WDTIMER);
+
+ wb977_device_select(7);
+
+ /*
+ * Group1 base address
+ */
+ wb977_ww(0x60, GP1_IO_BASE);
+ wb977_ww(0x62, 0);
+ wb977_ww(0x64, 0);
+
+ /*
+ * GP10 (Orage button) IRQ 10, active high, edge trigger
+ */
+ wb977_wb(0x70, 10);
+ wb977_wb(0x71, 0x02);
+
+ /*
+ * GP10: Debounce filter enabled, IRQ, input
+ */
+ wb977_wb(0xe0, 0x19);
+
+ /*
+ * Enable Group1
+ */
+ wb977_device_enable();
+
+ wb977_device_select(8);
+
+ /*
+ * Group2 base address
+ */
+ wb977_ww(0x60, GP2_IO_BASE);
+
+ /*
+ * Clear watchdog timer regs
+ * - timer disable
+ */
+ wb977_wb(0xf2, 0x00);
+
+ /*
+ * - disable LED, no mouse nor keyboard IRQ
+ */
+ wb977_wb(0xf3, 0x00);
+
+ /*
+ * - timer counting, disable power LED, disable timeouot
+ */
+ wb977_wb(0xf4, 0x00);
+
+ /*
+ * Enable group2
+ */
+ wb977_device_enable();
+
+ /*
+ * Set Group1/Group2 outputs
+ */
+ spin_lock_irqsave(&gpio_lock, flags);
+ gpio_modify_op(-1, GPIO_RED_LED | GPIO_FAN);
+ spin_unlock_irqrestore(&gpio_loc, flags);
+}
+
+/*
+ * Initialise the Winbond W83977F chip.
+ */
+__initfunc(static void wb977_init(void))
+{
+ request_region(0x370, 2, "W83977AF configuration");
+
+ /*
+ * Open up the SuperIO chip
+ */
+ wb977_open();
+
+ /*
+ * Initialise the global registers
+ */
+ wb977_init_global();
+
+ /*
+ * Initialise the various devices in
+ * the multi-IO chip.
+ */
+ wb977_init_printer();
+ wb977_init_keyboard();
+ wb977_init_irda();
+ wb977_init_gpio();
+
+ /*
+ * Close up the EFER gate
+ */
+ wb977_close();
+}
+
+void __netwinder_text cpld_modify(int mask, int set)
+{
+ int msk;
+
+ current_cpld = (current_cpld & ~mask) | set;
+
+ gpio_modify_io(GPIO_DATA, 0);
+ gpio_modify_op(GPIO_IOLOAD, 0);
+
+ for (msk = 8; msk; msk >>= 1) {
+ int bit = current_cpld & msk;
+
+ gpio_modify_op(GPIO_DATA | GPIO_IOCLK, bit ? GPIO_DATA : 0);
+ gpio_modify_op(GPIO_IOCLK, GPIO_IOCLK);
+ }
+
+ gpio_modify_op(GPIO_IOCLK|GPIO_DATA, 0);
+ gpio_modify_op(GPIO_IOLOAD|GPIO_DSCLK, GPIO_IOLOAD|GPIO_DSCLK);
+ gpio_modify_op(GPIO_IOLOAD, 0);
+}
+
+__initfunc(static void cpld_init(void))
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ cpld_modify(-1, CPLD_UNMUTE | 4);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+}
+
+static unsigned char rwa_unlock[] __initdata =
+{ 0x00, 0x00, 0x6a, 0xb5, 0xda, 0xed, 0xf6, 0xfb, 0x7d, 0xbe, 0xdf, 0x6f, 0x37, 0x1b,
+ 0x0d, 0x86, 0xc3, 0x61, 0xb0, 0x58, 0x2c, 0x16, 0x8b, 0x45, 0xa2, 0xd1, 0xe8, 0x74,
+ 0x3a, 0x9d, 0xce, 0xe7, 0x73, 0x39 };
+
+#ifndef DEBUG
+#define dprintk if (0) printk
+#else
+#define dprintk printk
+#endif
+
+#define WRITE_RWA(r,v) do { outb((r), 0x279); outb((v), 0xa79); } while (0)
+
+static inline void rwa010_unlock(void)
+{
+ int i;
+
+ WRITE_RWA(2, 2);
+ mdelay(10);
+
+ for (i = 0; i < sizeof(rwa_unlock); i++)
+ outb(rwa_unlock[i], 0x279);
+}
+
+static inline void rwa010_read_ident(void)
+{
+ unsigned char si[9];
+ int i, j;
+
+ WRITE_RWA(3, 0);
+ WRITE_RWA(0, 128);
+
+ outb(1, 0x279);
+
+ mdelay(10);
+
+ dprintk("Identifier: ");
+ for (i = 0; i < 9; i++) {
+ si[i] = 0;
+ for (j = 0; j < 8; j++) {
+ int bit;
+ mdelay(1);
+ inb(0x203);
+ mdelay(1);
+ bit = inb(0x203);
+ dprintk("%02X ", bit);
+ si[i] |= bit << j;
+ }
+ mdelay(10);
+ dprintk("%02X ", si[i]);
+ }
+ dprintk("\n");
+}
+
+static inline void rwa010_global_init(void)
+{
+ WRITE_RWA(6, 2); // Assign a card no = 2
+
+ dprintk("Card no = %d\n", inb(0x203));
+
+ WRITE_RWA(7, 3);
+ WRITE_RWA(0x30, 0);
+
+ WRITE_RWA(7, 4);
+ WRITE_RWA(0x30, 0);
+
+ WRITE_RWA(7, 2);
+ WRITE_RWA(0x30, 0);
+}
+
+static inline void rwa010_game_port_init(void)
+{
+ int i;
+
+ WRITE_RWA(7, 5);
+
+ dprintk("Slider base: ");
+ WRITE_RWA(0x61, 1);
+ i = inb(0x203);
+
+ WRITE_RWA(0x60, 2);
+ dprintk("%02X%02X (201)\n", inb(0x203), i);
+
+ WRITE_RWA(0x30, 1);
+}
+
+static inline void rwa010_waveartist_init(int base, int irq, int dma)
+{
+ int i;
+
+ WRITE_RWA(7, 0);
+
+ dprintk("WaveArtist base: ");
+ WRITE_RWA(0x61, base);
+ i = inb(0x203);
+
+ WRITE_RWA(0x60, base >> 8);
+ dprintk("%02X%02X (%X),", inb(0x203), i, base);
+
+ WRITE_RWA(0x70, irq);
+ dprintk(" irq: %d (%d),", inb(0x203), irq);
+
+ WRITE_RWA(0x74, dma);
+ dprintk(" dma: %d (%d)\n", inb(0x203), dma);
+
+ WRITE_RWA(0x30, 1);
+}
+
+static inline void rwa010_soundblaster_init(int sb_base, int al_base, int irq, int dma)
+{
+ int i;
+
+ WRITE_RWA(7, 1);
+
+ dprintk("SoundBlaster base: ");
+ WRITE_RWA(0x61, sb_base);
+ i = inb(0x203);
+
+ WRITE_RWA(0x60, sb_base >> 8);
+ dprintk("%02X%02X (%X),", inb(0x203), i, sb_base);
+
+ dprintk(" irq: ");
+ WRITE_RWA(0x70, irq);
+ dprintk("%d (%d),", inb(0x203), irq);
+
+ dprintk(" 8-bit DMA: ");
+ WRITE_RWA(0x74, dma);
+ dprintk("%d (%d)\n", inb(0x203), dma);
+
+ dprintk("AdLib base: ");
+ WRITE_RWA(0x63, al_base);
+ i = inb(0x203);
+
+ WRITE_RWA(0x62, al_base >> 8);
+ dprintk("%02X%02X (%X)\n", inb(0x203), i, al_base);
+
+ WRITE_RWA(0x30, 1);
+}
+
+static void rwa010_soundblaster_reset(void)
+{
+ int i;
+
+ outb(1, 0x226);
+ udelay(3);
+ outb(0, 0x226);
+
+ for (i = 0; i < 5; i++) {
+ if (inb(0x22e) & 0x80)
+ break;
+ mdelay(1);
+ }
+ if (i == 5)
+ printk("SoundBlaster: DSP reset failed\n");
+
+ dprintk("SoundBlaster DSP reset: %02X (AA)\n", inb(0x22a));
+
+ for (i = 0; i < 5; i++) {
+ if ((inb(0x22c) & 0x80) == 0)
+ break;
+ mdelay(1);
+ }
+
+ if (i == 5)
+ printk("SoundBlaster: DSP not ready\n");
+ else {
+ outb(0xe1, 0x22c);
+
+ dprintk("SoundBlaster DSP id: ");
+ i = inb(0x22a);
+ udelay(1);
+ i |= inb(0x22a) << 8;
+ dprintk("%04X\n", i);
+
+ for (i = 0; i < 5; i++) {
+ if ((inb(0x22c) & 0x80) == 0)
+ break;
+ mdelay(1);
+ }
+
+ if (i == 5)
+ printk("SoundBlaster: could not turn speaker off\n");
+
+ outb(0xd3, 0x22c);
+ }
+
+ /* turn on OPL3 */
+ outb(5, 0x38a);
+ outb(1, 0x38b);
+}
+
+__initfunc(static void rwa010_init(void))
+{
+ rwa010_unlock();
+ rwa010_read_ident();
+ rwa010_global_init();
+ rwa010_game_port_init();
+ rwa010_waveartist_init(0x250, 3, 7);
+ rwa010_soundblaster_init(0x220, 0x388, 3, 1);
+ rwa010_soundblaster_reset();
+}
+
+EXPORT_SYMBOL(gpio_lock);
+EXPORT_SYMBOL(gpio_modify_op);
+EXPORT_SYMBOL(gpio_modify_io);
+EXPORT_SYMBOL(cpld_modify);
+
+#endif
+
+#ifdef CONFIG_LEDS
+#define DEFAULT_LEDS 0
+#else
+#define DEFAULT_LEDS GPIO_GREEN_LED
+#endif
+
+__initfunc(void hw_init(void))
+{
+#ifdef CONFIG_ARCH_NETWINDER
+ /*
+ * this ought to have a better home...
+ * Since this calls the above routines, which are
+ * compiled only if CONFIG_ARCH_NETWINDER is set,
+ * these should only be parsed by the compiler
+ * in the same circumstance.
+ */
+ if (machine_is_netwinder()) {
+ unsigned long flags;
+
+ wb977_init();
+ cpld_init();
+ rwa010_init();
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ gpio_modify_op(GPIO_RED_LED|GPIO_GREEN_LED, DEFAULT_LEDS);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ }
+#endif
+
+ leds_event(led_start);
+}
*/
#include <linux/delay.h>
+#include <linux/errno.h>
#include <asm/system.h>
-#include <asm/io.h>
#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/ioc.h>
+
+#define FORCE_ONES 0xdc
/*
* if delay loop has been calibrated then us that,
* else use IOC timer 1.
*/
-static void iic_delay (void)
+static void iic_delay(void)
{
extern unsigned long loops_per_sec;
if (loops_per_sec != (1 << 12)) {
- udelay(10);
+ udelay(100); /* was 10 */
return;
} else {
unsigned long flags;
outb(255, IOC_T1LTCHH);
outb(0, IOC_T1GO);
outb(1<<6, IOC_IRQCLRA); /* clear T1 irq */
- outb(4, IOC_T1LTCHL);
+ outb(10, IOC_T1LTCHL); /* was 4 */
outb(0, IOC_T1LTCHH);
outb(0, IOC_T1GO);
while ((inb(IOC_IRQSTATA) & (1<<6)) == 0);
}
}
-static inline void iic_start (void)
+#define IIC_INIT() dat = (inb(IOC_CONTROL) | FORCE_ONES) & ~3
+#define IIC_SET_DAT outb(dat|=1, IOC_CONTROL);
+#define IIC_CLR_DAT outb(dat&=~1, IOC_CONTROL);
+#define IIC_SET_CLK outb(dat|=2, IOC_CONTROL);
+#define IIC_CLR_CLK outb(dat&=~2, IOC_CONTROL);
+#define IIC_DELAY iic_delay();
+#define IIC_READ_DATA() (inb(IOC_CONTROL) & 1)
+
+static inline void iic_set_lines(int clk, int dat)
{
- unsigned char out;
+ int old;
- out = inb(IOC_CONTROL) | 0xc2;
+ old = inb(IOC_CONTROL) | FORCE_ONES;
- outb(out, IOC_CONTROL);
- iic_delay();
+ old &= ~3;
+
+ if (clk)
+ old |= 2;
+ if (dat)
+ old |= 1;
+
+ outb(old, IOC_CONTROL);
- outb(out ^ 1, IOC_CONTROL);
iic_delay();
}
-static inline void iic_stop (void)
+static inline unsigned int iic_read_data(void)
{
- unsigned char out;
+ return inb(IOC_CONTROL) & 1;
+}
- out = inb(IOC_CONTROL) | 0xc3;
+/*
+ * C: ==~~_
+ * D: =~~__
+ */
+static inline void iic_start(void)
+{
+ unsigned int dat;
- iic_delay();
- outb(out ^ 1, IOC_CONTROL);
+ IIC_INIT();
- iic_delay();
- outb(out, IOC_CONTROL);
+ IIC_SET_DAT
+ IIC_DELAY
+ IIC_SET_CLK
+ IIC_DELAY
+
+ IIC_CLR_DAT
+ IIC_DELAY
+ IIC_CLR_CLK
+ IIC_DELAY
}
-static int iic_sendbyte (unsigned char b)
+/*
+ * C: __~~
+ * D: =__~
+ */
+static inline void iic_stop(void)
{
- unsigned char out, in;
- int i;
+ unsigned int dat;
- out = (inb(IOC_CONTROL) & 0xfc) | 0xc0;
+ IIC_INIT();
- outb(out, IOC_CONTROL);
- for (i = 7; i >= 0; i--) {
- unsigned char c;
- c = out | ((b & (1 << i)) ? 1 : 0);
+ IIC_CLR_DAT
+ IIC_DELAY
+ IIC_SET_CLK
+ IIC_DELAY
+ IIC_SET_DAT
+ IIC_DELAY
+}
- outb(c, IOC_CONTROL);
- iic_delay();
+/*
+ * C: __~_
+ * D: =___
+ */
+static inline void iic_acknowledge(void)
+{
+ unsigned int dat;
- outb(c | 2, IOC_CONTROL);
- iic_delay();
+ IIC_INIT();
- outb(c, IOC_CONTROL);
- }
- outb(out | 1, IOC_CONTROL);
- iic_delay();
+ IIC_CLR_DAT
+ IIC_DELAY
+ IIC_SET_CLK
+ IIC_DELAY
+ IIC_CLR_CLK
+ IIC_DELAY
+}
- outb(out | 3, IOC_CONTROL);
- iic_delay();
+/*
+ * C: __~_
+ * D: =~H~
+ */
+static inline int iic_is_acknowledged(void)
+{
+ unsigned int dat, ack_bit;
- in = inb(IOC_CONTROL) & 1;
+ IIC_INIT();
- outb(out | 1, IOC_CONTROL);
- iic_delay();
+ IIC_SET_DAT
+ IIC_DELAY
+ IIC_SET_CLK
+ IIC_DELAY
- outb(out, IOC_CONTROL);
- iic_delay();
+ ack_bit = IIC_READ_DATA();
+
+ IIC_CLR_CLK
+ IIC_DELAY
+
+ return ack_bit == 0;
+}
+
+/*
+ * C: _~__~__~__~__~__~__~__~_
+ * D: =DDXDDXDDXDDXDDXDDXDDXDD
+ */
+static void iic_sendbyte(unsigned int b)
+{
+ unsigned int dat, i;
+
+ IIC_INIT();
+
+ for (i = 0; i < 8; i++) {
+ if (b & 128)
+ IIC_SET_DAT
+ else
+ IIC_CLR_DAT
+ IIC_DELAY
+
+ IIC_SET_CLK
+ IIC_DELAY
+ IIC_CLR_CLK
+ IIC_DELAY
- if(in) {
- printk("No acknowledge from RTC\n");
- return 1;
- } else
- return 0;
+ b <<= 1;
+ }
}
-static unsigned char iic_recvbyte (void)
+/*
+ * C: __~_~_~_~_~_~_~_~_
+ * D: =~HHHHHHHHHHHHHHHH
+ */
+static unsigned char iic_recvbyte(void)
{
- unsigned char out, in;
- int i;
+ unsigned int dat, i, in;
- out = (inb(IOC_CONTROL) & 0xfc) | 0xc0;
+ IIC_INIT();
+
+ IIC_SET_DAT
+ IIC_DELAY
- outb(out, IOC_CONTROL);
in = 0;
- for (i = 7; i >= 0; i--) {
- outb(out | 1, IOC_CONTROL);
- iic_delay();
- outb(out | 3, IOC_CONTROL);
- iic_delay();
- in = (in << 1) | (inb(IOC_CONTROL) & 1);
- outb(out | 1, IOC_CONTROL);
- iic_delay();
+ for (i = 0; i < 8; i++) {
+ IIC_SET_CLK
+ IIC_DELAY
+
+ in = (in << 1) | IIC_READ_DATA();
+
+ IIC_CLR_CLK
+ IIC_DELAY
}
- outb(out, IOC_CONTROL);
- iic_delay();
- outb(out | 2, IOC_CONTROL);
- iic_delay();
return in;
}
-void iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len)
+int iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len)
{
- iic_start();
+ int i, err = -EIO;
- if (iic_sendbyte(addr & 0xfe))
+ iic_start();
+ iic_sendbyte(addr & 0xfe);
+ if (!iic_is_acknowledged())
goto error;
- if (iic_sendbyte(loc))
+ iic_sendbyte(loc);
+ if (!iic_is_acknowledged())
goto error;
if (addr & 1) {
- int i;
-
- for (i = 0; i < len; i++)
- if (iic_sendbyte (buf[i]))
- break;
- } else {
- int i;
-
iic_stop();
iic_start();
iic_sendbyte(addr|1);
- for (i = 0; i < len; i++)
- buf[i] = iic_recvbyte ();
+ if (!iic_is_acknowledged())
+ goto error;
+
+ for (i = 0; i < len - 1; i++) {
+ buf[i] = iic_recvbyte();
+ iic_acknowledge();
+ }
+ buf[i] = iic_recvbyte();
+ } else {
+ for (i = 0; i < len; i++) {
+ iic_sendbyte(buf[i]);
+
+ if (!iic_is_acknowledged())
+ goto error;
+ }
}
+
+ err = 0;
error:
iic_stop();
+
+ return err;
}
static struct vm_area_struct init_mmap = INIT_MMAP;
static struct fs_struct init_fs = INIT_FS;
+static struct file * init_fd_array[NR_OPEN] = { NULL, };
static struct files_struct init_files = INIT_FILES;
static struct signal_struct init_signals = INIT_SIGNALS;
-struct mm_struct init_mm = INIT_MM;
+struct mm_struct init_mm = INIT_MM(init_mm);
/*
* Initial task structure.
*
* The things we do for performance..
*/
-union task_union init_task_union __attribute__((__section__(".init.task"))) = { INIT_TASK };
+union task_union init_task_union __attribute__((__section__(".init.task"))) =
+ { INIT_TASK(init_task_union.task) };
+++ /dev/null
-/*
- * linux/arch/arm/kernel/ioport.c
- *
- * Io-port support is not used for ARM
- */
-
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/ioport.h>
-
-/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
-/*asmlinkage void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
-{
-}*/
-
-asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
-{
- return -ENOSYS;
-}
-
-asmlinkage int sys_iopl(long ebx,long ecx,long edx,
- long esi, long edi, long ebp, long eax, long ds,
- long es, long fs, long gs, long orig_eax,
- long eip,long cs,long eflags,long esp,long ss)
-{
- return -ENOSYS;
-}
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
-#include <linux/timex.h>
#include <linux/malloc.h>
#include <linux/random.h>
#include <linux/smp.h>
#include <asm/hardware.h>
#include <asm/io.h>
-#include <asm/pgtable.h>
#include <asm/system.h>
#ifndef SMP
#define cliIF()
#endif
+/*
+ * Maximum IRQ count. Currently, this is arbitary.
+ * However, it should not be set too low to prevent
+ * false triggering. Conversely, if it is set too
+ * high, then you could miss a stuck IRQ.
+ *
+ * Maybe we ought to set a timer and re-enable the
+ * IRQ at a later time?
+ */
+#define MAX_IRQ_CNT 100000
+
unsigned int local_bh_count[NR_CPUS];
unsigned int local_irq_count[NR_CPUS];
spinlock_t irq_controller_lock;
+int setup_arm_irq(int, struct irqaction *);
extern int get_fiq_list(char *);
extern void init_FIQ(void);
unsigned int probing : 1; /* IRQ in use for a probe */
unsigned int probe_ok : 1; /* IRQ can be used for probe */
unsigned int valid : 1; /* IRQ claimable */
- unsigned int unused :26;
+ unsigned int noautoenable : 1; /* don't automatically enable IRQ */
+ unsigned int unused :25;
void (*mask_ack)(unsigned int irq); /* Mask and acknowledge IRQ */
void (*mask)(unsigned int irq); /* Mask IRQ */
void (*unmask)(unsigned int irq); /* Unmask IRQ */
struct irqaction *action;
- unsigned int unused2[3];
+ /*
+ * IRQ lock detection
+ */
+ unsigned int lck_cnt;
+ unsigned int lck_pc;
+ unsigned int lck_jif;
};
static struct irqdesc irq_desc[NR_IRQS];
+/*
+ * Get architecture specific interrupt handlers
+ * and interrupt initialisation.
+ */
+#include <asm/arch/irq.h>
+
/*
* Dummy mask/unmask handler
*/
spin_lock_irqsave(&irq_controller_lock, flags);
cliIF();
- irq_desc[irq].enabled = 1;
irq_desc[irq].probing = 0;
irq_desc[irq].triggered = 0;
- irq_desc[irq].unmask(irq);
+ if (!irq_desc[irq].noautoenable) {
+ irq_desc[irq].enabled = 1;
+ irq_desc[irq].unmask(irq);
+ }
spin_unlock_irqrestore(&irq_controller_lock, flags);
}
*p++ = '\n';
}
-#ifdef CONFIG_ACORN
+#ifdef CONFIG_ARCH_ACORN
p += get_fiq_list(p);
#endif
return p - buf;
}
+/*
+ * IRQ lock detection.
+ *
+ * Hopefully, this should get us out of a few locked situations.
+ * However, it may take a while for this to happen, since we need
+ * a large number if IRQs to appear in the same jiffie with the
+ * same instruction pointer (or within 2 instructions).
+ */
+static void check_irq_lock(struct irqdesc *desc, int irq, struct pt_regs *regs)
+{
+ unsigned long instr_ptr = instruction_pointer(regs);
+
+ if (desc->lck_jif == jiffies &&
+ desc->lck_pc >= instr_ptr && desc->lck_pc < instr_ptr + 8) {
+ desc->lck_cnt += 1;
+
+ if (desc->lck_cnt > MAX_IRQ_CNT) {
+ printk(KERN_ERR "IRQ LOCK: IRQ%d is locking the system, disabled\n", irq);
+ disable_irq(irq);
+ }
+ } else {
+ desc->lck_cnt = 0;
+ desc->lck_pc = instruction_pointer(regs);
+ desc->lck_jif = jiffies;
+ }
+}
+
/*
* do_IRQ handles all normal device IRQ's
*/
asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
{
- struct irqdesc * desc = irq_desc + irq;
+ struct irqdesc * desc;
struct irqaction * action;
int status, cpu;
+ irq = fixup_irq(irq);
+
+ desc = irq_desc + irq;
+
spin_lock(&irq_controller_lock);
desc->mask_ack(irq);
spin_unlock(&irq_controller_lock);
}
}
+ /*
+ * Debug measure - hopefully we can continue if an
+ * IRQ lockup problem occurs...
+ */
+ check_irq_lock(desc, irq, regs);
+
irq_exit(cpu, irq);
/*
* a return code from the irq handler to tell us
* whether the handler wants us to do software bottom
* half handling or not..
- *
- * ** IMPORTANT NOTE: do_bottom_half() ENABLES IRQS!!! **
- * ** WE MUST DISABLE THEM AGAIN, ELSE IDE DISKS GO **
- * ** AWOL **
*/
if (1) {
if (bh_active & bh_mask)
do_bottom_half();
- __cli();
}
}
struct irqaction *old, **p;
unsigned long flags;
- if (new->flags & SA_SAMPLE_RANDOM)
+ /*
+ * Some drivers like serial.c use request_irq() heavily,
+ * so we have to be careful not to interfere with a
+ * running system.
+ */
+ if (new->flags & SA_SAMPLE_RANDOM) {
+ /*
+ * This function might sleep, we want to call it first,
+ * outside of the atomic block.
+ * Yes, this might clear the entropy pool if the wrong
+ * driver is attempted to be loaded, without actually
+ * installing a new handler, but is this really a problem,
+ * only the sysadmin is able to do this.
+ */
rand_initialize_irq(irq);
+ }
+ /*
+ * The following block of code has to be executed atomically
+ */
spin_lock_irqsave(&irq_controller_lock, flags);
-
p = &irq_desc[irq].action;
if ((old = *p) != NULL) {
/* Can't share interrupts unless both agree to */
if (!shared) {
irq_desc[irq].nomask = (new->flags & SA_IRQNOMASK) ? 1 : 0;
- irq_desc[irq].enabled = 1;
irq_desc[irq].probing = 0;
- irq_desc[irq].unmask(irq);
+ if (!irq_desc[irq].noautoenable) {
+ irq_desc[irq].enabled = 1;
+ irq_desc[irq].unmask(irq);
+ }
}
spin_unlock_irqrestore(&irq_controller_lock, flags);
return 0;
}
-/*
- * Using "struct sigaction" is slightly silly, but there
- * are historical reasons and it works well, so..
- */
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
unsigned long irq_flags, const char * devname, void *dev_id)
{
unsigned long retval;
struct irqaction *action;
- if (!irq_desc[irq].valid)
- return -EINVAL;
- if (!handler)
+ if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler)
return -EINVAL;
action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
struct irqaction * action, **p;
unsigned long flags;
- if (!irq_desc[irq].valid) {
+ if (irq >= NR_IRQS || !irq_desc[irq].valid) {
printk(KERN_ERR "Trying to free IRQ%d\n",irq);
#ifdef CONFIG_DEBUG_ERRORS
__backtrace();
#endif
return;
}
+
+ spin_lock_irqsave(&irq_controller_lock, flags);
for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) {
if (action->dev_id != dev_id)
continue;
/* Found it - now free it */
- save_flags_cli (flags);
*p = action->next;
- restore_flags (flags);
kfree(action);
- return;
+ goto out;
}
printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
#ifdef CONFIG_DEBUG_ERRORS
__backtrace();
#endif
+out:
+ spin_unlock_irqrestore(&irq_controller_lock, flags);
}
/* Start the interrupt probing. Unlike other architectures,
continue;
irq_desc[i].probing = 1;
- irq_desc[i].enabled = 1;
irq_desc[i].triggered = 0;
irq_desc[i].unmask(i);
irqs += 1;
*/
spin_lock_irq(&irq_controller_lock);
for (i = 0; i < NR_IRQS; i++) {
- if (irq_desc[i].probing && irq_desc[i].triggered) {
+ if (irq_desc[i].probing &&
+ irq_desc[i].triggered) {
irq_desc[i].probing = 0;
irqs -= 1;
}
int probe_irq_off(unsigned long irqs)
{
unsigned int i;
- int irq_found = -1;
+ int irq_found = NO_IRQ;
/*
* look at the interrupts, and find exactly one
for (i = 0; i < NR_IRQS; i++) {
if (irq_desc[i].probing &&
irq_desc[i].triggered) {
- if (irq_found != -1) {
+ if (irq_found != NO_IRQ) {
irq_found = NO_IRQ;
goto out;
}
irq_found = NO_IRQ;
out:
spin_unlock_irq(&irq_controller_lock);
+
return irq_found;
}
-/*
- * Get architecture specific interrupt handlers
- * and interrupt initialisation.
- */
-#include <asm/arch/irq.h>
-
__initfunc(void init_IRQ(void))
{
extern void init_dma(void);
int irq;
for (irq = 0; irq < NR_IRQS; irq++) {
+ irq_desc[irq].probe_ok = 0;
+ irq_desc[irq].valid = 0;
+ irq_desc[irq].noautoenable = 0;
irq_desc[irq].mask_ack = dummy_mask_unmask_irq;
irq_desc[irq].mask = dummy_mask_unmask_irq;
irq_desc[irq].unmask = dummy_mask_unmask_irq;
*
* - Red - toggles state every 50 timer interrupts
*/
+#include <linux/module.h>
+
#include <asm/hardware.h>
#include <asm/leds.h>
#include <asm/system.h>
-void leds_event(led_event_t ledevt)
+void ebsa110_leds_event(led_event_t ledevt)
{
unsigned long flags;
restore_flags(flags);
}
+
+void (*leds_event)(led_event_t) = ebsa110_leds_event;
+
+EXPORT_SYMBOL(leds_event);
+++ /dev/null
-/*
- * arch/arm/kernel/leds-ebsa285.c
- *
- * Copyright (C) 1998 Russell King
- *
- * EBSA-285 LED control routines. We use the leds as follows:
- *
- * - Green - toggles state every 50 timer interrupts
- * - Amber - On if system is not idle
- * - Red - currently unused
- */
-#include <asm/hardware.h>
-#include <asm/leds.h>
-#include <asm/system.h>
-
-static char led_state = XBUS_LED_RED | XBUS_LED_GREEN;
-
-void leds_event(led_event_t ledevt)
-{
- unsigned long flags;
-
- save_flags_cli(flags);
-
- switch(ledevt) {
- case led_idle_start:
- led_state |= XBUS_LED_AMBER;
- break;
-
- case led_idle_end:
- led_state &= ~XBUS_LED_AMBER;
- break;
-
- case led_timer:
- led_state ^= XBUS_LED_GREEN;
- break;
-
- default:
- break;
- }
-
- restore_flags(flags);
-
- *XBUS_LEDS = led_state;
-}
--- /dev/null
+/*
+ * arch/arm/kernel/leds-footbridge.c
+ *
+ * Copyright (C) 1998-1999 Russell King
+ *
+ * EBSA-285 and NetWinder LED control routines.
+ *
+ * The EBSA-285 uses the leds as follows:
+ * - Green - toggles state every 50 timer interrupts
+ * - Amber - On if system is not idle
+ * - Red - currently unused
+ *
+ * The Netwinder uses the leds as follows:
+ * - Green - toggles state every 50 timer interrupts
+ * - Red - On if the system is not idle
+ *
+ * Changelog:
+ * 02-05-1999 RMK Various cleanups
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/spinlock.h>
+#include <asm/system.h>
+
+#define LED_STATE_ENABLED 1
+#define LED_STATE_CLAIMED 2
+static char led_state;
+static char hw_led_state;
+
+static spinlock_t leds_lock = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_ARCH_EBSA285
+
+static void __ebsa285_text ebsa285_leds_event(led_event_t evt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&leds_lock, flags);
+
+ switch (evt) {
+ case led_start:
+ hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN;
+#ifndef CONFIG_LEDS_IDLE
+ hw_led_state |= XBUS_LED_AMBER;
+#endif
+ led_state |= LED_STATE_ENABLED;
+ break;
+
+ case led_stop:
+ led_state &= ~LED_STATE_ENABLED;
+ break;
+
+ case led_claim:
+ led_state |= LED_STATE_CLAIMED;
+ hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN | XBUS_LED_AMBER;
+ break;
+
+ case led_release:
+ led_state &= ~LED_STATE_CLAIMED;
+ hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN | XBUS_LED_AMBER;
+ break;
+
+#ifdef CONFIG_LEDS_TIMER
+ case led_timer:
+ if (!(led_state & LED_STATE_CLAIMED))
+ hw_led_state ^= XBUS_LED_GREEN;
+ break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+ case led_idle_start:
+ if (!(led_state & LED_STATE_CLAIMED))
+ hw_led_state |= XBUS_LED_RED;
+ break;
+
+ case led_idle_end:
+ if (!(led_state & LED_STATE_CLAIMED))
+ hw_led_state &= ~XBUS_LED_RED;
+ break;
+#endif
+
+ case led_green_on:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state &= ~XBUS_LED_GREEN;
+ break;
+
+ case led_green_off:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state |= XBUS_LED_GREEN;
+ break;
+
+ case led_amber_on:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state &= ~XBUS_LED_AMBER;
+ break;
+
+ case led_amber_off:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state |= XBUS_LED_AMBER;
+ break;
+
+ case led_red_on:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state &= ~XBUS_LED_RED;
+ break;
+
+ case led_red_off:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state |= XBUS_LED_RED;
+ break;
+
+ default:
+ break;
+ }
+
+ if (led_state & LED_STATE_ENABLED)
+ *XBUS_LEDS = hw_led_state;
+
+ spin_unlock_irqrestore(&leds_lock, flags);
+}
+
+#endif
+
+#ifdef CONFIG_ARCH_NETWINDER
+
+static void __netwinder_text netwinder_leds_event(led_event_t evt)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&leds_lock, flags);
+
+ switch (evt) {
+ case led_start:
+ led_state |= LED_STATE_ENABLED;
+ hw_led_state = 0;
+ break;
+
+ case led_stop:
+ led_state &= ~LED_STATE_ENABLED;
+ break;
+
+ case led_claim:
+ led_state |= LED_STATE_CLAIMED;
+ hw_led_state = 0;
+ break;
+
+ case led_release:
+ led_state &= ~LED_STATE_CLAIMED;
+ hw_led_state = 0;
+ break;
+
+#ifdef CONFIG_LEDS_TIMER
+ case led_timer:
+ if (!(led_state & LED_STATE_CLAIMED))
+ hw_led_state ^= GPIO_GREEN_LED;
+ break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+ case led_idle_start:
+ if (!(led_state & LED_STATE_CLAIMED))
+ hw_led_state &= ~GPIO_RED_LED;
+ break;
+
+ case led_idle_end:
+ if (!(led_state & LED_STATE_CLAIMED))
+ hw_led_state |= GPIO_RED_LED;
+ break;
+#endif
+
+ case led_green_on:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state |= GPIO_GREEN_LED;
+ break;
+
+ case led_green_off:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state &= ~GPIO_GREEN_LED;
+ break;
+
+ case led_amber_on:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state |= GPIO_GREEN_LED | GPIO_RED_LED;
+ break;
+
+ case led_amber_off:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state &= ~(GPIO_GREEN_LED | GPIO_RED_LED);
+ break;
+
+ case led_red_on:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state |= GPIO_RED_LED;
+ break;
+
+ case led_red_off:
+ if (led_state & LED_STATE_CLAIMED)
+ hw_led_state &= ~GPIO_RED_LED;
+ break;
+
+ default:
+ break;
+ }
+
+ spin_unlock_irqrestore(&leds_lock, flags);
+
+ if (led_state & LED_STATE_ENABLED) {
+ spin_lock_irqsave(&gpio_lock, flags);
+ gpio_modify_op(GPIO_RED_LED | GPIO_GREEN_LED, hw_led_state);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ }
+}
+
+#endif
+
+static void dummy_leds_event(led_event_t evt)
+{
+}
+
+__initfunc(void
+init_leds_event(led_event_t evt))
+{
+ switch (machine_arch_type) {
+#ifdef CONFIG_ARCH_EBSA285
+ case MACH_TYPE_EBSA285:
+ leds_event = ebsa285_leds_event;
+ break;
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
+ case MACH_TYPE_NETWINDER:
+ leds_event = netwinder_leds_event;
+ break;
+#endif
+
+ default:
+ leds_event = dummy_leds_event;
+ }
+
+ leds_event(evt);
+}
+
+void (*leds_event)(led_event_t) = init_leds_event;
+
+EXPORT_SYMBOL(leds_event);
* (c) David Alan Gilbert 1995/1996
*/
#include <linux/kernel.h>
+#include <linux/init.h>
#include <asm/io.h>
#include <asm/hardware.h>
}
#endif
-void oldlatch_init(void)
+void __init oldlatch_init(void)
{
printk("oldlatch: init\n");
#ifdef LATCHAADDR
#include <linux/init.h>
#include <asm/uaccess.h>
-#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/io.h>
}
/*
- * The idle loop on an arm..
+ * The idle loop on an ARM...
*/
asmlinkage int sys_idle(void)
{
- int ret = -EPERM;
-
- lock_kernel();
if (current->pid != 0)
- goto out;
+ return -EPERM;
+
/* endless idle loop with no priority at all */
- current->priority = -100;
- for (;;)
- {
+ while (1) {
+ if (!current->need_resched && !hlt_counter)
+ proc_idle();
+ current->policy = SCHED_YIELD;
+ schedule();
+#ifndef CONFIG_NO_PGT_CACHE
check_pgt_cache();
-#if 0 //def ARCH_IDLE_OK
- if (!hlt_counter && !current->need_resched)
- proc_idle ();
#endif
- run_task_queue(&tq_scheduler);
- schedule();
}
- ret = 0;
-out:
- unlock_kernel();
- return ret;
}
+static char reboot_mode = 'h';
+
__initfunc(void reboot_setup(char *str, int *ints))
{
+ reboot_mode = str[0];
}
-/*
- * This routine reboots the machine by resetting the expansion cards via
- * their loaders, turning off the processor cache (if ARM3), copying the
- * first instruction of the ROM to 0, and executing it there.
- */
void machine_restart(char * __unused)
{
- proc_hard_reset ();
- arch_hard_reset ();
+ arch_reset(reboot_mode);
+ panic("Reboot failed\n");
+ while (1);
}
void machine_halt(void)
#endif
}
+/*
+ * Task structure and kernel stack allocation.
+ *
+ * Taken from the i386 version.
+ */
+#ifdef CONFIG_CPU_32
+#define EXTRA_TASK_STRUCT 8
+static struct task_struct *task_struct_stack[EXTRA_TASK_STRUCT];
+static int task_struct_stack_ptr = -1;
+#endif
+
+struct task_struct *alloc_task_struct(void)
+{
+ struct task_struct *tsk;
+
+#ifndef EXTRA_TASK_STRUCT
+ tsk = ll_alloc_task_struct();
+#else
+ int index;
+
+ index = task_struct_stack_ptr;
+ if (index >= EXTRA_TASK_STRUCT/2)
+ goto use_cache;
+
+ tsk = ll_alloc_task_struct();
+
+ if (!tsk) {
+ index = task_struct_stack_ptr;
+
+ if (index >= 0) {
+use_cache: tsk = task_struct_stack[index];
+ task_struct_stack_ptr = index - 1;
+ }
+ }
+#endif
+#ifdef CONFIG_SYSRQ
+ /* You need this if you want SYSRQ-T to give sensible stack
+ * usage information
+ */
+ if (tsk) {
+ char *p = (char *)tsk;
+ memzero(p+KERNEL_STACK_SIZE, KERNEL_STACK_SIZE);
+ }
+#endif
+
+ return tsk;
+}
+
+void free_task_struct(struct task_struct *p)
+{
+#ifdef EXTRA_TASK_STRUCT
+ int index = task_struct_stack_ptr + 1;
+
+ if (index < EXTRA_TASK_STRUCT) {
+ task_struct_stack[index] = p;
+ task_struct_stack_ptr = index;
+ } else
+#endif
+ ll_free_task_struct(p);
+}
+
/*
* Free current thread data structures etc..
*/
childregs = ((struct pt_regs *)((unsigned long)p + 8192)) - 1;
*childregs = *regs;
childregs->ARM_r0 = 0;
+ childregs->ARM_sp = esp;
save = ((struct context_save_struct *)(childregs)) - 1;
- copy_thread_css(save);
+ init_thread_css(save);
p->tss.save = save;
return 0;
dump->regs = *regs;
dump->u_fpvalid = dump_fpu (regs, &dump->u_fp);
}
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be free'd until both the parent and the child have exited.
+ */
+pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+{
+ extern int sys_exit(int) __attribute__((noreturn));
+ pid_t __ret;
+
+ __asm__ __volatile__(
+ "mov r0, %1 @ kernel_thread sys_clone\n"
+" mov r1, #0\n"
+ __syscall(clone)"\n"
+" mov %0, r0"
+ : "=r" (__ret)
+ : "Ir" (flags | CLONE_VM) : "r0", "r1");
+ if (__ret == 0)
+ sys_exit((fn)(arg));
+ return __ret;
+}
+
*/
static inline long get_stack_long(struct task_struct *task, int offset)
{
- unsigned char *stack;
+ struct pt_regs *regs;
- stack = (unsigned char *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
- stack += offset << 2;
- return *(unsigned long *)stack;
+ regs = (struct pt_regs *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
+
+ return regs->uregs[offset];
}
/*
static inline long put_stack_long(struct task_struct *task, int offset,
unsigned long data)
{
- unsigned char *stack;
+ struct pt_regs *regs;
+
+ regs = (struct pt_regs *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
+
+ regs->uregs[offset] = data;
- stack = (unsigned char *)((unsigned long)task + 8192 - sizeof(struct pt_regs));
- stack += offset << 2;
- *(unsigned long *) stack = data;
return 0;
}
if (MAP_NR(page) < max_mapnr) {
page += addr & ~PAGE_MASK;
+
+ flush_cache_range(vma->vm_mm, addr, addr + sizeof(unsigned long));
+
*(unsigned long *)page = data;
- __flush_entry_to_ram(page);
+
+ clean_cache_area(page, sizeof(unsigned long));
+
+ set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
+ flush_tlb_page(vma, addr & PAGE_MASK);
}
- set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
- flush_tlb();
}
/*
printk ("=%08lX ", val);
return val;
}
-#undef pc_pointer
-#define pc_pointer(x) ((x) & 0x03fffffc)
+
int ptrace_set_bpt (struct task_struct *child)
{
unsigned long insn, pc, alt;
return 0;
wake_up_process (child);
child->exit_code = SIGKILL;
- ptrace_cancel_bpt (child);
/* make sure single-step breakpoint is gone. */
ptrace_cancel_bpt (child);
ret = 0;
#define SUPPORT_CPU_SA110
#endif
-#ifndef CONFIG_CMDLINE
-#define CONFIG_CMDLINE "root=/dev/nfs rw"
-#endif
#define MEM_SIZE (16*1024*1024)
#define COMMAND_LINE_SIZE 256
+#ifndef CONFIG_CMDLINE
+#define CONFIG_CMDLINE ""
+#endif
+
+extern void reboot_setup(char *str, int *ints);
+extern void fpe_init(void);
+extern void disable_hlt(void);
+
struct drive_info_struct { char dummy[32]; } drive_info;
struct screen_info screen_info = {
orig_video_lines: 30,
/*-- Match -- --- Mask -- -- Manu -- Processor uname -m --- ELF STUFF ---
--- processor asm funcs --- */
#if defined(CONFIG_CPU_26)
+ /* ARM2 fake ident */
{ 0x41560200, 0xfffffff0, "ARM/VLSI", "arm2" , "armv1" , "v1", 0,
&arm2_processor_functions },
+ /* ARM250 fake ident */
{ 0x41560250, 0xfffffff0, "ARM/VLSI", "arm250" , "armv2" , "v2", HWCAP_SWP,
&arm250_processor_functions },
+ /* ARM3 processors */
{ 0x41560300, 0xfffffff0, "ARM/VLSI", "arm3" , "armv2" , "v2", HWCAP_SWP,
&arm3_processor_functions },
#elif defined(CONFIG_CPU_32)
#ifdef SUPPORT_CPU_ARM6
+ /* ARM6 */
{ 0x41560600, 0xfffffff0, "ARM/VLSI", "arm6" , "armv3" , "v3", HWCAP_SWP,
&arm6_processor_functions },
+ /* ARM610 */
{ 0x41560610, 0xfffffff0, "ARM/VLSI", "arm610" , "armv3" , "v3", HWCAP_SWP,
&arm6_processor_functions },
#endif
#ifdef SUPPORT_CPU_ARM7
+ /* ARM7's have a strange numbering */
{ 0x41007000, 0xffffff00, "ARM/VLSI", "arm7" , "armv3" , "v3", HWCAP_SWP,
&arm7_processor_functions },
/* ARM710 IDs are non-standard */
&arm7_processor_functions },
#endif
#ifdef SUPPORT_CPU_SA110
- { 0x4401a100, 0xfffffff0, "DEC", "sa110" , "armv4" , "v3", HWCAP_SWP|HWCAP_HALF,
+#ifdef CONFIG_ARCH_RPC
+ /* Acorn RiscPC's can't handle ARMv4 half-word instructions */
+ { 0x4401a100, 0xfffffff0, "Intel", "sa110" , "armv4" , "v4", HWCAP_SWP,
+ &sa110_processor_functions },
+#else
+ { 0x4401a100, 0xfffffff0, "Intel", "sa110" , "armv4" , "v4", HWCAP_SWP|HWCAP_HALF,
&sa110_processor_functions },
#endif
+#endif
#endif
{ 0x00000000, 0x00000000, "***", "unknown", "unknown", "**", 0, NULL }
};
* From head-armv.S
*/
unsigned int processor_id;
-unsigned int machine_type;
+unsigned int __machine_arch_type;
int armidindex;
extern int root_mountflags;
* symbol to be empty if not configured.
*/
-/*
- * Risc-PC specific initialisation
- */
-#ifdef CONFIG_ARCH_RPC
-
-#include <asm/arch/mmu.h>
-
-unsigned int vram_half_sam;
-
-static void
-setup_rpc(struct param_struct *params)
-{
- extern void init_dram_banks(const struct param_struct *params);
-
- init_dram_banks(params);
-
- switch (params->u1.s.pages_in_vram) {
- case 256:
- vram_half_sam = 1024;
- break;
- case 512:
- default:
- vram_half_sam = 2048;
- }
-}
-#else
-#define setup_rpc(x)
-#endif
-
-#ifdef PARAMS_BASE
-
-#ifdef CONFIG_ARCH_ACORN
-int memc_ctrl_reg;
-int number_ide_drives;
-int number_mfm_drives;
-#endif
-
-static struct param_struct *params = (struct param_struct *)PARAMS_BASE;
-
-__initfunc(static char *
-setup_params(unsigned long *mem_end_p))
-{
- ROOT_DEV = to_kdev_t(params->u1.s.rootdev);
- ORIG_X = params->u1.s.video_x;
- ORIG_Y = params->u1.s.video_y;
- ORIG_VIDEO_COLS = params->u1.s.video_num_cols;
- ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
-
-#ifdef CONFIG_ARCH_ACORN
-#ifndef CONFIG_FB
- {
- extern int bytes_per_char_h;
- extern int bytes_per_char_v;
-
- bytes_per_char_h = params->u1.s.bytes_per_char_h;
- bytes_per_char_v = params->u1.s.bytes_per_char_v;
- }
-#endif
- memc_ctrl_reg = params->u1.s.memc_control_reg;
- number_ide_drives = (params->u1.s.adfsdrives >> 6) & 3;
- number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
-
- setup_rpc(params);
-
- if (!(params->u1.s.flags & FLAG_READONLY))
- root_mountflags &= ~MS_RDONLY;
-#endif
-#ifdef CONFIG_BLK_DEV_RAM
- {
- extern int rd_doload;
- extern int rd_prompt;
- extern int rd_image_start;
-
- rd_image_start = params->u1.s.rd_start;
- rd_prompt = (params->u1.s.flags & FLAG_RDPROMPT) == 0;
- rd_doload = (params->u1.s.flags & FLAG_RDLOAD) == 0;
- }
-#endif
-
-#ifdef CONFIG_ARCH_ACORN
- *mem_end_p = GET_MEMORY_END(params);
-#elif defined(CONFIG_ARCH_EBSA285)
- *mem_end_p = PAGE_OFFSET + params->u1.s.page_size * params->u1.s.nr_pages;
-#else
- *mem_end_p = PAGE_OFFSET + MEM_SIZE;
-#endif
-
- return params->commandline;
-}
-
-#else
-
-static char default_command_line[] __initdata = CONFIG_CMDLINE;
-
-__initfunc(static char *
-setup_params(unsigned long *mem_end_p))
-{
- ROOT_DEV = 0x00ff;
-
-#ifdef CONFIG_BLK_DEV_RAM
- {
- extern int rd_doload;
- extern int rd_prompt;
- extern int rd_image_start;
-
- rd_image_start = 0;
- rd_prompt = 1;
- rd_doload = 1;
- }
-#endif
-
- *mem_end_p = PAGE_OFFSET + MEM_SIZE;
-
- return default_command_line;
-}
-#endif
-
/*
* initial ram disk
*/
#ifdef CONFIG_BLK_DEV_INITRD
-__initfunc(static void
-setup_initrd(const struct param_struct *params))
-{
- if (params->u1.s.initrd_start) {
- initrd_start = params->u1.s.initrd_start;
- initrd_end = initrd_start + params->u1.s.initrd_size;
- } else {
- initrd_start = 0;
- initrd_end = 0;
- }
-}
-
__initfunc(static void
check_initrd(unsigned long mem_start, unsigned long mem_end))
{
}
#else
-#define setup_initrd(p)
#define check_initrd(ms,me)
#endif
armidlist[armidindex].mask)
armidindex += 1;
- if (armidlist[armidindex].id == 0) {
-#ifdef CONFIG_ARCH_ACORN
- int i;
-
- for (i = 0; i < 3200; i++)
- ((unsigned long *)SCREEN2_BASE)[i] = 0x77113322;
-#endif
+ if (armidlist[armidindex].id == 0)
while (1);
- }
processor = *armidlist[armidindex].proc;
processor._proc_init();
}
+static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
static char command_line[COMMAND_LINE_SIZE] = { 0, };
char saved_command_line[COMMAND_LINE_SIZE];
__initfunc(static void
-setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_end))
+setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_sz))
{
- char c, *to = command_line;
+ char c = ' ', *to = command_line;
int len = 0;
*mem_start = (unsigned long)&_end;
for (;;) {
- if (cmd_line[0] == ' ' &&
- cmd_line[1] == 'm' &&
- cmd_line[2] == 'e' &&
- cmd_line[3] == 'm' &&
- cmd_line[4] == '=') {
- *mem_end = simple_strtoul(cmd_line+5, &cmd_line, 0);
- switch(*cmd_line) {
- case 'M':
- case 'm':
- *mem_end <<= 10;
- case 'K':
- case 'k':
- *mem_end <<= 10;
+ if (c == ' ') {
+ if (cmd_line[0] == 'm' &&
+ cmd_line[1] == 'e' &&
+ cmd_line[2] == 'm' &&
+ cmd_line[3] == '=') {
+ *mem_sz = simple_strtoul(cmd_line+4, &cmd_line, 0);
+ switch(*cmd_line) {
+ case 'M':
+ case 'm':
+ *mem_sz <<= 10;
+ case 'K':
+ case 'k':
+ *mem_sz <<= 10;
+ cmd_line++;
+ }
+ }
+ /* if there are two spaces, remove one */
+ if (*cmd_line == ' ') {
cmd_line++;
+ continue;
}
- *mem_end = *mem_end + PAGE_OFFSET;
}
c = *cmd_line++;
if (!c)
}
*to = '\0';
+
+ /* remove trailing spaces */
+ while (*--to == ' ' && to != command_line)
+ *to = '\0';
+}
+
+__initfunc(static void
+setup_ram(int doload, int prompt, int image_start))
+{
+#ifdef CONFIG_BLK_DEV_RAM
+ extern int rd_doload;
+ extern int rd_prompt;
+ extern int rd_image_start;
+
+ rd_image_start = image_start;
+ rd_prompt = prompt;
+ rd_doload = doload;
+#endif
}
+/*
+ * initial ram disk
+ */
+__initfunc(static void
+setup_initrd(unsigned int start, unsigned int size))
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (start) {
+ initrd_start = start;
+ initrd_end = start + size;
+ } else {
+ initrd_start = 0;
+ initrd_end = 0;
+ }
+#endif
+}
+
+#ifdef CONFIG_ARCH_ACORN
+int memc_ctrl_reg;
+int number_mfm_drives;
+unsigned int vram_size;
+#endif
+
+#ifndef PARAMS_BASE
+#define PARAMS_BASE NULL
+#endif
+
+static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } };
+#define ENDIANNESS ((char)endian_test.l)
+
__initfunc(void
setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p))
{
+ struct param_struct *params = (struct param_struct *)PARAMS_BASE;
static unsigned char smptrap;
- unsigned long memory_end;
- char endian = 'l';
- char *from;
+ unsigned long memory_end = 0;
+ char *from = NULL;
if (smptrap == 1)
return;
smptrap = 1;
+#if defined(CONFIG_ARCH_ARC)
+ __machine_arch_type = MACH_TYPE_ARCHIMEDES;
+#elif defined(CONFIG_ARCH_A5K)
+ __machine_arch_type = MACH_TYPE_A5K;
+#endif
+
setup_processor();
- from = setup_params(&memory_end);
- setup_initrd(params);
+ init_task.mm->start_code = TASK_SIZE;
+ init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext;
+ init_task.mm->end_data = TASK_SIZE + (unsigned long) &_edata;
+ init_task.mm->brk = TASK_SIZE + (unsigned long) &_end;
+
+ /*
+ * Add your machine dependencies here
+ */
+ switch (machine_arch_type) {
+ case MACH_TYPE_EBSA110:
+ /* EBSA110 locks if we execute 'wait for interrupt' */
+ disable_hlt();
+ params = NULL;
+ break;
+
+ case MACH_TYPE_EBSA285:
+ if (params) {
+ ORIG_X = params->u1.s.video_x;
+ ORIG_Y = params->u1.s.video_y;
+ ORIG_VIDEO_COLS = params->u1.s.video_num_cols;
+ ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
+ }
+ break;
+
+ case MACH_TYPE_CO285:
+ {
+#if 0
+ extern unsigned long boot_memory_end;
+ extern char boot_command_line[];
+
+ from = boot_command_line;
+ memory_end = boot_memory_end;
+#endif
+ params = NULL;
+ }
+ break;
+
+ case MACH_TYPE_CATS:
+ /* CATS must use soft-reboot */
+ reboot_setup("s", NULL);
+ break;
+
+ case MACH_TYPE_NETWINDER:
+ /*
+ * to be fixed in a future NeTTrom
+ */
+ if (params->u1.s.page_size == 4096) {
+ if (params->u1.s.nr_pages != 0x2000 &&
+ params->u1.s.nr_pages != 0x4000) {
+ printk("Warning: bad NeTTrom parameters detected, using defaults\n");
+ /*
+ * This stuff doesn't appear to be initialised
+ * properly by NeTTrom 2.0.6 and 2.0.7
+ */
+ params->u1.s.nr_pages = 0x2000; /* 32MB */
+ params->u1.s.ramdisk_size = 0;
+ params->u1.s.flags = FLAG_READONLY;
+ params->u1.s.initrd_start = 0;
+ params->u1.s.initrd_size = 0;
+ params->u1.s.rd_start = 0;
+ params->u1.s.video_x = 0;
+ params->u1.s.video_y = 0;
+ params->u1.s.video_num_cols = 80;
+ params->u1.s.video_num_rows = 30;
+ }
+ } else {
+ printk("Warning: no NeTTrom parameter page detected, using "
+ "compiled-in settings\n");
+ params = NULL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (params) {
+ memory_end = params->u1.s.page_size *
+ params->u1.s.nr_pages;
+
+ ROOT_DEV = to_kdev_t(params->u1.s.rootdev);
+
+ setup_ram((params->u1.s.flags & FLAG_RDLOAD) == 0,
+ (params->u1.s.flags & FLAG_RDPROMPT) == 0,
+ params->u1.s.rd_start);
+
+ setup_initrd(params->u1.s.initrd_start,
+ params->u1.s.initrd_size);
+
+ if (!(params->u1.s.flags & FLAG_READONLY))
+ root_mountflags &= ~MS_RDONLY;
+
+#ifdef CONFIG_ARCH_ACORN
+#ifdef CONFIG_ARCH_RPC
+ {
+ extern void init_dram_banks(struct param_struct *);
+ init_dram_banks(params);
+ }
+#endif
+
+ memc_ctrl_reg = params->u1.s.memc_control_reg;
+ number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3;
+ vram_size = 0;
+
+ switch (params->u1.s.pages_in_vram) {
+ case 512:
+ vram_size += PAGE_SIZE * 256;
+ case 256:
+ vram_size += PAGE_SIZE * 256;
+ default:
+ break;
+ }
+
+ memory_end -= vram_size;
+#endif
+
+ from = params->commandline;
+ } else {
+ ROOT_DEV = 0x00ff;
+
+ setup_ram(1, 1, 0);
+ setup_initrd(0, 0);
+ }
+
+ if (!memory_end)
+ memory_end = MEM_SIZE;
+
+ if (!from)
+ from = default_command_line;
+
+#ifdef CONFIG_NWFPE
+ fpe_init();
+#endif
/* Save unparsed command line copy for /proc/cmdline */
memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
setup_mem(from, memory_start_p, &memory_end);
- check_initrd(*memory_start_p, memory_end);
- init_task.mm->start_code = TASK_SIZE;
- init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext;
- init_task.mm->end_data = TASK_SIZE + (unsigned long) &_edata;
- init_task.mm->brk = TASK_SIZE + (unsigned long) &_end;
+ memory_end += PAGE_OFFSET;
- *cmdline_p = command_line;
- *memory_end_p = memory_end;
+ check_initrd(*memory_start_p, memory_end);
- sprintf(system_utsname.machine, "%s%c", armidlist[armidindex].arch_vsn, endian);
- sprintf(elf_platform, "%s%c", armidlist[armidindex].elf_vsn, endian);
+ sprintf(system_utsname.machine, "%s%c", armidlist[armidindex].arch_vsn, ENDIANNESS);
+ sprintf(elf_platform, "%s%c", armidlist[armidindex].elf_vsn, ENDIANNESS);
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
+
+ *cmdline_p = command_line;
+ *memory_end_p = memory_end;
}
-static const struct {
- char *machine_name;
- char *bus_name;
-} machine_desc[] = {
- { "DEC-EBSA110", "DEC" },
- { "Acorn-RiscPC", "Acorn" },
- { "Nexus-NexusPCI", "PCI" },
- { "DEC-EBSA285", "PCI" },
- { "Corel-Netwinder", "PCI/ISA" },
- { "Chalice-CATS", "PCI" },
- { "unknown-TBOX", "PCI" }
+static const char *machine_desc[] = {
+ "EBSA110",
+ "Acorn-RiscPC",
+ "unknown",
+ "Nexus-FTV/PCI",
+ "EBSA285",
+ "Corel-NetWinder",
+ "Chalice-CATS",
+ "unknown-TBOX",
+ "co-EBSA285",
+ "CL-PS7110",
+ "Acorn-Archimedes",
+ "Acorn-A5000"
};
-#if defined(CONFIG_ARCH_ARC)
-#define HARDWARE "Acorn-Archimedes"
-#define IO_BUS "Acorn"
-#elif defined(CONFIG_ARCH_A5K)
-#define HARDWARE "Acorn-A5000"
-#define IO_BUS "Acorn"
-#endif
-
-#if defined(CONFIG_CPU_ARM2)
-#define OPTIMISATION "ARM2"
-#elif defined(CONFIG_CPU_ARM3)
-#define OPTIMISATION "ARM3"
-#elif defined(CONFIG_CPU_ARM6)
-#define OPTIMISATION "ARM6"
-#elif defined(CONFIG_CPU_ARM7)
-#define OPTIMISATION "ARM7"
-#elif defined(CONFIG_CPU_SA110)
-#define OPTIMISATION "StrongARM"
-#else
-#define OPTIMISATION "unknown"
-#endif
-
int get_cpuinfo(char * buffer)
{
int len;
len = sprintf(buffer,
"Processor\t: %s %s rev %d\n"
"BogoMips\t: %lu.%02lu\n"
- "Hardware\t: %s\n"
- "Optimisation\t: %s\n"
- "IO Bus\t\t: %s\n",
+ "Hardware\t: %s\n",
armidlist[armidindex].manu,
armidlist[armidindex].name,
(int)processor_id & 15,
(loops_per_sec+2500) / 500000,
((loops_per_sec+2500) / 5000) % 100,
-#ifdef HARDWARE
- HARDWARE,
-#else
- machine_desc[machine_type].machine_name,
-#endif
- OPTIMISATION,
-#ifdef IO_BUS
- IO_BUS
-#else
- machine_desc[machine_type].bus_name
-#endif
- );
+ machine_desc[machine_arch_type]);
return len;
}
asmlinkage int sys_wait4(pid_t pid, unsigned long * stat_addr,
int options, unsigned long *ru);
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs);
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
extern int ptrace_cancel_bpt (struct task_struct *);
extern int ptrace_set_bpt (struct task_struct *);
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
- if (do_signal(&saveset, regs))
+ if (do_signal(&saveset, regs, 0))
return regs->ARM_r0;
}
}
while (1) {
current->state = TASK_INTERRUPTIBLE;
schedule();
- if (do_signal(&saveset, regs))
+ if (do_signal(&saveset, regs, 0))
return regs->ARM_r0;
}
}
#ifdef CONFIG_CPU_32
err |= __get_user(regs->ARM_cpsr, &sc->arm_cpsr);
#endif
- if (!valid_user_regs(regs))
- return 1;
- /* send SIGTRAP if we're single-stepping */
- if (ptrace_cancel_bpt (current))
- send_sig (SIGTRAP, current, 1);
+ err |= !valid_user_regs(regs);
return err;
}
struct sigframe *frame;
sigset_t set;
+ /*
+ * Since we stacked the signal on a word boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (regs->ARM_sp & 3)
+ goto badframe;
+
frame = (struct sigframe *)regs->ARM_sp;
if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
if (restore_sigcontext(regs, &frame->sc))
goto badframe;
+ /* Send SIGTRAP if we're single-stepping */
+ if (ptrace_cancel_bpt (current))
+ send_sig(SIGTRAP, current, 1);
+
return regs->ARM_r0;
badframe:
struct rt_sigframe *frame;
sigset_t set;
+ /*
+ * Since we stacked the signal on a word boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (regs->ARM_sp & 3)
+ goto badframe;
+
frame = (struct rt_sigframe *)regs->ARM_sp;
if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
goto badframe;
+ /* Send SIGTRAP if we're single-stepping */
+ if (ptrace_cancel_bpt (current))
+ send_sig(SIGTRAP, current, 1);
+
return regs->ARM_r0;
badframe:
return err;
}
+static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
+ unsigned long framesize)
+{
+ unsigned long sp = regs->ARM_sp;
+
+ /*
+ * This is the X/Open sanctioned signal stack switching.
+ */
+ if ((ka->sa.sa_flags & SA_ONSTACK) && ! on_sig_stack(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+
+ /*
+ * No matter what happens, 'sp' must be word
+ * aligned otherwise nasty things could happen
+ */
+ sp &= ~3;
+
+ return (void *)(sp - framesize);
+}
+
static void setup_frame(int sig, struct k_sigaction *ka,
sigset_t *set, struct pt_regs *regs)
{
unsigned long retcode;
int err = 0;
- frame = (struct sigframe *)regs->ARM_sp - 1;
+ frame = get_sigframe(ka, regs, sizeof(*frame));
- if (!access_ok(VERIFT_WRITE, frame, sizeof (*frame)))
+ if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
goto segv_and_exit;
err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]);
} else {
retcode = (unsigned long)&frame->retcode;
err |= __put_user(SWI_SYS_SIGRETURN, &frame->retcode);
- __flush_entry_to_ram (&frame->retcode);
+ flush_icache_range(retcode, retcode + 4);
}
if (err)
regs->ARM_sp = (unsigned long)frame;
regs->ARM_lr = retcode;
regs->ARM_pc = (unsigned long)ka->sa.sa_handler;
+#if defined(CONFIG_CPU_32)
+ /* Maybe we need to deliver a 32-bit signal to a 26-bit task. */
+ if (ka->sa.sa_flags & SA_THIRTYTWO)
+ regs->ARM_cpsr = USR_MODE;
+#endif
if (valid_user_regs(regs))
return;
unsigned long retcode;
int err = 0;
- frame = (struct rt_sigframe *)regs->ARM_sp - 1;
+ frame = get_sigframe(ka, regs, sizeof(struct rt_sigframe));
+
if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
goto segv_and_exit;
} else {
retcode = (unsigned long)&frame->retcode;
err |= __put_user(SWI_SYS_RT_SIGRETURN, &frame->retcode);
- __flush_entry_to_ram (&frame->retcode);
+ flush_icache_range(retcode, retcode + 4);
}
if (err)
regs->ARM_sp = (unsigned long)frame;
regs->ARM_lr = retcode;
regs->ARM_pc = (unsigned long)ka->sa.sa_handler;
+#if defined(CONFIG_CPU_32)
+ /* Maybe we need to deliver a 32-bit signal to a 26-bit task. */
+ if (ka->sa.sa_flags & SA_THIRTYTWO)
+ regs->ARM_cpsr = USR_MODE;
+#endif
if (valid_user_regs(regs))
return;
* the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that.
*/
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs)
+asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
{
- unsigned long instr, *pc = (unsigned long *)(instruction_pointer(regs)-4);
struct k_sigaction *ka;
siginfo_t info;
- int single_stepping, swi_instr;
+ int single_stepping;
+
+ /*
+ * We want the common case to go fast, which
+ * is why we may in certain cases get here from
+ * kernel mode. Just return without doing anything
+ * if so.
+ */
+ if (!user_mode(regs))
+ return 0;
if (!oldset)
oldset = ¤t->blocked;
single_stepping = ptrace_cancel_bpt (current);
- swi_instr = (!get_user (instr, pc) && (instr & 0x0f000000) == 0x0f000000);
for (;;) {
unsigned long signr;
}
/* Are we from a system call? */
- if (swi_instr) {
+ if (syscall) {
switch (regs->ARM_r0) {
case -ERESTARTNOHAND:
regs->ARM_r0 = -EINTR;
return 1;
}
- if (swi_instr &&
+ if (syscall &&
(regs->ARM_r0 == -ERESTARTNOHAND ||
regs->ARM_r0 == -ERESTARTSYS ||
regs->ARM_r0 == -ERESTARTNOINTR)) {
*/
asmlinkage int sys_fork(struct pt_regs *regs)
{
- int ret;
-
- lock_kernel();
- ret = do_fork(SIGCHLD, regs->ARM_sp, regs);
- unlock_kernel();
-
- return ret;
+ return do_fork(SIGCHLD, regs->ARM_sp, regs);
}
/* Clone a task - this clones the calling program thread.
*/
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs *regs)
{
- int ret;
-
- lock_kernel();
if (!newsp)
newsp = regs->ARM_sp;
- ret = do_fork(clone_flags, newsp, regs);
- unlock_kernel();
- return ret;
+ return do_fork(clone_flags, newsp, regs);
+}
+
+asmlinkage int sys_vfork(struct pt_regs *regs)
+{
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs);
}
/* sys_execve() executes a new program.
time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT;
- sti ();
+ sti();
}
-/*
- * timer_interrupt() needs to keep up the real-time clock,
- * as well as call the "do_timer()" routine every clocktick.
- */
-static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- if (reset_timer ())
- do_timer(regs);
-
- update_rtc ();
-}
-
-static struct irqaction irqtimer = { timer_interrupt, 0, 0, "timer", NULL, NULL};
-
__initfunc(void time_init(void))
{
- xtime.tv_sec = setup_timer();
xtime.tv_usec = 0;
- setup_arm_irq(IRQ_TIMER, &irqtimer);
+ setup_timer();
}
#include <asm/atomic.h>
#include <asm/pgtable.h>
-extern void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret);
extern void c_backtrace (unsigned long fp, int pmode);
extern int ptrace_cancel_bpt (struct task_struct *);
int kstack_depth_to_print = 200;
-static int verify_stack_pointer (unsigned long stackptr, int size)
+/*
+ * Stack pointers should always be within the kernels view of
+ * physical memory. If it is not there, then we can't dump
+ * out any information relating to the stack.
+ */
+static int verify_stack(unsigned long sp)
{
-#ifdef CONFIG_CPU_26
- if (stackptr < 0x02048000 || stackptr + size > 0x03000000)
- return -EFAULT;
-#else
- if (stackptr < PAGE_OFFSET || stackptr + size > (unsigned long)high_memory)
+ if (sp < PAGE_OFFSET || sp > (unsigned long)high_memory)
return -EFAULT;
-#endif
- return 0;
+
+ return 0;
}
/*
static void dump_instr(unsigned long pc, int user)
{
- unsigned long module_start, module_end;
int pmin = -2, pmax = 3, ok = 0;
extern char start_kernel, _etext;
if (!user) {
+ unsigned long module_start, module_end;
+ unsigned long kernel_start, kernel_end;
+
module_start = VMALLOC_START;
module_end = module_start + MODULE_RANGE;
- if ((pc >= (unsigned long) &start_kernel) &&
- (pc <= (unsigned long) &_etext)) {
- if (pc + pmin < (unsigned long) &start_kernel)
- pmin = ((unsigned long) &start_kernel) - pc;
- if (pc + pmax > (unsigned long) &_etext)
- pmax = ((unsigned long) &_etext) - pc;
+ kernel_start = (unsigned long)&start_kernel;
+ kernel_end = (unsigned long)&_etext;
+
+ if (pc >= kernel_start && pc < kernel_end) {
+ if (pc + pmin < kernel_start)
+ pmin = kernel_start - pc;
+ if (pc + pmax > kernel_end)
+ pmax = kernel_end - pc;
ok = 1;
- } else if (pc >= module_start && pc <= module_end) {
+ } else if (pc >= module_start && pc < module_end) {
if (pc + pmin < module_start)
pmin = module_start - pc;
if (pc + pmax > module_end)
printk ("pc not in code space\n");
}
-static void dump_state(char *str, struct pt_regs *regs, int err)
+spinlock_t die_lock;
+
+/*
+ * This function is protected against re-entrancy.
+ */
+void die(const char *str, struct pt_regs *regs, int err)
{
+ struct task_struct *tsk = current;
+
+ spin_lock_irq(&die_lock);
+
console_verbose();
printk("Internal error: %s: %x\n", str, err);
printk("CPU: %d\n", smp_processor_id());
show_regs(regs);
printk("Process %s (pid: %d, stackpage=%08lx)\n",
- current->comm, current->pid, 4096+(unsigned long)current);
+ current->comm, current->pid, 4096+(unsigned long)tsk);
+
+ if (!user_mode(regs)) {
+ unsigned long sp = (unsigned long)(regs + 1);
+ unsigned long fp;
+ int dump_info = 1;
+
+ printk("Stack: ");
+ if (verify_stack(sp)) {
+ printk("invalid kernel stack pointer %08lx", sp);
+ dump_info = 0;
+ } else if (sp < 4096+(unsigned long)tsk)
+ printk("kernel stack pointer underflow");
+ printk("\n");
+
+ if (dump_info)
+ dump_mem(sp - 16, 8192+(unsigned long)tsk);
+
+ dump_info = 1;
+
+ printk("Backtrace: ");
+ fp = regs->ARM_fp;
+ if (!fp) {
+ printk("no frame pointer");
+ dump_info = 0;
+ } else if (verify_stack(fp)) {
+ printk("invalid frame pointer %08lx", fp);
+ dump_info = 0;
+ } else if (fp < 4096+(unsigned long)tsk)
+ printk("frame pointer underflow");
+ printk("\n");
+
+ if (dump_info)
+ c_backtrace(fp, processor_mode(regs));
+
+ dump_instr(instruction_pointer(regs), 0);
+ }
+
+ spin_unlock_irq(&die_lock);
}
-/*
- * This function is protected against kernel-mode re-entrancy. If it
- * is re-entered it will hang the system since we can't guarantee in
- * this case that any of the functions that it calls are safe any more.
- * Even the panic function could be a problem, but we'll give it a go.
- */
-void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret)
+static void die_if_kernel(const char *str, struct pt_regs *regs, int err)
{
- static int died = 0;
- unsigned long cstack, sstack, frameptr;
-
if (user_mode(regs))
return;
- switch (died) {
- case 2:
- while (1);
- case 1:
- died ++;
- panic ("die_if_kernel re-entered. Major kernel corruption. Please reboot me!");
- break;
- case 0:
- died ++;
- break;
- }
-
- dump_state(str, regs, err);
-
- cstack = (unsigned long)(regs + 1);
- sstack = 4096+(unsigned long)current;
-
- printk("Stack: ");
- if (verify_stack_pointer(cstack, 4))
- printk("invalid kernel stack pointer %08lx", cstack);
- else if(cstack > sstack + 4096)
- printk("(sp overflow)");
- else if(cstack < sstack)
- printk("(sp underflow)");
- printk("\n");
-
- dump_mem(cstack - 16, sstack + 4096);
-
- frameptr = regs->ARM_fp;
- if (frameptr) {
- if (verify_stack_pointer (frameptr, 4))
- printk ("Backtrace: invalid frame pointer\n");
- else {
- printk("Backtrace: \n");
- c_backtrace (frameptr, processor_mode(regs));
- }
- }
-
- dump_instr(instruction_pointer(regs), 0);
- died = 0;
- if (ret != -1)
- do_exit (ret);
- else {
- cli ();
- while (1);
- }
+ die(str, regs, err);
}
-void bad_user_access_alignment (const void *ptr)
+void bad_user_access_alignment(const void *ptr)
{
- void *pc;
- __asm__("mov %0, lr\n": "=r" (pc));
- printk (KERN_ERR "bad_user_access_alignment called: ptr = %p, pc = %p\n", ptr, pc);
+ printk(KERN_ERR "bad user access alignment: ptr = %p, pc = %p\n", ptr,
+ __builtin_return_address(0));
current->tss.error_code = 0;
current->tss.trap_no = 11;
- force_sig (SIGBUS, current);
-/* die_if_kernel("Oops - bad user access alignment", regs, mode, SIGBUS);*/
+ force_sig(SIGBUS, current);
+/* die_if_kernel("Oops - bad user access alignment", regs, mode);*/
}
-asmlinkage void do_undefinstr (int address, struct pt_regs *regs, int mode)
+asmlinkage void do_undefinstr(int address, struct pt_regs *regs, int mode)
{
+#ifdef CONFIG_DEBUG_USER
+ printk(KERN_INFO "%s (%d): undefined instruction: pc=%08lx\n",
+ current->comm, current->pid, instruction_pointer(regs));
+#endif
current->tss.error_code = 0;
current->tss.trap_no = 6;
- force_sig (SIGILL, current);
- die_if_kernel("Oops - undefined instruction", regs, mode, SIGILL);
+ force_sig(SIGILL, current);
+ die_if_kernel("Oops - undefined instruction", regs, mode);
}
-asmlinkage void do_excpt (int address, struct pt_regs *regs, int mode)
+asmlinkage void do_excpt(int address, struct pt_regs *regs, int mode)
{
+#ifdef CONFIG_DEBUG_USER
+ printk(KERN_INFO "%s (%d): address exception: pc=%08lx\n",
+ current->comm, current->pid, instruction_pointer(regs));
+#endif
current->tss.error_code = 0;
current->tss.trap_no = 11;
- force_sig (SIGBUS, current);
- die_if_kernel("Oops - address exception", regs, mode, SIGBUS);
+ force_sig(SIGBUS, current);
+ die_if_kernel("Oops - address exception", regs, mode);
}
asmlinkage void do_unexp_fiq (struct pt_regs *regs)
{
#ifndef CONFIG_IGNORE_FIQ
- printk ("Hmm. Unexpected FIQ received, but trying to continue\n");
- printk ("You may have a hardware problem...\n");
+ printk("Hmm. Unexpected FIQ received, but trying to continue\n");
+ printk("You may have a hardware problem...\n");
#endif
}
+/*
+ * bad_mode handles the impossible case in the vectors.
+ * If you see one of these, then it's extremely serious,
+ * and could mean you have buggy hardware. It never
+ * returns, and never tries to sync. We hope that we
+ * can dump out some state information...
+ */
asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode)
{
- printk (KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
- handler[reason],
- processor_modes[proc_mode]);
- die_if_kernel ("Oops", regs, 0, -1);
+ console_verbose();
+
+ printk(KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
+ handler[reason], processor_modes[proc_mode]);
+
+ /*
+ * Dump out the vectors and stub routines
+ */
+ printk(KERN_CRIT "Vectors:\n");
+ dump_mem(0, 0x40);
+ printk(KERN_CRIT "Stubs:\n");
+ dump_mem(0x200, 0x4b8);
+
+ die("Oops", regs, 0);
+ cli();
+ while(1);
}
/*
*/
asmlinkage void math_state_restore (void)
{
- current->used_math = 1;
+ current->used_math = 1;
}
-asmlinkage void arm_syscall (int no, struct pt_regs *regs)
+asmlinkage int arm_syscall (int no, struct pt_regs *regs)
{
switch (no) {
case 0: /* branch through 0 */
force_sig(SIGSEGV, current);
-// if (user_mode(regs)) {
-// dump_state("branch through zero", regs, 0);
-// if (regs->ARM_fp)
-// c_backtrace (regs->ARM_fp, processor_mode(regs));
-// }
- die_if_kernel ("branch through zero", regs, 0, SIGSEGV);
+ die_if_kernel("branch through zero", regs, 0);
break;
case 1: /* SWI_BREAK_POINT */
regs->ARM_pc -= 4; /* Decrement PC by one instruction */
- ptrace_cancel_bpt (current);
- force_sig (SIGTRAP, current);
+ ptrace_cancel_bpt(current);
+ force_sig(SIGTRAP, current);
+ return regs->ARM_r0;
+
+ case 2: /* sys_cacheflush */
+#ifdef CONFIG_CPU_32
+ /* r0 = start, r1 = length, r2 = flags */
+ processor.u.armv3v4._flush_cache_area(regs->ARM_r0,
+ regs->ARM_r1,
+ 1);
+#endif
break;
default:
- printk ("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
- force_sig (SIGILL, current);
+ /* Calls 9f00xx..9f07ff are defined to return -ENOSYS
+ if not implemented, rather than raising SIGILL. This
+ way the calling program can gracefully determine whether
+ a feature is supported. */
+ if (no <= 0x7ff)
+ return -ENOSYS;
+#ifdef CONFIG_DEBUG_USER
+ /* experiance shows that these seem to indicate that
+ * something catastrophic has happened
+ */
+ printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no);
if (user_mode(regs)) {
- show_regs (regs);
- c_backtrace (regs->ARM_fp, processor_mode(regs));
+ show_regs(regs);
+ c_backtrace(regs->ARM_fp, processor_mode(regs));
}
- die_if_kernel ("Oops", regs, no, SIGILL);
+#endif
+ force_sig(SIGILL, current);
+ die_if_kernel("Oops", regs, no);
break;
}
+ return 0;
}
asmlinkage void deferred(int n, struct pt_regs *regs)
{
- dump_state("old system call", regs, n);
- force_sig (SIGILL, current);
+ /* You might think just testing `handler' would be enough, but PER_LINUX
+ * points it to no_lcall7 to catch undercover SVr4 binaries. Gutted.
+ */
+ if (current->personality != PER_LINUX && current->exec_domain->handler) {
+ /* Hand it off to iBCS. The extra parameter and consequent type
+ * forcing is necessary because of the weird ARM calling convention.
+ */
+ void (*handler)(int nr, struct pt_regs *regs) = (void *)current->exec_domain->handler;
+ (*handler)(n, regs);
+ return;
+ }
+
+#ifdef CONFIG_DEBUG_USER
+ printk(KERN_ERR "[%d] %s: old system call.\n", current->pid,
+ current->comm);
+#endif
+ force_sig(SIGILL, current);
}
asmlinkage void arm_malalignedptr(const char *str, void *pc, volatile void *ptr)
{
- printk ("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc);
+ printk("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc);
}
-asmlinkage void arm_invalidptr (const char *function, int size)
+asmlinkage void arm_invalidptr(const char *function, int size)
{
- printk ("Invalid pointer size in %s (PC=%p) size %d\n",
+ printk("Invalid pointer size in %s (pc=%p) size %d\n",
function, __builtin_return_address(0), size);
}
L_TARGET := lib.a
L_OBJS := backtrace.o bitops.o checksum.o delay.o io.o memcpy.o \
- system.o string.o uaccess.o
+ semaphore.o string.o system.o uaccess.o
ifeq ($(PROCESSOR),armo)
L_OBJS += uaccess-armo.o
endif
ifdef CONFIG_ARCH_ACORN
- L_OBJS += loaders.o ll_char_wr.o io-acorn.o
+ L_OBJS += loaders.o io-acorn.o
ifdef CONFIG_ARCH_A5K
L_OBJS += floppydma.o
endif
L_OBJS += io-ebsa110.o
endif
-ifeq ($(MACHINE),vnc)
- L_OBJS += io-ebsa285.o
-endif
-
-ifeq ($(MACHINE),ebsa285)
- L_OBJS += io-ebsa285.o
+ifeq ($(MACHINE),footbridge)
+ L_OBJS += io-footbridge.o
endif
include $(TOPDIR)/Rules.make
checksum.o: constants.h
%.o: %.S
-ifneq ($(CONFIG_BINUTILS_NEW),y)
- $(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.$<.s
- $(CC) $(CFLAGS:-pipe=) -c -o $@ ..tmp.$<.s
- $(RM) ..tmp.$<.s
-else
$(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $<
-endif
LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
ldr r4, [r0], #4
tst r2, #2
- beq Lexit
+ beq Lexit_r4
adcs r3, r3, r4, lsl #16
strb r4, [r1], #1
mov r4, r4, lsr #8
strb r4, [r1], #1
mov r4, r4, lsr #8
- b Lexit
+ b Lexit_r4
Ltoo_small: teq r2, #0
LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
adds r3, r3, ip
strb ip, [r1], #1
strb r8, [r1], #1
-Lexit: tst r2, #1
-Ltoo_small1: ldrneb ip, [r0], #1
- strneb ip, [r1], #1
- adcnes r3, r3, ip
+ tst r2, #1
+Ltoo_small1: ldrneb r4, [r0], #1
+Lexit_r4: tst r2, #1
+ strneb r4, [r1], #1
+ andne r4, r4, #255
+ adcnes r3, r3, r4
adcs r0, r3, #0
LOADREGS(ea,fp,{r4 - r8, fp, sp, pc})
adceq r0, r3, #0
LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
tst r2, #2
- beq Lexit
+ beq Lexit_r4
adcs r3, r3, r4, lsl #16
strb r4, [r1], #1
mov r4, r4, lsr #8
strb r4, [r1], #1
mov r4, r4, lsr #8
- b Lexit
+ b Lexit_r4
Lsrc2_aligned: mov r4, r4, lsr #16
adds r3, r3, #0
adceq r0, r3, #0
LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
tst r2, #2
- beq Lexit
+ beq Lexit_r4
adcs r3, r3, r4, lsl #16
strb r4, [r1], #1
mov r4, r4, lsr #8
strb r4, [r1], #1
ldrb r4, [r0], #1
- b Lexit
+ b Lexit_r4
Lsrc3_aligned: mov r4, r4, lsr #24
adds r3, r3, #0
adceq r0, r3, #0
LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc})
tst r2, #2
- beq Lexit
+ beq Lexit_r4
adcs r3, r3, r4, lsl #16
strb r4, [r1], #1
ldr r4, [r0], #4
strb r4, [r1], #1
adcs r3, r3, r4, lsl #24
mov r4, r4, lsr #8
- b Lexit
+ b Lexit_r4
ENTRY(__csum_ipv6_magic)
stmfd sp!, {lr}
strb r12, [r11, #-4]
subs pc, lr, #4
SYMBOL_NAME(floppy_fiqout_end):
-
-@ Params:
-@ r0 = length
-@ r1 = address
-@ r2 = floppy port
-@ Puts these into R9_fiq, R10_fiq, R11_fiq
-ENTRY(floppy_fiqsetup)
- mov ip, sp
- stmfd sp!, {fp, ip, lr, pc}
- sub fp, ip, #4
- MODE(r3,ip,I_BIT|F_BIT|DEFAULT_FIQ) @ disable FIQs, IRQs, FIQ mode
- mov r0, r0
- mov r9, r0
- mov r10, r1
- mov r11, r2
- RESTOREMODE(r3) @ back to normal
- mov r0, r0
- LOADREGS(ea,fp,{fp, sp, pc})
-
-ENTRY(floppy_fiqresidual)
- mov ip, sp
- stmfd sp!, {fp, ip, lr, pc}
- sub fp, ip, #4
- MODE(r3,ip,I_BIT|F_BIT|DEFAULT_FIQ) @ disable FIQs, IRQs, FIQ mode
- mov r0, r0
- mov r0, r9
- RESTOREMODE(r3)
- mov r0, r0
- LOADREGS(ea,fp,{fp, sp, pc})
unsigned long PAGE_CLEAN = _PAGE_CLEAN;
#endif
+#ifdef PTE_TYPE_SMALL
+unsigned long HPTE_TYPE_SMALL = PTE_TYPE_SMALL;
+unsigned long HPTE_AP_READ = PTE_AP_READ;
+unsigned long HPTE_AP_WRITE = PTE_AP_WRITE;
+#endif
+
+#ifdef L_PTE_PRESENT
+unsigned long LPTE_PRESENT = L_PTE_PRESENT;
+unsigned long LPTE_YOUNG = L_PTE_YOUNG;
+unsigned long LPTE_BUFFERABLE = L_PTE_BUFFERABLE;
+unsigned long LPTE_CACHEABLE = L_PTE_CACHEABLE;
+unsigned long LPTE_USER = L_PTE_USER;
+unsigned long LPTE_WRITE = L_PTE_WRITE;
+unsigned long LPTE_EXEC = L_PTE_EXEC;
+unsigned long LPTE_DIRTY = L_PTE_DIRTY;
+#endif
+
unsigned long KSWI_BASE = 0x900000;
unsigned long KSWI_SYS_BASE = 0x9f0000;
unsigned long SYS_ERROR0 = 0x9f0000;
.text
.align
-#define OUT(reg) \
- mov r8, reg, lsl $16 ;\
- orr r8, r8, r8, lsr $16 ;\
- str r8, [r3, r0, lsl $2] ;\
- mov r8, reg, lsr $16 ;\
- orr r8, r8, r8, lsl $16 ;\
- str r8, [r3, r0, lsl $2]
-
-#define IN(reg) \
- ldr reg, [r0] ;\
- and reg, reg, ip ;\
- ldr lr, [r0] ;\
- orr reg, reg, lr, lsl $16
-
- .equ pcio_base_high, PCIO_BASE & 0xff000000
- .equ pcio_base_low, PCIO_BASE & 0x00ff0000
- .equ io_base_high, IO_BASE & 0xff000000
- .equ io_base_low, IO_BASE & 0x00ff0000
-
- .equ addr_io_diff_hi, pcio_base_high - io_base_high
- .equ addr_io_diff_lo, pcio_base_low - io_base_low
-
- .macro addr reg, off
- tst \off, #0x80000000
- .if addr_io_diff_hi
- movne \reg, #IO_BASE
- moveq \reg, #pcio_base_high
- .if pcio_base_low
- addeq \reg, \reg, #pcio_base_low
- .endif
- .else
- mov \reg, #IO_BASE
- addeq \reg, \reg, #addr_io_diff_lo
- .endif
+ .equ diff_pcio_base, PCIO_BASE - IO_BASE
+
+ .macro outw2 rd
+ mov r8, \rd, lsl #16
+ orr r8, r8, r8, lsr #16
+ str r8, [r3, r0, lsl #2]
+ mov r8, \rd, lsr #16
+ orr r8, r8, r8, lsl #16
+ str r8, [r3, r0, lsl #2]
+ .endm
+
+ .macro inw2 rd, mask, temp
+ ldr \rd, [r0]
+ and \rd, \rd, \mask
+ ldr \temp, [r0]
+ orr \rd, \rd, \temp, lsl #16
.endm
-@ Purpose: read a block of data from a hardware register to memory.
-@ Proto : insw(int from_port, void *to, int len_in_words);
-@ Proto : inswb(int from_port, void *to, int len_in_bytes);
-@ Notes : increment to
+ .macro addr rd
+ tst \rd, #0x80000000
+ mov \rd, \rd, lsl #2
+ add \rd, \rd, #IO_BASE
+ addeq \rd, \rd, #diff_pcio_base
+ .endm
+
+.iosw_bad_align_msg:
+ .ascii "insw: bad buffer alignment (%p), called from %08lX\n\0"
+.iosl_warning:
+ .ascii "<4>insl/outsl not implemented, called from %08lX\0"
+ .align
+
+/*
+ * These make no sense on Acorn machines.
+ * Print a warning message.
+ */
+ENTRY(insl)
+ENTRY(outsl)
+ adr r0, .iosl_warning
+ mov r1, lr
+ b SYMBOL_NAME(printk)
+
+.iosw_bad_alignment:
+ adr r0, .iosw_bad_align_msg
+ mov r2, lr
+ b SYMBOL_NAME(panic)
+
+
+/* Purpose: read a block of data from a hardware register to memory.
+ * Proto : void insw(int from_port, void *to, int len_in_words);
+ * Notes : increment to, 'to' must be 16-bit aligned
+ */
+
+.insw_align: tst r1, #1
+ bne .iosw_bad_alignment
+
+ ldr r3, [r0]
+ strb r3, [r1], #1
+ mov r3, r3, lsr #8
+ strb r3, [r1], #1
+
+ subs r2, r2, #1
+ bne .insw_aligned
ENTRY(insw)
+ teq r2, #0
+ RETINSTR(moveq,pc,lr)
+ addr r0
+ tst r1, #3
+ bne .insw_align
+
+.insw_aligned: mov ip, #0xff
+ orr ip, ip, ip, lsl #8
+ stmfd sp!, {r4, r5, r6, lr}
+
+ subs r2, r2, #8
+ bmi .no_insw_8
+
+.insw_8_lp: ldr r3, [r0]
+ and r3, r3, ip
+ ldr r4, [r0]
+ orr r3, r3, r4, lsl #16
+
+ ldr r4, [r0]
+ and r4, r4, ip
+ ldr r5, [r0]
+ orr r4, r4, r5, lsl #16
+
+ ldr r5, [r0]
+ and r5, r5, ip
+ ldr r6, [r0]
+ orr r5, r5, r6, lsl #16
+
+ ldr r6, [r0]
+ and r6, r6, ip
+ ldr lr, [r0]
+ orr r6, r6, lr, lsl #16
+
+ stmia r1!, {r3 - r6}
+ subs r2, r2, #8
+ bpl .insw_8_lp
+ tst r2, #7
+ LOADREGS(eqfd, sp!, {r4, r5, r6, pc})
+
+.no_insw_8: tst r2, #4
+ beq .no_insw_4
+
+ ldr r3, [r0]
+ and r3, r3, ip
+ ldr r4, [r0]
+ orr r3, r3, r4, lsl #16
+
+ ldr r4, [r0]
+ and r4, r4, ip
+ ldr r5, [r0]
+ orr r4, r4, r5, lsl #16
+
+ stmia r1!, {r3, r4}
+
+.no_insw_4: tst r2, #2
+ beq .no_insw_2
+
+ ldr r3, [r0]
+ and r3, r3, ip
+ ldr r4, [r0]
+ orr r3, r3, r4, lsl #16
+
+ str r3, [r1], #4
+
+.no_insw_2: tst r2, #1
+ ldrne r3, [r0]
+ strneb r3, [r1], #1
+ movne r3, r3, lsr #8
+ strneb r3, [r1]
+ LOADREGS(fd, sp!, {r4, r5, r6, pc})
+
+@ Purpose: write a block of data from memory to a hardware register.
+@ Proto : outsw(int to_reg, void *from, int len_in_words);
+@ Notes : increments from
+
+.outsw_align: tst r1, #1
+ bne .iosw_bad_alignment
+
+ add r1, r1, #2
+
+ ldr r3, [r1, #-4]
+ mov r3, r3, lsr #16
+ orr r3, r3, r3, lsl #16
+ str r3, [r0]
+ subs r2, r2, #1
+ bne .outsw_aligned
+
+ENTRY(outsw)
+ teq r2, #0
+ RETINSTR(moveq,pc,lr)
+ addr r0
+ tst r1, #3
+ bne .outsw_align
+
+.outsw_aligned: stmfd sp!, {r4, r5, r6, lr}
+
+ subs r2, r2, #8
+ bmi .no_outsw_8
+.outsw_8_lp: ldmia r1!, {r3, r4, r5, r6}
+
+ mov ip, r3, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r3, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r4, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r4, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r5, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r5, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r6, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r6, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ subs r2, r2, #8
+ bpl .outsw_8_lp
+ tst r2, #7
+ LOADREGS(eqfd, sp!, {r4, r5, r6, pc})
+
+.no_outsw_8: tst r2, #4
+ beq .no_outsw_4
+
+ ldmia r1!, {r3, r4}
+
+ mov ip, r3, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r3, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r4, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r4, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+.no_outsw_4: tst r2, #2
+ beq .no_outsw_2
+
+ ldr r3, [r1], #4
+
+ mov ip, r3, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r3, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+.no_outsw_2: tst r2, #1
+
+ ldrne r3, [r1]
+
+ movne ip, r3, lsl #16
+ orrne ip, ip, ip, lsr #16
+ strne ip, [r0]
+
+ LOADREGS(fd, sp!, {r4, r5, r6, pc})
+
+.insb_align: rsb ip, ip, #4
+ cmp ip, r2
+ movgt ip, r2
+ cmp ip, #2
+ ldrb r3, [r0]
+ strb r3, [r1], #1
+ ldrgeb r3, [r0]
+ strgeb r3, [r1], #1
+ ldrgtb r3, [r0]
+ strgtb r3, [r1], #1
+ subs r2, r2, ip
+ bne .insb_aligned
+
+ENTRY(insb)
+ teq r2, #0
+ moveq pc, lr
+ addr r0
+ ands ip, r1, #3
+ bne .insb_align
+
+.insb_aligned: stmfd sp!, {r4 - r6, lr}
+
+ subs r2, r2, #16
+ bmi .insb_no_16
+
+.insb_16_lp: ldrb r3, [r0]
+ ldrb r4, [r0]
+ orr r3, r3, r4, lsl #8
+ ldrb r4, [r0]
+ orr r3, r3, r4, lsl #16
+ ldrb r4, [r0]
+ orr r3, r3, r4, lsl #24
+ ldrb r4, [r0]
+ ldrb r5, [r0]
+ orr r4, r4, r5, lsl #8
+ ldrb r5, [r0]
+ orr r4, r4, r5, lsl #16
+ ldrb r5, [r0]
+ orr r4, r4, r5, lsl #24
+ ldrb r5, [r0]
+ ldrb r6, [r0]
+ orr r5, r5, r6, lsl #8
+ ldrb r6, [r0]
+ orr r5, r5, r6, lsl #16
+ ldrb r6, [r0]
+ orr r5, r5, r6, lsl #24
+ ldrb r6, [r0]
+ ldrb ip, [r0]
+ orr r6, r6, ip, lsl #8
+ ldrb ip, [r0]
+ orr r6, r6, ip, lsl #16
+ ldrb ip, [r0]
+ orr r6, r6, ip, lsl #24
+ stmia r1!, {r3 - r6}
+ subs r2, r2, #16
+ bpl .insb_16_lp
+
+ tst r2, #15
+ LOADREGS(eqfd, sp!, {r4 - r6, pc})
+
+.insb_no_16: tst r2, #8
+ beq .insb_no_8
+
+ ldrb r3, [r0]
+ ldrb r4, [r0]
+ orr r3, r3, r4, lsl #8
+ ldrb r4, [r0]
+ orr r3, r3, r4, lsl #16
+ ldrb r4, [r0]
+ orr r3, r3, r4, lsl #24
+ ldrb r4, [r0]
+ ldrb r5, [r0]
+ orr r4, r4, r5, lsl #8
+ ldrb r5, [r0]
+ orr r4, r4, r5, lsl #16
+ ldrb r5, [r0]
+ orr r4, r4, r5, lsl #24
+ stmia r1!, {r3, r4}
+
+.insb_no_8: tst r2, #4
+ bne .insb_no_4
+
+ ldrb r3, [r0]
+ ldrb r4, [r0]
+ orr r3, r3, r4, lsl #8
+ ldrb r4, [r0]
+ orr r3, r3, r4, lsl #16
+ ldrb r4, [r0]
+ orr r3, r3, r4, lsl #24
+ str r3, [r1], #4
+
+.insb_no_4: ands r2, r2, #3
+ LOADREGS(eqfd, sp!, {r4 - r6, pc})
+ cmp r2, #2
+ ldrb r3, [r0]
+ strb r3, [r1], #1
+ ldrgeb r3, [r0]
+ strgeb r3, [r1], #1
+ ldrgtb r3, [r0]
+ strgtb r3, [r1]
+ LOADREGS(fd, sp!, {r4 - r6, pc})
+
+
+
+.outsb_align: rsb ip, ip, #4
+ cmp ip, r2
+ mov ip, r2
+ cmp ip, #2
+ ldrb r3, [r1], #1
+ strb r3, [r0]
+ ldrgeb r3, [r1], #1
+ strgeb r3, [r0]
+ ldrgtb r3, [r1], #1
+ strgtb r3, [r0]
+ subs r2, r2, ip
+ bne .outsb_aligned
+
+ENTRY(outsb)
+ teq r2, #0
+ moveq pc, lr
+ addr r0
+ ands ip, r1, #3
+ bne .outsb_align
+
+.outsb_aligned: stmfd sp!, {r4 - r6, lr}
+
+ subs r2, r2, #16
+ bmi .outsb_no_16
+
+.outsb_16_lp: ldmia r1!, {r3 - r6}
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+
+ strb r4, [r0]
+ mov r4, r4, lsr #8
+ strb r4, [r0]
+ mov r4, r4, lsr #8
+ strb r4, [r0]
+ mov r4, r4, lsr #8
+ strb r4, [r0]
+
+ strb r5, [r0]
+ mov r5, r5, lsr #8
+ strb r5, [r0]
+ mov r5, r5, lsr #8
+ strb r5, [r0]
+ mov r5, r5, lsr #8
+ strb r5, [r0]
+
+ strb r6, [r0]
+ mov r6, r6, lsr #8
+ strb r6, [r0]
+ mov r6, r6, lsr #8
+ strb r6, [r0]
+ mov r6, r6, lsr #8
+ strb r6, [r0]
+ subs r2, r2, #16
+ bpl .outsb_16_lp
+
+ tst r2, #15
+ LOADREGS(eqfd, sp!, {r4 - r6, pc})
+
+.outsb_no_16: tst r2, #8
+ beq .outsb_no_8
+
+ ldmia r1, {r3, r4}
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+
+ strb r4, [r0]
+ mov r4, r4, lsr #8
+ strb r4, [r0]
+ mov r4, r4, lsr #8
+ strb r4, [r0]
+ mov r4, r4, lsr #8
+ strb r4, [r0]
+
+.outsb_no_8: tst r2, #4
+ bne .outsb_no_4
+
+ ldr r3, [r1], #4
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+ mov r3, r3, lsr #8
+ strb r3, [r0]
+
+.outsb_no_4: ands r2, r2, #3
+ LOADREGS(eqfd, sp!, {r4 - r6, pc})
+ cmp r2, #2
+ ldrb r3, [r1], #1
+ strb r3, [r0]
+ ldrgeb r3, [r1], #1
+ strgeb r3, [r0]
+ ldrgtb r3, [r1]
+ strgtb r3, [r0]
+ LOADREGS(fd, sp!, {r4 - r6, pc})
+
+
+
+
+@ Purpose: write a memc register
+@ Proto : void memc_write(int register, int value);
+@ Returns: nothing
+
+#if defined(CONFIG_CPU_26)
+ENTRY(memc_write)
+ cmp r0, #7
+ RETINSTR(movgt,pc,lr)
+ mov r0, r0, lsl #17
+ mov r1, r1, lsl #15
+ mov r1, r1, lsr #17
+ orr r0, r0, r1, lsl #2
+ add r0, r0, #0x03600000
+ strb r0, [r0]
+ RETINSTR(mov,pc,lr)
+#define CPSR2SPSR(rt)
+#else
+#define CPSR2SPSR(rt) \
+ mrs rt, cpsr; \
+ msr spsr, rt
+#endif
+
+@ Purpose: call an expansion card loader to read bytes.
+@ Proto : char read_loader(int offset, char *card_base, char *loader);
+@ Returns: byte read
+
+ENTRY(ecard_loader_read)
+ stmfd sp!, {r4 - r12, lr}
+ mov r11, r1
+ mov r1, r0
+ CPSR2SPSR(r0)
+ mov lr, pc
+ mov pc, r2
+ LOADREGS(fd, sp!, {r4 - r12, pc})
+
+@ Purpose: call an expansion card loader to reset the card
+@ Proto : void read_loader(int card_base, char *loader);
+@ Returns: byte read
+
+ENTRY(ecard_loader_reset)
+ stmfd sp!, {r4 - r12, lr}
+ mov r11, r0
+ CPSR2SPSR(r0)
+ mov lr, pc
+ add pc, r1, #8
+ LOADREGS(fd, sp!, {r4 - r12, pc})
+
+
+#if 0
mov r2, r2, lsl#1
-ENTRY(inswb)
mov ip, sp
stmfd sp!, {r4 - r10, fp, ip, lr, pc}
sub fp, ip, #4
bgt Linsw_notaligned
LOADREGS(ea, fp, {r4 - r10, fp, sp, pc})
-@ Purpose: write a block of data from memory to a hardware register.
-@ Proto : outsw(int to_reg, void *from, int len_in_words);
-@ Proto : outswb(int to_reg, void *from, int len_in_bytes);
-@ Notes : increments from
ENTRY(outsw)
- mov r2, r2, LSL#1
-ENTRY(outswb)
+ mov r2, r2, lsl#1
mov ip, sp
stmfd sp!, {r4 - r8, fp, ip, lr, pc}
sub fp, ip, #4
bgt 3b
LOADREGS(ea, fp, {r4 - r8, fp, sp, pc})
-/*
- * These make no sense on Acorn machines atm.
- */
-ENTRY(insl)
-ENTRY(outsl)
- RETINSTR(mov,pc,lr)
-
-@ Purpose: write a memc register
-@ Proto : void memc_write(int register, int value);
-@ Returns: nothing
-
-#if defined(CONFIG_CPU_26)
-ENTRY(memc_write)
- cmp r0, #7
- RETINSTR(movgt,pc,lr)
- mov r0, r0, lsl #17
- mov r1, r1, lsl #15
- mov r1, r1, lsr #17
- orr r0, r0, r1, lsl #2
- add r0, r0, #0x03600000
- strb r0, [r0]
- RETINSTR(mov,pc,lr)
-#define CPSR2SPSR(rt)
-#else
-#define CPSR2SPSR(rt) \
- mrs rt, cpsr; \
- msr spsr, rt
#endif
-@ Purpose: call an expansion card loader to read bytes.
-@ Proto : char read_loader(int offset, char *card_base, char *loader);
-@ Returns: byte read
-
-ENTRY(ecard_loader_read)
- stmfd sp!, {r4 - r12, lr}
- mov r11, r1
- mov r1, r0
- CPSR2SPSR(r0)
- mov lr, pc
- mov pc, r2
- LOADREGS(fd, sp!, {r4 - r12, pc})
-
-@ Purpose: call an expansion card loader to reset the card
-@ Proto : void read_loader(int card_base, char *loader);
-@ Returns: byte read
-
-ENTRY(ecard_loader_reset)
- stmfd sp!, {r4 - r12, lr}
- mov r11, r0
- CPSR2SPSR(r0)
- mov lr, pc
- add pc, r1, #8
- LOADREGS(fd, sp!, {r4 - r12, pc})
ldr lr, [r0] ;\
orr reg, reg, lr, lsl $16
+/*
+ * These make no sense on these machines.
+ * Print a warning message.
+ */
+ENTRY(insl)
+ENTRY(outsl)
+ENTRY(insb)
+ENTRY(outsb)
+ adr r0, io_long_warning
+ mov r1, lr
+ b SYMBOL_NAME(printk)
+
+io_long_warning:
+ .ascii "<4>ins?/outs? not implemented on this architecture\0"
+ .align
+
@ Purpose: read a block of data from a hardware register to memory.
@ Proto : insw(int from_port, void *to, int len_in_words);
@ Proto : inswb(int from_port, void *to, int len_in_bytes);
+++ /dev/null
-#include <linux/linkage.h>
-
-ENTRY(insl)
- add r0, r0, #0xff000000
- add r0, r0, #0x00e00000
- ands ip, r1, #3
- bne 2f
-
-1: ldr r3, [r0]
- str r3, [r1], #4
- subs r2, r2, #1
- bne 1b
- mov pc, lr
-
-2: cmp ip, #2
- ldr ip, [r0]
- blt 3f
- bgt 4f
-
- strh ip, [r1], #2
- mov ip, ip, lsr #16
-1: subs r2, r2, #1
- ldrne r3, [r0]
- orrne ip, ip, r3, lsl #16
- strne ip, [r1], #4
- movne ip, r3, lsr #16
- bne 1b
- strh ip, [r1], #2
- mov pc, lr
-
-3: strb ip, [r1], #1
- mov ip, ip, lsr #8
- strh ip, [r1], #2
- mov ip, ip, lsr #16
-1: subs r2, r2, #1
- ldrne r3, [r0]
- orrne ip, ip, r3, lsl #8
- strne ip, [r1], #4
- movne ip, r3, lsr #24
- bne 1b
- strb ip, [r1], #1
- mov pc, lr
-
-4: strb ip, [r1], #1
- mov ip, ip, lsr #8
-1: subs r2, r2, #1
- ldrne r3, [r0]
- orrne ip, ip, r3, lsl #24
- strne ip, [r1], #4
- movne ip, r3, lsr #8
- bne 1b
- strb ip, [r1], #1
- mov ip, ip, lsr #8
- strh ip, [r1], #2
- mov pc, lr
-
-ENTRY(outsl)
- add r0, r0, #0xff000000
- add r0, r0, #0x00e00000
- ands ip, r1, #3
- bne 2f
-
-1: ldr r3, [r1], #4
- str r3, [r0]
- subs r2, r2, #1
- bne 1b
- mov pc, lr
-
-2: bic r1, r1, #3
- cmp ip, #2
- ldr ip, [r1], #4
- mov ip, ip, lsr #16
- blt 3f
- bgt 4f
-
-1: ldr r3, [r1], #4
- orr ip, ip, r3, lsl #16
- str ip, [r0]
- mov ip, r3, lsr #16
- subs r2, r2, #1
- bne 1b
- mov pc, lr
-
-3: ldr r3, [r1], #4
- orr ip, ip, r3, lsl #8
- str ip, [r0]
- mov ip, r3, lsr #24
- subs r2, r2, #1
- bne 3b
- mov pc, lr
-
-4: ldr r3, [r1], #4
- orr ip, ip, r3, lsl #24
- str ip, [r0]
- mov ip, r3, lsr #8
- subs r2, r2, #1
- bne 4b
- mov pc, lr
-
- /* Nobody could say these are optimal, but not to worry. */
-
-ENTRY(outswb)
- mov r2, r2, lsr #1
-ENTRY(outsw)
- add r0, r0, #0xff000000
- add r0, r0, #0x00e00000
-1: subs r2, r2, #1
- ldrgeh r3, [r1], #2
- strgeh r3, [r0]
- bgt 1b
- mov pc, lr
-
-ENTRY(inswb)
- mov r2, r2, lsr #1
-ENTRY(insw)
- stmfd sp!, {r4, r5, lr}
- add r0, r0, #0xff000000
- add r0, r0, #0x00e00000
- @ + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17
- subs ip, r2, #8
- blo too_little
- @ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
- ands lr, r1, #3 @ check alignment
- beq 1f
-
- ldrh r3, [r0]
- strh r3, [r1], #2
- sub ip, ip, #1
- cmn ip, #8
- blo too_little
-
-1: ldrh r2, [r0]
- ldrh r3, [r0]
- orr r2, r2, r3, lsl #16
- ldrh r3, [r0]
- ldrh r4, [r0]
- orr r3, r3, r4, lsl #16
- ldrh r4, [r0]
- ldrh r5, [r0]
- orr r4, r4, r5, lsl #16
- ldrh r5, [r0]
- ldrh lr, [r0]
- orr r5, r5, lr, lsl #16
- stmia r1!, {r2, r3, r4, r5}
- subs ip, ip, #8
- @ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 + 0 + 1
- bhs 1b
- @ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 8 - 7
- cmn ip, #4
- ldrhsh r2, [r0] @ ... ... ... ... - 4 - 3 - 2 - 1 ... ...
- ldrhsh r3, [r0]
- orrhs r2, r2, r3, lsl #16
- ldrhsh r3, [r0]
- ldrhsh r4, [r0]
- orrhs r3, r3, r4, lsl #16
- stmhsia r1!, {r2, r3}
-
- tst ip, #2
- ldrneh r2, [r0] @ ... ... - 6 - 5 ... ... - 2 - 1 ... ...
- ldrneh r3, [r0]
- orrne r2, r2, r3, lsl #16
- strne r2, [r1], #4
-
- tst ip, #1
- ldrneh r2, [r0]
- strneh r2, [r1], #2
-
- ldmfd sp!, {r4, r5, pc}
-
-too_little: subs r2, r2, #1
- ldrgeh r3, [r0]
- strgeh r3, [r1], #2
- bgt too_little
-
- ldmfd sp!, {r4, r5, pc}
-
-
-ENTRY(insb)
- add r0, r0, #0xff000000
- add r0, r0, #0x00e00000
-1: teq r2, #0
- ldrneb r3, [r0]
- strneb r3, [r1], #1
- subne r2, r2, #1
- bne 1b
- mov pc, lr
-
-
-ENTRY(outsb)
- add r0, r0, #0xff000000
- add r0, r0, #0x00e00000
-1: teq r2, #0
- ldrneb r3, [r1], #1
- strneb r3, [r0]
- subne r2, r2, #1
- bne 1b
- mov pc, lr
--- /dev/null
+#include <linux/linkage.h>
+#include <asm/hardware.h>
+
+ .equ pcio_high, PCIO_BASE & 0xff000000
+ .equ pcio_low, PCIO_BASE & 0x00ffffff
+
+ .macro ioaddr, rd,rn
+ add \rd, \rn, #pcio_high
+ add \rd, \rd, #pcio_low
+ .endm
+
+ENTRY(insl)
+ ioaddr r0, r0
+ ands ip, r1, #3
+ bne 2f
+
+1: ldr r3, [r0]
+ str r3, [r1], #4
+ subs r2, r2, #1
+ bne 1b
+ mov pc, lr
+
+2: cmp ip, #2
+ ldr ip, [r0]
+ blt 4f
+ bgt 6f
+
+ strh ip, [r1], #2
+ mov ip, ip, lsr #16
+3: subs r2, r2, #1
+ ldrne r3, [r0]
+ orrne ip, ip, r3, lsl #16
+ strne ip, [r1], #4
+ movne ip, r3, lsr #16
+ bne 3b
+ strh ip, [r1], #2
+ mov pc, lr
+
+4: strb ip, [r1], #1
+ mov ip, ip, lsr #8
+ strh ip, [r1], #2
+ mov ip, ip, lsr #16
+5: subs r2, r2, #1
+ ldrne r3, [r0]
+ orrne ip, ip, r3, lsl #8
+ strne ip, [r1], #4
+ movne ip, r3, lsr #24
+ bne 5b
+ strb ip, [r1], #1
+ mov pc, lr
+
+6: strb ip, [r1], #1
+ mov ip, ip, lsr #8
+7: subs r2, r2, #1
+ ldrne r3, [r0]
+ orrne ip, ip, r3, lsl #24
+ strne ip, [r1], #4
+ movne ip, r3, lsr #8
+ bne 7b
+ strb ip, [r1], #1
+ mov ip, ip, lsr #8
+ strh ip, [r1], #2
+ mov pc, lr
+
+ENTRY(outsl)
+ ioaddr r0, r0
+ ands ip, r1, #3
+ bne 2f
+
+1: ldr r3, [r1], #4
+ str r3, [r0]
+ subs r2, r2, #1
+ bne 1b
+ mov pc, lr
+
+2: bic r1, r1, #3
+ cmp ip, #2
+ ldr ip, [r1], #4
+ mov ip, ip, lsr #16
+ blt 4f
+ bgt 5f
+
+3: ldr r3, [r1], #4
+ orr ip, ip, r3, lsl #16
+ str ip, [r0]
+ mov ip, r3, lsr #16
+ subs r2, r2, #1
+ bne 3b
+ mov pc, lr
+
+4: ldr r3, [r1], #4
+ orr ip, ip, r3, lsl #8
+ str ip, [r0]
+ mov ip, r3, lsr #24
+ subs r2, r2, #1
+ bne 4b
+ mov pc, lr
+
+5: ldr r3, [r1], #4
+ orr ip, ip, r3, lsl #24
+ str ip, [r0]
+ mov ip, r3, lsr #8
+ subs r2, r2, #1
+ bne 5b
+ mov pc, lr
+
+ /* Nobody could say these are optimal, but not to worry. */
+
+ENTRY(outswb)
+ mov r2, r2, lsr #1
+ENTRY(outsw)
+ ioaddr r0, r0
+1: subs r2, r2, #1
+ ldrgeh r3, [r1], #2
+ strgeh r3, [r0]
+ bgt 1b
+ mov pc, lr
+
+ENTRY(inswb)
+ mov r2, r2, lsr #1
+ENTRY(insw)
+ stmfd sp!, {r4, r5, lr}
+ ioaddr r0, r0
+ @ + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17
+ subs ip, r2, #8
+ blo too_little
+ @ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9
+ ands lr, r1, #3 @ check alignment
+ beq 1f
+
+ ldrh r3, [r0]
+ strh r3, [r1], #2
+ sub ip, ip, #1
+ cmn ip, #8
+ blo too_little
+
+1: ldrh r2, [r0]
+ ldrh r3, [r0]
+ orr r2, r2, r3, lsl #16
+ ldrh r3, [r0]
+ ldrh r4, [r0]
+ orr r3, r3, r4, lsl #16
+ ldrh r4, [r0]
+ ldrh r5, [r0]
+ orr r4, r4, r5, lsl #16
+ ldrh r5, [r0]
+ ldrh lr, [r0]
+ orr r5, r5, lr, lsl #16
+ stmia r1!, {r2, r3, r4, r5}
+ subs ip, ip, #8
+ @ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 + 0 + 1
+ bhs 1b
+ @ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 8 - 7
+ cmn ip, #4
+ ldrhsh r2, [r0] @ ... ... ... ... - 4 - 3 - 2 - 1 ... ...
+ ldrhsh r3, [r0]
+ orrhs r2, r2, r3, lsl #16
+ ldrhsh r3, [r0]
+ ldrhsh r4, [r0]
+ orrhs r3, r3, r4, lsl #16
+ stmhsia r1!, {r2, r3}
+
+ tst ip, #2
+ ldrneh r2, [r0] @ ... ... - 6 - 5 ... ... - 2 - 1 ... ...
+ ldrneh r3, [r0]
+ orrne r2, r2, r3, lsl #16
+ strne r2, [r1], #4
+
+ tst ip, #1
+ ldrneh r2, [r0]
+ strneh r2, [r1], #2
+
+ ldmfd sp!, {r4, r5, pc}
+
+too_little: subs r2, r2, #1
+ ldrgeh r3, [r0]
+ strgeh r3, [r1], #2
+ bgt too_little
+
+ ldmfd sp!, {r4, r5, pc}
+
+
+ENTRY(insb)
+ ioaddr r0, r0
+1: teq r2, #0
+ ldrneb r3, [r0]
+ strneb r3, [r1], #1
+ subne r2, r2, #1
+ bne 1b
+ mov pc, lr
+
+
+ENTRY(outsb)
+ ioaddr r0, r0
+1: teq r2, #0
+ ldrneb r3, [r1], #1
+ strneb r3, [r0]
+ subne r2, r2, #1
+ bne 1b
+ mov pc, lr
* Copy data from "real" memory space to IO memory space.
* This needs to be optimized.
*/
-void _memcpy_toio(unsigned long to, void * from, unsigned long count)
+void _memcpy_toio(unsigned long to, const void * from, unsigned long count)
{
while (count) {
count--;
+++ /dev/null
-/*
- * linux/arch/arm/lib/ll_char_wr.S
- *
- * Copyright (C) 1995, 1996 Russell King.
- *
- * Speedups & 1bpp code (C) 1996 Philip Blundell & Russell King.
- *
- * 10-04-96 RMK Various cleanups & reduced register usage.
- * 08-04-98 RMK Shifts re-ordered
- */
-
-@ Regs: [] = corruptible
-@ {} = used
-@ () = do not use
-
-#include <linux/linkage.h>
-#include <asm/assembler.h>
- .text
-
-#define BOLD 0x01
-#define ITALIC 0x02
-#define UNDERLINE 0x04
-#define FLASH 0x08
-#define INVERSE 0x10
-
-LC0: .word SYMBOL_NAME(bytes_per_char_h)
- .word SYMBOL_NAME(video_size_row)
- .word SYMBOL_NAME(cmap_80)
- .word SYMBOL_NAME(con_charconvtable)
-
-ENTRY(ll_write_char)
- stmfd sp!, {r4 - r7, lr}
-@
-@ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc)
-@
- eor ip, r1, #UNDERLINE << 9
-/*
- * calculate colours
- */
- tst r1, #INVERSE << 9
- moveq r2, r1, lsr #16
- moveq r3, r1, lsr #24
- movne r2, r1, lsr #24
- movne r3, r1, lsr #16
- and r3, r3, #255
- and r2, r2, #255
-/*
- * calculate offset into character table
- */
- mov r1, r1, lsl #23
- mov r1, r1, lsr #20
-/*
- * calculate offset required for each row [maybe I should make this an argument to this fn.
- * Have to see what the register usage is like in the calling routines.
- */
- adr r4, LC0
- ldmia r4, {r4, r5, r6, lr}
- ldr r4, [r4]
- ldr r5, [r5]
-/*
- * Go to resolution-dependent routine...
- */
- cmp r4, #4
- blt Lrow1bpp
- eor r2, r3, r2 @ Create eor mask to change colour from bg
- orr r3, r3, r3, lsl #8 @ to fg.
- orr r3, r3, r3, lsl #16
- add r0, r0, r5, lsl #3 @ Move to bottom of character
- add r1, r1, #7
- ldrb r7, [r6, r1]
- tst ip, #UNDERLINE << 9
- eoreq r7, r7, #255
- teq r4, #8
- beq Lrow8bpplp
-@
-@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
-@
- orr r3, r3, r3, lsl #4
-Lrow4bpplp: ldr r7, [lr, r7, lsl #2]
- mul r7, r2, r7
- tst r1, #7 @ avoid using r7 directly after
- eor ip, r3, r7
- str ip, [r0, -r5]!
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r1, r1, #1
- ldrb r7, [r6, r1]
- ldr r7, [lr, r7, lsl #2]
- mul r7, r2, r7
- tst r1, #7 @ avoid using r7 directly after
- eor ip, r3, r7
- str ip, [r0, -r5]!
- subne r1, r1, #1
- ldrneb r7, [r6, r1]
- bne Lrow4bpplp
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@
-@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc)
-@
-Lrow8bpplp: mov ip, r7, lsr #4
- ldr ip, [lr, ip, lsl #2]
- mul r4, r2, ip
- and ip, r7, #15 @ avoid r4
- ldr ip, [lr, ip, lsl #2] @ avoid r4
- mul ip, r2, ip @ avoid r4
- eor r4, r3, r4 @ avoid ip
- tst r1, #7 @ avoid ip
- sub r0, r0, r5 @ avoid ip
- eor ip, r3, ip
- stmia r0, {r4, ip}
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r1, r1, #1
- ldrb r7, [r6, r1]
- mov ip, r7, lsr #4
- ldr ip, [lr, ip, lsl #2]
- mul r4, r2, ip
- and ip, r7, #15 @ avoid r4
- ldr ip, [lr, ip, lsl #2] @ avoid r4
- mul ip, r2, ip @ avoid r4
- eor r4, r3, r4 @ avoid ip
- tst r1, #7 @ avoid ip
- sub r0, r0, r5 @ avoid ip
- eor ip, r3, ip
- stmia r0, {r4, ip}
- subne r1, r1, #1
- ldrneb r7, [r6, r1]
- bne Lrow8bpplp
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@
-@ Smashable regs: {r0 - r3}, [r4], {r5, r6}, [r7], (r8 - fp), [ip], (sp), [lr], (pc)
-@
-Lrow1bpp: add r6, r6, r1
- ldmia r6, {r4, r7}
- tst ip, #INVERSE << 9
- mvnne r4, r4
- mvnne r7, r7
- strb r4, [r0], r5
- mov r4, r4, lsr #8
- strb r4, [r0], r5
- mov r4, r4, lsr #8
- strb r4, [r0], r5
- mov r4, r4, lsr #8
- strb r4, [r0], r5
- strb r7, [r0], r5
- mov r7, r7, lsr #8
- strb r7, [r0], r5
- mov r7, r7, lsr #8
- strb r7, [r0], r5
- mov r7, r7, lsr #8
- tst ip, #UNDERLINE << 9
- mvneq r7, r7
- strb r7, [r0], r5
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
- .bss
-ENTRY(con_charconvtable)
- .space 1024
--- /dev/null
+/*
+ * linux/arch/arm/lib/semaphore.S
+ *
+ * Idea from i386 code, Copyright Linus Torvalds.
+ * Converted for ARM by Russell King
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+/*
+ * The semaphore operations have a special calling sequence
+ * that allows us to keep the distruption of the main code
+ * path to a minimum. These routines save and restore the
+ * registers that will be touched by __down etc.
+ */
+ENTRY(__down_failed)
+ stmfd sp!, {r0 - r3, ip, lr}
+ bl SYMBOL_NAME(__down)
+ LOADREGS(fd, sp!, {r0 - r3, ip, pc})
+
+ENTRY(__down_interruptible_failed)
+ stmfd sp!, {r1 - r3, ip, lr}
+ bl SYMBOL_NAME(__down_interruptible)
+ LOADREGS(fd, sp!, {r1 - r3, ip, pc})
+
+ENTRY(__down_trylock_failed)
+ stmfd sp!, {r1 - r3, ip, lr}
+ bl SYMBOL_NAME(__down_trylock)
+ LOADREGS(fd, sp!, {r1 - r3, ip, pc})
+
+ENTRY(__up_wakeup)
+ stmfd sp!, {r0 - r3, ip, lr}
+ bl SYMBOL_NAME(__up)
+ LOADREGS(fd, sp!, {r0 - r3, ip, pc})
set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE));
}
-static void
-kernel_page_fault(unsigned long addr, int mode, struct pt_regs *regs,
- struct task_struct *tsk, struct mm_struct *mm)
+/*
+ * This is useful to dump out the page tables associated with
+ * 'addr' in mm 'mm'.
+ */
+void show_pte(struct mm_struct *mm, unsigned long addr)
{
- char *reason;
- /*
- * Oops. The kernel tried to access some bad page. We'll have to
- * terminate things with extreme prejudice.
- */
pgd_t *pgd;
- if (addr < PAGE_SIZE)
- reason = "NULL pointer dereference";
- else
- reason = "paging request";
-
- printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n",
- reason, addr);
- printk(KERN_ALERT "memmap = %08lX, pgd = %p\n", tsk->tss.memmap, mm->pgd);
pgd = pgd_offset(mm, addr);
printk(KERN_ALERT "*pgd = %08lx", pgd_val(*pgd));
} while(0);
printk("\n");
+}
+
+/*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+static void
+kernel_page_fault(unsigned long addr, int mode, struct pt_regs *regs,
+ struct task_struct *tsk, struct mm_struct *mm)
+{
+ char *reason;
+
+ if (addr < PAGE_SIZE)
+ reason = "NULL pointer dereference";
+ else
+ reason = "paging request";
+
+ printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n",
+ reason, addr);
+ printk(KERN_ALERT "memmap = %08lX, pgd = %p\n", tsk->tss.memmap, mm->pgd);
+ show_pte(mm, addr);
die("Oops", regs, mode);
do_exit(SIGKILL);
{
void * addr;
struct vm_struct * area;
- unsigned long offset;
+ unsigned long offset, last_addr;
+
+ /* Don't allow wraparound or zero size */
+ last_addr = phys_addr + size - 1;
+ if (!size || last_addr < phys_addr)
+ return NULL;
/*
* Mappings have to be page-aligned
*/
offset = phys_addr & ~PAGE_MASK;
- size = PAGE_ALIGN(size + offset);
-
- /*
- * Don't allow mappings that wrap..
- */
- if (!size || size > phys_addr + size)
- return NULL;
+ phys_addr &= PAGE_MASK;
+ size = PAGE_ALIGN(last_addr) - phys_addr;
/*
* Ok, go for it..
LC0: .word SYMBOL_NAME(page_nr)
/*
* Function: arm2_switch_to (struct task_struct *prev, struct task_struct *next)
- *
* Params : prev Old task structure
* : next New task structure for process to run
- *
* Returns : prev
- *
* Purpose : Perform a task switch, saving the old processes state, and restoring
* the new.
- *
* Notes : We don't fiddle with the FP registers here - we postpone this until
* the new task actually uses FP. This way, we don't swap FP for tasks
* that do not require it.
_arm2_proc_fin: movs pc, lr
/*
* Function: arm3_switch_to (struct task_struct *prev, struct task_struct *next)
- *
* Params : prev Old task structure
* : next New task structure for process to run
- *
* Returns : prev
- *
* Purpose : Perform a task switch, saving the old processes state, and restoring
* the new.
- *
* Notes : We don't fiddle with the FP registers here - we postpone this until
* the new task actually uses FP. This way, we don't swap FP for tasks
* that do not require it.
str sp, [r0, #TSS_SAVE] @ Save sp_SVC
ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC
ldr r2, [r1, #TSK_ADDR_LIMIT]
+ ldr r3, [r1, #TSS_MEMMAP] @ Page table pointer
teq r2, #0
moveq r2, #DOM_KERNELDOMAIN
movne r2, #DOM_USERDOMAIN
mcr p15, 0, r2, c3, c0 @ Set domain reg
- ldr r2, [r1, #TSS_MEMMAP] @ Page table pointer
mov r1, #0
mcr p15, 0, r1, c7, c0, 0 @ flush cache
- mcr p15, 0, r2, c2, c0, 0 @ update page table ptr
+ mcr p15, 0, r3, c2, c0, 0 @ update page table ptr
mcr p15, 0, r1, c5, c0, 0 @ flush TLBs
ldmfd sp!, {ip}
msr spsr, ip @ Save tasks CPSR into SPSR for this return
mov r2, #1
_sa110_flush_cache_all_r2:
ldr r3, =Lclean_switch
+ ldr ip, =FLUSH_BASE
ldr r1, [r3]
ands r1, r1, #1
eor r1, r1, #1
str r1, [r3]
- ldr ip, =FLUSH_BASE
addne ip, ip, #32768
add r1, ip, #16384 @ only necessary for 16k
1: ldr r3, [ip], #32
ldr r2, [r0, #TSS_MEMMAP] @ Get old page tables
str sp, [r0, #TSS_SAVE] @ Save sp_SVC
ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC
- ldr r4, [r1, #TSK_ADDR_LIMIT]
- teq r4, #0
- moveq r4, #DOM_KERNELDOMAIN
- movne r4, #DOM_USERDOMAIN
- mcr p15, 0, r4, c3, c0 @ Set segment
+ ldr r5, [r1, #TSK_ADDR_LIMIT]
ldr r4, [r1, #TSS_MEMMAP] @ Page table pointer
+ teq r5, #0
+ moveq r5, #DOM_KERNELDOMAIN
+ movne r5, #DOM_USERDOMAIN
+ mcr p15, 0, r5, c3, c0 @ Set segment
/*
* Flushing the cache is nightmarishly slow, so we take any excuse
* to get out of it. If the old page table is the same as the new,
*/
.align 5
_sa110_set_pmd: str r1, [r0]
- mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns)
+ mcr p15, 0, r0, c7, c10, 1 @ clean D entry
+ mcr p15, 0, r0, c7, c10, 4 @ drain WB (TLB bypasses WB)
mov pc, lr
/*
str r2, [r0] @ hardware version
mov r0, r0
mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns)
+ mcr p15, 0, r0, c7, c10, 4 @ drain WB (TLB bypasses WB)
mov pc, lr
/*
--- /dev/null
+
+/*
+-------------------------------------------------------------------------------
+One of the macros `BIGENDIAN' or `LITTLEENDIAN' must be defined.
+-------------------------------------------------------------------------------
+*/
+#define LITTLEENDIAN
+
+/*
+-------------------------------------------------------------------------------
+The macro `BITS64' can be defined to indicate that 64-bit integer types are
+supported by the compiler.
+-------------------------------------------------------------------------------
+*/
+#define BITS64
+
+/*
+-------------------------------------------------------------------------------
+Each of the following `typedef's defines the most convenient type that holds
+integers of at least as many bits as specified. For example, `uint8' should
+be the most convenient type that can hold unsigned integers of as many as
+8 bits. The `flag' type must be able to hold either a 0 or 1. For most
+implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
+to the same as `int'.
+-------------------------------------------------------------------------------
+*/
+typedef char flag;
+typedef unsigned char uint8;
+typedef signed char int8;
+typedef int uint16;
+typedef int int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+#ifdef BITS64
+typedef unsigned long long int bits64;
+typedef signed long long int sbits64;
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Each of the following `typedef's defines a type that holds integers
+of _exactly_ the number of bits specified. For instance, for most
+implementation of C, `bits16' and `sbits16' should be `typedef'ed to
+`unsigned short int' and `signed short int' (or `short int'), respectively.
+-------------------------------------------------------------------------------
+*/
+typedef unsigned char bits8;
+typedef signed char sbits8;
+typedef unsigned short int bits16;
+typedef signed short int sbits16;
+typedef unsigned int bits32;
+typedef signed int sbits32;
+#ifdef BITS64
+typedef unsigned long long int uint64;
+typedef signed long long int int64;
+#endif
+
+#ifdef BITS64
+/*
+-------------------------------------------------------------------------------
+The `LIT64' macro takes as its argument a textual integer literal and if
+necessary ``marks'' the literal as having a 64-bit integer type. For
+example, the Gnu C Compiler (`gcc') requires that 64-bit literals be
+appended with the letters `LL' standing for `long long', which is `gcc's
+name for the 64-bit integer type. Some compilers may allow `LIT64' to be
+defined as the identity macro: `#define LIT64( a ) a'.
+-------------------------------------------------------------------------------
+*/
+#define LIT64( a ) a##LL
+#endif
+
+/*
+-------------------------------------------------------------------------------
+The macro `INLINE' can be used before functions that should be inlined. If
+a compiler does not support explicit inlining, this macro should be defined
+to be `static'.
+-------------------------------------------------------------------------------
+*/
+#define INLINE extern __inline__
+
+
+/* For use as a GCC soft-float library we need some special function names. */
+
+#ifdef __LIBFLOAT__
+
+/* Some 32-bit ops can be mapped straight across by just changing the name. */
+#define float32_add __addsf3
+#define float32_sub __subsf3
+#define float32_mul __mulsf3
+#define float32_div __divsf3
+#define int32_to_float32 __floatsisf
+#define float32_to_int32_round_to_zero __fixsfsi
+#define float32_to_uint32_round_to_zero __fixunssfsi
+
+/* These ones go through the glue code. To avoid namespace pollution
+ we rename the internal functions too. */
+#define float32_eq ___float32_eq
+#define float32_le ___float32_le
+#define float32_lt ___float32_lt
+
+/* All the 64-bit ops have to go through the glue, so we pull the same
+ trick. */
+#define float64_add ___float64_add
+#define float64_sub ___float64_sub
+#define float64_mul ___float64_mul
+#define float64_div ___float64_div
+#define int32_to_float64 ___int32_to_float64
+#define float64_to_int32_round_to_zero ___float64_to_int32_round_to_zero
+#define float64_to_uint32_round_to_zero ___float64_to_uint32_round_to_zero
+#define float64_to_float32 ___float64_to_float32
+#define float32_to_float64 ___float32_to_float64
+#define float64_eq ___float64_eq
+#define float64_le ___float64_le
+#define float64_lt ___float64_lt
+
+#if 0
+#define float64_add __adddf3
+#define float64_sub __subdf3
+#define float64_mul __muldf3
+#define float64_div __divdf3
+#define int32_to_float64 __floatsidf
+#define float64_to_int32_round_to_zero __fixdfsi
+#define float64_to_uint32_round_to_zero __fixunsdfsi
+#define float64_to_float32 __truncdfsf2
+#define float32_to_float64 __extendsfdf2
+#endif
+
+#endif
--- /dev/null
+1998-11-23 Scott Bambrough <scottb@corelcomputer.com>
+
+ * README.FPE - fix typo in description of lfm/sfm instructions
+ * NOTES - Added file to describe known bugs/problems
+ * fpmodule.c - Changed version number to 0.94
+
+1998-11-20 Scott Bambrough <scottb@corelcomputer.com>
+
+ * README.FPE - fix description of URD, NRM instructions
+ * TODO - remove URD, NRM instructions from TODO list
+ * single_cpdo.c - implement URD, NRM
+ * double_cpdo.c - implement URD, NRM
+ * extended_cpdo.c - implement URD, NRM
+
+1998-11-19 Scott Bambrough <scottb@corelcomputer.com>
+
+ * ChangeLog - Added this file to track changes made.
+ * fpa11.c - added code to initialize register types to typeNone
+ * fpa11_cpdt.c - fixed bug in storeExtended (typeExtended changed to
+ typeDouble in switch statement)
--- /dev/null
+#
+# linux/arch/arm/nwfpe/Makefile
+#
+# Copyright (C) 1998, 1999 Philip Blundell
+#
+
+NWFPE_OBJS := fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o \
+ fpmodule.o fpopcode.o softfloat.o \
+ single_cpdo.o double_cpdo.o extended_cpdo.o
+
+ifeq ($(CONFIG_CPU_26),y)
+NWFPE_OBJS += entry26.o
+else
+NWFPE_OBJS += entry.o
+endif
+
+L_TARGET := math-emu.a
+
+ifeq ($(CONFIG_NWFPE),y)
+L_OBJS = $(NWFPE_OBJS)
+else
+ ifeq ($(CONFIG_NWFPE),m)
+ M_OBJS = nwfpe.o
+ MI_OBJS = $(NWFPE_OBJS)
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+nwfpe.o: $(MI_OBJS) $(MIX_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS)
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 __CONFIG_H__
+#define __CONFIG_H__
+
+#if 1
+#define C_SYMBOL_NAME(foo) foo
+#else
+#define C_SYMBOL_NAME(foo) _##foo
+#endif
+
+#endif
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+
+extern FPA11 *fpa11;
+
+float64 getDoubleConstant(unsigned int);
+
+float64 float64_exp(float64 Fm);
+float64 float64_ln(float64 Fm);
+float64 float64_sin(float64 rFm);
+float64 float64_cos(float64 rFm);
+float64 float64_arcsin(float64 rFm);
+float64 float64_arctan(float64 rFm);
+float64 float64_log(float64 rFm);
+float64 float64_tan(float64 rFm);
+float64 float64_arccos(float64 rFm);
+float64 float64_pow(float64 rFn,float64 rFm);
+float64 float64_pol(float64 rFn,float64 rFm);
+
+unsigned int DoubleCPDO(const unsigned int opcode)
+{
+ float64 rFm, rFn;
+ unsigned int Fd, Fm, Fn, nRc = 1;
+
+ //fp_printk("DoubleCPDO(0x%08x)\n",opcode);
+
+ Fm = getFm(opcode);
+ if (CONSTANT_FM(opcode))
+ {
+ rFm = getDoubleConstant(Fm);
+ }
+ else
+ {
+ switch (fpa11->fpreg[Fm].fType)
+ {
+ case typeSingle:
+ rFm = float32_to_float64(fpa11->fpreg[Fm].fValue.fSingle);
+ break;
+
+ case typeDouble:
+ rFm = fpa11->fpreg[Fm].fValue.fDouble;
+ break;
+
+ case typeExtended:
+ // !! patb
+ //fp_printk("not implemented! why not?\n");
+ //!! ScottB
+ // should never get here, if extended involved
+ // then other operand should be promoted then
+ // ExtendedCPDO called.
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (!MONADIC_INSTRUCTION(opcode))
+ {
+ Fn = getFn(opcode);
+ switch (fpa11->fpreg[Fn].fType)
+ {
+ case typeSingle:
+ rFn = float32_to_float64(fpa11->fpreg[Fn].fValue.fSingle);
+ break;
+
+ case typeDouble:
+ rFn = fpa11->fpreg[Fn].fValue.fDouble;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ Fd = getFd(opcode);
+ /* !! this switch isn't optimized; better (opcode & MASK_ARITHMETIC_OPCODE)>>24, sort of */
+ switch (opcode & MASK_ARITHMETIC_OPCODE)
+ {
+ /* dyadic opcodes */
+ case ADF_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_add(rFn,rFm);
+ break;
+
+ case MUF_CODE:
+ case FML_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_mul(rFn,rFm);
+ break;
+
+ case SUF_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_sub(rFn,rFm);
+ break;
+
+ case RSF_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_sub(rFm,rFn);
+ break;
+
+ case DVF_CODE:
+ case FDV_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_div(rFn,rFm);
+ break;
+
+ case RDF_CODE:
+ case FRD_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_div(rFm,rFn);
+ break;
+
+#if 0
+ case POW_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_pow(rFn,rFm);
+ break;
+
+ case RPW_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_pow(rFm,rFn);
+ break;
+#endif
+
+ case RMF_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_rem(rFn,rFm);
+ break;
+
+#if 0
+ case POL_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_pol(rFn,rFm);
+ break;
+#endif
+
+ /* monadic opcodes */
+ case MVF_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = rFm;
+ break;
+
+ case MNF_CODE:
+ {
+ unsigned int *p = (unsigned int*)&rFm;
+ p[1] ^= 0x80000000;
+ fpa11->fpreg[Fd].fValue.fDouble = rFm;
+ }
+ break;
+
+ case ABS_CODE:
+ {
+ unsigned int *p = (unsigned int*)&rFm;
+ p[1] &= 0x7fffffff;
+ fpa11->fpreg[Fd].fValue.fDouble = rFm;
+ }
+ break;
+
+ case RND_CODE:
+ case URD_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble =
+ int32_to_float64(float64_to_int32(rFm));
+ break;
+
+ case SQT_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_sqrt(rFm);
+ break;
+
+#if 0
+ case LOG_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_log(rFm);
+ break;
+
+ case LGN_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_ln(rFm);
+ break;
+
+ case EXP_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_exp(rFm);
+ break;
+
+ case SIN_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_sin(rFm);
+ break;
+
+ case COS_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_cos(rFm);
+ break;
+
+ case TAN_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_tan(rFm);
+ break;
+
+ case ASN_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_arcsin(rFm);
+ break;
+
+ case ACS_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_arccos(rFm);
+ break;
+
+ case ATN_CODE:
+ fpa11->fpreg[Fd].fValue.fDouble = float64_arctan(rFm);
+ break;
+#endif
+
+ case NRM_CODE:
+ break;
+
+ default:
+ {
+ nRc = 0;
+ }
+ }
+
+ if (0 != nRc) fpa11->fpreg[Fd].fType = typeDouble;
+ return nRc;
+}
+
+#if 0
+float64 float64_exp(float64 rFm)
+{
+ return rFm;
+//series
+}
+
+float64 float64_ln(float64 rFm)
+{
+ return rFm;
+//series
+}
+
+float64 float64_sin(float64 rFm)
+{
+ return rFm;
+//series
+}
+
+float64 float64_cos(float64 rFm)
+{
+ return rFm;
+ //series
+}
+
+#if 0
+float64 float64_arcsin(float64 rFm)
+{
+//series
+}
+
+float64 float64_arctan(float64 rFm)
+{
+ //series
+}
+#endif
+
+float64 float64_log(float64 rFm)
+{
+ return float64_div(float64_ln(rFm),getDoubleConstant(7));
+}
+
+float64 float64_tan(float64 rFm)
+{
+ return float64_div(float64_sin(rFm),float64_cos(rFm));
+}
+
+float64 float64_arccos(float64 rFm)
+{
+return rFm;
+ //return float64_sub(halfPi,float64_arcsin(rFm));
+}
+
+float64 float64_pow(float64 rFn,float64 rFm)
+{
+ return float64_exp(float64_mul(rFm,float64_ln(rFn)));
+}
+
+float64 float64_pol(float64 rFn,float64 rFm)
+{
+ return float64_arctan(float64_div(rFn,rFm));
+}
+#endif
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+ (c) Philip Blundell 1998-1999
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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.
+*/
+
+/* This is the kernel's entry point into the floating point emulator.
+It is called from the kernel with code similar to this:
+
+ adrsvc al, r9, ret_from_exception @ r9 = normal FP return
+ adrsvc al, lr, fpundefinstr @ lr = undefined instr return
+
+ get_current_task r10
+ mov r8, #1
+ strb r8, [r10, #TSK_USED_MATH] @ set current->used_math
+ add r10, r10, #TSS_FPESAVE @ r10 = workspace
+ ldr r4, .LC2
+ ldr pc, [r4] @ Call FP emulator entry point
+
+The kernel expects the emulator to return via one of two possible
+points of return it passes to the emulator. The emulator, if
+successful in its emulation, jumps to ret_from_exception (passed in
+r9) and the kernel takes care of returning control from the trap to
+the user code. If the emulator is unable to emulate the instruction,
+it returns via _fpundefinstr (passed via lr) and the kernel halts the
+user program with a core dump.
+
+On entry to the emulator r10 points to an area of private FP workspace
+reserved in the thread structure for this process. This is where the
+emulator saves its registers across calls. The first word of this area
+is used as a flag to detect the first time a process uses floating point,
+so that the emulator startup cost can be avoided for tasks that don't
+want it.
+
+This routine does three things:
+
+1) It saves SP into a variable called userRegisters. The kernel has
+created a struct pt_regs on the stack and saved the user registers
+into it. See /usr/include/asm/proc/ptrace.h for details. The
+emulator code uses userRegisters as the base of an array of words from
+which the contents of the registers can be extracted.
+
+2) It calls EmulateAll to emulate a floating point instruction.
+EmulateAll returns 1 if the emulation was successful, or 0 if not.
+
+3) If an instruction has been emulated successfully, it looks ahead at
+the next instruction. If it is a floating point instruction, it
+executes the instruction, without returning to user space. In this
+way it repeatedly looks ahead and executes floating point instructions
+until it encounters a non floating point instruction, at which time it
+returns via _fpreturn.
+
+This is done to reduce the effect of the trap overhead on each
+floating point instructions. GCC attempts to group floating point
+instructions to allow the emulator to spread the cost of the trap over
+several floating point instructions. */
+
+ .globl nwfpe_enter
+nwfpe_enter:
+ /* ?? Could put userRegisters and fpa11 into fixed regs during
+ emulation. This would reduce load/store overhead at the expense
+ of stealing two regs from the register allocator. Not sure if
+ it's worth it. */
+ ldr r4, =userRegisters
+ str sp, [r4] @ save pointer to user regs
+ ldr r4, =fpa11
+ str r10, [r4] @ store pointer to our state
+ mov r4, sp @ use r4 for local pointer
+ mov r10, lr @ save the failure-return addresses
+
+ ldr r5, [r4, #60] @ get contents of PC;
+ ldr r0, [r5, #-4] @ get actual instruction into r0
+emulate:
+ bl EmulateAll @ emulate the instruction
+ cmp r0, #0 @ was emulation successful
+ moveq pc, r10 @ no, return failure
+
+next:
+__x1: ldrt r6, [r5], #4 @ get the next instruction and
+ @ increment PC
+
+ and r2, r6, #0x0F000000 @ test for FP insns
+ teq r2, #0x0C000000
+ teqne r2, #0x0D000000
+ teqne r2, #0x0E000000
+ movne pc, r9 @ return ok if not a fp insn
+
+ str r5, [r4, #60] @ update PC copy in regs
+
+ mov r0, r6 @ save a copy
+ ldr r1, [r4, #64] @ fetch the condition codes
+ bl checkCondition @ check the condition
+ cmp r0, #0 @ r0 = 0 ==> condition failed
+
+ @ if condition code failed to match, next insn
+ beq next @ get the next instruction;
+
+ mov r0, r6 @ prepare for EmulateAll()
+ b emulate @ if r0 != 0, goto EmulateAll
+
+ @ We need to be prepared for the instruction at __x1 to fault.
+ @ Emit the appropriate exception gunk to fix things up.
+ .section .fixup,"ax"
+ .align
+__f1: mov pc, r9
+ .previous
+ .section __ex_table,"a"
+ .align 3
+ .long __x1, __f1
+ .previous
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+ (c) Philip Blundell 1998-1999
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "../lib/constants.h"
+
+/* This is the kernel's entry point into the floating point emulator.
+It is called from the kernel with code similar to this:
+
+ mov fp, #0
+ teqp pc, #I_BIT | MODE_SVC
+ ldr r4, .LC2
+ ldr pc, [r4] @ Call FP module USR entry point
+
+The kernel expects the emulator to return via one of two possible
+points of return it passes to the emulator. The emulator, if
+successful in its emulation, jumps to ret_from_exception and the
+kernel takes care of returning control from the trap to the user code.
+If the emulator is unable to emulate the instruction, it returns to
+fpundefinstr and the kernel halts the user program with a core dump.
+
+This routine does four things:
+
+1) It saves SP into a variable called userRegisters. The kernel has
+created a struct pt_regs on the stack and saved the user registers
+into it. See /usr/include/asm/proc/ptrace.h for details. The
+emulator code uses userRegisters as the base of an array of words from
+which the contents of the registers can be extracted.
+
+2) It locates the FP emulator work area within the TSS structure and
+points `fpa11' to it.
+
+3) It calls EmulateAll to emulate a floating point instruction.
+EmulateAll returns 1 if the emulation was successful, or 0 if not.
+
+4) If an instruction has been emulated successfully, it looks ahead at
+the next instruction. If it is a floating point instruction, it
+executes the instruction, without returning to user space. In this
+way it repeatedly looks ahead and executes floating point instructions
+until it encounters a non floating point instruction, at which time it
+returns via _fpreturn.
+
+This is done to reduce the effect of the trap overhead on each
+floating point instructions. GCC attempts to group floating point
+instructions to allow the emulator to spread the cost of the trap over
+several floating point instructions. */
+
+ .globl nwfpe_enter
+nwfpe_enter:
+ ldr r4, =userRegisters
+ str sp, [r4] @ save pointer to user regs
+
+ mov r10, sp, lsr #13 @ find workspace
+ mov r10, r10, lsl #13
+ add r10, r10, #TSS_FPESAVE
+
+ ldr r4, =fpa11
+ str r10, [r4] @ store pointer to our state
+ mov r4, sp @ use r4 for local pointer
+
+ ldr r5, [r4, #60] @ get contents of PC
+ bic r5, r5, #0xfc000003
+ ldr r0, [r5, #-4] @ get actual instruction into r0
+ bl EmulateAll @ emulate the instruction
+1: cmp r0, #0 @ was emulation successful
+ beq fpundefinstr @ no, return failure
+
+next:
+ ldrt r6, [r5], #4 @ get the next instruction and
+ @ increment PC
+
+ and r2, r6, #0x0F000000 @ test for FP insns
+ teq r2, #0x0C000000
+ teqne r2, #0x0D000000
+ teqne r2, #0x0E000000
+ bne ret_from_exception @ return ok if not a fp insn
+
+ ldr r9, [r4, #60] @ get new condition codes
+ and r9, r9, #0xfc000003
+ orr r7, r5, r9
+ str r7, [r4, #60] @ update PC copy in regs
+
+ mov r0, r6 @ save a copy
+ mov r1, r9 @ fetch the condition codes
+ bl checkCondition @ check the condition
+ cmp r0, #0 @ r0 = 0 ==> condition failed
+
+ @ if condition code failed to match, next insn
+ beq next @ get the next instruction;
+
+ mov r0, r6 @ prepare for EmulateAll()
+ adr lr, 1b
+ orr lr, lr, #3
+ b EmulateAll @ if r0 != 0, goto EmulateAll
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+
+floatx80 getExtendedConstant(unsigned int);
+
+floatx80 floatx80_exp(floatx80 Fm);
+floatx80 floatx80_ln(floatx80 Fm);
+floatx80 floatx80_sin(floatx80 rFm);
+floatx80 floatx80_cos(floatx80 rFm);
+floatx80 floatx80_arcsin(floatx80 rFm);
+floatx80 floatx80_arctan(floatx80 rFm);
+floatx80 floatx80_log(floatx80 rFm);
+floatx80 floatx80_tan(floatx80 rFm);
+floatx80 floatx80_arccos(floatx80 rFm);
+floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm);
+floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm);
+
+unsigned int ExtendedCPDO(const unsigned int opcode)
+{
+ floatx80 rFm, rFn;
+ unsigned int Fd, Fm, Fn, nRc = 1;
+
+ //fp_printk("ExtendedCPDO(0x%08x)\n",opcode);
+
+ Fm = getFm(opcode);
+ if (CONSTANT_FM(opcode))
+ {
+ rFm = getExtendedConstant(Fm);
+ }
+ else
+ {
+ switch (fpa11->fpreg[Fm].fType)
+ {
+ case typeSingle:
+ rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle);
+ break;
+
+ case typeDouble:
+ rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble);
+ break;
+
+ case typeExtended:
+ rFm = fpa11->fpreg[Fm].fValue.fExtended;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (!MONADIC_INSTRUCTION(opcode))
+ {
+ Fn = getFn(opcode);
+ switch (fpa11->fpreg[Fn].fType)
+ {
+ case typeSingle:
+ rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle);
+ break;
+
+ case typeDouble:
+ rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble);
+ break;
+
+ case typeExtended:
+ rFn = fpa11->fpreg[Fn].fValue.fExtended;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ Fd = getFd(opcode);
+ switch (opcode & MASK_ARITHMETIC_OPCODE)
+ {
+ /* dyadic opcodes */
+ case ADF_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_add(rFn,rFm);
+ break;
+
+ case MUF_CODE:
+ case FML_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_mul(rFn,rFm);
+ break;
+
+ case SUF_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_sub(rFn,rFm);
+ break;
+
+ case RSF_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_sub(rFm,rFn);
+ break;
+
+ case DVF_CODE:
+ case FDV_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_div(rFn,rFm);
+ break;
+
+ case RDF_CODE:
+ case FRD_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_div(rFm,rFn);
+ break;
+
+#if 0
+ case POW_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_pow(rFn,rFm);
+ break;
+
+ case RPW_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_pow(rFm,rFn);
+ break;
+#endif
+
+ case RMF_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_rem(rFn,rFm);
+ break;
+
+#if 0
+ case POL_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_pol(rFn,rFm);
+ break;
+#endif
+
+ /* monadic opcodes */
+ case MVF_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = rFm;
+ break;
+
+ case MNF_CODE:
+ rFm.high ^= 0x8000;
+ fpa11->fpreg[Fd].fValue.fExtended = rFm;
+ break;
+
+ case ABS_CODE:
+ rFm.high &= 0x7fff;
+ fpa11->fpreg[Fd].fValue.fExtended = rFm;
+ break;
+
+ case RND_CODE:
+ case URD_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended =
+ int32_to_floatx80(floatx80_to_int32(rFm));
+ break;
+
+ case SQT_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_sqrt(rFm);
+ break;
+
+#if 0
+ case LOG_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_log(rFm);
+ break;
+
+ case LGN_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_ln(rFm);
+ break;
+
+ case EXP_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_exp(rFm);
+ break;
+
+ case SIN_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_sin(rFm);
+ break;
+
+ case COS_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_cos(rFm);
+ break;
+
+ case TAN_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_tan(rFm);
+ break;
+
+ case ASN_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_arcsin(rFm);
+ break;
+
+ case ACS_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_arccos(rFm);
+ break;
+
+ case ATN_CODE:
+ fpa11->fpreg[Fd].fValue.fExtended = floatx80_arctan(rFm);
+ break;
+#endif
+
+ case NRM_CODE:
+ break;
+
+ default:
+ {
+ nRc = 0;
+ }
+ }
+
+ if (0 != nRc) fpa11->fpreg[Fd].fType = typeExtended;
+ return nRc;
+}
+
+#if 0
+floatx80 floatx80_exp(floatx80 Fm)
+{
+//series
+}
+
+floatx80 floatx80_ln(floatx80 Fm)
+{
+//series
+}
+
+floatx80 floatx80_sin(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_cos(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_arcsin(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_arctan(floatx80 rFm)
+{
+ //series
+}
+
+floatx80 floatx80_log(floatx80 rFm)
+{
+ return floatx80_div(floatx80_ln(rFm),getExtendedConstant(7));
+}
+
+floatx80 floatx80_tan(floatx80 rFm)
+{
+ return floatx80_div(floatx80_sin(rFm),floatx80_cos(rFm));
+}
+
+floatx80 floatx80_arccos(floatx80 rFm)
+{
+ //return floatx80_sub(halfPi,floatx80_arcsin(rFm));
+}
+
+floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm)
+{
+ return floatx80_exp(floatx80_mul(rFm,floatx80_ln(rFn)));
+}
+
+floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm)
+{
+ return floatx80_arctan(floatx80_div(rFn,rFm));
+}
+#endif
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "config.h"
+#include "fpa11.h"
+#include "milieu.h"
+#include "fpopcode.h"
+
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+/* forward declarations */
+unsigned int EmulateCPDO(const unsigned int);
+unsigned int EmulateCPDT(const unsigned int);
+unsigned int EmulateCPRT(const unsigned int);
+
+/* Emulator registers */
+FPA11 *fpa11;
+
+/* Reset the FPA11 chip. Called to initialize and reset the emulator. */
+void resetFPA11(void)
+{
+ int i;
+ /* initialize the registers */
+ for (i=0;i<=7;i++)
+ {
+ fpa11->fpreg[i].fType = typeNone;
+ }
+
+ /* FPSR: set system id to FP_EMULATOR, clear all other bits */
+ fpa11->fpsr = FP_EMULATOR;
+
+ /* FPCR: set SB, AB and DA bits, clear all others */
+#if MAINTAIN_FPCR
+ fpa11->fpcr = MASK_RESET;
+#endif
+}
+
+void SetRoundingMode(const unsigned int opcode)
+{
+#if MAINTAIN_FPCR
+ fpa11->fpcr &= ~MASK_ROUNDING_MODE;
+#endif
+ switch (opcode & MASK_ROUNDING_MODE)
+ {
+ default:
+ case ROUND_TO_NEAREST:
+ float_rounding_mode = float_round_nearest_even;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_NEAREST;
+#endif
+ break;
+
+ case ROUND_TO_PLUS_INFINITY:
+ float_rounding_mode = float_round_up;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_PLUS_INFINITY;
+#endif
+ break;
+
+ case ROUND_TO_MINUS_INFINITY:
+ float_rounding_mode = float_round_down;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_MINUS_INFINITY;
+#endif
+ break;
+
+ case ROUND_TO_ZERO:
+ float_rounding_mode = float_round_to_zero;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_ZERO;
+#endif
+ break;
+ }
+}
+
+void SetRoundingPrecision(const unsigned int opcode)
+{
+#if MAINTAIN_FPCR
+ fpa11->fpcr &= ~MASK_ROUNDING_PRECISION;
+#endif
+ switch (opcode & MASK_ROUNDING_PRECISION)
+ {
+ case ROUND_SINGLE:
+ floatx80_rounding_precision = 32;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_SINGLE;
+#endif
+ break;
+
+ case ROUND_DOUBLE:
+ floatx80_rounding_precision = 64;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_DOUBLE;
+#endif
+ break;
+
+ case ROUND_EXTENDED:
+ floatx80_rounding_precision = 80;
+#if MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_EXTENDED;
+#endif
+ break;
+
+ default: floatx80_rounding_precision = 80;
+ }
+}
+
+/* Emulate the instruction in the opcode. */
+unsigned int EmulateAll(unsigned int opcode)
+{
+ unsigned int nRc = 0;
+
+ if (fpa11->initflag == 0) /* good place for __builtin_expect */
+ {
+ resetFPA11();
+ SetRoundingMode(ROUND_TO_NEAREST);
+ SetRoundingPrecision(ROUND_EXTENDED);
+ fpa11->initflag = 1;
+ }
+
+ if (TEST_OPCODE(opcode,MASK_CPRT))
+ {
+ /* Emulate conversion opcodes. */
+ /* Emulate register transfer opcodes. */
+ /* Emulate comparison opcodes. */
+ nRc = EmulateCPRT(opcode);
+ }
+ else if (TEST_OPCODE(opcode,MASK_CPDO))
+ {
+ /* Emulate monadic arithmetic opcodes. */
+ /* Emulate dyadic arithmetic opcodes. */
+ nRc = EmulateCPDO(opcode);
+ }
+ else if (TEST_OPCODE(opcode,MASK_CPDT))
+ {
+ /* Emulate load/store opcodes. */
+ /* Emulate load/store multiple opcodes. */
+ nRc = EmulateCPDT(opcode);
+ }
+ else
+ {
+ /* Invalid instruction detected. Return FALSE. */
+ nRc = 0;
+ }
+
+ return(nRc);
+}
+
+#if 0
+unsigned int EmulateAll1(unsigned int opcode)
+{
+ switch ((opcode >> 24) & 0xf)
+ {
+ case 0xc:
+ case 0xd:
+ if ((opcode >> 20) & 0x1)
+ {
+ switch ((opcode >> 8) & 0xf)
+ {
+ case 0x1: return PerformLDF(opcode); break;
+ case 0x2: return PerformLFM(opcode); break;
+ default: return 0;
+ }
+ }
+ else
+ {
+ switch ((opcode >> 8) & 0xf)
+ {
+ case 0x1: return PerformSTF(opcode); break;
+ case 0x2: return PerformSFM(opcode); break;
+ default: return 0;
+ }
+ }
+ break;
+
+ case 0xe:
+ if (opcode & 0x10)
+ return EmulateCPDO(opcode);
+ else
+ return EmulateCPRT(opcode);
+ break;
+
+ default: return 0;
+ }
+}
+#endif
+
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 __FPA11_H__
+#define __FPA11_H__
+
+/* includes */
+#include "fpsr.h" /* FP control and status register definitions */
+#include "softfloat.h"
+
+#define typeNone 0x00
+#define typeSingle 0x01
+#define typeDouble 0x02
+#define typeExtended 0x03
+
+typedef struct tagFPREG {
+ unsigned int fType;
+ union {
+ float32 fSingle;
+ float64 fDouble;
+ floatx80 fExtended;
+ } fValue;
+} FPREG;
+
+/* FPA11 device model */
+typedef struct tagFPA11 {
+ int initflag; /* this is special. The kernel guarantees
+ to set it to 0 when a thread is launched,
+ so we can use it to detect whether this
+ instance of the emulator needs to be
+ initialised. */
+ FPREG fpreg[8]; /* 8 floating point registers */
+ FPSR fpsr; /* floating point status register */
+ FPCR fpcr; /* floating point control register */
+} FPA11;
+
+extern void resetFPA11(void);
+extern void SetRoundingMode(const unsigned int);
+extern void SetRoundingPrecision(const unsigned int);
+
+extern FPA11 *fpa11;
+
+#endif
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "fpa11.h"
+
+/* Read and write floating point status register */
+extern __inline__ unsigned int readFPSR(void)
+{
+ return(fpa11->fpsr);
+}
+
+extern __inline__ void writeFPSR(FPSR reg)
+{
+ /* the sysid byte in the status register is readonly */
+ fpa11->fpsr = (fpa11->fpsr & MASK_SYSID) | (reg & ~MASK_SYSID);
+}
+
+/* Read and write floating point control register */
+extern __inline__ FPCR readFPCR(void)
+{
+ /* clear SB, AB and DA bits before returning FPCR */
+ return(fpa11->fpcr & ~MASK_RFC);
+}
+
+extern __inline__ void writeFPCR(FPCR reg)
+{
+ fpa11->fpcr &= ~MASK_WFC; /* clear SB, AB and DA bits */
+ fpa11->fpcr |= (reg & MASK_WFC); /* write SB, AB and DA bits */
+}
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "config.h"
+#include "fpa11.h"
+#include "fpopcode.h"
+
+unsigned int SingleCPDO(const unsigned int opcode);
+unsigned int DoubleCPDO(const unsigned int opcode);
+unsigned int ExtendedCPDO(const unsigned int opcode);
+
+unsigned int EmulateCPDO(const unsigned int opcode)
+{
+ unsigned int Fd, nType, nDest, nRc = 1;
+
+ //fp_printk("EmulateCPDO(0x%08x)\n",opcode);
+
+ /* Get the destination size. If not valid let Linux perform
+ an invalid instruction trap. */
+ nDest = getDestinationSize(opcode);
+ if (typeNone == nDest) return 0;
+
+ SetRoundingMode(opcode);
+
+ /* Compare the size of the operands in Fn and Fm.
+ Choose the largest size and perform operations in that size,
+ in order to make use of all the precision of the operands.
+ If Fm is a constant, we just grab a constant of a size
+ matching the size of the operand in Fn. */
+ if (MONADIC_INSTRUCTION(opcode))
+ nType = nDest;
+ else
+ nType = fpa11->fpreg[getFn(opcode)].fType;
+
+ if (!CONSTANT_FM(opcode))
+ {
+ register unsigned int Fm = getFm(opcode);
+ if (nType < fpa11->fpreg[Fm].fType)
+ {
+ nType = fpa11->fpreg[Fm].fType;
+ }
+ }
+
+ switch (nType)
+ {
+ case typeSingle : nRc = SingleCPDO(opcode); break;
+ case typeDouble : nRc = DoubleCPDO(opcode); break;
+ case typeExtended : nRc = ExtendedCPDO(opcode); break;
+ default : nRc = 0;
+ }
+
+ /* If the operation succeeded, check to see if the result in the
+ destination register is the correct size. If not force it
+ to be. */
+ Fd = getFd(opcode);
+ nType = fpa11->fpreg[Fd].fType;
+ if ((0 != nRc) && (nDest != nType))
+ {
+ switch (nDest)
+ {
+ case typeSingle:
+ {
+ if (typeDouble == nType)
+ fpa11->fpreg[Fd].fValue.fSingle =
+ float64_to_float32(fpa11->fpreg[Fd].fValue.fDouble);
+ else
+ fpa11->fpreg[Fd].fValue.fSingle =
+ floatx80_to_float32(fpa11->fpreg[Fd].fValue.fExtended);
+ }
+ break;
+
+ case typeDouble:
+ {
+ if (typeSingle == nType)
+ fpa11->fpreg[Fd].fValue.fDouble =
+ float32_to_float64(fpa11->fpreg[Fd].fValue.fSingle);
+ else
+ fpa11->fpreg[Fd].fValue.fDouble =
+ floatx80_to_float64(fpa11->fpreg[Fd].fValue.fExtended);
+ }
+ break;
+
+ case typeExtended:
+ {
+ if (typeSingle == nType)
+ fpa11->fpreg[Fd].fValue.fExtended =
+ float32_to_floatx80(fpa11->fpreg[Fd].fValue.fSingle);
+ else
+ fpa11->fpreg[Fd].fValue.fExtended =
+ float64_to_floatx80(fpa11->fpreg[Fd].fValue.fDouble);
+ }
+ break;
+ }
+
+ fpa11->fpreg[Fd].fType = nDest;
+ }
+
+ return nRc;
+}
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+ (c) Philip Blundell, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+#include <asm/uaccess.h>
+
+extern __inline__
+void loadSingle(const unsigned int Fn,const unsigned int *pMem)
+{
+ fpa11->fpreg[Fn].fType = typeSingle;
+ get_user(fpa11->fpreg[Fn].fValue.fSingle, pMem);
+}
+
+extern __inline__
+void loadDouble(const unsigned int Fn,const unsigned int *pMem)
+{
+ unsigned int *p;
+ p = (unsigned int*)&fpa11->fpreg[Fn].fValue.fDouble;
+ fpa11->fpreg[Fn].fType = typeDouble;
+ get_user(p[0], &pMem[1]);
+ get_user(p[1], &pMem[0]); /* sign & exponent */
+}
+
+extern __inline__
+void loadExtended(const unsigned int Fn,const unsigned int *pMem)
+{
+ unsigned int *p;
+ p = (unsigned int*)&fpa11->fpreg[Fn].fValue.fExtended;
+ fpa11->fpreg[Fn].fType = typeExtended;
+ get_user(p[0], &pMem[0]); /* sign & exponent */
+ get_user(p[1], &pMem[2]); /* ls bits */
+ get_user(p[2], &pMem[1]); /* ms bits */
+}
+
+extern __inline__
+void loadMultiple(const unsigned int Fn,const unsigned int *pMem)
+{
+ register unsigned int *p;
+ unsigned long x;
+
+ p = (unsigned int*)&(fpa11->fpreg[Fn].fValue);
+ get_user(x, &pMem[0]);
+ fpa11->fpreg[Fn].fType = (x >> 14) & 0x00000003;
+
+ switch (fpa11->fpreg[Fn].fType)
+ {
+ case typeSingle:
+ case typeDouble:
+ {
+ get_user(p[0], &pMem[2]); /* Single */
+ get_user(p[1], &pMem[1]); /* double msw */
+ p[2] = 0; /* empty */
+ }
+ break;
+
+ case typeExtended:
+ {
+ get_user(p[1], &pMem[2]);
+ get_user(p[2], &pMem[1]); /* msw */
+ p[0] = (x & 0x80003fff);
+ }
+ break;
+ }
+}
+
+extern __inline__
+void storeSingle(const unsigned int Fn,unsigned int *pMem)
+{
+ float32 val;
+ register unsigned int *p = (unsigned int*)&val;
+
+ switch (fpa11->fpreg[Fn].fType)
+ {
+ case typeDouble:
+ val = float64_to_float32(fpa11->fpreg[Fn].fValue.fDouble);
+ break;
+
+ case typeExtended:
+ val = floatx80_to_float32(fpa11->fpreg[Fn].fValue.fExtended);
+ break;
+
+ default: val = fpa11->fpreg[Fn].fValue.fSingle;
+ }
+
+ put_user(p[0], pMem);
+}
+
+extern __inline__
+void storeDouble(const unsigned int Fn,unsigned int *pMem)
+{
+ float64 val;
+ register unsigned int *p = (unsigned int*)&val;
+
+ switch (fpa11->fpreg[Fn].fType)
+ {
+ case typeSingle:
+ val = float32_to_float64(fpa11->fpreg[Fn].fValue.fSingle);
+ break;
+
+ case typeExtended:
+ val = floatx80_to_float64(fpa11->fpreg[Fn].fValue.fExtended);
+ break;
+
+ default: val = fpa11->fpreg[Fn].fValue.fDouble;
+ }
+ put_user(p[1], &pMem[0]); /* msw */
+ put_user(p[0], &pMem[1]); /* lsw */
+}
+
+extern __inline__
+void storeExtended(const unsigned int Fn,unsigned int *pMem)
+{
+ floatx80 val;
+ register unsigned int *p = (unsigned int*)&val;
+
+ switch (fpa11->fpreg[Fn].fType)
+ {
+ case typeSingle:
+ val = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle);
+ break;
+
+ case typeDouble:
+ val = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble);
+ break;
+
+ default: val = fpa11->fpreg[Fn].fValue.fExtended;
+ }
+
+ put_user(p[0], &pMem[0]); /* sign & exp */
+ put_user(p[1], &pMem[2]);
+ put_user(p[2], &pMem[1]); /* msw */
+}
+
+extern __inline__
+void storeMultiple(const unsigned int Fn,unsigned int *pMem)
+{
+ register unsigned int nType, *p;
+
+ p = (unsigned int*)&(fpa11->fpreg[Fn].fValue);
+ nType = fpa11->fpreg[Fn].fType;
+
+ switch (nType)
+ {
+ case typeSingle:
+ case typeDouble:
+ {
+ put_user(p[0], &pMem[2]); /* single */
+ put_user(p[1], &pMem[1]); /* double msw */
+ put_user(nType << 14, &pMem[0]);
+ }
+ break;
+
+ case typeExtended:
+ {
+ put_user(p[2], &pMem[1]); /* msw */
+ put_user(p[1], &pMem[2]);
+ put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]);
+ }
+ break;
+ }
+}
+
+unsigned int PerformLDF(const unsigned int opcode)
+{
+ unsigned int *pBase, *pAddress, *pFinal, nRc = 1;
+
+ //fp_printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
+
+ pBase = (unsigned int*)readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode)) pBase += 2;
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode);
+ else
+ pFinal -= getOffset(opcode);
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ switch (opcode & MASK_TRANSFER_LENGTH)
+ {
+ case TRANSFER_SINGLE : loadSingle(getFd(opcode),pAddress); break;
+ case TRANSFER_DOUBLE : loadDouble(getFd(opcode),pAddress); break;
+ case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break;
+ default: nRc = 0;
+ }
+
+ if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return nRc;
+}
+
+unsigned int PerformSTF(const unsigned int opcode)
+{
+ unsigned int *pBase, *pAddress, *pFinal, nRc = 1;
+
+ //fp_printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
+ SetRoundingMode(ROUND_TO_NEAREST);
+
+ pBase = (unsigned int*)readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode)) pBase += 2;
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode);
+ else
+ pFinal -= getOffset(opcode);
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ switch (opcode & MASK_TRANSFER_LENGTH)
+ {
+ case TRANSFER_SINGLE : storeSingle(getFd(opcode),pAddress); break;
+ case TRANSFER_DOUBLE : storeDouble(getFd(opcode),pAddress); break;
+ case TRANSFER_EXTENDED: storeExtended(getFd(opcode),pAddress); break;
+ default: nRc = 0;
+ }
+
+ if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return nRc;
+}
+
+unsigned int PerformLFM(const unsigned int opcode)
+{
+ unsigned int i, Fd, *pBase, *pAddress, *pFinal;
+ pBase = (unsigned int*)readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode)) pBase += 2;
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode);
+ else
+ pFinal -= getOffset(opcode);
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ Fd = getFd(opcode);
+ for (i=getRegisterCount(opcode);i>0;i--)
+ {
+ loadMultiple(Fd,pAddress);
+ pAddress += 3; Fd++;
+ if (Fd == 8) Fd = 0;
+ }
+
+ if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return 1;
+}
+
+unsigned int PerformSFM(const unsigned int opcode)
+{
+ unsigned int i, Fd, *pBase, *pAddress, *pFinal;
+
+ pBase = (unsigned int*)readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode)) pBase += 2;
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode);
+ else
+ pFinal -= getOffset(opcode);
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ Fd = getFd(opcode);
+ for (i=getRegisterCount(opcode);i>0;i--)
+ {
+ storeMultiple(Fd,pAddress);
+ pAddress += 3; Fd++;
+ if (Fd == 8) Fd = 0;
+ }
+
+ if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return 1;
+}
+
+#if 1
+unsigned int EmulateCPDT(const unsigned int opcode)
+{
+ unsigned int nRc = 0;
+
+ //fp_printk("EmulateCPDT(0x%08x)\n",opcode);
+
+ if (LDF_OP(opcode))
+ {
+ nRc = PerformLDF(opcode);
+ }
+ else if (LFM_OP(opcode))
+ {
+ nRc = PerformLFM(opcode);
+ }
+ else if (STF_OP(opcode))
+ {
+ nRc = PerformSTF(opcode);
+ }
+ else if (SFM_OP(opcode))
+ {
+ nRc = PerformSFM(opcode);
+ }
+ else
+ {
+ nRc = 0;
+ }
+
+ return nRc;
+}
+#endif
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+ (c) Philip Blundell, 1999
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "config.h"
+#include "milieu.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+#include "fpa11.inl"
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+extern flag floatx80_is_nan(floatx80);
+extern flag float64_is_nan( float64);
+extern flag float32_is_nan( float32);
+
+void SetRoundingMode(const unsigned int opcode);
+
+unsigned int PerformFLT(const unsigned int opcode);
+unsigned int PerformFIX(const unsigned int opcode);
+
+static unsigned int
+PerformComparison(const unsigned int opcode);
+
+unsigned int EmulateCPRT(const unsigned int opcode)
+{
+ unsigned int nRc = 1;
+
+ //fp_printk("EmulateCPRT(0x%08x)\n",opcode);
+
+ if (opcode & 0x800000)
+ {
+ /* This is some variant of a comparison (PerformComparison will
+ sort out which one). Since most of the other CPRT
+ instructions are oddball cases of some sort or other it makes
+ sense to pull this out into a fast path. */
+ return PerformComparison(opcode);
+ }
+
+ /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
+ switch ((opcode & 0x700000) >> 20)
+ {
+ case FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
+ case FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
+
+ case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
+ case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
+
+#if 0
+ /* ?? Not at all sure about the mode checks here. Linux never
+ calls the emulator from a non-USR fault but we always run in SVC
+ mode. Is there even any point trying to emulate the way FPA11
+ behaves in this respect?
+
+ No - and I quote: 'The FPCR may only be present in some
+ implementations: it is there to control the hardware in an
+ implementation-specific manner, ... The user mode of the
+ ARM is not permitted to use this register, and the WFC and
+ RFC instructions will trap if tried from user mode.'
+ Therefore, we do not provide the RFC and WFC instructions.
+ (rmk, 3/05/1999)
+ */
+ case WFC_CODE >> 20:
+ {
+ int mode = 0;
+ __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode));
+ nRc = (0x13 == mode) ? 1 : 0; /* in SVC processor mode? */
+ if (nRc) writeFPCR(readRegister(getRd(opcode)));
+ }
+ break;
+
+ case RFC_CODE >> 20:
+ {
+ int mode = 0;
+ __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode));
+ nRc = (0x13 == mode) ? 1 : 0; /* in SVC processor mode? */
+ if (nRc) writeRegister(getRd(opcode),readFPCR()); break;
+ }
+ break;
+#endif
+
+ default: nRc = 0;
+ }
+
+ return nRc;
+}
+
+unsigned int PerformFLT(const unsigned int opcode)
+{
+ unsigned int nRc = 1;
+ SetRoundingMode(opcode);
+ SetRoundingPrecision(opcode);
+
+ switch (opcode & MASK_ROUNDING_PRECISION)
+ {
+ case ROUND_SINGLE:
+ {
+ fpa11->fpreg[getFn(opcode)].fType = typeSingle;
+ fpa11->fpreg[getFn(opcode)].fValue.fSingle =
+ int32_to_float32(readRegister(getRd(opcode)));
+ }
+ break;
+
+ case ROUND_DOUBLE:
+ {
+ fpa11->fpreg[getFn(opcode)].fType = typeDouble;
+ fpa11->fpreg[getFn(opcode)].fValue.fDouble =
+ int32_to_float64(readRegister(getRd(opcode)));
+ }
+ break;
+
+ case ROUND_EXTENDED:
+ {
+ fpa11->fpreg[getFn(opcode)].fType = typeExtended;
+ fpa11->fpreg[getFn(opcode)].fValue.fExtended =
+ int32_to_floatx80(readRegister(getRd(opcode)));
+ }
+ break;
+
+ default: nRc = 0;
+ }
+
+ return nRc;
+}
+
+unsigned int PerformFIX(const unsigned int opcode)
+{
+ unsigned int nRc = 1;
+ unsigned int Fn = getFm(opcode);
+
+ SetRoundingMode(opcode);
+
+ switch (fpa11->fpreg[Fn].fType)
+ {
+ case typeSingle:
+ {
+ writeRegister(getRd(opcode),
+ float32_to_int32(fpa11->fpreg[Fn].fValue.fSingle));
+ }
+ break;
+
+ case typeDouble:
+ {
+ writeRegister(getRd(opcode),
+ float64_to_int32(fpa11->fpreg[Fn].fValue.fDouble));
+ }
+ break;
+
+ case typeExtended:
+ {
+ writeRegister(getRd(opcode),
+ floatx80_to_int32(fpa11->fpreg[Fn].fValue.fExtended));
+ }
+ break;
+
+ default: nRc = 0;
+ }
+
+ return nRc;
+}
+
+
+static unsigned int __inline__
+PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
+{
+ unsigned int flags = 0;
+
+ /* test for less than condition */
+ if (floatx80_lt(Fn,Fm))
+ {
+ flags |= CC_NEGATIVE;
+ }
+
+ /* test for equal condition */
+ if (floatx80_eq(Fn,Fm))
+ {
+ flags |= CC_ZERO;
+ }
+
+ /* test for greater than or equal condition */
+ if (floatx80_lt(Fm,Fn))
+ {
+ flags |= CC_CARRY;
+ }
+
+ writeConditionCodes(flags);
+ return 1;
+}
+
+/* This instruction sets the flags N, Z, C, V in the FPSR. */
+
+static unsigned int PerformComparison(const unsigned int opcode)
+{
+ unsigned int Fn, Fm;
+ floatx80 rFn, rFm;
+ int e_flag = opcode & 0x400000; /* 1 if CxFE */
+ int n_flag = opcode & 0x200000; /* 1 if CNxx */
+ unsigned int flags = 0;
+
+ //fp_printk("PerformComparison(0x%08x)\n",opcode);
+
+ Fn = getFn(opcode);
+ Fm = getFm(opcode);
+
+ /* Check for unordered condition and convert all operands to 80-bit
+ format.
+ ?? Might be some mileage in avoiding this conversion if possible.
+ Eg, if both operands are 32-bit, detect this and do a 32-bit
+ comparison (cheaper than an 80-bit one). */
+ switch (fpa11->fpreg[Fn].fType)
+ {
+ case typeSingle:
+ //fp_printk("single.\n");
+ if (float32_is_nan(fpa11->fpreg[Fn].fValue.fSingle))
+ goto unordered;
+ rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle);
+ break;
+
+ case typeDouble:
+ //fp_printk("double.\n");
+ if (float64_is_nan(fpa11->fpreg[Fn].fValue.fDouble))
+ goto unordered;
+ rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble);
+ break;
+
+ case typeExtended:
+ //fp_printk("extended.\n");
+ if (floatx80_is_nan(fpa11->fpreg[Fn].fValue.fExtended))
+ goto unordered;
+ rFn = fpa11->fpreg[Fn].fValue.fExtended;
+ break;
+
+ default: return 0;
+ }
+
+ if (CONSTANT_FM(opcode))
+ {
+ //fp_printk("Fm is a constant: #%d.\n",Fm);
+ rFm = getExtendedConstant(Fm);
+ if (floatx80_is_nan(rFm))
+ goto unordered;
+ }
+ else
+ {
+ //fp_printk("Fm = r%d which contains a ",Fm);
+ switch (fpa11->fpreg[Fm].fType)
+ {
+ case typeSingle:
+ //fp_printk("single.\n");
+ if (float32_is_nan(fpa11->fpreg[Fm].fValue.fSingle))
+ goto unordered;
+ rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle);
+ break;
+
+ case typeDouble:
+ //fp_printk("double.\n");
+ if (float64_is_nan(fpa11->fpreg[Fm].fValue.fDouble))
+ goto unordered;
+ rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble);
+ break;
+
+ case typeExtended:
+ //fp_printk("extended.\n");
+ if (floatx80_is_nan(fpa11->fpreg[Fm].fValue.fExtended))
+ goto unordered;
+ rFm = fpa11->fpreg[Fm].fValue.fExtended;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (n_flag)
+ {
+ rFm.high ^= 0x8000;
+ }
+
+ return PerformComparisonOperation(rFn,rFm);
+
+ unordered:
+ /* ?? The FPA data sheet is pretty vague about this, in particular
+ about whether the non-E comparisons can ever raise exceptions.
+ This implementation is based on a combination of what it says in
+ the data sheet, observation of how the Acorn emulator actually
+ behaves (and how programs expect it to) and guesswork. */
+ flags |= CC_OVERFLOW;
+
+ if (BIT_AC & readFPSR()) flags |= CC_CARRY;
+
+ if (e_flag) float_raise(float_flag_invalid);
+
+ writeConditionCodes(flags);
+ return 1;
+}
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+ (c) Philip Blundell, 1998-1999
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "config.h"
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+/* XXX */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+#include <asm/pgtable.h>
+/* XXX */
+
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpmodule.h"
+#include "fpa11.h"
+#include "fpa11.inl"
+
+/* external data */
+extern FPA11 *fpa11;
+
+/* kernel symbols required for signal handling */
+typedef struct task_struct* PTASK;
+
+#ifdef MODULE
+int fp_printk(const char *,...);
+void fp_send_sig(unsigned long sig, PTASK p, int priv);
+#if LINUX_VERSION_CODE > 0x20115
+MODULE_AUTHOR("Scott Bambrough <scottb@corelcomputer.com>");
+MODULE_DESCRIPTION("NWFPE floating point emulator");
+#endif
+
+#else
+#define fp_printk printk
+#define fp_send_sig send_sig
+#define kern_fp_enter fp_enter
+#endif
+
+/* kernel function prototypes required */
+void C_SYMBOL_NAME(fp_setup)(void);
+
+/* external declarations for saved kernel symbols */
+extern unsigned int C_SYMBOL_NAME(kern_fp_enter);
+
+/* forward declarations */
+extern void nwfpe_enter(void);
+
+/* Original value of fp_enter from kernel before patched by fpe_init. */
+static unsigned int orig_fp_enter;
+
+/* Address of user registers on the kernel stack. */
+unsigned int *userRegisters;
+
+void __init C_SYMBOL_NAME(fpe_version)(void)
+{
+ static const char szTitle[] = "<4>NetWinder Floating Point Emulator ";
+ static const char szVersion[] = "V0.94.1 ";
+ static const char szCopyright[] = "(c) 1998 Corel Computer Corp.\n";
+ C_SYMBOL_NAME(fp_printk)(szTitle);
+ C_SYMBOL_NAME(fp_printk)(szVersion);
+ C_SYMBOL_NAME(fp_printk)(szCopyright);
+}
+
+int __init fpe_init(void)
+{
+ /* Display title, version and copyright information. */
+ C_SYMBOL_NAME(fpe_version)();
+
+ /* Save pointer to the old FP handler and then patch ourselves in */
+ orig_fp_enter = C_SYMBOL_NAME(kern_fp_enter);
+ C_SYMBOL_NAME(kern_fp_enter) = (unsigned int)C_SYMBOL_NAME(nwfpe_enter);
+
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return(fpe_init());
+}
+
+void cleanup_module(void)
+{
+ /* Restore the values we saved earlier. */
+ C_SYMBOL_NAME(kern_fp_enter) = orig_fp_enter;
+}
+#endif
+
+#define _ARM_pc 60
+#define _ARM_cpsr 64
+
+/*
+ScottB: November 4, 1998
+
+Moved this function out of softfloat-specialize into fpmodule.c.
+This effectively isolates all the changes required for integrating with the
+Linux kernel into fpmodule.c. Porting to NetBSD should only require modifying
+fpmodule.c to integrate with the NetBSD kernel (I hope!).
+
+[1/1/99: Not quite true any more unfortunately. There is Linux-specific
+code to access data in user space in some other source files at the
+moment. --philb]
+
+float_exception_flags is a global variable in SoftFloat.
+
+This function is called by the SoftFloat routines to raise a floating
+point exception. We check the trap enable byte in the FPSR, and raise
+a SIGFPE exception if necessary. If not the relevant bits in the
+cumulative exceptions flag byte are set and we return.
+*/
+
+void float_raise(signed char flags)
+{
+#if 0
+ printk(KERN_DEBUG "NWFPE: exception %08x at %08x from %08x\n", flags,
+ __builtin_return_address(0), userRegisters[15]);
+#endif
+
+ float_exception_flags |= flags;
+ if (readFPSR() & (flags << 16))
+ {
+ /* raise exception */
+ C_SYMBOL_NAME(fp_send_sig)(SIGFPE,C_SYMBOL_NAME(current),1);
+ }
+ else
+ {
+ /* set the cumulative exceptions flags */
+ writeFPSR(flags);
+ }
+}
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 __FPMODULE_H__
+#define __FPMODULE_H__
+
+#include <linux/config.h>
+
+#ifdef CONFIG_CPU_32
+#define REG_ORIG_R0 17
+#define REG_CPSR 16
+#else
+#define REG_ORIG_R0 16
+#define REG_CPSR 15
+#endif
+
+#define REG_PC 15
+#define REG_LR 14
+#define REG_SP 13
+#define REG_IP 12
+#define REG_FP 11
+#define REG_R10 10
+#define REG_R9 9
+#define REG_R9 9
+#define REG_R8 8
+#define REG_R7 7
+#define REG_R6 6
+#define REG_R5 5
+#define REG_R4 4
+#define REG_R3 3
+#define REG_R2 2
+#define REG_R1 1
+#define REG_R0 0
+
+#endif
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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.
+ */
+
+/* Address of user registers on the kernel stack. */
+extern unsigned int *userRegisters;
+
+extern __inline__
+unsigned int readRegister(const unsigned int nReg)
+{
+ /* Note: The CPU thinks it has dealt with the current instruction. As
+ a result the program counter has been advanced to the next
+ instruction, and points 4 bytes beyond the actual instruction
+ that caused the invalid instruction trap to occur. We adjust
+ for this in this routine. LDF/STF instructions with Rn = PC
+ depend on the PC being correct, as they use PC+8 in their
+ address calculations. */
+ unsigned int val = userRegisters[nReg];
+
+ if (REG_PC == nReg)
+ val -= 4;
+
+ return val;
+}
+
+extern __inline__
+void writeRegister(const unsigned int nReg, const unsigned int val)
+{
+ userRegisters[nReg] = val;
+}
+
+extern __inline__
+unsigned int readCPSR(void)
+{
+ return (readRegister(REG_CPSR));
+}
+
+extern __inline__
+void writeCPSR(const unsigned int val)
+{
+ writeRegister(REG_CPSR, val);
+}
+
+extern __inline__
+unsigned int readConditionCodes(void)
+{
+#ifdef __FPEM_TEST__
+ return (0);
+#else
+ return (readCPSR() & CC_MASK);
+#endif
+}
+
+extern __inline__
+void writeConditionCodes(const unsigned int val)
+{
+ unsigned int rval;
+
+ /*
+ * Operate directly on userRegisters since
+ * the CPSR may be the PC register itself.
+ */
+ rval = userRegisters[REG_CPSR] & ~CC_MASK;
+ userRegisters[REG_CPSR] = rval | (val & CC_MASK);
+}
+
+extern __inline__
+unsigned int readMemoryInt(unsigned int *pMem)
+{
+ return *pMem;
+}
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "config.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpsr.h"
+#include "fpa11.h"
+#include "fpmodule.h"
+#include "fpmodule.inl"
+
+static floatx80 floatx80Constant[] = {
+ { 0x0000, 0x0000000000000000ULL}, /* extended 0.0 */
+ { 0x3fff, 0x8000000000000000ULL}, /* extended 1.0 */
+ { 0x4000, 0x8000000000000000ULL}, /* extended 2.0 */
+ { 0x4000, 0xc000000000000000ULL}, /* extended 3.0 */
+ { 0x4001, 0x8000000000000000ULL}, /* extended 4.0 */
+ { 0x4001, 0xa000000000000000ULL}, /* extended 5.0 */
+ { 0x3ffe, 0x8000000000000000ULL}, /* extended 0.5 */
+ { 0x4002, 0xa000000000000000ULL} /* extended 10.0 */
+};
+
+static float64 float64Constant[] = {
+ 0x0000000000000000ULL, /* double 0.0 */
+ 0x3ff0000000000000ULL, /* double 1.0 */
+ 0x4000000000000000ULL, /* double 2.0 */
+ 0x4008000000000000ULL, /* double 3.0 */
+ 0x4010000000000000ULL, /* double 4.0 */
+ 0x4014000000000000ULL, /* double 5.0 */
+ 0x3fe0000000000000ULL, /* double 0.5 */
+ 0x4024000000000000ULL /* double 10.0 */
+};
+
+static float32 float32Constant[] = {
+ 0x00000000, /* single 0.0 */
+ 0x3f800000, /* single 1.0 */
+ 0x40000000, /* single 2.0 */
+ 0x40400000, /* single 3.0 */
+ 0x40800000, /* single 4.0 */
+ 0x40a00000, /* single 5.0 */
+ 0x3f000000, /* single 0.5 */
+ 0x41200000 /* single 10.0 */
+};
+
+floatx80 getExtendedConstant(const unsigned int nIndex)
+{
+ return floatx80Constant[nIndex];
+}
+
+float64 getDoubleConstant(const unsigned int nIndex)
+{
+ return float64Constant[nIndex];
+}
+
+float32 getSingleConstant(const unsigned int nIndex)
+{
+ return float32Constant[nIndex];
+}
+
+unsigned int getTransferLength(const unsigned int opcode)
+{
+ unsigned int nRc;
+
+ switch (opcode & MASK_TRANSFER_LENGTH)
+ {
+ case 0x00000000: nRc = 1; break; /* single precision */
+ case 0x00008000: nRc = 2; break; /* double precision */
+ case 0x00400000: nRc = 3; break; /* extended precision */
+ default: nRc = 0;
+ }
+
+ return(nRc);
+}
+
+unsigned int getRegisterCount(const unsigned int opcode)
+{
+ unsigned int nRc;
+
+ switch (opcode & MASK_REGISTER_COUNT)
+ {
+ case 0x00000000: nRc = 4; break;
+ case 0x00008000: nRc = 1; break;
+ case 0x00400000: nRc = 2; break;
+ case 0x00408000: nRc = 3; break;
+ default: nRc = 0;
+ }
+
+ return(nRc);
+}
+
+unsigned int getRoundingPrecision(const unsigned int opcode)
+{
+ unsigned int nRc;
+
+ switch (opcode & MASK_ROUNDING_PRECISION)
+ {
+ case 0x00000000: nRc = 1; break;
+ case 0x00000080: nRc = 2; break;
+ case 0x00080000: nRc = 3; break;
+ default: nRc = 0;
+ }
+
+ return(nRc);
+}
+
+unsigned int getDestinationSize(const unsigned int opcode)
+{
+ unsigned int nRc;
+
+ switch (opcode & MASK_DESTINATION_SIZE)
+ {
+ case 0x00000000: nRc = typeSingle; break;
+ case 0x00000080: nRc = typeDouble; break;
+ case 0x00080000: nRc = typeExtended; break;
+ default: nRc = typeNone;
+ }
+
+ return(nRc);
+}
+
+/* contition code lookup table
+ index into the table is test code: EQ, NE, ... LT, GT, AL, NV
+ bit position in short is condition code: NZCV */
+unsigned short aCC[16] = {
+ 0xF0F0, // EQ == Z set
+ 0x0F0F, // NE
+ 0xCCCC, // CS == C set
+ 0x3333, // CC
+ 0xFF00, // MI == N set
+ 0x00FF, // PL
+ 0xAAAA, // VS == V set
+ 0x5555, // VC
+ 0x0C0C, // HI == C set && Z clear
+ 0xF3F3, // LS == C clear || Z set
+ 0xAA55, // GE == (N==V)
+ 0x55AA, // LT == (N!=V)
+ 0x0A05, // GT == (!Z && (N==V))
+ 0xF5FA, // LE == (Z || (N!=V))
+ 0xFFFF, // AL always
+ 0 // NV
+};
+
+unsigned int checkCondition(const unsigned int opcode, const unsigned int ccodes)
+{
+ return (aCC[opcode>>28] >> (ccodes>>28)) & 1;
+}
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 __FPOPCODE_H__
+#define __FPOPCODE_H__
+
+/*
+ARM Floating Point Instruction Classes
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+|c o n d|1 1 0 P|U|u|W|L| Rn |v| Fd |0|0|0|1| o f f s e t | CPDT
+|c o n d|1 1 0 P|U|w|W|L| Rn |x| Fd |0|0|0|1| o f f s e t | CPDT
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+|c o n d|1 1 1 0|a|b|c|d|e| Fn |j| Fd |0|0|0|1|f|g|h|0|i| Fm | CPDO
+|c o n d|1 1 1 0|a|b|c|L|e| Fn | Rd |0|0|0|1|f|g|h|1|i| Fm | CPRT
+|c o n d|1 1 1 0|a|b|c|1|e| Fn |1|1|1|1|0|0|0|1|f|g|h|1|i| Fm | comparisons
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+
+CPDT data transfer instructions
+ LDF, STF, LFM, SFM
+
+CPDO dyadic arithmetic instructions
+ ADF, MUF, SUF, RSF, DVF, RDF,
+ POW, RPW, RMF, FML, FDV, FRD, POL
+
+CPDO monadic arithmetic instructions
+ MVF, MNF, ABS, RND, SQT, LOG, LGN, EXP,
+ SIN, COS, TAN, ASN, ACS, ATN, URD, NRM
+
+CPRT joint arithmetic/data transfer instructions
+ FIX (arithmetic followed by load/store)
+ FLT (load/store followed by arithmetic)
+ CMF, CNF CMFE, CNFE (comparisons)
+ WFS, RFS (write/read floating point status register)
+ WFC, RFC (write/read floating point control register)
+
+cond condition codes
+P pre/post index bit: 0 = postindex, 1 = preindex
+U up/down bit: 0 = stack grows down, 1 = stack grows up
+W write back bit: 1 = update base register (Rn)
+L load/store bit: 0 = store, 1 = load
+Rn base register
+Rd destination/source register
+Fd floating point destination register
+Fn floating point source register
+Fm floating point source register or floating point constant
+
+uv transfer length (TABLE 1)
+wx register count (TABLE 2)
+abcd arithmetic opcode (TABLES 3 & 4)
+ef destination size (rounding precision) (TABLE 5)
+gh rounding mode (TABLE 6)
+j dyadic/monadic bit: 0 = dyadic, 1 = monadic
+i constant bit: 1 = constant (TABLE 6)
+*/
+
+/*
+TABLE 1
++-------------------------+---+---+---------+---------+
+| Precision | u | v | FPSR.EP | length |
++-------------------------+---+---+---------+---------+
+| Single | 0 ü 0 | x | 1 words |
+| Double | 1 ü 1 | x | 2 words |
+| Extended | 1 ü 1 | x | 3 words |
+| Packed decimal | 1 ü 1 | 0 | 3 words |
+| Expanded packed decimal | 1 ü 1 | 1 | 4 words |
++-------------------------+---+---+---------+---------+
+Note: x = don't care
+*/
+
+/*
+TABLE 2
++---+---+---------------------------------+
+| w | x | Number of registers to transfer |
++---+---+---------------------------------+
+| 0 ü 1 | 1 |
+| 1 ü 0 | 2 |
+| 1 ü 1 | 3 |
+| 0 ü 0 | 4 |
++---+---+---------------------------------+
+*/
+
+/*
+TABLE 3: Dyadic Floating Point Opcodes
++---+---+---+---+----------+-----------------------+-----------------------+
+| a | b | c | d | Mnemonic | Description | Operation |
++---+---+---+---+----------+-----------------------+-----------------------+
+| 0 | 0 | 0 | 0 | ADF | Add | Fd := Fn + Fm |
+| 0 | 0 | 0 | 1 | MUF | Multiply | Fd := Fn * Fm |
+| 0 | 0 | 1 | 0 | SUF | Subtract | Fd := Fn - Fm |
+| 0 | 0 | 1 | 1 | RSF | Reverse subtract | Fd := Fm - Fn |
+| 0 | 1 | 0 | 0 | DVF | Divide | Fd := Fn / Fm |
+| 0 | 1 | 0 | 1 | RDF | Reverse divide | Fd := Fm / Fn |
+| 0 | 1 | 1 | 0 | POW | Power | Fd := Fn ^ Fm |
+| 0 | 1 | 1 | 1 | RPW | Reverse power | Fd := Fm ^ Fn |
+| 1 | 0 | 0 | 0 | RMF | Remainder | Fd := IEEE rem(Fn/Fm) |
+| 1 | 0 | 0 | 1 | FML | Fast Multiply | Fd := Fn * Fm |
+| 1 | 0 | 1 | 0 | FDV | Fast Divide | Fd := Fn / Fm |
+| 1 | 0 | 1 | 1 | FRD | Fast reverse divide | Fd := Fm / Fn |
+| 1 | 1 | 0 | 0 | POL | Polar angle (ArcTan2) | Fd := arctan2(Fn,Fm) |
+| 1 | 1 | 0 | 1 | | undefined instruction | trap |
+| 1 | 1 | 1 | 0 | | undefined instruction | trap |
+| 1 | 1 | 1 | 1 | | undefined instruction | trap |
++---+---+---+---+----------+-----------------------+-----------------------+
+Note: POW, RPW, POL are deprecated, and are available for backwards
+ compatibility only.
+*/
+
+/*
+TABLE 4: Monadic Floating Point Opcodes
++---+---+---+---+----------+-----------------------+-----------------------+
+| a | b | c | d | Mnemonic | Description | Operation |
++---+---+---+---+----------+-----------------------+-----------------------+
+| 0 | 0 | 0 | 0 | MVF | Move | Fd := Fm |
+| 0 | 0 | 0 | 1 | MNF | Move negated | Fd := - Fm |
+| 0 | 0 | 1 | 0 | ABS | Absolute value | Fd := abs(Fm) |
+| 0 | 0 | 1 | 1 | RND | Round to integer | Fd := int(Fm) |
+| 0 | 1 | 0 | 0 | SQT | Square root | Fd := sqrt(Fm) |
+| 0 | 1 | 0 | 1 | LOG | Log base 10 | Fd := log10(Fm) |
+| 0 | 1 | 1 | 0 | LGN | Log base e | Fd := ln(Fm) |
+| 0 | 1 | 1 | 1 | EXP | Exponent | Fd := e ^ Fm |
+| 1 | 0 | 0 | 0 | SIN | Sine | Fd := sin(Fm) |
+| 1 | 0 | 0 | 1 | COS | Cosine | Fd := cos(Fm) |
+| 1 | 0 | 1 | 0 | TAN | Tangent | Fd := tan(Fm) |
+| 1 | 0 | 1 | 1 | ASN | Arc Sine | Fd := arcsin(Fm) |
+| 1 | 1 | 0 | 0 | ACS | Arc Cosine | Fd := arccos(Fm) |
+| 1 | 1 | 0 | 1 | ATN | Arc Tangent | Fd := arctan(Fm) |
+| 1 | 1 | 1 | 0 | URD | Unnormalized round | Fd := int(Fm) |
+| 1 | 1 | 1 | 1 | NRM | Normalize | Fd := norm(Fm) |
++---+---+---+---+----------+-----------------------+-----------------------+
+Note: LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN are deprecated, and are
+ available for backwards compatibility only.
+*/
+
+/*
+TABLE 5
++-------------------------+---+---+
+| Rounding Precision | e | f |
++-------------------------+---+---+
+| IEEE Single precision | 0 ü 0 |
+| IEEE Double precision | 0 ü 1 |
+| IEEE Extended precision | 1 ü 0 |
+| undefined (trap) | 1 ü 1 |
++-------------------------+---+---+
+*/
+
+/*
+TABLE 5
++---------------------------------+---+---+
+| Rounding Mode | g | h |
++---------------------------------+---+---+
+| Round to nearest (default) | 0 ü 0 |
+| Round toward plus infinity | 0 ü 1 |
+| Round toward negative infinity | 1 ü 0 |
+| Round toward zero | 1 ü 1 |
++---------------------------------+---+---+
+*/
+
+/*
+===
+=== Definitions for load and store instructions
+===
+*/
+
+/* bit masks */
+#define BIT_PREINDEX 0x01000000
+#define BIT_UP 0x00800000
+#define BIT_WRITE_BACK 0x00200000
+#define BIT_LOAD 0x00100000
+
+/* masks for load/store */
+#define MASK_CPDT 0x0c000000 /* data processing opcode */
+#define MASK_OFFSET 0x000000ff
+#define MASK_TRANSFER_LENGTH 0x00408000
+#define MASK_REGISTER_COUNT MASK_TRANSFER_LENGTH
+#define MASK_COPROCESSOR 0x00000f00
+
+/* Tests for transfer length */
+#define TRANSFER_SINGLE 0x00000000
+#define TRANSFER_DOUBLE 0x00008000
+#define TRANSFER_EXTENDED 0x00400000
+#define TRANSFER_PACKED MASK_TRANSFER_LENGTH
+
+/* Get the coprocessor number from the opcode. */
+#define getCoprocessorNumber(opcode) ((opcode & MASK_COPROCESSOR) >> 8)
+
+/* Get the offset from the opcode. */
+#define getOffset(opcode) (opcode & MASK_OFFSET)
+
+/* Tests for specific data transfer load/store opcodes. */
+#define TEST_OPCODE(opcode,mask) (((opcode) & (mask)) == (mask))
+
+#define LOAD_OP(opcode) TEST_OPCODE((opcode),MASK_CPDT | BIT_LOAD)
+#define STORE_OP(opcode) ((opcode & (MASK_CPDT | BIT_LOAD)) == MASK_CPDT)
+
+#define LDF_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
+#define LFM_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
+#define STF_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
+#define SFM_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
+
+#define PREINDEXED(opcode) ((opcode & BIT_PREINDEX) != 0)
+#define POSTINDEXED(opcode) ((opcode & BIT_PREINDEX) == 0)
+#define BIT_UP_SET(opcode) ((opcode & BIT_UP) != 0)
+#define BIT_UP_CLEAR(opcode) ((opcode & BIT_DOWN) == 0)
+#define WRITE_BACK(opcode) ((opcode & BIT_WRITE_BACK) != 0)
+#define LOAD(opcode) ((opcode & BIT_LOAD) != 0)
+#define STORE(opcode) ((opcode & BIT_LOAD) == 0)
+
+/*
+===
+=== Definitions for arithmetic instructions
+===
+*/
+/* bit masks */
+#define BIT_MONADIC 0x00008000
+#define BIT_CONSTANT 0x00000008
+
+#define CONSTANT_FM(opcode) ((opcode & BIT_CONSTANT) != 0)
+#define MONADIC_INSTRUCTION(opcode) ((opcode & BIT_MONADIC) != 0)
+
+/* instruction identification masks */
+#define MASK_CPDO 0x0e000000 /* arithmetic opcode */
+#define MASK_ARITHMETIC_OPCODE 0x00f08000
+#define MASK_DESTINATION_SIZE 0x00080080
+
+/* dyadic arithmetic opcodes. */
+#define ADF_CODE 0x00000000
+#define MUF_CODE 0x00100000
+#define SUF_CODE 0x00200000
+#define RSF_CODE 0x00300000
+#define DVF_CODE 0x00400000
+#define RDF_CODE 0x00500000
+#define POW_CODE 0x00600000
+#define RPW_CODE 0x00700000
+#define RMF_CODE 0x00800000
+#define FML_CODE 0x00900000
+#define FDV_CODE 0x00a00000
+#define FRD_CODE 0x00b00000
+#define POL_CODE 0x00c00000
+/* 0x00d00000 is an invalid dyadic arithmetic opcode */
+/* 0x00e00000 is an invalid dyadic arithmetic opcode */
+/* 0x00f00000 is an invalid dyadic arithmetic opcode */
+
+/* monadic arithmetic opcodes. */
+#define MVF_CODE 0x00008000
+#define MNF_CODE 0x00108000
+#define ABS_CODE 0x00208000
+#define RND_CODE 0x00308000
+#define SQT_CODE 0x00408000
+#define LOG_CODE 0x00508000
+#define LGN_CODE 0x00608000
+#define EXP_CODE 0x00708000
+#define SIN_CODE 0x00808000
+#define COS_CODE 0x00908000
+#define TAN_CODE 0x00a08000
+#define ASN_CODE 0x00b08000
+#define ACS_CODE 0x00c08000
+#define ATN_CODE 0x00d08000
+#define URD_CODE 0x00e08000
+#define NRM_CODE 0x00f08000
+
+/*
+===
+=== Definitions for register transfer and comparison instructions
+===
+*/
+
+#define MASK_CPRT 0x0e000010 /* register transfer opcode */
+#define MASK_CPRT_CODE 0x00f00000
+#define FLT_CODE 0x00000000
+#define FIX_CODE 0x00100000
+#define WFS_CODE 0x00200000
+#define RFS_CODE 0x00300000
+#define WFC_CODE 0x00400000
+#define RFC_CODE 0x00500000
+#define CMF_CODE 0x00900000
+#define CNF_CODE 0x00b00000
+#define CMFE_CODE 0x00d00000
+#define CNFE_CODE 0x00f00000
+
+/*
+===
+=== Common definitions
+===
+*/
+
+/* register masks */
+#define MASK_Rd 0x0000f000
+#define MASK_Rn 0x000f0000
+#define MASK_Fd 0x00007000
+#define MASK_Fm 0x00000007
+#define MASK_Fn 0x00070000
+
+/* condition code masks */
+#define CC_MASK 0xf0000000
+#define CC_NEGATIVE 0x80000000
+#define CC_ZERO 0x40000000
+#define CC_CARRY 0x20000000
+#define CC_OVERFLOW 0x10000000
+#define CC_EQ 0x00000000
+#define CC_NE 0x10000000
+#define CC_CS 0x20000000
+#define CC_HS CC_CS
+#define CC_CC 0x30000000
+#define CC_LO CC_CC
+#define CC_MI 0x40000000
+#define CC_PL 0x50000000
+#define CC_VS 0x60000000
+#define CC_VC 0x70000000
+#define CC_HI 0x80000000
+#define CC_LS 0x90000000
+#define CC_GE 0xa0000000
+#define CC_LT 0xb0000000
+#define CC_GT 0xc0000000
+#define CC_LE 0xd0000000
+#define CC_AL 0xe0000000
+#define CC_NV 0xf0000000
+
+/* rounding masks/values */
+#define MASK_ROUNDING_MODE 0x00000060
+#define ROUND_TO_NEAREST 0x00000000
+#define ROUND_TO_PLUS_INFINITY 0x00000020
+#define ROUND_TO_MINUS_INFINITY 0x00000040
+#define ROUND_TO_ZERO 0x00000060
+
+#define MASK_ROUNDING_PRECISION 0x00080080
+#define ROUND_SINGLE 0x00000000
+#define ROUND_DOUBLE 0x00000080
+#define ROUND_EXTENDED 0x00080000
+
+/* Get the condition code from the opcode. */
+#define getCondition(opcode) (opcode >> 28)
+
+/* Get the source register from the opcode. */
+#define getRn(opcode) ((opcode & MASK_Rn) >> 16)
+
+/* Get the destination floating point register from the opcode. */
+#define getFd(opcode) ((opcode & MASK_Fd) >> 12)
+
+/* Get the first source floating point register from the opcode. */
+#define getFn(opcode) ((opcode & MASK_Fn) >> 16)
+
+/* Get the second source floating point register from the opcode. */
+#define getFm(opcode) (opcode & MASK_Fm)
+
+/* Get the destination register from the opcode. */
+#define getRd(opcode) ((opcode & MASK_Rd) >> 12)
+
+/* Get the rounding mode from the opcode. */
+#define getRoundingMode(opcode) ((opcode & MASK_ROUNDING_MODE) >> 5)
+
+float32 getSingleConstant(const unsigned int nIndex);
+float64 getDoubleConstant(const unsigned int nIndex);
+floatx80 getExtendedConstant(const unsigned int nIndex);
+
+unsigned int getRegisterCount(const unsigned int opcode);
+unsigned int getDestinationSize(const unsigned int opcode);
+
+#endif
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 __FPSR_H__
+#define __FPSR_H__
+
+/*
+The FPSR is a 32 bit register consisting of 4 parts, each exactly
+one byte.
+
+ SYSTEM ID
+ EXCEPTION TRAP ENABLE BYTE
+ SYSTEM CONTROL BYTE
+ CUMULATIVE EXCEPTION FLAGS BYTE
+
+The FPCR is a 32 bit register consisting of bit flags.
+*/
+
+/* SYSTEM ID
+------------
+Note: the system id byte is read only */
+
+typedef unsigned int FPSR; /* type for floating point status register */
+typedef unsigned int FPCR; /* type for floating point control register */
+
+#define MASK_SYSID 0xff000000
+#define BIT_HARDWARE 0x80000000
+#define FP_EMULATOR 0x01000000 /* System ID for emulator */
+#define FP_ACCELERATOR 0x81000000 /* System ID for FPA11 */
+
+/* EXCEPTION TRAP ENABLE BYTE
+----------------------------- */
+
+#define MASK_TRAP_ENABLE 0x00ff0000
+#define MASK_TRAP_ENABLE_STRICT 0x001f0000
+#define BIT_IXE 0x00100000 /* inexact exception enable */
+#define BIT_UFE 0x00080000 /* underflow exception enable */
+#define BIT_OFE 0x00040000 /* overflow exception enable */
+#define BIT_DZE 0x00020000 /* divide by zero exception enable */
+#define BIT_IOE 0x00010000 /* invalid operation exception enable */
+
+/* SYSTEM CONTROL BYTE
+---------------------- */
+
+#define MASK_SYSTEM_CONTROL 0x0000ff00
+#define MASK_TRAP_STRICT 0x00001f00
+
+#define BIT_AC 0x00100000 /* use alternative C-flag definition
+ for compares */
+#define BIT_EP 0x00080000 /* use expanded packed decimal format */
+#define BIT_SO 0x00040000 /* select synchronous operation of FPA */
+#define BIT_NE 0x00020000 /* NaN exception bit */
+#define BIT_ND 0x00010000 /* no denormalized numbers bit */
+
+/* CUMULATIVE EXCEPTION FLAGS BYTE
+---------------------------------- */
+
+#define MASK_EXCEPTION_FLAGS 0x000000ff
+#define MASK_EXCEPTION_FLAGS_STRICT 0x0000001f
+
+#define BIT_IXC 0x00000010 /* inexact exception flag */
+#define BIT_UFC 0x00000008 /* underflow exception flag */
+#define BIT_OFC 0x00000004 /* overfloat exception flag */
+#define BIT_DZC 0x00000002 /* divide by zero exception flag */
+#define BIT_IOC 0x00000001 /* invalid operation exception flag */
+
+/* Floating Point Control Register
+----------------------------------*/
+
+#define BIT_RU 0x80000000 /* rounded up bit */
+#define BIT_IE 0x10000000 /* inexact bit */
+#define BIT_MO 0x08000000 /* mantissa overflow bit */
+#define BIT_EO 0x04000000 /* exponent overflow bit */
+#define BIT_SB 0x00000800 /* store bounce */
+#define BIT_AB 0x00000400 /* arithmetic bounce */
+#define BIT_RE 0x00000200 /* rounding exception */
+#define BIT_DA 0x00000100 /* disable FPA */
+
+#define MASK_OP 0x00f08010 /* AU operation code */
+#define MASK_PR 0x00080080 /* AU precision */
+#define MASK_S1 0x00070000 /* AU source register 1 */
+#define MASK_S2 0x00000007 /* AU source register 2 */
+#define MASK_DS 0x00007000 /* AU destination register */
+#define MASK_RM 0x00000060 /* AU rounding mode */
+#define MASK_ALU 0x9cfff2ff /* only ALU can write these bits */
+#define MASK_RESET 0x00000d00 /* bits set on reset, all others cleared */
+#define MASK_WFC MASK_RESET
+#define MASK_RFC ~MASK_RESET
+
+#endif
--- /dev/null
+
+/*
+===============================================================================
+
+This C header file is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+/*
+-------------------------------------------------------------------------------
+Include common integer types and flags.
+-------------------------------------------------------------------------------
+*/
+#include "ARM-gcc.h"
+
+/*
+-------------------------------------------------------------------------------
+Symbolic Boolean literals.
+-------------------------------------------------------------------------------
+*/
+enum {
+ FALSE = 0,
+ TRUE = 1
+};
+
--- /dev/null
+/*
+ NetWinder Floating Point Emulator
+ (c) Corel Computer Corporation, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@corelcomputer.com>
+
+ 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 "config.h"
+#include "milieu.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.h"
+
+float32 getSingleConstant(unsigned int);
+
+float32 float32_exp(float32 Fm);
+float32 float32_ln(float32 Fm);
+float32 float32_sin(float32 rFm);
+float32 float32_cos(float32 rFm);
+float32 float32_arcsin(float32 rFm);
+float32 float32_arctan(float32 rFm);
+float32 float32_log(float32 rFm);
+float32 float32_tan(float32 rFm);
+float32 float32_arccos(float32 rFm);
+float32 float32_pow(float32 rFn,float32 rFm);
+float32 float32_pol(float32 rFn,float32 rFm);
+
+unsigned int SingleCPDO(const unsigned int opcode)
+{
+ float32 rFm, rFn;
+ unsigned int Fd, Fm, Fn, nRc = 1;
+
+ Fm = getFm(opcode);
+ if (CONSTANT_FM(opcode))
+ {
+ rFm = getSingleConstant(Fm);
+ }
+ else
+ {
+ switch (fpa11->fpreg[Fm].fType)
+ {
+ case typeSingle:
+ rFm = fpa11->fpreg[Fm].fValue.fSingle;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (!MONADIC_INSTRUCTION(opcode))
+ {
+ Fn = getFn(opcode);
+ switch (fpa11->fpreg[Fn].fType)
+ {
+ case typeSingle:
+ rFn = fpa11->fpreg[Fn].fValue.fSingle;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ Fd = getFd(opcode);
+ switch (opcode & MASK_ARITHMETIC_OPCODE)
+ {
+ /* dyadic opcodes */
+ case ADF_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_add(rFn,rFm);
+ break;
+
+ case MUF_CODE:
+ case FML_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_mul(rFn,rFm);
+ break;
+
+ case SUF_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_sub(rFn,rFm);
+ break;
+
+ case RSF_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_sub(rFm,rFn);
+ break;
+
+ case DVF_CODE:
+ case FDV_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_div(rFn,rFm);
+ break;
+
+ case RDF_CODE:
+ case FRD_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_div(rFm,rFn);
+ break;
+
+#if 0
+ case POW_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_pow(rFn,rFm);
+ break;
+
+ case RPW_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_pow(rFm,rFn);
+ break;
+#endif
+
+ case RMF_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_rem(rFn,rFm);
+ break;
+
+#if 0
+ case POL_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_pol(rFn,rFm);
+ break;
+#endif
+
+ /* monadic opcodes */
+ case MVF_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = rFm;
+ break;
+
+ case MNF_CODE:
+ rFm ^= 0x80000000;
+ fpa11->fpreg[Fd].fValue.fSingle = rFm;
+ break;
+
+ case ABS_CODE:
+ rFm &= 0x7fffffff;
+ fpa11->fpreg[Fd].fValue.fSingle = rFm;
+ break;
+
+ case RND_CODE:
+ case URD_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle =
+ int32_to_float32(float32_to_int32(rFm));
+ break;
+
+ case SQT_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_sqrt(rFm);
+ break;
+
+#if 0
+ case LOG_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_log(rFm);
+ break;
+
+ case LGN_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_ln(rFm);
+ break;
+
+ case EXP_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_exp(rFm);
+ break;
+
+ case SIN_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_sin(rFm);
+ break;
+
+ case COS_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_cos(rFm);
+ break;
+
+ case TAN_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_tan(rFm);
+ break;
+
+ case ASN_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_arcsin(rFm);
+ break;
+
+ case ACS_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_arccos(rFm);
+ break;
+
+ case ATN_CODE:
+ fpa11->fpreg[Fd].fValue.fSingle = float32_arctan(rFm);
+ break;
+#endif
+
+ case NRM_CODE:
+ break;
+
+ default:
+ {
+ nRc = 0;
+ }
+ }
+
+ if (0 != nRc) fpa11->fpreg[Fd].fType = typeSingle;
+ return nRc;
+}
+
+#if 0
+float32 float32_exp(float32 Fm)
+{
+//series
+}
+
+float32 float32_ln(float32 Fm)
+{
+//series
+}
+
+float32 float32_sin(float32 rFm)
+{
+//series
+}
+
+float32 float32_cos(float32 rFm)
+{
+//series
+}
+
+float32 float32_arcsin(float32 rFm)
+{
+//series
+}
+
+float32 float32_arctan(float32 rFm)
+{
+ //series
+}
+
+float32 float32_arccos(float32 rFm)
+{
+ //return float32_sub(halfPi,float32_arcsin(rFm));
+}
+
+float32 float32_log(float32 rFm)
+{
+ return float32_div(float32_ln(rFm),getSingleConstant(7));
+}
+
+float32 float32_tan(float32 rFm)
+{
+ return float32_div(float32_sin(rFm),float32_cos(rFm));
+}
+
+float32 float32_pow(float32 rFn,float32 rFm)
+{
+ return float32_exp(float32_mul(rFm,float32_ln(rFn)));
+}
+
+float32 float32_pol(float32 rFn,float32 rFm)
+{
+ return float32_arctan(float32_div(rFn,rFm));
+}
+#endif
--- /dev/null
+
+/*
+===============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+/*
+-------------------------------------------------------------------------------
+Shifts `a' right by the number of bits given in `count'. If any nonzero
+bits are shifted off, they are ``jammed'' into the least significant bit of
+the result by setting the least significant bit to 1. The value of `count'
+can be arbitrarily large; in particular, if `count' is greater than 32, the
+result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+The result is stored in the location pointed to by `zPtr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr )
+{
+ bits32 z;
+ if ( count == 0 ) {
+ z = a;
+ }
+ else if ( count < 32 ) {
+ z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 );
+ }
+ else {
+ z = ( a != 0 );
+ }
+ *zPtr = z;
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts `a' right by the number of bits given in `count'. If any nonzero
+bits are shifted off, they are ``jammed'' into the least significant bit of
+the result by setting the least significant bit to 1. The value of `count'
+can be arbitrarily large; in particular, if `count' is greater than 64, the
+result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+The result is stored in the location pointed to by `zPtr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr )
+{
+ bits64 z;
+
+ __asm__("@shift64RightJamming -- start");
+ if ( count == 0 ) {
+ z = a;
+ }
+ else if ( count < 64 ) {
+ z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 );
+ }
+ else {
+ z = ( a != 0 );
+ }
+ __asm__("@shift64RightJamming -- end");
+ *zPtr = z;
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64
+_plus_ the number of bits given in `count'. The shifted result is at most
+64 nonzero bits; this is stored at the location pointed to by `z0Ptr'. The
+bits shifted off form a second 64-bit result as follows: The _last_ bit
+shifted off is the most-significant bit of the extra result, and the other
+63 bits of the extra result are all zero if and only if _all_but_the_last_
+bits shifted off were all zero. This extra result is stored in the location
+pointed to by `z1Ptr'. The value of `count' can be arbitrarily large.
+ (This routine makes more sense if `a0' and `a1' are considered to form a
+fixed-point value with binary point between `a0' and `a1'. This fixed-point
+value is shifted right by the number of bits given in `count', and the
+integer part of the result is returned at the location pointed to by
+`z0Ptr'. The fractional part of the result may be slightly corrupted as
+described above, and is returned at the location pointed to by `z1Ptr'.)
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift64ExtraRightJamming(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z0, z1;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z1 = a1;
+ z0 = a0;
+ }
+ else if ( count < 64 ) {
+ z1 = ( a0<<negCount ) | ( a1 != 0 );
+ z0 = a0>>count;
+ }
+ else {
+ if ( count == 64 ) {
+ z1 = a0 | ( a1 != 0 );
+ }
+ else {
+ z1 = ( ( a0 | a1 ) != 0 );
+ }
+ z0 = 0;
+ }
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+number of bits given in `count'. Any bits shifted off are lost. The value
+of `count' can be arbitrarily large; in particular, if `count' is greater
+than 128, the result will be 0. The result is broken into two 64-bit pieces
+which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift128Right(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z0, z1;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z1 = a1;
+ z0 = a0;
+ }
+ else if ( count < 64 ) {
+ z1 = ( a0<<negCount ) | ( a1>>count );
+ z0 = a0>>count;
+ }
+ else {
+ z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0;
+ z0 = 0;
+ }
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+number of bits given in `count'. If any nonzero bits are shifted off, they
+are ``jammed'' into the least significant bit of the result by setting the
+least significant bit to 1. The value of `count' can be arbitrarily large;
+in particular, if `count' is greater than 128, the result will be either 0
+or 1, depending on whether the concatenation of `a0' and `a1' is zero or
+nonzero. The result is broken into two 64-bit pieces which are stored at
+the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift128RightJamming(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z0, z1;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z1 = a1;
+ z0 = a0;
+ }
+ else if ( count < 64 ) {
+ z1 = ( a0<<negCount ) | ( a1>>count ) | ( ( a1<<negCount ) != 0 );
+ z0 = a0>>count;
+ }
+ else {
+ if ( count == 64 ) {
+ z1 = a0 | ( a1 != 0 );
+ }
+ else if ( count < 128 ) {
+ z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<<negCount ) | a1 ) != 0 );
+ }
+ else {
+ z1 = ( ( a0 | a1 ) != 0 );
+ }
+ z0 = 0;
+ }
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right
+by 64 _plus_ the number of bits given in `count'. The shifted result is
+at most 128 nonzero bits; these are broken into two 64-bit pieces which are
+stored at the locations pointed to by `z0Ptr' and `z1Ptr'. The bits shifted
+off form a third 64-bit result as follows: The _last_ bit shifted off is
+the most-significant bit of the extra result, and the other 63 bits of the
+extra result are all zero if and only if _all_but_the_last_ bits shifted off
+were all zero. This extra result is stored in the location pointed to by
+`z2Ptr'. The value of `count' can be arbitrarily large.
+ (This routine makes more sense if `a0', `a1', and `a2' are considered
+to form a fixed-point value with binary point between `a1' and `a2'. This
+fixed-point value is shifted right by the number of bits given in `count',
+and the integer part of the result is returned at the locations pointed to
+by `z0Ptr' and `z1Ptr'. The fractional part of the result may be slightly
+corrupted as described above, and is returned at the location pointed to by
+`z2Ptr'.)
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shift128ExtraRightJamming(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ int16 count,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z2 = a2;
+ z1 = a1;
+ z0 = a0;
+ }
+ else {
+ if ( count < 64 ) {
+ z2 = a1<<negCount;
+ z1 = ( a0<<negCount ) | ( a1>>count );
+ z0 = a0>>count;
+ }
+ else {
+ if ( count == 64 ) {
+ z2 = a1;
+ z1 = a0;
+ }
+ else {
+ a2 |= a1;
+ if ( count < 128 ) {
+ z2 = a0<<negCount;
+ z1 = a0>>( count & 63 );
+ }
+ else {
+ z2 = ( count == 128 ) ? a0 : ( a0 != 0 );
+ z1 = 0;
+ }
+ }
+ z0 = 0;
+ }
+ z2 |= ( a2 != 0 );
+ }
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the
+number of bits given in `count'. Any bits shifted off are lost. The value
+of `count' must be less than 64. The result is broken into two 64-bit
+pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shortShift128Left(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+ *z1Ptr = a1<<count;
+ *z0Ptr =
+ ( count == 0 ) ? a0 : ( a0<<count ) | ( a1>>( ( - count ) & 63 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left
+by the number of bits given in `count'. Any bits shifted off are lost.
+The value of `count' must be less than 64. The result is broken into three
+64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+`z1Ptr', and `z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ shortShift192Left(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ int16 count,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 negCount;
+
+ z2 = a2<<count;
+ z1 = a1<<count;
+ z0 = a0<<count;
+ if ( 0 < count ) {
+ negCount = ( ( - count ) & 63 );
+ z1 |= a2>>negCount;
+ z0 |= a1>>negCount;
+ }
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit
+value formed by concatenating `b0' and `b1'. Addition is modulo 2^128, so
+any carry out is lost. The result is broken into two 64-bit pieces which
+are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ add128(
+ bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z1;
+
+ z1 = a1 + b1;
+ *z1Ptr = z1;
+ *z0Ptr = a0 + b0 + ( z1 < a1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the
+192-bit value formed by concatenating `b0', `b1', and `b2'. Addition is
+modulo 2^192, so any carry out is lost. The result is broken into three
+64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+`z1Ptr', and `z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ add192(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ bits64 b0,
+ bits64 b1,
+ bits64 b2,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 carry0, carry1;
+
+ z2 = a2 + b2;
+ carry1 = ( z2 < a2 );
+ z1 = a1 + b1;
+ carry0 = ( z1 < a1 );
+ z0 = a0 + b0;
+ z1 += carry1;
+ z0 += ( z1 < carry1 );
+ z0 += carry0;
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the
+128-bit value formed by concatenating `a0' and `a1'. Subtraction is modulo
+2^128, so any borrow out (carry out) is lost. The result is broken into two
+64-bit pieces which are stored at the locations pointed to by `z0Ptr' and
+`z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ sub128(
+ bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+ *z1Ptr = a1 - b1;
+ *z0Ptr = a0 - b0 - ( a1 < b1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2'
+from the 192-bit value formed by concatenating `a0', `a1', and `a2'.
+Subtraction is modulo 2^192, so any borrow out (carry out) is lost. The
+result is broken into three 64-bit pieces which are stored at the locations
+pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ sub192(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ bits64 b0,
+ bits64 b1,
+ bits64 b2,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 borrow0, borrow1;
+
+ z2 = a2 - b2;
+ borrow1 = ( a2 < b2 );
+ z1 = a1 - b1;
+ borrow0 = ( a1 < b1 );
+ z0 = a0 - b0;
+ z0 -= ( z1 < borrow1 );
+ z1 -= borrow1;
+ z0 -= borrow0;
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Multiplies `a' by `b' to obtain a 128-bit product. The product is broken
+into two 64-bit pieces which are stored at the locations pointed to by
+`z0Ptr' and `z1Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits32 aHigh, aLow, bHigh, bLow;
+ bits64 z0, zMiddleA, zMiddleB, z1;
+
+ aLow = a;
+ aHigh = a>>32;
+ bLow = b;
+ bHigh = b>>32;
+ z1 = ( (bits64) aLow ) * bLow;
+ zMiddleA = ( (bits64) aLow ) * bHigh;
+ zMiddleB = ( (bits64) aHigh ) * bLow;
+ z0 = ( (bits64) aHigh ) * bHigh;
+ zMiddleA += zMiddleB;
+ z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 );
+ zMiddleA <<= 32;
+ z1 += zMiddleA;
+ z0 += ( z1 < zMiddleA );
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Multiplies the 128-bit value formed by concatenating `a0' and `a1' by `b' to
+obtain a 192-bit product. The product is broken into three 64-bit pieces
+which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and
+`z2Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ mul128By64To192(
+ bits64 a0,
+ bits64 a1,
+ bits64 b,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2, more1;
+
+ mul64To128( a1, b, &z1, &z2 );
+ mul64To128( a0, b, &z0, &more1 );
+ add128( z0, more1, 0, z1, &z0, &z1 );
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the
+128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit
+product. The product is broken into four 64-bit pieces which are stored at
+the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'.
+-------------------------------------------------------------------------------
+*/
+INLINE void
+ mul128To256(
+ bits64 a0,
+ bits64 a1,
+ bits64 b0,
+ bits64 b1,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr,
+ bits64 *z3Ptr
+ )
+{
+ bits64 z0, z1, z2, z3;
+ bits64 more1, more2;
+
+ mul64To128( a1, b1, &z2, &z3 );
+ mul64To128( a1, b0, &z1, &more2 );
+ add128( z1, more2, 0, z2, &z1, &z2 );
+ mul64To128( a0, b0, &z0, &more1 );
+ add128( z0, more1, 0, z1, &z0, &z1 );
+ mul64To128( a0, b1, &more1, &more2 );
+ add128( more1, more2, 0, z2, &more1, &z2 );
+ add128( z0, z1, 0, more1, &z0, &z1 );
+ *z3Ptr = z3;
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns an approximation to the 64-bit integer quotient obtained by dividing
+`b' into the 128-bit value formed by concatenating `a0' and `a1'. The
+divisor `b' must be at least 2^63. If q is the exact quotient truncated
+toward zero, the approximation returned lies between q and q + 2 inclusive.
+If the exact quotient q is larger than 64 bits, the maximum positive 64-bit
+unsigned integer is returned.
+-------------------------------------------------------------------------------
+*/
+static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b )
+{
+ bits64 b0, b1;
+ bits64 rem0, rem1, term0, term1;
+ bits64 z;
+ if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF );
+ b0 = b>>32;
+ z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32;
+ mul64To128( b, z, &term0, &term1 );
+ sub128( a0, a1, term0, term1, &rem0, &rem1 );
+ while ( ( (sbits64) rem0 ) < 0 ) {
+ z -= LIT64( 0x100000000 );
+ b1 = b<<32;
+ add128( rem0, rem1, b0, b1, &rem0, &rem1 );
+ }
+ rem0 = ( rem0<<32 ) | ( rem1>>32 );
+ z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns an approximation to the square root of the 32-bit significand given
+by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of
+`aExp' (the least significant bit) is 1, the integer returned approximates
+2^31*sqrt(`a'/2^31), where `a' is considered an integer. If bit 0 of `aExp'
+is 0, the integer returned approximates 2^31*sqrt(`a'/2^30). In either
+case, the approximation returned lies strictly within +/-2 of the exact
+value.
+-------------------------------------------------------------------------------
+*/
+static bits32 estimateSqrt32( int16 aExp, bits32 a )
+{
+ static const bits16 sqrtOddAdjustments[] = {
+ 0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
+ 0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
+ };
+ static const bits16 sqrtEvenAdjustments[] = {
+ 0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
+ 0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
+ };
+ int8 index;
+ bits32 z;
+
+ index = ( a>>27 ) & 15;
+ if ( aExp & 1 ) {
+ z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ];
+ z = ( ( a / z )<<14 ) + ( z<<15 );
+ a >>= 1;
+ }
+ else {
+ z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ];
+ z = a / z + z;
+ z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 );
+ if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 );
+ }
+ return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the number of leading 0 bits before the most-significant 1 bit
+of `a'. If `a' is zero, 32 is returned.
+-------------------------------------------------------------------------------
+*/
+static int8 countLeadingZeros32( bits32 a )
+{
+ static const int8 countLeadingZerosHigh[] = {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ int8 shiftCount;
+
+ shiftCount = 0;
+ if ( a < 0x10000 ) {
+ shiftCount += 16;
+ a <<= 16;
+ }
+ if ( a < 0x1000000 ) {
+ shiftCount += 8;
+ a <<= 8;
+ }
+ shiftCount += countLeadingZerosHigh[ a>>24 ];
+ return shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the number of leading 0 bits before the most-significant 1 bit
+of `a'. If `a' is zero, 64 is returned.
+-------------------------------------------------------------------------------
+*/
+static int8 countLeadingZeros64( bits64 a )
+{
+ int8 shiftCount;
+
+ shiftCount = 0;
+ if ( a < ( (bits64) 1 )<<32 ) {
+ shiftCount += 32;
+ }
+ else {
+ a >>= 32;
+ }
+ shiftCount += countLeadingZeros32( a );
+ return shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1'
+is equal to the 128-bit value formed by concatenating `b0' and `b1'.
+Otherwise, returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 == b0 ) && ( a1 == b1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+than or equal to the 128-bit value formed by concatenating `b0' and `b1'.
+Otherwise, returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+than the 128-bit value formed by concatenating `b0' and `b1'. Otherwise,
+returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is
+not equal to the 128-bit value formed by concatenating `b0' and `b1'.
+Otherwise, returns 0.
+-------------------------------------------------------------------------------
+*/
+INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 != b0 ) || ( a1 != b1 );
+
+}
+
--- /dev/null
+
+/*
+===============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+/*
+-------------------------------------------------------------------------------
+Underflow tininess-detection mode, statically initialized to default value.
+(The declaration in `softfloat.h' must match the `int8' type here.)
+-------------------------------------------------------------------------------
+*/
+int8 float_detect_tininess = float_tininess_after_rounding;
+
+/*
+-------------------------------------------------------------------------------
+Raises the exceptions specified by `flags'. Floating-point traps can be
+defined here if desired. It is currently not possible for such a trap to
+substitute a result value. If traps are not implemented, this routine
+should be simply `float_exception_flags |= flags;'.
+
+ScottB: November 4, 1998
+Moved this function out of softfloat-specialize into fpmodule.c.
+This effectively isolates all the changes required for integrating with the
+Linux kernel into fpmodule.c. Porting to NetBSD should only require modifying
+fpmodule.c to integrate with the NetBSD kernel (I hope!).
+-------------------------------------------------------------------------------
+void float_raise( int8 flags )
+{
+ float_exception_flags |= flags;
+}
+*/
+
+/*
+-------------------------------------------------------------------------------
+Internal canonical NaN format.
+-------------------------------------------------------------------------------
+*/
+typedef struct {
+ flag sign;
+ bits64 high, low;
+} commonNaNT;
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated single-precision NaN.
+-------------------------------------------------------------------------------
+*/
+#define float32_default_nan 0xFFFFFFFF
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is a NaN;
+otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float32_is_nan( float32 a )
+{
+
+ return ( 0xFF000000 < (bits32) ( a<<1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is a signaling
+NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float32_is_signaling_nan( float32 a )
+{
+
+ return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point NaN
+`a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
+exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT float32ToCommonNaN( float32 a )
+{
+ commonNaNT z;
+
+ if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+ z.sign = a>>31;
+ z.low = 0;
+ z.high = ( (bits64) a )<<41;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the single-
+precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static float32 commonNaNToFloat32( commonNaNT a )
+{
+
+ return ( ( (bits32) a.sign )<<31 ) | 0x7FC00000 | ( a.high>>41 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two single-precision floating-point values `a' and `b', one of which
+is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
+signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static float32 propagateFloat32NaN( float32 a, float32 b )
+{
+ flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+ aIsNaN = float32_is_nan( a );
+ aIsSignalingNaN = float32_is_signaling_nan( a );
+ bIsNaN = float32_is_nan( b );
+ bIsSignalingNaN = float32_is_signaling_nan( b );
+ a |= 0x00400000;
+ b |= 0x00400000;
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+ if ( aIsNaN ) {
+ return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+ }
+ else {
+ return b;
+ }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated double-precision NaN.
+-------------------------------------------------------------------------------
+*/
+#define float64_default_nan LIT64( 0xFFFFFFFFFFFFFFFF )
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is a NaN;
+otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float64_is_nan( float64 a )
+{
+
+ return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is a signaling
+NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float64_is_signaling_nan( float64 a )
+{
+
+ return
+ ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
+ && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point NaN
+`a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
+exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT float64ToCommonNaN( float64 a )
+{
+ commonNaNT z;
+
+ if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+ z.sign = a>>63;
+ z.low = 0;
+ z.high = a<<12;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the double-
+precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static float64 commonNaNToFloat64( commonNaNT a )
+{
+
+ return
+ ( ( (bits64) a.sign )<<63 )
+ | LIT64( 0x7FF8000000000000 )
+ | ( a.high>>12 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two double-precision floating-point values `a' and `b', one of which
+is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
+signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static float64 propagateFloat64NaN( float64 a, float64 b )
+{
+ flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+ aIsNaN = float64_is_nan( a );
+ aIsSignalingNaN = float64_is_signaling_nan( a );
+ bIsNaN = float64_is_nan( b );
+ bIsSignalingNaN = float64_is_signaling_nan( b );
+ a |= LIT64( 0x0008000000000000 );
+ b |= LIT64( 0x0008000000000000 );
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+ if ( aIsNaN ) {
+ return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+ }
+ else {
+ return b;
+ }
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated extended double-precision NaN. The
+`high' and `low' values hold the most- and least-significant bits,
+respectively.
+-------------------------------------------------------------------------------
+*/
+#define floatx80_default_nan_high 0xFFFF
+#define floatx80_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF )
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is a
+NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_is_nan( floatx80 a )
+{
+
+ return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is a
+signaling NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_is_signaling_nan( floatx80 a )
+{
+ //register int lr;
+ bits64 aLow;
+
+ //__asm__("mov %0, lr" : : "g" (lr));
+ //fp_printk("floatx80_is_signalling_nan() called from 0x%08x\n",lr);
+ aLow = a.low & ~ LIT64( 0x4000000000000000 );
+ return
+ ( ( a.high & 0x7FFF ) == 0x7FFF )
+ && (bits64) ( aLow<<1 )
+ && ( a.low == aLow );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the
+invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT floatx80ToCommonNaN( floatx80 a )
+{
+ commonNaNT z;
+
+ if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+ z.sign = a.high>>15;
+ z.low = 0;
+ z.high = a.low<<1;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the extended
+double-precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static floatx80 commonNaNToFloatx80( commonNaNT a )
+{
+ floatx80 z;
+
+ z.low = LIT64( 0xC000000000000000 ) | ( a.high>>1 );
+ z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two extended double-precision floating-point values `a' and `b', one
+of which is a NaN, and returns the appropriate NaN result. If either `a' or
+`b' is a signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b )
+{
+ flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+ aIsNaN = floatx80_is_nan( a );
+ aIsSignalingNaN = floatx80_is_signaling_nan( a );
+ bIsNaN = floatx80_is_nan( b );
+ bIsSignalingNaN = floatx80_is_signaling_nan( b );
+ a.low |= LIT64( 0xC000000000000000 );
+ b.low |= LIT64( 0xC000000000000000 );
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+ if ( aIsNaN ) {
+ return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+ }
+ else {
+ return b;
+ }
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+The pattern for a default generated quadruple-precision NaN. The `high' and
+`low' values hold the most- and least-significant bits, respectively.
+-------------------------------------------------------------------------------
+*/
+#define float128_default_nan_high LIT64( 0xFFFFFFFFFFFFFFFF )
+#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF )
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is a NaN;
+otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float128_is_nan( float128 a )
+{
+
+ return
+ ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
+ && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is a
+signaling NaN; otherwise returns 0.
+-------------------------------------------------------------------------------
+*/
+flag float128_is_signaling_nan( float128 a )
+{
+
+ return
+ ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
+ && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point NaN
+`a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
+exception is raised.
+-------------------------------------------------------------------------------
+*/
+static commonNaNT float128ToCommonNaN( float128 a )
+{
+ commonNaNT z;
+
+ if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
+ z.sign = a.high>>63;
+ shortShift128Left( a.high, a.low, 16, &z.high, &z.low );
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the canonical NaN `a' to the quadruple-
+precision floating-point format.
+-------------------------------------------------------------------------------
+*/
+static float128 commonNaNToFloat128( commonNaNT a )
+{
+ float128 z;
+
+ shift128Right( a.high, a.low, 16, &z.high, &z.low );
+ z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 );
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes two quadruple-precision floating-point values `a' and `b', one of
+which is a NaN, and returns the appropriate NaN result. If either `a' or
+`b' is a signaling NaN, the invalid exception is raised.
+-------------------------------------------------------------------------------
+*/
+static float128 propagateFloat128NaN( float128 a, float128 b )
+{
+ flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+ aIsNaN = float128_is_nan( a );
+ aIsSignalingNaN = float128_is_signaling_nan( a );
+ bIsNaN = float128_is_nan( b );
+ bIsSignalingNaN = float128_is_signaling_nan( b );
+ a.high |= LIT64( 0x0000800000000000 );
+ b.high |= LIT64( 0x0000800000000000 );
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
+ if ( aIsNaN ) {
+ return ( aIsSignalingNaN & bIsNaN ) ? b : a;
+ }
+ else {
+ return b;
+ }
+
+}
+
+#endif
+
--- /dev/null
+/*
+===============================================================================
+
+This C source file is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+#include "milieu.h"
+#include "softfloat.h"
+
+/*
+-------------------------------------------------------------------------------
+Floating-point rounding mode, extended double-precision rounding precision,
+and exception flags.
+-------------------------------------------------------------------------------
+*/
+int8 float_rounding_mode = float_round_nearest_even;
+int8 floatx80_rounding_precision = 80;
+int8 float_exception_flags = 0;
+
+/*
+-------------------------------------------------------------------------------
+Primitive arithmetic functions, including multi-word arithmetic, and
+division and square root approximations. (Can be specialized to target if
+desired.)
+-------------------------------------------------------------------------------
+*/
+#include "softfloat-macros"
+
+/*
+-------------------------------------------------------------------------------
+Functions and definitions to determine: (1) whether tininess for underflow
+is detected before or after rounding by default, (2) what (if anything)
+happens when exceptions are raised, (3) how signaling NaNs are distinguished
+from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs
+are propagated from function inputs to output. These details are target-
+specific.
+-------------------------------------------------------------------------------
+*/
+#include "softfloat-specialize"
+
+/*
+-------------------------------------------------------------------------------
+Takes a 64-bit fixed-point value `absZ' with binary point between bits 6
+and 7, and returns the properly rounded 32-bit integer corresponding to the
+input. If `zSign' is nonzero, the input is negated before being converted
+to an integer. Bit 63 of `absZ' must be zero. Ordinarily, the fixed-point
+input is simply rounded to an integer, with the inexact exception raised if
+the input cannot be represented exactly as an integer. If the fixed-point
+input is too large, however, the invalid exception is raised and the largest
+positive or negative integer is returned.
+-------------------------------------------------------------------------------
+*/
+static int32 roundAndPackInt32( flag zSign, bits64 absZ )
+{
+ int8 roundingMode;
+ flag roundNearestEven;
+ int8 roundIncrement, roundBits;
+ int32 z;
+
+ roundingMode = float_rounding_mode;
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ roundIncrement = 0x40;
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = 0x7F;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = absZ & 0x7F;
+ absZ = ( absZ + roundIncrement )>>7;
+ absZ &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+ z = absZ;
+ if ( zSign ) z = - z;
+ if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) {
+ float_exception_flags |= float_flag_invalid;
+ return zSign ? 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( roundBits ) float_exception_flags |= float_flag_inexact;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the fraction bits of the single-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits32 extractFloat32Frac( float32 a )
+{
+
+ return a & 0x007FFFFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the single-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int16 extractFloat32Exp( float32 a )
+{
+
+ return ( a>>23 ) & 0xFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the single-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloat32Sign( float32 a )
+{
+
+ return a>>31;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal single-precision floating-point value represented
+by the denormalized significand `aSig'. The normalized exponent and
+significand are stored at the locations pointed to by `zExpPtr' and
+`zSigPtr', respectively.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloat32Subnormal( bits32 aSig, int16 *zExpPtr, bits32 *zSigPtr )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros32( aSig ) - 8;
+ *zSigPtr = aSig<<shiftCount;
+ *zExpPtr = 1 - shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+single-precision floating-point value, returning the result. After being
+shifted into the proper positions, the three fields are simply added
+together to form the result. This means that any integer portion of `zSig'
+will be added into the exponent. Since a properly normalized significand
+will have an integer portion equal to 1, the `zExp' input should be 1 less
+than the desired result exponent whenever `zSig' is a complete, normalized
+significand.
+-------------------------------------------------------------------------------
+*/
+INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+#if 0
+ float32 f;
+ __asm__("@ packFloat32;
+ mov %0, %1, asl #31;
+ orr %0, %2, asl #23;
+ orr %0, %3"
+ : /* no outputs */
+ : "g" (f), "g" (zSign), "g" (zExp), "g" (zSig)
+ : "cc");
+ return f;
+#else
+ return ( ( (bits32) zSign )<<31 ) + ( ( (bits32) zExp )<<23 ) + zSig;
+#endif
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper single-precision floating-
+point value corresponding to the abstract input. Ordinarily, the abstract
+value is simply rounded and packed into the single-precision format, with
+the inexact exception raised if the abstract input cannot be represented
+exactly. If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned. If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal single-
+precision floating-point number.
+ The input significand `zSig' has its binary point between bits 30
+and 29, which is 7 bits to the left of the usual location. This shifted
+significand must be normalized or smaller. If `zSig' is not normalized,
+`zExp' must be 0; in that case, the result returned is a subnormal number,
+and it must not require rounding. In the usual case that `zSig' is
+normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+The handling of underflow and overflow follows the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float32 roundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+ int8 roundingMode;
+ flag roundNearestEven;
+ int8 roundIncrement, roundBits;
+ flag isTiny;
+
+ roundingMode = float_rounding_mode;
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ roundIncrement = 0x40;
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = 0x7F;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = zSig & 0x7F;
+ if ( 0xFD <= (bits16) zExp ) {
+ if ( ( 0xFD < zExp )
+ || ( ( zExp == 0xFD )
+ && ( (sbits32) ( zSig + roundIncrement ) < 0 ) )
+ ) {
+ float_raise( float_flag_overflow | float_flag_inexact );
+ return packFloat32( zSign, 0xFF, 0 ) - ( roundIncrement == 0 );
+ }
+ if ( zExp < 0 ) {
+ isTiny =
+ ( float_detect_tininess == float_tininess_before_rounding )
+ || ( zExp < -1 )
+ || ( zSig + roundIncrement < 0x80000000 );
+ shift32RightJamming( zSig, - zExp, &zSig );
+ zExp = 0;
+ roundBits = zSig & 0x7F;
+ if ( isTiny && roundBits ) float_raise( float_flag_underflow );
+ }
+ }
+ if ( roundBits ) float_exception_flags |= float_flag_inexact;
+ zSig = ( zSig + roundIncrement )>>7;
+ zSig &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+ if ( zSig == 0 ) zExp = 0;
+ return packFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper single-precision floating-
+point value corresponding to the abstract input. This routine is just like
+`roundAndPackFloat32' except that `zSig' does not have to be normalized in
+any way. In all cases, `zExp' must be 1 less than the ``true'' floating-
+point exponent.
+-------------------------------------------------------------------------------
+*/
+static float32
+ normalizeRoundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros32( zSig ) - 1;
+ return roundAndPackFloat32( zSign, zExp - shiftCount, zSig<<shiftCount );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the fraction bits of the double-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloat64Frac( float64 a )
+{
+
+ return a & LIT64( 0x000FFFFFFFFFFFFF );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the double-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int16 extractFloat64Exp( float64 a )
+{
+
+ return ( a>>52 ) & 0x7FF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the double-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloat64Sign( float64 a )
+{
+
+ return a>>63;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal double-precision floating-point value represented
+by the denormalized significand `aSig'. The normalized exponent and
+significand are stored at the locations pointed to by `zExpPtr' and
+`zSigPtr', respectively.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloat64Subnormal( bits64 aSig, int16 *zExpPtr, bits64 *zSigPtr )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros64( aSig ) - 11;
+ *zSigPtr = aSig<<shiftCount;
+ *zExpPtr = 1 - shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+double-precision floating-point value, returning the result. After being
+shifted into the proper positions, the three fields are simply added
+together to form the result. This means that any integer portion of `zSig'
+will be added into the exponent. Since a properly normalized significand
+will have an integer portion equal to 1, the `zExp' input should be 1 less
+than the desired result exponent whenever `zSig' is a complete, normalized
+significand.
+-------------------------------------------------------------------------------
+*/
+INLINE float64 packFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+
+ return ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<52 ) + zSig;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper double-precision floating-
+point value corresponding to the abstract input. Ordinarily, the abstract
+value is simply rounded and packed into the double-precision format, with
+the inexact exception raised if the abstract input cannot be represented
+exactly. If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned. If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal double-
+precision floating-point number.
+ The input significand `zSig' has its binary point between bits 62
+and 61, which is 10 bits to the left of the usual location. This shifted
+significand must be normalized or smaller. If `zSig' is not normalized,
+`zExp' must be 0; in that case, the result returned is a subnormal number,
+and it must not require rounding. In the usual case that `zSig' is
+normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+The handling of underflow and overflow follows the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float64 roundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+ int8 roundingMode;
+ flag roundNearestEven;
+ int16 roundIncrement, roundBits;
+ flag isTiny;
+
+ roundingMode = float_rounding_mode;
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ roundIncrement = 0x200;
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = 0x3FF;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = zSig & 0x3FF;
+ if ( 0x7FD <= (bits16) zExp ) {
+ if ( ( 0x7FD < zExp )
+ || ( ( zExp == 0x7FD )
+ && ( (sbits64) ( zSig + roundIncrement ) < 0 ) )
+ ) {
+ //register int lr;
+ //__asm__("mov %0, lr" :: "g" (lr));
+ //fp_printk("roundAndPackFloat64 called from 0x%08x\n",lr);
+ float_raise( float_flag_overflow | float_flag_inexact );
+ return packFloat64( zSign, 0x7FF, 0 ) - ( roundIncrement == 0 );
+ }
+ if ( zExp < 0 ) {
+ isTiny =
+ ( float_detect_tininess == float_tininess_before_rounding )
+ || ( zExp < -1 )
+ || ( zSig + roundIncrement < LIT64( 0x8000000000000000 ) );
+ shift64RightJamming( zSig, - zExp, &zSig );
+ zExp = 0;
+ roundBits = zSig & 0x3FF;
+ if ( isTiny && roundBits ) float_raise( float_flag_underflow );
+ }
+ }
+ if ( roundBits ) float_exception_flags |= float_flag_inexact;
+ zSig = ( zSig + roundIncrement )>>10;
+ zSig &= ~ ( ( ( roundBits ^ 0x200 ) == 0 ) & roundNearestEven );
+ if ( zSig == 0 ) zExp = 0;
+ return packFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand `zSig', and returns the proper double-precision floating-
+point value corresponding to the abstract input. This routine is just like
+`roundAndPackFloat64' except that `zSig' does not have to be normalized in
+any way. In all cases, `zExp' must be 1 less than the ``true'' floating-
+point exponent.
+-------------------------------------------------------------------------------
+*/
+static float64
+ normalizeRoundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros64( zSig ) - 1;
+ return roundAndPackFloat64( zSign, zExp - shiftCount, zSig<<shiftCount );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the fraction bits of the extended double-precision floating-point
+value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloatx80Frac( floatx80 a )
+{
+
+ return a.low;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the extended double-precision floating-point
+value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int32 extractFloatx80Exp( floatx80 a )
+{
+
+ return a.high & 0x7FFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the extended double-precision floating-point value
+`a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloatx80Sign( floatx80 a )
+{
+
+ return a.high>>15;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal extended double-precision floating-point value
+represented by the denormalized significand `aSig'. The normalized exponent
+and significand are stored at the locations pointed to by `zExpPtr' and
+`zSigPtr', respectively.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloatx80Subnormal( bits64 aSig, int32 *zExpPtr, bits64 *zSigPtr )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros64( aSig );
+ *zSigPtr = aSig<<shiftCount;
+ *zExpPtr = 1 - shiftCount;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', exponent `zExp', and significand `zSig' into an
+extended double-precision floating-point value, returning the result.
+-------------------------------------------------------------------------------
+*/
+INLINE floatx80 packFloatx80( flag zSign, int32 zExp, bits64 zSig )
+{
+ floatx80 z;
+
+ z.low = zSig;
+ z.high = ( ( (bits16) zSign )<<15 ) + zExp;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and extended significand formed by the concatenation of `zSig0' and `zSig1',
+and returns the proper extended double-precision floating-point value
+corresponding to the abstract input. Ordinarily, the abstract value is
+rounded and packed into the extended double-precision format, with the
+inexact exception raised if the abstract input cannot be represented
+exactly. If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned. If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal extended
+double-precision floating-point number.
+ If `roundingPrecision' is 32 or 64, the result is rounded to the same
+number of bits as single or double precision, respectively. Otherwise, the
+result is rounded to the full precision of the extended double-precision
+format.
+ The input significand must be normalized or smaller. If the input
+significand is not normalized, `zExp' must be 0; in that case, the result
+returned is a subnormal number, and it must not require rounding. The
+handling of underflow and overflow follows the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static floatx80
+ roundAndPackFloatx80(
+ int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ )
+{
+ int8 roundingMode;
+ flag roundNearestEven, increment, isTiny;
+ int64 roundIncrement, roundMask, roundBits;
+
+ roundingMode = float_rounding_mode;
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ if ( roundingPrecision == 80 ) goto precision80;
+ if ( roundingPrecision == 64 ) {
+ roundIncrement = LIT64( 0x0000000000000400 );
+ roundMask = LIT64( 0x00000000000007FF );
+ }
+ else if ( roundingPrecision == 32 ) {
+ roundIncrement = LIT64( 0x0000008000000000 );
+ roundMask = LIT64( 0x000000FFFFFFFFFF );
+ }
+ else {
+ goto precision80;
+ }
+ zSig0 |= ( zSig1 != 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = roundMask;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = zSig0 & roundMask;
+ if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+ if ( ( 0x7FFE < zExp )
+ || ( ( zExp == 0x7FFE ) && ( zSig0 + roundIncrement < zSig0 ) )
+ ) {
+ goto overflow;
+ }
+ if ( zExp <= 0 ) {
+ isTiny =
+ ( float_detect_tininess == float_tininess_before_rounding )
+ || ( zExp < 0 )
+ || ( zSig0 <= zSig0 + roundIncrement );
+ shift64RightJamming( zSig0, 1 - zExp, &zSig0 );
+ zExp = 0;
+ roundBits = zSig0 & roundMask;
+ if ( isTiny && roundBits ) float_raise( float_flag_underflow );
+ if ( roundBits ) float_exception_flags |= float_flag_inexact;
+ zSig0 += roundIncrement;
+ if ( (sbits64) zSig0 < 0 ) zExp = 1;
+ roundIncrement = roundMask + 1;
+ if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+ roundMask |= roundIncrement;
+ }
+ zSig0 &= ~ roundMask;
+ return packFloatx80( zSign, zExp, zSig0 );
+ }
+ }
+ if ( roundBits ) float_exception_flags |= float_flag_inexact;
+ zSig0 += roundIncrement;
+ if ( zSig0 < roundIncrement ) {
+ ++zExp;
+ zSig0 = LIT64( 0x8000000000000000 );
+ }
+ roundIncrement = roundMask + 1;
+ if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+ roundMask |= roundIncrement;
+ }
+ zSig0 &= ~ roundMask;
+ if ( zSig0 == 0 ) zExp = 0;
+ return packFloatx80( zSign, zExp, zSig0 );
+ precision80:
+ increment = ( (sbits64) zSig1 < 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ increment = 0;
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig1;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig1;
+ }
+ }
+ }
+ if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+ if ( ( 0x7FFE < zExp )
+ || ( ( zExp == 0x7FFE )
+ && ( zSig0 == LIT64( 0xFFFFFFFFFFFFFFFF ) )
+ && increment
+ )
+ ) {
+ roundMask = 0;
+ overflow:
+ float_raise( float_flag_overflow | float_flag_inexact );
+ if ( ( roundingMode == float_round_to_zero )
+ || ( zSign && ( roundingMode == float_round_up ) )
+ || ( ! zSign && ( roundingMode == float_round_down ) )
+ ) {
+ return packFloatx80( zSign, 0x7FFE, ~ roundMask );
+ }
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( zExp <= 0 ) {
+ isTiny =
+ ( float_detect_tininess == float_tininess_before_rounding )
+ || ( zExp < 0 )
+ || ! increment
+ || ( zSig0 < LIT64( 0xFFFFFFFFFFFFFFFF ) );
+ shift64ExtraRightJamming( zSig0, zSig1, 1 - zExp, &zSig0, &zSig1 );
+ zExp = 0;
+ if ( isTiny && zSig1 ) float_raise( float_flag_underflow );
+ if ( zSig1 ) float_exception_flags |= float_flag_inexact;
+ if ( roundNearestEven ) {
+ increment = ( (sbits64) zSig1 < 0 );
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig1;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig1;
+ }
+ }
+ if ( increment ) {
+ ++zSig0;
+ zSig0 &= ~ ( ( zSig1 + zSig1 == 0 ) & roundNearestEven );
+ if ( (sbits64) zSig0 < 0 ) zExp = 1;
+ }
+ return packFloatx80( zSign, zExp, zSig0 );
+ }
+ }
+ if ( zSig1 ) float_exception_flags |= float_flag_inexact;
+ if ( increment ) {
+ ++zSig0;
+ if ( zSig0 == 0 ) {
+ ++zExp;
+ zSig0 = LIT64( 0x8000000000000000 );
+ }
+ else {
+ zSig0 &= ~ ( ( zSig1 + zSig1 == 0 ) & roundNearestEven );
+ }
+ }
+ else {
+ if ( zSig0 == 0 ) zExp = 0;
+ }
+
+ return packFloatx80( zSign, zExp, zSig0 );
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent
+`zExp', and significand formed by the concatenation of `zSig0' and `zSig1',
+and returns the proper extended double-precision floating-point value
+corresponding to the abstract input. This routine is just like
+`roundAndPackFloatx80' except that the input significand does not have to be
+normalized.
+-------------------------------------------------------------------------------
+*/
+static floatx80
+ normalizeRoundAndPackFloatx80(
+ int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ )
+{
+ int8 shiftCount;
+
+ if ( zSig0 == 0 ) {
+ zSig0 = zSig1;
+ zSig1 = 0;
+ zExp -= 64;
+ }
+ shiftCount = countLeadingZeros64( zSig0 );
+ shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+ zExp -= shiftCount;
+ return
+ roundAndPackFloatx80( roundingPrecision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the least-significant 64 fraction bits of the quadruple-precision
+floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloat128Frac1( float128 a )
+{
+
+ return a.low;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the most-significant 48 fraction bits of the quadruple-precision
+floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE bits64 extractFloat128Frac0( float128 a )
+{
+
+ return a.high & LIT64( 0x0000FFFFFFFFFFFF );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the exponent bits of the quadruple-precision floating-point value
+`a'.
+-------------------------------------------------------------------------------
+*/
+INLINE int32 extractFloat128Exp( float128 a )
+{
+
+ return ( a.high>>48 ) & 0x7FFF;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the sign bit of the quadruple-precision floating-point value `a'.
+-------------------------------------------------------------------------------
+*/
+INLINE flag extractFloat128Sign( float128 a )
+{
+
+ return a.high>>63;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Normalizes the subnormal quadruple-precision floating-point value
+represented by the denormalized significand formed by the concatenation of
+`aSig0' and `aSig1'. The normalized exponent is stored at the location
+pointed to by `zExpPtr'. The most significant 49 bits of the normalized
+significand are stored at the location pointed to by `zSig0Ptr', and the
+least significant 64 bits of the normalized significand are stored at the
+location pointed to by `zSig1Ptr'.
+-------------------------------------------------------------------------------
+*/
+static void
+ normalizeFloat128Subnormal(
+ bits64 aSig0,
+ bits64 aSig1,
+ int32 *zExpPtr,
+ bits64 *zSig0Ptr,
+ bits64 *zSig1Ptr
+ )
+{
+ int8 shiftCount;
+
+ if ( aSig0 == 0 ) {
+ shiftCount = countLeadingZeros64( aSig1 ) - 15;
+ if ( shiftCount < 0 ) {
+ *zSig0Ptr = aSig1>>( - shiftCount );
+ *zSig1Ptr = aSig1<<( shiftCount & 63 );
+ }
+ else {
+ *zSig0Ptr = aSig1<<shiftCount;
+ *zSig1Ptr = 0;
+ }
+ *zExpPtr = - shiftCount - 63;
+ }
+ else {
+ shiftCount = countLeadingZeros64( aSig0 ) - 15;
+ shortShift128Left( aSig0, aSig1, shiftCount, zSig0Ptr, zSig1Ptr );
+ *zExpPtr = 1 - shiftCount;
+ }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Packs the sign `zSign', the exponent `zExp', and the significand formed
+by the concatenation of `zSig0' and `zSig1' into a quadruple-precision
+floating-point value, returning the result. After being shifted into the
+proper positions, the three fields `zSign', `zExp', and `zSig0' are simply
+added together to form the most significant 32 bits of the result. This
+means that any integer portion of `zSig0' will be added into the exponent.
+Since a properly normalized significand will have an integer portion equal
+to 1, the `zExp' input should be 1 less than the desired result exponent
+whenever `zSig0' and `zSig1' concatenated form a complete, normalized
+significand.
+-------------------------------------------------------------------------------
+*/
+INLINE float128
+ packFloat128( flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 )
+{
+ float128 z;
+
+ z.low = zSig1;
+ z.high = ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<48 ) + zSig0;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and extended significand formed by the concatenation of `zSig0', `zSig1',
+and `zSig2', and returns the proper quadruple-precision floating-point value
+corresponding to the abstract input. Ordinarily, the abstract value is
+simply rounded and packed into the quadruple-precision format, with the
+inexact exception raised if the abstract input cannot be represented
+exactly. If the abstract value is too large, however, the overflow and
+inexact exceptions are raised and an infinity or maximal finite value is
+returned. If the abstract value is too small, the input value is rounded to
+a subnormal number, and the underflow and inexact exceptions are raised if
+the abstract input cannot be represented exactly as a subnormal quadruple-
+precision floating-point number.
+ The input significand must be normalized or smaller. If the input
+significand is not normalized, `zExp' must be 0; in that case, the result
+returned is a subnormal number, and it must not require rounding. In the
+usual case that the input significand is normalized, `zExp' must be 1 less
+than the ``true'' floating-point exponent. The handling of underflow and
+overflow follows the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float128
+ roundAndPackFloat128(
+ flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1, bits64 zSig2 )
+{
+ int8 roundingMode;
+ flag roundNearestEven, increment, isTiny;
+
+ roundingMode = float_rounding_mode;
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ increment = ( (sbits64) zSig2 < 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ increment = 0;
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig2;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig2;
+ }
+ }
+ }
+ if ( 0x7FFD <= (bits32) zExp ) {
+ if ( ( 0x7FFD < zExp )
+ || ( ( zExp == 0x7FFD )
+ && eq128(
+ LIT64( 0x0001FFFFFFFFFFFF ),
+ LIT64( 0xFFFFFFFFFFFFFFFF ),
+ zSig0,
+ zSig1
+ )
+ && increment
+ )
+ ) {
+ float_raise( float_flag_overflow | float_flag_inexact );
+ if ( ( roundingMode == float_round_to_zero )
+ || ( zSign && ( roundingMode == float_round_up ) )
+ || ( ! zSign && ( roundingMode == float_round_down ) )
+ ) {
+ return
+ packFloat128(
+ zSign,
+ 0x7FFE,
+ LIT64( 0x0000FFFFFFFFFFFF ),
+ LIT64( 0xFFFFFFFFFFFFFFFF )
+ );
+ }
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( zExp < 0 ) {
+ isTiny =
+ ( float_detect_tininess == float_tininess_before_rounding )
+ || ( zExp < -1 )
+ || ! increment
+ || lt128(
+ zSig0,
+ zSig1,
+ LIT64( 0x0001FFFFFFFFFFFF ),
+ LIT64( 0xFFFFFFFFFFFFFFFF )
+ );
+ shift128ExtraRightJamming(
+ zSig0, zSig1, zSig2, - zExp, &zSig0, &zSig1, &zSig2 );
+ zExp = 0;
+ if ( isTiny && zSig2 ) float_raise( float_flag_underflow );
+ if ( roundNearestEven ) {
+ increment = ( (sbits64) zSig2 < 0 );
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig2;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig2;
+ }
+ }
+ }
+ }
+ if ( zSig2 ) float_exception_flags |= float_flag_inexact;
+ if ( increment ) {
+ add128( zSig0, zSig1, 0, 1, &zSig0, &zSig1 );
+ zSig1 &= ~ ( ( zSig2 + zSig2 == 0 ) & roundNearestEven );
+ }
+ else {
+ if ( ( zSig0 | zSig1 ) == 0 ) zExp = 0;
+ }
+ return packFloat128( zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+and significand formed by the concatenation of `zSig0' and `zSig1', and
+returns the proper quadruple-precision floating-point value corresponding to
+the abstract input. This routine is just like `roundAndPackFloat128' except
+that the input significand has fewer bits and does not have to be normalized
+in any way. In all cases, `zExp' must be 1 less than the ``true'' floating-
+point exponent.
+-------------------------------------------------------------------------------
+*/
+static float128
+ normalizeRoundAndPackFloat128(
+ flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 )
+{
+ int8 shiftCount;
+ bits64 zSig2;
+
+ if ( zSig0 == 0 ) {
+ zSig0 = zSig1;
+ zSig1 = 0;
+ zExp -= 64;
+ }
+ shiftCount = countLeadingZeros64( zSig0 ) - 15;
+ if ( 0 <= shiftCount ) {
+ zSig2 = 0;
+ shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+ }
+ else {
+ shift128ExtraRightJamming(
+ zSig0, zSig1, 0, - shiftCount, &zSig0, &zSig1, &zSig2 );
+ }
+ zExp -= shiftCount;
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a' to
+the single-precision floating-point format. The conversion is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 int32_to_float32( int32 a )
+{
+ flag zSign;
+
+ if ( a == 0 ) return 0;
+ if ( a == 0x80000000 ) return packFloat32( 1, 0x9E, 0 );
+ zSign = ( a < 0 );
+ return normalizeRoundAndPackFloat32( zSign, 0x9C, zSign ? - a : a );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a' to
+the double-precision floating-point format. The conversion is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 int32_to_float64( int32 a )
+{
+ flag aSign;
+ uint32 absA;
+ int8 shiftCount;
+ bits64 zSig;
+
+ if ( a == 0 ) return 0;
+ aSign = ( a < 0 );
+ absA = aSign ? - a : a;
+ shiftCount = countLeadingZeros32( absA ) + 21;
+ zSig = absA;
+ return packFloat64( aSign, 0x432 - shiftCount, zSig<<shiftCount );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a'
+to the extended double-precision floating-point format. The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 int32_to_floatx80( int32 a )
+{
+ flag zSign;
+ uint32 absA;
+ int8 shiftCount;
+ bits64 zSig;
+
+ if ( a == 0 ) return packFloatx80( 0, 0, 0 );
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros32( absA ) + 32;
+ zSig = absA;
+ return packFloatx80( zSign, 0x403E - shiftCount, zSig<<shiftCount );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the 32-bit two's complement integer `a' to
+the quadruple-precision floating-point format. The conversion is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 int32_to_float128( int32 a )
+{
+ flag zSign;
+ uint32 absA;
+ int8 shiftCount;
+ bits64 zSig0;
+
+ if ( a == 0 ) return packFloat128( 0, 0, 0, 0 );
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros32( absA ) + 17;
+ zSig0 = absA;
+ return packFloat128( zSign, 0x402E - shiftCount, zSig0<<shiftCount, 0 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the 32-bit two's complement integer format. The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode. If `a' is a NaN, the largest
+positive integer is returned. Otherwise, if the conversion overflows, the
+largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float32_to_int32( float32 a )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ bits64 zSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+ if ( aExp ) aSig |= 0x00800000;
+ shiftCount = 0xAF - aExp;
+ zSig = aSig;
+ zSig <<= 32;
+ if ( 0 < shiftCount ) shift64RightJamming( zSig, shiftCount, &zSig );
+ return roundAndPackInt32( aSign, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the 32-bit two's complement integer format. The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero. If
+`a' is a NaN, the largest positive integer is returned. Otherwise, if the
+conversion overflows, the largest integer with the same sign as `a' is
+returned.
+-------------------------------------------------------------------------------
+*/
+int32 float32_to_int32_round_to_zero( float32 a )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ int32 z;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ shiftCount = aExp - 0x9E;
+ if ( 0 <= shiftCount ) {
+ if ( a == 0xCF000000 ) return 0x80000000;
+ float_raise( float_flag_invalid );
+ if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) return 0x7FFFFFFF;
+ return 0x80000000;
+ }
+ else if ( aExp <= 0x7E ) {
+ if ( aExp | aSig ) float_exception_flags |= float_flag_inexact;
+ return 0;
+ }
+ aSig = ( aSig | 0x00800000 )<<8;
+ z = aSig>>( - shiftCount );
+ if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) {
+ float_exception_flags |= float_flag_inexact;
+ }
+ return aSign ? - z : z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the double-precision floating-point format. The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float32_to_float64( float32 a )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a ) );
+ return packFloat64( aSign, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat64( aSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ --aExp;
+ }
+ return packFloat64( aSign, aExp + 0x380, ( (bits64) aSig )<<29 );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the extended double-precision floating-point format. The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 float32_to_floatx80( float32 a )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a ) );
+ return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ aSig |= 0x00800000;
+ return packFloatx80( aSign, aExp + 0x3F80, ( (bits64) aSig )<<40 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the single-precision floating-point value
+`a' to the double-precision floating-point format. The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float32_to_float128( float32 a )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a ) );
+ return packFloat128( aSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ --aExp;
+ }
+ return packFloat128( aSign, aExp + 0x3F80, ( (bits64) aSig )<<25, 0 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the single-precision floating-point value `a' to an integer, and
+returns the result as a single-precision floating-point value. The
+operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_round_to_int( float32 a )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ float32 z;
+
+ aExp = extractFloat32Exp( a );
+ if ( 0x96 <= aExp ) {
+ if ( ( aExp == 0xFF ) && extractFloat32Frac( a ) ) {
+ return propagateFloat32NaN( a, a );
+ }
+ return a;
+ }
+ if ( aExp <= 0x7E ) {
+ if ( (bits32) ( a<<1 ) == 0 ) return a;
+ float_exception_flags |= float_flag_inexact;
+ aSign = extractFloat32Sign( a );
+ switch ( float_rounding_mode ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x7E ) && extractFloat32Frac( a ) ) {
+ return packFloat32( aSign, 0x7F, 0 );
+ }
+ break;
+ case float_round_down:
+ return aSign ? 0xBF800000 : 0;
+ case float_round_up:
+ return aSign ? 0x80000000 : 0x3F800000;
+ }
+ return packFloat32( aSign, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x96 - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z = a;
+ roundingMode = float_rounding_mode;
+ if ( roundingMode == float_round_nearest_even ) {
+ z += lastBitMask>>1;
+ if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat32Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+ z += roundBitsMask;
+ }
+ }
+ z &= ~ roundBitsMask;
+ if ( z != a ) float_exception_flags |= float_flag_inexact;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the single-precision
+floating-point values `a' and `b'. If `zSign' is true, the sum is negated
+before being returned. `zSign' is ignored if the result is a NaN. The
+addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float32 addFloat32Sigs( float32 a, float32 b, flag zSign )
+{
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 6;
+ bSig <<= 6;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= 0x20000000;
+ }
+ shift32RightJamming( bSig, expDiff, &bSig );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b );
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= 0x20000000;
+ }
+ shift32RightJamming( aSig, - expDiff, &aSig );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0xFF ) {
+ if ( aSig | bSig ) return propagateFloat32NaN( a, b );
+ return a;
+ }
+ if ( aExp == 0 ) return packFloat32( zSign, 0, ( aSig + bSig )>>6 );
+ zSig = 0x40000000 + aSig + bSig;
+ zExp = aExp;
+ goto roundAndPack;
+ }
+ aSig |= 0x20000000;
+ zSig = ( aSig + bSig )<<1;
+ --zExp;
+ if ( (sbits32) zSig < 0 ) {
+ zSig = aSig + bSig;
+ ++zExp;
+ }
+ roundAndPack:
+ return roundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the single-
+precision floating-point values `a' and `b'. If `zSign' is true, the
+difference is negated before being returned. `zSign' is ignored if the
+result is a NaN. The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float32 subFloat32Sigs( float32 a, float32 b, flag zSign )
+{
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 7;
+ bSig <<= 7;
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0xFF ) {
+ if ( aSig | bSig ) return propagateFloat32NaN( a, b );
+ float_raise( float_flag_invalid );
+ return float32_default_nan;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ if ( bSig < aSig ) goto aBigger;
+ if ( aSig < bSig ) goto bBigger;
+ return packFloat32( float_rounding_mode == float_round_down, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b );
+ return packFloat32( zSign ^ 1, 0xFF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= 0x40000000;
+ }
+ shift32RightJamming( aSig, - expDiff, &aSig );
+ bSig |= 0x40000000;
+ bBigger:
+ zSig = bSig - aSig;
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= 0x40000000;
+ }
+ shift32RightJamming( bSig, expDiff, &bSig );
+ aSig |= 0x40000000;
+ aBigger:
+ zSig = aSig - bSig;
+ zExp = aExp;
+ normalizeRoundAndPack:
+ --zExp;
+ return normalizeRoundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the single-precision floating-point values `a'
+and `b'. The operation is performed according to the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_add( float32 a, float32 b )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign == bSign ) {
+ return addFloat32Sigs( a, b, aSign );
+ }
+ else {
+ return subFloat32Sigs( a, b, aSign );
+ }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the single-precision floating-point values
+`a' and `b'. The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_sub( float32 a, float32 b )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign == bSign ) {
+ return subFloat32Sigs( a, b, aSign );
+ }
+ else {
+ return addFloat32Sigs( a, b, aSign );
+ }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the single-precision floating-point values
+`a' and `b'. The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_mul( float32 a, float32 b )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig;
+ bits64 zSig64;
+ bits32 zSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ bSign = extractFloat32Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0xFF ) {
+ if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+ return propagateFloat32NaN( a, b );
+ }
+ if ( ( bExp | bSig ) == 0 ) {
+ float_raise( float_flag_invalid );
+ return float32_default_nan;
+ }
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b );
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid );
+ return float32_default_nan;
+ }
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) return packFloat32( zSign, 0, 0 );
+ normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+ }
+ zExp = aExp + bExp - 0x7F;
+ aSig = ( aSig | 0x00800000 )<<7;
+ bSig = ( bSig | 0x00800000 )<<8;
+ shift64RightJamming( ( (bits64) aSig ) * bSig, 32, &zSig64 );
+ zSig = zSig64;
+ if ( 0 <= (sbits32) ( zSig<<1 ) ) {
+ zSig <<= 1;
+ --zExp;
+ }
+ return roundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the single-precision floating-point value `a'
+by the corresponding value `b'. The operation is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_div( float32 a, float32 b )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig, zSig;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ bSign = extractFloat32Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, b );
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b );
+ float_raise( float_flag_invalid );
+ return float32_default_nan;
+ }
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b );
+ return packFloat32( zSign, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid );
+ return float32_default_nan;
+ }
+ float_raise( float_flag_divbyzero );
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = aExp - bExp + 0x7D;
+ aSig = ( aSig | 0x00800000 )<<7;
+ bSig = ( bSig | 0x00800000 )<<8;
+ if ( bSig <= ( aSig + aSig ) ) {
+ aSig >>= 1;
+ ++zExp;
+ }
+ zSig = ( ( (bits64) aSig )<<32 ) / bSig;
+ if ( ( zSig & 0x3F ) == 0 ) {
+ zSig |= ( ( (bits64) bSig ) * zSig != ( (bits64) aSig )<<32 );
+ }
+ return roundAndPackFloat32( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the single-precision floating-point value `a'
+with respect to the corresponding value `b'. The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_rem( float32 a, float32 b )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, expDiff;
+ bits32 aSig, bSig;
+ bits32 q;
+ bits64 aSig64, bSig64, q64;
+ bits32 alternateASig;
+ sbits32 sigMean;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ bSign = extractFloat32Sign( b );
+ if ( aExp == 0xFF ) {
+ if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+ return propagateFloat32NaN( a, b );
+ }
+ float_raise( float_flag_invalid );
+ return float32_default_nan;
+ }
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ float_raise( float_flag_invalid );
+ return float32_default_nan;
+ }
+ normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return a;
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ expDiff = aExp - bExp;
+ aSig |= 0x00800000;
+ bSig |= 0x00800000;
+ if ( expDiff < 32 ) {
+ aSig <<= 8;
+ bSig <<= 8;
+ if ( expDiff < 0 ) {
+ if ( expDiff < -1 ) return a;
+ aSig >>= 1;
+ }
+ q = ( bSig <= aSig );
+ if ( q ) aSig -= bSig;
+ if ( 0 < expDiff ) {
+ q = ( ( (bits64) aSig )<<32 ) / bSig;
+ q >>= 32 - expDiff;
+ bSig >>= 2;
+ aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+ }
+ else {
+ aSig >>= 2;
+ bSig >>= 2;
+ }
+ }
+ else {
+ if ( bSig <= aSig ) aSig -= bSig;
+ aSig64 = ( (bits64) aSig )<<40;
+ bSig64 = ( (bits64) bSig )<<40;
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+ q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+ aSig64 = - ( ( bSig * q64 )<<38 );
+ expDiff -= 62;
+ }
+ expDiff += 64;
+ q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+ q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+ q = q64>>( 64 - expDiff );
+ bSig <<= 6;
+ aSig = ( ( aSig64>>33 )<<( expDiff - 1 ) ) - bSig * q;
+ }
+ do {
+ alternateASig = aSig;
+ ++q;
+ aSig -= bSig;
+ } while ( 0 <= (sbits32) aSig );
+ sigMean = aSig + alternateASig;
+ if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+ aSig = alternateASig;
+ }
+ zSign = ( (sbits32) aSig < 0 );
+ if ( zSign ) aSig = - aSig;
+ return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the single-precision floating-point value `a'.
+The operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float32_sqrt( float32 a )
+{
+ flag aSign;
+ int16 aExp, zExp;
+ bits32 aSig, zSig;
+ bits64 rem, term;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, 0 );
+ if ( ! aSign ) return a;
+ float_raise( float_flag_invalid );
+ return float32_default_nan;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig ) == 0 ) return a;
+ float_raise( float_flag_invalid );
+ return float32_default_nan;
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return 0;
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = ( ( aExp - 0x7F )>>1 ) + 0x7E;
+ aSig = ( aSig | 0x00800000 )<<8;
+ zSig = estimateSqrt32( aExp, aSig ) + 2;
+ if ( ( zSig & 0x7F ) <= 5 ) {
+ if ( zSig < 2 ) {
+ zSig = 0xFFFFFFFF;
+ }
+ else {
+ aSig >>= aExp & 1;
+ term = ( (bits64) zSig ) * zSig;
+ rem = ( ( (bits64) aSig )<<32 ) - term;
+ while ( (sbits64) rem < 0 ) {
+ --zSig;
+ rem += ( ( (bits64) zSig )<<1 ) | 1;
+ }
+ zSig |= ( rem != 0 );
+ }
+ }
+ shift32RightJamming( zSig, 1, &zSig );
+ return roundAndPackFloat32( 0, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise. The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_eq( float32 a, float32 b )
+{
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise. The comparison is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_le( float32 a, float32 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 );
+ return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise. The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_lt( float32 a, float32 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 );
+ return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise. The invalid exception is raised
+if either operand is a NaN. Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_eq_signaling( float32 a, float32 b )
+{
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not
+cause an exception. Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_le_quiet( float32 a, float32 b )
+{
+ flag aSign, bSign;
+ //int16 aExp, bExp;
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 );
+ return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the single-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an
+exception. Otherwise, the comparison is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float32_lt_quiet( float32 a, float32 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 );
+ return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement integer format. The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode. If `a' is a NaN, the largest
+positive integer is returned. Otherwise, if the conversion overflows, the
+largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_int32( float64 a )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+ if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = 0x42C - aExp;
+ if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig );
+ return roundAndPackInt32( aSign, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement integer format. The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero. If
+`a' is a NaN, the largest positive integer is returned. Otherwise, if the
+conversion overflows, the largest integer with the same sign as `a' is
+returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_int32_round_to_zero( float64 a )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig, savedASig;
+ int32 z;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ shiftCount = 0x433 - aExp;
+ if ( shiftCount < 21 ) {
+ if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+ goto invalid;
+ }
+ else if ( 52 < shiftCount ) {
+ if ( aExp || aSig ) float_exception_flags |= float_flag_inexact;
+ return 0;
+ }
+ aSig |= LIT64( 0x0010000000000000 );
+ savedASig = aSig;
+ aSig >>= shiftCount;
+ z = aSig;
+ if ( aSign ) z = - z;
+ if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+ float_exception_flags |= float_flag_invalid;
+ return aSign ? 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( ( aSig<<shiftCount ) != savedASig ) {
+ float_exception_flags |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement unsigned integer format. The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode. If `a' is a NaN, the largest
+positive integer is returned. Otherwise, if the conversion overflows, the
+largest positive integer is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_uint32( float64 a )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = 0; //extractFloat64Sign( a );
+ //if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+ if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = 0x42C - aExp;
+ if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig );
+ return roundAndPackInt32( aSign, aSig );
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the 32-bit two's complement integer format. The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero. If
+`a' is a NaN, the largest positive integer is returned. Otherwise, if the
+conversion overflows, the largest positive integer is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float64_to_uint32_round_to_zero( float64 a )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig, savedASig;
+ int32 z;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ shiftCount = 0x433 - aExp;
+ if ( shiftCount < 21 ) {
+ if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+ goto invalid;
+ }
+ else if ( 52 < shiftCount ) {
+ if ( aExp || aSig ) float_exception_flags |= float_flag_inexact;
+ return 0;
+ }
+ aSig |= LIT64( 0x0010000000000000 );
+ savedASig = aSig;
+ aSig >>= shiftCount;
+ z = aSig;
+ if ( aSign ) z = - z;
+ if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+ float_exception_flags |= float_flag_invalid;
+ return aSign ? 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( ( aSig<<shiftCount ) != savedASig ) {
+ float_exception_flags |= float_flag_inexact;
+ }
+ return z;
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the single-precision floating-point format. The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float64_to_float32( float64 a )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig;
+ bits32 zSig;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a ) );
+ return packFloat32( aSign, 0xFF, 0 );
+ }
+ shift64RightJamming( aSig, 22, &aSig );
+ zSig = aSig;
+ if ( aExp || zSig ) {
+ zSig |= 0x40000000;
+ aExp -= 0x381;
+ }
+ return roundAndPackFloat32( aSign, aExp, zSig );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the extended double-precision floating-point format. The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 float64_to_floatx80( float64 a )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a ) );
+ return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ return
+ packFloatx80(
+ aSign, aExp + 0x3C00, ( aSig | LIT64( 0x0010000000000000 ) )<<11 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the double-precision floating-point value
+`a' to the quadruple-precision floating-point format. The conversion is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float64_to_float128( float64 a )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig, zSig0, zSig1;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a ) );
+ return packFloat128( aSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ --aExp;
+ }
+ shift128Right( aSig, 0, 4, &zSig0, &zSig1 );
+ return packFloat128( aSign, aExp + 0x3C00, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the double-precision floating-point value `a' to an integer, and
+returns the result as a double-precision floating-point value. The
+operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_round_to_int( float64 a )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ float64 z;
+
+ aExp = extractFloat64Exp( a );
+ if ( 0x433 <= aExp ) {
+ if ( ( aExp == 0x7FF ) && extractFloat64Frac( a ) ) {
+ return propagateFloat64NaN( a, a );
+ }
+ return a;
+ }
+ if ( aExp <= 0x3FE ) {
+ if ( (bits64) ( a<<1 ) == 0 ) return a;
+ float_exception_flags |= float_flag_inexact;
+ aSign = extractFloat64Sign( a );
+ switch ( float_rounding_mode ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x3FE ) && extractFloat64Frac( a ) ) {
+ return packFloat64( aSign, 0x3FF, 0 );
+ }
+ break;
+ case float_round_down:
+ return aSign ? LIT64( 0xBFF0000000000000 ) : 0;
+ case float_round_up:
+ return
+ aSign ? LIT64( 0x8000000000000000 ) : LIT64( 0x3FF0000000000000 );
+ }
+ return packFloat64( aSign, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x433 - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z = a;
+ roundingMode = float_rounding_mode;
+ if ( roundingMode == float_round_nearest_even ) {
+ z += lastBitMask>>1;
+ if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat64Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+ z += roundBitsMask;
+ }
+ }
+ z &= ~ roundBitsMask;
+ if ( z != a ) float_exception_flags |= float_flag_inexact;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the double-precision
+floating-point values `a' and `b'. If `zSign' is true, the sum is negated
+before being returned. `zSign' is ignored if the result is a NaN. The
+addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float64 addFloat64Sigs( float64 a, float64 b, flag zSign )
+{
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 9;
+ bSig <<= 9;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= LIT64( 0x2000000000000000 );
+ }
+ shift64RightJamming( bSig, expDiff, &bSig );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b );
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= LIT64( 0x2000000000000000 );
+ }
+ shift64RightJamming( aSig, - expDiff, &aSig );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0x7FF ) {
+ if ( aSig | bSig ) return propagateFloat64NaN( a, b );
+ return a;
+ }
+ if ( aExp == 0 ) return packFloat64( zSign, 0, ( aSig + bSig )>>9 );
+ zSig = LIT64( 0x4000000000000000 ) + aSig + bSig;
+ zExp = aExp;
+ goto roundAndPack;
+ }
+ aSig |= LIT64( 0x2000000000000000 );
+ zSig = ( aSig + bSig )<<1;
+ --zExp;
+ if ( (sbits64) zSig < 0 ) {
+ zSig = aSig + bSig;
+ ++zExp;
+ }
+ roundAndPack:
+ return roundAndPackFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the double-
+precision floating-point values `a' and `b'. If `zSign' is true, the
+difference is negated before being returned. `zSign' is ignored if the
+result is a NaN. The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float64 subFloat64Sigs( float64 a, float64 b, flag zSign )
+{
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 10;
+ bSig <<= 10;
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0x7FF ) {
+ if ( aSig | bSig ) return propagateFloat64NaN( a, b );
+ float_raise( float_flag_invalid );
+ return float64_default_nan;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ if ( bSig < aSig ) goto aBigger;
+ if ( aSig < bSig ) goto bBigger;
+ return packFloat64( float_rounding_mode == float_round_down, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b );
+ return packFloat64( zSign ^ 1, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= LIT64( 0x4000000000000000 );
+ }
+ shift64RightJamming( aSig, - expDiff, &aSig );
+ bSig |= LIT64( 0x4000000000000000 );
+ bBigger:
+ zSig = bSig - aSig;
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= LIT64( 0x4000000000000000 );
+ }
+ shift64RightJamming( bSig, expDiff, &bSig );
+ aSig |= LIT64( 0x4000000000000000 );
+ aBigger:
+ zSig = aSig - bSig;
+ zExp = aExp;
+ normalizeRoundAndPack:
+ --zExp;
+ return normalizeRoundAndPackFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the double-precision floating-point values `a'
+and `b'. The operation is performed according to the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_add( float64 a, float64 b )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign == bSign ) {
+ return addFloat64Sigs( a, b, aSign );
+ }
+ else {
+ return subFloat64Sigs( a, b, aSign );
+ }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the double-precision floating-point values
+`a' and `b'. The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_sub( float64 a, float64 b )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign == bSign ) {
+ return subFloat64Sigs( a, b, aSign );
+ }
+ else {
+ return addFloat64Sigs( a, b, aSign );
+ }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the double-precision floating-point values
+`a' and `b'. The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_mul( float64 a, float64 b )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ bSign = extractFloat64Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FF ) {
+ if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+ return propagateFloat64NaN( a, b );
+ }
+ if ( ( bExp | bSig ) == 0 ) {
+ float_raise( float_flag_invalid );
+ return float64_default_nan;
+ }
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b );
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid );
+ return float64_default_nan;
+ }
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) return packFloat64( zSign, 0, 0 );
+ normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+ }
+ zExp = aExp + bExp - 0x3FF;
+ aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+ bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+ mul64To128( aSig, bSig, &zSig0, &zSig1 );
+ zSig0 |= ( zSig1 != 0 );
+ if ( 0 <= (sbits64) ( zSig0<<1 ) ) {
+ zSig0 <<= 1;
+ --zExp;
+ }
+ return roundAndPackFloat64( zSign, zExp, zSig0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the double-precision floating-point value `a'
+by the corresponding value `b'. The operation is performed according to
+the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_div( float64 a, float64 b )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig;
+ bits64 rem0, rem1;
+ bits64 term0, term1;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ bSign = extractFloat64Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, b );
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b );
+ float_raise( float_flag_invalid );
+ return float64_default_nan;
+ }
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b );
+ return packFloat64( zSign, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid );
+ return float64_default_nan;
+ }
+ float_raise( float_flag_divbyzero );
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = aExp - bExp + 0x3FD;
+ aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+ bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+ if ( bSig <= ( aSig + aSig ) ) {
+ aSig >>= 1;
+ ++zExp;
+ }
+ zSig = estimateDiv128To64( aSig, 0, bSig );
+ if ( ( zSig & 0x1FF ) <= 2 ) {
+ mul64To128( bSig, zSig, &term0, &term1 );
+ sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig;
+ add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+ }
+ zSig |= ( rem1 != 0 );
+ }
+ return roundAndPackFloat64( zSign, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the double-precision floating-point value `a'
+with respect to the corresponding value `b'. The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_rem( float64 a, float64 b )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, expDiff;
+ bits64 aSig, bSig;
+ bits64 q, alternateASig;
+ sbits64 sigMean;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ bSign = extractFloat64Sign( b );
+ if ( aExp == 0x7FF ) {
+ if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+ return propagateFloat64NaN( a, b );
+ }
+ float_raise( float_flag_invalid );
+ return float64_default_nan;
+ }
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ float_raise( float_flag_invalid );
+ return float64_default_nan;
+ }
+ normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return a;
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ expDiff = aExp - bExp;
+ aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<11;
+ bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+ if ( expDiff < 0 ) {
+ if ( expDiff < -1 ) return a;
+ aSig >>= 1;
+ }
+ q = ( bSig <= aSig );
+ if ( q ) aSig -= bSig;
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig, 0, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ aSig = - ( ( bSig>>2 ) * q );
+ expDiff -= 62;
+ }
+ expDiff += 64;
+ if ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig, 0, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ q >>= 64 - expDiff;
+ bSig >>= 2;
+ aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+ }
+ else {
+ aSig >>= 2;
+ bSig >>= 2;
+ }
+ do {
+ alternateASig = aSig;
+ ++q;
+ aSig -= bSig;
+ } while ( 0 <= (sbits64) aSig );
+ sigMean = aSig + alternateASig;
+ if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+ aSig = alternateASig;
+ }
+ zSign = ( (sbits64) aSig < 0 );
+ if ( zSign ) aSig = - aSig;
+ return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the double-precision floating-point value `a'.
+The operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float64_sqrt( float64 a )
+{
+ flag aSign;
+ int16 aExp, zExp;
+ bits64 aSig, zSig;
+ bits64 rem0, rem1, term0, term1; //, shiftedRem;
+ //float64 z;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, a );
+ if ( ! aSign ) return a;
+ float_raise( float_flag_invalid );
+ return float64_default_nan;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig ) == 0 ) return a;
+ float_raise( float_flag_invalid );
+ return float64_default_nan;
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return 0;
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = ( ( aExp - 0x3FF )>>1 ) + 0x3FE;
+ aSig |= LIT64( 0x0010000000000000 );
+ zSig = estimateSqrt32( aExp, aSig>>21 );
+ zSig <<= 31;
+ aSig <<= 9 - ( aExp & 1 );
+ zSig = estimateDiv128To64( aSig, 0, zSig ) + zSig + 2;
+ if ( ( zSig & 0x3FF ) <= 5 ) {
+ if ( zSig < 2 ) {
+ zSig = LIT64( 0xFFFFFFFFFFFFFFFF );
+ }
+ else {
+ aSig <<= 2;
+ mul64To128( zSig, zSig, &term0, &term1 );
+ sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig;
+ shortShift128Left( 0, zSig, 1, &term0, &term1 );
+ term1 |= 1;
+ add128( rem0, rem1, term0, term1, &rem0, &rem1 );
+ }
+ zSig |= ( ( rem0 | rem1 ) != 0 );
+ }
+ }
+ shift64RightJamming( zSig, 1, &zSig );
+ return roundAndPackFloat64( 0, zExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise. The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_eq( float64 a, float64 b )
+{
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise. The comparison is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_le( float64 a, float64 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 );
+ return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise. The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_lt( float64 a, float64 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 );
+ return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is equal to the
+corresponding value `b', and 0 otherwise. The invalid exception is raised
+if either operand is a NaN. Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_eq_signaling( float64 a, float64 b )
+{
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than or
+equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not
+cause an exception. Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_le_quiet( float64 a, float64 b )
+{
+ flag aSign, bSign;
+ //int16 aExp, bExp;
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 );
+ return ( a == b ) || ( aSign ^ ( a < b ) );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the double-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an
+exception. Otherwise, the comparison is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float64_lt_quiet( float64 a, float64 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 );
+ return ( a != b ) && ( aSign ^ ( a < b ) );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the 32-bit two's complement integer format. The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic---which means in particular that the conversion
+is rounded according to the current rounding mode. If `a' is a NaN, the
+largest positive integer is returned. Otherwise, if the conversion
+overflows, the largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 floatx80_to_int32( floatx80 a )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+ shiftCount = 0x4037 - aExp;
+ if ( shiftCount <= 0 ) shiftCount = 1;
+ shift64RightJamming( aSig, shiftCount, &aSig );
+ return roundAndPackInt32( aSign, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the 32-bit two's complement integer format. The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic, except that the conversion is always rounded
+toward zero. If `a' is a NaN, the largest positive integer is returned.
+Otherwise, if the conversion overflows, the largest integer with the same
+sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 floatx80_to_int32_round_to_zero( floatx80 a )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig, savedASig;
+ int32 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ shiftCount = 0x403E - aExp;
+ if ( shiftCount < 32 ) {
+ if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+ goto invalid;
+ }
+ else if ( 63 < shiftCount ) {
+ if ( aExp || aSig ) float_exception_flags |= float_flag_inexact;
+ return 0;
+ }
+ savedASig = aSig;
+ aSig >>= shiftCount;
+ z = aSig;
+ if ( aSign ) z = - z;
+ if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+ float_exception_flags |= float_flag_invalid;
+ return aSign ? 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( ( aSig<<shiftCount ) != savedASig ) {
+ float_exception_flags |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the single-precision floating-point format. The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 floatx80_to_float32( floatx80 a )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) {
+ return commonNaNToFloat32( floatx80ToCommonNaN( a ) );
+ }
+ return packFloat32( aSign, 0xFF, 0 );
+ }
+ shift64RightJamming( aSig, 33, &aSig );
+ if ( aExp || aSig ) aExp -= 0x3F81;
+ return roundAndPackFloat32( aSign, aExp, aSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the double-precision floating-point format. The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 floatx80_to_float64( floatx80 a )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig, zSig;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) {
+ return commonNaNToFloat64( floatx80ToCommonNaN( a ) );
+ }
+ return packFloat64( aSign, 0x7FF, 0 );
+ }
+ shift64RightJamming( aSig, 1, &zSig );
+ if ( aExp || aSig ) aExp -= 0x3C01;
+ return roundAndPackFloat64( aSign, aExp, zSig );
+
+}
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the extended double-precision floating-
+point value `a' to the quadruple-precision floating-point format. The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 floatx80_to_float128( floatx80 a )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig, zSig0, zSig1;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) {
+ return commonNaNToFloat128( floatx80ToCommonNaN( a ) );
+ }
+ shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 );
+ return packFloat128( aSign, aExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the extended double-precision floating-point value `a' to an integer,
+and returns the result as an extended quadruple-precision floating-point
+value. The operation is performed according to the IEC/IEEE Standard for
+Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_round_to_int( floatx80 a )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ floatx80 z;
+
+ aExp = extractFloatx80Exp( a );
+ if ( 0x403E <= aExp ) {
+ if ( ( aExp == 0x7FFF ) && (bits64) ( extractFloatx80Frac( a )<<1 ) ) {
+ return propagateFloatx80NaN( a, a );
+ }
+ return a;
+ }
+ if ( aExp <= 0x3FFE ) {
+ if ( ( aExp == 0 )
+ && ( (bits64) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) {
+ return a;
+ }
+ float_exception_flags |= float_flag_inexact;
+ aSign = extractFloatx80Sign( a );
+ switch ( float_rounding_mode ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x3FFE ) && (bits64) ( extractFloatx80Frac( a )<<1 )
+ ) {
+ return
+ packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) );
+ }
+ break;
+ case float_round_down:
+ return
+ aSign ?
+ packFloatx80( 1, 0x3FFF, LIT64( 0x8000000000000000 ) )
+ : packFloatx80( 0, 0, 0 );
+ case float_round_up:
+ return
+ aSign ? packFloatx80( 1, 0, 0 )
+ : packFloatx80( 0, 0x3FFF, LIT64( 0x8000000000000000 ) );
+ }
+ return packFloatx80( aSign, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x403E - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z = a;
+ roundingMode = float_rounding_mode;
+ if ( roundingMode == float_round_nearest_even ) {
+ z.low += lastBitMask>>1;
+ if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloatx80Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+ z.low += roundBitsMask;
+ }
+ }
+ z.low &= ~ roundBitsMask;
+ if ( z.low == 0 ) {
+ ++z.high;
+ z.low = LIT64( 0x8000000000000000 );
+ }
+ if ( z.low != a.low ) float_exception_flags |= float_flag_inexact;
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the extended double-
+precision floating-point values `a' and `b'. If `zSign' is true, the sum is
+negated before being returned. `zSign' is ignored if the result is a NaN.
+The addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign )
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ int32 expDiff;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ expDiff = aExp - bExp;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) --expDiff;
+ shift64ExtraRightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) ++expDiff;
+ shift64ExtraRightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+ return propagateFloatx80NaN( a, b );
+ }
+ return a;
+ }
+ zSig1 = 0;
+ zSig0 = aSig + bSig;
+ if ( aExp == 0 ) {
+ normalizeFloatx80Subnormal( zSig0, &zExp, &zSig0 );
+ goto roundAndPack;
+ }
+ zExp = aExp;
+ goto shiftRight1;
+ }
+
+ zSig0 = aSig + bSig;
+
+ if ( (sbits64) zSig0 < 0 ) goto roundAndPack;
+ shiftRight1:
+ shift64ExtraRightJamming( zSig0, zSig1, 1, &zSig0, &zSig1 );
+ zSig0 |= LIT64( 0x8000000000000000 );
+ ++zExp;
+ roundAndPack:
+ return
+ roundAndPackFloatx80(
+ floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the extended
+double-precision floating-point values `a' and `b'. If `zSign' is true,
+the difference is negated before being returned. `zSign' is ignored if the
+result is a NaN. The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign )
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ int32 expDiff;
+ floatx80 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ expDiff = aExp - bExp;
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+ return propagateFloatx80NaN( a, b );
+ }
+ float_raise( float_flag_invalid );
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ zSig1 = 0;
+ if ( bSig < aSig ) goto aBigger;
+ if ( aSig < bSig ) goto bBigger;
+ return packFloatx80( float_rounding_mode == float_round_down, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+ return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) ++expDiff;
+ shift128RightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+ bBigger:
+ sub128( bSig, 0, aSig, zSig1, &zSig0, &zSig1 );
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) --expDiff;
+ shift128RightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+ aBigger:
+ sub128( aSig, 0, bSig, zSig1, &zSig0, &zSig1 );
+ zExp = aExp;
+ normalizeRoundAndPack:
+ return
+ normalizeRoundAndPackFloatx80(
+ floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the extended double-precision floating-point
+values `a' and `b'. The operation is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_add( floatx80 a, floatx80 b )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign == bSign ) {
+ return addFloatx80Sigs( a, b, aSign );
+ }
+ else {
+ return subFloatx80Sigs( a, b, aSign );
+ }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the extended double-precision floating-
+point values `a' and `b'. The operation is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_sub( floatx80 a, floatx80 b )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign == bSign ) {
+ return subFloatx80Sigs( a, b, aSign );
+ }
+ else {
+ return addFloatx80Sigs( a, b, aSign );
+ }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the extended double-precision floating-
+point values `a' and `b'. The operation is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_mul( floatx80 a, floatx80 b )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ floatx80 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ bSign = extractFloatx80Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 )
+ || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+ return propagateFloatx80NaN( a, b );
+ }
+ if ( ( bExp | bSig ) == 0 ) goto invalid;
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+ if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid );
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+ normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) return packFloatx80( zSign, 0, 0 );
+ normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+ }
+ zExp = aExp + bExp - 0x3FFE;
+ mul64To128( aSig, bSig, &zSig0, &zSig1 );
+ if ( 0 < (sbits64) zSig0 ) {
+ shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 );
+ --zExp;
+ }
+ return
+ roundAndPackFloatx80(
+ floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the extended double-precision floating-point
+value `a' by the corresponding value `b'. The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_div( floatx80 a, floatx80 b )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ bits64 rem0, rem1, rem2, term0, term1, term2;
+ floatx80 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ bSign = extractFloatx80Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b );
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+ goto invalid;
+ }
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+ return packFloatx80( zSign, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid );
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ float_raise( float_flag_divbyzero );
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+ normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = aExp - bExp + 0x3FFE;
+ rem1 = 0;
+ if ( bSig <= aSig ) {
+ shift128Right( aSig, 0, 1, &aSig, &rem1 );
+ ++zExp;
+ }
+ zSig0 = estimateDiv128To64( aSig, rem1, bSig );
+ mul64To128( bSig, zSig0, &term0, &term1 );
+ sub128( aSig, rem1, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+ }
+ zSig1 = estimateDiv128To64( rem1, 0, bSig );
+ if ( (bits64) ( zSig1<<1 ) <= 8 ) {
+ mul64To128( bSig, zSig1, &term1, &term2 );
+ sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ add128( rem1, rem2, 0, bSig, &rem1, &rem2 );
+ }
+ zSig1 |= ( ( rem1 | rem2 ) != 0 );
+ }
+ return
+ roundAndPackFloatx80(
+ floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the extended double-precision floating-point value
+`a' with respect to the corresponding value `b'. The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_rem( floatx80 a, floatx80 b )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, expDiff;
+ bits64 aSig0, aSig1, bSig;
+ bits64 q, term0, term1, alternateASig0, alternateASig1;
+ floatx80 z;
+
+ aSig0 = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ bSign = extractFloatx80Sign( b );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig0<<1 )
+ || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+ return propagateFloatx80NaN( a, b );
+ }
+ goto invalid;
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid );
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( (bits64) ( aSig0<<1 ) == 0 ) return a;
+ normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+ }
+ bSig |= LIT64( 0x8000000000000000 );
+ zSign = aSign;
+ expDiff = aExp - bExp;
+ aSig1 = 0;
+ if ( expDiff < 0 ) {
+ if ( expDiff < -1 ) return a;
+ shift128Right( aSig0, 0, 1, &aSig0, &aSig1 );
+ expDiff = 0;
+ }
+ q = ( bSig <= aSig0 );
+ if ( q ) aSig0 -= bSig;
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ mul64To128( bSig, q, &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+ shortShift128Left( aSig0, aSig1, 62, &aSig0, &aSig1 );
+ expDiff -= 62;
+ }
+ expDiff += 64;
+ if ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ q >>= 64 - expDiff;
+ mul64To128( bSig, q<<( 64 - expDiff ), &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+ shortShift128Left( 0, bSig, 64 - expDiff, &term0, &term1 );
+ while ( le128( term0, term1, aSig0, aSig1 ) ) {
+ ++q;
+ sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+ }
+ }
+ else {
+ term1 = 0;
+ term0 = bSig;
+ }
+ sub128( term0, term1, aSig0, aSig1, &alternateASig0, &alternateASig1 );
+ if ( lt128( alternateASig0, alternateASig1, aSig0, aSig1 )
+ || ( eq128( alternateASig0, alternateASig1, aSig0, aSig1 )
+ && ( q & 1 ) )
+ ) {
+ aSig0 = alternateASig0;
+ aSig1 = alternateASig1;
+ zSign = ! zSign;
+ }
+ return
+ normalizeRoundAndPackFloatx80(
+ 80, zSign, bExp + expDiff, aSig0, aSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the extended double-precision floating-point
+value `a'. The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_sqrt( floatx80 a )
+{
+ flag aSign;
+ int32 aExp, zExp;
+ bits64 aSig0, aSig1, zSig0, zSig1;
+ bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+ bits64 shiftedRem0, shiftedRem1;
+ floatx80 z;
+
+ aSig0 = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a );
+ if ( ! aSign ) return a;
+ goto invalid;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig0 ) == 0 ) return a;
+ invalid:
+ float_raise( float_flag_invalid );
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ if ( aSig0 == 0 ) return packFloatx80( 0, 0, 0 );
+ normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+ }
+ zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFF;
+ zSig0 = estimateSqrt32( aExp, aSig0>>32 );
+ zSig0 <<= 31;
+ aSig1 = 0;
+ shift128Right( aSig0, 0, ( aExp & 1 ) + 2, &aSig0, &aSig1 );
+ zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0 ) + zSig0 + 4;
+ if ( 0 <= (sbits64) zSig0 ) zSig0 = LIT64( 0xFFFFFFFFFFFFFFFF );
+ shortShift128Left( aSig0, aSig1, 2, &aSig0, &aSig1 );
+ mul64To128( zSig0, zSig0, &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ shortShift128Left( 0, zSig0, 1, &term0, &term1 );
+ term1 |= 1;
+ add128( rem0, rem1, term0, term1, &rem0, &rem1 );
+ }
+ shortShift128Left( rem0, rem1, 63, &shiftedRem0, &shiftedRem1 );
+ zSig1 = estimateDiv128To64( shiftedRem0, shiftedRem1, zSig0 );
+ if ( (bits64) ( zSig1<<1 ) <= 10 ) {
+ if ( zSig1 == 0 ) zSig1 = 1;
+ mul64To128( zSig0, zSig1, &term1, &term2 );
+ shortShift128Left( term1, term2, 1, &term1, &term2 );
+ sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+ mul64To128( zSig1, zSig1, &term2, &term3 );
+ sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ shortShift192Left( 0, zSig0, zSig1, 1, &term1, &term2, &term3 );
+ term3 |= 1;
+ add192(
+ rem1, rem2, rem3, term1, term2, term3, &rem1, &rem2, &rem3 );
+ }
+ zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+ }
+ return
+ roundAndPackFloatx80(
+ floatx80_rounding_precision, 0, zExp, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is
+equal to the corresponding value `b', and 0 otherwise. The comparison is
+performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_eq( floatx80 a, floatx80 b )
+{
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ if ( floatx80_is_signaling_nan( a )
+ || floatx80_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is
+less than or equal to the corresponding value `b', and 0 otherwise. The
+comparison is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_le( floatx80 a, floatx80 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is
+less than the corresponding value `b', and 0 otherwise. The comparison
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_lt( floatx80 a, floatx80 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is equal
+to the corresponding value `b', and 0 otherwise. The invalid exception is
+raised if either operand is a NaN. Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_eq_signaling( floatx80 a, floatx80 b )
+{
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is less
+than or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs
+do not cause an exception. Otherwise, the comparison is performed according
+to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_le_quiet( floatx80 a, floatx80 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ if ( floatx80_is_signaling_nan( a )
+ || floatx80_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the extended double-precision floating-point value `a' is less
+than the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause
+an exception. Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag floatx80_lt_quiet( floatx80 a, floatx80 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ if ( floatx80_is_signaling_nan( a )
+ || floatx80_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the 32-bit two's complement integer format. The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic---which means in particular that the conversion is rounded
+according to the current rounding mode. If `a' is a NaN, the largest
+positive integer is returned. Otherwise, if the conversion overflows, the
+largest integer with the same sign as `a' is returned.
+-------------------------------------------------------------------------------
+*/
+int32 float128_to_int32( float128 a )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) aSign = 0;
+ if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+ aSig0 |= ( aSig1 != 0 );
+ shiftCount = 0x4028 - aExp;
+ if ( 0 < shiftCount ) shift64RightJamming( aSig0, shiftCount, &aSig0 );
+ return roundAndPackInt32( aSign, aSig0 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the 32-bit two's complement integer format. The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic, except that the conversion is always rounded toward zero. If
+`a' is a NaN, the largest positive integer is returned. Otherwise, if the
+conversion overflows, the largest integer with the same sign as `a' is
+returned.
+-------------------------------------------------------------------------------
+*/
+int32 float128_to_int32_round_to_zero( float128 a )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig0, aSig1, savedASig;
+ int32 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ aSig0 |= ( aSig1 != 0 );
+ shiftCount = 0x402F - aExp;
+ if ( shiftCount < 17 ) {
+ if ( ( aExp == 0x7FFF ) && aSig0 ) aSign = 0;
+ goto invalid;
+ }
+ else if ( 48 < shiftCount ) {
+ if ( aExp || aSig0 ) float_exception_flags |= float_flag_inexact;
+ return 0;
+ }
+ aSig0 |= LIT64( 0x0001000000000000 );
+ savedASig = aSig0;
+ aSig0 >>= shiftCount;
+ z = aSig0;
+ if ( aSign ) z = - z;
+ if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+ float_exception_flags |= float_flag_invalid;
+ return aSign ? 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( ( aSig0<<shiftCount ) != savedASig ) {
+ float_exception_flags |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the single-precision floating-point format. The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float32 float128_to_float32( float128 a )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig0, aSig1;
+ bits32 zSig;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) {
+ return commonNaNToFloat32( float128ToCommonNaN( a ) );
+ }
+ return packFloat32( aSign, 0xFF, 0 );
+ }
+ aSig0 |= ( aSig1 != 0 );
+ shift64RightJamming( aSig0, 18, &aSig0 );
+ zSig = aSig0;
+ if ( aExp || zSig ) {
+ zSig |= 0x40000000;
+ aExp -= 0x3F81;
+ }
+ return roundAndPackFloat32( aSign, aExp, zSig );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the double-precision floating-point format. The conversion
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float64 float128_to_float64( float128 a )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) {
+ return commonNaNToFloat64( float128ToCommonNaN( a ) );
+ }
+ return packFloat64( aSign, 0x7FF, 0 );
+ }
+ shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+ aSig0 |= ( aSig1 != 0 );
+ if ( aExp || aSig0 ) {
+ aSig0 |= LIT64( 0x4000000000000000 );
+ aExp -= 0x3C01;
+ }
+ return roundAndPackFloat64( aSign, aExp, aSig0 );
+
+}
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of converting the quadruple-precision floating-point
+value `a' to the extended double-precision floating-point format. The
+conversion is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+floatx80 float128_to_floatx80( float128 a )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) {
+ return commonNaNToFloatx80( float128ToCommonNaN( a ) );
+ }
+ return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloatx80( aSign, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ else {
+ aSig0 |= LIT64( 0x0001000000000000 );
+ }
+ shortShift128Left( aSig0, aSig1, 15, &aSig0, &aSig1 );
+ return roundAndPackFloatx80( 80, aSign, aExp, aSig0, aSig1 );
+
+}
+
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Rounds the quadruple-precision floating-point value `a' to an integer, and
+returns the result as a quadruple-precision floating-point value. The
+operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_round_to_int( float128 a )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ float128 z;
+
+ aExp = extractFloat128Exp( a );
+ if ( 0x402F <= aExp ) {
+ if ( 0x406F <= aExp ) {
+ if ( ( aExp == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) )
+ ) {
+ return propagateFloat128NaN( a, a );
+ }
+ return a;
+ }
+ lastBitMask = 1;
+ lastBitMask = ( lastBitMask<<( 0x406E - aExp ) )<<1;
+ roundBitsMask = lastBitMask - 1;
+ z = a;
+ roundingMode = float_rounding_mode;
+ if ( roundingMode == float_round_nearest_even ) {
+ if ( lastBitMask ) {
+ add128( z.high, z.low, 0, lastBitMask>>1, &z.high, &z.low );
+ if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+ }
+ else {
+ if ( (sbits64) z.low < 0 ) {
+ ++z.high;
+ if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1;
+ }
+ }
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat128Sign( z )
+ ^ ( roundingMode == float_round_up ) ) {
+ add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low );
+ }
+ }
+ z.low &= ~ roundBitsMask;
+ }
+ else {
+ if ( aExp <= 0x3FFE ) {
+ if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a;
+ float_exception_flags |= float_flag_inexact;
+ aSign = extractFloat128Sign( a );
+ switch ( float_rounding_mode ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x3FFE )
+ && ( extractFloat128Frac0( a )
+ | extractFloat128Frac1( a ) )
+ ) {
+ return packFloat128( aSign, 0x3FFF, 0, 0 );
+ }
+ break;
+ case float_round_down:
+ return
+ aSign ? packFloat128( 1, 0x3FFF, 0, 0 )
+ : packFloat128( 0, 0, 0, 0 );
+ case float_round_up:
+ return
+ aSign ? packFloat128( 1, 0, 0, 0 )
+ : packFloat128( 0, 0x3FFF, 0, 0 );
+ }
+ return packFloat128( aSign, 0, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x402F - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z.low = 0;
+ z.high = a.high;
+ roundingMode = float_rounding_mode;
+ if ( roundingMode == float_round_nearest_even ) {
+ z.high += lastBitMask>>1;
+ if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) {
+ z.high &= ~ lastBitMask;
+ }
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat128Sign( z )
+ ^ ( roundingMode == float_round_up ) ) {
+ z.high |= ( a.low != 0 );
+ z.high += roundBitsMask;
+ }
+ }
+ z.high &= ~ roundBitsMask;
+ }
+ if ( ( z.low != a.low ) || ( z.high != a.high ) ) {
+ float_exception_flags |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the absolute values of the quadruple-precision
+floating-point values `a' and `b'. If `zSign' is true, the sum is negated
+before being returned. `zSign' is ignored if the result is a NaN. The
+addition is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float128 addFloat128Sigs( float128 a, float128 b, flag zSign )
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+ int32 expDiff;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ expDiff = aExp - bExp;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig0 |= LIT64( 0x0001000000000000 );
+ }
+ shift128ExtraRightJamming(
+ bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2 );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig0 |= LIT64( 0x0001000000000000 );
+ }
+ shift128ExtraRightJamming(
+ aSig0, aSig1, 0, - expDiff, &aSig0, &aSig1, &zSig2 );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+ return propagateFloat128NaN( a, b );
+ }
+ return a;
+ }
+ add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+ if ( aExp == 0 ) return packFloat128( zSign, 0, zSig0, zSig1 );
+ zSig2 = 0;
+ zSig0 |= LIT64( 0x0002000000000000 );
+ zExp = aExp;
+ goto shiftRight1;
+ }
+ aSig0 |= LIT64( 0x0001000000000000 );
+ add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+ --zExp;
+ if ( zSig0 < LIT64( 0x0002000000000000 ) ) goto roundAndPack;
+ ++zExp;
+ shiftRight1:
+ shift128ExtraRightJamming(
+ zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+ roundAndPack:
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the absolute values of the quadruple-
+precision floating-point values `a' and `b'. If `zSign' is true, the
+difference is negated before being returned. `zSign' is ignored if the
+result is a NaN. The subtraction is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+static float128 subFloat128Sigs( float128 a, float128 b, flag zSign )
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1;
+ int32 expDiff;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ expDiff = aExp - bExp;
+ shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+ shortShift128Left( bSig0, bSig1, 14, &bSig0, &bSig1 );
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+ return propagateFloat128NaN( a, b );
+ }
+ float_raise( float_flag_invalid );
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ if ( bSig0 < aSig0 ) goto aBigger;
+ if ( aSig0 < bSig0 ) goto bBigger;
+ if ( bSig1 < aSig1 ) goto aBigger;
+ if ( aSig1 < bSig1 ) goto bBigger;
+ return packFloat128( float_rounding_mode == float_round_down, 0, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+ return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig0 |= LIT64( 0x4000000000000000 );
+ }
+ shift128RightJamming( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+ bSig0 |= LIT64( 0x4000000000000000 );
+ bBigger:
+ sub128( bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1 );
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig0 |= LIT64( 0x4000000000000000 );
+ }
+ shift128RightJamming( bSig0, bSig1, expDiff, &bSig0, &bSig1 );
+ aSig0 |= LIT64( 0x4000000000000000 );
+ aBigger:
+ sub128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+ zExp = aExp;
+ normalizeRoundAndPack:
+ --zExp;
+ return normalizeRoundAndPackFloat128( zSign, zExp - 14, zSig0, zSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of adding the quadruple-precision floating-point values
+`a' and `b'. The operation is performed according to the IEC/IEEE Standard
+for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_add( float128 a, float128 b )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign == bSign ) {
+ return addFloat128Sigs( a, b, aSign );
+ }
+ else {
+ return subFloat128Sigs( a, b, aSign );
+ }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of subtracting the quadruple-precision floating-point
+values `a' and `b'. The operation is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_sub( float128 a, float128 b )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign == bSign ) {
+ return subFloat128Sigs( a, b, aSign );
+ }
+ else {
+ return addFloat128Sigs( a, b, aSign );
+ }
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of multiplying the quadruple-precision floating-point
+values `a' and `b'. The operation is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_mul( float128 a, float128 b )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ bSign = extractFloat128Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( ( aSig0 | aSig1 )
+ || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+ return propagateFloat128NaN( a, b );
+ }
+ if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid;
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+ if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid );
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ if ( bExp == 0 ) {
+ if ( ( bSig0 | bSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+ normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+ }
+ zExp = aExp + bExp - 0x4000;
+ aSig0 |= LIT64( 0x0001000000000000 );
+ shortShift128Left( bSig0, bSig1, 16, &bSig0, &bSig1 );
+ mul128To256( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3 );
+ add128( zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1 );
+ zSig2 |= ( zSig3 != 0 );
+ if ( LIT64( 0x0002000000000000 ) <= zSig0 ) {
+ shift128ExtraRightJamming(
+ zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+ ++zExp;
+ }
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the result of dividing the quadruple-precision floating-point value
+`a' by the corresponding value `b'. The operation is performed according to
+the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_div( float128 a, float128 b )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+ bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ bSign = extractFloat128Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b );
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+ goto invalid;
+ }
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+ return packFloat128( zSign, 0, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( ( bSig0 | bSig1 ) == 0 ) {
+ if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid );
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ float_raise( float_flag_divbyzero );
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ zExp = aExp - bExp + 0x3FFD;
+ shortShift128Left(
+ aSig0 | LIT64( 0x0001000000000000 ), aSig1, 15, &aSig0, &aSig1 );
+ shortShift128Left(
+ bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+ if ( le128( bSig0, bSig1, aSig0, aSig1 ) ) {
+ shift128Right( aSig0, aSig1, 1, &aSig0, &aSig1 );
+ ++zExp;
+ }
+ zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 );
+ mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 );
+ sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 );
+ }
+ zSig1 = estimateDiv128To64( rem1, rem2, bSig0 );
+ if ( ( zSig1 & 0x3FFF ) <= 4 ) {
+ mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 );
+ sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 );
+ }
+ zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+ }
+ shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 );
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the remainder of the quadruple-precision floating-point value `a'
+with respect to the corresponding value `b'. The operation is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_rem( float128 a, float128 b )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, expDiff;
+ bits64 aSig0, aSig1, bSig0, bSig1;
+ bits64 q, term0, term1, term2, allZero, alternateASig0, alternateASig1;
+ bits64 sigMean1;
+ sbits64 sigMean0;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ bSign = extractFloat128Sign( b );
+ if ( aExp == 0x7FFF ) {
+ if ( ( aSig0 | aSig1 )
+ || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+ return propagateFloat128NaN( a, b );
+ }
+ goto invalid;
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( ( bSig0 | bSig1 ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid );
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return a;
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ expDiff = aExp - bExp;
+ if ( expDiff < -1 ) return a;
+ shortShift128Left(
+ aSig0 | LIT64( 0x0001000000000000 ),
+ aSig1,
+ 15 - ( expDiff < 0 ),
+ &aSig0,
+ &aSig1
+ );
+ shortShift128Left(
+ bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+ q = le128( bSig0, bSig1, aSig0, aSig1 );
+ if ( q ) sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+ q = ( 4 < q ) ? q - 4 : 0;
+ mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+ shortShift192Left( term0, term1, term2, 61, &term1, &term2, &allZero );
+ shortShift128Left( aSig0, aSig1, 61, &aSig0, &allZero );
+ sub128( aSig0, 0, term1, term2, &aSig0, &aSig1 );
+ expDiff -= 61;
+ }
+ if ( -64 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+ q = ( 4 < q ) ? q - 4 : 0;
+ q >>= - expDiff;
+ shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+ expDiff += 52;
+ if ( expDiff < 0 ) {
+ shift128Right( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+ }
+ else {
+ shortShift128Left( aSig0, aSig1, expDiff, &aSig0, &aSig1 );
+ }
+ mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+ sub128( aSig0, aSig1, term1, term2, &aSig0, &aSig1 );
+ }
+ else {
+ shift128Right( aSig0, aSig1, 12, &aSig0, &aSig1 );
+ shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+ }
+ do {
+ alternateASig0 = aSig0;
+ alternateASig1 = aSig1;
+ ++q;
+ sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+ } while ( 0 <= (sbits64) aSig0 );
+ add128(
+ aSig0, aSig1, alternateASig0, alternateASig1, &sigMean0, &sigMean1 );
+ if ( ( sigMean0 < 0 )
+ || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) {
+ aSig0 = alternateASig0;
+ aSig1 = alternateASig1;
+ }
+ zSign = ( (sbits64) aSig0 < 0 );
+ if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 );
+ return
+ normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns the square root of the quadruple-precision floating-point value `a'.
+The operation is performed according to the IEC/IEEE Standard for Binary
+Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+float128 float128_sqrt( float128 a )
+{
+ flag aSign;
+ int32 aExp, zExp;
+ bits64 aSig0, aSig1, zSig0, zSig1, zSig2;
+ bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+ bits64 shiftedRem0, shiftedRem1;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, a );
+ if ( ! aSign ) return a;
+ goto invalid;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig0 | aSig1 ) == 0 ) return a;
+ invalid:
+ float_raise( float_flag_invalid );
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( 0, 0, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFE;
+ aSig0 |= LIT64( 0x0001000000000000 );
+ zSig0 = estimateSqrt32( aExp, aSig0>>17 );
+ zSig0 <<= 31;
+ shortShift128Left( aSig0, aSig1, 13 - ( aExp & 1 ), &aSig0, &aSig1 );
+ zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0 ) + zSig0 + 4;
+ if ( 0 <= (sbits64) zSig0 ) zSig0 = LIT64( 0xFFFFFFFFFFFFFFFF );
+ shortShift128Left( aSig0, aSig1, 2, &aSig0, &aSig1 );
+ mul64To128( zSig0, zSig0, &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ shortShift128Left( 0, zSig0, 1, &term0, &term1 );
+ term1 |= 1;
+ add128( rem0, rem1, term0, term1, &rem0, &rem1 );
+ }
+ shortShift128Left( rem0, rem1, 63, &shiftedRem0, &shiftedRem1 );
+ zSig1 = estimateDiv128To64( shiftedRem0, shiftedRem1, zSig0 );
+ if ( ( zSig1 & 0x3FFF ) <= 5 ) {
+ if ( zSig1 == 0 ) zSig1 = 1;
+ mul64To128( zSig0, zSig1, &term1, &term2 );
+ shortShift128Left( term1, term2, 1, &term1, &term2 );
+ sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+ mul64To128( zSig1, zSig1, &term2, &term3 );
+ sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ shortShift192Left( 0, zSig0, zSig1, 1, &term1, &term2, &term3 );
+ term3 |= 1;
+ add192(
+ rem1, rem2, rem3, term1, term2, term3, &rem1, &rem2, &rem3 );
+ }
+ zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+ }
+ shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 );
+ return roundAndPackFloat128( 0, zExp, zSig0, zSig1, zSig2 );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is equal to
+the corresponding value `b', and 0 otherwise. The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_eq( float128 a, float128 b )
+{
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ if ( float128_is_signaling_nan( a )
+ || float128_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+or equal to the corresponding value `b', and 0 otherwise. The comparison
+is performed according to the IEC/IEEE Standard for Binary Floating-point
+Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_le( float128 a, float128 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise. The comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_lt( float128 a, float128 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is equal to
+the corresponding value `b', and 0 otherwise. The invalid exception is
+raised if either operand is a NaN. Otherwise, the comparison is performed
+according to the IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_eq_signaling( float128 a, float128 b )
+{
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ float_raise( float_flag_invalid );
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not
+cause an exception. Otherwise, the comparison is performed according to the
+IEC/IEEE Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_le_quiet( float128 a, float128 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ if ( float128_is_signaling_nan( a )
+ || float128_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*
+-------------------------------------------------------------------------------
+Returns 1 if the quadruple-precision floating-point value `a' is less than
+the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an
+exception. Otherwise, the comparison is performed according to the IEC/IEEE
+Standard for Binary Floating-point Arithmetic.
+-------------------------------------------------------------------------------
+*/
+flag float128_lt_quiet( float128 a, float128 b )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ if ( float128_is_signaling_nan( a )
+ || float128_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid );
+ }
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
--- /dev/null
+
+/*
+===============================================================================
+
+This C header file is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/
+arithmetic/softfloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort
+has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
+TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO
+PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
+AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) they include prominent notice that the work is derivative, and (2) they
+include prominent notice akin to these three paragraphs for those parts of
+this code that are retained.
+
+===============================================================================
+*/
+
+#ifndef __SOFTFLOAT_H__
+#define __SOFTFLOAT_H__
+
+/*
+-------------------------------------------------------------------------------
+The macro `FLOATX80' must be defined to enable the extended double-precision
+floating-point format `floatx80'. If this macro is not defined, the
+`floatx80' type will not be defined, and none of the functions that either
+input or output the `floatx80' type will be defined. The same applies to
+the `FLOAT128' macro and the quadruple-precision format `float128'.
+-------------------------------------------------------------------------------
+*/
+#define FLOATX80
+/* #define FLOAT128 */
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point types.
+-------------------------------------------------------------------------------
+*/
+typedef unsigned long int float32;
+typedef unsigned long long float64;
+#ifdef FLOATX80
+typedef struct {
+ unsigned short high;
+ unsigned long long low;
+} floatx80;
+#endif
+#ifdef FLOAT128
+typedef struct {
+ unsigned long long high, low;
+} float128;
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point underflow tininess-detection mode.
+-------------------------------------------------------------------------------
+*/
+extern signed char float_detect_tininess;
+enum {
+ float_tininess_after_rounding = 0,
+ float_tininess_before_rounding = 1
+};
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point rounding mode.
+-------------------------------------------------------------------------------
+*/
+extern signed char float_rounding_mode;
+enum {
+ float_round_nearest_even = 0,
+ float_round_to_zero = 1,
+ float_round_down = 2,
+ float_round_up = 3
+};
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE floating-point exception flags.
+-------------------------------------------------------------------------------
+extern signed char float_exception_flags;
+enum {
+ float_flag_inexact = 1,
+ float_flag_underflow = 2,
+ float_flag_overflow = 4,
+ float_flag_divbyzero = 8,
+ float_flag_invalid = 16
+};
+
+ScottB: November 4, 1998
+Changed the enumeration to match the bit order in the FPA11.
+*/
+
+extern signed char float_exception_flags;
+enum {
+ float_flag_invalid = 1,
+ float_flag_divbyzero = 2,
+ float_flag_overflow = 4,
+ float_flag_underflow = 8,
+ float_flag_inexact = 16
+};
+
+/*
+-------------------------------------------------------------------------------
+Routine to raise any or all of the software IEC/IEEE floating-point
+exception flags.
+-------------------------------------------------------------------------------
+*/
+void float_raise( signed char );
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE integer-to-floating-point conversion routines.
+-------------------------------------------------------------------------------
+*/
+float32 int32_to_float32( signed int );
+float64 int32_to_float64( signed int );
+#ifdef FLOATX80
+floatx80 int32_to_floatx80( signed int );
+#endif
+#ifdef FLOAT128
+float128 int32_to_float128( signed int );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE single-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int float32_to_int32( float32 );
+signed int float32_to_int32_round_to_zero( float32 );
+float64 float32_to_float64( float32 );
+#ifdef FLOATX80
+floatx80 float32_to_floatx80( float32 );
+#endif
+#ifdef FLOAT128
+float128 float32_to_float128( float32 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE single-precision operations.
+-------------------------------------------------------------------------------
+*/
+float32 float32_round_to_int( float32 );
+float32 float32_add( float32, float32 );
+float32 float32_sub( float32, float32 );
+float32 float32_mul( float32, float32 );
+float32 float32_div( float32, float32 );
+float32 float32_rem( float32, float32 );
+float32 float32_sqrt( float32 );
+char float32_eq( float32, float32 );
+char float32_le( float32, float32 );
+char float32_lt( float32, float32 );
+char float32_eq_signaling( float32, float32 );
+char float32_le_quiet( float32, float32 );
+char float32_lt_quiet( float32, float32 );
+char float32_is_signaling_nan( float32 );
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE double-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int float64_to_int32( float64 );
+signed int float64_to_int32_round_to_zero( float64 );
+float32 float64_to_float32( float64 );
+#ifdef FLOATX80
+floatx80 float64_to_floatx80( float64 );
+#endif
+#ifdef FLOAT128
+float128 float64_to_float128( float64 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE double-precision operations.
+-------------------------------------------------------------------------------
+*/
+float64 float64_round_to_int( float64 );
+float64 float64_add( float64, float64 );
+float64 float64_sub( float64, float64 );
+float64 float64_mul( float64, float64 );
+float64 float64_div( float64, float64 );
+float64 float64_rem( float64, float64 );
+float64 float64_sqrt( float64 );
+char float64_eq( float64, float64 );
+char float64_le( float64, float64 );
+char float64_lt( float64, float64 );
+char float64_eq_signaling( float64, float64 );
+char float64_le_quiet( float64, float64 );
+char float64_lt_quiet( float64, float64 );
+char float64_is_signaling_nan( float64 );
+
+#ifdef FLOATX80
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE extended double-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int floatx80_to_int32( floatx80 );
+signed int floatx80_to_int32_round_to_zero( floatx80 );
+float32 floatx80_to_float32( floatx80 );
+float64 floatx80_to_float64( floatx80 );
+#ifdef FLOAT128
+float128 floatx80_to_float128( floatx80 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE extended double-precision rounding precision. Valid
+values are 32, 64, and 80.
+-------------------------------------------------------------------------------
+*/
+extern signed char floatx80_rounding_precision;
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE extended double-precision operations.
+-------------------------------------------------------------------------------
+*/
+floatx80 floatx80_round_to_int( floatx80 );
+floatx80 floatx80_add( floatx80, floatx80 );
+floatx80 floatx80_sub( floatx80, floatx80 );
+floatx80 floatx80_mul( floatx80, floatx80 );
+floatx80 floatx80_div( floatx80, floatx80 );
+floatx80 floatx80_rem( floatx80, floatx80 );
+floatx80 floatx80_sqrt( floatx80 );
+char floatx80_eq( floatx80, floatx80 );
+char floatx80_le( floatx80, floatx80 );
+char floatx80_lt( floatx80, floatx80 );
+char floatx80_eq_signaling( floatx80, floatx80 );
+char floatx80_le_quiet( floatx80, floatx80 );
+char floatx80_lt_quiet( floatx80, floatx80 );
+char floatx80_is_signaling_nan( floatx80 );
+
+#endif
+
+#ifdef FLOAT128
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE quadruple-precision conversion routines.
+-------------------------------------------------------------------------------
+*/
+signed int float128_to_int32( float128 );
+signed int float128_to_int32_round_to_zero( float128 );
+float32 float128_to_float32( float128 );
+float64 float128_to_float64( float128 );
+#ifdef FLOATX80
+floatx80 float128_to_floatx80( float128 );
+#endif
+
+/*
+-------------------------------------------------------------------------------
+Software IEC/IEEE quadruple-precision operations.
+-------------------------------------------------------------------------------
+*/
+float128 float128_round_to_int( float128 );
+float128 float128_add( float128, float128 );
+float128 float128_sub( float128, float128 );
+float128 float128_mul( float128, float128 );
+float128 float128_div( float128, float128 );
+float128 float128_rem( float128, float128 );
+float128 float128_sqrt( float128 );
+char float128_eq( float128, float128 );
+char float128_le( float128, float128 );
+char float128_lt( float128, float128 );
+char float128_eq_signaling( float128, float128 );
+char float128_le_quiet( float128, float128 );
+char float128_lt_quiet( float128, float128 );
+char float128_is_signaling_nan( float128 );
+
+#endif
+
+#endif
ENTRY(_start)
SECTIONS
{
- _text = .; /* Text and read-only data */
- .text : {
+ _text = .; /* Text and read-only data */
+ .text : { } /* Set text start address */
+
+ __init_begin = .; /* Init code and data */
+ .text.init : { *(.text.init) }
+ .data.init : { *(.data.init) }
+ . = ALIGN(4096);
+ __init_end = .;
+
+ __ebsa285_begin = .;
+ .text.ebsa285 : { *(.text.ebsa285) }
+ .data.ebsa285 : { *(.data.ebsa285) }
+ . = ALIGN(4096);
+ __ebsa285_end = .;
+
+ __netwinder_begin = .;
+ .text.netwinder : { *(.text.netwinder) }
+ .data.netwinder : { *(.data.netwinder) }
+ . = ALIGN(4096);
+ __netwinder_end = .;
+
+ .text.real : { /* Real text segment */
*(.text)
*(.fixup)
*(.gnu.warning)
- } = 0x9090
+ }
+
.text.lock : { *(.text.lock) } /* out-of-line lock text */
.rodata : { *(.rodata) }
.kstrtab : { *(.kstrtab) }
- . = ALIGN(16); /* Exception table */
+ . = ALIGN(16); /* Exception table */
__start___ex_table = .;
__ex_table : { *(__ex_table) }
__stop___ex_table = .;
- __start___ksymtab = .; /* Kernel symbol table */
+ __start___ksymtab = .; /* Kernel symbol table */
__ksymtab : { *(__ksymtab) }
__stop___ksymtab = .;
- _etext = .; /* End of text section */
+ _etext = .; /* End of text section */
. = ALIGN(8192);
- .data : { /* Data */
+ .data : { /* Data */
*(.init.task)
*(.data)
CONSTRUCTORS
}
- _edata = .; /* End of data section */
-
- . = ALIGN(4096); /* Init code and data */
- __init_begin = .;
- .text.init : { *(.text.init) }
- .data.init : { *(.data.init) }
- . = ALIGN(4096);
- __init_end = .;
+ _edata = .; /* End of data section */
- __bss_start = .; /* BSS */
+ __bss_start = .; /* BSS */
.bss : {
*(.bss)
}
_end = . ;
- /* Stabs debugging sections. */
+ /* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
CONFIG_BLK_DEV_RZ1000=y
CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_BLK_DEV_IDEDMA_PCI is not set
# CONFIG_BLK_DEV_OFFBOARD is not set
# CONFIG_BLK_DEV_AEC6210 is not set
-CONFIG_IDEDMA_AUTO=y
# CONFIG_IDE_CHIPSETS is not set
#
#
# CONFIG_CODA_FS is not set
CONFIG_NFS_FS=y
-# CONFIG_NFSD_SUN is not set
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
# CONFIG_SMB_FS is not set
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
CONFIG_SMB_FS=m
-CONFIG_SMB_WIN95=y
CONFIG_NCP_FS=m
# CONFIG_NCPFS_PACKET_SIGNING is not set
# CONFIG_NCPFS_IOCTL_LOCKING is not set
-/* $Id: signal.c,v 1.91 1999/01/26 11:00:44 jj Exp $
+/* $Id: signal.c,v 1.92 1999/06/14 05:23:53 davem Exp $
* linux/arch/sparc/kernel/signal.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
default:
lock_kernel();
sigaddset(¤t->signal, signr);
+ recalc_sigpending(current);
current->flags |= PF_SIGNALED;
do_exit(exit_code);
/* NOT REACHED */
-/* $Id: sys_sunos.c,v 1.98 1999/06/09 08:23:39 davem Exp $
+/* $Id: sys_sunos.c,v 1.99 1999/06/11 11:40:39 davem Exp $
* sys_sunos.c: SunOS specific syscall compatibility support.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
* fool it, but this should catch most mistakes.
*/
freepages = buffermem >> PAGE_SHIFT;
- freepages += page_cache_size;
+ freepages += atomic_read(&page_cache_size);
freepages >>= 1;
freepages += nr_free_pages;
freepages += nr_swap_pages;
* Ok, we have probably got enough memory - let it rip.
*/
current->mm->brk = brk;
- do_brk(oldbrk, newbrk-oldbrk)
+ do_brk(oldbrk, newbrk-oldbrk);
retval = 0;
out:
up(¤t->mm->mmap_sem);
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
CONFIG_SMB_FS=m
-CONFIG_SMB_WIN95=y
CONFIG_NCP_FS=m
# CONFIG_NCPFS_PACKET_SIGNING is not set
# CONFIG_NCPFS_IOCTL_LOCKING is not set
* and that it is in the task area before calling this: this routine does
* no checking.
*/
-static pte_t *get_page(struct task_struct * tsk,
+static pte_t *ptrace_get_page(struct task_struct * tsk,
struct vm_area_struct * vma, unsigned long addr, int write)
{
pgd_t * pgdir;
pte_t * pgtable;
unsigned long page, retval;
- if (!(pgtable = get_page (tsk, vma, addr, 0))) return 0;
+ if (!(pgtable = ptrace_get_page (tsk, vma, addr, 0))) return 0;
page = pte_page(*pgtable);
/* this is a hack for non-kernel-mapped video buffers and similar */
if (MAP_NR(page) >= max_mapnr)
pte_t *pgtable;
unsigned long page;
- if (!(pgtable = get_page (tsk, vma, addr, 1))) return;
+ if (!(pgtable = ptrace_get_page (tsk, vma, addr, 1))) return;
page = pte_page(*pgtable);
/* this is a hack for non-kernel-mapped video buffers and similar */
flush_cache_page(vma, addr);
unsigned long page;
unsigned int retval;
- if (!(pgtable = get_page (tsk, vma, addr, 0))) return 0;
+ if (!(pgtable = ptrace_get_page (tsk, vma, addr, 0))) return 0;
page = pte_page(*pgtable);
/* this is a hack for non-kernel-mapped video buffers and similar */
if (MAP_NR(page) >= max_mapnr)
pte_t *pgtable;
unsigned long page;
- if (!(pgtable = get_page (tsk, vma, addr, 1))) return;
+ if (!(pgtable = ptrace_get_page (tsk, vma, addr, 1))) return;
page = pte_page(*pgtable);
/* this is a hack for non-kernel-mapped video buffers and similar */
flush_cache_page(vma, addr);
pt_error_return(regs, EIO);
goto flush_and_out;
}
- pgtable = get_page (child, vma, src, 0);
+ pgtable = ptrace_get_page (child, vma, src, 0);
up(&child->mm->mmap_sem);
if (src & ~PAGE_MASK) {
curlen = PAGE_SIZE - (src & ~PAGE_MASK);
pt_error_return(regs, EIO);
goto flush_and_out;
}
- pgtable = get_page (child, vma, dest, 1);
+ pgtable = ptrace_get_page (child, vma, dest, 1);
up(&child->mm->mmap_sem);
if (dest & ~PAGE_MASK) {
curlen = PAGE_SIZE - (dest & ~PAGE_MASK);
-/* $Id: signal.c,v 1.40 1999/06/02 19:19:52 jj Exp $
+/* $Id: signal.c,v 1.41 1999/06/14 05:23:58 davem Exp $
* arch/sparc64/kernel/signal.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
default:
lock_kernel();
sigaddset(¤t->signal, signr);
+ recalc_sigpending(current);
current->flags |= PF_SIGNALED;
do_exit(exit_code);
/* NOT REACHED */
-/* $Id: signal32.c,v 1.47 1998/10/13 09:07:40 davem Exp $
+/* $Id: signal32.c,v 1.48 1999/06/14 05:24:01 davem Exp $
* arch/sparc64/kernel/signal32.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
default:
lock_kernel();
sigaddset(¤t->signal, signr);
+ recalc_sigpending(current);
current->flags |= PF_SIGNALED;
do_exit(exit_code);
/* NOT REACHED */
* fool it, but this should catch most mistakes.
*/
freepages = buffermem >> PAGE_SHIFT;
- freepages += page_cache_size;
+ freepages += atomic_read(&page_cache_size);
freepages >>= 1;
freepages += nr_free_pages;
freepages += nr_swap_pages;
mainmenu_option next_comment
comment 'Acorn-specific block devices'
-bool ' Support expansion card IDE interfaces' CONFIG_BLK_DEV_IDE_CARDS
-if [ "$CONFIG_BLK_DEV_IDE_CARDS" = "y" ]; then
- dep_tristate ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_BLK_DEV_IDE
- dep_tristate ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_BLK_DEV_IDE
-fi
-
-tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM
-if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then
- bool ' Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT
+if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then
+ tristate 'Old Archimedes floppy (1772) support' CONFIG_BLK_DEV_FD1772
+ tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM
+ if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then
+ bool ' Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT
+ fi
fi
endmenu
M_OBJS :=
MOD_LIST_NAME := ACORN_BLOCK_MODULES
-ifeq ($(CONFIG_ARCH_ARC),y)
- ifeq ($(CONFIG_BLK_DEV_FD),y)
- L_OBJS += fd1772.o fd1772dma.o
- else
- ifeq ($(CONFIG_BLK_DEV_FD),m)
- M_OBJS += fd1772_mod.o
- endif
- endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
- L_OBJS += ide-ics.o
-else
- ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),m)
- M_OBJS += ide-ics.o
- endif
-endif
-
-ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
- L_OBJS += ide-rapide.o
+ifeq ($(CONFIG_BLK_DEV_FD1772),y)
+ L_OBJS += fd1772.o fd1772dma.o
else
- ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),m)
- M_OBJS += ide-rapide.o
+ ifeq ($(CONFIG_BLK_DEV_FD1772),m)
+ M_OBJS += fd1772_mod.o
endif
endif
* I wish I knew why that timer didn't work.....
*
* 16/11/96 - Fiddled and frigged for 2.0.18
+ *
+ * DAG 30/01/99 - Started frobbing for 2.2.1
*/
#include <linux/sched.h>
#include <asm/dma.h>
#include <asm/hardware.h>
#include <asm/io.h>
+#include <asm/ioc.h>
#include <asm/irq.h>
-#include <asm/irq-no.h>
#include <asm/pgtable.h>
#include <asm/segment.h>
#define MAJOR_NR FLOPPY_MAJOR
#define FLOPPY_DMA 0
-#include "blk.h"
+#include <linux/blk.h>
/* Note: FD_MAX_UNITS could be redefined to 2 for the Atari (with
* little additional rework in this file). But I'm not yet sure if
.text
- .global _fdc1772_dataaddr
-_fdc1772_fiqdata:
+ .global fdc1772_dataaddr
+fdc1772_fiqdata:
@ Number of bytes left to DMA
- .global _fdc1772_bytestogo
-_fdc1772_bytestogo:
+ .global fdc1772_bytestogo
+fdc1772_bytestogo:
.word 0
@ Place to put/get data from in DMA
- .global _fdc1772_dataaddr
-_fdc1772_dataaddr:
+ .global fdc1772_dataaddr
+fdc1772_dataaddr:
.word 0
- .global _fdc1772_fdc_int_done
-_fdc1772_fdc_int_done:
+ .global fdc1772_fdc_int_done
+fdc1772_fdc_int_done:
.word 0
- .global _fdc1772_comendstatus
-_fdc1772_comendstatus:
+ .global fdc1772_comendstatus
+fdc1772_comendstatus:
.word 0
@ We hang this off DMA channel 1
- .global _fdc1772_comendhandler
-_fdc1772_comendhandler:
+ .global fdc1772_comendhandler
+fdc1772_comendhandler:
mov r8,#IOC_BASE
ldrb r9,[r8,#0x34] @ IOC FIQ status
tst r9,#2
subeqs pc,r14,#4 @ should I leave a space here
orr r9,r8,#0x10000 @ FDC base
- adr r8,_fdc1772_fdc_int_done
+ adr r8,fdc1772_fdc_int_done
ldrb r10,[r9,#0] @ FDC status
mov r9,#1 @ Got a FIQ flag
stmia r8,{r9,r10}
subs pc,r14,#4
- .global _fdc1772_dma_read
-_fdc1772_dma_read:
+ .global fdc1772_dma_read
+fdc1772_dma_read:
mov r8,#IOC_BASE
ldrb r9,[r8,#0x34] @ IOC FIQ status
tst r9,#1
- beq _fdc1772_dma_read_notours
+ beq fdc1772_dma_read_notours
orr r8,r8,#0x10000 @ FDC base
ldrb r10,[r8,#0xc] @ Read from FDC data reg (also clears interrupt)
ldmia r11,{r8,r9}
strplb r10,[r9],#1 @ Store the data and increment the pointer
stmplia r11,{r8,r9} @ Update count/pointers
@ Handle any other interrupts if there are any
-_fdc1772_dma_read_notours:
+fdc1772_dma_read_notours:
@ Cant branch because this code has been copied down to the FIQ vector
ldr pc,[pc,#-4]
- .word _fdc1772_comendhandler
- .global _fdc1772_dma_read_end
-_fdc1772_dma_read_end:
+ .word fdc1772_comendhandler
+ .global fdc1772_dma_read_end
+fdc1772_dma_read_end:
- .global _fdc1772_dma_write
-_fdc1772_dma_write:
+ .global fdc1772_dma_write
+fdc1772_dma_write:
mov r8,#IOC_BASE
ldrb r9,[r8,#0x34] @ IOC FIQ status
tst r9,#1
- beq _fdc1772_dma_write_notours
+ beq fdc1772_dma_write_notours
orr r8,r8,#0x10000 @ FDC base
ldmia r11,{r9,r10}
subs r9,r9,#1 @ One less byte to go
strplb r12,[r8,#0xc] @ write it to FDC data reg
stmplia r11,{r9,r10} @ Update count and pointer - should clear interrupt
@ Handle any other interrupts
-_fdc1772_dma_write_notours:
+fdc1772_dma_write_notours:
@ Cant branch because this code has been copied down to the FIQ vector
ldr pc,[pc,#-4]
- .word _fdc1772_comendhandler
+ .word fdc1772_comendhandler
- .global _fdc1772_dma_write_end
-_fdc1772_dma_write_end:
+ .global fdc1772_dma_write_end
+fdc1772_dma_write_end:
@ Setup the FIQ R11 to point to the data and store the count, address
@ for this dma
@ R0=count
@ R1=address
- .global _fdc1772_setupdma
-_fdc1772_setupdma:
+ .global fdc1772_setupdma
+fdc1772_setupdma:
@ The big job is flipping in and out of FIQ mode
- adr r2,_fdc1772_fiqdata @ This is what we really came here for
+ adr r2,fdc1772_fiqdata @ This is what we really came here for
stmia r2,{r0,r1}
mov r3, pc
teqp pc,#0x0c000001 @ Disable FIQs, IRQs and switch to FIQ mode
+++ /dev/null
-/*
- * linux/arch/arm/drivers/block/ide-ics.c
- *
- * Copyright (c) 1996,1997 Russell King.
- *
- * Changelog:
- * 08-06-1996 RMK Created
- * 12-09-1997 RMK Added interrupt enable/disable
- */
-
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/malloc.h>
-#include <linux/blkdev.h>
-#include <linux/errno.h>
-#include <linux/hdreg.h>
-
-#include <asm/ecard.h>
-#include <asm/io.h>
-
-#include "../../block/ide.h"
-
-/*
- * Maximum number of interfaces per card
- */
-#define MAX_IFS 2
-
-#define ICS_IDENT_OFFSET 0x8a0
-
-#define ICS_ARCIN_V5_INTRSTAT 0x000
-#define ICS_ARCIN_V5_INTROFFSET 0x001
-#define ICS_ARCIN_V5_IDEOFFSET 0xa00
-#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0
-#define ICS_ARCIN_V5_IDESTEPPING 4
-
-#define ICS_ARCIN_V6_IDEOFFSET_1 0x800
-#define ICS_ARCIN_V6_INTROFFSET_1 0x880
-#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4
-#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0
-#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00
-#define ICS_ARCIN_V6_INTROFFSET_2 0xc80
-#define ICS_ARCIN_V6_INTRSTAT_2 0xca4
-#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0
-#define ICS_ARCIN_V6_IDESTEPPING 4
-
-static const card_ids icside_cids[] = {
- { MANU_ICS, PROD_ICS_IDE },
- { 0xffff, 0xffff }
-};
-
-typedef enum {
- ics_if_unknown,
- ics_if_arcin_v5,
- ics_if_arcin_v6
-} iftype_t;
-
-static struct expansion_card *ec[MAX_ECARDS];
-static int result[MAX_ECARDS][MAX_IFS];
-
-
-/* ---------------- Version 5 PCB Support Functions --------------------- */
-/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
- * Purpose : enable interrupts from card
- */
-static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
-{
- unsigned int memc_port = (unsigned int)ec->irq_data;
- outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
-}
-
-/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
- * Purpose : disable interrupts from card
- */
-static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
-{
- unsigned int memc_port = (unsigned int)ec->irq_data;
- inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
-}
-
-static const expansioncard_ops_t icside_ops_arcin_v5 = {
- icside_irqenable_arcin_v5,
- icside_irqdisable_arcin_v5,
- NULL,
- NULL
-};
-
-
-/* ---------------- Version 6 PCB Support Functions --------------------- */
-/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
- * Purpose : enable interrupts from card
- */
-static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
-{
- unsigned int ide_base_port = (unsigned int)ec->irq_data;
- outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
- outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
-}
-
-/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
- * Purpose : disable interrupts from card
- */
-static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
-{
- unsigned int ide_base_port = (unsigned int)ec->irq_data;
- inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
- inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
-}
-
-static const expansioncard_ops_t icside_ops_arcin_v6 = {
- icside_irqenable_arcin_v6,
- icside_irqdisable_arcin_v6,
- NULL,
- NULL
-};
-
-
-
-/* Prototype: icside_identifyif (struct expansion_card *ec)
- * Purpose : identify IDE interface type
- * Notes : checks the description string
- */
-static iftype_t icside_identifyif (struct expansion_card *ec)
-{
- unsigned int addr;
- iftype_t iftype;
- int id = 0;
-
- iftype = ics_if_unknown;
-
- addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
-
- id = inb (addr) & 1;
- id |= (inb (addr + 1) & 1) << 1;
- id |= (inb (addr + 2) & 1) << 2;
- id |= (inb (addr + 3) & 1) << 3;
-
- switch (id) {
- case 0: /* A3IN */
- printk ("icside: A3IN unsupported\n");
- break;
-
- case 1: /* A3USER */
- printk ("icside: A3USER unsupported\n");
- break;
-
- case 3: /* ARCIN V6 */
- printk ("icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
- iftype = ics_if_arcin_v6;
- break;
-
- case 15:/* ARCIN V5 (no id) */
- printk ("icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
- iftype = ics_if_arcin_v5;
- break;
-
- default:/* we don't know - complain very loudly */
- printk ("icside: ***********************************\n");
- printk ("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
- printk ("icside: ***********************************\n");
- printk ("icside: please report this to: linux@arm.uk.linux.org\n");
- printk ("icside: defaulting to ARCIN V5\n");
- iftype = ics_if_arcin_v5;
- break;
- }
-
- return iftype;
-}
-
-static int icside_register_port(unsigned long dataport, unsigned long ctrlport, int stepping, int irq)
-{
- hw_regs_t hw;
- int i;
-
- memset(&hw, 0, sizeof(hw));
-
- for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
- hw.io_ports[i] = (ide_ioreg_t)dataport;
- dataport += 1 << stepping;
- }
- hw.io_ports[IDE_CONTROL_OFFSET] = ctrlport;
- hw.irq = irq;
-
- return ide_register_hw(&hw, NULL);
-}
-
-/* Prototype: icside_register (struct expansion_card *ec)
- * Purpose : register an ICS IDE card with the IDE driver
- * Notes : we make sure that interrupts are disabled from the card
- */
-static inline void icside_register (struct expansion_card *ec, int index)
-{
- unsigned long port;
-
- result[index][0] = -1;
- result[index][1] = -1;
-
- switch (icside_identifyif (ec)) {
- case ics_if_unknown:
- default:
- printk ("** Warning: ICS IDE Interface unrecognised! **\n");
- break;
-
- case ics_if_arcin_v5:
- port = ecard_address (ec, ECARD_MEMC, 0);
- ec->irqaddr = ioaddr(port + ICS_ARCIN_V5_INTRSTAT);
- ec->irqmask = 1;
- ec->irq_data = (void *)port;
- ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5;
-
- /*
- * Be on the safe side - disable interrupts
- */
- inb (port + ICS_ARCIN_V5_INTROFFSET);
- result[index][0] = icside_register_port(port + ICS_ARCIN_V5_IDEOFFSET,
- port + ICS_ARCIN_V5_IDEALTOFFSET,
- ICS_ARCIN_V5_IDESTEPPING,
- ec->irq);
- result[index][1] = -1;
- break;
-
- case ics_if_arcin_v6:
- port = ecard_address (ec, ECARD_IOC, ECARD_FAST);
- ec->irqaddr = ioaddr(port + ICS_ARCIN_V6_INTRSTAT_1);
- ec->irqmask = 1;
- ec->irq_data = (void *)port;
- ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6;
-
- /*
- * Be on the safe side - disable interrupts
- */
- inb (port + ICS_ARCIN_V6_INTROFFSET_1);
- inb (port + ICS_ARCIN_V6_INTROFFSET_2);
-
- result[index][0] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_1,
- port + ICS_ARCIN_V6_IDEALTOFFSET_1,
- ICS_ARCIN_V6_IDESTEPPING,
- ec->irq);
- result[index][1] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_2,
- port + ICS_ARCIN_V6_IDEALTOFFSET_2,
- ICS_ARCIN_V6_IDESTEPPING,
- ec->irq);
- break;
- }
-}
-
-int icside_init (void)
-{
- int i;
-
- for (i = 0; i < MAX_ECARDS; i++)
- ec[i] = NULL;
-
- ecard_startfind ();
-
- for (i = 0; ; i++) {
- if ((ec[i] = ecard_find (0, icside_cids)) == NULL)
- break;
-
- ecard_claim (ec[i]);
- icside_register (ec[i], i);
- }
-
- for (i = 0; i < MAX_ECARDS; i++)
- if (ec[i] && result[i][0] < 0 && result[i][1] < 0) {
- ecard_release (ec[i]);
- ec[i] = NULL;
- }
- return 0;
-}
-
-#ifdef MODULE
-int init_module (void)
-{
- return icside_init();
-}
-
-void cleanup_module (void)
-{
- int i;
-
- for (i = 0; i < MAX_ECARDS; i++)
- if (ec[i]) {
- if (result[i][0] >= 0)
- ide_unregister (result[i][0]);
-
- if (result[i][1] >= 0)
- ide_unregister (result[i][1]);
-
- ecard_release (ec[i]);
- ec[i] = NULL;
- }
-}
-#endif
-
+++ /dev/null
-/*
- * linux/arch/arm/drivers/block/ide-rapide.c
- *
- * Copyright (c) 1996-1998 Russell King.
- *
- * Changelog:
- * 08-06-1996 RMK Created
- * 13-04-1998 RMK Added manufacturer and product IDs
- */
-
-#include <linux/module.h>
-#include <linux/malloc.h>
-#include <linux/blkdev.h>
-#include <linux/errno.h>
-#include <asm/ecard.h>
-#include <asm/ide.h>
-
-#include "../../block/ide.h"
-
-static const card_ids rapide_cids[] = {
- { MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 },
- { 0xffff, 0xffff }
-};
-
-static struct expansion_card *ec[MAX_ECARDS];
-static int result[MAX_ECARDS];
-
-static inline int rapide_register(struct expansion_card *ec)
-{
- unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
- ide_ioregspec_t spec;
-
- spec.base = port;
- spec.ctrl = port + 0x206;
- spec.offset = 1 << 4;
- spec.irq = ec->irq;
-
- return ide_register_port(&spec);
-}
-
-int rapide_init(void)
-{
- int i;
-
- for (i = 0; i < MAX_ECARDS; i++)
- ec[i] = NULL;
-
- ecard_startfind();
-
- for (i = 0; ; i++) {
- if ((ec[i] = ecard_find(0, rapide_cids)) == NULL)
- break;
-
- ecard_claim(ec[i]);
- result[i] = rapide_register(ec[i]);
- }
- for (i = 0; i < MAX_ECARDS; i++)
- if (ec[i] && result[i] < 0) {
- ecard_release(ec[i]);
- ec[i] = NULL;
- }
- return 0;
-}
-
-#ifdef MODULE
-
-int init_module (void)
-{
- return rapide_init();
-}
-
-void cleanup_module (void)
-{
- int i;
-
- for (i = 0; i < MAX_ECARDS; i++)
- if (ec[i]) {
- unsigned long port;
- port = ecard_address(ec[i], ECARD_MEMC, 0);
-
- ide_unregister_port(port, ec[i]->irq, 16);
- ecard_release(ec[i]);
- ec[i] = NULL;
- }
-}
-#endif
-
-@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400
-@ motherboard on ST506 podules.
-@ (c) David Alan Gilbert (gilbertd@cs.man.ac.uk) 1996
+@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 Acorn Archimedes
+@ motherboard and on ST506 expansion podules.
+@ (c) David Alan Gilbert (linux@treblig.org) 1996-1999
#include <asm/assembler.h>
-_hdc63463_irqdata:
+hdc63463_irqdata:
@ Controller base address
- .global _hdc63463_baseaddress
-_hdc63463_baseaddress:
+ .global hdc63463_baseaddress
+hdc63463_baseaddress:
.word 0
- .global _hdc63463_irqpolladdress
-_hdc63463_irqpolladdress:
+ .global hdc63463_irqpolladdress
+hdc63463_irqpolladdress:
.word 0
- .global _hdc63463_irqpollmask
-_hdc63463_irqpollmask:
+ .global hdc63463_irqpollmask
+hdc63463_irqpollmask:
.word 0
@ where to read/write data from the kernel data space
- .global _hdc63463_dataptr
-_hdc63463_dataptr:
+ .global hdc63463_dataptr
+hdc63463_dataptr:
.word 0
@ Number of bytes left to transfer
- .global _hdc63463_dataleft
-_hdc63463_dataleft:
+ .global hdc63463_dataleft
+hdc63463_dataleft:
.word 0
@ -------------------------------------------------------------------------
@ hdc63463_writedma: DMA from host to controller
@ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask
@ r3=data ptr, r4=data left, r5,r6=temporary
- .global _hdc63463_writedma
-_hdc63463_writedma:
+ .global hdc63463_writedma
+hdc63463_writedma:
stmfd sp!,{r4-r7}
- adr r5,_hdc63463_irqdata
+ adr r5,hdc63463_irqdata
ldmia r5,{r0,r1,r2,r3,r4}
-
writedma_again:
@ test number of remaining bytes to transfer
@ If we were too slow we had better go through again - DAG - took out with new interrupt routine
@ sub r0,r0,#32+8
- @ adr r2,_hdc63463_irqdata
+ @ adr r2,hdc63463_irqdata
@ ldr r2,[r2,#8]
@ b writedma_again
writedma_end:
- adr r5,_hdc63463_irqdata+12
+ adr r5,hdc63463_irqdata+12
stmia r5,{r3,r4}
ldmfd sp!,{r4-r7}
RETINSTR(mov,pc,lr)
@ hdc63463_readdma: DMA from controller to host
@ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask
@ r3=data ptr, r4=data left, r5,r6=temporary
- .global _hdc63463_readdma
-_hdc63463_readdma:
+ .global hdc63463_readdma
+hdc63463_readdma:
stmfd sp!,{r4-r7}
- adr r5,_hdc63463_irqdata
+ adr r5,hdc63463_irqdata
ldmia r5,{r0,r1,r2,r3,r4}
readdma_again:
@ b readdma_again
readdma_end:
- adr r5,_hdc63463_irqdata+12
+ adr r5,hdc63463_irqdata+12
stmia r5,{r3,r4}
ldmfd sp!,{r4-r7}
RETINSTR(mov,pc,lr)
#include <asm/dma.h>
#include <asm/hardware.h>
#include <asm/ecard.h>
+#include <asm/ioc.h>
/*
* This sort of stuff should be in a header file shared with ide.c, hd.c, xd.c etc
void (*done) (int st); /* done handler */
} *cont = NULL;
+#if 0
static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0};
+#endif
int number_mfm_drives = 1;
+++ /dev/null
-if [ "$CONFIG_SERIAL" != "n" ]; then
- tristate ' Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL
- tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL
-fi
-
-if [ "$CONFIG_MOUSE" = "y" ]; then
- if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
- if [ "$CONFIG_ARCH_RPC" != "y" ]; then
- define_bool CONFIG_KBDMOUSE y
- else
- define_bool CONFIG_RPCMOUSE y
- fi
- fi
-fi
-
# parent makes..
#
-L_TARGET := acorn-char.a
-M_OBJS :=
-L_OBJS :=
+L_TARGET := acorn-char.a
+M_OBJS :=
+L_OBJS :=
-ifeq ($(MACHINE),rpc)
- MOUSE_OBJS += mouse_rpc.o
- L_OBJS += keyb_ps2.o
-endif
+L_OBJS_arc := keyb_arc.o
+L_OBJS_a5k := keyb_arc.o
+L_OBJS_rpc := keyb_ps2.o
-ifeq ($(CONFIG_MOUSE),y)
- LX_OBJS += $(MOUSE_OBJS)
-else
- ifeq ($(CONFIG_MOUSE),m)
- MX_OBJS += $(MOUSE_OBJS)
+ifeq ($(MACHINE),rpc)
+ ifeq ($(CONFIG_MOUSE),y)
+ LX_OBJS += mouse_rpc.o
+ else
+ ifeq ($(CONFIG_MOUSE),m)
+ MX_OBJS += mouse_rpc.o
+ endif
endif
endif
endif
endif
+L_OBJS += $(L_OBJS_$(MACHINE))
+
include $(TOPDIR)/Rules.make
--- /dev/null
+/*
+ * linux/arch/arm/drivers/char1/keyb_arc.c
+ *
+ * Acorn keyboard driver for ARM Linux.
+ *
+ * The Acorn keyboard appears to have a ***very*** buggy reset protocol -
+ * every reset behaves differently. We try to get round this by attempting
+ * a few things...
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/ptrace.h>
+#include <linux/signal.h>
+#include <linux/timer.h>
+#include <linux/random.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/kbd_ll.h>
+#include <linux/tty.h>
+#include <linux/kbd_kern.h>
+#include <linux/delay.h>
+
+#include <asm/bitops.h>
+#include <asm/keyboard.h>
+#include <asm/irq.h>
+#include <asm/ioc.h>
+#include <asm/hardware.h>
+
+#include "../../char/mouse.h"
+
+extern void kbd_reset_kdown(void);
+
+#define VERSION 108
+
+#define KBD_REPORT_ERR
+#define KBD_REPORT_UNKN
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+static char kbd_txval[4];
+static unsigned char kbd_txhead, kbd_txtail;
+#define KBD_INCTXPTR(ptr) ((ptr) = ((ptr) + 1) & 3)
+static int kbd_id = -1;
+static struct wait_queue *kbd_waitq;
+#ifdef CONFIG_KBDMOUSE
+static int mousedev;
+#endif
+
+/*
+ * Protocol codes to send the keyboard.
+ */
+#define HRST 0xff /* reset keyboard */
+#define RAK1 0xfe /* reset response */
+#define RAK2 0xfd /* reset response */
+#define BACK 0x3f /* Ack for first keyboard pair */
+#define SMAK 0x33 /* Last data byte ack (key scanning + mouse movement scanning) */
+#define MACK 0x32 /* Last data byte ack (mouse movement scanning) */
+#define SACK 0x31 /* Last data byte ack (key scanning) */
+#define NACK 0x30 /* Last data byte ack (no scanning, mouse data) */
+#define RQMP 0x22 /* Request mouse data */
+#define PRST 0x21 /* nothing */
+#define RQID 0x20 /* Request ID */
+
+#define UP_FLAG 1
+
+#ifdef CONFIG_MAGIC_SYSRQ
+unsigned char a5kkbd_sysrq_xlate[] =
+{
+ 27, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ '`', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '0', '-', '=', '£', 127, 0,
+ 0, 0, 0, '/', '*', '#', 9, 'q',
+ 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o',
+ 'p', '[', ']', '\\', 22, 23, 25, '7',
+ '8', '9', '-', 0, 'a', 's', 'd', 'f',
+ 'g', 'h', 'j', 'k', 'l', ';', '\'', 13,
+ '4', '5', '6', '+', 0, 0, 'z', 'x',
+ 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
+ 0, 0, '1', '2', '3', 0, 0, ' ',
+ 0, 0, 0, 0, 0, '0', '.', 10,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+#endif
+
+/*
+ * This array converts the scancode that we get from the keyboard to the
+ * real rows/columns on the A5000 keyboard. This might be keyboard specific...
+ *
+ * It is these values that we use to maintain the key down array. That way, we
+ * should pick up on the ghost key presses (which is what happens when you press
+ * three keys, and the keyboard thinks you have pressed four!)
+ *
+ * Row 8 (0x80+c) is actually a column with one key per row. It is isolated from
+ * the other keys, and can't cause these problems (its used for shift, ctrl, alt etc).
+ *
+ * Illegal scancodes are denoted by an 0xff (in other words, we don't know about
+ * them, and can't process them for ghosts). This does however, cause problems with
+ * autorepeat processing...
+ */
+static unsigned char scancode_2_colrow[256] = {
+ 0x01, 0x42, 0x32, 0x33, 0x43, 0x56, 0x5a, 0x6c, 0x7c, 0x5c, 0x5b, 0x6b, 0x7b, 0x84, 0x70, 0x60,
+ 0x11, 0x51, 0x62, 0x63, 0x44, 0x54, 0x55, 0x45, 0x46, 0x4a, 0x3c, 0x4b, 0x59, 0x49, 0x69, 0x79,
+ 0x83, 0x40, 0x30, 0x3b, 0x39, 0x38, 0x31, 0x61, 0x72, 0x73, 0x64, 0x74, 0x75, 0x65, 0x66, 0x6a,
+ 0x1c, 0x2c, 0x7a, 0x36, 0x48, 0x68, 0x78, 0x20, 0x2b, 0x29, 0x28, 0x81, 0x71, 0x22, 0x23, 0x34,
+ 0x24, 0x25, 0x35, 0x26, 0x3a, 0x0c, 0x2a, 0x76, 0x10, 0x1b, 0x19, 0x18, 0x82, 0xff, 0x21, 0x12,
+ 0x13, 0x14, 0x04, 0x05, 0x15, 0x16, 0x1a, 0x0a, 0x85, 0x77, 0x00, 0x0b, 0x09, 0x02, 0x80, 0x03,
+ 0x87, 0x86, 0x06, 0x17, 0x27, 0x07, 0x37, 0x08, 0xff,
+};
+
+#define BITS_PER_SHORT (8*sizeof(unsigned short))
+static unsigned short ghost_down[128/BITS_PER_SHORT];
+
+static void a5kkbd_key(unsigned int keycode, unsigned int up_flag)
+{
+ unsigned int real_keycode;
+
+ if (keycode > 0x72) {
+#ifdef KBD_REPORT_UNKN
+ printk ("kbd: unknown scancode 0x%04x\n", keycode);
+#endif
+ return;
+ }
+ if (keycode >= 0x70) {
+#ifdef CONFIG_KBDMOUSE
+ if (mousedev >= 0)
+ switch (keycode) {
+ case 0x70: /* Left mouse button */
+ busmouse_add_buttons(mousedev, 4, up_flag ? 4 : 0);
+ break;
+
+ case 0x71: /* Middle mouse button */
+ busmouse_add_buttons(mousedev, 2, up_flag ? 2 : 0);
+ break;
+
+ case 0x72:/* Right mouse button */
+ busmouse_add_buttons(mousedev, 1, up_flag ? 1 : 0);
+ break;
+ }
+#endif
+ return;
+ }
+
+ /*
+ * We have to work out if we accept this key press as a real key, or
+ * if it is a ghost. IE. If you press three keys, the keyboard will think
+ * that you've pressed a fourth: (@ = key down, # = ghost)
+ *
+ * 0 1 2 3 4 5 6 7
+ * | | | | | | | |
+ * 0-+-+-+-+-+-+-+-+-
+ * | | | | | | | |
+ * 1-+-@-+-+-+-@-+-+-
+ * | | | | | | | |
+ * 2-+-+-+-+-+-+-+-+-
+ * | | | | | | | |
+ * 3-+-@-+-+-+-#-+-+-
+ * | | | | | | | |
+ *
+ * This is what happens when you have a matrix keyboard...
+ */
+
+ real_keycode = scancode_2_colrow[keycode];
+
+ if ((real_keycode & 0x80) == 0) {
+ int rr, kc = (real_keycode >> 4) & 7;
+ int cc;
+ unsigned short res, kdownkc;
+
+ kdownkc = ghost_down[kc] | (1 << (real_keycode & 15));
+
+ for (rr = 0; rr < 128/BITS_PER_SHORT; rr++)
+ if (rr != kc && (res = ghost_down[rr] & kdownkc)) {
+ /*
+ * we have found a second row with at least one key pressed in the
+ * same column.
+ */
+ for (cc = 0; res; res >>= 1)
+ cc += (res & 1);
+ if (cc > 1)
+ return; /* ignore it */
+ }
+ if (up_flag)
+ clear_bit (real_keycode, ghost_down);
+ else
+ set_bit (real_keycode, ghost_down);
+ }
+
+ handle_scancode(keycode, !up_flag);
+}
+
+static inline void a5kkbd_sendbyte(unsigned char val)
+{
+ kbd_txval[kbd_txhead] = val;
+ KBD_INCTXPTR(kbd_txhead);
+ enable_irq(IRQ_KEYBOARDTX);
+}
+
+static inline void a5kkbd_reset(void)
+{
+ int i;
+
+ for (i = 0; i < NR_SCANCODES/BITS_PER_SHORT; i++)
+ ghost_down[i] = 0;
+
+ kbd_reset_kdown();
+}
+
+void a5kkbd_leds(unsigned char leds)
+{
+ leds = ((leds & (1<<VC_SCROLLOCK))?4:0) | ((leds & (1<<VC_NUMLOCK))?2:0) |
+ ((leds & (1<<VC_CAPSLOCK))?1:0);
+ a5kkbd_sendbyte(leds);
+}
+
+/* Keyboard states:
+ * 0 initial reset condition, receive HRST, send RRAK1
+ * 1 Sent RAK1, wait for RAK1, send RRAK2
+ * 2 Sent RAK2, wait for RAK2, send SMAK or RQID
+ * 3 Sent RQID, expect KBID, send SMAK
+ * 4 Sent SMAK, wait for anything
+ * 5 Wait for second keyboard nibble for key pressed
+ * 6 Wait for second keyboard nibble for key released
+ * 7 Wait for second part of mouse data
+ *
+ * This function returns 1 when we successfully enter the IDLE state
+ * (and hence need to do some keyboard processing).
+ */
+#define KBD_INITRST 0
+#define KBD_RAK1 1
+#define KBD_RAK2 2
+#define KBD_ID 3
+#define KBD_IDLE 4
+#define KBD_KEYDOWN 5
+#define KBD_KEYUP 6
+#define KBD_MOUSE 7
+
+static int handle_rawcode(unsigned int keyval)
+{
+ static signed char kbd_mousedx = 0;
+ signed char kbd_mousedy;
+ static unsigned char kbd_state = KBD_INITRST;
+ static unsigned char kbd_keyhigh = 0;
+
+ if (keyval == HRST && kbd_state != KBD_INITRST && kbd_state != KBD_ID) {
+ a5kkbd_sendbyte (HRST);
+ a5kkbd_reset ();
+ kbd_state = KBD_INITRST;
+ } else switch(kbd_state) {
+ case KBD_INITRST: /* hard reset - sent HRST */
+ if (keyval == HRST) {
+ a5kkbd_sendbyte (RAK1);
+ kbd_state = KBD_RAK1;
+ } else if (keyval == RAK1) {
+ /* Some A5000 keyboards are very fussy and don't follow Acorn's
+ * specs - this appears to fix them, but them it might stop
+ * them from being initialised.
+ * fix by Philip Blundell
+ */
+ printk(KERN_DEBUG "keyboard sent early RAK1 -- ignored\n");
+ } else
+ goto kbd_wontreset;
+ break;
+
+ case KBD_RAK1: /* sent RAK1 - expect RAK1 and send RAK2 */
+ if (keyval == RAK1) {
+ a5kkbd_sendbyte (RAK2);
+ kbd_state = KBD_RAK2;
+ } else
+ goto kbd_wontreset;
+ break;
+
+ case KBD_RAK2: /* Sent RAK2 - expect RAK2 and send either RQID or SMAK */
+ if (keyval == RAK2) {
+ if (kbd_id == -1) {
+ a5kkbd_sendbyte (NACK);
+ a5kkbd_sendbyte (RQID);
+ kbd_state = KBD_ID;
+ } else {
+ a5kkbd_sendbyte (SMAK);
+ kbd_state = KBD_IDLE;
+ }
+ } else
+ goto kbd_wontreset;
+ break;
+
+ case KBD_ID: /* Sent RQID - expect KBID */
+ if (keyval == HRST) {
+ kbd_id = -2;
+ a5kkbd_reset ();
+ a5kkbd_sendbyte (HRST);
+ kbd_state = KBD_INITRST;
+ wake_up (&kbd_waitq);
+ } else if ((keyval & 0xc0) == 0x80) {
+ kbd_id = keyval & 0x3f;
+ a5kkbd_sendbyte (SMAK);
+ kbd_state = KBD_IDLE;
+ wake_up (&kbd_waitq);
+ }
+ break;
+
+ case KBD_IDLE: /* Send SMAK, ready for any reply */
+ switch (keyval & 0xf0) {
+ default: /* 0x00 - 0x7f */
+ kbd_mousedx = keyval & 0x40 ? keyval|0x80 : keyval;
+ kbd_state = KBD_MOUSE;
+ a5kkbd_sendbyte (BACK);
+ break;
+
+ case 0x80:
+ case 0x90:
+ case 0xa0:
+ case 0xb0:
+ if (kbd_id == -1)
+ kbd_id = keyval & 0x3f;
+ break;
+
+ case 0xc0:
+ kbd_keyhigh = keyval;
+ kbd_state = KBD_KEYDOWN;
+ a5kkbd_sendbyte (BACK);
+ break;
+
+ case 0xd0:
+ kbd_keyhigh = keyval;
+ kbd_state = KBD_KEYUP;
+ a5kkbd_sendbyte (BACK);
+ break;
+
+ case 0xe0:
+ case 0xf0:
+ goto kbd_error;
+ }
+ break;
+
+ case KBD_KEYDOWN:
+ if ((keyval & 0xf0) != 0xc0)
+ goto kbd_error;
+ else {
+ kbd_state = KBD_IDLE;
+ a5kkbd_sendbyte (SMAK);
+ if (((kbd_keyhigh ^ keyval) & 0xf0) == 0)
+ a5kkbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), 0);
+ }
+ break;
+
+ case KBD_KEYUP:
+ if ((keyval & 0xf0) != 0xd0)
+ goto kbd_error;
+ else {
+ kbd_state = KBD_IDLE;
+ a5kkbd_sendbyte (SMAK);
+ if (((kbd_keyhigh ^ keyval) & 0xf0) == 0)
+ a5kkbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), UP_FLAG);
+ }
+ break;
+
+ case KBD_MOUSE:
+ if (keyval & 0x80)
+ goto kbd_error;
+ else {
+ kbd_state = KBD_IDLE;
+ a5kkbd_sendbyte (SMAK);
+ kbd_mousedy = (char)(keyval & 0x40 ? keyval | 0x80 : keyval);
+#ifdef CONFIG_KBDMOUSE
+ if (mousedev >= 0)
+ busmouse_add_movement(mousedev, (int)kbd_mousedx, (int)kbd_mousedy);
+#endif
+ }
+ }
+ return kbd_state == KBD_IDLE ? 1 : 0;
+
+kbd_wontreset:
+#ifdef KBD_REPORT_ERR
+ printk ("kbd: keyboard won't reset (kbdstate %d, keyval %02X)\n",
+ kbd_state, keyval);
+#endif
+ mdelay(1);
+ inb(IOC_KARTRX);
+ a5kkbd_sendbyte (HRST);
+ kbd_state = KBD_INITRST;
+ return 0;
+
+kbd_error:
+#ifdef KBD_REPORT_ERR
+ printk ("kbd: keyboard out of sync - resetting\n");
+#endif
+ a5kkbd_sendbyte (HRST);
+ kbd_state = KBD_INITRST;
+ return 0;
+}
+
+static void a5kkbd_rx(int irq, void *dev_id, struct pt_regs *regs)
+{
+ kbd_pt_regs = regs;
+ if (handle_rawcode(inb(IOC_KARTRX)))
+ mark_bh (KEYBOARD_BH);
+}
+
+static void a5kkbd_tx(int irq, void *dev_id, struct pt_regs *regs)
+{
+ outb (kbd_txval[kbd_txtail], IOC_KARTTX);
+ KBD_INCTXPTR(kbd_txtail);
+ if (kbd_txtail == kbd_txhead)
+ disable_irq(irq);
+}
+
+#ifdef CONFIG_KBDMOUSE
+static struct busmouse a5kkbd_mouse = {
+ 6, "kbdmouse", NULL, NULL, 7
+};
+#endif
+
+__initfunc(void a5kkbd_init_hw (void))
+{
+ unsigned long flags;
+
+ save_flags_cli (flags);
+ if (request_irq (IRQ_KEYBOARDTX, a5kkbd_tx, 0, "keyboard", NULL) != 0)
+ panic("Could not allocate keyboard transmit IRQ!");
+ disable_irq (IRQ_KEYBOARDTX);
+ if (request_irq (IRQ_KEYBOARDRX, a5kkbd_rx, 0, "keyboard", NULL) != 0)
+ panic("Could not allocate keyboard receive IRQ!");
+ (void)inb(IOC_KARTRX);
+ restore_flags (flags);
+
+ a5kkbd_sendbyte (HRST); /* send HRST (expect HRST) */
+
+ /* wait 1s for keyboard to initialise */
+ interruptible_sleep_on_timeout(&kbd_waitq, HZ);
+
+#ifdef CONFIG_KBDMOUSE
+ mousedev = register_busmouse(&a5kkbd_mouse);
+ if (mousedev < 0)
+ printk(KERN_ERR "Unable to register mouse driver\n");
+#endif
+
+ printk (KERN_INFO "Keyboard driver v%d.%02d. (", VERSION/100, VERSION%100);
+ if (kbd_id != -1)
+ printk ("id=%d ", kbd_id);
+ printk ("English)\n");
+}
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/io.h>
+#include <asm/iomd.h>
#include <asm/system.h>
extern void kbd_reset_kdown(void);
};
#endif
-int ps2kbd_translate(unsigned char scancode, unsigned char *keycode_p, char *uf_p)
-{
- *uf_p = scancode & 0200;
- *keycode_p = scancode & 0x7f;
- return 1;
-}
-
-static void ps2kbd_key(unsigned int keycode, unsigned int up_flag)
+static inline void ps2kbd_key(unsigned int keycode, unsigned int up_flag)
{
handle_scancode(keycode, !up_flag);
}
/*
- * linux/drivers/char/rpcmouse.c
+ * linux/drivers/char/mouse_rpc.c
*
* Copyright (C) 1996-1998 Russell King
*
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
+#include <asm/iomd.h>
#include "../../char/mouse.h"
#ifdef MODULE
static int __serial_ports[NUM_SERIALS];
static int __serial_pcount;
+static int __serial_addr[NUM_SERIALS];
static struct expansion_card *expcard[MAX_ECARDS];
#define ADD_ECARD(ec,card) expcard[(card)] = (ec)
-#define ADD_PORT(port) __serial_ports[__serial_pcount++] = (port)
+#define ADD_PORT(port,addr) \
+ do { \
+ __serial_ports[__serial_pcount] = (port); \
+ __serial_addr[__serial_pcount] = (addr); \
+ __serial_pcount += 1; \
+ } while (0)
#undef MY_INIT
#define MY_INIT init_module
#else
#define ADD_ECARD(ec,card)
-#define ADD_PORT(port)
+#define ADD_PORT(port,addr)
#endif
static const card_ids serial_cids[] = { MY_CARD_LIST, { 0xffff, 0xffff } };
cardaddr = MY_BASE_ADDRESS(ec);
for (port = 0; port < MY_NUMPORTS; port ++) {
+ unsigned long address;
int line;
- line = serial_register_onedev (MY_PORT_ADDRESS(port, cardaddr), ec->irq);
+ address = MY_PORT_ADDRESS(port, cardaddr);
+
+ line = serial_register_onedev (address, ec->irq);
if (line < 0)
break;
- ADD_PORT(line);
+ ADD_PORT(line, address);
}
if (port) {
{
int i;
- for (i = 0; i < __serial_pcount; i++)
- unregister_serial (__serial_ports[i]);
+ for (i = 0; i < __serial_pcount; i++) {
+ unregister_serial(__serial_ports[i]);
+ release_region(__serial_addr[i], 8);
+ }
for (i = 0; i < MAX_ECARDS; i++)
if (expcard[i])
#define BUS_16 16
#define BUS_8 8
-static const card_ids ether1_cids[] = {
+static const card_ids __init ether1_cids[] = {
{ MANU_ACORN, PROD_ACORN_ETHER1 },
{ 0xffff, 0xffff }
};
{
int used;
- addr = IO_BASE + (addr << 2);
+ addr = ioaddr(addr);
__asm__ __volatile__(
"subs %3, %3, #2
{
int used;
- addr = IO_BASE + (addr << 2);
+ addr = ioaddr(addr);
__asm__ __volatile__(
"subs %3, %3, #2
/* Fill in the fields of the device structure with ethernet values */
ether_setup (dev);
-#ifndef CLAIM_IRQ_AT_OPEN
- if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev)) {
- kfree (dev->priv);
- return -EAGAIN;
- }
-#endif
return 0;
}
ether1_open (struct device *dev)
{
struct ether1_priv *priv = (struct ether1_priv *)dev->priv;
-#ifdef CLAIM_IRQ_AT_OPEN
+
if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev))
return -EAGAIN;
-#endif
+
MOD_INC_USE_COUNT;
memset (&priv->stats, 0, sizeof (struct enet_statistics));
if (ether1_init_for_open (dev)) {
-#ifdef CLAIM_IRQ_AT_OPEN
free_irq (dev->irq, dev);
-#endif
MOD_DEC_USE_COUNT;
return -EAGAIN;
}
static int
ether1_close (struct device *dev)
{
-#ifdef CLAIM_IRQ_AT_OPEN
- free_irq (dev->irq, dev);
-#endif
-
ether1_reset (dev);
+ free_irq(dev->irq, dev);
+
dev->start = 0;
dev->tbusy = 0;
#ifdef MODULE
-static char ethernames[MAX_ECARDS][9];
-static struct device *my_ethers[MAX_ECARDS];
-static struct expansion_card *ec[MAX_ECARDS];
+static struct ether_dev {
+ struct expansion_card *ec;
+ char name[9];
+ struct device dev;
+} ether_devs[MAX_ECARDS];
int
init_module (void)
{
- int i;
+ struct expansion_card *ec;
+ int i, ret = -ENODEV;
- for (i = 0; i < MAX_ECARDS; i++) {
- my_ethers[i] = NULL;
- ec[i] = NULL;
- strcpy (ethernames[i], " ");
- }
+ memset(ether_devs, 0, sizeof(ether_devs));
+ ecard_startfind ();
+ ec = ecard_find(0, ether1_cids);
i = 0;
- ecard_startfind ();
+ while (ec && i < MAX_ECARDS) {
+ ecard_claim(ec);
- do {
- if ((ec[i] = ecard_find(0, ether1_cids)) == NULL)
- break;
+ ether_devs[i].ec = ec;
+ ether_devs[i].dev.irq = ec->irq;
+ ether_devs[i].dev.base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST);
+ ether_devs[i].dev.init = ether1_probe;
+ ether_devs[i].dev.name = ether_devs[i].name;
- my_ethers[i] = (struct device *)kmalloc (sizeof (struct device), GFP_KERNEL);
- memset (my_ethers[i], 0, sizeof (struct device));
-
- my_ethers[i]->irq = ec[i]->irq;
- my_ethers[i]->base_addr = ecard_address (ec[i], ECARD_IOC, ECARD_FAST);
- my_ethers[i]->init = ether1_probe;
- my_ethers[i]->name = ethernames[i];
-
- ecard_claim (ec[i]);
-
- if (register_netdev (my_ethers[i]) != 0) {
- for (i = 0; i < 4; i++) {
- if (my_ethers[i]) {
- kfree (my_ethers[i]);
- my_ethers[i] = NULL;
- }
- if (ec[i]) {
- ecard_release (ec[i]);
- ec[i] = NULL;
- }
- }
- return -EIO;
+ ret = register_netdev(ðer_devs[i].dev);
+
+ if (ret) {
+ ecard_release(ec);
+ ether_devs[i].ec = NULL;
+ break;
}
- i++;
- } while (i < MAX_ECARDS);
- return i != 0 ? 0 : -ENODEV;
+ i += 1;
+ ec = ecard_find(0, ether1_cids);
+ }
+
+ return i != 0 ? 0 : ret;
}
void
int i;
for (i = 0; i < MAX_ECARDS; i++) {
- if (my_ethers[i]) {
- unregister_netdev (my_ethers[i]);
- release_region (my_ethers[i]->base_addr, 16);
- release_region (my_ethers[i]->base_addr + 0x800, 4096);
-#ifndef CLAIM_IRQ_AT_OPEN
- free_irq (my_ethers[i]->irq, my_ethers[i]);
-#endif
- my_ethers[i] = NULL;
- }
- if (ec[i]) {
- ecard_release (ec[i]);
- ec[i] = NULL;
+ if (ether_devs[i].ec) {
+ unregister_netdev(ðer_devs[i].dev);
+
+ release_region(ether_devs[i].dev.base_addr, 16);
+ release_region(ether_devs[i].dev.base_addr + 0x800, 4096);
+
+ ecard_release(ether_devs[i].ec);
+
+ ether_devs[i].ec = NULL;
}
}
}
* packet starts two bytes from the end of the
* buffer, it corrupts the receiver chain, and
* never updates the transmit status correctly.
- * TODO:
- * When we detect a fatal error on the interface, we should restart it.
+ * 1.14 RMK 07/01/1998 Added initial code for ETHERB addressing.
+ * 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy
+ * hardware.
*/
-static char *version = "ether3 ethernet driver (c) 1995-1998 R.M.King v1.13\n";
+static char *version = "ether3 ethernet driver (c) 1995-1999 R.M.King v1.15\n";
#include <linux/module.h>
#include <linux/kernel.h>
#include "ether3.h"
static unsigned int net_debug = NET_DEBUG;
-static const card_ids ether3_cids[] = {
+static const card_ids __init ether3_cids[] = {
{ MANU_ANT2, PROD_ANT_ETHER3 },
{ MANU_ANT, PROD_ANT_ETHER3 },
{ MANU_ANT, PROD_ANT_ETHERB }, /* trial - will etherb work? */
static int ether3_rx(struct device *dev, struct dev_priv *priv, unsigned int maxcnt);
static void ether3_tx(struct device *dev, struct dev_priv *priv);
-extern int inswb(int reg, void *buffer, int len);
-extern int outswb(int reg, void *buffer, int len);
-
#define BUS_16 2
#define BUS_8 1
#define BUS_UNKNOWN 0
* I'm not sure what address we should default to if the internal one
* is corrupted...
*/
-unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+unsigned char def_eth_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
/* --------------------------------------------------------------------------- */
/*
* ether3 read/write. Slow things down a bit...
+ * The SEEQ8005 doesn't like us writing to it's registers
+ * too quickly.
*/
#define ether3_outb(v,r) { outb((v),(r)); udelay(1); }
#define ether3_outw(v,r) { outw((v),(r)); udelay(1); }
* write data to the buffer memory
*/
#define ether3_writebuffer(dev,data,length) \
- outswb(REG_BUFWIN, (data), (length))
+ outsw(REG_BUFWIN, (data), (length) >> 1)
#define ether3_writeword(dev,data) \
outw((data), REG_BUFWIN)
* read data from the buffer memory
*/
#define ether3_readbuffer(dev,data,length) \
- inswb(REG_BUFWIN, (data), (length))
+ insw(REG_BUFWIN, (data), (length) >> 1)
#define ether3_readword(dev) \
inw(REG_BUFWIN)
}
} else {
if (bad != -1) {
- if (bad != i - 1)
+ if (bad != i - 1)
printk(" - 0x%04X\n", i - 1);
printk("\n");
bad = -1;
for (i = 0; i < 6; i++)
ether3_outb(dev->dev_addr[i], REG_BUFWIN);
- priv->tx_used = 0;
priv->tx_head = 0;
priv->tx_tail = 0;
priv->regs.config2 |= CFG2_CTRLO;
return error;
}
+__initfunc(static void
+ether3_get_dev(struct device *dev, struct expansion_card *ec))
+{
+ ecard_claim(ec);
+
+ dev->base_addr = ecard_address(ec, ECARD_MEMC, 0);
+ dev->irq = ec->irq;
+
+ if (ec->cid.manufacturer == MANU_ANT &&
+ ec->cid.product == PROD_ANT_ETHERB) {
+ dev->base_addr += 0x200;
+ }
+
+ ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr);
+ ec->irqmask = 0xf0;
+
+ ether3_addr(dev->dev_addr, ec);
+}
+
#ifndef MODULE
__initfunc(int
ether3_probe(struct device *dev))
if ((ec = ecard_find(0, ether3_cids)) == NULL)
return ENODEV;
- dev->base_addr = ecard_address(ec, ECARD_MEMC, 0);
- dev->irq = ec->irq;
-
- ecard_claim(ec);
+ ether3_get_dev(dev, ec);
- ether3_addr(dev->dev_addr, ec);
return ether3_probe1(dev);
}
#endif
ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
}
-/*
- * Allocate memory in transmitter ring buffer.
- */
-static int
-ether3_alloc_tx(struct dev_priv *priv, int length, int alloc)
-{
- int start, head, tail;
-
- tail = priv->tx_tail;
- start = priv->tx_head;
- head = start + length + 4;
-
- if (head >= TX_END) {
- if (tail > priv->tx_head)
- return -1;
- head -= TX_END - TX_START;
- if (tail < head)
- return -1;
- } else if (start < tail && tail < head)
- return -1;
-
- if (alloc)
- priv->tx_head = head;
-
- return start;
-}
-
/*
* Transmit a packet
*/
if (!test_and_set_bit(0, (void *)&dev->tbusy)) {
unsigned long flags;
unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- int ptr;
+ unsigned int ptr, next_ptr;
length = (length + 1) & ~1;
return 0;
}
+ next_ptr = (priv->tx_head + 1) & 15;
+
save_flags_cli(flags);
- ptr = ether3_alloc_tx(priv, length, 1);
- if (ptr == -1)
+ if (priv->tx_tail == next_ptr) {
+ restore_flags(flags);
return 1; /* unable to queue */
+ }
+
+ dev->trans_start = jiffies;
+ ptr = 0x600 * priv->tx_head;
+ priv->tx_head = next_ptr;
+ next_ptr *= 0x600;
#define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS)
- ether3_setbuffer(dev, buffer_write, priv->tx_head);
+ ether3_setbuffer(dev, buffer_write, next_ptr);
ether3_writelong(dev, 0);
-
ether3_setbuffer(dev, buffer_write, ptr);
ether3_writelong(dev, 0);
ether3_writebuffer(dev, skb->data, length);
-
+ ether3_writeword(dev, htons(next_ptr));
+ ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16);
ether3_setbuffer(dev, buffer_write, ptr);
- ether3_writeword(dev, htons(priv->tx_head));
+ ether3_writeword(dev, htons((ptr + length + 4)));
ether3_writeword(dev, TXHDR_FLAGS >> 16);
ether3_ledon(dev, priv);
ether3_outw(priv->regs.command | CMD_TXON, REG_COMMAND);
}
- if (ether3_alloc_tx(priv, 2044, 0) != -1)
+ next_ptr = (priv->tx_head + 1) & 15;
+ if (priv->tx_tail != next_ptr)
dev->tbusy = 0;
- dev->trans_start = jiffies;
-
restore_flags(flags);
dev_kfree_skb(skb);
ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2));
printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name,
ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR));
- printk(KERN_ERR "%s: tx head=%04X tx tail=%04X\n", dev->name,
+ printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name,
priv->tx_head, priv->tx_tail);
ether3_setbuffer(dev, buffer_read, priv->tx_tail);
printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev));
dev->tbusy = 0;
priv->regs.config2 |= CFG2_CTRLO;
priv->stats.tx_errors += 1;
- ether3_outw(priv->regs.config2 , REG_CONFIG2);
+ ether3_outw(priv->regs.config2, REG_CONFIG2);
dev->trans_start = jiffies;
+ priv->tx_head = priv->tx_tail = 0;
goto retry;
}
}
ether3_tx(struct device *dev, struct dev_priv *priv)
{
unsigned int tx_tail = priv->tx_tail;
+ int max_work = 14;
do {
unsigned long status;
/*
* Read the packet header
*/
- ether3_setbuffer(dev, buffer_read, tx_tail);
+ ether3_setbuffer(dev, buffer_read, tx_tail * 0x600);
status = ether3_readlong(dev);
/*
if (status & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++;
}
- tx_tail = htons(status & TX_NEXT);
- if (tx_tail < TX_START || tx_tail >= TX_END) {
- printk("%s: transmit error: next pointer = %04X\n", dev->name, tx_tail);
- tx_tail = TX_START;
- priv->tx_head = TX_START;
- priv->tx_tail = TX_END;
- }
- } while (1);
+ tx_tail = (tx_tail + 1) & 15;
+ } while (--max_work);
if (priv->tx_tail != tx_tail) {
priv->tx_tail = tx_tail;
- if (priv->tx_used <= MAX_TX_BUFFERED) {
- dev->tbusy = 0;
- mark_bh(NET_BH); /* Inform upper layers. */
- }
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
}
}
#ifdef MODULE
-char ethernames[MAX_ECARDS][9];
-
-static struct device *my_ethers[MAX_ECARDS];
-static struct expansion_card *ec[MAX_ECARDS];
+static struct ether_dev {
+ struct expansion_card *ec;
+ char name[9];
+ struct device dev;
+} ether_devs[MAX_ECARDS];
int
init_module(void)
{
- int i;
+ struct expansion_card *ec;
+ int i, ret = -ENODEV;
- for(i = 0; i < MAX_ECARDS; i++) {
- my_ethers[i] = NULL;
- ec[i] = NULL;
- strcpy(ethernames[i], " ");
- }
+ memset(ether_devs, 0, sizeof(ether_devs));
+ ecard_startfind ();
+ ec = ecard_find(0, ether3_cids);
i = 0;
- ecard_startfind();
+ while (ec && i < MAX_ECARDS) {
+ ecard_claim(ec);
- do {
- if ((ec[i] = ecard_find(0, ether3_cids)) == NULL)
- break;
+ ether_devs[i].ec = ec;
+ ether_devs[i].dev.init = ether3_probe1;
+ ether_devs[i].dev.name = ether_devs[i].name;
+ ether3_get_dev(ðer_devs[i].dev, ec);
- my_ethers[i] = (struct device *)kmalloc(sizeof(struct device), GFP_KERNEL);
- memset(my_ethers[i], 0, sizeof(struct device));
+ ret = register_netdev(ðer_devs[i].dev);
- my_ethers[i]->irq = ec[i]->irq;
- my_ethers[i]->base_addr= ecard_address(ec[i], ECARD_MEMC, 0);
- my_ethers[i]->init = ether3_probe1;
- my_ethers[i]->name = ethernames[i];
-
- ether3_addr(my_ethers[i]->dev_addr, ec[i]);
-
- ecard_claim(ec[i]);
+ if (ret) {
+ ecard_release(ec);
+ ether_devs[i].ec = NULL;
+ } else
+ i += 1;
- if(register_netdev(my_ethers[i]) != 0) {
- for (i = 0; i < 4; i++) {
- if(my_ethers[i]) {
- kfree(my_ethers[i]);
- my_ethers[i] = NULL;
- }
- if(ec[i]) {
- ecard_release(ec[i]);
- ec[i] = NULL;
- }
- }
- return -EIO;
- }
- i++;
+ ec = ecard_find(0, ether3_cids);
}
- while(i < MAX_ECARDS);
- return i != 0 ? 0 : -ENODEV;
+ return i != 0 ? 0 : ret;
}
void
cleanup_module(void)
{
int i;
+
for (i = 0; i < MAX_ECARDS; i++) {
- if (my_ethers[i]) {
- release_region(my_ethers[i]->base_addr, 128);
- unregister_netdev(my_ethers[i]);
- my_ethers[i] = NULL;
- }
- if (ec[i]) {
- ecard_release(ec[i]);
- ec[i] = NULL;
+ if (ether_devs[i].ec) {
+ unregister_netdev(ðer_devs[i].dev);
+
+ release_region(ether_devs[i].dev.base_addr, 128);
+
+ ecard_release(ether_devs[i].ec);
+
+ ether_devs[i].ec = NULL;
}
}
}
unsigned int config1;
unsigned int config2;
} regs;
- unsigned int tx_head; /* address to insert next packet */
- unsigned int tx_tail; /* address of transmitting packet */
- unsigned int tx_used; /* number of 'slots' used */
+ unsigned char tx_head; /* buffer nr to insert next packet */
+ unsigned char tx_tail; /* buffer nr of transmitting packet */
unsigned int rx_head; /* address to fetch next packet from */
struct enet_statistics stats;
struct timer_list timer;
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
+#include <linux/init.h>
#include <asm/system.h>
#include <asm/bitops.h>
#define DEBUG_INIT 2
static unsigned int net_debug = NET_DEBUG;
-static const card_ids etherh_cids[] = {
+static const card_ids __init etherh_cids[] = {
{ MANU_I3, PROD_I3_ETHERLAN500 },
{ MANU_I3, PROD_I3_ETHERLAN600 },
{ MANU_I3, PROD_I3_ETHERLAN600A },
{ 0xffff, 0xffff }
};
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("i3 EtherH driver");
+
static char *version = "etherh [500/600/600A] ethernet driver (c) 1998 R.M.King v1.05\n";
#define ETHERH500_DATAPORT 0x200 /* MEMC */
* Read the ethernet address string from the on board rom.
* This is an ascii string...
*/
-static int
-etherh_addr(char *addr, struct expansion_card *ec)
+__initfunc(static int
+etherh_addr(char *addr, struct expansion_card *ec))
{
struct in_chunk_dir cd;
char *s;
if (ei_status.word16)
outsw (dma_addr, buf, count >> 1);
-#ifdef BIT8
else
outsb (dma_addr, buf, count);
-#endif
dma_start = jiffies;
insw (dma_addr, buf, count >> 1);
if (count & 1)
buf[count - 1] = inb (dma_addr);
- }
-#ifdef BIT8
- else
+ } else
insb (dma_addr, buf, count);
-#endif
outb (ENISR_RDC, addr + EN0_ISR);
ei_status.dmaing &= ~1;
if (ei_status.word16)
insw (dma_addr, hdr, sizeof (*hdr) >> 1);
-#ifdef BIT8
else
insb (dma_addr, hdr, sizeof (*hdr));
-#endif
outb (ENISR_RDC, addr + EN0_ISR);
ei_status.dmaing &= ~1;
/*
* This is the real probe routine.
*/
-static int
-etherh_probe1(struct device *dev)
+__initfunc(static int
+etherh_probe1(struct device *dev))
{
static int version_printed;
unsigned int addr, i, reg0, tmp;
etherh_irq_enable,
etherh_irq_disable,
NULL,
+ NULL,
+ NULL,
NULL
};
-static void etherh_initdev (ecard_t *ec, struct device *dev)
+__initfunc(static void
+etherh_initdev(ecard_t *ec, struct device *dev))
{
ecard_claim (ec);
}
ec->ops = ðerh_ops;
- etherh_addr (dev->dev_addr, ec);
+ etherh_addr(dev->dev_addr, ec);
}
#ifndef MODULE
-int
-etherh_probe(struct device *dev)
+__initfunc(int
+etherh_probe(struct device *dev))
{
if (!dev)
return ENODEV;
- ecard_startfind ();
-
- if (!dev->base_addr) {
+ if (!dev->base_addr || dev->base_addr == 0xffe0) {
struct expansion_card *ec;
+ ecard_startfind();
+
if ((ec = ecard_find (0, etherh_cids)) == NULL)
return ENODEV;
- etherh_initdev (ec, dev);
+ etherh_initdev(ec, dev);
}
- return etherh_probe1 (dev);
+ return etherh_probe1(dev);
}
#endif
init_all_cards(void)
{
struct device *dev = NULL;
- struct expansion_card *boguscards[MAX_ETHERH_CARDS];
int i, found = 0;
for (i = 0; i < MAX_ETHERH_CARDS; i++) {
my_ethers[i] = NULL;
- boguscards[i] = NULL;
ec[i] = NULL;
strcpy (ethernames[i], " ");
}
if (register_netdev(dev) != 0) {
printk (KERN_WARNING "No etherh card found at %08lX\n", dev->base_addr);
if (ec[i]) {
- boguscards[i] = ec[i];
+ ecard_release(ec[i]);
ec[i] = NULL;
}
continue;
if (dev)
kfree (dev);
- for (i = 0; i < MAX_ETHERH_CARDS; i++)
- if (boguscards[i]) {
- boguscards[i]->ops = NULL;
- ecard_release (boguscards[i]);
- }
-
return found ? 0 : -ENODEV;
}
bool ' Support SCSI 2 Synchronous Transfers' CONFIG_SCSI_ACORNSCSI_SYNC
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate 'ARXE SCSI support (Experimental)' CONFIG_SCSI_ARXESCSI $CONFIG_SCSI
dep_tristate 'CumanaSCSI II support (Experimental)' CONFIG_SCSI_CUMANA_2 $CONFIG_SCSI
dep_tristate 'EESOX support (Experimental)' CONFIG_SCSI_EESOXSCSI $CONFIG_SCSI
dep_tristate 'PowerTec support (Experimental)' CONFIG_SCSI_POWERTECSCSI $CONFIG_SCSI
- comment 'The following drives are not fully supported'
+ comment 'The following drivers are not fully supported'
dep_tristate 'CumanaSCSI I support' CONFIG_SCSI_CUMANA_1 $CONFIG_SCSI
if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then
endif
endif
+ifeq ($(CONFIG_SCSI_ARXESCSI),y)
+ L_OBJS += arxescsi.o
+ CONFIG_FAS216_BUILTIN=y
+else
+ ifeq ($(CONFIG_SCSI_ARXESCSI),m)
+ M_OBJS += arxescsi.o
+ CONFIG_FAS216_MODULE=y
+ endif
+endif
+
ifeq ($(CONFIG_SCSI_CUMANA_1),y)
L_OBJS += cumana_1.o
else
ifeq ($(CONFIG_SCSI_CUMANA_2),y)
L_OBJS += cumana_2.o
- CONFIG_QUEUE_BUILTIN=y
CONFIG_FAS216_BUILTIN=y
else
ifeq ($(CONFIG_SCSI_CUMANA_2),m)
M_OBJS += cumana_2.o
- CONFIG_QUEUE_MODULE=y
CONFIG_FAS216_MODULE=y
endif
endif
ifeq ($(CONFIG_SCSI_POWERTECSCSI),y)
L_OBJS += powertec.o
- CONFIG_QUEUE_BUILTIN=y
CONFIG_FAS216_BUILTIN=y
else
ifeq ($(CONFIG_SCSI_POWERTECSCSI),m)
M_OBJS += powertec.o
- CONFIG_QUEUE_MODULE=y
CONFIG_FAS216_MODULE=y
endif
endif
ifeq ($(CONFIG_SCSI_EESOXSCSI),y)
L_OBJS += eesox.o
- CONFIG_QUEUE_BUILTIN=y
CONFIG_FAS216_BUILTIN=y
else
ifeq ($(CONFIG_SCSI_EESOXSCSI),m)
M_OBJS += eesox.o
- CONFIG_QUEUE_MODULE=y
CONFIG_FAS216_MODULE=y
endif
endif
-ifeq ($(CONFIG_QUEUE_BUILTIN),y)
- LX_OBJS += queue.o msgqueue.o
-else
- ifeq ($(CONFIG_QUEUE_MODULE),y)
- MX_OBJS += queue.o msgqueue.o
- endif
-endif
-
ifeq ($(CONFIG_FAS216_BUILTIN),y)
LX_OBJS += fas216.o
+ CONFIG_QUEUE_BUILTIN=y
else
ifeq ($(CONFIG_FAS216_MODULE),y)
MX_OBJS += fas216.o
+ CONFIG_QUEUE_MODULE=y
+ endif
+endif
+
+ifeq ($(CONFIG_QUEUE_BUILTIN),y)
+ LX_OBJS += queue.o msgqueue.o
+else
+ ifeq ($(CONFIG_QUEUE_MODULE),y)
+ MX_OBJS += queue.o msgqueue.o
endif
endif
* 12-Oct-1997 RMK Added catch for re-entering interrupt routine.
* 15-Oct-1997 RMK Improved handling of commands.
* 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h.
+ * 13-Dec-1998 RMK Better abort code and command handling. Extra state
+ * transitions added to allow dodgy devices to work.
*/
#define DEBUG_NO_WRITE 1
#define DEBUG_QUEUES 2
#define DEBUG_RESET 1024
#define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\
DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\
- DEBUG_DMA|DEBUG_QUEUES|DEBUG_NO_WRITE)
+ DEBUG_DMA|DEBUG_QUEUES)
/* DRIVER CONFIGURATION
*
#ifndef STRINGIFY
#define STRINGIFY(x) #x
#endif
-#define STR(x) STRINGIFY(x)
-#define NO_WRITE_STR STR(NO_WRITE)
+#define STRx(x) STRINGIFY(x)
+#define NO_WRITE_STR STRx(NO_WRITE)
#include <linux/config.h>
#include <linux/module.h>
#include <asm/bitops.h>
#include <asm/system.h>
#include <asm/io.h>
+#include <asm/irq.h>
#include <asm/ecard.h>
#include "../../scsi/scsi.h"
#error "Yippee! ABORT TAG is now defined! Remove this error!"
#endif
-#ifndef NO_IRQ
-#define NO_IRQ 255
-#endif
-
#ifdef CONFIG_SCSI_ACORNSCSI_LINK
#error SCSI2 LINKed commands not supported (yet)!
#endif
#define DMAC_BUFFER_SIZE 65536
#endif
-/*
- * This is used to dump the previous states of the SBIC
- */
-static struct status_entry {
- unsigned long when;
- unsigned char ssr;
- unsigned char ph;
- unsigned char irq;
- unsigned char unused;
-} status[9][16];
-static unsigned char status_ptr[9];
-
-#define ADD_STATUS(_q,_ssr,_ph,_irq) \
-({ \
- status[(_q)][status_ptr[(_q)]].when = jiffies; \
- status[(_q)][status_ptr[(_q)]].ssr = (_ssr); \
- status[(_q)][status_ptr[(_q)]].ph = (_ph); \
- status[(_q)][status_ptr[(_q)]].irq = (_irq); \
- status_ptr[(_q)] = (status_ptr[(_q)] + 1) & 15; \
-})
+#define STATUS_BUFFER_TO_PRINT 24
unsigned int sdtr_period = SDTR_PERIOD;
unsigned int sdtr_size = SDTR_SIZE;
PROC_SCSI_EATA, 9, "acornscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2
};
-static void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result);
-static int acornscsi_reconnect_finish (AS_Host *host);
-static void acornscsi_dma_cleanup (AS_Host *host);
-static void acornscsi_abortcmd (AS_Host *host, unsigned char tag);
+static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result);
+static int acornscsi_reconnect_finish(AS_Host *host);
+static void acornscsi_dma_cleanup(AS_Host *host);
+static void acornscsi_abortcmd(AS_Host *host, unsigned char tag);
/* ====================================================================================
* Miscellaneous
*/
static inline void
-sbic_arm_write (unsigned int io_port, int reg, int value)
+sbic_arm_write(unsigned int io_port, int reg, int value)
{
- outb_t (reg, io_port);
- outb_t (value, io_port + 4);
+ outb_t(reg, io_port);
+ outb_t(value, io_port + 4);
}
#define sbic_arm_writenext(io,val) \
- outb_t ((val), (io) + 4)
+ outb_t((val), (io) + 4)
static inline
-int sbic_arm_read (unsigned int io_port, int reg)
+int sbic_arm_read(unsigned int io_port, int reg)
{
if(reg == ASR)
return inl_t(io_port) & 255;
- outb_t (reg, io_port);
+ outb_t(reg, io_port);
return inl_t(io_port + 4) & 255;
}
#ifdef USE_DMAC
#define dmac_read(io_port,reg) \
- inb ((io_port) + (reg))
+ inb((io_port) + (reg))
#define dmac_write(io_port,reg,value) \
- ({ outb ((value), (io_port) + (reg)); })
+ ({ outb((value), (io_port) + (reg)); })
#define dmac_clearintr(io_port) \
- ({ outb (0, (io_port)); })
+ ({ outb(0, (io_port)); })
static inline
-unsigned int dmac_address (unsigned int io_port)
+unsigned int dmac_address(unsigned int io_port)
{
- return dmac_read (io_port, TXADRHI) << 16 |
- dmac_read (io_port, TXADRMD) << 8 |
- dmac_read (io_port, TXADRLO);
+ return dmac_read(io_port, TXADRHI) << 16 |
+ dmac_read(io_port, TXADRMD) << 8 |
+ dmac_read(io_port, TXADRLO);
}
static
-void acornscsi_dumpdma (AS_Host *host, char *where)
+void acornscsi_dumpdma(AS_Host *host, char *where)
{
unsigned int mode, addr, len;
- mode = dmac_read (host->dma.io_port, MODECON);
- addr = dmac_address (host->dma.io_port);
- len = dmac_read (host->dma.io_port, TXCNTHI) << 8 |
- dmac_read (host->dma.io_port, TXCNTLO);
+ mode = dmac_read(host->dma.io_port, MODECON);
+ addr = dmac_address(host->dma.io_port);
+ len = dmac_read(host->dma.io_port, TXCNTHI) << 8 |
+ dmac_read(host->dma.io_port, TXCNTLO);
- printk ("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",
+ printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",
host->host->host_no, where,
mode, addr, (len + 1) & 0xffff,
- dmac_read (host->dma.io_port, MASKREG));
+ dmac_read(host->dma.io_port, MASKREG));
- printk ("DMA @%06x, ", host->dma.start_addr);
- printk ("BH @%p +%04x, ", host->scsi.SCp.ptr,
+ printk("DMA @%06x, ", host->dma.start_addr);
+ printk("BH @%p +%04x, ", host->scsi.SCp.ptr,
host->scsi.SCp.this_residual);
- printk ("DT @+%04x ST @+%04x", host->dma.transferred,
+ printk("DT @+%04x ST @+%04x", host->dma.transferred,
host->scsi.SCp.scsi_xferred);
- printk ("\n");
+ printk("\n");
}
#endif
static
-unsigned long acornscsi_sbic_xfcount (AS_Host *host)
+unsigned long acornscsi_sbic_xfcount(AS_Host *host)
{
unsigned long length;
- length = sbic_arm_read (host->scsi.io_port, TRANSCNTH) << 16;
- length |= sbic_arm_readnext (host->scsi.io_port) << 8;
- length |= sbic_arm_readnext (host->scsi.io_port);
+ length = sbic_arm_read(host->scsi.io_port, TRANSCNTH) << 16;
+ length |= sbic_arm_readnext(host->scsi.io_port) << 8;
+ length |= sbic_arm_readnext(host->scsi.io_port);
return length;
}
-static
-int acornscsi_sbic_issuecmd (AS_Host *host, int command)
+static int
+acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg)
{
- int asr;
+ int asr;
- do {
- asr = sbic_arm_read (host->scsi.io_port, ASR);
- } while (asr & ASR_CIP);
+ do {
+ asr = sbic_arm_read(host->scsi.io_port, ASR);
+
+ if ((asr & stat_mask) == stat)
+ return 0;
+
+ udelay(1);
+ } while (--timeout);
+
+ printk("scsi%d: timeout while %s\n", host->host->host_no, msg);
+
+ return -1;
+}
- sbic_arm_write (host->scsi.io_port, CMND, command);
+static
+int acornscsi_sbic_issuecmd(AS_Host *host, int command)
+{
+ if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command"))
+ return -1;
+
+ sbic_arm_write(host->scsi.io_port, CMND, command);
return 0;
}
static void
-acornscsi_csdelay (unsigned int cs)
+acornscsi_csdelay(unsigned int cs)
{
unsigned long target_jiffies, flags;
target_jiffies = jiffies + 1 + cs * HZ / 100;
- save_flags (flags);
- sti ();
+ save_flags(flags);
+ sti();
while (time_before(jiffies, target_jiffies)) barrier();
- restore_flags (flags);
+ restore_flags(flags);
}
static
-void acornscsi_resetcard (AS_Host *host)
+void acornscsi_resetcard(AS_Host *host)
{
- unsigned int i;
+ unsigned int i, timeout;
/* assert reset line */
host->card.page_reg = 0x80;
- outb (host->card.page_reg, host->card.io_page);
+ outb(host->card.page_reg, host->card.io_page);
/* wait 3 cs. SCSI standard says 25ms. */
- acornscsi_csdelay (3);
+ acornscsi_csdelay(3);
host->card.page_reg = 0;
- outb (host->card.page_reg, host->card.io_page);
+ outb(host->card.page_reg, host->card.io_page);
/*
* Should get a reset from the card
*/
- while (!(inb (host->card.io_intr) & 8));
- sbic_arm_read (host->scsi.io_port, ASR);
- sbic_arm_read (host->scsi.io_port, SSR);
+ timeout = 1000;
+ do {
+ if (inb(host->card.io_intr) & 8)
+ break;
+ udelay(1);
+ } while (--timeout);
+
+ if (timeout == 0)
+ printk("scsi%d: timeout while resetting card\n",
+ host->host->host_no);
+
+ sbic_arm_read(host->scsi.io_port, ASR);
+ sbic_arm_read(host->scsi.io_port, SSR);
/* setup sbic - WD33C93A */
- sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
- sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET);
+ sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
+ sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET);
/*
* Command should cause a reset interrupt
*/
- while (!(inb (host->card.io_intr) & 8));
- sbic_arm_read (host->scsi.io_port, ASR);
- if (sbic_arm_read (host->scsi.io_port, SSR) != 0x01)
- printk (KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",
+ timeout = 1000;
+ do {
+ if (inb(host->card.io_intr) & 8)
+ break;
+ udelay(1);
+ } while (--timeout);
+
+ if (timeout == 0)
+ printk("scsi%d: timeout while resetting card\n",
host->host->host_no);
- sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
- sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
- sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
- sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+ sbic_arm_read(host->scsi.io_port, ASR);
+ if (sbic_arm_read(host->scsi.io_port, SSR) != 0x01)
+ printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",
+ host->host->host_no);
+
+ sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
+ sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
+ sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+ sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
host->card.page_reg = 0x40;
- outb (host->card.page_reg, host->card.io_page);
+ outb(host->card.page_reg, host->card.io_page);
/* setup dmac - uPC71071 */
dmac_write(host->dma.io_port, INIT, 0);
}
/* wait 25 cs. SCSI standard says 250ms. */
- acornscsi_csdelay (25);
+ acornscsi_csdelay(25);
}
/*=============================================================================================
};
static
-void print_scsi_status (unsigned int ssr)
+void print_scsi_status(unsigned int ssr)
{
if (acornscsi_map[ssr] != -1)
- printk ("%s:%s",
+ printk("%s:%s",
acornscsi_interrupttype[(ssr >> 4)],
acornscsi_interruptcode[acornscsi_map[ssr]]);
else
- printk ("%X:%X", ssr >> 4, ssr & 0x0f);
+ printk("%X:%X", ssr >> 4, ssr & 0x0f);
}
#endif
static
-void print_sbic_status (int asr, int ssr, int cmdphase)
+void print_sbic_status(int asr, int ssr, int cmdphase)
{
#ifdef CONFIG_ACORNSCSI_CONSTANTS
- printk ("sbic: %c%c%c%c%c%c ",
+ printk("sbic: %c%c%c%c%c%c ",
asr & ASR_INT ? 'I' : 'i',
asr & ASR_LCI ? 'L' : 'l',
asr & ASR_BSY ? 'B' : 'b',
asr & ASR_CIP ? 'C' : 'c',
asr & ASR_PE ? 'P' : 'p',
asr & ASR_DBR ? 'D' : 'd');
- printk ("scsi: ");
- print_scsi_status (ssr);
- printk (" ph %02X\n", cmdphase);
+ printk("scsi: ");
+ print_scsi_status(ssr);
+ printk(" ph %02X\n", cmdphase);
#else
- printk ("sbic: %02X scsi: %X:%X ph: %02X\n",
+ printk("sbic: %02X scsi: %X:%X ph: %02X\n",
asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);
#endif
}
-static
-void acornscsi_dumplog (AS_Host *host, int target)
+static void
+acornscsi_dumplogline(AS_Host *host, int target, int line)
{
- unsigned int prev;
- do {
- signed int statptr;
-
- printk ("%c:", target == 8 ? 'H' : ('0' + target));
- statptr = status_ptr[target] - 10;
+ unsigned long prev;
+ signed int ptr;
+
+ ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT;
+ if (ptr < 0)
+ ptr += STATUS_BUFFER_SIZE;
+
+ printk("%c: %3s:", target == 8 ? 'H' : '0' + target,
+ line == 0 ? "ph" : line == 1 ? "ssr" : "int");
+
+ prev = host->status[target][ptr].when;
+
+ for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
+ unsigned long time_diff;
+
+ if (!host->status[target][ptr].when)
+ continue;
+
+ switch (line) {
+ case 0:
+ printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ',
+ host->status[target][ptr].ph);
+ break;
+
+ case 1:
+ printk(" %02X", host->status[target][ptr].ssr);
+ break;
+
+ case 2:
+ time_diff = host->status[target][ptr].when - prev;
+ prev = host->status[target][ptr].when;
+ if (time_diff == 0)
+ printk("==^");
+ else if (time_diff >= 100)
+ printk(" ");
+ else
+ printk(" %02ld", time_diff);
+ break;
+ }
+ }
- if (statptr < 0)
- statptr += 16;
+ printk("\n");
+}
- prev = status[target][statptr].when;
+static
+void acornscsi_dumplog(AS_Host *host, int target)
+{
+ do {
+ acornscsi_dumplogline(host, target, 0);
+ acornscsi_dumplogline(host, target, 1);
+ acornscsi_dumplogline(host, target, 2);
- for (; statptr != status_ptr[target]; statptr = (statptr + 1) & 15) {
- if (status[target][statptr].when) {
-#ifdef CONFIG_ACORNSCSI_CONSTANTS
- printk ("%c%02X:S=",
- status[target][statptr].irq ? '-' : ' ',
- status[target][statptr].ph);
- print_scsi_status (status[target][statptr].ssr);
-#else
- printk ("%c%02X:%02X",
- status[target][statptr].irq ? '-' : ' ',
- status[target][statptr].ph,
- status[target][statptr].ssr);
-#endif
- printk ("+%02ld",
- (status[target][statptr].when - prev) < 100 ?
- (status[target][statptr].when - prev) : 99);
- prev = status[target][statptr].when;
- }
- }
- printk ("\n");
if (target == 8)
break;
+
target = 8;
} while (1);
}
static
-char acornscsi_target (AS_Host *host)
+char acornscsi_target(AS_Host *host)
{
if (host->SCpnt)
return '0' + host->SCpnt->target;
}
/*
- * Prototype: cmdtype_t acornscsi_cmdtype (int command)
+ * Prototype: cmdtype_t acornscsi_cmdtype(int command)
* Purpose : differentiate READ from WRITE from other commands
* Params : command - command to interpret
* Returns : CMD_READ - command reads data,
* CMD_MISC - everything else
*/
static inline
-cmdtype_t acornscsi_cmdtype (int command)
+cmdtype_t acornscsi_cmdtype(int command)
{
switch (command) {
case WRITE_6: case WRITE_10: case WRITE_12:
}
/*
- * Prototype: int acornscsi_datadirection (int command)
+ * Prototype: int acornscsi_datadirection(int command)
* Purpose : differentiate between commands that have a DATA IN phase
* and a DATA OUT phase
* Params : command - command to interpret
* DATADIR_IN - data in phase expected
*/
static
-datadir_t acornscsi_datadirection (int command)
+datadir_t acornscsi_datadirection(int command)
{
switch (command) {
case CHANGE_DEFINITION: case COMPARE: case COPY:
};
/*
- * Prototype: int acornscsi_getperiod (unsigned char syncxfer)
+ * Prototype: int acornscsi_getperiod(unsigned char syncxfer)
* Purpose : period for the synchronous transfer setting
* Params : syncxfer SYNCXFER register value
* Returns : period in ns.
*/
static
-int acornscsi_getperiod (unsigned char syncxfer)
+int acornscsi_getperiod(unsigned char syncxfer)
{
int i;
}
/*
- * Prototype: int round_period (unsigned int period)
+ * Prototype: int round_period(unsigned int period)
* Purpose : return index into above table for a required REQ period
* Params : period - time (ns) for REQ
* Returns : table index
* Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
*/
static inline
-int round_period (unsigned int period)
+int round_period(unsigned int period)
{
int i;
}
/*
- * Prototype: unsigned char calc_sync_xfer (unsigned int period, unsigned int offset)
+ * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
* Purpose : calculate value for 33c93s SYNC register
* Params : period - time (ns) for REQ
* offset - offset in bytes between REQ/ACK
* Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting
*/
static
-unsigned char calc_sync_xfer (unsigned int period, unsigned int offset)
+unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)
{
return sync_xfer_table[round_period(period)].reg_value |
((offset < SDTR_SIZE) ? offset : SDTR_SIZE);
* Command functions
*/
/*
- * Function: acornscsi_kick (AS_Host *host)
+ * Function: acornscsi_kick(AS_Host *host)
* Purpose : kick next command to interface
* Params : host - host to send command to
* Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING
* Notes : interrupts are always disabled!
*/
static
-intr_ret_t acornscsi_kick (AS_Host *host)
+intr_ret_t acornscsi_kick(AS_Host *host)
{
int from_queue = 0;
Scsi_Cmnd *SCpnt;
/* retrieve next command */
if (!SCpnt) {
- SCpnt = queue_remove_exclude (&host->queues.issue, host->busyluns);
+ SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns);
if (!SCpnt)
return INTR_IDLE;
}
if (host->scsi.disconnectable && host->SCpnt) {
- queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt);
+ queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
host->scsi.disconnectable = 0;
#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- DBG(host->SCpnt, printk ("scsi%d.%c: moved command to disconnected queue\n",
- host->host->host_no, acornscsi_target (host)));
+ DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n",
+ host->host->host_no, acornscsi_target(host)));
#endif
host->SCpnt = NULL;
}
* If we have an interrupt pending, then we may have been reselected.
* In this case, we don't want to write to the registers
*/
- if (!(sbic_arm_read (host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
- sbic_arm_write (host->scsi.io_port, DESTID, SCpnt->target);
- sbic_arm_write (host->scsi.io_port, CMND, CMND_SELWITHATN);
+ if (!(sbic_arm_read(host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {
+ sbic_arm_write(host->scsi.io_port, DESTID, SCpnt->target);
+ sbic_arm_write(host->scsi.io_port, CMND, CMND_SELWITHATN);
}
/*
host->scsi.SCp = SCpnt->SCp;
host->dma.xfer_setup = 0;
host->dma.xfer_required = 0;
+ host->dma.xfer_done = 0;
#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT))
- DBG(SCpnt,printk ("scsi%d.%c: starting cmd %02X\n",
+ DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n",
host->host->host_no, '0' + SCpnt->target,
SCpnt->cmnd[0]));
#endif
SCpnt->tag = SCpnt->device->current_tag;
} else
#endif
- set_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+ set_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns);
host->stats.removes += 1;
- switch (acornscsi_cmdtype (SCpnt->cmnd[0])) {
+ switch (acornscsi_cmdtype(SCpnt->cmnd[0])) {
case CMD_WRITE:
host->stats.writes += 1;
break;
}
/*
- * Function: void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
+ * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
* Purpose : complete processing for command
* Params : host - interface that completed
* result - driver byte of result
*/
static
-void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
+void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result)
{
Scsi_Cmnd *SCpnt = *SCpntp;
/* clean up */
- sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+ sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
host->stats.fins += 1;
if (SCpnt) {
*SCpntp = NULL;
- acornscsi_dma_cleanup (host);
+ acornscsi_dma_cleanup(host);
SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status;
* It doesn't appear to be set to something meaningful by the higher
* levels all the time.
*/
- if (host->scsi.SCp.ptr && result == DID_OK &&
- acornscsi_cmdtype (SCpnt->cmnd[0]) != CMD_MISC) {
- switch (status_byte (SCpnt->result)) {
- case CHECK_CONDITION:
- case COMMAND_TERMINATED:
- case BUSY:
- case QUEUE_FULL:
- case RESERVATION_CONFLICT:
- break;
-
- default:
- printk (KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
- host->host->host_no, SCpnt->result);
- print_command (SCpnt->cmnd);
- acornscsi_dumpdma (host, "done");
- acornscsi_dumplog (host, SCpnt->target);
- SCpnt->result &= 0xffff;
- SCpnt->result |= DID_ERROR << 16;
- }
+ if (result == DID_OK) {
+ int xfer_warn = 0;
+
+ if (SCpnt->underflow == 0) {
+ if (host->scsi.SCp.ptr &&
+ acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC)
+ xfer_warn = 1;
+ } else {
+ if (host->scsi.SCp.scsi_xferred < SCpnt->underflow ||
+ host->scsi.SCp.scsi_xferred != host->dma.transferred)
+ xfer_warn = 1;
+ }
+
+ /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6)
+ * Targets which break data transfers into multiple
+ * connections shall end each successful connection
+ * (except possibly the last) with a SAVE DATA
+ * POINTER - DISCONNECT message sequence.
+ *
+ * This makes it difficult to ensure that a transfer has
+ * completed. If we reach the end of a transfer during
+ * the command, then we can only have finished the transfer.
+ * therefore, if we seem to have some data remaining, this
+ * is not a problem.
+ */
+ if (host->dma.xfer_done)
+ xfer_warn = 0;
+
+ if (xfer_warn) {
+ switch (status_byte(SCpnt->result)) {
+ case CHECK_CONDITION:
+ case COMMAND_TERMINATED:
+ case BUSY:
+ case QUEUE_FULL:
+ case RESERVATION_CONFLICT:
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
+ host->host->host_no, SCpnt->result);
+ print_command(SCpnt->cmnd);
+ acornscsi_dumpdma(host, "done");
+ acornscsi_dumplog(host, SCpnt->target);
+ SCpnt->result &= 0xffff;
+ SCpnt->result |= DID_ERROR << 16;
+ }
+ }
}
if (!SCpnt->scsi_done)
- panic ("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
+ panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no);
- clear_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+ clear_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns);
- SCpnt->scsi_done (SCpnt);
+ SCpnt->scsi_done(SCpnt);
} else
- printk ("scsi%d: null command in acornscsi_done", host->host->host_no);
+ printk("scsi%d: null command in acornscsi_done", host->host->host_no);
host->scsi.phase = PHASE_IDLE;
}
* Notes : this will only be one SG entry or less
*/
static
-void acornscsi_data_updateptr (AS_Host *host, Scsi_Pointer *SCp, unsigned int length)
+void acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length)
{
SCp->ptr += length;
SCp->this_residual -= length;
SCp->buffers_residual--;
SCp->ptr = (char *)SCp->buffer->address;
SCp->this_residual = SCp->buffer->length;
- } else
+ } else {
SCp->ptr = NULL;
+ host->dma.xfer_done = 1;
+ }
}
}
/*
- * Prototype: void acornscsi_data_read (AS_Host *host, char *ptr,
+ * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr,
* unsigned int start_addr, unsigned int length)
* Purpose : read data from DMA RAM
* Params : host - host to transfer from
* Notes : this will only be one SG entry or less
*/
static
-void acornscsi_data_read (AS_Host *host, char *ptr,
+void acornscsi_data_read(AS_Host *host, char *ptr,
unsigned int start_addr, unsigned int length)
{
- extern void __acornscsi_in (int port, char *buf, int len);
+ extern void __acornscsi_in(int port, char *buf, int len);
unsigned int page, offset, len = length;
page = (start_addr >> 12);
offset = start_addr & ((1 << 12) - 1);
- outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+ outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
while (len > 0) {
unsigned int this_len;
else
this_len = len;
- __acornscsi_in (host->card.io_ram + (offset << 1), ptr, this_len);
+ __acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len);
offset += this_len;
ptr += this_len;
if (offset == (1 << 12)) {
offset = 0;
page ++;
- outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+ outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
}
}
- outb (host->card.page_reg, host->card.io_page);
+ outb(host->card.page_reg, host->card.io_page);
}
/*
- * Prototype: void acornscsi_data_write (AS_Host *host, char *ptr,
+ * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr,
* unsigned int start_addr, unsigned int length)
* Purpose : write data to DMA RAM
* Params : host - host to transfer from
* Notes : this will only be one SG entry or less
*/
static
-void acornscsi_data_write (AS_Host *host, char *ptr,
+void acornscsi_data_write(AS_Host *host, char *ptr,
unsigned int start_addr, unsigned int length)
{
- extern void __acornscsi_out (int port, char *buf, int len);
+ extern void __acornscsi_out(int port, char *buf, int len);
unsigned int page, offset, len = length;
page = (start_addr >> 12);
offset = start_addr & ((1 << 12) - 1);
- outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+ outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
while (len > 0) {
unsigned int this_len;
else
this_len = len;
- __acornscsi_out (host->card.io_ram + (offset << 1), ptr, this_len);
+ __acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len);
offset += this_len;
ptr += this_len;
if (offset == (1 << 12)) {
offset = 0;
page ++;
- outb ((page & 0x3f) | host->card.page_reg, host->card.io_page);
+ outb((page & 0x3f) | host->card.page_reg, host->card.io_page);
}
}
- outb (host->card.page_reg, host->card.io_page);
+ outb(host->card.page_reg, host->card.io_page);
}
/* =========================================================================================
*/
#ifdef USE_DMAC
/*
- * Prototype: void acornscsi_dmastop (AS_Host *host)
+ * Prototype: void acornscsi_dmastop(AS_Host *host)
* Purpose : stop all DMA
* Params : host - host on which to stop DMA
* Notes : This is called when leaving DATA IN/OUT phase,
* or when interface is RESET
*/
static inline
-void acornscsi_dma_stop (AS_Host *host)
+void acornscsi_dma_stop(AS_Host *host)
{
- dmac_write (host->dma.io_port, MASKREG, MASK_ON);
- dmac_clearintr (host->dma.io_intr_clear);
+ dmac_write(host->dma.io_port, MASKREG, MASK_ON);
+ dmac_clearintr(host->dma.io_intr_clear);
#if (DEBUG & DEBUG_DMA)
- DBG(host->SCpnt, acornscsi_dumpdma (host, "stop"));
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "stop"));
#endif
}
/*
- * Function: void acornscsi_dma_setup (AS_Host *host, dmadir_t direction)
+ * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
* Purpose : setup DMA controller for data transfer
* Params : host - host to setup
* direction - data transfer direction
* while we're in a DATA I/O phase
*/
static
-void acornscsi_dma_setup (AS_Host *host, dmadir_t direction)
+void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)
{
unsigned int address, length, mode;
host->dma.direction = direction;
- dmac_write (host->dma.io_port, MASKREG, MASK_ON);
+ dmac_write(host->dma.io_port, MASKREG, MASK_ON);
if (direction == DMA_OUT) {
#if (DEBUG & DEBUG_NO_WRITE)
if (NO_WRITE & (1 << host->SCpnt->target)) {
- printk (KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",
- host->host->host_no, acornscsi_target (host));
+ printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",
+ host->host->host_no, acornscsi_target(host));
return;
}
#endif
/*
* Allocate some buffer space, limited to half the buffer size
*/
- length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
+ length = min(host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
if (length) {
host->dma.start_addr = address = host->dma.free_addr;
host->dma.free_addr = (host->dma.free_addr + length) &
* Transfer data to DMA memory
*/
if (direction == DMA_OUT)
- acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr,
+ acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
length);
length -= 1;
- dmac_write (host->dma.io_port, TXCNTLO, length);
- dmac_write (host->dma.io_port, TXCNTHI, length >> 8);
- dmac_write (host->dma.io_port, TXADRLO, address);
- dmac_write (host->dma.io_port, TXADRMD, address >> 8);
- dmac_write (host->dma.io_port, TXADRHI, 0);
- dmac_write (host->dma.io_port, MODECON, mode);
- dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+ dmac_write(host->dma.io_port, TXCNTLO, length);
+ dmac_write(host->dma.io_port, TXCNTHI, length >> 8);
+ dmac_write(host->dma.io_port, TXADRLO, address);
+ dmac_write(host->dma.io_port, TXADRMD, address >> 8);
+ dmac_write(host->dma.io_port, TXADRHI, 0);
+ dmac_write(host->dma.io_port, MODECON, mode);
+ dmac_write(host->dma.io_port, MASKREG, MASK_OFF);
#if (DEBUG & DEBUG_DMA)
- DBG(host->SCpnt, acornscsi_dumpdma (host, "strt"));
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "strt"));
#endif
host->dma.xfer_setup = 1;
}
}
/*
- * Function: void acornscsi_dma_cleanup (AS_Host *host)
+ * Function: void acornscsi_dma_cleanup(AS_Host *host)
* Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct
* Params : host - host to finish
* Notes : This is called when a command is:
* : This must not return until all transfers are completed.
*/
static
-void acornscsi_dma_cleanup (AS_Host *host)
+void acornscsi_dma_cleanup(AS_Host *host)
{
- dmac_write (host->dma.io_port, MASKREG, MASK_ON);
- dmac_clearintr (host->dma.io_intr_clear);
+ dmac_write(host->dma.io_port, MASKREG, MASK_ON);
+ dmac_clearintr(host->dma.io_intr_clear);
/*
* Check for a pending transfer
if (host->dma.xfer_required) {
host->dma.xfer_required = 0;
if (host->dma.direction == DMA_IN)
- acornscsi_data_read (host, host->dma.xfer_ptr,
+ acornscsi_data_read(host, host->dma.xfer_ptr,
host->dma.xfer_start, host->dma.xfer_length);
}
/*
* Calculate number of bytes transferred from DMA.
*/
- transferred = dmac_address (host->dma.io_port) - host->dma.start_addr;
+ transferred = dmac_address(host->dma.io_port) - host->dma.start_addr;
host->dma.transferred += transferred;
if (host->dma.direction == DMA_IN)
- acornscsi_data_read (host, host->scsi.SCp.ptr,
+ acornscsi_data_read(host, host->scsi.SCp.ptr,
host->dma.start_addr, transferred);
/*
* Update SCSI pointers
*/
- acornscsi_data_updateptr (host, &host->scsi.SCp, transferred);
+ acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
#if (DEBUG & DEBUG_DMA)
DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo"));
#endif
}
/*
- * Function: void acornscsi_dmacintr (AS_Host *host)
+ * Function: void acornscsi_dmacintr(AS_Host *host)
* Purpose : handle interrupts from DMAC device
* Params : host - host to process
* Notes : If reading, we schedule the read to main memory &
* : Called whenever DMAC finished it's current transfer.
*/
static
-void acornscsi_dma_intr (AS_Host *host)
+void acornscsi_dma_intr(AS_Host *host)
{
unsigned int address, length, transferred;
#if (DEBUG & DEBUG_DMA)
- DBG(host->SCpnt, acornscsi_dumpdma (host, "inti"));
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "inti"));
#endif
- dmac_write (host->dma.io_port, MASKREG, MASK_ON);
- dmac_clearintr (host->dma.io_intr_clear);
+ dmac_write(host->dma.io_port, MASKREG, MASK_ON);
+ dmac_clearintr(host->dma.io_intr_clear);
/*
* Calculate amount transferred via DMA
*/
- transferred = dmac_address (host->dma.io_port) - host->dma.start_addr;
+ transferred = dmac_address(host->dma.io_port) - host->dma.start_addr;
host->dma.transferred += transferred;
/*
host->dma.xfer_required = 1;
}
- acornscsi_data_updateptr (host, &host->scsi.SCp, transferred);
+ acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);
/*
* Allocate some buffer space, limited to half the on-board RAM size
*/
- length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
+ length = min(host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);
if (length) {
host->dma.start_addr = address = host->dma.free_addr;
host->dma.free_addr = (host->dma.free_addr + length) &
* Transfer data to DMA memory
*/
if (host->dma.direction == DMA_OUT)
- acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr,
+ acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,
length);
length -= 1;
- dmac_write (host->dma.io_port, TXCNTLO, length);
- dmac_write (host->dma.io_port, TXCNTHI, length >> 8);
- dmac_write (host->dma.io_port, TXADRLO, address);
- dmac_write (host->dma.io_port, TXADRMD, address >> 8);
- dmac_write (host->dma.io_port, TXADRHI, 0);
- dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+ dmac_write(host->dma.io_port, TXCNTLO, length);
+ dmac_write(host->dma.io_port, TXCNTHI, length >> 8);
+ dmac_write(host->dma.io_port, TXADRLO, address);
+ dmac_write(host->dma.io_port, TXADRMD, address >> 8);
+ dmac_write(host->dma.io_port, TXADRHI, 0);
+ dmac_write(host->dma.io_port, MASKREG, MASK_OFF);
#if (DEBUG & DEBUG_DMA)
- DBG(host->SCpnt, acornscsi_dumpdma (host, "into"));
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "into"));
#endif
} else {
host->dma.xfer_setup = 0;
* attention condition. We continue giving one byte until
* the device recognises the attention.
*/
- if (dmac_read (host->dma.io_port, STATUS) & STATUS_RQ0) {
- acornscsi_abortcmd (host, host->SCpnt->tag);
-
- dmac_write (host->dma.io_port, TXCNTLO, 0);
- dmac_write (host->dma.io_port, TXCNTHI, 0);
- dmac_write (host->dma.io_port, TXADRLO, 0);
- dmac_write (host->dma.io_port, TXADRMD, 0);
- dmac_write (host->dma.io_port, TXADRHI, 0);
- dmac_write (host->dma.io_port, MASKREG, MASK_OFF);
+ if (dmac_read(host->dma.io_port, STATUS) & STATUS_RQ0) {
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+
+ dmac_write(host->dma.io_port, TXCNTLO, 0);
+ dmac_write(host->dma.io_port, TXCNTHI, 0);
+ dmac_write(host->dma.io_port, TXADRLO, 0);
+ dmac_write(host->dma.io_port, TXADRMD, 0);
+ dmac_write(host->dma.io_port, TXADRHI, 0);
+ dmac_write(host->dma.io_port, MASKREG, MASK_OFF);
}
#endif
}
}
/*
- * Function: void acornscsi_dma_xfer (AS_Host *host)
+ * Function: void acornscsi_dma_xfer(AS_Host *host)
* Purpose : transfer data between AcornSCSI and memory
* Params : host - host to process
*/
static
-void acornscsi_dma_xfer (AS_Host *host)
+void acornscsi_dma_xfer(AS_Host *host)
{
host->dma.xfer_required = 0;
if (host->dma.direction == DMA_IN)
- acornscsi_data_read (host, host->dma.xfer_ptr,
+ acornscsi_data_read(host, host->dma.xfer_ptr,
host->dma.xfer_start, host->dma.xfer_length);
}
/*
- * Function: void acornscsi_dma_adjust (AS_Host *host)
+ * Function: void acornscsi_dma_adjust(AS_Host *host)
* Purpose : adjust DMA pointers & count for bytes transfered to
* SBIC but not SCSI bus.
* Params : host - host to adjust DMA count for
*/
static
-void acornscsi_dma_adjust (AS_Host *host)
+void acornscsi_dma_adjust(AS_Host *host)
{
if (host->dma.xfer_setup) {
signed long transferred;
#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
- DBG(host->SCpnt, acornscsi_dumpdma (host, "adji"));
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "adji"));
#endif
/*
* Calculate correct DMA address - DMA is ahead of SCSI bus while
*/
transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred;
if (transferred < 0)
- printk ("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",
- host->host->host_no, acornscsi_target (host), transferred);
+ printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",
+ host->host->host_no, acornscsi_target(host), transferred);
else if (transferred == 0)
host->dma.xfer_setup = 0;
else {
transferred += host->dma.start_addr;
- dmac_write (host->dma.io_port, TXADRLO, transferred);
- dmac_write (host->dma.io_port, TXADRMD, transferred >> 8);
- dmac_write (host->dma.io_port, TXADRHI, transferred >> 16);
+ dmac_write(host->dma.io_port, TXADRLO, transferred);
+ dmac_write(host->dma.io_port, TXADRMD, transferred >> 8);
+ dmac_write(host->dma.io_port, TXADRHI, transferred >> 16);
#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))
- DBG(host->SCpnt, acornscsi_dumpdma (host, "adjo"));
+ DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo"));
#endif
}
}
/* =========================================================================================
* Data I/O
*/
+static int
+acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout)
+{
+ unsigned int asr, timeout = max_timeout;
+ int my_ptr = *ptr;
+
+ while (my_ptr < len) {
+ asr = sbic_arm_read(host->scsi.io_port, ASR);
+
+ if (asr & ASR_DBR) {
+ timeout = max_timeout;
+
+ sbic_arm_write(host->scsi.io_port, DATA, bytes[my_ptr++]);
+ } else if (asr & ASR_INT)
+ break;
+ else if (--timeout == 0)
+ break;
+ udelay(1);
+ }
+
+ *ptr = my_ptr;
+
+ return (timeout == 0) ? -1 : 0;
+}
+
/*
- * Function: void acornscsi_sendcommand (AS_Host *host)
+ * Function: void acornscsi_sendcommand(AS_Host *host)
* Purpose : send a command to a target
* Params : host - host which is connected to target
*/
-static
-void acornscsi_sendcommand (AS_Host *host)
+static void
+acornscsi_sendcommand(AS_Host *host)
{
Scsi_Cmnd *SCpnt = host->SCpnt;
- unsigned int asr;
- unsigned char *cmdptr, *cmdend;
-
- sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0);
- sbic_arm_writenext (host->scsi.io_port, 0);
- sbic_arm_writenext (host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command);
- acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
-
- cmdptr = SCpnt->cmnd + host->scsi.SCp.sent_command;
- cmdend = SCpnt->cmnd + SCpnt->cmd_len;
-
- while (cmdptr < cmdend) {
- asr = sbic_arm_read (host->scsi.io_port, ASR);
- if (asr & ASR_DBR)
- sbic_arm_write (host->scsi.io_port, DATA, *cmdptr++);
- else if (asr & ASR_INT)
- break;
- }
- if (cmdptr >= cmdend)
- host->scsi.SCp.sent_command = cmdptr - SCpnt->cmnd;
+
+ sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0);
+ sbic_arm_writenext(host->scsi.io_port, 0);
+ sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command);
+
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
+
+ if (acornscsi_write_pio(host, SCpnt->cmnd,
+ (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000))
+ printk("scsi%d: timeout while sending command\n", host->host->host_no);
+
host->scsi.phase = PHASE_COMMAND;
}
static
-void acornscsi_sendmessage (AS_Host *host)
+void acornscsi_sendmessage(AS_Host *host)
{
- unsigned int message_length = msgqueue_msglength (&host->scsi.msgs);
- int msgnr;
+ unsigned int message_length = msgqueue_msglength(&host->scsi.msgs);
+ unsigned int msgnr;
struct message *msg;
#if (DEBUG & DEBUG_MESSAGES)
- printk ("scsi%d.%c: sending message ",
- host->host->host_no, acornscsi_target (host));
+ printk("scsi%d.%c: sending message ",
+ host->host->host_no, acornscsi_target(host));
#endif
switch (message_length) {
case 0:
- acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
- while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
- sbic_arm_write (host->scsi.io_port, DATA, NOP);
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
+
+ acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1");
+
+ sbic_arm_write(host->scsi.io_port, DATA, NOP);
+
host->scsi.last_message = NOP;
#if (DEBUG & DEBUG_MESSAGES)
- printk ("NOP");
+ printk("NOP");
#endif
break;
case 1:
- acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
msg = msgqueue_getmsg(&host->scsi.msgs, 0);
- while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
- sbic_arm_write (host->scsi.io_port, DATA, msg->msg[0]);
+
+ acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2");
+
+ sbic_arm_write(host->scsi.io_port, DATA, msg->msg[0]);
+
host->scsi.last_message = msg->msg[0];
#if (DEBUG & DEBUG_MESSAGES)
print_msg(msg->msg);
* initiator. This provides an interlock so that the
* initiator can determine which message byte is rejected.
*/
- sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0);
- sbic_arm_writenext (host->scsi.io_port, 0);
- sbic_arm_writenext (host->scsi.io_port, message_length);
- acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
+ sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0);
+ sbic_arm_writenext(host->scsi.io_port, 0);
+ sbic_arm_writenext(host->scsi.io_port, message_length);
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
msgnr = 0;
while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) {
- unsigned int asr, i;
+ unsigned int i;
#if (DEBUG & DEBUG_MESSAGES)
- print_msg (msg);
+ print_msg(msg);
#endif
- for (i = 0; i < msg->length;) {
- asr = sbic_arm_read (host->scsi.io_port, ASR);
- if (asr & ASR_DBR)
- sbic_arm_write (host->scsi.io_port, DATA, msg->msg[i++]);
- if (asr & ASR_INT)
- break;
- }
+ i = 0;
+ if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000))
+ printk("scsi%d: timeout while sending message\n", host->host->host_no);
+
host->scsi.last_message = msg->msg[0];
if (msg->msg[0] == EXTENDED_MESSAGE)
host->scsi.last_message |= msg->msg[2] << 8;
- if (asr & ASR_INT)
+
+ if (i != msg->length)
break;
}
break;
}
#if (DEBUG & DEBUG_MESSAGES)
- printk ("\n");
+ printk("\n");
#endif
}
/*
- * Function: void acornscsi_readstatusbyte (AS_Host *host)
+ * Function: void acornscsi_readstatusbyte(AS_Host *host)
* Purpose : Read status byte from connected target
* Params : host - host connected to target
*/
static
-void acornscsi_readstatusbyte (AS_Host *host)
+void acornscsi_readstatusbyte(AS_Host *host)
{
- acornscsi_sbic_issuecmd (host, CMND_XFERINFO|CMND_SBT);
- while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
-
- host->scsi.SCp.Status = sbic_arm_read (host->scsi.io_port, DATA);
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT);
+ acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte");
+ host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, DATA);
}
/*
- * Function: unsigned char acornscsi_readmessagebyte (AS_Host *host)
+ * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host)
* Purpose : Read one message byte from connected target
* Params : host - host connected to target
*/
static
-unsigned char acornscsi_readmessagebyte (AS_Host *host)
+unsigned char acornscsi_readmessagebyte(AS_Host *host)
{
unsigned char message;
- acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT);
- while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0);
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);
+
+ acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte");
- message = sbic_arm_read (host->scsi.io_port, DATA);
+ message = sbic_arm_read(host->scsi.io_port, DATA);
/* wait for MSGIN-XFER-PAUSED */
- while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0);
- sbic_arm_read (host->scsi.io_port, SSR);
+ acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte");
+
+ sbic_arm_read(host->scsi.io_port, SSR);
return message;
}
/*
- * Function: void acornscsi_message (AS_Host *host)
+ * Function: void acornscsi_message(AS_Host *host)
* Purpose : Read complete message from connected target & action message
* Params : host - host connected to target
*/
static
-void acornscsi_message (AS_Host *host)
+void acornscsi_message(AS_Host *host)
{
unsigned char message[16];
unsigned int msgidx = 0, msglen = 1;
do {
- message[msgidx] = acornscsi_readmessagebyte (host);
+ message[msgidx] = acornscsi_readmessagebyte(host);
switch (msgidx) {
case 0:
}
msgidx += 1;
if (msgidx < msglen) {
- acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+ acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
/* wait for next msg-in */
- while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0);
- sbic_arm_read (host->scsi.io_port, SSR);
+ acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack");
+ sbic_arm_read(host->scsi.io_port, SSR);
}
} while (msgidx < msglen);
#if (DEBUG & DEBUG_MESSAGES)
printk("scsi%d.%c: message in: ",
- host->host->host_no, acornscsi_target (host));
+ host->host->host_no, acornscsi_target(host));
print_msg(message);
printk("\n");
#endif
*/
if (message[0] == SIMPLE_QUEUE_TAG)
host->scsi.reconnected.tag = message[1];
- if (acornscsi_reconnect_finish (host))
+ if (acornscsi_reconnect_finish(host))
host->scsi.phase = PHASE_MSGIN;
}
case COMMAND_COMPLETE:
if (host->scsi.phase != PHASE_STATUSIN) {
printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n",
- host->host->host_no, acornscsi_target (host));
+ host->host->host_no, acornscsi_target(host));
acornscsi_dumplog(host, host->SCpnt->target);
}
host->scsi.phase = PHASE_DONE;
* direct the initiator to copy the active data pointer to
* the saved data pointer for the current I/O process.
*/
- acornscsi_dma_cleanup (host);
+ acornscsi_dma_cleanup(host);
host->SCpnt->SCp = host->scsi.SCp;
host->SCpnt->SCp.sent_command = 0;
host->scsi.phase = PHASE_MSGIN;
* status pointers shall be restored to the beginning of
* the present command and status areas.'
*/
- acornscsi_dma_cleanup (host);
+ acornscsi_dma_cleanup(host);
host->scsi.SCp = host->SCpnt->SCp;
host->scsi.phase = PHASE_MSGIN;
break;
* message. When reconnection is completed, the most recent
* saved pointer values are restored.'
*/
- acornscsi_dma_cleanup (host);
+ acornscsi_dma_cleanup(host);
host->scsi.phase = PHASE_DISCONNECT;
break;
/*
* If we have any messages waiting to go out, then assert ATN now
*/
- if (msgqueue_msglength (&host->scsi.msgs))
- acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
+ if (msgqueue_msglength(&host->scsi.msgs))
+ acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
switch (host->scsi.last_message) {
#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
* message is received, it shall respond with a MESSAGE REJECT
* message and accept the I/O process as if it were untagged.
*/
- printk (KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
- host->host->host_no, acornscsi_target (host));
+ printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n",
+ host->host->host_no, acornscsi_target(host));
host->SCpnt->device->tagged_queue = 0;
- set_bit (host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns);
+ set_bit(host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns);
break;
#endif
case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8):
/*
* Target can't handle synchronous transfers
*/
- printk (KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",
- host->host->host_no, acornscsi_target (host));
+ printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",
+ host->host->host_no, acornscsi_target(host));
host->device[host->SCpnt->target].sync_xfer = SYNCHTRANSFER_2DBA;
host->device[host->SCpnt->target].sync_state = SYNC_ASYNCHRONOUS;
- sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+ sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
break;
default:
case SIMPLE_QUEUE_TAG:
/* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */
- printk ("scsi%d.%c: reconnect queue tag %02X\n",
- host->host->host_no, acornscsi_target (host),
+ printk("scsi%d.%c: reconnect queue tag %02X\n",
+ host->host->host_no, acornscsi_target(host),
message[1]);
break;
* and the target retries fail, then we fallback to asynchronous mode
*/
host->device[host->SCpnt->target].sync_state = SYNC_COMPLETED;
- printk (KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",
+ printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",
host->host->host_no, acornscsi_target(host),
message[4], message[3] * 4);
host->device[host->SCpnt->target].sync_xfer =
- calc_sync_xfer (message[3] * 4, message[4]);
+ calc_sync_xfer(message[3] * 4, message[4]);
} else {
unsigned char period, length;
/*
* Target requested synchronous transfers. The agreement is only
* to be in operation AFTER the target leaves message out phase.
*/
- acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
- period = max (message[3], sdtr_period / 4);
- length = min (message[4], sdtr_size);
- msgqueue_addmsg (&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,
+ acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+ period = max(message[3], sdtr_period / 4);
+ length = min(message[4], sdtr_size);
+ msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,
EXTENDED_SDTR, period, length);
host->device[host->SCpnt->target].sync_xfer =
- calc_sync_xfer (period * 4, length);
+ calc_sync_xfer(period * 4, length);
}
- sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+ sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
break;
#else
/* We do not accept synchronous transfers. Respond with a
* to a wide data transfer request.
*/
default:
- acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
- msgqueue_flush (&host->scsi.msgs);
- msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT);
+ acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+ msgqueue_flush(&host->scsi.msgs);
+ msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
break;
}
break;
* if there are more linked commands available.
*/
if (!host->SCpnt->next_link) {
- printk (KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n",
- instance->host_no, acornscsi_target (host), host->SCpnt->tag);
- acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
- msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
+ printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n",
+ instance->host_no, acornscsi_target(host), host->SCpnt->tag);
+ acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+ msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
} else {
Scsi_Cmnd *SCpnt = host->SCpnt;
- acornscsi_dma_cleanup (host);
+ acornscsi_dma_cleanup(host);
host->SCpnt = host->SCpnt->next_link;
host->SCpnt->tag = SCpnt->tag;
SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status;
- SCpnt->done (SCpnt);
+ SCpnt->done(SCpnt);
/* initialise host->SCpnt->SCp */
}
#endif
default: /* reject message */
- printk (KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
- host->host->host_no, acornscsi_target (host),
+ printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",
+ host->host->host_no, acornscsi_target(host),
message[0]);
- acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
- msgqueue_flush (&host->scsi.msgs);
- msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT);
+ acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);
+ msgqueue_flush(&host->scsi.msgs);
+ msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);
host->scsi.phase = PHASE_MSGIN;
break;
}
- acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+ acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
}
/*
- * Function: int acornscsi_buildmessages (AS_Host *host)
+ * Function: int acornscsi_buildmessages(AS_Host *host)
* Purpose : build the connection messages for a host
* Params : host - host to add messages to
*/
static
-void acornscsi_buildmessages (AS_Host *host)
+void acornscsi_buildmessages(AS_Host *host)
{
#if 0
/* does the device need resetting? */
if (cmd_reset) {
- msgqueue_addmsg (&host->scsi.msgs, 1, BUS_DEVICE_RESET);
+ msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET);
return;
}
#endif
- msgqueue_addmsg (&host->scsi.msgs, 1,
+ msgqueue_addmsg(&host->scsi.msgs, 1,
IDENTIFY(host->device[host->SCpnt->target].disconnect_ok,
host->SCpnt->lun));
#if 0
/* does the device need the current command aborted */
if (cmd_aborted) {
- acornscsi_abortcmd (host->SCpnt->tag);
+ acornscsi_abortcmd(host->SCpnt->tag);
return;
}
#endif
tag_type = HEAD_OF_QUEUE_TAG;
else
tag_type = SIMPLE_QUEUE_TAG;
- msgqueue_addmsg (&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
+ msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag);
}
#endif
#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
if (host->device[host->SCpnt->target].sync_state == SYNC_NEGOCIATE) {
host->device[host->SCpnt->target].sync_state = SYNC_SENT_REQUEST;
- msgqueue_addmsg (&host->scsi.msgs, 5,
+ msgqueue_addmsg(&host->scsi.msgs, 5,
EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
sdtr_period / 4, sdtr_size);
}
}
/*
- * Function: int acornscsi_starttransfer (AS_Host *host)
+ * Function: int acornscsi_starttransfer(AS_Host *host)
* Purpose : transfer data to/from connected target
* Params : host - host to which target is connected
* Returns : 0 if failure
*/
static
-int acornscsi_starttransfer (AS_Host *host)
+int acornscsi_starttransfer(AS_Host *host)
{
int residual;
if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) {
- printk (KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",
- host->host->host_no, acornscsi_target (host));
+ printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",
+ host->host->host_no, acornscsi_target(host));
return 0;
}
residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred;
- sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
- sbic_arm_writenext (host->scsi.io_port, residual >> 16);
- sbic_arm_writenext (host->scsi.io_port, residual >> 8);
- sbic_arm_writenext (host->scsi.io_port, residual);
- acornscsi_sbic_issuecmd (host, CMND_XFERINFO);
+ sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer);
+ sbic_arm_writenext(host->scsi.io_port, residual >> 16);
+ sbic_arm_writenext(host->scsi.io_port, residual >> 8);
+ sbic_arm_writenext(host->scsi.io_port, residual);
+ acornscsi_sbic_issuecmd(host, CMND_XFERINFO);
return 1;
}
* Connection & Disconnection
*/
/*
- * Function : acornscsi_reconnect (AS_Host *host)
+ * Function : acornscsi_reconnect(AS_Host *host)
* Purpose : reconnect a previously disconnected command
* Params : host - host specific data
* Remarks : SCSI spec says:
* of saved pointers upon reconnection of the I/O process'
*/
static
-int acornscsi_reconnect (AS_Host *host)
+int acornscsi_reconnect(AS_Host *host)
{
unsigned int target, lun, ok = 0;
- target = sbic_arm_read (host->scsi.io_port, SOURCEID);
+ target = sbic_arm_read(host->scsi.io_port, SOURCEID);
if (!(target & 8))
- printk (KERN_ERR "scsi%d: invalid source id after reselection "
+ printk(KERN_ERR "scsi%d: invalid source id after reselection "
"- device fault?\n",
host->host->host_no);
target &= 7;
if (host->SCpnt && !host->scsi.disconnectable) {
- printk (KERN_ERR "scsi%d.%d: reconnected while command in "
+ printk(KERN_ERR "scsi%d.%d: reconnected while command in "
"progress to target %d?\n",
host->host->host_no, target, host->SCpnt->target);
host->SCpnt = NULL;
}
- lun = sbic_arm_read (host->scsi.io_port, DATA) & 7;
+ lun = sbic_arm_read(host->scsi.io_port, DATA) & 7;
host->scsi.reconnected.target = target;
host->scsi.reconnected.lun = lun;
host->SCpnt->target == target && host->SCpnt->lun == lun)
ok = 1;
- if (!ok && queue_probetgtlun (&host->queues.disconnected, target, lun))
+ if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun))
ok = 1;
ADD_STATUS(target, 0x81, host->scsi.phase, 0);
host->scsi.phase = PHASE_RECONNECTED;
} else {
/* this doesn't seem to work */
- printk (KERN_ERR "scsi%d.%c: reselected with no command "
+ printk(KERN_ERR "scsi%d.%c: reselected with no command "
"to reconnect with\n",
host->host->host_no, '0' + target);
- acornscsi_dumplog (host, target);
- acornscsi_sbic_issuecmd (host, CMND_ASSERTATN);
- msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
- host->scsi.phase = PHASE_ABORTED;
+ acornscsi_dumplog(host, target);
+ acornscsi_abortcmd(host, 0);
+ if (host->SCpnt) {
+ queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
+ host->SCpnt = NULL;
+ }
}
- acornscsi_sbic_issuecmd (host, CMND_NEGATEACK);
+ acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);
return !ok;
}
/*
- * Function: int acornscsi_reconect_finish (AS_Host *host)
+ * Function: int acornscsi_reconect_finish(AS_Host *host)
* Purpose : finish reconnecting a command
* Params : host - host to complete
* Returns : 0 if failed
*/
static
-int acornscsi_reconnect_finish (AS_Host *host)
+int acornscsi_reconnect_finish(AS_Host *host)
{
if (host->scsi.disconnectable && host->SCpnt) {
host->scsi.disconnectable = 0;
host->SCpnt->lun == host->scsi.reconnected.lun &&
host->SCpnt->tag == host->scsi.reconnected.tag) {
#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- DBG(host->SCpnt, printk ("scsi%d.%c: reconnected",
- host->host->host_no, acornscsi_target (host)));
+ DBG(host->SCpnt, printk("scsi%d.%c: reconnected",
+ host->host->host_no, acornscsi_target(host)));
#endif
} else {
- queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt);
+ queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);
#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- DBG(host->SCpnt, printk ("scsi%d.%c: had to move command "
+ DBG(host->SCpnt, printk("scsi%d.%c: had to move command "
"to disconnected queue\n",
- host->host->host_no, acornscsi_target (host)));
+ host->host->host_no, acornscsi_target(host)));
#endif
host->SCpnt = NULL;
}
}
if (!host->SCpnt) {
- host->SCpnt = queue_remove_tgtluntag (&host->queues.disconnected,
+ host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected,
host->scsi.reconnected.target,
host->scsi.reconnected.lun,
host->scsi.reconnected.tag);
#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- DBG(host->SCpnt, printk ("scsi%d.%c: had to get command",
- host->host->host_no, acornscsi_target (host)));
+ DBG(host->SCpnt, printk("scsi%d.%c: had to get command",
+ host->host->host_no, acornscsi_target(host)));
#endif
}
- if (!host->SCpnt) {
- acornscsi_abortcmd (host, host->scsi.reconnected.tag);
- host->scsi.phase = PHASE_ABORTED;
- } else {
+ if (!host->SCpnt)
+ acornscsi_abortcmd(host, host->scsi.reconnected.tag);
+ else {
/*
* Restore data pointer from SAVED pointers.
*/
host->scsi.SCp = host->SCpnt->SCp;
#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- printk (", data pointers: [%p, %X]",
+ printk(", data pointers: [%p, %X]",
host->scsi.SCp.ptr, host->scsi.SCp.this_residual);
#endif
}
#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))
- printk ("\n");
+ printk("\n");
#endif
host->dma.transferred = host->scsi.SCp.scsi_xferred;
}
/*
- * Function: void acornscsi_disconnect_unexpected (AS_Host *host)
+ * Function: void acornscsi_disconnect_unexpected(AS_Host *host)
* Purpose : handle an unexpected disconnect
* Params : host - host on which disconnect occurred
*/
static
-void acornscsi_disconnect_unexpected (AS_Host *host)
+void acornscsi_disconnect_unexpected(AS_Host *host)
{
- printk (KERN_ERR "scsi%d.%c: unexpected disconnect\n",
- host->host->host_no, acornscsi_target (host));
+ printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n",
+ host->host->host_no, acornscsi_target(host));
#if (DEBUG & DEBUG_ABORT)
- acornscsi_dumplog (host, 8);
+ acornscsi_dumplog(host, 8);
#endif
- acornscsi_done (host, &host->SCpnt, DID_ABORT);
+ acornscsi_done(host, &host->SCpnt, DID_ERROR);
}
/*
- * Function: void acornscsi_abortcmd (AS_host *host, unsigned char tag)
+ * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag)
* Purpose : abort a currently executing command
* Params : host - host with connected command to abort
* tag - tag to abort
*/
static
-void acornscsi_abortcmd (AS_Host *host, unsigned char tag)
+void acornscsi_abortcmd(AS_Host *host, unsigned char tag)
{
- sbic_arm_write (host->scsi.io_port, CMND, CMND_ASSERTATN);
+ host->scsi.phase = PHASE_ABORTED;
+ sbic_arm_write(host->scsi.io_port, CMND, CMND_ASSERTATN);
- msgqueue_flush (&host->scsi.msgs);
+ msgqueue_flush(&host->scsi.msgs);
#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE
if (tag)
- msgqueue_addmsg (&host->scsi.msgs, 2, ABORT_TAG, tag);
+ msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag);
else
#endif
- msgqueue_addmsg (&host->scsi.msgs, 1, ABORT);
+ msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);
}
/* ==========================================================================================
* Interrupt routines.
*/
/*
- * Function: int acornscsi_sbicintr (AS_Host *host)
+ * Function: int acornscsi_sbicintr(AS_Host *host)
* Purpose : handle interrupts from SCSI device
* Params : host - host to process
* Returns : INTR_PROCESS if expecting another SBIC interrupt
* INTR_NEXT_COMMAND if we have finished processing the command
*/
static
-intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq)
+intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)
{
unsigned int asr, ssr;
- asr = sbic_arm_read (host->scsi.io_port, ASR);
+ asr = sbic_arm_read(host->scsi.io_port, ASR);
if (!(asr & ASR_INT))
return INTR_IDLE;
- ssr = sbic_arm_read (host->scsi.io_port, SSR);
+ ssr = sbic_arm_read(host->scsi.io_port, SSR);
#if (DEBUG & DEBUG_PHASES)
print_sbic_status(asr, ssr, host->scsi.phase);
switch (ssr) {
case 0x00: /* reset state - not advanced */
- printk (KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
+ printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",
host->host->host_no);
/* setup sbic - WD33C93A */
- sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
- sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET);
+ sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id);
+ sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET);
return INTR_IDLE;
case 0x01: /* reset state - advanced */
- sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
- sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
- sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
- sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
- msgqueue_flush (&host->scsi.msgs);
+ sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI);
+ sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME);
+ sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA);
+ sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP);
+ msgqueue_flush(&host->scsi.msgs);
return INTR_IDLE;
case 0x41: /* unexpected disconnect aborted command */
- acornscsi_disconnect_unexpected (host);
+ acornscsi_disconnect_unexpected(host);
return INTR_NEXT_COMMAND;
}
case 0x11: /* -> PHASE_CONNECTED */
/* BUS FREE -> SELECTION */
host->scsi.phase = PHASE_CONNECTED;
- msgqueue_flush (&host->scsi.msgs);
+ msgqueue_flush(&host->scsi.msgs);
host->dma.transferred = host->scsi.SCp.scsi_xferred;
/* 33C93 gives next interrupt indicating bus phase */
- asr = sbic_arm_read (host->scsi.io_port, ASR);
+ asr = sbic_arm_read(host->scsi.io_port, ASR);
if (!(asr & ASR_INT))
break;
- ssr = sbic_arm_read (host->scsi.io_port, SSR);
+ ssr = sbic_arm_read(host->scsi.io_port, SSR);
ADD_STATUS(8, ssr, host->scsi.phase, 1);
ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, 1);
goto connected;
case 0x42: /* select timed out */
/* -> PHASE_IDLE */
- acornscsi_done (host, &host->SCpnt, DID_NO_CONNECT);
+ acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT);
return INTR_NEXT_COMMAND;
case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */
/* BUS FREE -> RESELECTION */
host->origSCpnt = host->SCpnt;
host->SCpnt = NULL;
- msgqueue_flush (&host->scsi.msgs);
- acornscsi_reconnect (host);
+ msgqueue_flush(&host->scsi.msgs);
+ acornscsi_reconnect(host);
break;
default:
- printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
- acornscsi_abortcmd (host, host->SCpnt->tag);
+ printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
+ acornscsi_abortcmd(host, host->SCpnt->tag);
}
return INTR_PROCESSING;
#ifdef NONSTANDARD
case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
/* SELECTION -> COMMAND */
- acornscsi_sendcommand (host);
+ acornscsi_sendcommand(host);
break;
case 0x8b: /* -> PHASE_STATUS */
/* SELECTION -> STATUS */
- acornscsi_readstatusbyte (host);
+ acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
#endif
case 0x8e: /* -> PHASE_MSGOUT */
/* SELECTION ->MESSAGE OUT */
host->scsi.phase = PHASE_MSGOUT;
- acornscsi_buildmessages (host);
- acornscsi_sendmessage (host);
+ acornscsi_buildmessages(host);
+ acornscsi_sendmessage(host);
break;
/* these should not happen */
case 0x85: /* target disconnected */
- acornscsi_done (host, &host->SCpnt, DID_ERROR);
+ acornscsi_done(host, &host->SCpnt, DID_ERROR);
break;
default:
- printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
- acornscsi_abortcmd (host, host->SCpnt->tag);
+ printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
+ acornscsi_abortcmd(host, host->SCpnt->tag);
}
return INTR_PROCESSING;
case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */
/*
- * SCSI standard says th at a MESSAGE OUT phases can be followed by a DATA phase
+ * SCSI standard says that MESSAGE OUT phases can be followed by a
+ * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase
*/
switch (ssr) {
- case 0x8a:
+ case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
/* MESSAGE OUT -> COMMAND */
- acornscsi_sendcommand (host);
+ acornscsi_sendcommand(host);
break;
+ case 0x8b: /* -> PHASE_STATUS */
case 0x1b: /* -> PHASE_STATUS */
/* MESSAGE OUT -> STATUS */
- acornscsi_readstatusbyte (host);
+ acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
case 0x8e: /* -> PHASE_MSGOUT */
/* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */
- acornscsi_sendmessage (host);
+ acornscsi_sendmessage(host);
break;
- case 0x4f:
+ case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
/* MESSAGE OUT -> MESSAGE IN */
- acornscsi_message (host);
+ acornscsi_message(host);
break;
default:
- printk (KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
case 0x18: /* -> PHASE_DATAOUT */
/* COMMAND -> DATA OUT */
if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
- acornscsi_abortcmd (host, host->SCpnt->tag);
- acornscsi_dma_setup (host, DMA_OUT);
- if (!acornscsi_starttransfer (host))
- acornscsi_abortcmd (host, host->SCpnt->tag);
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_dma_setup(host, DMA_OUT);
+ if (!acornscsi_starttransfer(host))
+ acornscsi_abortcmd(host, host->SCpnt->tag);
host->scsi.phase = PHASE_DATAOUT;
return INTR_IDLE;
case 0x19: /* -> PHASE_DATAIN */
/* COMMAND -> DATA IN */
if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)
- acornscsi_abortcmd (host, host->SCpnt->tag);
- acornscsi_dma_setup (host, DMA_IN);
- if (!acornscsi_starttransfer (host))
- acornscsi_abortcmd (host, host->SCpnt->tag);
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ acornscsi_dma_setup(host, DMA_IN);
+ if (!acornscsi_starttransfer(host))
+ acornscsi_abortcmd(host, host->SCpnt->tag);
host->scsi.phase = PHASE_DATAIN;
return INTR_IDLE;
case 0x1b: /* -> PHASE_STATUS */
/* COMMAND -> STATUS */
- acornscsi_readstatusbyte (host);
+ acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
case 0x1e: /* -> PHASE_MSGOUT */
/* COMMAND -> MESSAGE OUT */
- acornscsi_sendmessage (host);
+ acornscsi_sendmessage(host);
break;
case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */
/* COMMAND -> MESSAGE IN */
- acornscsi_message (host);
+ acornscsi_message(host);
break;
default:
- printk (KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
host->scsi.phase = PHASE_IDLE;
host->stats.disconnects += 1;
} else {
- printk (KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_NEXT_COMMAND;
case PHASE_IDLE: /* STATE: disconnected */
if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */
- acornscsi_reconnect (host);
+ acornscsi_reconnect(host);
else {
- printk (KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
* If we reconnected and we're not in MESSAGE IN phase after IDENTIFY,
* reconnect I_T_L command
*/
- if (ssr != 0x8f && !acornscsi_reconnect_finish (host))
+ if (ssr != 0x8f && !acornscsi_reconnect_finish(host))
return INTR_IDLE;
ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, in_irq);
switch (ssr) {
case 0x88: /* data out phase */
/* -> PHASE_DATAOUT */
/* MESSAGE IN -> DATA OUT */
- acornscsi_dma_setup (host, DMA_OUT);
- if (!acornscsi_starttransfer (host))
- acornscsi_abortcmd (host, host->SCpnt->tag);
+ acornscsi_dma_setup(host, DMA_OUT);
+ if (!acornscsi_starttransfer(host))
+ acornscsi_abortcmd(host, host->SCpnt->tag);
host->scsi.phase = PHASE_DATAOUT;
return INTR_IDLE;
case 0x89: /* data in phase */
/* -> PHASE_DATAIN */
/* MESSAGE IN -> DATA IN */
- acornscsi_dma_setup (host, DMA_IN);
- if (!acornscsi_starttransfer (host))
- acornscsi_abortcmd (host, host->SCpnt->tag);
+ acornscsi_dma_setup(host, DMA_IN);
+ if (!acornscsi_starttransfer(host))
+ acornscsi_abortcmd(host, host->SCpnt->tag);
host->scsi.phase = PHASE_DATAIN;
return INTR_IDLE;
case 0x8a: /* command out */
/* MESSAGE IN -> COMMAND */
- acornscsi_sendcommand (host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
+ acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */
break;
case 0x8b: /* status in */
/* -> PHASE_STATUSIN */
/* MESSAGE IN -> STATUS */
- acornscsi_readstatusbyte (host);
+ acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
case 0x8e: /* message out */
/* -> PHASE_MSGOUT */
/* MESSAGE IN -> MESSAGE OUT */
- acornscsi_sendmessage (host);
+ acornscsi_sendmessage(host);
break;
case 0x8f: /* message in */
- acornscsi_message (host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
+ acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
break;
default:
- printk (KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
*/
switch (ssr) {
case 0x19: /* -> PHASE_DATAIN */
- acornscsi_abortcmd (host, host->SCpnt->tag);
+ case 0x89: /* -> PHASE_DATAIN */
+ acornscsi_abortcmd(host, host->SCpnt->tag);
return INTR_IDLE;
- case 0x4b: /* -> PHASE_STATUSIN */
case 0x1b: /* -> PHASE_STATUSIN */
+ case 0x4b: /* -> PHASE_STATUSIN */
+ case 0x8b: /* -> PHASE_STATUSIN */
/* DATA IN -> STATUS */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount (host);
- acornscsi_dma_stop (host);
- acornscsi_readstatusbyte (host);
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
case 0x1e: /* -> PHASE_MSGOUT */
case 0x4e: /* -> PHASE_MSGOUT */
+ case 0x8e: /* -> PHASE_MSGOUT */
/* DATA IN -> MESSAGE OUT */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount (host);
- acornscsi_dma_stop (host);
- acornscsi_sendmessage (host);
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_sendmessage(host);
break;
case 0x1f: /* message in */
case 0x4f: /* message in */
+ case 0x8f: /* message in */
/* DATA IN -> MESSAGE IN */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount (host);
- acornscsi_dma_stop (host);
- acornscsi_message (host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
break;
default:
- printk (KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
*/
switch (ssr) {
case 0x18: /* -> PHASE_DATAOUT */
- acornscsi_abortcmd (host, host->SCpnt->tag);
+ case 0x88: /* -> PHASE_DATAOUT */
+ acornscsi_abortcmd(host, host->SCpnt->tag);
return INTR_IDLE;
- case 0x4b: /* -> PHASE_STATUSIN */
case 0x1b: /* -> PHASE_STATUSIN */
+ case 0x4b: /* -> PHASE_STATUSIN */
+ case 0x8b: /* -> PHASE_STATUSIN */
/* DATA OUT -> STATUS */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount (host);
- acornscsi_dma_stop (host);
- acornscsi_dma_adjust (host);
- acornscsi_readstatusbyte (host);
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_dma_adjust(host);
+ acornscsi_readstatusbyte(host);
host->scsi.phase = PHASE_STATUSIN;
break;
case 0x1e: /* -> PHASE_MSGOUT */
case 0x4e: /* -> PHASE_MSGOUT */
+ case 0x8e: /* -> PHASE_MSGOUT */
/* DATA OUT -> MESSAGE OUT */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount (host);
- acornscsi_dma_stop (host);
- acornscsi_dma_adjust (host);
- acornscsi_sendmessage (host);
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_dma_adjust(host);
+ acornscsi_sendmessage(host);
break;
case 0x1f: /* message in */
case 0x4f: /* message in */
+ case 0x8f: /* message in */
/* DATA OUT -> MESSAGE IN */
host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen -
- acornscsi_sbic_xfcount (host);
- acornscsi_dma_stop (host);
- acornscsi_dma_adjust (host);
- acornscsi_message (host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
+ acornscsi_sbic_xfcount(host);
+ acornscsi_dma_stop(host);
+ acornscsi_dma_adjust(host);
+ acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */
break;
default:
- printk (KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
case PHASE_STATUSIN: /* STATE: status in complete */
- if (ssr == 0x1f) /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+ switch (ssr) {
+ case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
+ case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
/* STATUS -> MESSAGE IN */
- acornscsi_message (host);
- else if (ssr == 0x1e) /* -> PHASE_MSGOUT */
+ acornscsi_message(host);
+ break;
+
+ case 0x1e: /* -> PHASE_MSGOUT */
+ case 0x8e: /* -> PHASE_MSGOUT */
/* STATUS -> MESSAGE OUT */
- acornscsi_sendmessage (host);
- else {
- printk (KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ acornscsi_sendmessage(host);
+ break;
+
+ default:
+ printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
switch (ssr) {
case 0x1e: /* -> PHASE_MSGOUT */
case 0x4e: /* -> PHASE_MSGOUT */
+ case 0x8e: /* -> PHASE_MSGOUT */
/* MESSAGE IN -> MESSAGE OUT */
- acornscsi_sendmessage (host);
+ acornscsi_sendmessage(host);
break;
case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */
case 0x2f:
case 0x4f:
case 0x8f:
- acornscsi_message (host);
+ acornscsi_message(host);
+ break;
+
+ case 0x85:
+ printk("scsi%d.%c: strange message in disconnection\n",
+ host->host->host_no, acornscsi_target(host));
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
+ acornscsi_done(host, &host->SCpnt, DID_ERROR);
break;
default:
- printk (KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
case PHASE_DONE: /* STATE: received status & message */
switch (ssr) {
case 0x85: /* -> PHASE_IDLE */
- acornscsi_done (host, &host->SCpnt, DID_OK);
+ acornscsi_done(host, &host->SCpnt, DID_OK);
return INTR_NEXT_COMMAND;
+ case 0x1e:
case 0x8e:
- acornscsi_sendmessage (host);
+ acornscsi_sendmessage(host);
break;
default:
- printk (KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
case PHASE_ABORTED:
switch (ssr) {
case 0x85:
- acornscsi_done (host, &host->SCpnt, DID_ABORT);
+ if (host->SCpnt)
+ acornscsi_done(host, &host->SCpnt, DID_ABORT);
+ else {
+ clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun,
+ host->busyluns);
+ host->scsi.phase = PHASE_IDLE;
+ }
return INTR_NEXT_COMMAND;
case 0x1e:
case 0x2e:
case 0x4e:
case 0x8e:
- acornscsi_sendmessage (host);
+ acornscsi_sendmessage(host);
break;
default:
- printk (KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
default:
- printk (KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
- host->host->host_no, acornscsi_target (host), ssr);
- acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8);
+ printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n",
+ host->host->host_no, acornscsi_target(host), ssr);
+ acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8);
}
return INTR_PROCESSING;
}
/*
- * Prototype: void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs)
+ * Prototype: void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
* Purpose : handle interrupts from Acorn SCSI card
* Params : irq - interrupt number
* dev_id - device specific data (AS_Host structure)
* regs - processor registers when interrupt occurred
*/
static
-void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs)
+void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs)
{
AS_Host *host = (AS_Host *)dev_id;
intr_ret_t ret;
int in_irq = 0;
if (host->scsi.interrupt)
- printk ("scsi%d: interrupt re-entered\n", host->host->host_no);
+ printk("scsi%d: interrupt re-entered\n", host->host->host_no);
host->scsi.interrupt = 1;
do {
ret = INTR_IDLE;
- iostatus = inb (host->card.io_intr);
+ iostatus = inb(host->card.io_intr);
if (iostatus & 2) {
- acornscsi_dma_intr (host);
- iostatus = inb (host->card.io_intr);
+ acornscsi_dma_intr(host);
+ iostatus = inb(host->card.io_intr);
}
if (iostatus & 8)
- ret = acornscsi_sbicintr (host, in_irq);
+ ret = acornscsi_sbicintr(host, in_irq);
/*
* If we have a transfer pending, start it.
* it's data
*/
if (host->dma.xfer_required)
- acornscsi_dma_xfer (host);
+ acornscsi_dma_xfer(host);
if (ret == INTR_NEXT_COMMAND)
- ret = acornscsi_kick (host);
+ ret = acornscsi_kick(host);
in_irq = 1;
} while (ret != INTR_IDLE);
*/
/*
- * Function : acornscsi_queuecmd (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+ * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
* Purpose : queues a SCSI command
* Params : cmd - SCSI command
* done - function called on completion, with pointer to command descriptor
* Returns : 0, or < 0 on error.
*/
-int acornscsi_queuecmd (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
AS_Host *host = (AS_Host *)SCpnt->host->hostdata;
if (!done) {
/* there should be some way of rejecting errors like this without panicing... */
- panic ("scsi%d: queuecommand called with NULL done function [cmd=%p]",
+ panic("scsi%d: queuecommand called with NULL done function [cmd=%p]",
SCpnt->host->host_no, SCpnt);
return -EINVAL;
}
#if (DEBUG & DEBUG_NO_WRITE)
- if (acornscsi_cmdtype (SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) {
- printk (KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
+ if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) {
+ printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",
SCpnt->host->host_no, '0' + SCpnt->target);
SCpnt->result = DID_NO_CONNECT << 16;
- done (SCpnt);
+ done(SCpnt);
return 0;
}
#endif
SCpnt->host_scribble = NULL;
SCpnt->result = 0;
SCpnt->tag = 0;
- SCpnt->SCp.phase = (int)acornscsi_datadirection (SCpnt->cmnd[0]);
+ SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]);
SCpnt->SCp.sent_command = 0;
SCpnt->SCp.scsi_xferred = 0;
SCpnt->SCp.Status = 0;
{
unsigned long flags;
- if (!queue_add_cmd_ordered (&host->queues.issue, SCpnt)) {
+ if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) {
SCpnt->result = DID_ERROR << 16;
- done (SCpnt);
+ done(SCpnt);
return 0;
}
- save_flags_cli (flags);
+ save_flags_cli(flags);
if (host->scsi.phase == PHASE_IDLE)
- acornscsi_kick (host);
- restore_flags (flags);
+ acornscsi_kick(host);
+ restore_flags(flags);
}
return 0;
}
/*
- * Prototype: void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
+ * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
* Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2
* Params : SCpntp1 - pointer to command to return
* SCpntp2 - pointer to command to check
* Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2.
*/
static inline
-void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
+void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result)
{
Scsi_Cmnd *SCpnt = *SCpntp1;
*SCpntp1 = NULL;
SCpnt->result = result;
- SCpnt->scsi_done (SCpnt);
+ SCpnt->scsi_done(SCpnt);
}
if (SCpnt == *SCpntp2)
*SCpntp2 = NULL;
}
+enum res_abort { res_not_running, res_success, res_success_clear, res_snooze };
+
/*
- * Prototype: int acornscsi_abort (Scsi_Cmnd *SCpnt)
+ * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt)
* Purpose : abort a command on this host
* Params : SCpnt - command to abort
- * Returns : one of SCSI_ABORT_ macros
+ * Returns : our abort status
*/
-int acornscsi_abort (Scsi_Cmnd *SCpnt)
+static enum res_abort
+acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt)
{
- AS_Host *host = (AS_Host *) SCpnt->host->hostdata;
- int result = SCSI_ABORT_NOT_RUNNING;
+ enum res_abort res = res_not_running;
- host->stats.aborts += 1;
+ if (queue_removecmd(&host->queues.issue, SCpnt)) {
+ /*
+ * The command was on the issue queue, and has not been
+ * issued yet. We can remove the command from the queue,
+ * and acknowledge the abort. Neither the devices nor the
+ * interface know about the command.
+ */
+//#if (DEBUG & DEBUG_ABORT)
+ printk("on issue queue ");
+//#endif
+ res = res_success;
+ } else if (queue_removecmd(&host->queues.disconnected, SCpnt)) {
+ /*
+ * The command was on the disconnected queue. Simply
+ * acknowledge the abort condition, and when the target
+ * reconnects, we will give it an ABORT message. The
+ * target should then disconnect, and we will clear
+ * the busylun bit.
+ */
+//#if (DEBUG & DEBUG_ABORT)
+ printk("on disconnected queue ");
+//#endif
+ res = res_success;
+ } else if (host->SCpnt == SCpnt) {
+ unsigned long flags;
+
+//#if (DEBUG & DEBUG_ABORT)
+ printk("executing ");
+//#endif
+
+ save_flags(flags);
+ cli();
+ switch (host->scsi.phase) {
+ /*
+ * If the interface is idle, and the command is 'disconnectable',
+ * then it is the same as on the disconnected queue. We simply
+ * remove all traces of the command. When the target reconnects,
+ * we will give it an ABORT message since the command could not
+ * be found. When the target finally disconnects, we will clear
+ * the busylun bit.
+ */
+ case PHASE_IDLE:
+ if (host->scsi.disconnectable) {
+ host->scsi.disconnectable = 0;
+ host->SCpnt = NULL;
+ res = res_success;
+ }
+ break;
-#if (DEBUG & DEBUG_ABORT)
- {
- int asr, ssr;
- asr = sbic_arm_read (host->scsi.io_port, ASR);
- ssr = sbic_arm_read (host->scsi.io_port, SSR);
+ /*
+ * If the command has connected and done nothing further,
+ * simply force a disconnect. We also need to clear the
+ * busylun bit.
+ */
+ case PHASE_CONNECTED:
+ sbic_arm_write(host->scsi.io_port, CMND, CMND_DISCONNECT);
+ host->SCpnt = NULL;
+ res = res_success_clear;
+ break;
+
+ default:
+ acornscsi_abortcmd(host, host->SCpnt->tag);
+ res = res_snooze;
+ }
+ restore_flags(flags);
+ } else if (host->origSCpnt == SCpnt) {
+ /*
+ * The command will be executed next, but a command
+ * is currently using the interface. This is similar to
+ * being on the issue queue, except the busylun bit has
+ * been set.
+ */
+ host->origSCpnt = NULL;
+//#if (DEBUG & DEBUG_ABORT)
+ printk("waiting for execution ");
+//#endif
+ res = res_success_clear;
+ } else
+ printk("unknown ");
- printk (KERN_WARNING "acornscsi_abort: ");
- print_sbic_status(asr, ssr, host->scsi.phase);
- acornscsi_dumplog (host, SCpnt->target);
- }
-#endif
+ return res;
+}
+
+/*
+ * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt)
+ * Purpose : abort a command on this host
+ * Params : SCpnt - command to abort
+ * Returns : one of SCSI_ABORT_ macros
+ */
+int acornscsi_abort(Scsi_Cmnd *SCpnt)
+{
+ AS_Host *host = (AS_Host *) SCpnt->host->hostdata;
+ int result;
+
+ host->stats.aborts += 1;
- if (queue_removecmd (&host->queues.issue, SCpnt)) {
- SCpnt->result = DID_ABORT << 16;
- SCpnt->scsi_done (SCpnt);
-#if (DEBUG & DEBUG_ABORT)
- printk ("scsi%d: command on issue queue\n", host->host->host_no);
-#endif
- result = SCSI_ABORT_SUCCESS;
- } else if (queue_cmdonqueue (&host->queues.disconnected, SCpnt)) {
- printk ("scsi%d: command on disconnected queue\n", host->host->host_no);
- result = SCSI_ABORT_SNOOZE;
- } else if (host->SCpnt == SCpnt) {
- acornscsi_abortcmd (host, host->SCpnt->tag);
- printk ("scsi%d: command executing\n", host->host->host_no);
- result = SCSI_ABORT_SNOOZE;
- } else if (host->origSCpnt == SCpnt) {
- host->origSCpnt = NULL;
- SCpnt->result = DID_ABORT << 16;
- SCpnt->scsi_done (SCpnt);
#if (DEBUG & DEBUG_ABORT)
- printk ("scsi%d: command waiting for execution\n", host->host->host_no);
+ {
+ int asr, ssr;
+ asr = sbic_arm_read(host->scsi.io_port, ASR);
+ ssr = sbic_arm_read(host->scsi.io_port, SSR);
+
+ printk(KERN_WARNING "acornscsi_abort: ");
+ print_sbic_status(asr, ssr, host->scsi.phase);
+ acornscsi_dumplog(host, SCpnt->target);
+ }
#endif
- result = SCSI_ABORT_SUCCESS;
- }
- if (result == SCSI_ABORT_NOT_RUNNING) {
- printk ("scsi%d: abort(): command not running\n", host->host->host_no);
- acornscsi_dumplog (host, SCpnt->target);
+ printk("scsi%d: ", host->host->host_no);
+
+ switch (acornscsi_do_abort(host, SCpnt)) {
+ /*
+ * We managed to find the command and cleared it out.
+ * We do not expect the command to be executing on the
+ * target, but we have set the busylun bit.
+ */
+ case res_success_clear:
+//#if (DEBUG & DEBUG_ABORT)
+ printk("clear ");
+//#endif
+ clear_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns);
+
+ /*
+ * We found the command, and cleared it out. Either
+ * the command is still known to be executing on the
+ * target, or the busylun bit is not set.
+ */
+ case res_success:
+//#if (DEBUG & DEBUG_ABORT)
+ printk("success\n");
+//#endif
+ SCpnt->result = DID_ABORT << 16;
+ SCpnt->scsi_done(SCpnt);
+ result = SCSI_ABORT_SUCCESS;
+ break;
+
+ /*
+ * We did find the command, but unfortunately we couldn't
+ * unhook it from ourselves. Wait some more, and if it
+ * still doesn't complete, reset the interface.
+ */
+ case res_snooze:
+//#if (DEBUG & DEBUG_ABORT)
+ printk("snooze\n");
+//#endif
+ result = SCSI_ABORT_SNOOZE;
+ break;
+
+ /*
+ * The command could not be found (either because it completed,
+ * or it got dropped.
+ */
+ default:
+ case res_not_running:
+ acornscsi_dumplog(host, SCpnt->target);
#if (DEBUG & DEBUG_ABORT)
- result = SCSI_ABORT_SNOOZE;
+ result = SCSI_ABORT_SNOOZE;
+#else
+ result = SCSI_ABORT_NOT_RUNNING;
#endif
- }
- return result;
+//#if (DEBUG & DEBUG_ABORT)
+ printk("not running\n");
+//#endif
+ break;
+ }
+
+ return result;
}
/*
- * Prototype: int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+ * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
* Purpose : reset a command on this host/reset this host
* Params : SCpnt - command causing reset
* result - what type of reset to perform
* Returns : one of SCSI_RESET_ macros
*/
-int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
{
AS_Host *host = (AS_Host *)SCpnt->host->hostdata;
Scsi_Cmnd *SCptr;
{
int asr, ssr;
- asr = sbic_arm_read (host->scsi.io_port, ASR);
- ssr = sbic_arm_read (host->scsi.io_port, SSR);
+ asr = sbic_arm_read(host->scsi.io_port, ASR);
+ ssr = sbic_arm_read(host->scsi.io_port, SSR);
- printk (KERN_WARNING "acornscsi_reset: ");
+ printk(KERN_WARNING "acornscsi_reset: ");
print_sbic_status(asr, ssr, host->scsi.phase);
- acornscsi_dumplog (host, SCpnt->target);
+ acornscsi_dumplog(host, SCpnt->target);
}
#endif
- acornscsi_dma_stop (host);
+ acornscsi_dma_stop(host);
SCptr = host->SCpnt;
* do hard reset. This resets all devices on this host, and so we
* must set the reset status on all commands.
*/
- acornscsi_resetcard (host);
+ acornscsi_resetcard(host);
/*
* report reset on commands current connected/disconnected
*/
- acornscsi_reportstatus (&host->SCpnt, &SCptr, DID_RESET);
+ acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET);
- while ((SCptr = queue_remove (&host->queues.disconnected)) != NULL)
- acornscsi_reportstatus (&SCptr, &SCpnt, DID_RESET);
+ while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL)
+ acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET);
if (SCpnt) {
SCpnt->result = DID_RESET << 16;
- SCpnt->scsi_done (SCpnt);
+ SCpnt->scsi_done(SCpnt);
}
return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS;
static struct expansion_card *ecs[MAX_ECARDS];
/*
- * Prototype: void acornscsi_init (AS_Host *host)
+ * Prototype: void acornscsi_init(AS_Host *host)
* Purpose : initialise the AS_Host structure for one interface & setup hardware
* Params : host - host to setup
*/
static
-void acornscsi_init (AS_Host *host)
+void acornscsi_init(AS_Host *host)
{
- memset (&host->stats, 0, sizeof (host->stats));
- queue_initialise (&host->queues.issue);
- queue_initialise (&host->queues.disconnected);
- msgqueue_initialise (&host->scsi.msgs);
+ memset(&host->stats, 0, sizeof (host->stats));
+ queue_initialise(&host->queues.issue);
+ queue_initialise(&host->queues.disconnected);
+ msgqueue_initialise(&host->scsi.msgs);
- acornscsi_resetcard (host);
+ acornscsi_resetcard(host);
}
int acornscsi_detect(Scsi_Host_Template * tpnt)
for (i = 0; i < MAX_ECARDS; i++)
ecs[i] = NULL;
- ecard_startfind ();
+ ecard_startfind();
while(1) {
ecs[count] = ecard_find(0, acornscsi_cids);
break;
if (ecs[count]->irq == 0xff) {
- printk ("scsi: WD33C93 does not have IRQ enabled - ignoring\n");
+ printk("scsi: WD33C93 does not have IRQ enabled - ignoring\n");
continue;
}
ecard_claim(ecs[count]); /* Must claim here - card produces irq on reset */
- instance = scsi_register (tpnt, sizeof(AS_Host));
+ instance = scsi_register(tpnt, sizeof(AS_Host));
host = (AS_Host *)instance->hostdata;
- instance->io_port = ecard_address (ecs[count], ECARD_MEMC, 0);
+ instance->io_port = ecard_address(ecs[count], ECARD_MEMC, 0);
instance->irq = ecs[count]->irq;
host->host = instance;
- host->scsi.io_port = ioaddr (instance->io_port + 0x800);
+ host->scsi.io_port = ioaddr(instance->io_port + 0x800);
host->scsi.irq = instance->irq;
host->card.io_intr = POD_SPACE(instance->io_port) + 0x800;
host->card.io_page = POD_SPACE(instance->io_port) + 0xc00;
- host->card.io_ram = ioaddr (instance->io_port);
+ host->card.io_ram = ioaddr(instance->io_port);
host->dma.io_port = instance->io_port + 0xc00;
host->dma.io_intr_clear = POD_SPACE(instance->io_port) + 0x800;
ecs[count]->irqaddr = (char *)ioaddr(host->card.io_intr);
ecs[count]->irqmask = 0x0a;
- request_region (instance->io_port + 0x800, 2, "acornscsi(sbic)");
- request_region (host->card.io_intr, 1, "acornscsi(intr)");
- request_region (host->card.io_page, 1, "acornscsi(page)");
+ request_region(instance->io_port + 0x800, 2, "acornscsi(sbic)");
+ request_region(host->card.io_intr, 1, "acornscsi(intr)");
+ request_region(host->card.io_page, 1, "acornscsi(page)");
#ifdef USE_DMAC
- request_region (host->dma.io_port, 256, "acornscsi(dmac)");
+ request_region(host->dma.io_port, 256, "acornscsi(dmac)");
#endif
- request_region (instance->io_port, 2048, "acornscsi(ram)");
+ request_region(instance->io_port, 2048, "acornscsi(ram)");
if (request_irq(host->scsi.irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", host)) {
printk(KERN_CRIT "scsi%d: IRQ%d not free, interrupts disabled\n",
host->scsi.irq = NO_IRQ;
}
- acornscsi_init (host);
+ acornscsi_init(host);
++count;
}
}
/*
- * Function: int acornscsi_release (struct Scsi_Host *host)
+ * Function: int acornscsi_release(struct Scsi_Host *host)
* Purpose : release all resources used by this adapter
* Params : host - driver structure to release
* Returns : nothing of any consequence
*/
-int acornscsi_release (struct Scsi_Host *instance)
+int acornscsi_release(struct Scsi_Host *instance)
{
AS_Host *host = (AS_Host *)instance->hostdata;
int i;
/*
* Put card into RESET state
*/
- outb (0x80, host->card.io_page);
+ outb(0x80, host->card.io_page);
if (host->scsi.irq != NO_IRQ)
- free_irq (host->scsi.irq, host);
+ free_irq(host->scsi.irq, host);
- release_region (instance->io_port + 0x800, 2);
- release_region (host->card.io_intr, 1);
- release_region (host->card.io_page, 1);
- release_region (host->dma.io_port, 256);
- release_region (instance->io_port, 2048);
+ release_region(instance->io_port + 0x800, 2);
+ release_region(host->card.io_intr, 1);
+ release_region(host->card.io_page, 1);
+ release_region(host->dma.io_port, 256);
+ release_region(instance->io_port, 2048);
for (i = 0; i < MAX_ECARDS; i++)
- if (ecs[i] && instance->io_port == ecard_address (ecs[i], ECARD_MEMC, 0))
- ecard_release (ecs[i]);
+ if (ecs[i] && instance->io_port == ecard_address(ecs[i], ECARD_MEMC, 0))
+ ecard_release(ecs[i]);
- msgqueue_free (&host->scsi.msgs);
- queue_free (&host->queues.disconnected);
- queue_free (&host->queues.issue);
+ msgqueue_free(&host->scsi.msgs);
+ queue_free(&host->queues.disconnected);
+ queue_free(&host->queues.issue);
return 0;
}
/*
- * Function: char *acornscsi_info (struct Scsi_Host *host)
+ * Function: char *acornscsi_info(struct Scsi_Host *host)
* Purpose : return a string describing this interface
* Params : host - host to give information on
* Returns : a constant string
p = string;
- p += sprintf (string, "%s at port %lX irq %d v%d.%d.%d"
+ p += sprintf(string, "%s at port %X irq %d v%d.%d.%d"
#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
" SYNC"
#endif
host = (AS_Host *)instance->hostdata;
- p += sprintf (p, "AcornSCSI driver v%d.%d.%d"
+ p += sprintf(p, "AcornSCSI driver v%d.%d.%d"
#ifdef CONFIG_SCSI_ACORNSCSI_SYNC
" SYNC"
#endif
#endif
"\n\n", VER_MAJOR, VER_MINOR, VER_PATCH);
- p += sprintf (p, "SBIC: WD33C93A Address: %08X IRQ : %d\n",
+ p += sprintf(p, "SBIC: WD33C93A Address: %08X IRQ : %d\n",
host->scsi.io_port, host->scsi.irq);
#ifdef USE_DMAC
- p += sprintf (p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n",
+ p += sprintf(p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n",
host->dma.io_port, host->scsi.irq);
#endif
- p += sprintf (p, "Statistics:\n"
+ p += sprintf(p, "Statistics:\n"
"Queued commands: %-10u Issued commands: %-10u\n"
"Done commands : %-10u Reads : %-10u\n"
"Writes : %-10u Others : %-10u\n"
for (devidx = 0; devidx < 9; devidx ++) {
unsigned int statptr, prev;
- p += sprintf (p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
- statptr = status_ptr[devidx] - 10;
+ p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));
+ statptr = host->status_ptr[devidx] - 10;
if ((signed int)statptr < 0)
- statptr += 16;
-
- prev = status[devidx][statptr].when;
-
- for (; statptr != status_ptr[devidx]; statptr = (statptr + 1) & 15) {
- if (status[devidx][statptr].when) {
- p += sprintf (p, "%c%02X:%02X+%2ld",
- status[devidx][statptr].irq ? '-' : ' ',
- status[devidx][statptr].ph,
- status[devidx][statptr].ssr,
- (status[devidx][statptr].when - prev) < 100 ?
- (status[devidx][statptr].when - prev) : 99);
- prev = status[devidx][statptr].when;
+ statptr += STATUS_BUFFER_SIZE;
+
+ prev = host->status[devidx][statptr].when;
+
+ for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) {
+ if (host->status[devidx][statptr].when) {
+ p += sprintf(p, "%c%02X:%02X+%2ld",
+ host->status[devidx][statptr].irq ? '-' : ' ',
+ host->status[devidx][statptr].ph,
+ host->status[devidx][statptr].ssr,
+ (host->status[devidx][statptr].when - prev) < 100 ?
+ (host->status[devidx][statptr].when - prev) : 99);
+ prev = host->status[devidx][statptr].when;
}
}
}
- p += sprintf (p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none");
+ p += sprintf(p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none");
for (scd = instance->host_queue; scd; scd = scd->next) {
int len;
- proc_print_scsidevice (scd, p, &len, 0);
+ proc_print_scsidevice(scd, p, &len, 0);
p += len;
- p += sprintf (p, "Extensions: ");
+ p += sprintf(p, "Extensions: ");
if (scd->tagged_supported)
- p += sprintf (p, "TAG %sabled [%d] ",
+ p += sprintf(p, "TAG %sabled [%d] ",
scd->tagged_queue ? "en" : "dis", scd->current_tag);
- p += sprintf (p, "\nTransfers: ");
+ p += sprintf(p, "\nTransfers: ");
if (host->device[scd->id].sync_xfer & 15)
- p += sprintf (p, "sync, offset %d, %d ns\n",
+ p += sprintf(p, "sync, offset %d, %d ns\n",
host->device[scd->id].sync_xfer & 15,
- acornscsi_getperiod (host->device[scd->id].sync_xfer));
+ acornscsi_getperiod(host->device[scd->id].sync_xfer));
else
- p += sprintf (p, "async\n");
+ p += sprintf(p, "async\n");
pos = p - buffer;
if (pos + begin < offset) {
#include "queue.h"
#include "msgqueue.h"
+#define STATUS_BUFFER_SIZE 32
+/*
+ * This is used to dump the previous states of the SBIC
+ */
+struct status_entry {
+ unsigned long when;
+ unsigned char ssr;
+ unsigned char ph;
+ unsigned char irq;
+ unsigned char unused;
+};
+
+#define ADD_STATUS(_q,_ssr,_ph,_irq) \
+({ \
+ host->status[(_q)][host->status_ptr[(_q)]].when = jiffies; \
+ host->status[(_q)][host->status_ptr[(_q)]].ssr = (_ssr); \
+ host->status[(_q)][host->status_ptr[(_q)]].ph = (_ph); \
+ host->status[(_q)][host->status_ptr[(_q)]].irq = (_irq); \
+ host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \
+})
+
/*
* AcornSCSI host specific data
*/
/* driver information */
struct {
unsigned int io_port; /* base address of WD33C93 */
- unsigned char irq; /* interrupt */
+ unsigned int irq; /* interrupt */
phase_t phase; /* current phase */
struct {
char *xfer_ptr; /* pointer to area */
unsigned char xfer_required:1; /* set if we need to transfer something */
unsigned char xfer_setup:1; /* set if DMA is setup */
+ unsigned char xfer_done:1; /* set if DMA reached end of BH list */
} dma;
/* card info */
unsigned int io_ram; /* base address of RAM access */
unsigned char page_reg; /* current setting of page reg */
} card;
+
+ unsigned char status_ptr[9];
+ struct status_entry status[9][STATUS_BUFFER_SIZE];
} AS_Host;
#endif /* ndef HOSTS_C */
--- /dev/null
+/*
+ * linux/arch/arm/drivers/scsi/cumana_2.c
+ *
+ * Copyright (C) 1997,1998 Russell King
+ *
+ * This driver is based on experimentation. Hence, it may have made
+ * assumptions about the particular card that I have available, and
+ * may not be reliable!
+ *
+ * Changelog:
+ * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c
+ * 22-01-1998 RMK 0.0.1 Updated to 2.1.80
+ * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it.
+ * 11-06-1998 0.0.2 Changed to support ARXE 16-bit SCSI card, enabled writing
+ * by Stefan Hanske
+ */
+
+#include <linux/module.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/stat.h>
+
+#include <asm/delay.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ecard.h>
+
+#include "../../scsi/sd.h"
+#include "../../scsi/hosts.h"
+#include "arxescsi.h"
+#include "fas216.h"
+
+/* Hmm - this should go somewhere else */
+#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE)
+
+/* Configuration */
+#define ARXESCSI_XTALFREQ 24
+#define ARXESCSI_ASYNC_PERIOD 200
+#define ARXESCSI_SYNC_DEPTH 0
+
+/*
+ * List of devices that the driver will recognise
+ */
+#define ARXESCSI_LIST { MANU_ARXE, PROD_ARXE_SCSI }
+
+/*
+ * Version
+ */
+#define VER_MAJOR 0
+#define VER_MINOR 0
+#define VER_PATCH 2
+
+static struct expansion_card *ecs[MAX_ECARDS];
+
+static struct proc_dir_entry proc_scsi_arxescsi = {
+ PROC_SCSI_QLOGICFAS, 6, "arxescsi",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/*
+ * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type)
+ * Purpose : initialises DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * min_type - minimum DMA support that we must have for this transfer
+ * Returns : 0 if we should not set CMD_WITHDMA for transfer info command
+ */
+static fasdmatype_t
+arxescsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, fasdmatype_t min_type)
+{
+ /*
+ * We don't do real DMA
+ */
+ return fasdma_pseudo;
+}
+
+
+
+/* Faster transfer routines, written by SH to speed up the loops */
+
+static __inline__ unsigned char getb(unsigned int address, unsigned int reg)
+{
+ unsigned char value;
+
+ __asm__ __volatile__(
+ "ldrb %0, [%1, %2, lsl #5]"
+ : "=r" (value)
+ : "r" (address), "r" (reg) );
+ return value;
+}
+
+static __inline__ unsigned int getw(unsigned int address, unsigned int reg)
+{
+ unsigned int value;
+
+ __asm__ __volatile__(
+ "ldr %0, [%1, %2, lsl #5]\n\t"
+ "mov %0, %0, lsl #16\n\t"
+ "mov %0, %0, lsr #16"
+ : "=r" (value)
+ : "r" (address), "r" (reg) );
+ return value;
+}
+
+static __inline__ void putw(unsigned int address, unsigned int reg, unsigned long value)
+{
+ __asm__ __volatile__(
+ "mov %0, %0, lsl #16\n\t"
+ "str %0, [%1, %2, lsl #5]"
+ :
+ : "r" (value), "r" (address), "r" (reg) );
+}
+
+
+/*
+ * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer)
+ * Purpose : handles pseudo DMA
+ * Params : host - host
+ * SCpnt - command
+ * direction - DMA on to/off of card
+ * transfer - minimum number of bytes we expect to transfer
+ */
+void arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp,
+ fasdmadir_t direction, int transfer)
+{
+ ARXEScsi_Info *info = (ARXEScsi_Info *)host->hostdata;
+ unsigned int length, io, error=0;
+ unsigned char *addr;
+
+ length = SCp->this_residual;
+ addr = SCp->ptr;
+ io = __ioaddr(host->io_port);
+
+ if (direction == DMA_OUT) {
+ while (length > 0) {
+ unsigned long word;
+
+
+ word = *addr | *(addr + 1) << 8;
+ if (getb(io, 4) & STAT_INT)
+ break;
+
+ if (!(getb(io, 48) & CSTATUS_IRQ))
+ continue;
+
+ putw(io, 16, word);
+ if (length > 1) {
+ addr += 2;
+ length -= 2;
+ } else {
+ addr += 1;
+ length -= 1;
+ }
+ }
+ }
+ else {
+ if (transfer && (transfer & 255)) {
+ while (length >= 256) {
+ if (getb(io, 4) & STAT_INT) {
+ error=1;
+ break;
+ }
+
+ if (!(getb(io, 48) & CSTATUS_IRQ))
+ continue;
+
+ insw(info->dmaarea, addr, 256 >> 1);
+ addr += 256;
+ length -= 256;
+ }
+ }
+
+ if (!(error))
+ while (length > 0) {
+ unsigned long word;
+
+ if (getb(io, 4) & STAT_INT)
+ break;
+
+ if (!(getb(io, 48) & CSTATUS_IRQ))
+ continue;
+
+ word = getw(io, 16);
+ *addr++ = word;
+ if (--length > 0) {
+ *addr++ = word >> 8;
+ length --;
+ }
+ }
+ }
+}
+
+/*
+ * Function: int arxescsi_dma_stop(host, SCpnt)
+ * Purpose : stops DMA/PIO
+ * Params : host - host
+ * SCpnt - command
+ */
+static void arxescsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp)
+{
+ /*
+ * no DMA to stop
+ */
+}
+
+/*
+ * Function: int arxescsi_detect(Scsi_Host_Template * tpnt)
+ * Purpose : initialises ARXE SCSI driver
+ * Params : tpnt - template for this SCSI adapter
+ * Returns : >0 if host found, 0 otherwise.
+ */
+int arxescsi_detect(Scsi_Host_Template *tpnt)
+{
+ static const card_ids arxescsi_cids[] = { ARXESCSI_LIST, { 0xffff, 0xffff} };
+ int count = 0;
+ struct Scsi_Host *host;
+
+ tpnt->proc_dir = &proc_scsi_arxescsi;
+ memset(ecs, 0, sizeof (ecs));
+
+ ecard_startfind();
+
+ while (1) {
+ ARXEScsi_Info *info;
+
+ ecs[count] = ecard_find(0, arxescsi_cids);
+ if (!ecs[count])
+ break;
+
+ ecard_claim(ecs[count]);
+
+ host = scsi_register(tpnt, sizeof (ARXEScsi_Info));
+ if (!host) {
+ ecard_release(ecs[count]);
+ break;
+ }
+
+ host->io_port = ecard_address(ecs[count], ECARD_MEMC, 0) + 0x0800;
+ host->irq = NO_IRQ;
+ host->dma_channel = NO_DMA;
+ host->can_queue = 0; /* no command queueing */
+ info = (ARXEScsi_Info *)host->hostdata;
+
+ info->info.scsi.io_port = host->io_port;
+ info->info.scsi.irq = host->irq;
+ info->info.scsi.io_shift = 3;
+ info->info.ifcfg.clockrate = ARXESCSI_XTALFREQ;
+ info->info.ifcfg.select_timeout = 255;
+ info->info.ifcfg.asyncperiod = ARXESCSI_ASYNC_PERIOD;
+ info->info.ifcfg.sync_max_depth = ARXESCSI_SYNC_DEPTH;
+ info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK;
+ info->info.ifcfg.disconnect_ok = 0;
+ info->info.ifcfg.wide_max_size = 0;
+ info->info.dma.setup = arxescsi_dma_setup;
+ info->info.dma.pseudo = arxescsi_dma_pseudo;
+ info->info.dma.stop = arxescsi_dma_stop;
+ info->dmaarea = host->io_port + 128;
+ info->cstatus = host->io_port + 384;
+
+ ecs[count]->irqaddr = (unsigned char *)BUS_ADDR(host->io_port);
+ ecs[count]->irqmask = CSTATUS_IRQ;
+
+ request_region(host->io_port , 120, "arxescsi-fas");
+ request_region(host->io_port + 128, 384, "arxescsi-dma");
+
+ printk("scsi%d: Has no interrupts - using polling mode\n",
+ host->host_no);
+
+ fas216_init(host);
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function: int arxescsi_release(struct Scsi_Host * host)
+ * Purpose : releases all resources used by this adapter
+ * Params : host - driver host structure to return info for.
+ * Returns : nothing
+ */
+int arxescsi_release(struct Scsi_Host *host)
+{
+ int i;
+
+ fas216_release(host);
+
+ release_region(host->io_port, 120);
+ release_region(host->io_port + 128, 384);
+
+ for (i = 0; i < MAX_ECARDS; i++)
+ if (ecs[i] && host->io_port == (ecard_address(ecs[i], ECARD_MEMC, 0) + 0x0800))
+ ecard_release(ecs[i]);
+ return 0;
+}
+
+/*
+ * Function: const char *arxescsi_info(struct Scsi_Host * host)
+ * Purpose : returns a descriptive string about this interface,
+ * Params : host - driver host structure to return info for.
+ * Returns : pointer to a static buffer containing null terminated string.
+ */
+const char *arxescsi_info(struct Scsi_Host *host)
+{
+ ARXEScsi_Info *info = (ARXEScsi_Info *)host->hostdata;
+ static char string[100], *p;
+
+ p = string;
+ p += sprintf(string, "%s at port %lX irq %d v%d.%d.%d scsi %s",
+ host->hostt->name, host->io_port, host->irq,
+ VER_MAJOR, VER_MINOR, VER_PATCH,
+ info->info.scsi.type);
+
+ return string;
+}
+
+/*
+ * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset,
+ * int length, int host_no, int inout)
+ * Purpose : Return information about the driver to a user process accessing
+ * the /proc filesystem.
+ * Params : buffer - a buffer to write information to
+ * start - a pointer into this buffer set by this routine to the start
+ * of the required information.
+ * offset - offset into information that we have read upto.
+ * length - length of buffer
+ * host_no - host number to return information for
+ * inout - 0 for reading, 1 for writing.
+ * Returns : length of data written to buffer.
+ */
+int arxescsi_proc_info(char *buffer, char **start, off_t offset,
+ int length, int host_no, int inout)
+{
+ int pos, begin;
+ struct Scsi_Host *host = scsi_hostlist;
+ ARXEScsi_Info *info;
+ Scsi_Device *scd;
+
+ while (host) {
+ if (host->host_no == host_no)
+ break;
+ host = host->next;
+ }
+ if (!host)
+ return 0;
+
+ info = (ARXEScsi_Info *)host->hostdata;
+ if (inout == 1)
+ return -EINVAL;
+
+ begin = 0;
+ pos = sprintf(buffer,
+ "ARXE 16-bit SCSI driver version %d.%d.%d\n",
+ VER_MAJOR, VER_MINOR, VER_PATCH);
+ pos += sprintf(buffer + pos,
+ "Address: %08lX IRQ : %d\n"
+ "FAS : %s\n\n"
+ "Statistics:\n",
+ host->io_port, host->irq, info->info.scsi.type);
+
+ pos += fas216_print_stats(&info->info, buffer + pos);
+
+ pos += sprintf (buffer+pos, "\nAttached devices:\n");
+
+ for (scd = host->host_queue; scd; scd = scd->next) {
+ pos += fas216_print_device(&info->info, scd, buffer + pos);
+
+ if (pos + begin < offset) {
+ begin += pos;
+ pos = 0;
+ }
+ if (pos + begin > offset + length)
+ break;
+ }
+
+ *start = buffer + (offset - begin);
+ pos -= offset - begin;
+ if (pos > length)
+ pos = length;
+
+ return pos;
+}
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = ARXEScsi;
+
+#include "../../scsi/scsi_module.c"
+#endif
--- /dev/null
+/*
+ * ARXE SCSI card driver
+ *
+ * Copyright (C) 1997 Russell King
+ * Changes to support ARXE 16-bit SCSI card by Stefan Hanske
+ */
+#ifndef ARXE_SCSI_H
+#define ARXE_SCSI_H
+
+#define MANU_ARXE 0x0041
+#define PROD_ARXE_SCSI 0x00be
+
+extern int arxescsi_detect (Scsi_Host_Template *);
+extern int arxescsi_release (struct Scsi_Host *);
+extern const char *arxescsi_info (struct Scsi_Host *);
+extern int arxescsi_proc_info (char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#ifndef CAN_QUEUE
+/*
+ * Default queue size
+ */
+#define CAN_QUEUE 1
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 1
+#endif
+
+#ifndef SCSI_ID
+/*
+ * Default SCSI host ID
+ */
+#define SCSI_ID 7
+#endif
+
+#include <scsi/scsicam.h>
+
+#ifndef HOSTS_C
+#include "fas216.h"
+#endif
+
+#define ARXEScsi { \
+proc_info: arxescsi_proc_info, \
+name: "ARXE SCSI card", \
+detect: arxescsi_detect, /* detect */ \
+release: arxescsi_release, /* release */ \
+info: arxescsi_info, /* info */ \
+command: fas216_command, /* command */ \
+queuecommand: fas216_queue_command, /* queuecommand */ \
+abort: fas216_abort, /* abort */ \
+reset: fas216_reset, /* reset */ \
+bios_param: scsicam_bios_param, /* biosparam */ \
+can_queue: CAN_QUEUE, /* can queue */ \
+this_id: SCSI_ID, /* scsi host id */ \
+sg_tablesize: SG_ALL, /* sg_tablesize */ \
+cmd_per_lun: CMD_PER_LUN, /* cmd per lun */ \
+use_clustering: DISABLE_CLUSTERING \
+ }
+
+#ifndef HOSTS_C
+
+typedef struct {
+ FAS216_Info info;
+
+ /* other info... */
+ unsigned int cstatus; /* card status register */
+ unsigned int dmaarea; /* Pseudo DMA area */
+} ARXEScsi_Info;
+
+#define CSTATUS_IRQ (1 << 0)
+#define CSTATUS_DRQ (1 << 0)
+
+#endif /* HOSTS_C */
+
+#endif /* ARXE_SCSI_H */
* Copyright (C) 1997-1998 Russell King
*
* Changelog:
- * 30-08-1997 RMK 0.0.0 Created, READONLY version
- * 22-01-1998 RMK 0.0.1 Updated to 2.1.80
+ * 30-08-1997 RMK 0.0.0 Created, READONLY version.
+ * 22-01-1998 RMK 0.0.1 Updated to 2.1.80.
* 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it.
- * 02-05-1998 RMK 0.0.2 Updated & added DMA support
+ * 02-05-1998 RMK 0.0.2 Updated & added DMA support.
* 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
- * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth
+ * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth.
*/
#include <linux/module.h>
cumanascsi_2_irqenable,
cumanascsi_2_irqdisable,
NULL,
+ NULL,
+ NULL,
NULL
};
info->info.ifcfg.sync_max_depth = CUMANASCSI2_SYNC_DEPTH;
info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
info->info.ifcfg.disconnect_ok = 1;
+ info->info.ifcfg.wide_max_size = 0;
info->info.dma.setup = cumanascsi_2_dma_setup;
info->info.dma.pseudo = cumanascsi_2_dma_pseudo;
info->info.dma.stop = cumanascsi_2_dma_stop;
#include "../../scsi/hosts.h"
#include "eesox.h"
-#define NO_IRQ 255
-#define NO_DMA 255
-
/* Configuration */
#define EESOX_XTALFREQ 40
#define EESOX_ASYNC_PERIOD 200
eesoxscsi_irqenable,
eesoxscsi_irqdisable,
NULL,
+ NULL,
+ NULL,
NULL
};
info->info.ifcfg.sync_max_depth = EESOX_SYNC_DEPTH;
info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
info->info.ifcfg.disconnect_ok = 1;
+ info->info.ifcfg.wide_max_size = 0;
info->info.dma.setup = eesoxscsi_dma_setup;
info->info.dma.pseudo = eesoxscsi_dma_pseudo;
info->info.dma.stop = eesoxscsi_dma_stop;
* 02-05-1998 RMK Added extra checks in fas216_reset
* 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns
* 27-06-1998 RMK Changed asm/delay.h to linux/delay.h
+ * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT
*
* Todo:
- * - tighten up the MESSAGE_REJECT support.
* - allow individual devices to enable sync xfers.
*/
#define VER_MAJOR 0
#define VER_MINOR 0
-#define VER_PATCH 4
+#define VER_PATCH 5
#define SCSI2_TAG
*/
#define SCSI2_SYNC
+#define SCSI2_WIDE
+
#undef DEBUG_CONNECT
#undef DEBUG_BUSSERVICE
#undef DEBUG_FUNCTIONDONE
printk(" SCp={ ptr=%p this_residual=%X buffer=%p buffers_residual=%X }\n",
info->scsi.SCp.ptr, info->scsi.SCp.this_residual,
info->scsi.SCp.buffer, info->scsi.SCp.buffers_residual);
- printk(" msgs async_stp=%X last_message=%X disconnectable=%d aborting=%d }\n",
- info->scsi.async_stp, info->scsi.last_message,
+ printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n",
+ info->scsi.async_stp,
info->scsi.disconnectable, info->scsi.aborting);
printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n"
" disconnects=%X aborts=%X resets=%X }\n",
info->ifcfg.clockrate, info->ifcfg.select_timeout,
info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth);
for (i = 0; i < 8; i++) {
- printk(" busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X negstate=%X }\n",
+ printk(" busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n",
i, info->busyluns[i], i,
info->device[i].disconnect_ok, info->device[i].stp,
- info->device[i].sof, info->device[i].negstate);
+ info->device[i].sof, info->device[i].sync_state);
}
printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n",
info->dma.transfer_type, info->dma.setup,
static const char *fas216_drv_phase(FAS216_Info *info)
{
switch (info->scsi.phase) {
- case PHASE_IDLE: return "idle";
- case PHASE_SELECTION: return "selection";
- case PHASE_MESSAGESENT: return "message sent";
- case PHASE_RECONNECTED: return "reconnected";
- case PHASE_DATAOUT: return "data out";
- case PHASE_DATAIN: return "data in";
- case PHASE_MSGOUT: return "message out";
- case PHASE_MSGIN: return "message in";
- case PHASE_AFTERMSGOUT: return "after message out";
- case PHASE_STATUS: return "status";
- case PHASE_DISCONNECT: return "disconnect";
- case PHASE_DONE: return "done";
- default: return "???";
+ case PHASE_IDLE: return "idle";
+ case PHASE_SELECTION: return "selection";
+ case PHASE_COMMAND: return "command";
+ case PHASE_RECONNECTED: return "reconnected";
+ case PHASE_DATAOUT: return "data out";
+ case PHASE_DATAIN: return "data in";
+ case PHASE_MSGIN: return "message in";
+ case PHASE_MSGIN_DISCONNECT: return "disconnect";
+ case PHASE_MSGOUT_EXPECT: return "expect message out";
+ case PHASE_MSGOUT: return "message out";
+ case PHASE_STATUS: return "status";
+ case PHASE_DONE: return "done";
+ default: return "???";
}
}
return clock;
}
+/* Function: unsigned short fas216_get_last_msg(FAS216_Info *info, int pos)
+ * Purpose : retrieve a last message from the list, using position in fifo
+ * Params : info - interface to search
+ * : pos - current fifo position
+ */
+static inline unsigned short
+fas216_get_last_msg(FAS216_Info *info, int pos)
+{
+ unsigned short packed_msg = NOP;
+ struct message *msg;
+ int msgnr = 0;
+
+ while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+ if (pos >= msg->fifo)
+ break;
+ }
+
+ if (msg) {
+ if (msg->msg[0] == EXTENDED_MESSAGE)
+ packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8;
+ else
+ packed_msg = msg->msg[0];
+ }
+
+#ifdef DEBUG_MESSAGES
+ printk("Message: %04X found at position %02X\n",
+ packed_msg, pos);
+#endif
+ return packed_msg;
+}
+
/* Function: int fas216_syncperiod(FAS216_Info *info, int ns)
* Purpose : Calculate value to be loaded into the STP register
* for a given period in ns
outb(info->scsi.cfg[2], REG_CNTL3(info));
}
+/* Synchronous transfer support
+ *
+ * Note: The SCSI II r10 spec says (5.6.12):
+ *
+ * (2) Due to historical problems with early host adapters that could
+ * not accept an SDTR message, some targets may not initiate synchronous
+ * negotiation after a power cycle as required by this standard. Host
+ * adapters that support synchronous mode may avoid the ensuing failure
+ * modes when the target is independently power cycled by initiating a
+ * synchronous negotiation on each REQUEST SENSE and INQUIRY command.
+ * This approach increases the SCSI bus overhead and is not recommended
+ * for new implementations. The correct method is to respond to an
+ * SDTR message with a MESSAGE REJECT message if the either the
+ * initiator or target devices does not support synchronous transfers
+ * or does not want to negotiate for synchronous transfers at the time.
+ * Using the correct method assures compatibility with wide data
+ * transfers and future enhancements.
+ *
+ * We will always initiate a synchronous transfer negociation request on
+ * every INQUIRY or REQUEST SENSE message, unless the target itself has
+ * at some point performed a synchronous transfer negociation request, or
+ * we have synchronous transfers disabled for this device.
+ */
+
+/* Function: void fas216_handlesync(FAS216_Info *info, char *msg)
+ * Purpose : Handle a synchronous transfer message from the target
+ * Params : info - state structure for interface
+ * : msg - message from target
+ */
+static void
+fas216_handlesync(FAS216_Info *info, char *msg)
+{
+ struct fas216_device *dev = &info->device[info->SCpnt->target];
+ enum { sync, async, none, reject } res = none;
+
+#ifdef SCSI2_SYNC
+ switch (msg[0]) {
+ case MESSAGE_REJECT:
+ /* Synchronous transfer request failed.
+ * Note: SCSI II r10:
+ *
+ * SCSI devices that are capable of synchronous
+ * data transfers shall not respond to an SDTR
+ * message with a MESSAGE REJECT message.
+ *
+ * Hence, if we get this condition, we disable
+ * negociation for this device.
+ */
+ if (dev->sync_state == neg_inprogress) {
+ dev->sync_state = neg_invalid;
+ res = async;
+ }
+ break;
+
+ case EXTENDED_MESSAGE:
+ switch (dev->sync_state) {
+ /* We don't accept synchronous transfer requests.
+ * Respond with a MESSAGE_REJECT to prevent a
+ * synchronous transfer agreement from being reached.
+ */
+ case neg_invalid:
+ res = reject;
+ break;
+
+ /* We were not negociating a synchronous transfer,
+ * but the device sent us a negociation request.
+ * Honour the request by sending back a SDTR
+ * message containing our capability, limited by
+ * the targets capability.
+ */
+ default:
+ outb(CMD_SETATN, REG_CMD(info));
+ if (msg[4] > info->ifcfg.sync_max_depth)
+ msg[4] = info->ifcfg.sync_max_depth;
+ if (msg[3] < 1000 / info->ifcfg.clockrate)
+ msg[3] = 1000 / info->ifcfg.clockrate;
+
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 5,
+ EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
+ msg[3], msg[4]);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+
+ /* This is wrong. The agreement is not in effect
+ * until this message is accepted by the device
+ */
+ dev->sync_state = neg_targcomplete;
+ res = sync;
+ break;
+
+ /* We initiated the synchronous transfer negociation,
+ * and have successfully received a response from the
+ * target. The synchronous transfer agreement has been
+ * reached. Note: if the values returned are out of our
+ * bounds, we must reject the message.
+ */
+ case neg_inprogress:
+ res = reject;
+ if (msg[4] <= info->ifcfg.sync_max_depth &&
+ msg[3] >= 1000 / info->ifcfg.clockrate) {
+ dev->sync_state = neg_complete;
+ res = sync;
+ }
+ break;
+ }
+ }
+#else
+ res = reject;
+#endif
+
+ switch (res) {
+ case sync:
+ dev->period = msg[3];
+ dev->sof = msg[4];
+ dev->stp = fas216_syncperiod(info, msg[3] * 4);
+ fas216_set_sync(info, info->SCpnt->target);
+ break;
+
+ case reject:
+ outb(CMD_SETATN, REG_CMD(info));
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+
+ case async:
+ dev->period = info->ifcfg.asyncperiod / 4;
+ dev->sof = 0;
+ dev->stp = info->scsi.async_stp;
+ fas216_set_sync(info, info->SCpnt->target);
+ break;
+
+ case none:
+ break;
+ }
+}
+
+/* Function: void fas216_handlewide(FAS216_Info *info, char *msg)
+ * Purpose : Handle a wide transfer message from the target
+ * Params : info - state structure for interface
+ * : msg - message from target
+ */
+static void
+fas216_handlewide(FAS216_Info *info, char *msg)
+{
+ struct fas216_device *dev = &info->device[info->SCpnt->target];
+ enum { wide, bit8, none, reject } res = none;
+
+#ifdef SCSI2_WIDE
+ switch (msg[0]) {
+ case MESSAGE_REJECT:
+ /* Wide transfer request failed.
+ * Note: SCSI II r10:
+ *
+ * SCSI devices that are capable of wide
+ * data transfers shall not respond to a
+ * WDTR message with a MESSAGE REJECT message.
+ *
+ * Hence, if we get this condition, we never
+ * reattempt negociation for this device.
+ */
+ if (dev->wide_state == neg_inprogress) {
+ dev->wide_state = neg_invalid;
+ res = bit8;
+ }
+ break;
+
+ case EXTENDED_MESSAGE:
+ switch (dev->wide_state) {
+ /* We don't accept wide data transfer requests.
+ * Respond with a MESSAGE REJECT to prevent a
+ * wide data transfer agreement from being reached.
+ */
+ case neg_invalid:
+ res = reject;
+ break;
+
+ /* We were not negociating a wide data transfer,
+ * but the device sent is a negociation request.
+ * Honour the request by sending back a WDTR
+ * message containing our capability, limited by
+ * the targets capability.
+ */
+ default:
+ outb(CMD_SETATN, REG_CMD(info));
+ if (msg[3] > info->ifcfg.wide_max_size)
+ msg[3] = info->ifcfg.wide_max_size;
+
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 4,
+ EXTENDED_MESSAGE, 2, EXTENDED_WDTR,
+ msg[3]);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+ res = wide;
+ break;
+
+ /* We initiated the wide data transfer negociation,
+ * and have successfully received a response from the
+ * target. The synchronous transfer agreement has been
+ * reached. Note: if the values returned are out of our
+ * bounds, we must reject the message.
+ */
+ case neg_inprogress:
+ res = reject;
+ if (msg[3] <= info->ifcfg.wide_max_size) {
+ dev->wide_state = neg_complete;
+ res = wide;
+ }
+ break;
+ }
+ }
+#else
+ res = reject;
+#endif
+
+ switch (res) {
+ case wide:
+ dev->wide_xfer = msg[3];
+ break;
+
+ case reject:
+ outb(CMD_SETATN, REG_CMD(info));
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+
+ case bit8:
+ dev->wide_xfer = 0;
+ break;
+
+ case none:
+ break;
+ }
+}
+
/* Function: void fas216_updateptrs(FAS216_Info *info, int bytes_transferred)
* Purpose : update data pointers after transfer suspended/paused
* Params : info - interface's local pointer to update
residual -= bytes_transferred;
ptr += bytes_transferred;
+ if (residual == 0)
+ ptr = NULL;
+
info->scsi.SCp.ptr = ptr;
info->scsi.SCp.this_residual = residual;
}
{
unsigned int residual;
char *ptr;
- int correction;
+ int correction = 0;
fas216_checkmagic(info, "fas216_pio");
ptr = info->scsi.SCp.ptr;
if (direction == DMA_OUT) {
- while (residual > 0) {
- if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) {
+// while (residual > 0) {
+// if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) {
outb(*ptr++, REG_FF(info));
residual -= 1;
- } else if (inb(REG_STAT(info)) & STAT_INT)
- break;
- }
- correction = inb(REG_CFIS(info)) & CFIS_CF;
+// }
+// if (inb(REG_STAT(info)) & STAT_INT)
+// break;
+// }
+// correction = inb(REG_CFIS(info)) & CFIS_CF;
} else {
- while (residual > 0) {
- if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) {
+// while (residual > 0) {
+// if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) {
*ptr++ = inb(REG_FF(info));
residual -= 1;
- } else if (inb(REG_STAT(info)) & STAT_INT)
- break;
- }
- correction = 0;
+// }
+// if (inb(REG_STAT(info)) & STAT_INT)
+// break;
+// }
}
ptr -= correction;
switch (info->scsi.phase) {
case PHASE_SELECTION: /* while selecting - no target */
+ case PHASE_SELSTEPS:
fas216_done(info, DID_NO_CONNECT);
break;
- case PHASE_DISCONNECT: /* message in - disconnecting */
+ case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */
outb(CMD_ENABLESEL, REG_CMD(info));
info->scsi.disconnectable = 1;
info->scsi.reconnected.tag = 0;
fas216_done(info, DID_OK);
break;
- case PHASE_AFTERMSGOUT: /* message out - possible ABORT message */
- if (info->scsi.last_message == ABORT) {
+ case PHASE_MSGOUT: /* message out - possible ABORT message */
+ if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) {
info->scsi.aborting = 0;
fas216_done(info, DID_ABORT);
break;
fas216_checkmagic(info, "fas216_reselected_intr");
- if (info->scsi.phase == PHASE_SELECTION && info->SCpnt) {
+ if ((info->scsi.phase == PHASE_SELECTION ||
+ info->scsi.phase == PHASE_SELSTEPS) && info->SCpnt) {
Scsi_Cmnd *SCpnt = info->SCpnt;
info->origSCpnt = SCpnt;
info->SCpnt = NULL;
- if (info->device[SCpnt->target].negstate == syncneg_sent)
- info->device[SCpnt->target].negstate = syncneg_start;
+ if (info->device[SCpnt->target].wide_state == neg_inprogress)
+ info->device[SCpnt->target].wide_state = neg_wait;
+ if (info->device[SCpnt->target].sync_state == neg_inprogress)
+ info->device[SCpnt->target].sync_state = neg_wait;
}
#ifdef DEBUG_CONNECT
fas216_target(info), info->scsi.phase);
#endif
- msgqueue_flush(&info->scsi.msgs);
-
if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) {
printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n",
info->host->host_no);
outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
- info->scsi.phase = PHASE_MSGOUT;
outb(CMD_MSGACCEPTED, REG_CMD(info));
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
return;
}
if (!ok) {
/*
- * Something went wrong - abort the command on
- * the target. Should this be INITIATOR_ERROR ?
+ * Something went wrong - send an initiator error to
+ * the target.
*/
outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
- info->scsi.phase = PHASE_MSGOUT;
outb(CMD_MSGACCEPTED, REG_CMD(info));
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
return;
}
if (!ok && queue_probetgtlun(&info->queues.disconnected, target, identify_msg))
ok = 1;
+ msgqueue_flush(&info->scsi.msgs);
if (ok) {
info->scsi.phase = PHASE_RECONNECTED;
outb(target, REG_SDID(info));
} else {
/*
- * Our command structure not found - abort the command on the target
- * Should this be INITIATOR_ERROR ?
+ * Our command structure not found - abort the
+ * command on the target. Since we have no
+ * record of this command, we can't send
+ * an INITIATOR DETECTED ERROR message.
*/
outb(CMD_SETATN, REG_CMD(info));
msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
- info->scsi.phase = PHASE_MSGOUT;
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
}
outb(CMD_MSGACCEPTED, REG_CMD(info));
}
}
if (!info->SCpnt) {
outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
- info->scsi.phase = PHASE_MSGOUT;
+ msgqueue_flush(&info->scsi.msgs);
+#if 0
+ if (info->scsi.reconnected.tag)
+ msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, info->scsi.reconnected.tag);
+ else
+#endif
+ msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
info->scsi.aborting = 1;
} else {
/*
#endif
}
+static unsigned char fas216_get_msg_byte(FAS216_Info *info)
+{
+ int tout;
+
+ outb(CMD_MSGACCEPTED, REG_CMD(info));
+ for (tout = 1000000; tout; tout --)
+ if (inb(REG_STAT(info)) & STAT_INT)
+ break;
+
+ inb(REG_INST(info));
+
+ outb(CMD_TRANSFERINFO, REG_CMD(info));
+
+ for (tout = 1000000; tout; tout --)
+ if (inb(REG_STAT(info)) & STAT_INT)
+ break;
+
+ inb(REG_INST(info));
+
+ return inb(REG_FF(info));
+}
+
/* Function: void fas216_message(FAS216_Info *info)
* Purpose : handle a function done interrupt from FAS216 chip
* Params : info - interface which caused function done interrupt
message[0] = inb(REG_FF(info));
if (message[0] == EXTENDED_MESSAGE) {
- int tout;
- outb(CMD_MSGACCEPTED, REG_CMD(info));
- for (tout = 1000000; tout; tout--)
- if (inb(REG_STAT(info)) & STAT_INT)
- break;
- inb(REG_INST(info));
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- for (tout = 1000000; tout; tout--)
- if (inb(REG_STAT(info)) & STAT_INT)
- break;
- inb(REG_INST(info));
-
- message[1] = inb(REG_FF(info));
+ message[1] = fas216_get_msg_byte(info);
- for (msglen = 2; msglen < message[1] + 2; msglen++) {
- outb(CMD_MSGACCEPTED, REG_CMD(info));
- for (tout = 1000000; tout; tout--)
- if (inb(REG_STAT(info)) & STAT_INT)
- break;
- inb(REG_INST(info));
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- for (tout = 1000000; tout; tout--)
- if (inb(REG_STAT(info)) & STAT_INT)
- break;
- inb(REG_INST(info));
-
- message[msglen] = inb(REG_FF(info));
- }
+ for (msglen = 2; msglen < message[1] + 2; msglen++)
+ message[msglen] = fas216_get_msg_byte(info);
}
#ifdef DEBUG_MESSAGES
printk("\n");
}
#endif
+
if (info->scsi.phase == PHASE_RECONNECTED) {
if (message[0] == SIMPLE_QUEUE_TAG)
info->scsi.reconnected.tag = message[1];
switch (message[0]) {
case COMMAND_COMPLETE:
- printk("fas216: command complete with no status in MESSAGE_IN?\n");
+ printk(KERN_ERR "scsi%d.%c: command complete with no "
+ "status in MESSAGE_IN?\n",
+ info->host->host_no, fas216_target(info));
break;
case SAVE_POINTERS:
/*
* Save current data pointer to SAVED data pointer
+ * SCSI II standard says that we must not acknowledge
+ * this until we have really saved pointers.
+ * NOTE: we DO NOT save the command nor status pointers
+ * as required by the SCSI II standard. These always
+ * point to the start of their respective areas.
*/
info->SCpnt->SCp = info->scsi.SCp;
+ info->SCpnt->SCp.sent_command = 0;
#if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT)
printk("scsi%d.%c: save data pointers: [%p, %X]\n",
info->host->host_no, fas216_target(info),
break;
case DISCONNECT:
- info->scsi.phase = PHASE_DISCONNECT;
+ info->scsi.phase = PHASE_MSGIN_DISCONNECT;
break;
case MESSAGE_REJECT:
- printk("scsi%d.%c: reject, last message %04X\n",
- info->host->host_no, fas216_target(info),
- info->scsi.last_message);
+ switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) {
+ case EXTENDED_MESSAGE | EXTENDED_SDTR << 8:
+ fas216_handlesync(info, message);
+ break;
+
+ case EXTENDED_MESSAGE | EXTENDED_WDTR << 8:
+ fas216_handlewide(info, message);
+ break;
+
+ default:
+ printk("scsi%d.%c: reject, last message %04X\n",
+ info->host->host_no, fas216_target(info),
+ fas216_get_last_msg(info, info->scsi.msgin_fifo));
+ }
+ break;
+
+ case NOP:
break;
case SIMPLE_QUEUE_TAG:
case EXTENDED_MESSAGE:
switch (message[2]) {
case EXTENDED_SDTR: /* Sync transfer negociation request/reply */
- switch (info->device[info->SCpnt->target].negstate) {
- case syncneg_invalid:
- msgqueue_flush(&info->scsi.msgs);
- outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
- info->scsi.phase = PHASE_MSGOUT;
- break;
-
- default:
- if (message[4] > info->ifcfg.sync_max_depth)
- message[4] = info->ifcfg.sync_max_depth;
- if (message[3] < 1000 / info->ifcfg.clockrate)
- message[3] = 1000 / info->ifcfg.clockrate;
-
- outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg(&info->scsi.msgs, 5,
- EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
- message[3], message[4]);
- info->scsi.phase = PHASE_MSGOUT;
- case syncneg_sent:
- info->device[info->SCpnt->target].negstate = syncneg_complete;
- info->device[info->SCpnt->target].period = message[3];
- info->device[info->SCpnt->target].sof = message[4];
- info->device[info->SCpnt->target].stp =
- fas216_syncperiod(info, message[3] * 4);
- printk(KERN_NOTICE "scsi%d.%c: using synchronous transfer, offset %d, %d ns\n",
- info->host->host_no, fas216_target(info), message[4], message[3] * 4);
- fas216_set_sync(info, info->SCpnt->target);
- break;
- }
+ fas216_handlesync(info, message);
break;
case EXTENDED_WDTR: /* Wide transfer negociation request/reply */
- /* We don't do wide transfers - reject message */
+ fas216_handlewide(info, message);
+ break;
+
default:
printk("scsi%d.%c: unrecognised extended message %02X, rejecting\n",
info->host->host_no, fas216_target(info),
message[2]);
- msgqueue_flush(&info->scsi.msgs);
- outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
- info->scsi.phase = PHASE_MSGOUT;
- break;
+ goto reject_message;
}
break;
printk("scsi%d.%c: unrecognised message %02X, rejecting\n",
info->host->host_no, fas216_target(info),
message[0]);
- msgqueue_flush(&info->scsi.msgs);
- outb(CMD_SETATN, REG_CMD(info));
- msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
- info->scsi.phase = PHASE_MSGOUT;
- break;
+ goto reject_message;
}
outb(CMD_MSGACCEPTED, REG_CMD(info));
+ return;
+
+reject_message:
+ outb(CMD_SETATN, REG_CMD(info));
+ outb(CMD_MSGACCEPTED, REG_CMD(info));
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
}
/* Function: void fas216_send_command(FAS216_Info *info)
outb(CMD_FLUSHFIFO, REG_CMD(info));
/* load command */
- for (i = 0; i < info->SCpnt->cmd_len; i++)
+ for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++)
outb(info->SCpnt->cmnd[i], REG_FF(info));
outb(CMD_TRANSFERINFO, REG_CMD(info));
-}
-
-/* Function: int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in selection phase
- * Params : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- */
-static int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
-{
- fas216_checkmagic(info, "fas216_busservice_selection");
-
- switch (stat & STAT_BUSMASK) {
- case STAT_DATAOUT: /* data out phase */
- fas216_starttransfer(info, DMA_OUT, 1);
- return 1;
-
- case STAT_DATAIN: /* data in phase */
- fas216_starttransfer(info, DMA_IN, 0);
- return 1;
-
- case STAT_STATUS: /* status phase */
- info->scsi.phase = PHASE_STATUS;
- outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
- return 1;
-
- case STAT_MESGIN: /* message in phase */
- info->scsi.phase = PHASE_MSGIN;
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- return 1;
-
- case STAT_MESGOUT:{ /* message out phase */
- char *msg;
- int start = 1, msglen;
-
- /* load message bytes, but don't forget to miss the first
- * byte!
- */
- while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
- int i;
-
- for (i = start; i < msglen; i++)
- outb(msg[i], REG_FF(info));
- start = 0;
- }
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- info->scsi.phase = PHASE_MESSAGESENT;
- return 1;
- }
- default:
- return 0;
- }
-}
-
-/* Function: int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service after the IDENTIFY message has been sent
- * Params : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- */
-static int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat)
-{
- fas216_checkmagic(info, "fas216_busservice_messagesent");
-
- switch (stat & STAT_BUSMASK) {
- case STAT_MESGIN: /* message in phase */
- info->scsi.phase = PHASE_MSGIN;
- outb(CMD_FLUSHFIFO, REG_CMD(info));
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- return 1;
- case STAT_COMMAND: /* command phase */
- fas216_send_command(info);
- return 1;
-
- default:
- return 0;
- }
+ info->scsi.phase = PHASE_COMMAND;
}
-/* Function: int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in a data in/out phase.
- * Params : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- * Note : We do not allow the device to change the data direction!
- */
-static int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
-{
- fas216_checkmagic(info, "fas216_busservice_dataphase");
-
- switch (stat & STAT_BUSMASK) {
- case STAT_DATAIN: /* continue data in phase */
- if (info->scsi.phase == PHASE_DATAIN) {
- fas216_starttransfer(info, DMA_IN, 0);
- return 1;
- } else
- return 0;
-
- case STAT_DATAOUT: /* continue data out phase */
- if (info->scsi.phase == PHASE_DATAOUT) {
- fas216_starttransfer(info, DMA_OUT, 0);
- return 1;
- } else
- return 0;
-
- case STAT_STATUS: /* status in phase */
- fas216_stoptransfer(info);
- info->scsi.phase = PHASE_STATUS;
- outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
- return 1;
-
- case STAT_MESGIN: /* message in phase */
- fas216_stoptransfer(info);
- info->scsi.phase = PHASE_MSGIN;
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- return 1;
-
- default:
- return 0;
- }
-}
-
-/* Function: int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service in after a reconnection
+/* Function: void fas216_send_messageout(FAS216_Info *info, int start)
+ * Purpose : handle bus service to send a message
* Params : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
* Note : We do not allow the device to change the data direction!
*/
-static int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
+static void fas216_send_messageout(FAS216_Info *info, int start)
{
- fas216_checkmagic(info, "fas216_busservice_reconnected");
-
- switch (stat & STAT_BUSMASK) {
- case STAT_MESGIN:
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- return 1;
-
- case STAT_STATUS:
- fas216_finish_reconnect(info);
- info->scsi.phase = PHASE_STATUS;
- outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
- return 1;
-
- case STAT_DATAOUT: /* data out phase */
- fas216_finish_reconnect(info);
- fas216_starttransfer(info, DMA_OUT, 1);
- return 1;
+ unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs);
- case STAT_DATAIN: /* data in phase */
- fas216_finish_reconnect(info);
- fas216_starttransfer(info, DMA_IN, 0);
- return 1;
+ fas216_checkmagic(info, "fas216_send_messageout");
- default:
- return 0;
- }
-}
+ outb(CMD_FLUSHFIFO, REG_CMD(info));
-/* Function: int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat)
- * Purpose : handle bus service to send a message
- * Params : info - interface which caused bus service
- * Returns : 0 if unable to service this interrupt
- * Note : We do not allow the device to change the data direction!
- */
-static int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat)
-{
- fas216_checkmagic(info, "fas216_busservice_messageout");
+ if (tot_msglen) {
+ struct message *msg;
+ int msgnr = 0;
- if ((stat & STAT_BUSMASK) != STAT_MESGOUT) {
- printk("scsi%d.%c: didn't manage MESSAGE OUT phase\n",
- info->host->host_no, fas216_target(info));
- return 0;
- } else {
- unsigned int msglen = msgqueue_msglength(&info->scsi.msgs);
+ while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+ int i;
- outb(CMD_FLUSHFIFO, REG_CMD(info));
+ for (i = start; i < msg->length; i++)
+ outb(msg->msg[i], REG_FF(info));
- if (msglen == 0)
- outb(NOP, REG_FF(info));
- else {
- char *msg;
+ msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF);
+ start = 0;
+ }
+ } else
+ outb(NOP, REG_FF(info));
- while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
- int i;
+ outb(CMD_TRANSFERINFO, REG_CMD(info));
- for (i = 0; i < msglen; i++)
- outb(msg[i], REG_FF(info));
- }
- }
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- info->scsi.phase = PHASE_AFTERMSGOUT;
- return 1;
- }
+ info->scsi.phase = PHASE_MSGOUT;
}
/* Function: void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
case IS_COMPLETE: /* last action completed */
outb(CMD_NOP, REG_CMD(info));
- switch (info->scsi.phase) {
- case PHASE_SELECTION: /* while selecting - selected target */
- if (!fas216_busservice_selection(info, stat))
- printk("scsi%d.%c: bus phase %s after connect?\n",
- info->host->host_no, fas216_target(info),
- fas216_bus_phase(stat));
- break;
-
- case PHASE_MESSAGESENT:
- if (!fas216_busservice_messagesent(info, stat))
- printk("scsi%d.%c: bus phase %s after message sent?\n",
- info->host->host_no, fas216_target(info),
- fas216_bus_phase(stat));
- break;
+#define STATE(st,ph) ((ph) << 3 | (st))
+ /* This table describes the legal SCSI state transitions,
+ * as described by the SCSI II spec.
+ */
+ switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) {
+ /* Reselmsgin -> Data In */
+ case STATE(STAT_DATAIN, PHASE_RECONNECTED):
+ fas216_finish_reconnect(info);
+ case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In */
+ case STATE(STAT_DATAIN, PHASE_DATAIN): /* Data In -> Data In */
+ case STATE(STAT_DATAIN, PHASE_MSGOUT): /* Message Out -> Data In */
+ case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command -> Data In */
+ case STATE(STAT_DATAIN, PHASE_MSGIN): /* Message In -> Data In */
+ fas216_starttransfer(info, DMA_IN, 0);
+ return;
- case PHASE_DATAIN: /* while transfering data in */
- case PHASE_DATAOUT: /* while transfering data out */
- if (!fas216_busservice_dataphase(info, stat))
- printk("scsi%d.%c: bus phase %s after %s?\n",
- info->host->host_no, fas216_target(info),
- fas216_bus_phase(stat), fas216_drv_phase(info));
- break;
+ case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out -> Data Out */
+ fas216_starttransfer(info, DMA_OUT, 0);
+ return;
+
+ /* Reselmsgin -> Data Out */
+ case STATE(STAT_DATAOUT, PHASE_RECONNECTED):
+ fas216_finish_reconnect(info);
+ case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out */
+ case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out -> Data Out */
+ case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command -> Data Out */
+ case STATE(STAT_DATAOUT, PHASE_MSGIN): /* Message In -> Data Out */
+ fas216_starttransfer(info, DMA_OUT, 1);
+ return;
+
+ /* Reselmsgin -> Status */
+ case STATE(STAT_STATUS, PHASE_RECONNECTED):
+ fas216_finish_reconnect(info);
+ goto status;
+ case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */
+ case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */
+ fas216_stoptransfer(info);
+ case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */
+ case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */
+ case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */
+ case STATE(STAT_STATUS, PHASE_MSGIN): /* Message In -> Status */
+ status:
+ outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
+ info->scsi.phase = PHASE_STATUS;
+ return;
+
+ case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */
+ case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */
+ fas216_stoptransfer(info);
+ case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */
+ case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */
+ info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
+ outb(CMD_TRANSFERINFO, REG_CMD(info));
+ info->scsi.phase = PHASE_MSGIN;
+ return;
- case PHASE_RECONNECTED: /* newly reconnected device */
- /*
- * Command reconnected - if MESGIN, get message - it may be
- * the tag. If not, get command out of the disconnected queue
+ /* Reselmsgin -> Message In */
+ case STATE(STAT_MESGIN, PHASE_RECONNECTED):
+ case STATE(STAT_MESGIN, PHASE_MSGIN):
+ info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
+ outb(CMD_TRANSFERINFO, REG_CMD(info));
+ return;
+
+ /* Reselmsgin -> Command */
+ case STATE(STAT_COMMAND, PHASE_RECONNECTED):
+ fas216_finish_reconnect(info);
+ case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out -> Command */
+ case STATE(STAT_COMMAND, PHASE_MSGIN): /* Message In -> Command */
+ fas216_send_command(info);
+ info->scsi.phase = PHASE_COMMAND;
+ return;
+ /* Selection -> Message Out */
+ case STATE(STAT_MESGOUT, PHASE_SELECTION):
+ fas216_send_messageout(info, 1);
+ return;
+ /* Any -> Message Out */
+ case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT):
+ fas216_send_messageout(info, 0);
+ return;
+
+ /* Error recovery rules.
+ * These either attempt to abort or retry the operation.
+ * TODO: we need more of these
+ */
+ case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command -> Command */
+ /* error - we've sent out all the command bytes
+ * we have.
+ * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS
+ * to include the command bytes sent for this to work
+ * correctly.
*/
- if (!fas216_busservice_reconnected(info, stat))
- printk("scsi%d.%c: bus phase %s after reconnect?\n",
- info->host->host_no, fas216_target(info),
- fas216_bus_phase(stat));
- break;
-
- case PHASE_MSGIN:
- case PHASE_AFTERMSGOUT:
- switch (stat & STAT_BUSMASK) {
- case STAT_MESGIN:
- info->scsi.phase = PHASE_MSGIN;
- outb(CMD_TRANSFERINFO, REG_CMD(info));
- break;
-
- case STAT_COMMAND: /* command phase */
- fas216_send_command(info);
- info->scsi.phase = PHASE_SELECTION;
- break;
-
- default:
- printk("scsi%d.%c: bus phase %s after %s?\n",
- info->host->host_no, fas216_target(info),
- fas216_bus_phase(stat),
- fas216_drv_phase(info));
- }
- break;
+ printk(KERN_ERR "scsi%d.%c: "
+ "target trying to receive more command bytes\n",
+ info->host->host_no, fas216_target(info));
+ outb(CMD_SETATN, REG_CMD(info));
+ outb(15, REG_STCL(info));
+ outb(0, REG_STCM(info));
+ outb(0, REG_STCH(info));
+ outb(CMD_PADBYTES | CMD_WITHDMA, REG_CMD(info));
+ msgqueue_flush(&info->scsi.msgs);
+ msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
+ return;
+
+ /* Selection -> Message Out */
+ case STATE(STAT_MESGOUT, PHASE_SELSTEPS):
+ case STATE(STAT_MESGOUT, PHASE_MSGOUT): /* Message Out -> Message Out */
+ /* If we get another message out phase, this
+ * usually means some parity error occurred.
+ * Resend complete set of messages. If we have
+ * more than 1 byte to send, we need to assert
+ * ATN again.
+ */
+ if (msgqueue_msglength(&info->scsi.msgs) > 1)
+ outb(CMD_SETATN, REG_CMD(info));
- case PHASE_MSGOUT:
- if (!fas216_busservice_messageout(info, stat))
- printk("scsi%d.%c: bus phase %s instead of message out?\n",
- info->host->host_no, fas216_target(info),
- fas216_bus_phase(stat));
- break;
+ fas216_send_messageout(info, 0);
+ return;
+ }
- case PHASE_DISCONNECT:
- printk("scsi%d.%c: disconnect message received, but bus service %s?\n",
+ if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) {
+ printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n",
info->host->host_no, fas216_target(info),
fas216_bus_phase(stat));
+ msgqueue_flush(&info->scsi.msgs);
outb(CMD_SETATN, REG_CMD(info));
msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
- info->scsi.phase = PHASE_MSGOUT;
+ info->scsi.phase = PHASE_MSGOUT_EXPECT;
info->scsi.aborting = 1;
outb(CMD_TRANSFERINFO, REG_CMD(info));
- break;
-
- default:
- printk("scsi%d.%c: internal phase %s for bus service?"
- " What do I do with this?\n",
- info->host->host_no, fas216_target(info),
- fas216_drv_phase(info));
+ return;
}
- break;
+ printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n",
+ info->host->host_no, fas216_target(info),
+ fas216_bus_phase(stat),
+ fas216_drv_phase(info));
+ print_debug_list();
+ return;
default:
printk("scsi%d.%c: bus service at step %d?\n",
info->host->host_no, fas216_target(info),
ssr & IS_BITS);
+ print_debug_list();
}
}
case PHASE_MSGIN: /* message in phase */
case PHASE_RECONNECTED: /* reconnected command */
if ((stat & STAT_BUSMASK) == STAT_MESGIN) {
+ info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
fas216_message(info);
break;
}
if (stat & STAT_INT) {
if (isr & INST_BUSRESET)
- printk("scsi%d.H: fas216: bus reset detected\n", instance->host_no);
- else if (isr & INST_ILLEGALCMD)
+ printk(KERN_DEBUG "scsi%d.H: bus reset detected\n", instance->host_no);
+ else if (isr & INST_ILLEGALCMD) {
printk(KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no);
- else if (isr & INST_DISCONNECT)
+ fas216_dumpstate(info);
+ } else if (isr & INST_DISCONNECT)
fas216_disconnect_intr(info);
else if (isr & INST_RESELECTED) /* reselected */
fas216_reselected_intr(info);
static void fas216_kick(FAS216_Info *info)
{
Scsi_Cmnd *SCpnt;
- int i, msglen, from_queue = 0;
+ int tot_msglen, from_queue = 0;
fas216_checkmagic(info, "fas216_kick");
if (from_queue) {
#ifdef SCSI2_TAG
- if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE) {
+ if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE &&
+ SCpnt->cmnd[0] != INQUIRY) {
SCpnt->device->current_tag += 1;
if (SCpnt->device->current_tag == 0)
SCpnt->device->current_tag = 1;
/* build outgoing message bytes */
msgqueue_flush(&info->scsi.msgs);
+
if (info->device[SCpnt->target].disconnect_ok)
msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(1, SCpnt->lun));
else
if (SCpnt->tag)
msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag);
- /* add synchronous negociation */
- if (SCpnt->cmnd[0] == REQUEST_SENSE &&
- info->device[SCpnt->target].negstate == syncneg_start) {
- info->device[SCpnt->target].negstate = syncneg_sent;
+#ifdef SCSI2_WIDE
+ if (info->device[SCpnt->target].wide_state == neg_wait) {
+ info->device[SCpnt->target].wide_state = neg_inprogress;
+ msgqueue_addmsg(&info->scsi.msgs, 4,
+ EXTENDED_MESSAGE, 2, EXTENDED_WDTR,
+ info->ifcfg.wide_max_size);
+ }
+#ifdef SCSI2_SYNC
+ else
+#endif
+#endif
+#ifdef SCSI2_SYNC
+ if ((info->device[SCpnt->target].sync_state == neg_wait ||
+ info->device[SCpnt->target].sync_state == neg_complete) &&
+ (SCpnt->cmnd[0] == REQUEST_SENSE ||
+ SCpnt->cmnd[0] == INQUIRY)) {
+ info->device[SCpnt->target].sync_state = neg_inprogress;
msgqueue_addmsg(&info->scsi.msgs, 5,
EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
1000 / info->ifcfg.clockrate,
info->ifcfg.sync_max_depth);
}
+#endif
/* following what the ESP driver says */
outb(0, REG_STCL(info));
/* synchronous transfers */
fas216_set_sync(info, SCpnt->target);
- msglen = msgqueue_msglength(&info->scsi.msgs);
+ tot_msglen = msgqueue_msglength(&info->scsi.msgs);
- if (msglen == 1 || msglen == 3) {
+ if (tot_msglen == 1 || tot_msglen == 3) {
/*
* We have an easy message length to send...
*/
- char *msg;
+ struct message *msg;
+ int msgnr = 0, i;
+
+ info->scsi.phase = PHASE_SELSTEPS;
/* load message bytes */
- while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
- for (i = 0; i < msglen; i++)
- outb(msg[i], REG_FF(info));
+ while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+ for (i = 0; i < msg->length; i++)
+ outb(msg->msg[i], REG_FF(info));
+ msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF);
}
/* load command */
for (i = 0; i < SCpnt->cmd_len; i++)
outb(SCpnt->cmnd[i], REG_FF(info));
- if (msglen == 1)
+ if (tot_msglen == 1)
outb(CMD_SELECTATN, REG_CMD(info));
else
outb(CMD_SELECTATN3, REG_CMD(info));
* We have an unusual number of message bytes to send.
* Load first byte into fifo, and issue SELECT with ATN and
* stop steps.
- * Note: we only peek at t his message - we need the rest
- * later on!
*/
- int thismsg;
- char *msg = msgqueue_peeknextmsg(&info->scsi.msgs, &thismsg);
+ struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0);
- if (!msg || thismsg < 1)
- printk(KERN_CRIT "scsi%d.%c: no message to send, but %d bytes\n",
- info->host->host_no, fas216_target(info), msglen);
- else
- outb(msg[0], REG_FF(info));
+ outb(msg->msg[0], REG_FF(info));
+ msg->fifo = 1;
outb(CMD_SELECTATNSTOP, REG_CMD(info));
}
/*
* In theory, this should not happen, but just in case it does.
*/
- if (info->scsi.SCp.ptr && result == DID_OK) {
+ if (info->scsi.SCp.ptr &&
+ info->scsi.SCp.this_residual &&
+ result == DID_OK) {
switch (SCpnt->cmnd[0]) {
case INQUIRY:
case START_STOP:
case READ_CAPACITY:
+ case TEST_UNIT_READY:
+ case MODE_SENSE:
break;
default:
int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+ unsigned long flags;
fas216_checkmagic(info, "fas216_queue_command");
info->stats.queues += 1;
SCpnt->tag = 0;
- if (info->scsi.irq != NO_IRQ) {
- unsigned long flags;
-
- /* add command into execute queue and let it complete under
- * the drivers interrupts.
- */
- if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) {
- SCpnt->result = DID_ERROR << 16;
- done(SCpnt);
- }
- save_flags_cli(flags);
- if (!info->SCpnt || info->scsi.disconnectable)
- fas216_kick(info);
- restore_flags(flags);
- } else {
- /* no interrupts to rely on - we'll have to handle the
- * command ourselves. For now, we give up.
- */
+ /* add command into execute queue and let it complete under
+ * whatever scheme we're using.
+ */
+ if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) {
SCpnt->result = DID_ERROR << 16;
done(SCpnt);
}
+ save_flags_cli(flags);
+ if (!info->SCpnt || info->scsi.disconnectable)
+ fas216_kick(info);
+ restore_flags(flags);
+
return 0;
}
/*
* This wastes time, since we can't return until the command is
- * complete. We can't seep either since we may get re-entered!
+ * complete. We can't sleep either since we may get re-entered!
* However, we must re-enable interrupts, or else we'll be
* waiting forever.
*/
save_flags(flags);
sti();
- while (!info->internal_done)
- barrier();
+ while (!info->internal_done) {
+ /*
+ * If we don't have an IRQ, then we must
+ * poll the card for it's interrupt, and
+ * use that to call this driver's interrupt
+ * routine. That way, we keep the command
+ * progressing.
+ */
+ if (info->scsi.irq == NO_IRQ) {
+ sti();
+ while (!(inb(REG_STAT(info)) & STAT_INT));
+ cli();
+ fas216_intr(info->host);
+ }
+ }
restore_flags(flags);
return FAILED;
}
+enum res_abort { res_not_running, res_success, res_success_clear, res_snooze };
+
+/*
+ * Prototype: enum res_abort fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+ * Purpose : abort a command on this host
+ * Params : SCpnt - command to abort
+ * Returns : abort status
+ */
+static enum res_abort
+fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+ enum res_abort res = res_not_running;
+
+ if (queue_removecmd(&info->queues.issue, SCpnt)) {
+ /*
+ * The command was on the issue queue, and has not been
+ * issued yet. We can remove the command from the queue,
+ * and acknowledge the abort. Neither the devices nor the
+ * interface know about the command.
+ */
+ printk("on issue queue ");
+
+ res = res_success;
+ } else if (queue_removecmd(&info->queues.disconnected, SCpnt)) {
+ /*
+ * The command was on the disconnected queue. Simply
+ * acknowledge the abort condition, and when the target
+ * reconnects, we will give it an ABORT message. The
+ * target should then disconnect, and we will clear
+ * the busylun bit.
+ */
+ printk("on disconnected queue ");
+
+ res = res_success;
+ } else if (info->SCpnt == SCpnt) {
+ unsigned long flags;
+
+ printk("executing ");
+
+ save_flags(flags);
+ cli();
+ switch (info->scsi.phase) {
+ /*
+ * If the interface is idle, and the command is 'disconnectable',
+ * then it is the same as on the disconnected queue. We simply
+ * remove all traces of the command. When the target reconnects,
+ * we will give it an ABORT message since the command could not
+ * be found. When the target finally disconnects, we will clear
+ * the busylun bit.
+ */
+ case PHASE_IDLE:
+ if (info->scsi.disconnectable) {
+ info->scsi.disconnectable = 0;
+ info->SCpnt = NULL;
+ res = res_success;
+ }
+ break;
+
+ /*
+ * If the command has connected and done nothing futher,
+ * simply force a disconnect. We also need to clear the
+ * busylun bit.
+ */
+ case PHASE_SELECTION:
+// info->SCpnt = NULL;
+// res = res_success_clear;
+// break;
+
+ default:
+ res = res_snooze;
+ break;
+ }
+ restore_flags(flags);
+ } else if (info->origSCpnt == SCpnt) {
+ /*
+ * The command will be executed next, but a command
+ * is currently using the interface. This is similar to
+ * being on the issue queue, except the busylun bit has
+ * been set.
+ */
+ info->origSCpnt = NULL;
+ printk("waiting for execution ");
+ res = res_success_clear;
+ } else
+ printk("unknown ");
+
+ return res;
+}
+
/* Function: int fas216_abort(Scsi_Cmnd *SCpnt)
* Purpose : abort a command if something horrible happens.
* Params : SCpnt - Command that is believed to be causing a problem.
print_debug_list();
fas216_dumpstate(info);
fas216_dumpinfo(info);
- printk(KERN_WARNING "scsi%d: fas216_abort: ", info->host->host_no);
- do {
- /* If command is waiting in the issue queue, then we can
- * simply remove the command and return abort status
- */
- if (queue_removecmd(&info->queues.issue, SCpnt)) {
- SCpnt->result = DID_ABORT << 16;
- SCpnt->scsi_done(SCpnt);
- printk("command on issue queue");
- result = SCSI_ABORT_SUCCESS;
- break;
- }
+ printk(KERN_WARNING "scsi%d: abort ", info->host->host_no);
- /* If the command is on the disconencted queue, we need to
- * reconnect to the device
- */
- if (queue_cmdonqueue(&info->queues.disconnected, SCpnt))
- printk("command on disconnected queue");
+ switch (fas216_do_abort(info, SCpnt)) {
+ /*
+ * We managed to find the command and cleared it out.
+ * We do not expect the command to be executing on the
+ * target, but we have set the busylun bit.
+ */
+ case res_success_clear:
+ printk("clear ");
+ clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
- /* If the command is connected, we need to flag that the
- * command needs to be aborted
- */
- if (info->SCpnt == SCpnt)
- printk("command executing");
+ /*
+ * We found the command, and cleared it out. Either
+ * the command is still known to be executing on the
+ * target, or the busylun bit is not set.
+ */
+ case res_success:
+ printk("success\n");
+ SCpnt->result = DID_ABORT << 16;
+ SCpnt->scsi_done(SCpnt);
+ result = SCSI_ABORT_SUCCESS;
+ break;
- /* If the command is pending for execution, then again
- * this is simple - we remove it and report abort status
- */
- if (info->origSCpnt == SCpnt) {
- info->origSCpnt = NULL;
- SCpnt->result = DID_ABORT << 16;
- SCpnt->scsi_done(SCpnt);
- printk("command waiting for execution");
- result = SCSI_ABORT_SUCCESS;
- break;
- }
- } while (0);
+ /*
+ * We did find the command, but unfortunately we couldn't
+ * unhook it from ourselves. Wait some more, and if it
+ * still doesn't complete, reset the interface.
+ */
+ case res_snooze:
+ printk("snooze\n");
+ result = SCSI_ABORT_SNOOZE;
+ break;
- printk("\n");
+ /*
+ * The command could not be found (either because it completed,
+ * or it got dropped.
+ */
+ default:
+ case res_not_running:
+ result = SCSI_ABORT_SNOOZE;
+ printk("not running\n");
+ break;
+ }
return result;
}
*/
static void fas216_reset_state(FAS216_Info *info)
{
- syncneg_t negstate;
+ neg_t sync_state, wide_state;
int i;
fas216_checkmagic(info, "fas216_reset_state");
info->scsi.reconnected.lun = 0;
info->scsi.reconnected.tag = 0;
info->scsi.disconnectable = 0;
- info->scsi.last_message = 0;
info->scsi.aborting = 0;
info->scsi.phase = PHASE_IDLE;
- info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
+ info->scsi.async_stp =
+ fas216_syncperiod(info, info->ifcfg.asyncperiod);
+
+ if (info->ifcfg.wide_max_size == 0)
+ wide_state = neg_invalid;
+ else
+#ifdef SCSI2_WIDE
+ wide_state = neg_wait;
+#else
+ wide_state = neg_invalid;
+#endif
if (info->host->dma_channel == NO_DMA || !info->dma.setup)
- negstate = syncneg_invalid;
+ sync_state = neg_invalid;
else
#ifdef SCSI2_SYNC
- negstate = syncneg_start;
+ sync_state = neg_wait;
#else
- negstate = syncneg_invalid;
+ sync_state = neg_invalid;
#endif
for (i = 0; i < 8; i++) {
- info->device[i].disconnect_ok = info->ifcfg.disconnect_ok;
- info->device[i].negstate = negstate;
- info->device[i].period = info->ifcfg.asyncperiod / 4;
- info->device[i].stp = info->scsi.async_stp;
- info->device[i].sof = 0;
+ info->device[i].disconnect_ok = info->ifcfg.disconnect_ok;
+ info->device[i].sync_state = sync_state;
+ info->device[i].wide_state = wide_state;
+ info->device[i].period = info->ifcfg.asyncperiod / 4;
+ info->device[i].stp = info->scsi.async_stp;
+ info->device[i].sof = 0;
+ info->device[i].wide_xfer = 0;
}
}
info->stats.resets += 1;
print_debug_list();
- printk(KERN_WARNING "scsi%d: fas216_reset: ", info->host->host_no);
+ printk(KERN_WARNING "scsi%d: reset ", info->host->host_no);
if (SCpnt)
- printk(" for target %d ", SCpnt->target);
+ printk("for target %d ", SCpnt->target);
+
+ printk("\n");
outb(info->scsi.cfg[3], REG_CNTL3(info));
SCpnt->scsi_done(SCpnt);
}
- printk("\n");
-
return result | SCSI_RESET_SUCCESS;
}
return 0;
}
+int fas216_print_stats(FAS216_Info *info, char *buffer)
+{
+ return sprintf(buffer,
+ "Queued commands: %-10u Issued commands: %-10u\n"
+ "Done commands : %-10u Reads : %-10u\n"
+ "Writes : %-10u Others : %-10u\n"
+ "Disconnects : %-10u Aborts : %-10u\n"
+ "Resets : %-10u\n",
+ info->stats.queues, info->stats.removes,
+ info->stats.fins, info->stats.reads,
+ info->stats.writes, info->stats.miscs,
+ info->stats.disconnects, info->stats.aborts,
+ info->stats.resets);
+}
+
+int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer)
+{
+ struct fas216_device *dev = &info->device[scd->id];
+ int len = 0;
+ char *p;
+
+ proc_print_scsidevice(scd, buffer, &len, 0);
+ p = buffer + len;
+
+ p += sprintf(p, " Extensions: ");
+
+ if (scd->tagged_supported)
+ p += sprintf(p, "TAG %sabled [%d] ",
+ scd->tagged_queue ? "en" : "dis",
+ scd->current_tag);
+
+ p += sprintf(p, "\n Transfers : %d-bit ",
+ 8 << dev->wide_xfer);
+
+ if (dev->sof)
+ p += sprintf(p, "sync offset %d, %d ns\n",
+ dev->sof, dev->period * 4);
+ else
+ p += sprintf(p, "async\n");
+
+ return p - buffer;
+}
+
EXPORT_SYMBOL(fas216_init);
EXPORT_SYMBOL(fas216_abort);
EXPORT_SYMBOL(fas216_reset);
EXPORT_SYMBOL(fas216_eh_device_reset);
EXPORT_SYMBOL(fas216_eh_bus_reset);
EXPORT_SYMBOL(fas216_eh_host_reset);
-
+EXPORT_SYMBOL(fas216_print_stats);
+EXPORT_SYMBOL(fas216_print_device);
#ifdef MODULE
int init_module(void)
#define CMD_TRANSFERINFO 0x10
#define CMD_INITCMDCOMPLETE 0x11
#define CMD_MSGACCEPTED 0x12
+#define CMD_PADBYTES 0x18
#define CMD_SETATN 0x1a
#define CMD_RSETATN 0x1b
typedef enum {
PHASE_IDLE, /* we're not planning on doing anything */
PHASE_SELECTION, /* selecting a device */
+ PHASE_SELSTEPS, /* selection with command steps */
+ PHASE_COMMAND, /* command sent */
PHASE_MESSAGESENT, /* selected, and we're sending cmd */
PHASE_RECONNECTED, /* reconnected */
PHASE_DATAOUT, /* data out to device */
PHASE_DATAIN, /* data in from device */
PHASE_MSGIN, /* message in from device */
- PHASE_MSGOUT, /* message out to device */
- PHASE_AFTERMSGOUT, /* after message out phase */
+ PHASE_MSGIN_DISCONNECT, /* disconnecting from bus */
+ PHASE_MSGOUT, /* after message out phase */
+ PHASE_MSGOUT_EXPECT, /* expecting message out */
PHASE_STATUS, /* status from device */
- PHASE_DISCONNECT, /* disconnecting from bus */
PHASE_DONE /* Command complete */
} phase_t;
} fasdmatype_t;
typedef enum {
- syncneg_start, /* Negociate with device for Sync xfers */
- syncneg_sent, /* Sync Xfer negociation sent */
- syncneg_complete, /* Sync Xfer complete */
- syncneg_invalid /* Sync Xfer not supported */
-} syncneg_t;
+ neg_wait, /* Negociate with device */
+ neg_inprogress, /* Negociation sent */
+ neg_complete, /* Negociation complete */
+ neg_targcomplete, /* Target completed negociation */
+ neg_invalid /* Negociation not supported */
+} neg_t;
#define MAGIC 0x441296bdUL
+#define NR_MSGS 8
typedef struct {
unsigned long magic_start;
MsgQueue_t msgs; /* message queue for connected device */
unsigned int async_stp; /* Async transfer STP value */
- unsigned short last_message; /* last message to be sent */
+ unsigned char msgin_fifo; /* bytes in fifo at time of message in */
unsigned char disconnectable:1; /* this command can be disconnected */
unsigned char aborting:1; /* aborting command */
unsigned char clockrate; /* clock rate of FAS device (MHz) */
unsigned char select_timeout; /* timeout (R5) */
unsigned char sync_max_depth; /* Synchronous xfer max fifo depth */
+ unsigned char wide_max_size; /* Maximum wide transfer size */
unsigned char cntl3; /* Control Reg 3 */
unsigned int asyncperiod; /* Async transfer period (ns) */
unsigned int disconnect_ok:1; /* Disconnects allowed? */
} queues;
/* per-device info */
- struct {
+ struct fas216_device {
unsigned char disconnect_ok:1; /* device can disconnect */
- unsigned int period; /* sync xfer period (*4ns) */
+ unsigned char period; /* sync xfer period in (*4ns) */
unsigned char stp; /* synchronous transfer period */
unsigned char sof; /* synchronous offset register */
- syncneg_t negstate; /* synchronous transfer mode */
+ unsigned char wide_xfer; /* currently negociated wide transfer */
+ neg_t sync_state; /* synchronous transfer mode */
+ neg_t wide_state; /* wide transfer mode */
} device[8];
unsigned char busyluns[8]; /* array of bits indicating LUNs busy */
*/
extern int fas216_release (struct Scsi_Host *instance);
+extern int fas216_print_stats(FAS216_Info *info, char *buffer);
+extern int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer);
+
/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt)
* Purpose : abort this command
* Params : SCpnt - command to abort
int length = 0;
for (mq = msgq->qe; mq; mq = mq->next)
- length += mq->length;
+ length += mq->msg.length;
return length;
}
/*
- * Function: char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length)
- * Purpose : return a message & its length
+ * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
+ * Purpose : return a message
* Params : msgq - queue to obtain message from
- * length - pointer to int for message length
+ * : msgno - message number
* Returns : pointer to message string, or NULL
*/
-char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length)
+struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
{
struct msgqueue_entry *mq;
- if ((mq = msgq->qe) != NULL) {
- msgq->qe = mq->next;
- mqe_free(msgq, mq);
- *length = mq->length;
- }
-
- return mq ? mq->msg : NULL;
-}
-
-/*
- * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
- * Purpose : return next message & length without removing it from the list
- * Params : msgq - queue to obtain message from
- * : length - pointer to int for message length
- * Returns : pointer to message string, or NULL
- */
-char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
-{
- struct msgqueue_entry *mq = msgq->qe;
-
- *length = mq ? mq->length : 0;
+ for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--);
- return mq ? mq->msg : NULL;
+ return mq ? &mq->msg : NULL;
}
/*
va_start(ap, length);
for (i = 0; i < length; i++)
- mq->msg[i] = va_arg(ap, unsigned char);
+ mq->msg.msg[i] = va_arg(ap, unsigned char);
va_end(ap);
- mq->length = length;
+ mq->msg.length = length;
+ mq->msg.fifo = 0;
mq->next = NULL;
mqp = &msgq->qe;
EXPORT_SYMBOL(msgqueue_initialise);
EXPORT_SYMBOL(msgqueue_free);
EXPORT_SYMBOL(msgqueue_msglength);
-EXPORT_SYMBOL(msgqueue_getnextmsg);
-EXPORT_SYMBOL(msgqueue_peeknextmsg);
+EXPORT_SYMBOL(msgqueue_getmsg);
EXPORT_SYMBOL(msgqueue_addmsg);
EXPORT_SYMBOL(msgqueue_flush);
#ifndef MSGQUEUE_H
#define MSGQUEUE_H
-struct msgqueue_entry {
+struct message {
char msg[8];
int length;
+ int fifo;
+};
+
+struct msgqueue_entry {
+ struct message msg;
struct msgqueue_entry *next;
};
} MsgQueue_t;
/*
- * Function: void msgqueue_initialise (MsgQueue_t *msgq)
+ * Function: void msgqueue_initialise(MsgQueue_t *msgq)
* Purpose : initialise a message queue
* Params : msgq - queue to initialise
*/
-extern void msgqueue_initialise (MsgQueue_t *msgq);
+extern void msgqueue_initialise(MsgQueue_t *msgq);
/*
- * Function: void msgqueue_free (MsgQueue_t *msgq)
+ * Function: void msgqueue_free(MsgQueue_t *msgq)
* Purpose : free a queue
* Params : msgq - queue to free
*/
-extern void msgqueue_free (MsgQueue_t *msgq);
+extern void msgqueue_free(MsgQueue_t *msgq);
/*
- * Function: int msgqueue_msglength (MsgQueue_t *msgq)
+ * Function: int msgqueue_msglength(MsgQueue_t *msgq)
* Purpose : calculate the total length of all messages on the message queue
* Params : msgq - queue to examine
* Returns : number of bytes of messages in queue
*/
-extern int msgqueue_msglength (MsgQueue_t *msgq);
+extern int msgqueue_msglength(MsgQueue_t *msgq);
/*
- * Function: char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length)
+ * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno)
* Purpose : return a message & its length
* Params : msgq - queue to obtain message from
- * length - pointer to int for message length
- * Returns : pointer to message string, or NULL
- */
-extern char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length);
-
-/*
- * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length)
- * Purpose : return next message & length without removing it from the list
- * Params : msgq - queue to obtain message from
- * : length - pointer to int for message length
+ * : msgno - message number
* Returns : pointer to message string, or NULL
*/
-extern char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length);
+extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno);
/*
- * Function: int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...)
+ * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...)
* Purpose : add a message onto a message queue
* Params : msgq - queue to add message on
* length - length of message
* ... - message bytes
* Returns : != 0 if successful
*/
-extern int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...);
+extern int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...);
/*
- * Function: void msgqueue_flush (MsgQueue_t *msgq)
+ * Function: void msgqueue_flush(MsgQueue_t *msgq)
* Purpose : flush all messages from message queue
* Params : msgq - queue to flush
*/
-extern void msgqueue_flush (MsgQueue_t *msgq);
+extern void msgqueue_flush(MsgQueue_t *msgq);
#endif
powertecscsi_irqenable,
powertecscsi_irqdisable,
NULL,
+ NULL,
+ NULL,
NULL
};
info->info.ifcfg.select_timeout = 255;
info->info.ifcfg.asyncperiod = POWERTEC_ASYNC_PERIOD;
info->info.ifcfg.sync_max_depth = POWERTEC_SYNC_DEPTH;
- info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+ info->info.ifcfg.cntl3 = /*CNTL3_BS8 |*/ CNTL3_FASTSCSI | CNTL3_FASTCLK;
info->info.ifcfg.disconnect_ok = 1;
+ info->info.ifcfg.wide_max_size = 0;
info->info.dma.setup = powertecscsi_dma_setup;
info->info.dma.pseudo = NULL;
info->info.dma.stop = powertecscsi_dma_stop;
host->io_port, host->irq, host->dma_channel,
info->info.scsi.type, info->control.terms ? "on" : "off");
- pos += sprintf(buffer+pos,
- "Queued commands: %-10u Issued commands: %-10u\n"
- "Done commands : %-10u Reads : %-10u\n"
- "Writes : %-10u Others : %-10u\n"
- "Disconnects : %-10u Aborts : %-10u\n"
- "Resets : %-10u\n",
- info->info.stats.queues, info->info.stats.removes,
- info->info.stats.fins, info->info.stats.reads,
- info->info.stats.writes, info->info.stats.miscs,
- info->info.stats.disconnects, info->info.stats.aborts,
- info->info.stats.resets);
+ pos += fas216_print_stats(&info->info, buffer + pos);
- pos += sprintf (buffer+pos, "\nAttached devices:%s\n", host->host_queue ? "" : " none");
+ pos += sprintf (buffer+pos, "\nAttached devices:\n");
for (scd = host->host_queue; scd; scd = scd->next) {
- int len;
-
- proc_print_scsidevice (scd, buffer, &len, pos);
- pos += len;
- pos += sprintf (buffer+pos, "Extensions: ");
- if (scd->tagged_supported)
- pos += sprintf (buffer+pos, "TAG %sabled [%d] ",
- scd->tagged_queue ? "en" : "dis",
- scd->current_tag);
- pos += sprintf (buffer+pos, "\n");
+ pos += fas216_print_device(&info->info, scd, buffer + pos);
if (pos + begin < offset) {
begin += pos;
q->magic = QUEUE_MAGIC_FREE;
q->SCpnt = NULL;
}
+ q -= 1;
q->next = NULL;
}
bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
bool ' Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
- bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA
+ bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
+ if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
+ bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO
+ fi
bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210
- if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then
- bool ' Use DMA by default when available' CONFIG_IDEDMA_AUTO
- fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
bool ' Intel PIIXn chipsets support (EXPERIMENTAL)' CONFIG_BLK_DEV_PIIX
- if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then
+ if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then
bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
bool ' VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586
fi
fi
fi
- if [ "$CONFIG_PPC" = "y" ]; then
+ if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
fi
fi
if [ "$CONFIG_BLK_DEV_IDE_PMAC" != "n" ]; then
bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC
if [ "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" ]; then
- define_bool CONFIG_BLK_DEV_IDEDMA y
bool ' Use DMA by default' CONFIG_PMAC_IDEDMA_AUTO
fi
fi
fi
+ if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+ bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE
+ if [ "$CONFIG_BLK_DEV_IDE_ICSIDE" = "y" ]; then
+ bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS
+ if [ "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
+ bool ' Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO
+ fi
+ fi
+ bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE
+ fi
+ if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \
+ "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \
+ "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
+ define_bool CONFIG_BLK_DEV_IDEDMA y
+ fi
bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS
if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
comment 'Note: most of these also require special kernel boot parameters'
"$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \
"$CONFIG_BLK_DEV_CY82C693" = "y" -o \
"$CONFIG_BLK_DEV_HPT343" = "y" -o \
- "$CONFIG_BLK_DEV_PIIX" = "y" ]; then
+ "$CONFIG_BLK_DEV_PIIX" = "y" -o \
+ "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then
define_bool CONFIG_BLK_DEV_IDE_MODES y
else
define_bool CONFIG_BLK_DEV_IDE_MODES n
--- /dev/null
+/*
+ * linux/drivers/block/icside.c
+ *
+ * Copyright (c) 1996,1997 Russell King.
+ *
+ * Changelog:
+ * 08-Jun-1996 RMK Created
+ * 12-Sep-1997 RMK Added interrupt enable/disable
+ * 17-Apr-1999 RMK Added support for V6 EASI
+ * 22-May-1999 RMK Added support for V6 DMA
+ */
+
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/dma.h>
+#include <asm/ecard.h>
+#include <asm/io.h>
+
+/*
+ * Maximum number of interfaces per card
+ */
+#define MAX_IFS 2
+
+#define ICS_IDENT_OFFSET 0x8a0
+
+#define ICS_ARCIN_V5_INTRSTAT 0x000
+#define ICS_ARCIN_V5_INTROFFSET 0x001
+#define ICS_ARCIN_V5_IDEOFFSET 0xa00
+#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0
+#define ICS_ARCIN_V5_IDESTEPPING 4
+
+#define ICS_ARCIN_V6_IDEOFFSET_1 0x800
+#define ICS_ARCIN_V6_INTROFFSET_1 0x880
+#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4
+#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0
+#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00
+#define ICS_ARCIN_V6_INTROFFSET_2 0xc80
+#define ICS_ARCIN_V6_INTRSTAT_2 0xca4
+#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0
+#define ICS_ARCIN_V6_IDESTEPPING 4
+
+struct cardinfo {
+ unsigned int dataoffset;
+ unsigned int ctrloffset;
+ unsigned int stepping;
+};
+
+static struct cardinfo icside_cardinfo_v5 = {
+ ICS_ARCIN_V5_IDEOFFSET,
+ ICS_ARCIN_V5_IDEALTOFFSET,
+ ICS_ARCIN_V5_IDESTEPPING
+};
+
+static struct cardinfo icside_cardinfo_v6_1 = {
+ ICS_ARCIN_V6_IDEOFFSET_1,
+ ICS_ARCIN_V6_IDEALTOFFSET_1,
+ ICS_ARCIN_V6_IDESTEPPING
+};
+
+static struct cardinfo icside_cardinfo_v6_2 = {
+ ICS_ARCIN_V6_IDEOFFSET_2,
+ ICS_ARCIN_V6_IDEALTOFFSET_2,
+ ICS_ARCIN_V6_IDESTEPPING
+};
+
+static const card_ids icside_cids[] = {
+ { MANU_ICS, PROD_ICS_IDE },
+ { MANU_ICS2, PROD_ICS2_IDE },
+ { 0xffff, 0xffff }
+};
+
+typedef enum {
+ ics_if_unknown,
+ ics_if_arcin_v5,
+ ics_if_arcin_v6
+} iftype_t;
+
+/* ---------------- Version 5 PCB Support Functions --------------------- */
+/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose : enable interrupts from card
+ */
+static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+ unsigned int memc_port = (unsigned int)ec->irq_data;
+ outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose : disable interrupts from card
+ */
+static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+ unsigned int memc_port = (unsigned int)ec->irq_data;
+ inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+static const expansioncard_ops_t icside_ops_arcin_v5 = {
+ icside_irqenable_arcin_v5,
+ icside_irqdisable_arcin_v5,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+/* ---------------- Version 6 PCB Support Functions --------------------- */
+/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose : enable interrupts from card
+ */
+static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+ unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+ outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+ outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose : disable interrupts from card
+ */
+static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+ unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+ inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
+ inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: icside_irqprobe(struct expansion_card *ec)
+ * Purpose : detect an active interrupt from card
+ */
+static int icside_irqpending_arcin_v6(struct expansion_card *ec)
+{
+ unsigned int ide_base_port = (unsigned int)ec->irq_data;
+
+ return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
+ inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
+}
+
+static const expansioncard_ops_t icside_ops_arcin_v6 = {
+ icside_irqenable_arcin_v6,
+ icside_irqdisable_arcin_v6,
+ icside_irqpending_arcin_v6,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* Prototype: icside_identifyif (struct expansion_card *ec)
+ * Purpose : identify IDE interface type
+ * Notes : checks the description string
+ */
+static iftype_t icside_identifyif (struct expansion_card *ec)
+{
+ unsigned int addr;
+ iftype_t iftype;
+ int id = 0;
+
+ iftype = ics_if_unknown;
+
+ addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
+
+ id = inb (addr) & 1;
+ id |= (inb (addr + 1) & 1) << 1;
+ id |= (inb (addr + 2) & 1) << 2;
+ id |= (inb (addr + 3) & 1) << 3;
+
+ switch (id) {
+ case 0: /* A3IN */
+ printk("icside: A3IN unsupported\n");
+ break;
+
+ case 1: /* A3USER */
+ printk("icside: A3USER unsupported\n");
+ break;
+
+ case 3: /* ARCIN V6 */
+ printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
+ iftype = ics_if_arcin_v6;
+ break;
+
+ case 15:/* ARCIN V5 (no id) */
+ printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
+ iftype = ics_if_arcin_v5;
+ break;
+
+ default:/* we don't know - complain very loudly */
+ printk("icside: ***********************************\n");
+ printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
+ printk("icside: ***********************************\n");
+ printk("icside: please report this to linux@arm.linux.org.uk\n");
+ printk("icside: defaulting to ARCIN V5\n");
+ iftype = ics_if_arcin_v5;
+ break;
+ }
+
+ return iftype;
+}
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+/*
+ * SG-DMA support.
+ *
+ * Similar to the BM-DMA, but we use the RiscPCs IOMD
+ * DMA controllers. There is only one DMA controller
+ * per card, which means that only one drive can be
+ * accessed at one time. NOTE! We do not inforce that
+ * here, but we rely on the main IDE driver spotting
+ * that both interfaces use the same IRQ, which should
+ * guarantee this.
+ *
+ * We are limited by the drives IOR/IOW pulse time.
+ * The closest that we can get to the requirements is
+ * a type C cycle for both mode 1 and mode 2. However,
+ * this does give a burst of 8MB/s.
+ *
+ * This has been tested with a couple of Conner
+ * Peripherals 1080MB CFS1081A drives, one on each
+ * interface, which deliver about 2MB/s each. I
+ * believe that this is limited by the lack of
+ * on-board drive cache.
+ */
+#define TABLE_SIZE 2048
+
+static int
+icside_build_dmatable(ide_drive_t *drive, int reading)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct buffer_head *bh = rq->bh;
+ unsigned long addr, size;
+ unsigned char *virt_addr;
+ unsigned int count = 0;
+ dmasg_t *sg = (dmasg_t *)HWIF(drive)->dmatable;
+
+ do {
+ if (bh == NULL) {
+ /* paging requests have (rq->bh == NULL) */
+ virt_addr = rq->buffer;
+ addr = virt_to_bus (virt_addr);
+ size = rq->nr_sectors << 9;
+ } else {
+ /* group sequential buffers into one large buffer */
+ virt_addr = bh->b_data;
+ addr = virt_to_bus (virt_addr);
+ size = bh->b_size;
+ while ((bh = bh->b_reqnext) != NULL) {
+ if ((addr + size) != virt_to_bus (bh->b_data))
+ break;
+ size += bh->b_size;
+ }
+ }
+
+ if (addr & 3) {
+ printk("%s: misaligned DMA buffer\n", drive->name);
+ return 0;
+ }
+
+ if (size) {
+ if (reading)
+ dma_cache_inv((unsigned int)virt_addr, size);
+ else
+ dma_cache_wback((unsigned int)virt_addr, size);
+ }
+
+ sg[count].address = addr;
+ sg[count].length = size;
+ if (++count >= (TABLE_SIZE / sizeof(dmasg_t))) {
+ printk("%s: DMA table too small\n", drive->name);
+ return 0;
+ }
+ } while (bh != NULL);
+
+ if (!count)
+ printk("%s: empty DMA table?\n", drive->name);
+
+ return count;
+}
+
+static int
+icside_config_drive(ide_drive_t *drive, int mode)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ int speed, err;
+
+ if (mode == 2) {
+ speed = XFER_MW_DMA_2;
+ drive->drive_data = 250;
+ } else {
+ speed = XFER_MW_DMA_1;
+ drive->drive_data = 250;
+ }
+
+ /*
+ * Don't use ide_wait_cmd here - it will
+ * attempt to set_geometry and recalibrate,
+ * but for some reason these don't work at
+ * this point (lost interrupt).
+ */
+ SELECT_DRIVE(hwif, drive);
+ OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
+ OUT_BYTE(speed, IDE_NSECTOR_REG);
+ OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
+ OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
+
+ err = ide_wait_stat(drive, DRIVE_READY,
+ BUSY_STAT|DRQ_STAT|ERR_STAT, WAIT_CMD);
+
+ if (err == 0) {
+ drive->id->dma_mword &= 0x00ff;
+ drive->id->dma_mword |= 256 << mode;
+ } else
+ drive->drive_data = 0;
+
+ return err;
+}
+
+static int
+icside_dma_check(ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ int autodma = hwif->autodma;
+
+ if (id && (id->capability & 1) && autodma) {
+ int dma_mode = 0;
+
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive))
+ return hwif->dmaproc(ide_dma_off, drive);
+
+ /* Enable DMA on any drive that has
+ * UltraDMA (mode 0/1/2) enabled
+ */
+ if (id->field_valid & 4 && id->dma_ultra & 7)
+ dma_mode = 2;
+
+ /* Enable DMA on any drive that has mode1
+ * or mode2 multiword DMA enabled
+ */
+ if (id->field_valid & 2 && id->dma_mword & 6)
+ dma_mode = id->dma_mword & 4 ? 2 : 1;
+
+ /* Consult the list of known "good" drives */
+ if (ide_dmaproc(ide_dma_good_drive, drive))
+ dma_mode = 1;
+
+ if (dma_mode && icside_config_drive(drive, dma_mode) == 0)
+ return hwif->dmaproc(ide_dma_on, drive);
+ }
+ return hwif->dmaproc(ide_dma_off_quietly, drive);
+}
+
+static int
+icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ int count, reading = 0;
+
+ switch (func) {
+ case ide_dma_check:
+ return icside_dma_check(drive);
+
+ case ide_dma_read:
+ reading = 1;
+ case ide_dma_write:
+ count = icside_build_dmatable(drive, reading);
+ if (!count)
+ return 1;
+ disable_dma(hwif->hw.dma);
+
+ /* Route the DMA signals to
+ * to the correct interface.
+ */
+ outb(hwif->select_data, hwif->config_data);
+
+ /* Select the correct timing
+ * for this drive
+ */
+ set_dma_speed(hwif->hw.dma, drive->drive_data);
+
+ set_dma_sg(hwif->hw.dma, (dmasg_t *)hwif->dmatable, count);
+ set_dma_mode(hwif->hw.dma, reading ? DMA_MODE_READ
+ : DMA_MODE_WRITE);
+
+ drive->waiting_for_dma = 1;
+ if (drive->media != ide_disk)
+ return 0;
+
+ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD);
+ OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA,
+ IDE_COMMAND_REG);
+
+ case ide_dma_begin:
+ enable_dma(hwif->hw.dma);
+ return 0;
+
+ case ide_dma_end:
+ drive->waiting_for_dma = 0;
+ disable_dma(hwif->hw.dma);
+ return get_dma_residue(hwif->hw.dma) != 0;
+
+ case ide_dma_test_irq:
+ return inb((unsigned long)hwif->hw.priv) & 1;
+
+ default:
+ return ide_dmaproc(func, drive);
+ }
+}
+
+static unsigned long
+icside_alloc_dmatable(void)
+{
+ static unsigned long dmatable;
+ static unsigned int leftover;
+ unsigned long table;
+
+ if (leftover < TABLE_SIZE) {
+#if PAGE_SIZE == TABLE_SIZE * 2
+ dmatable = __get_free_pages(GFP_KERNEL, 1);
+ leftover = PAGE_SIZE;
+#else
+ dmatable = kmalloc(TABLE_SIZE, GFP_KERNEL);
+ leftover = TABLE_SIZE;
+#endif
+ }
+
+ table = dmatable;
+ if (table) {
+ dmatable += TABLE_SIZE;
+ leftover -= TABLE_SIZE;
+ }
+
+ return table;
+}
+
+static int
+icside_setup_dma(ide_hwif_t *hwif, int autodma)
+{
+ unsigned long table = icside_alloc_dmatable();
+
+ printk(" %s: SG-DMA", hwif->name);
+
+ if (!table)
+ printk(" -- ERROR, unable to allocate DMA table\n");
+ else {
+ hwif->dmatable = (void *)table;
+ hwif->dmaproc = icside_dmaproc;
+ hwif->autodma = autodma;
+
+ printk(" capable%s\n", autodma ?
+ ", auto-enable" : "");
+ }
+
+ return hwif->dmatable != NULL;
+}
+#endif
+
+static ide_hwif_t *
+icside_find_hwif(unsigned long dataport)
+{
+ ide_hwif_t *hwif;
+ int index;
+
+ for (index = 0; index < MAX_HWIFS; ++index) {
+ hwif = &ide_hwifs[index];
+ if (hwif->hw.io_ports[IDE_DATA_OFFSET] == (ide_ioreg_t)dataport)
+ goto found;
+ }
+
+ for (index = 0; index < MAX_HWIFS; ++index) {
+ hwif = &ide_hwifs[index];
+ if (!hwif->hw.io_ports[IDE_DATA_OFFSET])
+ goto found;
+ }
+
+ return NULL;
+found:
+ return hwif;
+}
+
+static ide_hwif_t *
+icside_setup(unsigned long base, struct cardinfo *info, int irq)
+{
+ unsigned long port = base + info->dataoffset;
+ ide_hwif_t *hwif;
+
+ hwif = icside_find_hwif(base);
+ if (hwif) {
+ int i;
+
+ memset(&hwif->hw, 0, sizeof(hw_regs_t));
+
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+ hwif->hw.io_ports[i] = (ide_ioreg_t)port;
+ port += 1 << info->stepping;
+ }
+ hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset;
+ hwif->hw.irq = irq;
+ hwif->hw.dma = NO_DMA;
+ hwif->noprobe = 0;
+ hwif->chipset = ide_acorn;
+ }
+
+ return hwif;
+}
+
+static int icside_register_v5(struct expansion_card *ec, int autodma)
+{
+ unsigned long slot_port;
+ ide_hwif_t *hwif;
+
+ slot_port = ecard_address(ec, ECARD_MEMC, 0);
+
+ ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT);
+ ec->irqmask = 1;
+ ec->irq_data = (void *)slot_port;
+ ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5;
+
+ /*
+ * Be on the safe side - disable interrupts
+ */
+ inb(slot_port + ICS_ARCIN_V5_INTROFFSET);
+
+ hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq);
+
+ return hwif ? 0 : -1;
+}
+
+static int icside_register_v6(struct expansion_card *ec, int autodma)
+{
+ unsigned long slot_port, port;
+ ide_hwif_t *hwif, *mate;
+ int sel = 0;
+
+ slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST);
+ port = ecard_address(ec, ECARD_EASI, ECARD_FAST);
+
+ if (port == 0)
+ port = slot_port;
+ else
+ sel = 1 << 5;
+
+ outb(sel, slot_port);
+
+ ec->irq_data = (void *)port;
+ ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6;
+
+ /*
+ * Be on the safe side - disable interrupts
+ */
+ inb(port + ICS_ARCIN_V6_INTROFFSET_1);
+ inb(port + ICS_ARCIN_V6_INTROFFSET_2);
+
+ hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq);
+ mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA_ICS
+ if (ec->dma != NO_DMA) {
+ if (request_dma(ec->dma, hwif->name))
+ goto no_dma;
+
+ if (hwif) {
+ hwif->config_data = slot_port;
+ hwif->select_data = sel;
+ hwif->hw.dma = ec->dma;
+ hwif->hw.priv = (void *)
+ (port + ICS_ARCIN_V6_INTRSTAT_1);
+ hwif->channel = 0;
+ icside_setup_dma(hwif, autodma);
+ }
+ if (mate) {
+ mate->config_data = slot_port;
+ mate->select_data = sel | 1;
+ mate->hw.dma = ec->dma;
+ mate->hw.priv = (void *)
+ (port + ICS_ARCIN_V6_INTRSTAT_2);
+ mate->channel = 1;
+ icside_setup_dma(mate, autodma);
+ }
+ }
+#endif
+
+no_dma:
+ return hwif || mate ? 0 : -1;
+}
+
+int icside_init(void)
+{
+ int autodma = 0;
+
+#ifdef CONFIG_IDEDMA_ICS_AUTO
+ autodma = 1;
+#endif
+
+ ecard_startfind ();
+
+ do {
+ struct expansion_card *ec;
+ int result;
+
+ ec = ecard_find(0, icside_cids);
+ if (ec == NULL)
+ break;
+
+ ecard_claim(ec);
+
+ switch (icside_identifyif(ec)) {
+ case ics_if_arcin_v5:
+ result = icside_register_v5(ec, autodma);
+ break;
+
+ case ics_if_arcin_v6:
+ result = icside_register_v6(ec, autodma);
+ break;
+
+ default:
+ result = -1;
+ break;
+ }
+
+ if (result)
+ ecard_release(ec);
+ } while (1);
+
+ return 0;
+}
--- /dev/null
+/*
+ * linux/arch/arm/drivers/block/ide-rapide.c
+ *
+ * Copyright (c) 1996-1998 Russell King.
+ *
+ * Changelog:
+ * 08-06-1996 RMK Created
+ * 13-04-1998 RMK Added manufacturer and product IDs
+ */
+
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/ide.h>
+
+#include <asm/ecard.h>
+
+static const card_ids rapide_cids[] = {
+ { MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 },
+ { 0xffff, 0xffff }
+};
+
+static struct expansion_card *ec[MAX_ECARDS];
+static int result[MAX_ECARDS];
+
+static inline int rapide_register(struct expansion_card *ec)
+{
+ unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
+ hw_regs_t hw;
+
+ int i;
+
+ memset(&hw, 0, sizeof(hw));
+
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+ hw.io_ports[i] = (ide_ioreg_t)port;
+ port += 1 << 4;
+ }
+ hw.io_ports[IDE_CONTROL_OFFSET] = port + 0x206;
+ hw.irq = ec->irq;
+
+ return ide_register_hw(&hw, NULL);
+}
+
+int rapide_init(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_ECARDS; i++)
+ ec[i] = NULL;
+
+ ecard_startfind();
+
+ for (i = 0; ; i++) {
+ if ((ec[i] = ecard_find(0, rapide_cids)) == NULL)
+ break;
+
+ ecard_claim(ec[i]);
+ result[i] = rapide_register(ec[i]);
+ }
+ for (i = 0; i < MAX_ECARDS; i++)
+ if (ec[i] && result[i] < 0) {
+ ecard_release(ec[i]);
+ ec[i] = NULL;
+ }
+ return 0;
+}
+
+#ifdef MODULE
+
+int init_module (void)
+{
+ return rapide_init();
+}
+
+void cleanup_module (void)
+{
+ int i;
+
+ for (i = 0; i < MAX_ECARDS; i++)
+ if (ec[i]) {
+ unsigned long port;
+ port = ecard_address(ec[i], ECARD_MEMC, 0);
+
+ ide_unregister_port(port, ec[i]->irq, 16);
+ ecard_release(ec[i]);
+ ec[i] = NULL;
+ }
+}
+#endif
+
endif
endif
-ifeq ($(CONFIG_ETHERH),y)
-CONFIG_8390_BUILTIN = y
-else
- ifeq ($(CONFIG_ETHERH),m)
- CONFIG_8390_MODULE = y
- endif
-endif
-
ifeq ($(CONFIG_WD80x3),y)
L_OBJS += wd.o
CONFIG_8390_BUILTIN = y
endif
endif
-ifeq ($(CONFIG_ETHERH),y)
-CONFIG_8390_BUILTIN = y
-else
- ifeq ($(CONFIG_ETHERH),m)
- CONFIG_8390_MODULE = y
- endif
-endif
-
ifeq ($(CONFIG_NE2K_PCI),y)
L_OBJS += ne2k-pci.o
CONFIG_8390_BUILTIN = y
from = pci_devices;
else
from = from->next;
- while (from && (from->vendor != vendor || from->device != device))
+ while (from && (from->vendor != vendor && vendor != PCI_ANY_ID || from->device != device && device != PCI_ANY_ID))
from = from->next;
return from;
}
if (pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l) ||
/* some broken boards return 0 if a slot is empty: */
- l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) {
- is_multi = 0;
+ l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000)
continue;
- }
dev = kmalloc(sizeof(*dev), GFP_ATOMIC);
memset(dev, 0, sizeof(*dev));
struct cs4215 {
__u8 data[4]; /* Data mode: Time slots 5-8 */
__u8 ctrl[4]; /* Ctrl mode: Time slots 1-4 */
- __volatile__ struct dbri_mem td;
- __volatile__ struct dbri_mem rd;
__u8 onboard;
- __u32 status;
- __u32 version;
+ __u8 offset; /* Bit offset from frame sync to time slot 1 */
+ volatile __u32 status;
+ volatile __u32 version;
};
#define D_CMD (1<<2)
#define D_MM (1<<3)
#define D_USR (1<<4)
+#define D_DESC (1<<5)
-/* static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR; */
static int dbri_debug = 0;
MODULE_PARM(dbri_debug, "i");
+static int dbri_trace = 0;
+MODULE_PARM(dbri_trace, "i");
+#define tprintk(x) if(dbri_trace) printk x
+
static char *cmds[] = {
"WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS",
"SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV"
};
-/* Bit hunting */
-#define dumpcmd {int i; for(i=0; i<n; i++) printk("DBRI: %x\n", dbri->dma->cmd[i]); }
-
#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (1 << 27) | value)
#else
#define dprintk(a, x)
-#define dumpcmd
#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value)
#endif /* DBRI_DEBUG */
****************************************************************************
************** DBRI initialization and command synchronization *************
****************************************************************************
-*/
+Commands are sent to the DBRI by building a list of them in memory,
+then writing the address of the first list item to DBRI register 8.
+The list is terminated with a WAIT command, which can generate a
+CPU interrupt if required.
+
+Since the DBRI can run in parallel with the CPU, several means of
+synchronization present themselves. The original scheme (Rudolf's)
+was to set a flag when we "cmdlock"ed the DBRI, clear the flag when
+an interrupt signaled completion, and wait on a wait_queue if a routine
+attempted to cmdlock while the flag was set. The problems arose when
+we tried to cmdlock from inside an interrupt handler, which might
+cause scheduling in an interrupt (if we waited), etc, etc
+
+A more sophisticated scheme might involve a circular command buffer
+or an array of command buffers. A routine could fill one with
+commands and link it onto a list. When a interrupt signaled
+completion of the current command buffer, look on the list for
+the next one.
+
+I've decided to implement something much simpler - after each command,
+the CPU waits for the DBRI to finish the command by polling the P bit
+in DBRI register 0. I've tried to implement this in such a way
+that might make implementing a more sophisticated scheme easier.
+
+Every time a routine wants to write commands to the DBRI, it must
+first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd
+in return. After the commands have been writen, dbri_cmdsend() is
+called with the final pointer value.
+
+Something a little more clever is required if this code is ever run
+on an SMP machine.
-/*
- * Commands are sent to the DBRI by building a list of them in memory,
- * then writing the address of the first list item to DBRI register 8.
- * The list is terminated with a WAIT command, which can generate a
- * CPU interrupt if required.
- *
- * Since the DBRI can run asynchronously to the CPU, several means of
- * synchronization present themselves. The original scheme (Rudolf's)
- * was to set a flag when we "cmdlock"ed the DBRI, clear the flag when
- * an interrupt signaled completion, and wait on a wait_queue if a routine
- * attempted to cmdlock while the flag was set. The problems arose when
- * we tried to cmdlock from inside an interrupt handler, which might
- * cause scheduling in an interrupt (if we waited), etc, etc
- *
- * A more sophisticated scheme might involve a circular command buffer
- * or an array of command buffers. A routine could fill one with
- * commands and link it onto a list. When a interrupt signaled
- * completion of the current command buffer, look on the list for
- * the next one.
- *
- * I've decided to implement something much simpler - after each command,
- * the CPU waits for the DBRI to finish the command by polling the P bit
- * in DBRI register 0. I've tried to implement this in such a way
- * that might make implementing a more sophisticated scheme easier.
- *
- * Every time a routine wants to write commands to the DBRI, it must
- * first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd
- * in return. After the commands have been writen, dbri_cmdsend() is
- * called with the final pointer value.
- */
+*/
-static int dbri_locked = 0; /* XXX not SMP safe! XXX */
+static int dbri_locked = 0;
static volatile int * dbri_cmdlock(struct dbri *dbri)
{
return dbri->dma->cmd;
}
+static void dbri_process_interrupt_buffer(struct dbri *);
+
static void dbri_cmdsend(struct dbri *dbri, volatile int * cmd)
{
- int maxloops = 1000000;
+ int MAXLOOPS = 1000000;
+ int maxloops = MAXLOOPS;
+ unsigned int flags;
+ volatile int * ptr;
+
+ for (ptr = dbri->dma->cmd; ptr < cmd; ptr ++) {
+ dprintk(D_CMD, ("DBRI cmd: %08x:%08x\n",
+ (unsigned int) ptr, *ptr));
+ }
+
+ save_and_cli(flags);
dbri_locked --;
if (dbri_locked != 0) {
printk("DBRI: Command buffer overflow! (bug in driver)\n");
} else {
*(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
- *(cmd++) = DBRI_CMD(D_WAIT, 0, 0);
+ *(cmd++) = DBRI_CMD(D_WAIT, 1, 0);
+ dbri->wait_seen = 0;
dbri->regs->reg8 = (int)dbri->dma_dvma->cmd;
- while ((maxloops--) > 0 && (dbri->regs->reg0 & D_P));
+ while ((--maxloops) > 0 && (dbri->regs->reg0 & D_P));
+ if (maxloops == 0) {
+ printk("DBRI: Chip never completed command buffer\n");
+ } else {
+ while ((--maxloops) > 0 && (! dbri->wait_seen))
+ dbri_process_interrupt_buffer(dbri);
+ if (maxloops == 0) {
+ printk("DBRI: Chip never acked WAIT\n");
+ } else {
+ dprintk(D_INT, ("DBRI: Chip completed command buffer (%d)\n",
+ MAXLOOPS - maxloops));
+ }
+ }
}
- if (maxloops == 0) {
- printk("DBRI: Chip never completed command buffer\n");
- }
+ restore_flags(flags);
}
static void dbri_reset(struct dbri *dbri)
dbri_reset(dbri);
free_irq(dbri->irq, dbri);
sparc_free_io(dbri->regs, dbri->regs_size);
- /* Should we release the DMA structure dbri->dma here? */
+ release_region((unsigned long) dbri->dma, sizeof(struct dbri_dma));
kfree(dbri);
}
****************************************************************************
*************************** DBRI interrupt handler *************************
****************************************************************************
+
+The DBRI communicates with the CPU mainly via a circular interrupt
+buffer. When an interrupt is signaled, the CPU walks through the
+buffer and calls dbri_process_one_interrupt() for each interrupt word.
+Complicated interrupts are handled by dedicated functions (which
+appear first in this file). Any pending interrupts can be serviced by
+calling dbri_process_interrupt_buffer(), which works even if the CPU's
+interrupts are disabled. This function is used by dbri_cmdsend()
+to make sure we're synced up with the chip after each command sequence,
+even if we're running cli'ed.
+
*/
case 2:
b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1);
case 1:
+ case 0:
break;
default:
printk("DBRI reverse_bytes: unsupported length\n");
/* transmission_complete_intr()
*
* Called by main interrupt handler when DBRI signals transmission complete
- * on a pipe.
+ * on a pipe (interrupt triggered by the B bit in a transmit descriptor).
*
* Walks through the pipe's list of transmit buffer descriptors, releasing
- * each one's DMA buffer (if present) and signaling its callback routine
- * (if present), before flaging the descriptor available and proceeding
- * to the next one.
- *
- * Assumes that only the last in a chain of descriptors will have FINT
- * sent to signal an interrupt, so that the chain will be completely
- * transmitted by the time we get here, and there's no need to save
- * any of the descriptors. In particular, use of the DBRI's CDP command
- * is precluded, but I've not been able to get CDP working reliably anyway.
+ * each one's DMA buffer (if present), flagging the descriptor available,
+ * and signaling its callback routine (if present), before proceeding
+ * to the next one. Stops when the first descriptor is found without
+ * TBC (Transmit Buffer Complete) set, or we've run through them all.
*/
static void transmission_complete_intr(struct dbri *dbri, int pipe)
{
- int td = dbri->pipes[pipe].desc;
+ int td;
int status;
void *buffer;
void (*callback)(void *, int);
+ void *callback_arg;
- dbri->pipes[pipe].desc = -1;
+ td = dbri->pipes[pipe].desc;
- for (; td >= 0; td = dbri->descs[td].next) {
+ while (td >= 0) {
if (td >= DBRI_NO_DESCS) {
printk("DBRI: invalid td on pipe %d\n", pipe);
return;
}
- status = dbri->dma->desc[td].word4;
+ status = DBRI_TD_STATUS(dbri->dma->desc[td].word4);
+
+ if (! (status & DBRI_TD_TBC)) {
+ break;
+ }
+
+ dprintk(D_INT, ("DBRI: TD %d, status 0x%02x\n", td, status));
buffer = dbri->descs[td].buffer;
if (buffer) {
}
callback = dbri->descs[td].output_callback;
- if (callback != NULL) {
- callback(dbri->descs[td].output_callback_arg,
- DBRI_TD_STATUS(status) & 0xe);
- }
+ callback_arg = dbri->descs[td].output_callback_arg;
dbri->descs[td].inuse = 0;
+
+ td = dbri->descs[td].next;
+ dbri->pipes[pipe].desc = td;
+
+ if (callback != NULL) {
+ callback(callback_arg, status & 0xe);
+ }
}
}
}
dbri->descs[rd].inuse = 0;
- dbri->pipes[pipe].desc = -1;
+ dbri->pipes[pipe].desc = dbri->descs[rd].next;
status = dbri->dma->desc[rd].word1;
buffer = dbri->descs[rd].buffer;
DBRI_RD_STATUS(status),
DBRI_RD_CNT(status)-2);
}
+
+ dprintk(D_INT, ("DBRI: Recv RD %d, status 0x%02x, len %d\n",
+ rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status)));
}
-static void dbri_intr(int irq, void *opaque, struct pt_regs *regs)
+static void dbri_process_one_interrupt(struct dbri *dbri, int x)
{
- struct dbri *dbri = (struct dbri *)opaque;
- int x;
-
- /*
- * Read it, so the interrupt goes away.
- */
- x = dbri->regs->reg1;
+ int val = D_INTR_GETVAL(x);
+ int channel = D_INTR_GETCHAN(x);
+ int command = D_INTR_GETCMD(x);
+ int code = D_INTR_GETCODE(x);
+ int rval = D_INTR_GETRVAL(x);
- if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
- /*
- * What should I do here ?
- */
- if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n");
- if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n");
- if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n");
- if(x & D_MBE) printk("DBRI: Burst Error on SBus\n");
+ if (channel == D_INTR_CMD) {
+ dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n",
+ cmds[command], val));
+ } else {
+ dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n",
+ channel, code, rval));
}
- if (!(x & D_IR)) /* Not for us */
- return;
+ if (channel == D_INTR_CMD && command == D_WAIT) {
+ dbri->wait_seen ++;
+ }
- x = dbri->dma->intr[dbri->dbri_irqp];
- while (x != 0) {
- int val = D_INTR_GETVAL(x);
- int channel = D_INTR_GETCHAN(x);
+ if (code == D_INTR_SBRI) {
- dbri->dma->intr[dbri->dbri_irqp] = 0;
+ /* SBRI - BRI status change */
- if(D_INTR_GETCHAN(x) == D_INTR_CMD) {
- dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n",
- cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x)));
- } else {
- dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n",
- D_INTR_GETCHAN(x), D_INTR_GETCODE(x),
- D_INTR_GETRVAL(x)));
- }
+ int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7};
+ dbri->liu_state = liu_states[val & 0x7];
+ if (dbri->liu_callback)
+ dbri->liu_callback(dbri->liu_callback_arg);
+ }
- if (D_INTR_GETCODE(x) == D_INTR_SBRI) {
+ if (code == D_INTR_BRDY) {
+ reception_complete_intr(dbri, channel);
+ }
- /* SBRI - BRI status change */
+ if (code == D_INTR_XCMP) {
+ transmission_complete_intr(dbri, channel);
+ }
- int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7};
- dbri->liu_state = liu_states[val & 0x7];
- if (dbri->liu_callback)
- dbri->liu_callback(dbri->liu_callback_arg);
- }
+ if (code == D_INTR_UNDR) {
- if (D_INTR_GETCODE(x) == D_INTR_BRDY) {
- reception_complete_intr(dbri, channel);
- }
+ /* UNDR - Transmission underrun
+ * resend SDP command with clear pipe bit (C) set
+ */
- if (D_INTR_GETCODE(x) == D_INTR_XCMP) {
- transmission_complete_intr(dbri, channel);
- }
+ volatile int *cmd;
+ int pipe = channel;
+ int td = dbri->pipes[pipe].desc;
- if (D_INTR_GETCODE(x) == D_INTR_FXDT) {
+ dbri->dma->desc[td].word4 = 0;
- /* FXDT - Fixed data change */
+ cmd = dbri_cmdlock(dbri);
+ *(cmd++) = DBRI_CMD(D_SDP, 0,
+ dbri->pipes[pipe].sdp
+ | D_SDP_P | D_SDP_C | D_SDP_2SAME);
+ *(cmd++) = (int) & dbri->dma_dvma->desc[td];
+ dbri_cmdsend(dbri, cmd);
+ }
- if (dbri->pipes[D_INTR_GETCHAN(x)].sdp & D_SDP_MSB) {
- val = reverse_bytes(val, dbri->pipes[channel].length);
- }
+ if (code == D_INTR_FXDT) {
- if (dbri->pipes[D_INTR_GETCHAN(x)].recv_fixed_ptr) {
- * dbri->pipes[channel].recv_fixed_ptr = val;
- }
+ /* FXDT - Fixed data change */
+
+ if (dbri->pipes[channel].sdp & D_SDP_MSB) {
+ val = reverse_bytes(val, dbri->pipes[channel].length);
}
+ if (dbri->pipes[channel].recv_fixed_ptr) {
+ * dbri->pipes[channel].recv_fixed_ptr = val;
+ }
+ }
+}
+
+/* dbri_process_interrupt_buffer advances through the DBRI's interrupt
+ * buffer until it finds a zero word (indicating nothing more to do
+ * right now). Non-zero words require processing and are handed off
+ * to dbri_process_one_interrupt AFTER advancing the pointer. This
+ * order is important since we might recurse back into this function
+ * and need to make sure the pointer has been advanced first.
+ */
+
+static void dbri_process_interrupt_buffer(struct dbri *dbri)
+{
+ int x;
+
+ while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) {
+
+ dbri->dma->intr[dbri->dbri_irqp] = 0;
dbri->dbri_irqp++;
if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK))
dbri->dbri_irqp = 1;
else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0)
dbri->dbri_irqp++;
- x = dbri->dma->intr[dbri->dbri_irqp];
+
+ dbri_process_one_interrupt(dbri, x);
+ }
+}
+
+static void dbri_intr(int irq, void *opaque, struct pt_regs *regs)
+{
+ struct dbri *dbri = (struct dbri *)opaque;
+ int x;
+
+ /*
+ * Read it, so the interrupt goes away.
+ */
+ x = dbri->regs->reg1;
+
+ dprintk(D_INT, ("DBRI: Interrupt! (reg1=0x%08x)\n", x));
+
+ if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) {
+
+ if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n");
+ if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n");
+ if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n");
+ if(x & D_MBE) printk("DBRI: Burst Error on SBus\n");
+
+ /* Some of these SBus errors cause the chip's SBus circuitry
+ * to be disabled, so just re-enable and try to keep going.
+ *
+ * The only one I've seen is MRR, which will be triggered
+ * if you let a transmit pipe underrun, then try to CDP it.
+ *
+ * If these things persist, we should probably reset
+ * and re-init the chip.
+ */
+
+ dbri->regs->reg0 &= ~D_D;
}
+
+#if 0
+ if (!(x & D_IR)) /* Not for us */
+ return;
+#endif
+
+ dbri_process_interrupt_buffer(dbri);
}
****************************************************************************
************************** DBRI data pipe management ***********************
****************************************************************************
+
+While DBRI control functions use the command and interrupt buffers, the
+main data path takes the form of data pipes, which can be short (command
+and interrupt driven), or long (attached to DMA buffers). These functions
+provide a rudimentary means of setting up and managing the DBRI's pipes,
+but the calling functions have to make sure they respect the pipes' linked
+list ordering, among other things. The transmit and receive functions
+here interface closely with the transmit and receive interrupt code.
+
*/
+static int pipe_active(struct dbri *dbri, int pipe)
+{
+ return (dbri->pipes[pipe].desc != -1);
+}
+
/* reset_pipe(dbri, pipe)
*
static void reset_pipe(struct dbri *dbri, int pipe)
{
int sdp;
+ int desc;
volatile int *cmd;
if (pipe < 0 || pipe > 31) {
*(cmd++) = 0;
dbri_cmdsend(dbri, cmd);
+ desc = dbri->pipes[pipe].desc;
+ while (desc != -1) {
+ void *buffer = dbri->descs[desc].buffer;
+ void (*output_callback) (void *, int)
+ = dbri->descs[desc].output_callback;
+ void *output_callback_arg
+ = dbri->descs[desc].output_callback_arg;
+ void (*input_callback) (void *, int, unsigned int)
+ = dbri->descs[desc].input_callback;
+ void *input_callback_arg
+ = dbri->descs[desc].input_callback_arg;
+
+ if (buffer) {
+ mmu_release_scsi_one(sbus_dvma_addr(buffer),
+ dbri->descs[desc].len,
+ dbri->sdev->my_bus);
+ }
+
+ dbri->descs[desc].inuse = 0;
+ desc = dbri->descs[desc].next;
+
+ if (output_callback) {
+ output_callback(output_callback_arg, -1);
+ }
+ if (input_callback) {
+ input_callback(input_callback_arg, -1, 0);
+ }
+ }
+
dbri->pipes[pipe].desc = -1;
}
/* sdp &= 0xf800; */
}
+ /* If this is a fixed receive pipe, arrange for an interrupt
+ * every time its data changes
+ */
+
+ if (D_SDP_MODE(sdp) == D_SDP_FIXED && ! (sdp & D_SDP_TO_SER)) {
+ sdp |= D_SDP_CHANGE;
+ }
+
sdp |= D_PIPE(pipe);
dbri->pipes[pipe].sdp = sdp;
+ dbri->pipes[pipe].desc = -1;
reset_pipe(dbri, pipe);
}
-enum master_or_slave { CHImaster, CHIslave };
-
-static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave,
- int bits_per_frame)
+static void link_time_slot(struct dbri *dbri, int pipe,
+ enum in_or_out direction, int basepipe,
+ int length, int cycle)
{
volatile int *cmd;
int val;
+ int prevpipe;
+ int nextpipe;
- cmd = dbri_cmdlock(dbri);
+ if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) {
+ printk("DBRI: link_time_slot called with illegal pipe number\n");
+ return;
+ }
- /* Set CHI Anchor: Pipe 16 */
+ if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) {
+ printk("DBRI: link_time_slot called on uninitialized pipe\n");
+ return;
+ }
- val = D_DTS_VI | D_DTS_VO | D_DTS_INS |
- D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16);
- *(cmd++) = DBRI_CMD(D_DTS, 0, val);
- *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
- *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16);
+ /* Deal with CHI special case:
+ * "If transmission on edges 0 or 1 is desired, then cycle n
+ * (where n = # of bit times per frame...) must be used."
+ * - DBRI data sheet, page 11
+ */
- dbri->pipes[16].sdp = 1;
- dbri->pipes[16].nextpipe = 16;
+ if (basepipe == 16 && direction == PIPEoutput && cycle == 0) {
+ cycle = dbri->chi_bpf;
+ }
- if (master_or_slave == CHIslave) {
- /* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
- *
- * CHICM = 0 (slave mode, 8 kHz frame rate)
- * IR = give immediate CHI status interrupt
- * EN = give CHI status interrupt upon change
- */
- *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0)
- | D_CHI_IR | D_CHI_EN);
+ if (basepipe == pipe) {
+ prevpipe = pipe;
+ nextpipe = pipe;
} else {
- /* Setup DBRI for CHI Master - generate clock, FS
- *
- * BPF = bits per 8 kHz frame
- * 12.288 MHz / CHICM_divisor = clock rate
- * FD = 1 - drive CHIFS on rising edge of CHICK
+
+ /* We're not initializing a new linked list (basepipe != pipe),
+ * so run through the linked list and find where this pipe
+ * should be sloted in, based on its cycle. CHI confuses
+ * things a bit, since it has a single anchor for both its
+ * transmit and receive lists.
*/
- int clockrate = bits_per_frame * 8;
- int divisor = 12288 / clockrate;
+ if (basepipe == 16) {
+ if (direction == PIPEinput) {
+ prevpipe = dbri->chi_in_pipe;
+ } else {
+ prevpipe = dbri->chi_out_pipe;
+ }
+ } else {
+ prevpipe = basepipe;
+ }
+
+ nextpipe = dbri->pipes[prevpipe].nextpipe;
- if (divisor > 255 || divisor * clockrate != 12288) {
- printk("DBRI: illegal bits_per_frame in setup_chi\n");
+ while (dbri->pipes[nextpipe].cycle < cycle
+ && dbri->pipes[nextpipe].nextpipe != basepipe) {
+ prevpipe = nextpipe;
+ nextpipe = dbri->pipes[nextpipe].nextpipe;
}
+ }
- *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
- | D_CHI_IR | D_CHI_EN
- | D_CHI_BPF(bits_per_frame));
+ if (prevpipe == 16) {
+ if (direction == PIPEinput) {
+ dbri->chi_in_pipe = pipe;
+ } else {
+ dbri->chi_out_pipe = pipe;
+ }
+ } else {
+ dbri->pipes[prevpipe].nextpipe = pipe;
}
- /* CHI Data Mode
- *
- * RCE = 0 - receive on falling edge of CHICK
- * XCE = 1 - transmit on rising edge of CHICK
- * XEN = 1 - enable transmitter
- * REN = 1 - enable receiver
- */
+ dbri->pipes[pipe].nextpipe = nextpipe;
+ dbri->pipes[pipe].cycle = cycle;
+ dbri->pipes[pipe].length = length;
- *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+ cmd = dbri_cmdlock(dbri);
- *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+ if (direction == PIPEinput) {
+ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+ *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+ *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+ *(cmd++) = 0;
+ } else {
+ val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
+ *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+ *(cmd++) = 0;
+ *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+ }
dbri_cmdsend(dbri, cmd);
}
-enum in_or_out { PIPEinput, PIPEoutput };
+/* I don't use this function, so it's basically untested. */
-static void link_time_slot(struct dbri *dbri, int pipe,
- enum in_or_out direction, int prevpipe,
- int length, int cycle)
+static void unlink_time_slot(struct dbri *dbri, int pipe,
+ enum in_or_out direction, int prevpipe,
+ int nextpipe)
{
volatile int *cmd;
int val;
- int nextpipe;
if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) {
- printk("DBRI: link_time_slot called with illegal pipe number\n");
- return;
- }
-
- if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[prevpipe].sdp == 0) {
- printk("DBRI: link_time_slot called on uninitialized pipe\n");
+ printk("DBRI: unlink_time_slot called with illegal pipe number\n");
return;
}
- if (pipe == prevpipe) {
- nextpipe = pipe;
- } else {
- nextpipe = dbri->pipes[prevpipe].nextpipe;
- }
-
- dbri->pipes[pipe].nextpipe = nextpipe;
- dbri->pipes[pipe].cycle = cycle;
- dbri->pipes[pipe].length = length;
-
cmd = dbri_cmdlock(dbri);
if (direction == PIPEinput) {
- val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe;
+ val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe;
*(cmd++) = DBRI_CMD(D_DTS, 0, val);
- *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+ *(cmd++) = D_TS_NEXT(nextpipe);
*(cmd++) = 0;
} else {
- val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe;
+ val = D_DTS_VO | D_DTS_DEL | D_DTS_PRVOUT(prevpipe) | pipe;
*(cmd++) = DBRI_CMD(D_DTS, 0, val);
*(cmd++) = 0;
- *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe);
+ *(cmd++) = D_TS_NEXT(nextpipe);
}
dbri_cmdsend(dbri, cmd);
}
+/* xmit_fixed() / recv_fixed()
+ *
+ * Transmit/receive data on a "fixed" pipe - i.e, one whose contents are not
+ * expected to change much, and which we don't need to buffer.
+ * The DBRI only interrupts us when the data changes (receive pipes),
+ * or only changes the data when this function is called (transmit pipes).
+ * Only short pipes (numbers 16-31) can be used in fixed data mode.
+ *
+ * These function operate on a 32-bit field, no matter how large
+ * the actual time slot is. The interrupt handler takes care of bit
+ * ordering and alignment. An 8-bit time slot will always end up
+ * in the low-order 8 bits, filled either MSB-first or LSB-first,
+ * depending on the settings passed to setup_pipe()
+ */
+
static void xmit_fixed(struct dbri *dbri, int pipe, unsigned int data)
{
volatile int *cmd;
if (pipe < 16 || pipe > 31) {
- printk("DBRI: xmit_fixed called with illegal pipe number\n");
+ printk("DBRI: xmit_fixed: Illegal pipe number\n");
+ return;
+ }
+
+ if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) {
+ printk("DBRI: xmit_fixed: Uninitialized pipe %d\n", pipe);
return;
}
if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
- printk("DBRI: xmit_fixed called on non-fixed pipe\n");
+ printk("DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe);
return;
}
if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
- printk("DBRI: xmit_fixed called on receive pipe\n");
+ printk("DBRI: xmit_fixed: Called on receive pipe %d\n", pipe);
return;
}
dbri_cmdsend(dbri, cmd);
}
-/* recv_fixed()
- *
- * Receive data on a "fixed" pipe - i.e, one whose contents are not
- * expected to change much, and which we don't need to read constantly
- * into a buffer. The DBRI only interrupts us when the data changes.
- * Only short pipes (numbers 16-31) can be used in fixed data mode.
- *
- * Pass this function a pointer to a 32-bit field, no matter how large
- * the actual time slot is. The interrupt handler takes care of bit
- * ordering and alignment. An 8-bit time slot will always end up
- * in the low-order 8 bits, filled either MSB-first or LSB-first,
- * depending on the settings passed to setup_pipe()
- */
-
-static void recv_fixed(struct dbri *dbri, int pipe, __u32 *ptr)
+static void recv_fixed(struct dbri *dbri, int pipe, volatile __u32 *ptr)
{
if (pipe < 16 || pipe > 31) {
printk("DBRI: recv_fixed called with illegal pipe number\n");
}
if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) {
- printk("DBRI: recv_fixed called on non-fixed pipe\n");
+ printk("DBRI: recv_fixed called on non-fixed pipe %d\n", pipe);
return;
}
if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
- printk("DBRI: recv_fixed called on transmit pipe\n");
+ printk("DBRI: recv_fixed called on transmit pipe %d\n", pipe);
return;
}
}
+/* xmit_on_pipe() / recv_on_pipe()
+ *
+ * Transmit/receive data on a "long" pipe - i.e, one associated
+ * with a DMA buffer.
+ *
+ * Only pipe numbers 0-15 can be used in this mode.
+ *
+ * Both functions take pointer/len arguments pointing to a data buffer,
+ * and both provide callback functions (may be NULL) to notify higher
+ * level code when transmission/reception is complete.
+ *
+ * Both work by building chains of descriptors which identify the
+ * data buffers. Buffers too large for a single descriptor will
+ * be spread across multiple descriptors.
+ */
+
static void xmit_on_pipe(struct dbri *dbri, int pipe,
void * buffer, unsigned int len,
void (*callback)(void *, int), void * callback_arg)
{
volatile int *cmd;
+ register unsigned int flags;
int td = 0;
int first_td = -1;
- int last_td;
+ int last_td = -1;
__u32 dvma_buffer;
if (pipe < 0 || pipe > 15) {
- printk("DBRI: xmit_on_pipe called with illegal pipe number\n");
+ printk("DBRI: xmit_on_pipe: Illegal pipe number\n");
return;
}
if (dbri->pipes[pipe].sdp == 0) {
- printk("DBRI: xmit_on_pipe called on uninitialized pipe\n");
+ printk("DBRI: xmit_on_pipe: Uninitialized pipe %d\n", pipe);
return;
}
if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
- printk("DBRI: xmit_on_pipe called on receive pipe\n");
- return;
- }
-
- /* XXX Fix this XXX
- * Should be able to queue multiple buffers to send on a pipe
- */
-
- if (dbri->pipes[pipe].desc != -1) {
- printk("DBRI: xmit_on_pipe called on active pipe\n");
+ printk("DBRI: xmit_on_pipe: Called on receive pipe %d\n",
+ pipe);
return;
}
while (len > 0) {
int mylen;
- for (td; td < DBRI_NO_DESCS; td ++) {
+ for (; td < DBRI_NO_DESCS; td ++) {
if (! dbri->descs[td].inuse) break;
}
if (td == DBRI_NO_DESCS) {
+ printk("DBRI: xmit_on_pipe: No descriptors\n");
break;
}
len -= mylen;
}
- if (first_td == -1) {
- printk("xmit_on_pipe: No descriptors available\n");
+ if (first_td == -1 || last_td == -1) {
return;
}
- if (len > 0) {
- printk("xmit_on_pipe: Insufficient descriptors; data truncated\n");
- }
-
dbri->dma->desc[last_td].word1 |= DBRI_TD_I | DBRI_TD_F | DBRI_TD_B;
dbri->descs[last_td].buffer = buffer;
dbri->descs[last_td].output_callback = callback;
dbri->descs[last_td].output_callback_arg = callback_arg;
- dbri->pipes[pipe].desc = first_td;
+ for (td=first_td; td != -1; td = dbri->descs[td].next) {
+ dprintk(D_DESC, ("DBRI TD %d: %08x %08x %08x %08x\n",
+ td,
+ dbri->dma->desc[td].word1,
+ dbri->dma->desc[td].ba,
+ dbri->dma->desc[td].nda,
+ dbri->dma->desc[td].word4));
+ }
- cmd = dbri_cmdlock(dbri);
+ save_and_cli(flags);
- *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C);
- *(cmd++) = (int) & dbri->dma_dvma->desc[first_td];
+ if (pipe_active(dbri, pipe)) {
- dbri_cmdsend(dbri, cmd);
+ /* Pipe is already active - find last TD in use
+ * and link our first TD onto its end. Then issue
+ * a CDP command to let the DBRI know there's more data.
+ */
+
+ last_td = dbri->pipes[pipe].desc;
+ while (dbri->descs[last_td].next != -1)
+ last_td = dbri->descs[last_td].next;
+
+ dbri->descs[last_td].next = first_td;
+ dbri->dma->desc[last_td].nda =
+ (int) & dbri->dma_dvma->desc[first_td];
+
+ cmd = dbri_cmdlock(dbri);
+ *(cmd++) = DBRI_CMD(D_CDP, 0, pipe);
+ dbri_cmdsend(dbri,cmd);
+
+ } else {
+
+ /* Pipe isn't active - issue an SDP command to start
+ * our chain of TDs running.
+ */
+
+ dbri->pipes[pipe].desc = first_td;
+
+ cmd = dbri_cmdlock(dbri);
+ *(cmd++) = DBRI_CMD(D_SDP, 0,
+ dbri->pipes[pipe].sdp
+ | D_SDP_P | D_SDP_EVERY | D_SDP_C);
+ *(cmd++) = (int) & dbri->dma_dvma->desc[first_td];
+ dbri_cmdsend(dbri, cmd);
+
+ }
+
+ restore_flags(flags);
}
static void recv_on_pipe(struct dbri *dbri, int pipe,
void * callback_arg)
{
volatile int *cmd;
+ int first_rd = -1;
+ int last_rd = -1;
int rd;
+ __u32 bus_buffer;
if (pipe < 0 || pipe > 15) {
- printk("DBRI: recv_on_pipe called with illegal pipe number\n");
+ printk("DBRI: recv_on_pipe: Illegal pipe number\n");
return;
}
if (dbri->pipes[pipe].sdp == 0) {
- printk("DBRI: recv_on_pipe called on uninitialized pipe\n");
+ printk("DBRI: recv_on_pipe: Uninitialized pipe %d\n", pipe);
return;
}
if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) {
- printk("DBRI: recv_on_pipe called on transmit pipe\n");
+ printk("DBRI: recv_on_pipe: Called on transmit pipe %d\n",
+ pipe);
return;
}
/* XXX Fix this XXX
- * Should be able to queue multiple buffers to send on a pipe
+ * Should be able to queue multiple buffers to receive on a pipe
*/
if (dbri->pipes[pipe].desc != -1) {
- printk("DBRI: recv_on_pipe called on active pipe\n");
+ printk("DBRI: recv_on_pipe: Called on active pipe %d\n", pipe);
return;
}
- /* XXX Fix this XXX
- * Use multiple descriptors, if needed, to fit in all the data
- */
-
- if (len > (1 << 13) - 1) {
- printk("recv_on_pipe called with len=%d; truncated\n", len);
- len = (1 << 13) - 1;
- }
-
/* Make sure buffer size is multiple of four */
len &= ~3;
- for (rd = 0; rd < DBRI_NO_DESCS; rd ++) {
- if (! dbri->descs[rd].inuse) break;
+ bus_buffer = mmu_get_scsi_one(buffer, len, dbri->sdev->my_bus);
+
+ while (len > 0) {
+ int rd;
+ int mylen;
+
+ if (len > (1 << 13) - 4) {
+ mylen = (1 << 13) - 4;
+ } else {
+ mylen = len;
+ }
+
+ for (rd = 0; rd < DBRI_NO_DESCS; rd ++) {
+ if (! dbri->descs[rd].inuse) break;
+ }
+ if (rd == DBRI_NO_DESCS) {
+ printk("DBRI recv_on_pipe: No descriptors\n");
+ break;
+ }
+
+ dbri->dma->desc[rd].word1 = 0;
+ dbri->dma->desc[rd].ba = bus_buffer;
+ dbri->dma->desc[rd].nda = 0;
+ dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(mylen);
+
+ dbri->descs[rd].buffer = NULL;
+ dbri->descs[rd].len = 0;
+ dbri->descs[rd].input_callback = NULL;
+ dbri->descs[rd].output_callback = NULL;
+ dbri->descs[rd].next = -1;
+ dbri->descs[rd].inuse = 1;
+
+ if (first_rd == -1) first_rd = rd;
+ if (last_rd != -1) {
+ dbri->dma->desc[last_rd].nda =
+ (int) & dbri->dma_dvma->desc[rd];
+ dbri->descs[last_rd].next = rd;
+ }
+ last_rd = rd;
+
+ bus_buffer += mylen;
+ len -= mylen;
}
- if (rd == DBRI_NO_DESCS) {
- printk("DBRI xmit_on_pipe: No descriptors available\n");
+
+ if (last_rd == -1 || first_rd == -1) {
return;
}
- dbri->dma->desc[rd].word1 = 0;
- dbri->dma->desc[rd].ba = mmu_get_scsi_one(buffer, len,
- dbri->sdev->my_bus);
- dbri->dma->desc[rd].nda = 0;
- dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(len);
+ for (rd=first_rd; rd != -1; rd = dbri->descs[rd].next) {
+ dprintk(D_DESC, ("DBRI RD %d: %08x %08x %08x %08x\n",
+ rd,
+ dbri->dma->desc[rd].word1,
+ dbri->dma->desc[rd].ba,
+ dbri->dma->desc[rd].nda,
+ dbri->dma->desc[rd].word4));
+ }
- dbri->descs[rd].buffer = buffer;
- dbri->descs[rd].len = len;
- dbri->descs[rd].input_callback = callback;
- dbri->descs[rd].input_callback_arg = callback_arg;
+ dbri->descs[last_rd].buffer = buffer;
+ dbri->descs[last_rd].len = len;
+ dbri->descs[last_rd].input_callback = callback;
+ dbri->descs[last_rd].input_callback_arg = callback_arg;
- dbri->pipes[pipe].desc = rd;
+ dbri->pipes[pipe].desc = first_rd;
cmd = dbri_cmdlock(dbri);
- *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P);
- *(cmd++) = (int) & dbri->dma_dvma->desc[rd];
+ *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C);
+ *(cmd++) = (int) & dbri->dma_dvma->desc[first_rd];
dbri_cmdsend(dbri, cmd);
}
+/*
+****************************************************************************
+************************** DBRI - CHI interface ****************************
+****************************************************************************
+
+The CHI is a four-wire (clock, frame sync, data in, data out) time-division
+multiplexed serial interface which the DBRI can operate in either master
+(give clock/frame sync) or slave (take clock/frame sync) mode.
+
+*/
+
+enum master_or_slave { CHImaster, CHIslave };
+
+static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave,
+ int bits_per_frame)
+{
+ volatile int *cmd;
+ int val;
+ static int chi_initialized=0;
+
+ if (!chi_initialized) {
+
+ cmd = dbri_cmdlock(dbri);
+
+ /* Set CHI Anchor: Pipe 16 */
+
+ val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16);
+ *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+ *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+ *(cmd++) = 0;
+
+ val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16);
+ *(cmd++) = DBRI_CMD(D_DTS, 0, val);
+ *(cmd++) = 0;
+ *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16);
+
+ dbri->pipes[16].sdp = 1;
+ dbri->pipes[16].nextpipe = 16;
+ dbri->chi_in_pipe = 16;
+ dbri->chi_out_pipe = 16;
+
+#if 0
+ chi_initialized ++;
+#endif
+ } else {
+ int pipe;
+
+ for (pipe = dbri->chi_in_pipe;
+ pipe != 16;
+ pipe = dbri->pipes[pipe].nextpipe) {
+ unlink_time_slot(dbri, pipe, PIPEinput,
+ 16, dbri->pipes[pipe].nextpipe);
+ }
+ for (pipe = dbri->chi_out_pipe;
+ pipe != 16;
+ pipe = dbri->pipes[pipe].nextpipe) {
+ unlink_time_slot(dbri, pipe, PIPEoutput,
+ 16, dbri->pipes[pipe].nextpipe);
+ }
+
+ dbri->chi_in_pipe = 16;
+ dbri->chi_out_pipe = 16;
+
+ cmd = dbri_cmdlock(dbri);
+
+ }
+
+ if (master_or_slave == CHIslave) {
+ /* Setup DBRI for CHI Slave - receive clock, frame sync (FS)
+ *
+ * CHICM = 0 (slave mode, 8 kHz frame rate)
+ * IR = give immediate CHI status interrupt
+ * EN = give CHI status interrupt upon change
+ */
+ *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0));
+ } else {
+ /* Setup DBRI for CHI Master - generate clock, FS
+ *
+ * BPF = bits per 8 kHz frame
+ * 12.288 MHz / CHICM_divisor = clock rate
+ * FD = 1 - drive CHIFS on rising edge of CHICK
+ */
+
+ int clockrate = bits_per_frame * 8;
+ int divisor = 12288 / clockrate;
+
+ if (divisor > 255 || divisor * clockrate != 12288) {
+ printk("DBRI: illegal bits_per_frame in setup_chi\n");
+ }
+
+ *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD
+ | D_CHI_BPF(bits_per_frame));
+ }
+
+ dbri->chi_bpf = bits_per_frame;
+
+ /* CHI Data Mode
+ *
+ * RCE = 0 - receive on falling edge of CHICK
+ * XCE = 1 - transmit on rising edge of CHICK
+ * XEN = 1 - enable transmitter
+ * REN = 1 - enable receiver
+ */
+
+ *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0);
+
+ *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN);
+
+ dbri_cmdsend(dbri, cmd);
+}
+
/*
****************************************************************************
*********************** CS4215 audio codec management **********************
****************************************************************************
+
+In the standard SPARC audio configuration, the CS4215 codec is attached
+to the DBRI via the CHI interface and few of the DBRI's PIO pins.
+
*/
* 2: Serial enable, CHI master, 128 bits per frame, clock 1
* 3: Tests disabled
*/
- mm->ctrl[0] = CS4215_RSRVD_1;
+ mm->ctrl[0] = CS4215_RSRVD_1 | CS4215_MLB;
mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
mm->ctrl[2] = CS4215_XCLK |
CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
mm->ctrl[3] = 0;
}
+static void mmcodec_setup_pipes(struct dbri *dbri)
+{
+ /*
+ * Data mode:
+ * Pipe 4: Send timeslots 1-4 (audio data)
+ * Pipe 20: Send timeslots 5-8 (part of ctrl data)
+ * Pipe 6: Receive timeslots 1-4 (audio data)
+ * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via
+ * interrupt, and the rest of the data (slot 5 and 8) is
+ * not relevant for us (only for doublechecking).
+ *
+ * Control mode:
+ * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly)
+ * Pipe 18: Receive timeslot 1 (clb).
+ * Pipe 19: Receive timeslot 7 (version).
+ */
+
+ setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB);
+ setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
+ setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB);
+ setup_pipe(dbri, 21, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+
+ setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
+ setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+ setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+
+ dbri->mm.status = 0;
+
+ recv_fixed(dbri, 18, & dbri->mm.status);
+ recv_fixed(dbri, 19, & dbri->mm.version);
+}
+
+static void mmcodec_setgain(struct dbri *dbri, int muted)
+{
+ if (muted || dbri->perchip_info.output_muted) {
+ dbri->mm.data[0] = 63;
+ dbri->mm.data[1] = 63;
+ } else {
+ int left_gain = (dbri->perchip_info.play.gain / 4) % 64;
+ int right_gain = (dbri->perchip_info.play.gain / 4) % 64;
+
+ if (dbri->perchip_info.play.balance < AUDIO_MID_BALANCE) {
+ right_gain *= dbri->perchip_info.play.balance;
+ right_gain /= AUDIO_MID_BALANCE;
+ } else {
+ left_gain *= AUDIO_RIGHT_BALANCE
+ - dbri->perchip_info.play.balance;
+ left_gain /= AUDIO_MID_BALANCE;
+ }
+
+ dprintk(D_MM, ("DBRI: Setting codec gain left: %d right: %d\n",
+ left_gain, right_gain));
+
+ dbri->mm.data[0] = CS4215_LE | CS4215_HE | (63 - left_gain);
+ dbri->mm.data[1] = CS4215_SE | (63 - right_gain);
+ }
+
+ xmit_fixed(dbri, 20, *(int *)dbri->mm.data);
+}
+
static void mmcodec_init_data(struct dbri *dbri)
{
+ int data_width;
+
/*
* Data mode:
* Pipe 4: Send timeslots 1-4 (audio data)
- * Pipe 17: Send timeslots 5-8 (part of ctrl data)
+ * Pipe 20: Send timeslots 5-8 (part of ctrl data)
* Pipe 6: Receive timeslots 1-4 (audio data)
- * Pipe 20: Receive timeslots 6-7. We can only receive 20 bits via
+ * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via
* interrupt, and the rest of the data (slot 5 and 8) is
* not relevant for us (only for doublechecking).
*
*/
+ dbri->regs->reg0 &= ~D_C; /* Disable CHI */
+
/* Switch CS4215 to data mode - set PIO3 to 1 */
dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 |
(dbri->mm.onboard ? D_PIO0 : D_PIO2);
- reset_chi(dbri, CHIslave, 0);
+ reset_chi(dbri, CHIslave, 128);
+
+ /* Note: this next doesn't work for 8-bit stereo, because the two
+ * channels would be on timeslots 1 and 3, with 2 and 4 idle.
+ * (See CS4215 datasheet Fig 15)
+ *
+ * DBRI non-contiguous mode would be required to make this work.
+ */
- setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB);
- setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
- setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB);
- setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB);
+ data_width = dbri->perchip_info.play.channels
+ * dbri->perchip_info.play.precision;
- /* Pipes 4 and 6 - Single time slot, 8 bit mono */
+ link_time_slot(dbri, 20, PIPEoutput, 16,
+ 32, dbri->mm.offset + 32);
+ link_time_slot(dbri, 4, PIPEoutput, 16,
+ data_width, dbri->mm.offset);
+ link_time_slot(dbri, 6, PIPEinput, 16,
+ data_width, dbri->mm.offset);
+ link_time_slot(dbri, 21, PIPEinput, 16,
+ 16, dbri->mm.offset + 40);
- link_time_slot(dbri, 17, PIPEoutput, 16, 32, 32);
- link_time_slot(dbri, 4, PIPEoutput, 17, 8, 128);
- link_time_slot(dbri, 6, PIPEinput, 16, 8, 0);
- link_time_slot(dbri, 20, PIPEinput, 6, 16, 40);
+ mmcodec_setgain(dbri, 0);
- xmit_fixed(dbri, 17, *(int *)dbri->mm.data);
+ dbri->regs->reg0 |= D_C; /* Enable CHI */
}
/*
* Send the control information (i.e. audio format)
*/
-static void mmcodec_setctrl(struct dbri *dbri)
+static int mmcodec_setctrl(struct dbri *dbri)
{
int i, val;
+ /* XXX - let the CPU do something useful during these delays */
+
+ /* Temporarily mute outputs, and wait 1/8000 sec (125 us)
+ * to make sure this takes. This avoids clicking noises.
+ */
+
+ mmcodec_setgain(dbri, 1);
+ udelay(125);
+
/*
* Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait
* 12 cycles <= 12/(5512.5*64) sec = 34.01 usec
* frame sync signal by eight clock cycles. Anybody know why?
*/
+ dbri->regs->reg0 &= ~D_C; /* Disable CHI */
+
reset_chi(dbri, CHImaster, 128);
/*
* Pipe 19: Receive timeslot 7 (version).
*/
- setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB);
- setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB);
- setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB);
-
- link_time_slot(dbri, 17, PIPEoutput, 16, 32, 128);
- link_time_slot(dbri, 18, PIPEinput, 16, 8, 0);
- link_time_slot(dbri, 19, PIPEinput, 18, 8, 48);
-
- recv_fixed(dbri, 18, & dbri->mm.status);
- recv_fixed(dbri, 19, & dbri->mm.version);
+ link_time_slot(dbri, 17, PIPEoutput, 16,
+ 32, dbri->mm.offset);
+ link_time_slot(dbri, 18, PIPEinput, 16,
+ 8, dbri->mm.offset);
+ link_time_slot(dbri, 19, PIPEinput, 16,
+ 8, dbri->mm.offset + 48);
/* Wait for the chip to echo back CLB (Control Latch Bit) as zero */
dbri->mm.ctrl[0] &= ~CS4215_CLB;
xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl);
- i = 1000000;
- while ((! dbri->mm.status & CS4215_CLB) && i--);
+ dbri->regs->reg0 |= D_C; /* Enable CHI */
+
+ i = 10;
+ while (((dbri->mm.status & 0xe4) != 0x20) && --i) udelay(125);
if (i == 0) {
- printk("CS4215 didn't respond to CLB\n");
- return;
+ dprintk(D_MM, ("DBRI: CS4215 didn't respond to CLB (0x%02x)\n",
+ dbri->mm.status));
+ return -1;
}
/* Terminate CS4215 control mode - data sheet says
/* Two frames of control info @ 8kHz frame rate = 250 us delay */
udelay(250);
+
+ mmcodec_setgain(dbri, 0);
+
+ return 0;
}
static int mmcodec_init(struct sparcaudio_driver *drv)
}
- /* Now talk to our baby */
- dbri->regs->reg0 |= D_C; /* Enable CHI */
+ mmcodec_setup_pipes(dbri);
mmcodec_default(&dbri->mm);
dbri->mm.version = 0xff;
- mmcodec_setctrl(dbri);
- if(dbri->mm.version == 0xff)
+ dbri->mm.offset = dbri->mm.onboard ? 0 : 8;
+ if (mmcodec_setctrl(dbri) == -1 || dbri->mm.version == 0xff) {
+ dprintk(D_MM, ("DBRI: CS4215 failed probe at offset %d\n",
+ dbri->mm.offset));
return -EIO;
+ }
+ dprintk(D_MM, ("DBRI: Found CS4215 at offset %d\n", dbri->mm.offset));
+
+ dbri->perchip_info.play.channels = 1;
+ dbri->perchip_info.play.precision = 8;
+ dbri->perchip_info.play.gain = 255;
+ dbri->perchip_info.play.balance = AUDIO_MID_BALANCE;
mmcodec_init_data(dbri);
return 0;
****************************************************************************
******************** Interface with sparcaudio midlevel ********************
****************************************************************************
-*/
+The sparcaudio midlevel is contained in the file audio.c. It interfaces
+to the user process and performs buffering, intercepts SunOS-style ioctl's,
+etc. It interfaces to a abstract audio device via a struct sparcaudio_driver.
+This code presents such an interface for the DBRI with an attached CS4215.
+All our routines are defined, and then comes our struct sparcaudio_driver.
-static int dbri_open(struct inode * inode, struct file * file,
- struct sparcaudio_driver *drv)
-{
- struct dbri *dbri = (struct dbri *)drv->private;
-
- MOD_INC_USE_COUNT;
+*/
- return 0;
-}
+/******************* sparcaudio midlevel - audio output *******************/
-static void dbri_release(struct inode * inode, struct file * file,
- struct sparcaudio_driver *drv)
-{
- MOD_DEC_USE_COUNT;
-}
-
-static int dbri_ioctl(struct inode * inode, struct file * file,
- unsigned int x, unsigned long y,
- struct sparcaudio_driver *drv)
-{
- return 0;
-}
static void dbri_audio_output_callback(void * callback_arg, int status)
{
struct sparcaudio_driver *drv = callback_arg;
- sparcaudio_output_done(drv, 1);
+ if (status != -1) {
+ sparcaudio_output_done(drv, 1);
+ }
}
static void dbri_start_output(struct sparcaudio_driver *drv,
{
struct dbri *dbri = (struct dbri *)drv->private;
+ dprintk(D_USR, ("DBRI: start audio output buf=%lx/%ld\n",
+ (unsigned long) buffer, count));
+
/* Pipe 4 is audio transmit */
- xmit_on_pipe(dbri, 4, buffer, count, &dbri_audio_output_callback, drv);
+
+ xmit_on_pipe(dbri, 4, buffer, count,
+ &dbri_audio_output_callback, drv);
+
+#if 0
+ /* Notify midlevel that we're a DMA-capable driver that
+ * can accept another buffer immediately. We should probably
+ * check that we've got enough resources (i.e, descriptors)
+ * available before doing this, but the default midlevel
+ * settings only buffer 64KB, which we can handle with 16
+ * of our DBRI_NO_DESCS (64) descriptors.
+ *
+ * This code is #ifdef'ed out because it's caused me more
+ * problems than it solved. It'd be nice to provide the
+ * DBRI with a chain of buffers, but the midlevel code is
+ * so tricky that I really don't want to deal with it.
+ */
+
+ sparcaudio_output_done(drv, 2);
+#endif
+
}
static void dbri_stop_output(struct sparcaudio_driver *drv)
reset_pipe(dbri, 4);
}
+/******************* sparcaudio midlevel - audio input ********************/
+
+static void dbri_audio_input_callback(void * callback_arg, int status,
+ unsigned int len)
+{
+ struct sparcaudio_driver * drv =
+ (struct sparcaudio_driver *) callback_arg;
+
+ if (status != -1) {
+ sparcaudio_input_done(drv, 3);
+ }
+}
+
static void dbri_start_input(struct sparcaudio_driver *drv,
__u8 * buffer, unsigned long len)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ /* Pipe 6 is audio receive */
+ recv_on_pipe(dbri, 6, buffer, len,
+ &dbri_audio_input_callback, (void *)drv);
+ dprintk(D_USR, ("DBRI: start audio input buf=%lx/%ld\n",
+ (unsigned long) buffer, len));
}
static void dbri_stop_input(struct sparcaudio_driver *drv)
{
-}
+ struct dbri *dbri = (struct dbri *)drv->private;
-static void dbri_audio_getdev(struct sparcaudio_driver *drv,
- audio_device_t *devptr)
-{
+ reset_pipe(dbri, 6);
}
+/******************* sparcaudio midlevel - volume & balance ***************/
+
static int dbri_set_output_volume(struct sparcaudio_driver *drv, int volume)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ dbri->perchip_info.play.gain = volume;
+ mmcodec_setgain(dbri, 0);
+
return 0;
}
static int dbri_get_output_volume(struct sparcaudio_driver *drv)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.gain;
}
static int dbri_set_input_volume(struct sparcaudio_driver *drv, int volume)
static int dbri_set_output_balance(struct sparcaudio_driver *drv, int balance)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ dbri->perchip_info.play.balance = balance;
+ mmcodec_setgain(dbri, 0);
+
return 0;
}
static int dbri_get_output_balance(struct sparcaudio_driver *drv)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.balance;
}
static int dbri_set_input_balance(struct sparcaudio_driver *drv, int balance)
return 0;
}
+static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute)
+{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ dbri->perchip_info.output_muted = mute;
+
+ return 0;
+}
+
+static int dbri_get_output_muted(struct sparcaudio_driver *drv)
+{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.output_muted;
+}
+
+/******************* sparcaudio midlevel - encoding format ****************/
+
static int dbri_set_output_channels(struct sparcaudio_driver *drv, int chan)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ switch (chan) {
+ case 0:
+ return 0;
+ case 1:
+ dbri->mm.ctrl[1] &= ~CS4215_DFR_STEREO;
+ break;
+ case 2:
+ dbri->mm.ctrl[1] |= CS4215_DFR_STEREO;
+ break;
+ default:
+ return -1;
+ }
+
+ dbri->perchip_info.play.channels = chan;
+ mmcodec_setctrl(dbri);
+ mmcodec_init_data(dbri);
return 0;
}
static int dbri_get_output_channels(struct sparcaudio_driver *drv)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.channels;
}
static int dbri_set_input_channels(struct sparcaudio_driver *drv, int chan)
{
- return 0;
+ return dbri_set_output_channels(drv, chan);
}
static int dbri_get_input_channels(struct sparcaudio_driver *drv)
{
- return 0;
+ return dbri_get_output_channels(drv);
}
static int dbri_set_output_precision(struct sparcaudio_driver *drv, int prec)
{
- return 8;
+ return 0;
}
static int dbri_get_output_precision(struct sparcaudio_driver *drv)
{
- return 8;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.precision;
}
static int dbri_set_input_precision(struct sparcaudio_driver *drv, int prec)
{
- return 8;
+ return 0;
}
static int dbri_get_input_precision(struct sparcaudio_driver *drv)
{
- return 8;
-}
+ struct dbri *dbri = (struct dbri *)drv->private;
-static int dbri_set_output_port(struct sparcaudio_driver *drv, int port)
-{
- return 0;
+ return dbri->perchip_info.play.precision;
}
-static int dbri_get_output_port(struct sparcaudio_driver *drv)
+static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ /* For ULAW and ALAW, audio.c enforces precision = 8,
+ * for LINEAR, precision must be 16
+ */
+
+ switch (enc) {
+ case AUDIO_ENCODING_NONE:
+ return 0;
+ case AUDIO_ENCODING_ULAW:
+ dbri->mm.ctrl[1] &= ~3;
+ dbri->mm.ctrl[1] |= CS4215_DFR_ULAW;
+ dbri->perchip_info.play.encoding = enc;
+ dbri->perchip_info.play.precision = 8;
+ break;
+ case AUDIO_ENCODING_ALAW:
+ dbri->mm.ctrl[1] &= ~3;
+ dbri->mm.ctrl[1] |= CS4215_DFR_ALAW;
+ dbri->perchip_info.play.encoding = enc;
+ dbri->perchip_info.play.precision = 8;
+ break;
+ case AUDIO_ENCODING_LINEAR:
+ dbri->mm.ctrl[1] &= ~3;
+ dbri->mm.ctrl[1] |= CS4215_DFR_LINEAR16;
+ dbri->perchip_info.play.encoding = enc;
+ dbri->perchip_info.play.precision = 16;
+ break;
+ default:
+ return -1;
+ }
+ mmcodec_setctrl(dbri);
+ mmcodec_init_data(dbri);
return 0;
}
-static int dbri_set_input_port(struct sparcaudio_driver *drv, int port)
+static int dbri_get_output_encoding(struct sparcaudio_driver *drv)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.encoding;
}
-static int dbri_get_input_port(struct sparcaudio_driver *drv)
+static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc)
{
- return 0;
+ return dbri_set_output_encoding(drv, enc);
}
-static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc)
+static int dbri_get_input_encoding(struct sparcaudio_driver *drv)
{
- return 0;
+ return dbri_get_output_encoding(drv);
}
-static int dbri_get_output_encoding(struct sparcaudio_driver *drv)
+static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate)
{
+ struct dbri *dbri = (struct dbri *)drv->private;
+ int i;
+
+ if (rate == 0) {
+ return 0;
+ }
+
+ for (i=0; CS4215_FREQ[i].freq; i++) {
+ if (CS4215_FREQ[i].freq == rate) break;
+ }
+ if (CS4215_FREQ[i].freq == 0) {
+ return -1;
+ }
+
+ dbri->mm.ctrl[1] &= ~ 0x38;
+ dbri->mm.ctrl[1] |= CS4215_FREQ[i].csval;
+ dbri->mm.ctrl[2] &= ~ 0x70;
+ dbri->mm.ctrl[2] |= CS4215_FREQ[i].xtal;
+
+ dbri->perchip_info.play.sample_rate = rate;
+
+ mmcodec_setctrl(dbri);
+ mmcodec_init_data(dbri);
return 0;
}
-static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc)
+static int dbri_get_output_rate(struct sparcaudio_driver *drv)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ return dbri->perchip_info.play.sample_rate;
}
-static int dbri_get_input_encoding(struct sparcaudio_driver *drv)
+static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate)
{
- return 0;
+ return dbri_set_output_rate(drv, rate);
}
-static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate)
+static int dbri_get_input_rate(struct sparcaudio_driver *drv)
{
- return 0;
+ return dbri_get_output_rate(drv);
}
-static int dbri_get_output_rate(struct sparcaudio_driver *drv)
+/******************* sparcaudio midlevel - ports ***********************/
+
+static int dbri_set_output_port(struct sparcaudio_driver *drv, int port)
{
return 0;
}
-static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate)
+static int dbri_get_output_port(struct sparcaudio_driver *drv)
{
return 0;
}
-static int dbri_get_input_rate(struct sparcaudio_driver *drv)
+static int dbri_set_input_port(struct sparcaudio_driver *drv, int port)
{
return 0;
}
-static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
+static int dbri_get_input_port(struct sparcaudio_driver *drv)
{
return 0;
}
return 0;
}
-static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute)
+/******************* sparcaudio midlevel - driver ID ********************/
+
+static void dbri_audio_getdev(struct sparcaudio_driver *drv,
+ audio_device_t *audinfo)
{
- return 0;
+ struct dbri *dbri = (struct dbri *)drv->private;
+
+ strncpy(audinfo->name, "SUNW,DBRI", sizeof(audinfo->name) - 1);
+
+ audinfo->version[0] = dbri->dbri_version;
+ audinfo->version[1] = '\0';
+
+ strncpy(audinfo->config, "onboard1", sizeof(audinfo->config) - 1);
}
-static int dbri_get_output_muted(struct sparcaudio_driver *drv)
+static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv)
{
- return 0;
+ return AUDIO_DEV_CODEC;
+}
+
+/******************* sparcaudio midlevel - open & close ******************/
+
+static int dbri_open(struct inode * inode, struct file * file,
+ struct sparcaudio_driver *drv)
+{
+ MOD_INC_USE_COUNT;
+
+ /* SunOS 5.5.1 audio(7I) man page says:
+ * "Upon the initial open() of the audio device, the driver
+ * will reset the data format of the device to the default
+ * state of 8-bit, 8KHz, mono u-law data."
+ *
+ * I've also taken the liberty of setting half gain and
+ * mid balance, to put the codec in a known state.
+ */
+
+ dbri_set_output_channels(drv, 1);
+ dbri_set_output_encoding(drv, AUDIO_ENCODING_ULAW);
+ dbri_set_output_rate(drv, 8000);
+
+ dbri_set_output_balance(drv, AUDIO_MID_BALANCE);
+ dbri_set_output_volume(drv, AUDIO_MAX_GAIN/2);
+
+ return 0;
}
+static void dbri_release(struct inode * inode, struct file * file,
+ struct sparcaudio_driver *drv)
+{
+ MOD_DEC_USE_COUNT;
+}
+static int dbri_ioctl(struct inode * inode, struct file * file,
+ unsigned int x, unsigned long y,
+ struct sparcaudio_driver *drv)
+{
+ return -EINVAL;
+}
+
+/*********** sparcaudio midlevel - struct sparcaudio_driver ************/
static struct sparcaudio_operations dbri_ops = {
dbri_open,
setup_pipe(dbri,11, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB);
link_time_slot(dbri, 0, PIPEinput, 0, 2, 17);
- link_time_slot(dbri, 8, PIPEinput, 8, 8, 0);
- link_time_slot(dbri, 9, PIPEinput, 9, 8, 8);
+ link_time_slot(dbri, 8, PIPEinput, 0, 8, 0);
+ link_time_slot(dbri, 9, PIPEinput, 8, 8, 8);
link_time_slot(dbri, 1, PIPEoutput, 1, 2, 17);
link_time_slot(dbri, 10, PIPEoutput, 1, 8, 0);
dbri = (struct dbri *) drivers[dev].private;
+ tprintk(("dbri_get_irqnum()\n"));
+
/* On the sparc, the cpu's irq number is only part of the "irq" */
return (dbri->irq & NR_IRQS);
}
dbri = (struct dbri *) drivers[dev].private;
+ tprintk(("dbri_get_liu_state() returns %d\n", dbri->liu_state));
+
return dbri->liu_state;
}
dbri = (struct dbri *) drivers[dev].private;
+ tprintk(("dbri_liu_init()\n"));
+
/* Set callback for LIU state change */
- dbri->liu_callback = callback;
+ dbri->liu_callback = callback;
dbri->liu_callback_arg = callback_arg;
- dbri_isdn_init(dbri);
- dbri_liu_activate(dev, 0);
+ dbri_isdn_init(dbri);
+ dbri_liu_activate(dev, 0);
}
void dbri_liu_activate(int dev, int priority)
dbri = (struct dbri *) drivers[dev].private;
- cmd = dbri_cmdlock(dbri);
+ tprintk(("dbri_liu_activate()\n"));
- /* Turn on the ISDN TE interface and request activation */
- val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT;
- *(cmd++) = DBRI_CMD(D_TE, 0, val);
+ if (dbri->liu_state <= 3) {
- dbri_cmdsend(dbri, cmd);
+ cmd = dbri_cmdlock(dbri);
+
+ /* Turn on the ISDN TE interface and request activation */
+ val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT;
+#ifdef LOOPBACK_D
+ val |= D_NT_LLB(4);
+#endif
+ *(cmd++) = DBRI_CMD(D_TE, 0, val);
- /* Activate the interface */
- dbri->regs->reg0 |= D_T;
+ dbri_cmdsend(dbri, cmd);
+
+ /* Activate the interface */
+ dbri->regs->reg0 |= D_T;
+ }
}
void dbri_liu_deactivate(int dev)
dbri = (struct dbri *) drivers[dev].private;
+ tprintk(("dbri_liu_deactivate()\n"));
+
+#if 0
/* Turn off the ISDN TE interface */
dbri->regs->reg0 &= ~D_T;
+
+ dbri->liu_state = 0;
+#endif
}
void dbri_dxmit(int dev, __u8 *buffer, unsigned int count,
"DBRI DMA Cmd Block", &dma_dvma);
dbri->dma_dvma = (struct dbri_dma *) dma_dvma;
+ memset((void *) dbri->dma, 0, sizeof(struct dbri_dma));
+
+ dprintk(D_GEN, ("DBRI: DMA Cmd Block 0x%08x (0x%08x)\n",
+ (int)dbri->dma, (int)dbri->dma_dvma));
+
dbri->dbri_version = sdev->prom_name[9];
dbri->sdev = sdev;
"DBRI Registers", sdev->reg_addrs[0].which_io, 0);
if (!dbri->regs) {
printk(KERN_ERR "DBRI: could not allocate registers\n");
+ release_region((unsigned long) dbri->dma,
+ sizeof(struct dbri_dma));
kfree(drv->private);
return -EIO;
}
if (err) {
printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
sparc_free_io(dbri->regs, dbri->regs_size);
+ release_region((unsigned long) dbri->dma,
+ sizeof(struct dbri_dma));
kfree(drv->private);
return err;
}
struct dbri_mem desc[DBRI_NO_DESCS]; /* Xmit/receive descriptors */
};
+enum in_or_out { PIPEinput, PIPEoutput };
+
+enum direction { in, out };
+
struct dbri_pipe {
u32 sdp; /* SDP command word */
+ enum direction direction;
int nextpipe; /* Next pipe in linked list */
+ int prevpipe;
int cycle; /* Offset of timeslot (bits) */
int length; /* Length of timeslot (bits) */
int desc; /* Index of active descriptor*/
- __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */
+ volatile __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */
};
struct dbri_desc {
struct dbri_regs *regs; /* dbri HW regs */
int dbri_version; /* 'e' and up is OK */
int dbri_irqp; /* intr queue pointer */
+ int wait_seen;
struct dbri_pipe pipes[32]; /* DBRI's 32 data pipes */
struct dbri_desc descs[DBRI_NO_DESCS];
+ int chi_in_pipe;
+ int chi_out_pipe;
+ int chi_bpf;
+
struct cs4215 mm; /* mmcodec special info */
#if 0
/* Time Slot defines */
#define D_TS_LEN(v) ((v)<<24) /* Number of bits in this time slot */
#define D_TS_CYCLE(v) ((v)<<14) /* Bit Count at start of TS */
-#define D_TS_DI(v) (1<<13) /* Data Invert */
+#define D_TS_DI (1<<13) /* Data Invert */
#define D_TS_1CHANNEL (0<<10) /* Single Channel / Normal mode */
#define D_TS_MONITOR (2<<10) /* Monitor pipe */
#define D_TS_NONCONTIG (3<<10) /* Non contiguous mode */
#define D_NT_IFA (1<<10) /* Inhibit Final Activation */
#define D_NT_ACT (1<<9) /* Activate Interface */
#define D_NT_MFE (1<<8) /* Multiframe Enable */
-#define D_NT_RLB(v) (1<<5) /* Remote Loopback */
-#define D_NT_LLB(v) (1<<2) /* Local Loopback */
+#define D_NT_RLB(v) ((v)<<5) /* Remote Loopback */
+#define D_NT_LLB(v) ((v)<<2) /* Local Loopback */
#define D_NT_FACT (1<<1) /* Force Activation */
#define D_NT_ABV (1<<0) /* Activate Bipolar Violation */
-/* $Id: su.c,v 1.20 1999/06/03 15:02:40 davem Exp $
+/* $Id: su.c,v 1.21 1999/06/11 10:23:42 davem Exp $
* su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
struct su_struct *info = su_table;
int lsr;
- if (!info->port_type != SU_PORT_KBD)
+ if (info->port_type != SU_PORT_KBD)
++info;
- if (!info->port_type != SU_PORT_KBD)
+ if (info->port_type != SU_PORT_KBD)
return;
do {
{
struct su_struct *info = su_table;
- if (!info->port_type != SU_PORT_MS)
+ if (info->port_type != SU_PORT_MS)
++info;
- if (!info->port_type != SU_PORT_MS)
+ if (info->port_type != SU_PORT_MS)
return;
info->cflag &= ~(CBAUDEX | CBAUD);
*/
__initfunc(static __inline__ void show_su_version(void))
{
- char *revision = "$Revision: 1.20 $";
+ char *revision = "$Revision: 1.21 $";
char *version, *p;
version = strchr(revision, ' ');
orl %%ecx, %%ecx \n \
jz 1f \n \
rep \n \
- insw %%dx \n \
+ insw (%%dx),%%es:(%%edi) \n \
1: " \
: "=D" (sp) /* output */ \
: "d" (f), "D" (sp), "c" (i) /* input */ \
orl %%ecx, %%ecx \n \
jz 1f \n \
rep \n \
- outsw %%dx \n \
+ outsw %%ds:(%%esi),(%%dx) \n \
1: " \
: "=S" (sp) /* output */ \
: "d" (f), "S" (sp), "c" (i) /* input */ \
#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
fillertype = TYPE(format, channels);
-printk("filler type: %X\n", fillertype);
+
switch (fillertype)
{
default:
sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype);
conf_printf(name, hw_config);
+ memset(dma_buf, 0, sizeof(dma_buf));
for (i = 0; i < 2; i++)
{
dma_buf[i] = get_free_page(GFP_KERNEL);
+ if (!dma_buf[i])
+ goto nomem;
dma_pbuf[i] = virt_to_phys(dma_buf[i]);
}
printk(KERN_ERR "VIDCsound: can't allocate DMA interrupt\n");
return;
}
+
// vidc_synth_init(hw_config);
vidc_audio_init(hw_config);
vidc_mixer_init(hw_config);
+ return;
+
+nomem:
+ for (i = 0; i < 2; i++)
+ free_page(dma_buf[i]);
+ printk(KERN_ERR "VIDCsound: can't allocate required buffers\n");
}
int probe_vidc(struct address_info *hw_config)
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/system.h>
+#include <asm/iomd.h>
#include "sound_config.h"
#include "vidc.h"
static int vidc_audio_set_bits(int fmt)
{
-printk("setting format: %d\n", fmt);
switch (fmt)
{
case AFMT_QUERY:
if (!(adev->flags & DMA_ACTIVE))
{
unsigned long flags;
-printk("kicking output: %lX+%lX [%lX]\n", dma_start, dma_count, *(unsigned long *)dma_start);
save_flags_cli(flags);
+
vidc_sound_dma_irq(0, NULL, NULL);
outb(DMA_CR_E | 0x10, IOMD_SD0CR);
+
restore_flags(flags);
}
}
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/hardware.h>
+#include <asm/iomd.h>
.text
#include <linux/delay.h>
#include <linux/smp.h>
+#include <asm/dec21285.h>
#include <asm/hardware.h>
#include "soundmodule.h"
#include "sound_config.h"
#include "waveartist.h"
+#ifndef _ISA_DMA
+#define _ISA_DMA(x) (x)
+#endif
+#ifndef _ISA_IRQ
+#define _ISA_IRQ(x) (x)
+#endif
+
#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec
#define MIXER_PRIVATE3_RESET 0x53570000
static int nr_waveartist_devs;
static wavnc_info adev_info[MAX_AUDIO_DEV];
+
+static int waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level);
+
+/*
+ * Corel Netwinder specifics...
+ */
static struct timer_list vnc_timer;
+extern spinlock_t gpio_lock;
+
+static void
+vnc_mute(wavnc_info *devc, int mute)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ devc->mute_state = mute;
+}
+
+static int
+vnc_volume_slider(wavnc_info *devc)
+{
+ static signed int old_slider_volume;
+ unsigned long flags;
+ signed int volume = 255;
+
+ *CSR_TIMER1_LOAD = 0x00ffffff;
+
+ save_flags(flags);
+ cli();
+
+ outb(0xFF, 0x201);
+ *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
+
+ while (volume && (inb(0x201) & 0x01))
+ volume--;
+
+ *CSR_TIMER1_CNTL = 0;
+
+ restore_flags(flags);
+
+ volume = 0x00ffffff - *CSR_TIMER1_VALUE;
+
+
+#ifndef REVERSE
+ volume = 150 - (volume >> 5);
+#else
+ volume = (volume >> 6) - 25;
+#endif
+
+ if (volume < 0)
+ volume = 0;
+
+ if (volume > 100)
+ volume = 100;
+
+ /*
+ * slider quite often reads +-8, so debounce this random noise
+ */
+ if ((volume - old_slider_volume) > 7 ||
+ (old_slider_volume - volume) > 7) {
+ old_slider_volume = volume;
+
+ DEB(printk("Slider volume: %d.\n", old_slider_volume));
+ }
+
+ return old_slider_volume;
+}
+
+static int
+vnc_slider(wavnc_info *devc)
+{
+ signed int slider_volume;
+ unsigned int temp;
+
+ /*
+ * read the "buttons" state.
+ * Bit 4 = handset present,
+ * Bit 5 = offhook
+ */
+ // the state should be "querable" via a private IOCTL call
+ temp = inb(0x201) & 0x30;
+
+ if (!devc->handset_mute_sw &&
+ (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) {
+ devc->handset_state = temp;
+ devc->handset_mute_sw = 0;
+
+ vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0);
+ }
+
+ slider_volume = vnc_volume_slider(devc);
+
+ /*
+ * If we're using software controlled volume, and
+ * the slider moves by more than 20%, then we
+ * switch back to slider controlled volume.
+ */
+ if (devc->slider_vol > slider_volume) {
+ if (devc->slider_vol - slider_volume > 20)
+ devc->use_slider = 1;
+ } else {
+ if (slider_volume - devc->slider_vol > 20)
+ devc->use_slider = 1;
+ }
+
+ /*
+ * use only left channel
+ */
+ temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
+
+ if (slider_volume != temp && devc->use_slider) {
+ devc->slider_vol = slider_volume;
+
+ waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
+ slider_volume | slider_volume << 8);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+vnc_slider_tick(unsigned long data)
+{
+ int next_timeout;
+
+ if (vnc_slider(adev_info + data))
+ next_timeout = 5; // mixer reported change
+ else
+ next_timeout = VNC_TIMER_PERIOD;
+
+ mod_timer(&vnc_timer, jiffies + next_timeout);
+}
+
+static int
+vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+ wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
+ int val, temp;
+
+ if (cmd == SOUND_MIXER_PRIVATE1) {
+ /*
+ * Use this call to override the automatic handset
+ * behaviour - ignore handset.
+ * bit 7 = total control over handset - do not
+ * to plug/unplug
+ * bit 4 = internal mic
+ * bit 0 = mute internal speaker
+ */
+ if (get_user(val, (int *)arg))
+ return -EFAULT;
+
+ devc->handset_mute_sw = val;
+
+ temp = val & VNC_INTERNAL_SPKR;
+ if (devc->mute_state != temp)
+ vnc_mute(devc, temp);
+
+ devc->handset_state = val & VNC_INTERNAL_MIC;
+ waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
+ return 0;
+ }
+#if 0
+ if (cmd == SOUND_MIXER_PRIVATE2) {
+#define VNC_SOUND_PAUSE 0x53 //to pause the DSP
+#define VNC_SOUND_RESUME 0x57 //to unpause the DSP
+ int val;
+
+ val = *(int *) arg;
+
+ printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val);
+ if (val == VNC_SOUND_PAUSE) {
+ wa_sendcmd(0x16); //PAUSE the ADC
+ } else if (val == VNC_SOUND_RESUME) {
+ wa_sendcmd(0x18); //RESUME the ADC
+ } else {
+ return -EINVAL; //invalid parameters...
+ }
+ return 0;
+ }
+
+ if (cmd == SOUND_MIXER_PRIVATE3) {
+ long unsigned flags;
+ int mixer_reg[15]; //reg 14 is actually a command: read,write,reset
+
+ int val;
+ int i;
+
+ val = *(int *) arg;
+
+ if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT))
+ return (-EFAULT);
+
+ memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg));
+
+ if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { //reset command??
+ wavnc_mixer_reset(devc);
+ return (0);
+ } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) { //write command??
+// printk("WaveArtist Mixer: Private write command.\n");
+
+ wa_sendcmd(0x32); //Pair1 - word 1 and 5
+ wa_sendcmd(mixer_reg[0]);
+ wa_sendcmd(mixer_reg[4]);
+
+ wa_sendcmd(0x32); //Pair2 - word 2 and 6
+ wa_sendcmd(mixer_reg[1]);
+ wa_sendcmd(mixer_reg[5]);
+
+ wa_sendcmd(0x32); //Pair3 - word 3 and 7
+ wa_sendcmd(mixer_reg[2]);
+ wa_sendcmd(mixer_reg[6]);
+
+ wa_sendcmd(0x32); //Pair4 - word 4 and 8
+ wa_sendcmd(mixer_reg[3]);
+ wa_sendcmd(mixer_reg[7]);
+
+ wa_sendcmd(0x32); //Pair5 - word 9 and 10
+ wa_sendcmd(mixer_reg[8]);
+ wa_sendcmd(mixer_reg[9]);
+
+ wa_sendcmd(0x0031); //set left and right PCM
+ wa_sendcmd(mixer_reg[0x0A]);
+ wa_sendcmd(mixer_reg[0x0B]);
+
+ wa_sendcmd(0x0131); //set left and right FM
+ wa_sendcmd(mixer_reg[0x0C]);
+ wa_sendcmd(mixer_reg[0x0D]);
+
+ return 0;
+ } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) { //read command?
+// printk("WaveArtist Mixer: Private read command.\n");
+
+ //first read all current values...
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < 14; i++) {
+ wa_sendcmd((i << 8) + 0x30); // get ready for command nn30H
+
+ while (!(inb(STATR) & CMD_RF)) {
+ }; //wait for response ready...
+
+ mixer_reg[i] = inw(CMDR);
+ }
+ restore_flags(flags);
+
+ if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT))
+ return (-EFAULT);
+
+ memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg));
+ return 0;
+ } else
+ return -EINVAL;
+ }
+#endif
+ return -EINVAL;
+}
static inline void
waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set)
waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
-#if 0
- //use this call to override the automatic handset behaviour - ignore handset
- //bit 0x80 = total control over handset - do not react to plug/unplug
- //bit 0x10 = 1 == internal mic, otherwise handset mic
- //bit 0x01 = 1 == mute internal speaker, otherwise unmute
-
- if (cmd == SOUND_MIXER_PRIVATE1) {
- int val, temp;
-
- val = *(int *) arg;
-
-// printk("MIXER_PRIVATE1: passed parameter = 0x%X.\n",val);
- return -EINVAL; //check if parameter is logical...
-
- devc->soft_mute_flag = val;
-
- temp = val & VNC_INTERNAL_SPKR;
- if (temp != devc->mute_state) {
-// printk("MIXER_PRIVATE1: mute_mono(0x%X).\n",temp);
- vnc_mute(devc, temp);
- }
-
-// temp = devc->handset_state;
-
- // do not check if it is not already in
- // the right setting, since we are
- // laying about the current state...
-
-// if ((val & VNC_INTERNAL_MIC) != temp) {
- devc->handset_state = val & VNC_INTERNAL_MIC;
-// printk("MIXER_PRIVATE1: mixer_set(0x%X).\n",devc->handset_state);
- wa_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC);
-// devc->handset_state = temp;
- }
- return 0;
- }
-#if 0
- if (cmd == SOUND_MIXER_PRIVATE2) {
-#define VNC_SOUND_PAUSE 0x53 //to pause the DSP
-#define VNC_SOUND_RESUME 0x57 //to unpause the DSP
- int val;
-
- val = *(int *) arg;
-
- printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val);
-
- if (val == VNC_SOUND_PAUSE) {
- wa_sendcmd(0x16); //PAUSE the ADC
- } else if (val == VNC_SOUND_RESUME) {
- wa_sendcmd(0x18); //RESUME the ADC
- } else {
- return -EINVAL; //invalid parameters...
- }
- return 0;
- }
-#endif
- if (cmd == SOUND_MIXER_PRIVATE3) {
- long unsigned flags;
- int mixer_reg[15]; //reg 14 is actually a command: read,write,reset
-
- int val;
- int i;
-
- val = *(int *) arg;
-
- if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT))
- return (-EFAULT);
-
- memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg));
-
- if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { //reset command??
- wavnc_mixer_reset(devc);
- return (0);
- } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) { //write command??
-// printk("WaveArtist Mixer: Private write command.\n");
-
- wa_sendcmd(0x32); //Pair1 - word 1 and 5
- wa_sendcmd(mixer_reg[0]);
- wa_sendcmd(mixer_reg[4]);
+ if (cmd == SOUND_MIXER_PRIVATE1 ||
+ cmd == SOUND_MIXER_PRIVATE2 ||
+ cmd == SOUND_MIXER_PRIVATE3)
+ return vnc_private_ioctl(dev, cmd, arg);
- wa_sendcmd(0x32); //Pair2 - word 2 and 6
- wa_sendcmd(mixer_reg[1]);
- wa_sendcmd(mixer_reg[5]);
-
- wa_sendcmd(0x32); //Pair3 - word 3 and 7
- wa_sendcmd(mixer_reg[2]);
- wa_sendcmd(mixer_reg[6]);
-
- wa_sendcmd(0x32); //Pair4 - word 4 and 8
- wa_sendcmd(mixer_reg[3]);
- wa_sendcmd(mixer_reg[7]);
-
- wa_sendcmd(0x32); //Pair5 - word 9 and 10
- wa_sendcmd(mixer_reg[8]);
- wa_sendcmd(mixer_reg[9]);
-
- wa_sendcmd(0x0031); //set left and right PCM
- wa_sendcmd(mixer_reg[0x0A]);
- wa_sendcmd(mixer_reg[0x0B]);
-
- wa_sendcmd(0x0131); //set left and right FM
- wa_sendcmd(mixer_reg[0x0C]);
- wa_sendcmd(mixer_reg[0x0D]);
-
- return 0;
- } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) { //read command?
-// printk("WaveArtist Mixer: Private read command.\n");
-
- //first read all current values...
- save_flags(flags);
- cli();
-
- for (i = 0; i < 14; i++) {
- wa_sendcmd((i << 8) + 0x30); // get ready for command nn30H
-
- while (!(inb(STATR) & CMD_RF)) {
- }; //wait for response ready...
-
- mixer_reg[i] = inw(CMDR);
- }
- restore_flags(flags);
-
- if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT))
- return (-EFAULT);
-
- memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg));
- return 0;
- } else
- return -EINVAL;
- }
-#endif
if (((cmd >> 8) & 0xff) == 'M') {
if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
int val;
return -1;
}
-/*
- * Corel Netwinder specifics...
- */
-static void
-vnc_mute(wavnc_info *devc, int mute)
-{
- extern spinlock_t gpio_lock;
- unsigned long flags;
-
- spin_lock_irqsave(&gpio_lock, flags);
- cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE);
- spin_unlock_irqrestore(&gpio_lock, flags);
-
- devc->mute_state = mute;
-}
-
-static int
-vnc_volume_slider(wavnc_info *devc)
-{
- static signed int old_slider_volume;
- unsigned long flags;
- signed int volume = 255;
-
- *CSR_TIMER1_LOAD = 0x00ffffff;
-
- save_flags(flags);
- cli();
-
- outb(0xFF, 0x201);
- *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
-
- while (volume && (inb(0x201) & 0x01))
- volume--;
-
- *CSR_TIMER1_CNTL = 0;
-
- restore_flags(flags);
-
- volume = 0x00ffffff - *CSR_TIMER1_VALUE;
-
-
-#ifndef REVERSE
- volume = 150 - (volume >> 5);
-#else
- volume = (volume >> 6) - 25;
-#endif
-
- if (volume < 0)
- volume = 0;
-
- if (volume > 100)
- volume = 100;
-
- /*
- * slider quite often reads +-8, so debounce this random noise
- */
- if ((volume - old_slider_volume) > 7 ||
- (old_slider_volume - volume) > 7) {
- old_slider_volume = volume;
-
- DEB(printk("Slider volume: %d.\n", old_slider_volume));
- }
-
- return old_slider_volume;
-}
-
-static int
-vnc_slider(wavnc_info *devc)
-{
- signed int slider_volume;
- unsigned int temp;
-
- /*
- * read the "buttons" state.
- * Bit 4 = handset present,
- * Bit 5 = offhook
- */
- // the state should be "querable" via a private IOCTL call
- temp = inb(0x201) & 0x30;
-
- if (!devc->handset_mute_sw &&
- (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) {
- devc->handset_state = temp;
- devc->handset_mute_sw = 0;
-
- vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0);
- }
-
- slider_volume = vnc_volume_slider(devc);
-
- /*
- * If we're using software controlled volume, and
- * the slider moves by more than 20%, then we
- * switch back to slider controlled volume.
- */
- if (devc->slider_vol > slider_volume) {
- if (devc->slider_vol - slider_volume > 20)
- devc->use_slider = 1;
- } else {
- if (slider_volume - devc->slider_vol > 20)
- devc->use_slider = 1;
- }
-
- /*
- * use only left channel
- */
- temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
-
- if (slider_volume != temp && devc->use_slider) {
- devc->slider_vol = slider_volume;
-
- waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,
- slider_volume | slider_volume << 8);
-
- return 1;
- }
-
- return 0;
-}
-
-static void
-vnc_slider_tick(unsigned long data)
-{
- int next_timeout;
-
- if (vnc_slider(adev_info + data))
- next_timeout = 5; // mixer reported change
- else
- next_timeout = VNC_TIMER_PERIOD;
-
- mod_timer(&vnc_timer, jiffies + next_timeout);
-}
-
int
probe_waveartist(struct address_info *hw_config)
{
return 0;
}
- if (hw_config->irq > 31 || hw_config->irq < 16) {
+ if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) {
printk("WaveArtist: Bad IRQ %d\n", hw_config->irq);
return 0;
}
- if (hw_config->dma != 3) {
+ if (hw_config->dma != _ISA_DMA(3)) {
printk("WaveArtist: Bad DMA %d\n", hw_config->dma);
return 0;
}
// def file for Rockwell RWA010 chip set, as installed in Corel NetWinder
//registers
-#define WA_BASE 0
-//x250
-
-#define CMDR WA_BASE+0
-#define DATR WA_BASE+2
-
-#define CTLR WA_BASE+4
-#define STATR WA_BASE+5
-
-#define IRQSTAT WA_BASE+12
+#define CMDR 0
+#define DATR 2
+#define CTLR 4
+#define STATR 5
+#define IRQSTAT 12
//bit defs
//reg STATR
bandwidth = var->pixclock * 8 / var->bits_per_pixel;
/* 25.175, 4bpp = 79.444ns per byte, 317.776ns per word: fifo = 2,6 */
- if (bandwidth > 71750)
+ if (bandwidth > 143500)
+ vidc_ctl |= VIDC_CTRL_FIFO_3_7;
+ else if (bandwidth > 71750)
vidc_ctl |= VIDC_CTRL_FIFO_2_6;
else if (bandwidth > 35875)
vidc_ctl |= VIDC_CTRL_FIFO_1_5;
1600, 1200,
{
0xff, 0xc7, 0xc9, 0x9f, 0xcf, 0xa0, 0xfe, 0x10,
- 0x00, 0x40,
+ 0x00, 0x40,
0xcf, 0x89, 0xaf, 0xc8, 0x00, 0xbc, 0xf1, 0xe3
},
0x1f,
debug_printf("init vga hw for %dx%d\n", res->xres, res->yres);
cyber2000_outb(0xef, 0x3c2);
- cyber2000_crtcw(0x0b, 0x11);
- cyber2000_attrw(0x00, 0x11);
+ cyber2000_crtcw(0x11, 0x0b);
+ cyber2000_attrw(0x11, 0x00);
- cyber2000_seqw(0x01, 0x00);
+ cyber2000_seqw(0x00, 0x01);
cyber2000_seqw(0x01, 0x01);
- cyber2000_seqw(0x0f, 0x02);
- cyber2000_seqw(0x00, 0x03);
- cyber2000_seqw(0x0e, 0x04);
+ cyber2000_seqw(0x02, 0x0f);
cyber2000_seqw(0x03, 0x00);
+ cyber2000_seqw(0x04, 0x0e);
+ cyber2000_seqw(0x00, 0x03);
for (i = 0; i < sizeof(crtc_idx); i++)
- cyber2000_crtcw(res->crtc_regs[i], crtc_idx[i]);
+ cyber2000_crtcw(crtc_idx[i], res->crtc_regs[i]);
for (i = 0x0a; i < 0x10; i++)
- cyber2000_crtcw(0, i);
+ cyber2000_crtcw(i, 0);
- cyber2000_crtcw(0xff, 0x18);
+ cyber2000_crtcw(0x18, 0xff);
cyber2000_grphw(0x00, 0x00);
- cyber2000_grphw(0x00, 0x01);
- cyber2000_grphw(0x00, 0x02);
- cyber2000_grphw(0x00, 0x03);
- cyber2000_grphw(0x00, 0x04);
- cyber2000_grphw(0x60, 0x05);
- cyber2000_grphw(0x05, 0x06);
- cyber2000_grphw(0x0f, 0x07);
- cyber2000_grphw(0xff, 0x08);
+ cyber2000_grphw(0x01, 0x00);
+ cyber2000_grphw(0x02, 0x00);
+ cyber2000_grphw(0x03, 0x00);
+ cyber2000_grphw(0x04, 0x00);
+ cyber2000_grphw(0x05, 0x60);
+ cyber2000_grphw(0x06, 0x05);
+ cyber2000_grphw(0x07, 0x0f);
+ cyber2000_grphw(0x08, 0xff);
for (i = 0; i < 16; i++)
cyber2000_attrw(i, i);
- cyber2000_attrw(0x01, 0x10);
- cyber2000_attrw(0x00, 0x11);
- cyber2000_attrw(0x0f, 0x12);
- cyber2000_attrw(0x00, 0x13);
- cyber2000_attrw(0x00, 0x14);
+ cyber2000_attrw(0x10, 0x01);
+ cyber2000_attrw(0x11, 0x00);
+ cyber2000_attrw(0x12, 0x0f);
+ cyber2000_attrw(0x13, 0x00);
+ cyber2000_attrw(0x14, 0x00);
for (i = 0; i < sizeof(igs_regs); i += 2)
- cyber2000_grphw(igs_regs[i+1], igs_regs[i]);
+ cyber2000_grphw(igs_regs[i], igs_regs[i+1]);
- cyber2000_grphw(res->crtc_ofl, 0x11);
+ cyber2000_grphw(0x11, res->crtc_ofl);
for (i = 0; i < 4; i += 1)
- cyber2000_grphw(res->clk_regs[i], 0xb0 + i);
+ cyber2000_grphw(0xb0 + i, res->clk_regs[i]);
- cyber2000_grphw(0x01, 0x90);
- cyber2000_grphw(0x80, 0xb9);
- cyber2000_grphw(0x00, 0xb9);
+ cyber2000_grphw(0x90, 0x01);
+ cyber2000_grphw(0xb9, 0x80);
+ cyber2000_grphw(0xb9, 0x00);
cyber2000_outb(0x56, 0x3ce);
i = cyber2000_inb(0x3cf);
cyber2000_outw(height, 0xbf062);
switch (p->var.bits_per_pixel) {
+ case 15:
case 16:
bgx = ((u16 *)p->dispsw_data)[bgx];
case 8:
current_par.palette[regno].blue = blue;
switch (fb_display[current_par.currcon].var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
case 8:
cyber2000_outb(regno, 0x3c8);
cyber2000_outb(red, 0x3c9);
cyber2000_outb(green, 0x3c9);
cyber2000_outb(blue, 0x3c9);
break;
+#endif
#ifdef FBCON_HAS_CFB16
+ case 15:
+ if (regno < 32) {
+ cyber2000_outb(regno << 3, 0x3c8);
+ cyber2000_outb(red, 0x3c9);
+ cyber2000_outb(green, 0x3c9);
+ cyber2000_outb(blue, 0x3c9);
+ }
+ if (regno < 16)
+ current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10;
+ break;
+
case 16:
if (regno < 64) {
/* write green */
return 0;
}
-static int cyber2000fb_set_timing(struct fb_var_screeninfo *var)
+static void cyber2000fb_calculate_timing(unsigned char *v, struct fb_var_screeninfo *var)
{
- int width = var->xres_virtual;
- int scr_pitch, fetchrow;
- int i;
- char b, col;
+ int Htotal, Hdispend, Hblankstart, Hblankend, Hsyncstart, Hsyncend;
+ int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend;
+#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2)
+
+ Hdispend = var->xres;
+ Hsyncstart = var->xres + var->right_margin;
+ Hsyncend = var->xres + var->right_margin + var->hsync_len;
+ Htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin;
+
+ Hblankstart = var->xres;
+ Hblankend = Htotal - 4*8;
+
+ Vdispend = var->yres;
+ Vsyncstart = var->yres + var->lower_margin;
+ Vsyncend = var->yres + var->lower_margin + var->vsync_len;
+ Vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin;
+
+ Vblankstart = var->yres + 7;
+ Vblankend = Vtotal - 11;
+
+ Hdispend >>= 3;
+ Hsyncstart >>= 3;
+ Hsyncend >>= 3;
+ Htotal >>= 3;
+ Hblankstart >>= 3;
+ Hblankend >>= 3;
+
+ Htotal -= 5;
+ Hdispend -= 1;
+ Vtotal -= 2;
+ Vdispend -= 1;
+ Vblankstart -= 1;
+ Vblankend -= 1;
+
+ v[0] = Htotal;
+ v[1] = Hdispend;
+ v[2] = Hblankstart;
+ v[3] = BIT(Hblankend, 0, 0x1f, 0) |
+ BIT(1, 0, 0x01, 7);
+ v[4] = Hsyncstart;
+ v[5] = BIT(Hsyncend, 0, 0x1f, 0) |
+ BIT(Hblankend, 5, 0x01, 7);
+
+ v[6] = Vtotal;
+ v[7] = BIT(Vtotal, 8, 0x01, 0) |
+ BIT(Vdispend, 8, 0x01, 1) |
+ BIT(Vsyncstart, 8, 0x01, 2) |
+ BIT(Vblankstart,8, 0x01, 3) |
+ BIT(1, 0, 0x01, 4) |
+ BIT(Vtotal, 9, 0x01, 5) |
+ BIT(Vdispend, 9, 0x01, 6) |
+ BIT(Vsyncstart, 9, 0x01, 7);
+ v[8] = 0;
+ v[9] = BIT(0, 0, 0x1f, 0) |
+ BIT(Vblankstart,9, 0x01, 5) |
+ BIT(1, 0, 0x01, 6);
+ v[10] = Vsyncstart;
+ v[11] = BIT(Vsyncend, 0, 0x0f, 0) |
+ BIT(1, 0, 0x01, 7);
+ v[12] = Vdispend;
+ v[14] = 0;
+ v[15] = Vblankstart;
+ v[16] = Vblankend;
+ v[17] = 0xe3;
+
+ /* overflow - graphics reg 0x11 */
+ v[18] = BIT(Vtotal, 10, 0x01, 0) | /* guess */
+ BIT(Vdispend, 10, 0x01, 1) |
+ BIT(Vsyncstart, 10, 0x01, 2) | /* guess */
+ BIT(Vblankstart,10, 0x01, 3) | /* guess */
+ BIT(Hblankend, 6, 0x01, 4); /* guess */
+}
+
+static void cyber2000fb_set_timing(struct fb_var_screeninfo *var)
+{
+ unsigned int width = var->xres_virtual;
+ unsigned int scr_pitch, fetchrow, i;
+ char b, graph_r77, crtc[32];
switch (var->bits_per_pixel) {
case 8: /* PSEUDOCOLOUR, 256 */
b = 0;
- col = 1;
- scr_pitch = var->xres_virtual / 8;
+ graph_r77 = 1;
+ scr_pitch = width;
+ break;
+
+ case 15:/* DIRECTCOLOUR, 32k */
+ b = 1;
+ graph_r77 = 6;
+ scr_pitch = width * 2;
break;
case 16:/* DIRECTCOLOUR, 64k */
b = 1;
- col = 2;
- scr_pitch = var->xres_virtual / 8 * 2;
+ graph_r77 = 2;
+ scr_pitch = width * 2;
break;
+
case 24:/* TRUECOLOUR, 16m */
b = 2;
- col = 4;
- scr_pitch = var->xres_virtual / 8 * 3;
+ graph_r77 = 4;
width *= 3;
+ scr_pitch = width;
break;
default:
- return 1;
+ return;
}
+ width -= 1;
+ scr_pitch >>= 3;
+ fetchrow = scr_pitch + 1;
+
+ cyber2000fb_calculate_timing(crtc, var);
+
for (i = 0; i < NUM_TOTAL_MODES; i++)
if (var->xres == cyber2000_res[i].xres &&
var->yres == cyber2000_res[i].yres)
if (i < NUM_TOTAL_MODES)
cyber2000_init_hw(cyber2000_res + i);
- fetchrow = scr_pitch + 1;
+ crtc[13] = scr_pitch;
- debug_printf("Setting regs: pitch=%X, fetchrow=%X, col=%X, b=%X\n",
- scr_pitch, fetchrow, col, b);
+ /*
+ * reprogram the CRTC with the values we calculated
+ * above. This should be cleaned up once we're
+ * confident that we're generating the correct
+ * values. Disable this if you're having problems,
+ * and report the values obtained from the kernel
+ * messages.
+ */
+#if 1
+ cyber2000_crtcw(0x11, 0x0b);
+ for (i = 0; i < sizeof(crtc_idx); i++)
+ cyber2000_crtcw(crtc_idx[i], crtc[i]);
+#else
+ cyber2000_crtcw(0x13, crtc[13]);
+#endif
- cyber2000_outb(0x13, 0x3d4);
- cyber2000_outb(scr_pitch, 0x3d5);
- cyber2000_outb(0x14, 0x3ce);
- cyber2000_outb(fetchrow, 0x3cf);
- cyber2000_outb(0x15, 0x3ce);
+ cyber2000_grphw(0x14, fetchrow);
/* FIXME: is this the right way round? */
- cyber2000_outb(((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f), 0x3cf);
- cyber2000_outb(0x77, 0x3ce);
- cyber2000_outb(col, 0x3cf);
-
-
- cyber2000_outb(0x33, 0x3ce);
- cyber2000_outb(0x1c, 0x3cf);
-
- cyber2000_outw(width - 1, 0xbf018);
- cyber2000_outw(width - 1, 0xbf218);
- cyber2000_outb(b, 0xbf01c);
-
- return 0;
+ cyber2000_grphw(0x15, ((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f));
+ cyber2000_grphw(0x77, graph_r77);
+ cyber2000_grphw(0x33, 0x1c);
+
+ cyber2000_outw(width, 0xbf018);
+ cyber2000_outw(width, 0xbf218);
+ cyber2000_outb(b, 0xbf01c);
+
+{ int j;
+ printk(KERN_DEBUG);
+ for (j = 0; j < 19; j++) printk("%2d ", j); printk("\n"KERN_DEBUG);
+ for (j = 0; j < 19; j++) printk("%02X ", crtc[j]); printk("\n"KERN_DEBUG);
+ for (j = 0; j < 18; j++) printk("%02X ", cyber2000_res[i].crtc_regs[j]);
+ printk("%02X\n", cyber2000_res[i].crtc_ofl);
+}
}
static inline void
base = var->yoffset * var->xres_virtual + var->xoffset;
- cyber2000_outb(0x0c, 0x3d4);
- cyber2000_outb(base, 0x3d5);
- cyber2000_outb(0x0d, 0x3d4);
- cyber2000_outb(base >> 8, 0x3d5);
+ cyber2000_crtcw(0x0c, base);
+ cyber2000_crtcw(0x0d, base >> 8);
/* FIXME: need the upper bits of the start offset */
-/* cyber2000_outb(0x??, 0x3d4);
- cyber2000_outb(base >> 16, 0x3d5);*/
+/* cyber2000_crtcw(0x??, base >> 16);*/
#endif
}
break;
#endif
#ifdef FBCON_HAS_CFB16
+ case 15:
case 16:
*visual = FB_VISUAL_DIRECTCOLOR;
break;
}
display->var = *var;
+ display->var.activate &= ~FB_ACTIVATE_ALL;
+
+ if (var->activate & FB_ACTIVATE_ALL)
+ global_disp.var = display->var;
display->screen_base = (char *)current_par.screen_base;
display->visual = visual;
display->type_aux = 0;
display->ypanstep = 0;
display->ywrapstep = 0;
- display->line_length =
- display->next_line = (var->xres_virtual * var->bits_per_pixel) / 8;
display->can_soft_blank = 1;
display->inverse = 0;
case 8:
dispsw = &fbcon_cfb8;
display->dispsw_data = NULL;
+ display->next_line = var->xres_virtual;
break;
#endif
#ifdef FBCON_HAS_CFB16
+ case 15:
case 16:
dispsw = &fbcon_cfb16;
display->dispsw_data = current_par.c_table.cfb16;
+ display->next_line = var->xres_virtual * 2;
break;
#endif
#ifdef FBCON_HAS_CFB24
case 24:
dispsw = &fbcon_cfb24;
display->dispsw_data = current_par.c_table.cfb24;
+ display->next_line = var->xres_virtual * 3;
break;
#endif
default:
break;
}
+ display->line_length = display->next_line;
+
if (display->var.accel_flags & FB_ACCELF_TEXT &&
dispsw != &fbcon_dummy)
display->dispsw = &fbcon_cyber_accel;
return -EINVAL;
if (y_bottom > fb_display[con].var.yres_virtual)
return -EINVAL;
+/*disabled until we can update the start address properly */
return -EINVAL;
cyber2000fb_update_start(var);
init_var.yres = DEFAULT_YRES;
init_var.bits_per_pixel = DEFAULT_BPP;
+ /*
+ * These parameters give
+ * 640x480, hsync 31.5kHz, vsync 60Hz
+ */
+ init_var.left_margin = 56;
+ init_var.right_margin = 16;
+ init_var.upper_margin = 34;
+ init_var.lower_margin = 9;
+ init_var.hsync_len = 88;
+ init_var.vsync_len = 2;
+ init_var.pixclock = 39722;
+
init_var.red.msb_right = 0;
init_var.green.msb_right = 0;
init_var.blue.msb_right = 0;
switch(init_var.bits_per_pixel) {
- case 8:
+ default:
+ init_var.bits_per_pixel = 8;
+ case 8: /* PSEUDOCOLOUR */
init_var.bits_per_pixel = 8;
init_var.red.offset = 0;
init_var.red.length = 8;
init_var.blue.length = 8;
break;
- case 16:
+ case 15: /* RGB555 */
+ init_var.bits_per_pixel = 15;
+ init_var.red.offset = 10;
+ init_var.red.length = 5;
+ init_var.green.offset = 5;
+ init_var.green.length = 5;
+ init_var.blue.offset = 0;
+ init_var.blue.length = 5;
+ break;
+
+ case 16: /* RGB565 */
init_var.bits_per_pixel = 16;
init_var.red.offset = 11;
init_var.red.length = 5;
init_var.blue.length = 5;
break;
- case 24:
+ case 24: /* RGB888 */
init_var.bits_per_pixel = 24;
init_var.red.offset = 16;
init_var.red.length = 8;
#define cyber2000_inw(reg) (*(unsigned short *)&CyberRegs[reg])
#define cyber2000_inl(reg) (*(unsigned long *)&CyberRegs[reg])
-static inline void cyber2000_crtcw(int val, int reg)
+static inline void cyber2000_crtcw(int reg, int val)
{
cyber2000_outb(reg, 0x3d4);
cyber2000_outb(val, 0x3d5);
}
-static inline void cyber2000_grphw(int val, int reg)
+static inline void cyber2000_grphw(int reg, int val)
{
cyber2000_outb(reg, 0x3ce);
cyber2000_outb(val, 0x3cf);
}
-static inline void cyber2000_attrw(int val, int reg)
+static inline void cyber2000_attrw(int reg, int val)
{
cyber2000_inb(0x3da);
cyber2000_outb(reg, 0x3c0);
cyber2000_outb(val, 0x3c0);
}
-static inline void cyber2000_seqw(int val, int reg)
+static inline void cyber2000_seqw(int reg, int val)
{
cyber2000_outb(reg, 0x3c4);
cyber2000_outb(val, 0x3c5);
tmp = tmp->b_this_page;
if (!buffer_busy(p))
continue;
-{
- static int count = 30;
- if (count) {
- count--;
- printk("bh %p (%04x:%ld): count=%d, state=0x%04x\n", p, p->b_dev, p->b_blocknr, p->b_count, p->b_state);
- }
-}
wakeup_bdflush(0);
return 0;
* dispose_list.
*/
#define CAN_UNUSE(inode) \
- (((inode)->i_count | (inode)->i_state) == 0)
+ (((inode)->i_count | (inode)->i_state | (inode)->i_nrpages) == 0)
#define INODE(entry) (list_entry(entry, struct inode, i_list))
static int free_inodes(void)
hash = page_hash(inode, offset);
repeat:
- page = __find_lock_page(inode, offset, *hash);
+ page = __find_lock_page(inode, offset, hash);
if (page) {
page_cache_free(page_cache);
goto unlock_out;
goto no_dirent_page;
hash = page_hash(inode, offset);
- page = __find_get_page(inode, offset, *hash);
+ page = __find_get_page(inode, offset, hash);
if (!page)
goto no_dirent_page;
if (!Page_Uptodate(page))
hash = page_hash(inode, 0);
repeat:
- page = __find_lock_page(inode, 0, *hash);
+ page = __find_lock_page(inode, 0, hash);
if (page) {
page_cache_free(page_cache);
goto unlock_out;
int retval;
struct vfsmount *vfsmnt;
- /*
- * Invalidate the inodes, as some mount options may be changed.
- * N.B. If we are changing media, we should check the return
- * from invalidate_inodes ... can't allow _any_ open files.
- */
- invalidate_inodes(sb);
-
if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev))
return -EACCES;
/*flags |= MS_RDONLY;*/
vfsmnt = lookup_vfsmnt(sb->s_dev);
if (vfsmnt)
vfsmnt->mnt_flags = sb->s_flags;
+
+ /*
+ * Invalidate the inodes, as some mount options may be changed.
+ * N.B. If we are changing media, we should check the return
+ * from invalidate_inodes ... can't allow _any_ open files.
+ */
+ invalidate_inodes(sb);
+
return 0;
}
* Set up a hw structure for a specified data port, control port and IRQ.
* This should follow whatever the default interface uses.
*/
-static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
+static __inline__ void
+ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
{
ide_ioreg_t reg = (ide_ioreg_t) data_port;
int i;
* Set up a hw structure for a specified data port, control port and IRQ.
* This should follow whatever the default interface uses.
*/
-static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
+static __inline__ void
+ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq)
{
ide_ioreg_t reg = (ide_ioreg_t) data_port;
int i;
reg += 1;
}
hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port;
- hw->irq = *irq;
+ hw->irq = irq;
}
/*
* 26-Jan-1999 PJB Don't use IACK on CATS
* 16-Mar-1999 RMK Added autodetect of ISA PICs
*/
-#include <linux/config.h>
#include <asm/hardware.h>
#include <asm/dec21285.h>
#include <asm/irq.h>
#ifndef __ASM_ARCH_MMU_H
#define __ASM_ARCH_MMU_H
-#include <linux/config.h>
-
#if defined(CONFIG_HOST_FOOTBRIDGE)
/*
mcr p15, 0, ip, c7, c7 @ flush caches
mov pc, lr" : : : "cc");
} else {
- if (machine_is_ebsa285() || machine_is_co285()) {
- /* To reboot, we set up the 21285 watchdog and
- * enable it. We then wait for it to timeout.
- */
- *CSR_TIMER4_LOAD = 0x8000;
- *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE |
- TIMER_CNTL_AUTORELOAD |
- TIMER_CNTL_DIV16;
- *CSR_SA110_CNTL |= 1 << 13;
- } else if (machine_is_netwinder()) {
+ if (machine_is_netwinder()) {
/* open up the SuperIO chip
*/
outb(0x87, 0x370);
/* set a RED LED and toggle WD_TIMER for rebooting
*/
outb(0xc4, 0x338);
+ } else {
+ /* To reboot, we set up the 21285 watchdog and
+ * enable it. We then wait for it to timeout.
+ */
+ *CSR_TIMER4_LOAD = 0x8000;
+ *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE |
+ TIMER_CNTL_AUTORELOAD |
+ TIMER_CNTL_DIV16;
+ *CSR_SA110_CNTL |= 1 << 13;
}
}
}
set_rtc_mmss = set_dummy_time;
}
- if (machine_is_ebsa285()) {
+ if (machine_is_ebsa285() || machine_is_co285()) {
gettimeoffset = timer1_gettimeoffset;
*CSR_TIMER1_CLR = 0;
* Set up a hw structure for a specified data port, control port and IRQ.
* This should follow whatever the default interface uses.
*/
-static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq)
+static __inline__ void
+ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq)
{
ide_ioreg_t reg = (ide_ioreg_t) data_port;
int i;
+ memset(hw, 0, sizeof(*hw));
+
for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
hw->io_ports[i] = reg;
reg += 1;
}
hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port;
- hw->irq = *irq;
+ hw->irq = irq;
}
/*
* This registers the standard ports for this architecture with the IDE
* driver.
*/
-static __inline__ void ide_init_default_hwifs(void)
+static __inline__ void
+ide_init_default_hwifs(void)
{
hw_regs_t hw;
- memset(hw, 0, sizeof(*hw));
-
- ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL);
- hw.irq = IRQ_HARDDISK;
+ ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, IRQ_HARDDISK);
ide_register_hw(&hw, NULL);
}
static inline unsigned long get_sp(void)
{
unsigned long sp;
- __asm__ ("mov %0,sp" : "=r" (sp));
+ __asm__ ("mov %0,sp" : "=r" (sp));
return sp;
}
+//static inline struct task_struct *get_current(void) __attribute__ (( __const__ ));
+
static inline struct task_struct *get_current(void)
{
struct task_struct *ts;
- __asm__ __volatile__("
- bic %0, sp, #0x1f00
- bic %0, %0, #0x00ff
- " : "=r" (ts));
+ __asm__ __volatile__ (
+ "bic %0, sp, #0x1f00 @ get_current
+ bic %0, %0, #0x00ff"
+ : "=r" (ts));
return ts;
}
*/
extern void set_dma_mode(dmach_t channel, dmamode_t mode);
+/* Set the transfer speed for this channel
+ */
+extern void set_dma_speed(dmach_t channel, int cycle_ns);
+
/* Get DMA residue count. After a DMA transfer, this
* should return zero. Reading this while a DMA transfer is
* still in progress will return unpredictable results.
#define ide_release_lock(lock) do {} while (0)
#define ide_get_lock(lock, hdlr, data) do {} while (0)
+/*
+ * We always use the new IDE port registering,
+ * so these are fixed here.
+ */
+#define ide_default_io_base(i) ((ide_ioreg_t)0)
+#define ide_default_irq(b) (0)
+
#endif /* __KERNEL__ */
#endif /* __ASMARM_IDE_H */
#define inl_p(port) __inl_p((port))
#endif
-/* Nothing to do */
-
-#ifndef dma_cache_inv
-#define dma_cache_inv(_start,_size) do { } while (0)
-#endif
-#ifndef dma_cache_wback
-#define dma_cache_wback(_start,_size) do { } while (0)
-#ifndef ARCH_READWRITE
-#ifndef dma_cache_wback_inv
-#define dma_cache_wback_inv(_start,_size) do { } while (0)
#endif
-#endif /* __KERNEL__ */
+#ifndef ARCH_READWRITE
-#endif /* __ASM_ARM_IO_H */
/* for panic */
#include <linux/kernel.h>
extern void disable_irq(unsigned int);
extern void enable_irq(unsigned int);
-#define __STR(x) #x
-#define STR(x) __STR(x)
-
#endif
#define CC_Z_BIT (1 << 30)
#define CC_N_BIT (1 << 31)
+#ifdef __KERNEL__
+
#define processor_mode(regs) \
((regs)->ARM_pc & MODE_MASK)
*/
static inline int valid_user_regs(struct pt_regs *regs)
{
- if (!user_mode(regs) || regs->ARM_pc & (F_BIT | I_BIT))
+ if (user_mode(regs) &&
+ (regs->ARM_pc & (F_BIT | I_BIT)) == 0)
return 1;
+ /*
+ * force it to be something sensible
+ */
+ regs->ARM_pc &= ~(MODE_MASK | F_BIT | I_BIT);
+
return 0;
}
+#endif /* __KERNEL__ */
+
#endif
@ atomic down operation
mov r0, pc
orr lr, r0, #0x08000000
- and r0, r0, #0x0c000003
teqp lr, #0
ldr lr, [%0]
+ and r0, r0, #0x0c000003
subs lr, lr, #1
str lr, [%0]
- mov lr, pc, lsr #28
- teqp r0, lr, lsl #28
+ orrmi r0, r0, #0x80000000 @ set N
+ teqp r0, #0
movmi r0, %0
blmi " SYMBOL_NAME_STR(__down_failed)
:
@ atomic down operation
mov r0, pc
orr lr, r0, #0x08000000
- and r0, r0, #0x0c000003
teqp lr, #0
ldr lr, [%1]
+ and r0, r0, #0x0c000003
subs lr, lr, #1
str lr, [%1]
- mov lr, pc, lsr #28
orrmi r0, r0, #0x80000000 @ set N
- teqp r0, lr, lsl #28
+ teqp r0, #0
movmi r0, %1
movpl r0, #0
blmi " SYMBOL_NAME_STR(__down_interruptible_failed) "
@ atomic down operation
mov r0, pc
orr lr, r0, #0x08000000
- and r0, r0, #0x0c000003
teqp lr, #0
ldr lr, [%1]
+ and r0, r0, #0x0c000003
subs lr, lr, #1
str lr, [%1]
- mov lr, pc, lsr #28
orrmi r0, r0, #0x80000000 @ set N
- teqp r0, lr, lsl #28
+ teqp r0, #0
movmi r0, %1
movpl r0, #0
blmi " SYMBOL_NAME_STR(__down_trylock_failed) "
@ atomic up operation
mov r0, pc
orr lr, r0, #0x08000000
- and r0, r0, #0x0c000003
teqp lr, #0
ldr lr, [%0]
+ and r0, r0, #0x0c000003
adds lr, lr, #1
str lr, [%0]
- mov lr, pc, lsr #28
- orrls r0, r0, #0x80000000 @ set N
- teqp r0, lr, lsl #28
+ orrle r0, r0, #0x80000000 @ set N
+ teqp r0, #0
movmi r0, %0
blmi " SYMBOL_NAME_STR(__up_wakeup)
:
: "memory"); \
} while (0)
+/* For spinlocks etc */
+#define local_irq_save(x) __save_flags_cli(x)
+#define local_irq_restore(x) __restore_flags(x)
+#define local_irq_disable() __cli()
+#define local_irq_enable() __sti()
+
#ifdef __SMP__
#error SMP not supported
#else
#define CC_Z_BIT (1 << 30)
#define CC_N_BIT (1 << 31)
+#ifdef __KERNEL__
+
#if 0 /* GCC/egcs should be able to optimise this, IMHO */
#define user_mode(regs) \
((((regs)->ARM_cpsr & MODE_MASK) == USR_MODE) || \
*/
static inline int valid_user_regs(struct pt_regs *regs)
{
- if ((regs->ARM_cpsr & 0xf) == 0 ||
- (regs->ARM_cpsr & (F_BIT|I_BIT)))
+ if ((regs->ARM_cpsr & 0xf) == 0 &&
+ (regs->ARM_cpsr & (F_BIT|I_BIT)) == 0)
return 1;
/*
return 0;
}
+#endif /* __KERNEL__ */
+
#endif
@ atomic down operation
mrs %0, cpsr
orr %1, %0, #128 @ disable IRQs
- bic %0, %0, #0x80000000 @ clear N
msr cpsr, %1
ldr %1, [%2]
+ bic %0, %0, #0x80000000 @ clear N
subs %1, %1, #1
- orrmi %0, %0, #0x80000000 @ set N
str %1, [%2]
+ orrmi %0, %0, #0x80000000 @ set N
msr cpsr, %0
movmi r0, %2
blmi " SYMBOL_NAME_STR(__down_failed)
@ atomic down interruptible operation
mrs %0, cpsr
orr %1, %0, #128 @ disable IRQs
- bic %0, %0, #0x80000000 @ clear N
msr cpsr, %1
ldr %1, [%2]
+ bic %0, %0, #0x80000000 @ clear N
subs %1, %1, #1
- orrmi %0, %0, #0x80000000 @ set N
str %1, [%2]
+ orrmi %0, %0, #0x80000000 @ set N
msr cpsr, %0
movmi r0, %2
movpl r0, #0
@ atomic down try lock operation
mrs %0, cpsr
orr %1, %0, #128 @ disable IRQs
- bic %0, %0, #0x80000000 @ clear N
msr cpsr, %1
ldr %1, [%2]
+ bic %0, %0, #0x80000000 @ clear N
subs %1, %1, #1
- orrmi %0, %0, #0x80000000 @ set N
str %1, [%2]
+ orrmi %0, %0, #0x80000000 @ set N
msr cpsr, %0
movmi r0, %2
movpl r0, #0
@ atomic up operation
mrs %0, cpsr
orr %1, %0, #128 @ disable IRQs
- bic %0, %0, #0x80000000 @ clear N
msr cpsr, %1
ldr %1, [%2]
+ bic %0, %0, #0x80000000 @ clear N
adds %1, %1, #1
- orrls %0, %0, #0x80000000 @ set N
str %1, [%2]
+ orrle %0, %0, #0x80000000 @ set N
msr cpsr, %0
movmi r0, %2
blmi " SYMBOL_NAME_STR(__up_wakeup)
: "memory"); \
} while (0)
+/* For spinlocks etc */
+#define local_irq_save(x) __save_flags_cli(x)
+#define local_irq_restore(x) __restore_flags(x)
+#define local_irq_disable() __cli()
+#define local_irq_enable() __sti()
+
#ifdef __SMP__
#error SMP not supported
#else
#define NR_DEBUGS 5
+#include <asm/proc/ptrace.h>
#include <asm/arch/processor.h>
#include <asm/proc/processor.h>
}
/* Forward declaration, a strange C thing */
+struct task_struct;
struct mm_struct;
/* Free all resources held by a thread. */
#include <linux/linkage.h>
#include <asm/atomic.h>
+#include <linux/wait.h>
struct semaphore {
atomic_t count;
wait_queue_head_t wait;
};
-#define MUTEX ((struct semaphore) { ATOMIC_INIT(1), 0, NULL })
-#define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), 0, NULL })
+#define __SEMAPHORE_INIT(name,count) \
+ { ATOMIC_INIT(count), 0, \
+ __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) }
+
+#define __MUTEX_INITIALIZER(name) \
+ __SEMAPHORE_INIT(name,1)
+
+#define __DECLARE_SEMAPHORE_GENERIC(name,count) \
+ struct semaphore name = __SEMAPHORE_INIT(name,count)
+
+#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1)
+#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0)
+
+#define sema_init(sem, val) \
+do { \
+ atomic_set(&((sem)->count), (val)); \
+ (sem)->waking = 0; \
+ init_waitqueue_head(&(sem)->wait); \
+} while (0)
+
+static inline void init_MUTEX(struct semaphore *sem)
+{
+ sema_init(sem, 1);
+}
+
+static inline void init_MUTEX_LOCKED(struct semaphore *sem)
+{
+ sema_init(sem, 0);
+}
asmlinkage void __down_failed (void /* special register calling convention */);
asmlinkage int __down_interruptible_failed (void /* special register calling convention */);
extern int __down_trylock(struct semaphore * sem);
extern void __up(struct semaphore * sem);
-#define sema_init(sem, val) atomic_set(&((sem)->count), (val))
+extern spinlock_t semaphore_wake_lock;
#include <asm/proc/semaphore.h>
#include <asm/hardirq.h>
extern unsigned int local_bh_count[NR_CPUS];
-#define in_bh() (local_bh_count[smp_processor_id()] != 0)
+
+#define cpu_bh_disable(cpu) do { local_bh_count[(cpu)]++; barrier(); } while (0)
+#define cpu_bh_enable(cpu) do { barrier(); local_bh_count[(cpu)]--; } while (0)
+
+#define cpu_bh_trylock(cpu) (local_bh_count[(cpu)] ? 0 : (local_bh_count[(cpu)] = 1))
+#define cpu_bh_endlock(cpu) (local_bh_count[(cpu)] = 0)
+
+#define local_bh_disable() cpu_bh_disable(smp_processor_id())
+#define local_bh_enable() cpu_bh_enable(smp_processor_id())
#define get_active_bhs() (bh_mask & bh_active)
-#define clear_active_bhs(x) atomic_clear_mask((int)(x),&bh_active)
+#define clear_active_bhs(x) atomic_clear_mask((x),&bh_active)
extern inline void init_bh(int nr, void (*routine)(void))
{
extern inline void remove_bh(int nr)
{
- bh_base[nr] = NULL;
bh_mask &= ~(1 << nr);
+ mb();
+ bh_base[nr] = NULL;
}
extern inline void mark_bh(int nr)
extern inline void start_bh_atomic(void)
{
- local_bh_count[smp_processor_id()]++;
+ local_bh_disable();
barrier();
}
extern inline void end_bh_atomic(void)
{
barrier();
- local_bh_count[smp_processor_id()]--;
+ local_bh_enable();
}
/* These are for the irq's testing the lock */
-#define softirq_trylock(cpu) (in_bh() ? 0 : (local_bh_count[smp_processor_id()]=1))
-#define softirq_endlock(cpu) (local_bh_count[smp_processor_id()] = 0)
-#define synchronize_bh() do { } while (0)
+#define softirq_trylock(cpu) (cpu_bh_trylock(cpu))
+#define softirq_endlock(cpu) (cpu_bh_endlock(cpu))
+#define synchronize_bh() barrier()
#endif /* SMP */
#ifndef __ASM_SPINLOCK_H
#define __ASM_SPINLOCK_H
-#ifndef __SMP__
-
/*
* To be safe, we assume the only compiler that can cope with
* empty initialisers is EGCS.
*/
#if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 90))
-#define EMPTY_INIT_OK
+#define EMPTY_STRUCT struct { }
+#define EMPTY_STRUCT_INIT(t) (t) { }
+#else
+#define EMPTY_STRUCT unsigned char
+#define EMPTY_STRUCT_INIT(t) (t) 0
#endif
+/*
+ * These are the generic versions of the spinlocks
+ * and read-write locks.. We should actually do a
+ * <linux/spinlock.h> with all of this. Oh, well.
+ */
+#define spin_lock_irqsave(lock, flags) do { local_irq_save(flags); spin_lock(lock); } while (0)
+#define spin_lock_irq(lock) do { local_irq_disable(); spin_lock(lock); } while (0)
+#define spin_lock_bh(lock) do { local_bh_disable(); spin_lock(lock); } while (0)
+
+#define read_lock_irqsave(lock, flags) do { local_irq_save(flags); read_lock(lock); } while (0)
+#define read_lock_irq(lock) do { local_irq_disable(); read_lock(lock); } while (0)
+#define read_lock_bh(lock) do { local_bh_disable(); read_lock(lock); } while (0)
+
+#define write_lock_irqsave(lock, flags) do { local_irq_save(flags); write_lock(lock); } while (0)
+#define write_lock_irq(lock) do { local_irq_disable(); write_lock(lock); } while (0)
+#define write_lock_bh(lock) do { local_bh_disable(); write_lock(lock); } while (0)
+
+#define spin_unlock_irqrestore(lock, flags) do { spin_unlock(lock); local_irq_restore(flags); } while (0)
+#define spin_unlock_irq(lock) do { spin_unlock(lock); local_irq_enable(); } while (0)
+#define spin_unlock_bh(lock) do { spin_unlock(lock); local_bh_enable(); } while (0)
+
+#define read_unlock_irqrestore(lock, flags) do { read_unlock(lock); local_irq_restore(flags); } while (0)
+#define read_unlock_irq(lock) do { read_unlock(lock); local_irq_enable(); } while (0)
+#define read_unlock_bh(lock) do { read_unlock(lock); local_bh_enable(); } while (0)
+
+#define write_unlock_irqrestore(lock, flags) do { write_unlock(lock); local_irq_restore(flags); } while (0)
+#define write_unlock_irq(lock) do { write_unlock(lock); local_irq_enable(); } while (0)
+#define write_unlock_bh(lock) do { write_unlock(lock); local_bh_enable(); } while (0)
+
+#ifndef __SMP__
+
+#define DEBUG_SPINLOCKS 0 /* 0 == no debugging, 1 == maintain lock state, 2 == full debugging */
+
+#if (DEBUG_SPINLOCKS < 1)
/*
* Your basic spinlocks, allowing only a single CPU anywhere
*/
-#ifdef EMPTY_INIT_OK
- typedef struct { } spinlock_t;
-# define SPIN_LOCK_UNLOCKED (spinlock_t) { }
-#else
- typedef unsigned char spinlock_t;
-# define SPIN_LOCK_UNLOCKED 0
-#endif
+typedef EMPTY_STRUCT spinlock_t;
+#define SPIN_LOCK_UNLOCKED EMPTY_STRUCT_INIT(spinlock_t)
#define spin_lock_init(lock) do { } while(0)
#define spin_lock(lock) do { } while(0)
-#define spin_trylock(lock) do { } while(0)
+#define spin_trylock(lock) (1)
#define spin_unlock_wait(lock) do { } while(0)
#define spin_unlock(lock) do { } while(0)
-#define spin_lock_irq(lock) cli()
-#define spin_unlock_irq(lock) sti()
-#define spin_lock_irqsave(lock, flags) \
- do { __save_flags_cli(flags); } while (0)
-#define spin_unlock_irqrestore(lock, flags) \
- restore_flags(flags)
+#elif (DEBUG_SPINLOCKS < 2)
+
+typedef struct {
+ volatile unsigned int lock;
+} spinlock_t;
+#define SPIN_LOCK_UNLOCKED (pinlock_t) { 0 }
+
+#define spin_lock_init(x) do { (x)->lock = 0; } while (0)
+#define spin_lock(x) do { (x)->lock = 1; } while (0)
+#define spin_trylock(lock) (!test_and_set_bit(0,(lock)))
+#define spin_unlock_wait(x) do { } while (0)
+#define spin_unlock(x) do { (x)->lock = 0; } while (0)
+
+#else /* (DEBUG_SPINLOCKS >= 2) */
+
+typedef struct {
+ volatule unsigned int lock;
+ volatile unsigned int babble;
+ const char *module;
+} spinlock_t;
+#define SPIN_LOCK_UNLOCKED (spinlock_t) { 0, 25, __BASE_FILE__ }
+
+#include <linux/kernel.h>
+
+#define spin_lock_init(x) do { (x)->lock = 0; } while (0)
+#define spin_trylock(lock) (!test_and_set_bit(0,(lock)))
+
+#define spin_lock(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1; restore_flags(__spinflags);} while (0)
+#define spin_unlock_wait(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_wait(%s:%p) deadlock\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} restore_flags(__spinflags);} while (0)
+#define spin_unlock(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(__spinflags);} while (0)
+
+#endif
/*
* Read-write spinlocks, allowing multiple readers
* irq-safe write-lock, but readers can get non-irqsafe
* read-locks.
*/
-#ifdef EMPTY_INIT_OK
- typedef struct { } rwlock_t;
-# define RW_LOCK_UNLOCKED (rwlock_t) { }
-#else
- typedef unsigned char rwlock_t;
-# define RW_LOCK_UNLOCKED 0
-#endif
+typedef EMPTY_STRUCT rwlock_t;
+#define RW_LOCK_UNLOCKED EMPTY_STRUCT_INIT(rwlock_t)
#define read_lock(lock) do { } while(0)
#define read_unlock(lock) do { } while(0)
#define write_lock(lock) do { } while(0)
#define write_unlock(lock) do { } while(0)
-#define read_lock_irq(lock) cli()
-#define read_unlock_irq(lock) sti()
-#define write_lock_irq(lock) cli()
-#define write_unlock_irq(lock) sti()
-
-#define read_lock_irqsave(lock, flags) \
- do { __save_flags_cli(flags); } while (0)
-#define read_unlock_irqrestore(lock, flags) \
- restore_flags(flags)
-#define write_lock_irqsave(lock, flags) \
- do { __save_flags_cli(flags); } while (0)
-#define write_unlock_irqrestore(lock, flags) \
- restore_flags(flags)
#else
#error ARM architecture does not support spin locks
/*
* Sort out a definition for machine_arch_type
- * The rules basically are:
+ * The rules are:
* 1. If one architecture is selected, then all machine_is_xxx()
* are constant.
* 2. If two or more architectures are selected, then the selected
#define machine_arch_type __machine_arch_type
#endif
-/*
- * task_struct isn't always declared - forward-declare it here.
- */
-struct task_struct;
-
#include <asm/proc-fns.h>
-extern void arm_malalignedptr(const char *, void *, volatile void *);
-extern void arm_invalidptr(const char *, int);
-
#define xchg(ptr,x) \
((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
#define tas(ptr) (xchg((ptr),1))
-/*
- * switch_to(prev, next) should switch from task `prev' to `next'
- * `prev' will never be the same as `next'.
- *
- * `next' and `prev' should be struct task_struct, but it isn't always defined
- */
-#define switch_to(prev,next,last) do { last = processor._switch_to(prev,next); } while (0)
+extern void arm_malalignedptr(const char *, void *, volatile void *);
+extern void arm_invalidptr(const char *, int);
+extern asmlinkage void __backtrace(void);
/*
* Include processor dependent parts
#define wmb() mb()
#define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t");
-extern asmlinkage void __backtrace(void);
+/*
+ * switch_to(prev, next) should switch from task `prev' to `next'
+ * `prev' will never be the same as `next'.
+ * The `mb' is to tell GCC not to cache `current' across this call.
+ */
+#define switch_to(prev,next,last) \
+ do { \
+ last = processor._switch_to(prev,next); \
+ mb(); \
+ } while (0)
#endif
#define __NR_geteuid (__NR_SYSCALL_BASE+ 49)
#define __NR_getegid (__NR_SYSCALL_BASE+ 50)
#define __NR_acct (__NR_SYSCALL_BASE+ 51)
-#define __NR_phys (__NR_SYSCALL_BASE+ 52)
+#define __NR_umount2 (__NR_SYSCALL_BASE+ 52)
#define __NR_lock (__NR_SYSCALL_BASE+ 53)
#define __NR_ioctl (__NR_SYSCALL_BASE+ 54)
#define __NR_fcntl (__NR_SYSCALL_BASE+ 55)
-/* $Id: namei.h,v 1.13 1999/04/06 06:54:36 jj Exp $
+/* $Id: namei.h,v 1.14 1999/06/10 05:23:12 davem Exp $
* linux/include/asm-sparc/namei.h
*
* Routines to handle famous /usr/gnemul/s*.
#ifndef __ASSEMBLY__
+#define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0)
+#define PAGE_BUG(page) do { \
+ BUG(); } while (0)
+
#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE)
#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE)
#define spin_lock_init(lock) do { } while(0)
#define spin_lock(lock) do { } while(0)
-#define spin_trylock(lock) do { } while(0)
+#define spin_trylock(lock) (1)
#define spin_unlock_wait(lock) do { } while(0)
#define spin_unlock(lock) do { } while(0)
#define spin_lock_irq(lock) cli()
-/* $Id: elf.h,v 1.18 1998/09/09 05:36:08 davem Exp $ */
+/* $Id: elf.h,v 1.19 1999/06/11 13:26:04 jj Exp $ */
#ifndef __ASM_SPARC64_ELF_H
#define __ASM_SPARC64_ELF_H
*/
#include <asm/ptrace.h>
+#ifdef __KERNEL__
#include <asm/processor.h>
+#endif
/*
* These are used to set parameters in the core dumps.
-/* $Id: namei.h,v 1.14 1999/04/06 06:54:39 jj Exp $
+/* $Id: namei.h,v 1.15 1999/06/10 05:23:17 davem Exp $
* linux/include/asm-sparc64/namei.h
*
* Routines to handle famous /usr/gnemul/s*.
#ifndef __ASSEMBLY__
+#define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0)
+#define PAGE_BUG(page) do { \
+ BUG(); } while (0)
+
extern void clear_page(unsigned long page);
extern void copy_page(unsigned long to, unsigned long from);
#define spin_lock_init(lock) do { } while(0)
#define spin_lock(lock) do { } while(0)
-#define spin_trylock(lock) do { } while(0)
+#define spin_trylock(lock) (1)
#define spin_unlock_wait(lock) do { } while(0)
#define spin_unlock(lock) do { } while(0)
#define spin_lock_irq(lock) cli()
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
+#elif (MAJOR_NR == MFM_ACORN_MAJOR)
+
+#define DEVICE_NAME "mfm disk"
+#define DEVICE_INTR do_mfm
+#define DEVICE_REQUEST do_mfm_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
#elif (MAJOR_NR == NBD_MAJOR)
#define DEVICE_NAME "nbd"
--- /dev/null
+#ifndef _LINUX_FD1772REG_H
+#define _LINUX_FD1772REG_H
+
+/*
+** WD1772 stuff - originally from the M68K Linux
+ * Modified for Archimedes by Dave Gilbert (gilbertd@cs.man.ac.uk)
+ */
+
+/* register codes */
+
+#define FDC1772SELREG_STP (0x80) /* command/status register */
+#define FDC1772SELREG_TRA (0x82) /* track register */
+#define FDC1772SELREG_SEC (0x84) /* sector register */
+#define FDC1772SELREG_DTA (0x86) /* data register */
+
+/* register names for FDC1772_READ/WRITE macros */
+
+#define FDC1772REG_CMD 0
+#define FDC1772REG_STATUS 0
+#define FDC1772REG_TRACK 2
+#define FDC1772REG_SECTOR 4
+#define FDC1772REG_DATA 6
+
+/* command opcodes */
+
+#define FDC1772CMD_RESTORE (0x00) /* - */
+#define FDC1772CMD_SEEK (0x10) /* | */
+#define FDC1772CMD_STEP (0x20) /* | TYP 1 Commands */
+#define FDC1772CMD_STIN (0x40) /* | */
+#define FDC1772CMD_STOT (0x60) /* - */
+#define FDC1772CMD_RDSEC (0x80) /* - TYP 2 Commands */
+#define FDC1772CMD_WRSEC (0xa0) /* - " */
+#define FDC1772CMD_RDADR (0xc0) /* - */
+#define FDC1772CMD_RDTRA (0xe0) /* | TYP 3 Commands */
+#define FDC1772CMD_WRTRA (0xf0) /* - */
+#define FDC1772CMD_FORCI (0xd0) /* - TYP 4 Command */
+
+/* command modifier bits */
+
+#define FDC1772CMDADD_SR6 (0x00) /* step rate settings */
+#define FDC1772CMDADD_SR12 (0x01)
+#define FDC1772CMDADD_SR2 (0x02)
+#define FDC1772CMDADD_SR3 (0x03)
+#define FDC1772CMDADD_V (0x04) /* verify */
+#define FDC1772CMDADD_H (0x08) /* wait for spin-up */
+#define FDC1772CMDADD_U (0x10) /* update track register */
+#define FDC1772CMDADD_M (0x10) /* multiple sector access */
+#define FDC1772CMDADD_E (0x04) /* head settling flag */
+#define FDC1772CMDADD_P (0x02) /* precompensation */
+#define FDC1772CMDADD_A0 (0x01) /* DAM flag */
+
+/* status register bits */
+
+#define FDC1772STAT_MOTORON (0x80) /* motor on */
+#define FDC1772STAT_WPROT (0x40) /* write protected (FDC1772CMD_WR*) */
+#define FDC1772STAT_SPINUP (0x20) /* motor speed stable (Type I) */
+#define FDC1772STAT_DELDAM (0x20) /* sector has deleted DAM (Type II+III) */
+#define FDC1772STAT_RECNF (0x10) /* record not found */
+#define FDC1772STAT_CRC (0x08) /* CRC error */
+#define FDC1772STAT_TR00 (0x04) /* Track 00 flag (Type I) */
+#define FDC1772STAT_LOST (0x04) /* Lost Data (Type II+III) */
+#define FDC1772STAT_IDX (0x02) /* Index status (Type I) */
+#define FDC1772STAT_DRQ (0x02) /* DRQ status (Type II+III) */
+#define FDC1772STAT_BUSY (0x01) /* FDC1772 is busy */
+
+
+/* PSG Port A Bit Nr 0 .. Side Sel .. 0 -> Side 1 1 -> Side 2 */
+#define DSKSIDE (0x01)
+
+#define DSKDRVNONE (0x06)
+#define DSKDRV0 (0x02)
+#define DSKDRV1 (0x04)
+
+/* step rates */
+#define FDC1772STEP_6 0x00
+#define FDC1772STEP_12 0x01
+#define FDC1772STEP_2 0x02
+#define FDC1772STEP_3 0x03
+
+#endif
#define page_hash(inode,offset) (page_hash_table+_page_hashfn(inode,offset))
extern struct page * __find_get_page (struct inode * inode,
- unsigned long offset, struct page *page);
+ unsigned long offset, struct page **hash);
#define find_get_page(inode, offset) \
- __find_get_page(inode, offset, *page_hash(inode, offset))
+ __find_get_page(inode, offset, page_hash(inode, offset))
extern struct page * __find_lock_page (struct inode * inode,
- unsigned long offset, struct page *page);
+ unsigned long offset, struct page **hash);
extern void lock_page(struct page *page);
#define find_lock_page(inode, offset) \
- __find_lock_page(inode, offset, *page_hash(inode, offset))
+ __find_lock_page(inode, offset, page_hash(inode, offset))
extern void __add_page_to_hash_queue(struct page * page, struct page **p);
__add_page_to_hash_queue(page, page_hash(inode,offset));
}
-static inline void remove_page_from_inode_queue(struct page * page)
-{
- struct inode * inode = page->inode;
-
- inode->i_nrpages--;
- if (inode->i_pages == page)
- inode->i_pages = page->next;
- if (page->next)
- page->next->prev = page->prev;
- if (page->prev)
- page->prev->next = page->next;
- page->next = NULL;
- page->prev = NULL;
-}
-
static inline void add_page_to_inode_queue(struct inode * inode, struct page * page)
{
struct page **p = &inode->i_pages;
/*
* Vendor and card ID's: sort these numerically according to vendor
* (and according to card ID within vendor). Send all updates to
- * <linux-pcisupport@cck.uni-kl.de>.
+ * <pci-ids@ucw.cz>.
*/
#define PCI_VENDOR_ID_COMPAQ 0x0e11
#define PCI_DEVICE_ID_COMPAQ_1280 0x3033
struct pci_dev *pci_find_class (unsigned int class, struct pci_dev *from);
struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
+#define PCI_ANY_ID (~0)
+
#define pci_present pcibios_present
int pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val);
int pci_read_config_word(struct pci_dev *dev, u8 where, u16 *val);
atomic_dec(&page_cache_size);
}
+static void remove_page_from_inode_queue(struct page * page)
+{
+ struct inode * inode = page->inode;
+ struct page *prev, *next;
+
+ inode->i_nrpages--;
+ next = page->next;
+ prev = page->prev;
+ if (inode->i_pages == page)
+ inode->i_pages = next;
+ if (next)
+ next->prev = prev;
+ if (prev)
+ prev->next = next;
+ page->next = NULL;
+ page->prev = NULL;
+}
+
+/*
+ * Remove a page from the page cache and free it. Caller has to make
+ * sure the page is locked and that nobody else uses it - or that usage
+ * is safe.
+ */
+void remove_inode_page(struct page *page)
+{
+ if (!PageLocked(page))
+ PAGE_BUG(page);
+
+ spin_lock(&pagecache_lock);
+ remove_page_from_inode_queue(page);
+ remove_page_from_hash_queue(page);
+ page->inode = NULL;
+ spin_unlock(&pagecache_lock);
+}
+
void invalidate_inode_pages(struct inode * inode)
{
struct page ** p;
}
if (page_count(page) != 2)
printk("hm, busy page invalidated? (not necesserily a bug)\n");
- inode->i_nrpages--;
- if ((*p = page->next) != NULL)
- (*p)->prev = page->prev;
- page->next = NULL;
- page->prev = NULL;
+
+ remove_page_from_inode_queue(page);
remove_page_from_hash_queue(page);
page->inode = NULL;
UnlockPage(page);
/* page wholly truncated - free it */
if (offset >= start) {
get_page(page);
- if (TryLockPage(page)) {
- spin_unlock(&pagecache_lock);
- wait_on_page(page);
- page_cache_release(page);
- goto repeat;
- }
spin_unlock(&pagecache_lock);
+ lock_page(page);
+
if (inode->i_op->flushpage)
inode->i_op->flushpage(inode, page, 0);
* page cache and creates a buffer-cache alias
* to it causing all sorts of fun problems ...
*/
- spin_lock(&pagecache_lock);
- inode->i_nrpages--;
- if ((*p = page->next) != NULL)
- (*p)->prev = page->prev;
- page->next = NULL;
- page->prev = NULL;
- remove_page_from_hash_queue(page);
- page->inode = NULL;
-
- if (page_count(page) == 1) {
- spin_unlock(&pagecache_lock);
- PAGE_BUG(page);
- }
-
- spin_unlock(&pagecache_lock);
+ remove_inode_page(page);
UnlockPage(page);
page_cache_release(page);
if (offset < PAGE_CACHE_SIZE) {
unsigned long address;
get_page(page);
- if (TryLockPage(page)) {
- spin_unlock(&pagecache_lock);
- wait_on_page(page);
- page_cache_release(page);
- goto repeat;
- }
- /*
- * It's worth dropping the write lock only at
- * this point. We are holding the page lock
- * so nobody can do anything bad to us.
- */
spin_unlock(&pagecache_lock);
+
+ lock_page(page);
partial = 1;
address = page_address(page);
spin_unlock(&pagecache_lock);
}
-/*
- * Remove a page from the page cache and free it. Caller has to make
- * sure the page is locked and that nobody else uses it - or that usage
- * is safe.
- */
-void remove_inode_page(struct page *page)
-{
- if (!PageLocked(page))
- PAGE_BUG(page);
-
- spin_lock(&pagecache_lock);
- remove_page_from_inode_queue(page);
- remove_page_from_hash_queue(page);
- page->inode = NULL;
- spin_unlock(&pagecache_lock);
-}
-
int shrink_mmap(int priority, int gfp_mask)
{
static unsigned long clock = 0;
* hashed page atomically, waiting for it if it's locked.
*/
struct page * __find_get_page (struct inode * inode,
- unsigned long offset, struct page *page)
+ unsigned long offset, struct page **hash)
{
+ struct page *page;
/*
* We scan the hash list read-only. Addition to and removal from
*/
repeat:
spin_lock(&pagecache_lock);
- page = __find_page_nolock(inode, offset, page);
+ page = __find_page_nolock(inode, offset, *hash);
if (page)
get_page(page);
spin_unlock(&pagecache_lock);
* Get the lock to a page atomically.
*/
struct page * __find_lock_page (struct inode * inode,
- unsigned long offset, struct page *page)
+ unsigned long offset, struct page **hash)
{
int locked;
-
+ struct page *page;
/*
* We scan the hash list read-only. Addition to and removal from
*/
repeat:
spin_lock(&pagecache_lock);
- page = __find_page_nolock(inode, offset, page);
+ page = __find_page_nolock(inode, offset, *hash);
locked = 0;
if (page) {
get_page(page);
*/
hash = page_hash(inode, offset);
retry_find:
- page = __find_get_page(inode, offset, *hash);
+ page = __find_get_page(inode, offset, hash);
if (!page)
goto no_cached_page;
* cache.. The page we just got may be useful if we
* can't share, so don't get rid of it here.
*/
- page = __find_get_page(inode, offset, *hash);
+ page = __find_get_page(inode, offset, hash);
if (page)
goto found_page;
hash = page_hash(inode, pgpos);
repeat_find:
- page = __find_lock_page(inode, pgpos, *hash);
+ page = __find_lock_page(inode, pgpos, hash);
if (!page) {
if (!page_cache) {
page_cache = page_cache_alloc();