From 9d1ec5d7a336d165466cbfcabadd5e10e771d817 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:40:34 -0500 Subject: [PATCH] - Alan Cox: synch. PA-RISC arch and bitops cleanups - Maciej Rozycki: even more proper apic setup order. - Andrew Morton: exec_usermodehelper fixes - Adam Richter, Kai Germaschewski, Linus: PCI irq routing. - revert A20 code changes. We really need to use the keyboard controller if one exists. - Johannes Erdfelt: USB updates - Ralf Baechle: MIPS memmove() fix. --- CREDITS | 5 + Documentation/Configure.help | 7 +- Documentation/parisc/00-INDEX | 10 + Documentation/parisc/IODC.txt | 68 + Documentation/parisc/debugging | 39 + Documentation/parisc/mm | 31 + Documentation/parisc/registers | 126 ++ Documentation/usb/URB.txt | 61 +- Documentation/usb/error-codes.txt | 44 +- Documentation/usb/usb-help.txt | 1 + Documentation/usb/usb-serial.txt | 9 + MAINTAINERS | 9 +- arch/i386/boot/setup.S | 93 +- arch/i386/kernel/apic.c | 118 +- arch/i386/kernel/pci-irq.c | 42 +- arch/i386/kernel/setup.c | 2 +- arch/m68k/atari/atakeyb.c | 1 + arch/mips/lib/Makefile | 1 - arch/mips/lib/memcpy.S | 3 +- arch/mips/lib/memset.S | 4 - arch/mips64/lib/Makefile | 1 - arch/mips64/lib/memcpy.S | 1 + arch/mips64/lib/memset.S | 11 +- arch/parisc/Makefile | 91 ++ arch/parisc/config.in | 208 +++ arch/parisc/defconfig | 363 +++++ arch/parisc/hpux/Makefile | 16 + arch/parisc/hpux/entry_hpux.S | 537 +++++++ arch/parisc/hpux/fs.c | 250 ++++ arch/parisc/hpux/gate.S | 74 + arch/parisc/hpux/ioctl.c | 63 + arch/parisc/hpux/sys_hpux.c | 334 +++++ arch/parisc/hpux/wrappers.S | 244 ++++ arch/parisc/kernel/Makefile | 52 + arch/parisc/kernel/cache.c | 253 ++++ arch/parisc/kernel/ccio-dma.c | 1208 ++++++++++++++++ arch/parisc/kernel/ccio-rm-dma.c | 212 +++ arch/parisc/kernel/drivers.c | 135 ++ arch/parisc/kernel/entry.S | 1866 +++++++++++++++++++++++++ arch/parisc/kernel/hardware.c | 1446 +++++++++++++++++++ arch/parisc/kernel/head.S | 156 +++ arch/parisc/kernel/hpmc.S | 319 +++++ arch/parisc/kernel/init_task.c | 29 + arch/parisc/kernel/inventory.c | 398 ++++++ arch/parisc/kernel/iosapic.c | 1101 +++++++++++++++ arch/parisc/kernel/iosapic_private.h | 165 +++ arch/parisc/kernel/irq.c | 537 +++++++ arch/parisc/kernel/keyboard.c | 82 ++ arch/parisc/kernel/lasimap.map | 322 +++++ arch/parisc/kernel/lba_pci.c | 1347 ++++++++++++++++++ arch/parisc/kernel/led.c | 450 ++++++ arch/parisc/kernel/pa7300lc.c | 54 + arch/parisc/kernel/parisc_ksyms.c | 138 ++ arch/parisc/kernel/pci-dma.c | 547 ++++++++ arch/parisc/kernel/pci.c | 534 +++++++ arch/parisc/kernel/pdc.c | 217 +++ arch/parisc/kernel/pdc_cons.c | 179 +++ arch/parisc/kernel/process.c | 254 ++++ arch/parisc/kernel/ptrace.c | 305 ++++ arch/parisc/kernel/real1.c | 154 ++ arch/parisc/kernel/real2.S | 274 ++++ arch/parisc/kernel/sba_iommu.c | 1752 +++++++++++++++++++++++ arch/parisc/kernel/semaphore.c | 239 ++++ arch/parisc/kernel/setup.c | 616 ++++++++ arch/parisc/kernel/signal.c | 655 +++++++++ arch/parisc/kernel/sys_parisc.c | 88 ++ arch/parisc/kernel/syscall.S | 563 ++++++++ arch/parisc/kernel/time.c | 99 ++ arch/parisc/kernel/traps.c | 896 ++++++++++++ arch/parisc/lib/Makefile | 13 + arch/parisc/lib/bitops.c | 59 + arch/parisc/lib/checksum.c | 130 ++ arch/parisc/lib/lusercopy.S | 242 ++++ arch/parisc/mm/Makefile | 13 + arch/parisc/mm/extable.c | 69 + arch/parisc/mm/fault.c | 283 ++++ arch/parisc/mm/init.c | 479 +++++++ arch/parisc/mm/kmap.c | 143 ++ arch/parisc/mm/pa11.c | 170 +++ arch/parisc/mm/pa20.c | 170 +++ arch/parisc/tools/Makefile | 28 + arch/parisc/tools/offset.c | 326 +++++ arch/parisc/vmlinux.lds | 79 ++ drivers/block/ataflop.c | 2 +- drivers/ide/q40ide.c | 1 - drivers/isdn/avmb1/capi.c | 1 - drivers/isdn/hisax/config.c | 2 +- drivers/isdn/hisax/hisax.h | 15 +- drivers/isdn/hisax/l3ni1.c | 1 - drivers/isdn/hisax/netjet.c | 1 - drivers/md/xor.c | 1 - drivers/media/video/tvaudio.c | 1 + drivers/net/3c503.c | 14 +- drivers/net/3c505.h | 2 +- drivers/net/aironet4500.h | 2 +- drivers/net/arlan.c | 2 +- drivers/net/arlan.h | 6 +- drivers/net/bagetlance.c | 6 +- drivers/net/dgrs.c | 16 +- drivers/net/dmfe.c | 14 +- drivers/net/e2100.c | 19 +- drivers/net/eepro.c | 9 +- drivers/net/eexpress.c | 24 +- drivers/net/es3210.c | 22 +- drivers/net/hplance.c | 3 +- drivers/net/lance.c | 1 - drivers/net/lasi_82596.c | 1550 ++++++++++++++++++++ drivers/net/ne.c | 6 +- drivers/net/ne2.c | 4 +- drivers/net/ne2k-pci.c | 11 +- drivers/net/ne3210.c | 4 +- drivers/net/plip.c | 3 +- drivers/net/tokenring/abyss.c | 316 ++--- drivers/net/tokenring/olympic.c | 7 + drivers/net/tokenring/tmspci.c | 326 ++--- drivers/net/tulip/tulip_core.c | 4 +- drivers/pci/pci.c | 6 +- drivers/scsi/wd33c93.c | 1 + drivers/sound/ymfpci.c | 5 +- drivers/usb/acm.c | 183 +-- drivers/usb/serial/empeg.c | 13 + drivers/usb/serial/ftdi_sio.c | 358 +++-- drivers/usb/serial/ftdi_sio.h | 42 +- drivers/usb/uhci.c | 1 + drivers/usb/usb.c | 4 +- drivers/video/dummycon.c | 3 + drivers/video/fbcon-sti.c | 330 +++++ drivers/video/fbmem.c | 5 + drivers/video/sti-bmode.h | 287 ++++ drivers/video/sti.h | 289 ++++ drivers/video/sticon-bmode.c | 920 ++++++++++++ drivers/video/sticon.c | 229 +++ drivers/video/sticore.c | 599 ++++++++ drivers/video/stifb.c | 230 +++ fs/file_table.c | 2 - fs/smbfs/file.c | 31 +- fs/stat.c | 8 +- fs/udf/inode.c | 2 +- include/asm-m68k/keyboard.h | 1 + include/asm-m68k/motorola_pgtable.h | 2 + include/asm-m68k/pgalloc.h | 1 + include/asm-parisc/a.out.h | 29 + include/asm-parisc/asmregs.h | 183 +++ include/asm-parisc/assembly.h | 374 +++++ include/asm-parisc/atomic.h | 102 ++ include/asm-parisc/bitops.h | 247 ++++ include/asm-parisc/bootdata.h | 16 + include/asm-parisc/bugs.h | 20 + include/asm-parisc/byteorder.h | 75 + include/asm-parisc/cache.h | 60 + include/asm-parisc/checksum.h | 174 +++ include/asm-parisc/current.h | 19 + include/asm-parisc/delay.h | 45 + include/asm-parisc/div64.h | 54 + include/asm-parisc/elf.h | 97 ++ include/asm-parisc/errno.h | 147 ++ include/asm-parisc/fcntl.h | 90 ++ include/asm-parisc/fixmap.h | 8 + include/asm-parisc/gsc.h | 80 ++ include/asm-parisc/hardirq.h | 87 ++ include/asm-parisc/hardware.h | 103 ++ include/asm-parisc/hdreg.h | 6 + include/asm-parisc/hil.h | 26 + include/asm-parisc/hw_irq.h | 17 + include/asm-parisc/ide.h | 111 ++ include/asm-parisc/io.h | 60 + include/asm-parisc/ioctl.h | 67 + include/asm-parisc/ioctls.h | 82 ++ include/asm-parisc/iosapic.h | 53 + include/asm-parisc/ipcbuf.h | 11 + include/asm-parisc/irq.h | 89 ++ include/asm-parisc/keyboard.h | 67 + include/asm-parisc/led.h | 33 + include/asm-parisc/linux_logo.h | 48 + include/asm-parisc/machdep.h | 16 + include/asm-parisc/mc146818rtc.h | 9 + include/asm-parisc/md.h | 13 + include/asm-parisc/mman.h | 52 + include/asm-parisc/mmu.h | 64 + include/asm-parisc/mmu_context.h | 67 + include/asm-parisc/msgbuf.h | 31 + include/asm-parisc/namei.h | 17 + include/asm-parisc/page.h | 86 ++ include/asm-parisc/param.h | 24 + include/asm-parisc/parport.h | 18 + include/asm-parisc/parport_gsc.h | 193 +++ include/asm-parisc/pci.h | 215 +++ include/asm-parisc/pdc.h | 629 +++++++++ include/asm-parisc/pdcpat.h | 247 ++++ include/asm-parisc/pgalloc.h | 404 ++++++ include/asm-parisc/pgtable.h | 338 +++++ include/asm-parisc/poll.h | 25 + include/asm-parisc/posix_types.h | 140 ++ include/asm-parisc/processor.h | 341 +++++ include/asm-parisc/psw.h | 54 + include/asm-parisc/ptrace.h | 56 + include/asm-parisc/real.h | 5 + include/asm-parisc/resource.h | 47 + include/asm-parisc/runway.h | 9 + include/asm-parisc/scatterlist.h | 20 + include/asm-parisc/segment.h | 6 + include/asm-parisc/semaphore-helper.h | 89 ++ include/asm-parisc/semaphore.h | 301 ++++ include/asm-parisc/sembuf.h | 25 + include/asm-parisc/serial.h | 47 + include/asm-parisc/setup.h | 10 + include/asm-parisc/shmbuf.h | 50 + include/asm-parisc/shmparam.h | 6 + include/asm-parisc/sigcontext.h | 20 + include/asm-parisc/siginfo.h | 234 ++++ include/asm-parisc/signal.h | 163 +++ include/asm-parisc/smp.h | 8 + include/asm-parisc/smplock.h | 49 + include/asm-parisc/socket.h | 59 + include/asm-parisc/sockios.h | 12 + include/asm-parisc/softirq.h | 15 + include/asm-parisc/som.h | 8 + include/asm-parisc/spinlock.h | 95 ++ include/asm-parisc/stat.h | 71 + include/asm-parisc/statfs.h | 25 + include/asm-parisc/string.h | 2 + include/asm-parisc/system.h | 147 ++ include/asm-parisc/termbits.h | 174 +++ include/asm-parisc/termios.h | 103 ++ include/asm-parisc/timex.h | 22 + include/asm-parisc/traps.h | 4 + include/asm-parisc/types.h | 54 + include/asm-parisc/uaccess.h | 190 +++ include/asm-parisc/ucontext.h | 12 + include/asm-parisc/unaligned.h | 20 + include/asm-parisc/unistd.h | 901 ++++++++++++ include/asm-parisc/user.h | 5 + include/linux/hdlcdrv.h | 2 +- include/linux/highuid.h | 28 +- include/linux/module.h | 2 +- include/linux/sched.h | 1 + include/linux/stallion.h | 2 +- include/linux/tty.h | 1 - include/linux/usb.h | 4 +- include/linux/wanrouter.h | 2 +- init/main.c | 9 +- kernel/context.c | 42 +- kernel/dma.c | 1 + kernel/kmod.c | 121 +- mm/mprotect.c | 1 + net/sunrpc/sched.c | 2 +- 246 files changed, 36658 insertions(+), 948 deletions(-) create mode 100644 Documentation/parisc/00-INDEX create mode 100644 Documentation/parisc/IODC.txt create mode 100644 Documentation/parisc/debugging create mode 100644 Documentation/parisc/mm create mode 100644 Documentation/parisc/registers create mode 100644 arch/parisc/Makefile create mode 100644 arch/parisc/config.in create mode 100644 arch/parisc/defconfig create mode 100644 arch/parisc/hpux/Makefile create mode 100644 arch/parisc/hpux/entry_hpux.S create mode 100644 arch/parisc/hpux/fs.c create mode 100644 arch/parisc/hpux/gate.S create mode 100644 arch/parisc/hpux/ioctl.c create mode 100644 arch/parisc/hpux/sys_hpux.c create mode 100644 arch/parisc/hpux/wrappers.S create mode 100644 arch/parisc/kernel/Makefile create mode 100644 arch/parisc/kernel/cache.c create mode 100644 arch/parisc/kernel/ccio-dma.c create mode 100644 arch/parisc/kernel/ccio-rm-dma.c create mode 100644 arch/parisc/kernel/drivers.c create mode 100644 arch/parisc/kernel/entry.S create mode 100644 arch/parisc/kernel/hardware.c create mode 100644 arch/parisc/kernel/head.S create mode 100644 arch/parisc/kernel/hpmc.S create mode 100644 arch/parisc/kernel/init_task.c create mode 100644 arch/parisc/kernel/inventory.c create mode 100644 arch/parisc/kernel/iosapic.c create mode 100644 arch/parisc/kernel/iosapic_private.h create mode 100644 arch/parisc/kernel/irq.c create mode 100644 arch/parisc/kernel/keyboard.c create mode 100644 arch/parisc/kernel/lasimap.map create mode 100644 arch/parisc/kernel/lba_pci.c create mode 100644 arch/parisc/kernel/led.c create mode 100644 arch/parisc/kernel/pa7300lc.c create mode 100644 arch/parisc/kernel/parisc_ksyms.c create mode 100644 arch/parisc/kernel/pci-dma.c create mode 100644 arch/parisc/kernel/pci.c create mode 100644 arch/parisc/kernel/pdc.c create mode 100644 arch/parisc/kernel/pdc_cons.c create mode 100644 arch/parisc/kernel/process.c create mode 100644 arch/parisc/kernel/ptrace.c create mode 100644 arch/parisc/kernel/real1.c create mode 100644 arch/parisc/kernel/real2.S create mode 100644 arch/parisc/kernel/sba_iommu.c create mode 100644 arch/parisc/kernel/semaphore.c create mode 100644 arch/parisc/kernel/setup.c create mode 100644 arch/parisc/kernel/signal.c create mode 100644 arch/parisc/kernel/sys_parisc.c create mode 100644 arch/parisc/kernel/syscall.S create mode 100644 arch/parisc/kernel/time.c create mode 100644 arch/parisc/kernel/traps.c create mode 100644 arch/parisc/lib/Makefile create mode 100644 arch/parisc/lib/bitops.c create mode 100644 arch/parisc/lib/checksum.c create mode 100644 arch/parisc/lib/lusercopy.S create mode 100644 arch/parisc/mm/Makefile create mode 100644 arch/parisc/mm/extable.c create mode 100644 arch/parisc/mm/fault.c create mode 100644 arch/parisc/mm/init.c create mode 100644 arch/parisc/mm/kmap.c create mode 100644 arch/parisc/mm/pa11.c create mode 100644 arch/parisc/mm/pa20.c create mode 100644 arch/parisc/tools/Makefile create mode 100644 arch/parisc/tools/offset.c create mode 100644 arch/parisc/vmlinux.lds create mode 100644 drivers/net/lasi_82596.c create mode 100644 drivers/video/fbcon-sti.c create mode 100644 drivers/video/sti-bmode.h create mode 100644 drivers/video/sti.h create mode 100644 drivers/video/sticon-bmode.c create mode 100644 drivers/video/sticon.c create mode 100644 drivers/video/sticore.c create mode 100644 drivers/video/stifb.c create mode 100644 include/asm-parisc/a.out.h create mode 100644 include/asm-parisc/asmregs.h create mode 100644 include/asm-parisc/assembly.h create mode 100644 include/asm-parisc/atomic.h create mode 100644 include/asm-parisc/bitops.h create mode 100644 include/asm-parisc/bootdata.h create mode 100644 include/asm-parisc/bugs.h create mode 100644 include/asm-parisc/byteorder.h create mode 100644 include/asm-parisc/cache.h create mode 100644 include/asm-parisc/checksum.h create mode 100644 include/asm-parisc/current.h create mode 100644 include/asm-parisc/delay.h create mode 100644 include/asm-parisc/div64.h create mode 100644 include/asm-parisc/elf.h create mode 100644 include/asm-parisc/errno.h create mode 100644 include/asm-parisc/fcntl.h create mode 100644 include/asm-parisc/fixmap.h create mode 100644 include/asm-parisc/gsc.h create mode 100644 include/asm-parisc/hardirq.h create mode 100644 include/asm-parisc/hardware.h create mode 100644 include/asm-parisc/hdreg.h create mode 100644 include/asm-parisc/hil.h create mode 100644 include/asm-parisc/hw_irq.h create mode 100644 include/asm-parisc/ide.h create mode 100644 include/asm-parisc/io.h create mode 100644 include/asm-parisc/ioctl.h create mode 100644 include/asm-parisc/ioctls.h create mode 100644 include/asm-parisc/iosapic.h create mode 100644 include/asm-parisc/ipcbuf.h create mode 100644 include/asm-parisc/irq.h create mode 100644 include/asm-parisc/keyboard.h create mode 100644 include/asm-parisc/led.h create mode 100644 include/asm-parisc/linux_logo.h create mode 100644 include/asm-parisc/machdep.h create mode 100644 include/asm-parisc/mc146818rtc.h create mode 100644 include/asm-parisc/md.h create mode 100644 include/asm-parisc/mman.h create mode 100644 include/asm-parisc/mmu.h create mode 100644 include/asm-parisc/mmu_context.h create mode 100644 include/asm-parisc/msgbuf.h create mode 100644 include/asm-parisc/namei.h create mode 100644 include/asm-parisc/page.h create mode 100644 include/asm-parisc/param.h create mode 100644 include/asm-parisc/parport.h create mode 100644 include/asm-parisc/parport_gsc.h create mode 100644 include/asm-parisc/pci.h create mode 100644 include/asm-parisc/pdc.h create mode 100644 include/asm-parisc/pdcpat.h create mode 100644 include/asm-parisc/pgalloc.h create mode 100644 include/asm-parisc/pgtable.h create mode 100644 include/asm-parisc/poll.h create mode 100644 include/asm-parisc/posix_types.h create mode 100644 include/asm-parisc/processor.h create mode 100644 include/asm-parisc/psw.h create mode 100644 include/asm-parisc/ptrace.h create mode 100644 include/asm-parisc/real.h create mode 100644 include/asm-parisc/resource.h create mode 100644 include/asm-parisc/runway.h create mode 100644 include/asm-parisc/scatterlist.h create mode 100644 include/asm-parisc/segment.h create mode 100644 include/asm-parisc/semaphore-helper.h create mode 100644 include/asm-parisc/semaphore.h create mode 100644 include/asm-parisc/sembuf.h create mode 100644 include/asm-parisc/serial.h create mode 100644 include/asm-parisc/setup.h create mode 100644 include/asm-parisc/shmbuf.h create mode 100644 include/asm-parisc/shmparam.h create mode 100644 include/asm-parisc/sigcontext.h create mode 100644 include/asm-parisc/siginfo.h create mode 100644 include/asm-parisc/signal.h create mode 100644 include/asm-parisc/smp.h create mode 100644 include/asm-parisc/smplock.h create mode 100644 include/asm-parisc/socket.h create mode 100644 include/asm-parisc/sockios.h create mode 100644 include/asm-parisc/softirq.h create mode 100644 include/asm-parisc/som.h create mode 100644 include/asm-parisc/spinlock.h create mode 100644 include/asm-parisc/stat.h create mode 100644 include/asm-parisc/statfs.h create mode 100644 include/asm-parisc/string.h create mode 100644 include/asm-parisc/system.h create mode 100644 include/asm-parisc/termbits.h create mode 100644 include/asm-parisc/termios.h create mode 100644 include/asm-parisc/timex.h create mode 100644 include/asm-parisc/traps.h create mode 100644 include/asm-parisc/types.h create mode 100644 include/asm-parisc/uaccess.h create mode 100644 include/asm-parisc/ucontext.h create mode 100644 include/asm-parisc/unaligned.h create mode 100644 include/asm-parisc/unistd.h create mode 100644 include/asm-parisc/user.h diff --git a/CREDITS b/CREDITS index 1a013c565b30..a9ad20d9d45d 100644 --- a/CREDITS +++ b/CREDITS @@ -341,6 +341,7 @@ D: Texas Instruments PCILynx IEEE 1394 driver N: Al Borchers E: alborchers@steinerpoint.com D: Author/maintainer of Digi AccelePort USB driver +D: work on usbserial and keyspan_pda drivers S: 4912 Zenith Ave. S. S: Minneapolis, MN 55410 S: USA @@ -410,6 +411,10 @@ N: Zach Brown E: zab@zabbo.net D: maestro pci sound +N: Gary Brubaker +E: xavyer@ix.netcom.com +D: USB Serial Empeg Empeg-car Mark I/II Driver + N: Ray Burr E: ryb@nightmare.com D: Original author of Amiga FFS filesystem diff --git a/Documentation/Configure.help b/Documentation/Configure.help index eef5afa3da8a..7e813107d15a 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -10372,6 +10372,9 @@ CONFIG_USB_SERIAL_DIGI_ACCELEPORT parallel port on the USB 2 appears as a third serial port on Linux. The Digi Acceleport USB 8 is not yet supported by this driver. + This driver works under SMP with the usb-uhci driver. It does not + work under SMP with the uhci driver. + This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called digi_acceleport.o. If you want to compile @@ -10380,7 +10383,9 @@ CONFIG_USB_SERIAL_DIGI_ACCELEPORT USB Empeg empeg-car Mark I/II Driver CONFIG_USB_SERIAL_EMPEG Say Y here if you want to connect to your Empeg empeg-car Mark I/II - mp3 player via USB. + mp3 player via USB. The driver uses a single ttyUSB{0,1,2,...} + device node. See Documentation/usb/usb-serial.txt for more + tidbits of information. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). diff --git a/Documentation/parisc/00-INDEX b/Documentation/parisc/00-INDEX new file mode 100644 index 000000000000..7c8494ea2952 --- /dev/null +++ b/Documentation/parisc/00-INDEX @@ -0,0 +1,10 @@ +00-INDEX + - this file. +IODC.txt + - Documentation IODC +debugging + - some debugging hints for real-mode code +mm + - Documentation on parisc mm status +registers + - current/planned usage of registers diff --git a/Documentation/parisc/IODC.txt b/Documentation/parisc/IODC.txt new file mode 100644 index 000000000000..d686536f14d1 --- /dev/null +++ b/Documentation/parisc/IODC.txt @@ -0,0 +1,68 @@ +Some notes on IODC, its general brokenness, and how to work around it. + +Short Version + +IODC is HP's pre-PCI standard for device identification (a la PCI vendor, +device IDs), detection, configuration, initialization and so on. + +It also can provide firmware function to do the actual IO, which are slow, +not really defined for runtime usage and generally not desirable. (There +are other firmware standards, such as STI, to do real IO). + +Usually, there are two parts to IODC. The actual ROMs, which are laid out, +detected aso in a bus-specific manner (IO_DC_ADDRESS / IO_DC_DATA on +GSC/Runway, PCI spec - compliant ROMs for PCI, God-only-knows how on EISA), +and the slightly cooked data read by PDC_IODC. + +The ROM layout is generally icky (only one byte out of every 4-byte-word +might be valid, and many devices don't implement required options), so +using PDC_IODC is highly recommended. (In fact, you should use the device +lists set up by the kernel proper instead of calling PDC_IODC yourself). + +Now, let's have a look at what the cooked ROM looks like (see iodc.pdf for +the details, this is the simplified version). + +Basically, the first 8 bytes of IODC contain two 32-bit ids called HVERSION +and SVERSION. Those are further split up into bit fields, and +unfortunately just ignoring this split up isn't an option. + +SVERSION consists of a 4-bit revision field, a 20-bit model field and a +8-bit opt field. Now, forget the revision and opt fields exist. Basically, +the model field is equivalent to a PCI device id (there is no vendor id. +this is proprietary hardware we're talking about). That is, all your +driver cares for, in 90 % of the cases, is to find all devices that match +the model field. + +The rev field is - you guessed it - roughly equivalent to the revision +byte for PCI, with the exception that higher revisions should be strict +supersets of lower revisions. + +The last byte of HVERSION, "type", and the last byte of SVERSION, "opt", +belong together; type gives a very rough indication of what the device +is supposed to do, and opt contains some type-specific information. (For +example, the "bus converter" (ie bus bridge) type encodes the kind of +bus behind the bridge in the opt field. + +The rest of HVERSION contains, in most cases, a number identifying the +machine the chip was used in, or a revision indicator that just fixed +bugs and didn't add any features (or was done in a shrinked process or +whatever). + +So, here's the interface you actually should use to find your devices: + + +/* Find a device, matching the model field of sversion only (from=NULL + * for the first call */ +struct iodc_dev *iodc_find_device(u32 sversion, struct iodc_dev *from); + + +Here's a function you should use if you have special requirements, such +as finding devices by type rather than by model. Generally, if you're +using this, you should be me). + +/* Find a device, masking out bits as specified */ +struct iodc_dev *iodc_find_device_mask(u32 hversion, u32 sversion, + u32 hversion_mask, u32 sversion_mask, struct iodc_dev *from); + + + Philipp Rumpf diff --git a/Documentation/parisc/debugging b/Documentation/parisc/debugging new file mode 100644 index 000000000000..5e060917ac8f --- /dev/null +++ b/Documentation/parisc/debugging @@ -0,0 +1,39 @@ +okay, here are some hints for debugging the lower-level parts of +linux/parisc. + + +1. Absolute addresses + +A lot of the assembly code currently runs in real mode, which means +absolute addresses are used instead of virtual addresses as in the +rest of the kernel. To translate an absolute address to a virtual +address you can lookup in System.map, add __PAGE_OFFSET (0xc0000000 +currently). + + +2. HPMCs + +When real-mode code tries to access non-existent memory, you'll get +an HPMC instead of a kernel oops. To debug an HPMC, try to find +the System Responder/Requestor addresses. The System Requestor +address should match (one of the) processor HPAs (high addresses in +the I/O range); the System Responder address is the address real-mode +code tried to access. + +Typical values for the System Responder address are addresses larger +than __PAGE_OFFSET (0xc0000000) which mean a virtual address didn't +get translated to a physical address before real-mode code tried to +access it. + + +3. Q bit fun + +Certain, very critical code has to clear the Q bit in the PSW. What +happens when the Q bit is cleared is the CPU does not update the +registers interruption handlers read to find out where the machine +was interrupted - so if you get an interruption between the instruction +that clears the Q bit and the RFI that sets it again you don't know +where exactly it happened. If you're lucky the IAOQ will point to the +instrucion that cleared the Q bit, if you're not it points anywhere +at all. Usually Q bit problems will show themselves in unexplainable +system hangs or running off the end of physical memory. diff --git a/Documentation/parisc/mm b/Documentation/parisc/mm new file mode 100644 index 000000000000..d53b295086a1 --- /dev/null +++ b/Documentation/parisc/mm @@ -0,0 +1,31 @@ + +The current state of Linux/PA-RISC mm is BROKEN. + +Someone needs to sit down and thoroughly rewrite all the cache flushing +macro definitions. Here are some of the problems, followed by what I +think needs to be done about them. + +(1) We're using fdce / fice everywhere. This has to stop (except in +the routines which flush the entire cache). The right instructions to +be using are fdc/fic. + +(2) fdc/fic will throw exceptions if the address they reference isn't +mapped. Therefore we need to check the page is mapped before flushing +(we're guaranteed not to have the page dirty if we don't have a software +mapping for it any longer, right?) + +(3) the flush macros are right now tunnelled down to one routine to flush +the data cache and one routine to flush the insn cache. this is wrong. +we should take hints from how we're called and optimise our routines +accordingly. + +(4) fdc/fic actually take space register arguments. fic takes an 3-bit sr +argument and fdc takes a 2-bit sr argument. right now, there's a lot of +pissing about with %sr1 and all the macros use %sr1. This is crazy. We +normally _know_ what's being referred to, and it's the current task. So +if we want to flush that, just use %sr3. If it happens to be kernel, +use %sr0 for fdc and %sr4 for fic. + +(5) we need to write flush_kernel_dcache_range and use it on kernel +addresses. all the macros are defined to work on the _current task's_ +virtual address space. diff --git a/Documentation/parisc/registers b/Documentation/parisc/registers new file mode 100644 index 000000000000..28097fe49595 --- /dev/null +++ b/Documentation/parisc/registers @@ -0,0 +1,126 @@ +Register Usage for Linux/PA-RISC + +[ an asterisk is used for planned usage which is currently unimplemented ] + + General Registers as specified by ABI + + FPU Registers must not be used in kernel mode + + Control Registers + +CR 0 (Recovery Counter) used for ptrace +CR 1-CR 7(undefined) unused +CR 8 (Protection ID) per-process value* +CR 9, 12, 13 (PIDS) unused +CR10 (CCR) lazy FPU saving* +CR11 as specified by ABI +CR14 (interruption vector) initialized to fault_vector +CR15 (EIEM) initialized to all ones* +CR16 (Interval Timer) timer interrupt +CR17-CR22 interruption parameters +CR23 (EIRR) read for pending interrupts +CR24 (TR 0) Kernel Space Page Directory Pointer +CR25 (TR 1) User Space Page Directory Pointer +CR26 (TR 2) +CR27 (TR 3) +CR28 (TR 4) used by interruption handlers +CR29 (TR 5) used by interruption handlers +CR30 (TR 6) current / 0 +CR31 (TR 7) used by interruption handlers + + Space Registers (kernel mode) + +SR0 temporary space register +SR4-SR7 set to 0 +SR1 temporary space register +SR2 unused +SR3 used for userspace accesses (current process)* + + Space Registers (user mode) + +SR0 temporary space register +SR1 temporary space register +SR2 holds space of linux gateway page +SR3 holds user address space value while in kernel +SR4-SR7 Defines short address space for user/kernel + + + Processor Status Word + +W (64-bit addresses) 0 +E (Little-endian) 0 +S (Secure Interval Timer) 0 +T (Taken Branch Trap) 0 +H (Higher-privilege trap) 0 +L (Lower-privilege trap) 0 +N (Nullify next instruction) used by C code +X (Data memory break disable) 0 +B (Taken Branch) used by C code +C (code address translation) 1, 0 while executing real-mode code +V (divide step correction) used by C code +M (HPMC mask) 0, 1 while executing HPMC handler* +C/B (carry/borrow bits) used by C code +O (ordered references) 1* +F (performance monitor) 0 +R (Recovery Counter trap) 0 +Q (collect interruption state) 1 (0 in code directly preceding an rfi) +P (Protection Identifiers) 1* +D (Data address translation) 1, 0 while executing real-mode code +I (external interrupt mask) used by cli()/sti() macros + + "Invisible" Registers + +PSW default W value 0 +PSW default E value 0 +Shadow Registers used by interruption handler code +TOC enable bit 1 + +========================================================================= +Info from John Marvin: + +From: "John Marvin" +To: randolf@tausq.org +Subject: Re: parisc asm questions + +[...] + +For the general registers: + +r1,r2,r19-r26,r28,r29 & r31 can be used without saving them first. And of +course, you need to save them if you care about them, before calling +another procedure. Some of the above registers do have special meanings +that you should be aware of: + + r1: The addil instruction is hardwired to place its result in r1, + so if you use that instruction be aware of that. + + r2: This is the return pointer. In general you don't want to + use this, since you need the pointer to get back to your + caller. However, it is grouped with this set of registers + since the caller can't rely on the value being the same + when you return, i.e. you can copy r2 to another register + and return through that register after trashing r2, and + that should not cause a problem for the calling routine. + + r19-r22: these are generally regarded as temporary registers. + Note that in 64 bit they are arg7-arg4. + + r23-r26: these are arg3-arg0, i.e. you can use them if you + don't care about the values that were passed in anymore. + + r28,r29: are ret0 and ret1. They are what you pass return values + in. r28 is the primary return. I'm not sure I remember + under what circumstances stuff is returned in r29 (millicode + perhaps). + + r31: the ble instruction puts the return pointer in here. + + +r3-r18,r27,r30 need to be saved and restored. r3-r18 are just + general purpose registers. r27 is the data pointer, and is + used to make references to global variables easier. r30 is + the stack pointer. + +John + + diff --git a/Documentation/usb/URB.txt b/Documentation/usb/URB.txt index 7d99567adcdd..234c75cf3614 100644 --- a/Documentation/usb/URB.txt +++ b/Documentation/usb/URB.txt @@ -1,3 +1,5 @@ +Revised: 2000-Dec-05. + 1. Specification of the API 1.1. Basic concept or 'What is an URB?' @@ -8,16 +10,16 @@ called USB Request Block, or URB for short. - An URB consists of all relevant information to execute any USB transaction and deliver the data and status back. -- Execution of an URB is an inherently asynchronous operation, i.e. the -submit_urb(urb) call returns immediately after it has successfully queued +- Execution of an URB is inherently an asynchronous operation, i.e. the +usb_submit_urb(urb) call returns immediately after it has successfully queued the requested action. - Ongoing transfers for one URB (e.g. ISO) can simply be canceled with -unlink_urb(urb) at any time. +usb_unlink_urb(urb) at any time. - Each URB has a completion handler, which is called after the action has been successfully completed or canceled (INT transfers behave a bit -different, see below). The URB also contains a context-pointer for free +differently, see below). The URB also contains a context-pointer for free usage and information passing to the completion handler. - URBs can be linked. After completing one URB, the next one can be @@ -31,6 +33,8 @@ URB-machinery. typedef struct urb { + spinlock_t lock; // lock for the URB + // ignore, for host controller/URB machine internal use void *hcpriv; // private data for host controller struct list_head urb_list; // list pointer to all active urbs @@ -39,12 +43,12 @@ typedef struct urb struct urb* next; // pointer to next URB struct usb_device *dev; // pointer to associated USB device -// pipe is assembled by the various well known pipe-macros in usb.h +// pipe is assembled by the various well-known pipe macros in usb.h unsigned int pipe; // pipe information // status after each completion int status; // returned status - unsigned int transfer_flags; // ASAP, SP_OK, etc. + unsigned int transfer_flags; // ASAP, DISABLE_SPD, etc. // for data stage (CTRL), BULK, INT and ISO void *transfer_buffer; // associated data buffer @@ -55,7 +59,7 @@ typedef struct urb // setup stage for CTRL (always 8 bytes!) unsigned char* setup_packet; // setup packet (control only) - + // with ASAP, start_frame is set to the determined frame int start_frame; // start frame (iso/irq) int number_of_packets; // # of packets (iso/int) @@ -66,7 +70,7 @@ typedef struct urb usb_complete_t complete; // pointer to completion routine // // specification of the requested data offsets and length for ISO - iso_packet_descriptor_t iso_frame_desc[0]; + iso_packet_descriptor_t iso_frame_desc[0]; } urb_t, *purb_t; @@ -74,7 +78,7 @@ typedef struct urb URBs are allocated with the following call - purb_t alloc_urb(int isoframes) + purb_t usb_alloc_urb(int isoframes) Return value is a pointer to the allocated URB, 0 if allocation failed. The parameter isoframes specifies the number of isochronous transfer frames @@ -82,7 +86,7 @@ you want to schedule. For CTRL/BULK/INT, use 0. To free an URB, use - void free_urb(purb_t purb) + void usb_free_urb(purb_t purb) This call also may free internal (host controller specific) memory in the future. @@ -91,12 +95,13 @@ future. 1.4. What has to be filled in? Depending on the type of transaction, there are some macros -(FILL_CONTROL_URB, FILL_BULK_URB, and FILL_INT_URB, defined in uhci.h) +(FILL_CONTROL_URB, FILL_CONTROL_URB_TO, FILL_BULK_URB, +FILL_BULK_URB_TO, and FILL_INT_URB, defined in usb.h) that simplify the URB creation. In general, all macros need the usb -device pointer, the pipe (usual format), the transfer buffer, the -desired transfer length, the completion handler, and its context. -Take a look at the uhci_control_msg-function that convert the old API -into an URB. +device pointer, the pipe (usual format from usb.h), the transfer buffer, +the desired transfer length, the completion handler, and its context. +Take a look at the usb_control_msg function that converts the old API +into the URB API. Flags: For ISO there are two startup behaviors: Specified start_frame or ASAP. @@ -114,7 +119,7 @@ re-submission for INT transfers that are being continued. Just call - int submit_urb(purb_t purb) + int usb_submit_urb(purb_t purb) It immediately returns, either with status 0 (request queued) or some error code, usually caused by the following: @@ -128,7 +133,7 @@ error code, usually caused by the following: - Invalid INT interval (-EINVAL) - More than one packet for INT (-EINVAL) -After submission, urb->status is USB_ST_URB_PENDING. +After submission, urb->status is USB_ST_URB_PENDING (-EINPROGRESS). For isochronous endpoints, subsequent submitting of URBs to the same endpoint with the ASAP flag result in a seamless ISO streaming. Exception: The @@ -142,18 +147,18 @@ independent of the transfer flags (implicitly ASAP). For an URB which you've submitted, but which hasn't been returned to your driver by the host controller, call - int unlink_urb(purb_t purb) + int usb_unlink_urb(purb_t purb) It removes the urb from the internal list and frees all allocated HW descriptors. The status is changed to USB_ST_URB_KILLED. After -unlink_urb() returns, you can safely free the URB with free_urb(urb) +usb_unlink_urb() returns, you can safely free the URB with usb_free_urb(urb) and all other possibly associated data (urb->context etc.) There is also an asynchronous unlink mode. To use this, set the the USB_ASYNC_UNLINK flag in urb->transfer flags before calling usb_unlink_urb(). When using async unlinking, the URB will not -normally be unlinked when unlink_urb() returns. Instead, wait for -the completion handler to be called. +normally be unlinked when usb_unlink_urb() returns. Instead, wait +for the completion handler to be called. 1.7. What about the completion handler? @@ -187,13 +192,13 @@ in completion handlers. 1.8. How to do isochronous (ISO) transfers? For ISO transfers you have to append the iso_packet_descriptor_t structure -to the URB for each frame you want to schedule. When using alloc_urb(n) -(recommended), the isoframe-parameter n can be used to allocate the -structures for n frames. +to the URB for each frame you want to schedule. When using usb_alloc_urb(n) +(recommended), the iso_packets parameter can be used to allocate the +structures for iso_packets frames. For each entry you have to specify the data offset for this frame (base is transfer_buffer), and the length you want to write/expect to read. -After completion, actual_length contains the actual transfered length and +After completion, actual_length contains the actual transferred length and status contains the resulting USB-status for the ISO transfer for this frame. It is allowed to specify a varying length from frame to frame (e.g. for audio synchronisation/adaptive transfer rates). You can also use the length @@ -217,7 +222,7 @@ for 1, 2, 4,... 128ms. Only one URB is allocated for each interrupt. After calling the completion handler, that URB is recycled by the host controller driver (HCD). With the submission of one URB, the interrupt is scheduled until it is -canceled by unlink_urb. - -The submit_urb()-call modifies urb->interval to the rounded value. +canceled by usb_unlink_urb. +The usb_submit_urb() call modifies urb->interval to the implemented interval +value that is less than or equal to the requested interval value. diff --git a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt index d1dccece539c..e42ab36e07c6 100644 --- a/Documentation/usb/error-codes.txt +++ b/Documentation/usb/error-codes.txt @@ -1,12 +1,12 @@ -$Id: README.error-codes,v 1.1 1999/12/14 14:03:02 fliegl Exp $ +Revised: 2000-Dec-05. This is the documentation of (hopefully) all possible error codes (and -their interpretation) that can be returned from the hostcontroller driver +their interpretation) that can be returned from the host controller drivers and from usbcore. NOTE: -The USB_ST_* codes are deferred and are only listed for compatibility, new -software should use only -E* instead! +The USB_ST_* codes are deprecated and are only listed for compatibility; +new software should use only -E* instead! @@ -25,12 +25,16 @@ USB-specific: -ENODEV specified USB-device or bus doesn't exist --ENXIO specified endpoint doesn't exist on the device +USB_ST_REQUEST_ERROR +-ENXIO a) specified endpoint doesn't exist on the device + b) an URB is already queued to this endpoint and + USB_QUEUE_BULK wasn't used (UHCI HCDs only) USB_ST_URB_INVALID_ERROR -EINVAL a) Invalid transfer type specified (or not supported) b) Invalid interrupt interval (0<=n<256) c) more than one interrupt packet requested + d) ISO: number_of_packets is < 0 -EAGAIN a) specified ISO start frame too early b) (using ISO-ASAP) too much scheduled for the future @@ -38,6 +42,7 @@ USB_ST_URB_INVALID_ERROR -EFBIG too much ISO frames requested (currently uhci>900) +USB_ST_STALL -EPIPE specified pipe-handle is already stalled -EMSGSIZE endpoint message size is zero, do interface/alternate setting @@ -59,7 +64,7 @@ USB_ST_NOERROR 0 Transfer completed successfully USB_ST_URB_KILLED --ENOENT URB was canceled by unlink_urb +-ENOENT URB was canceled by usb_unlink_urb USB_ST_URB_PENDING -EINPROGRESS URB still pending, no results yet @@ -73,12 +78,28 @@ USB_ST_INTERNALERROR USB_ST_CRC -EILSEQ CRC mismatch +USB_ST_STALL -EPIPE a) babble detect b) endpoint stalled -USB_ST_BUFFERUNDERRUN --ENOST buffer error +USB_ST_BUFFEROVERRUN +-ECOMM During an IN transfer, the host controller + received data from an endpoint faster than it + could be written to system memory +USB_ST_BUFFERUNDERRUN +-ENOSR During an OUT transfer, the host controller + could not retrieve data from system memory fast + enough to keep up with the USB data rate + +USB_ST_DATAOVERRUN +-EOVERFLOW The amount of data returned by the endpoint was + greater than either the max packet size of the + endpoint or the remaining buffer size + +USB_ST_DATAUNDERRUN +-EREMOTEIO The endpoint returned less than max packet size + and that amount did not fill the specified buffer USB_ST_NORESPONSE USB_ST_TIMEOUT -ETIMEDOUT transfer timed out, NAK @@ -104,14 +125,7 @@ USB_ST_URB_INVALID_ERROR ************************************************************************** usb_register(): -USB_ST_NOTSUPPORTED -EINVAL error during registering new driver -usb_terminate_bulk(): -USB_ST_REMOVED --ENODEV urb already removed - usb_get_*/usb_set_*(): All USB errors (submit/status) can occur - - diff --git a/Documentation/usb/usb-help.txt b/Documentation/usb/usb-help.txt index 5ad2aaa57b85..98ade189e61b 100644 --- a/Documentation/usb/usb-help.txt +++ b/Documentation/usb/usb-help.txt @@ -7,6 +7,7 @@ linux/Documentation/usb/*, see the following: Linux-USB project: http://www.linux-usb.org mirrors at http://www.suse.cz/development/linux-usb/ and http://usb.in.tum.de/linux-usb/ + and http://it.linux-usb.org Linux USB Guide: http://linux-usb.sourceforge.net Linux-USB device overview (working devices and drivers): http://www.qbik.ch/usb/devices/ diff --git a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt index 00ce3dfd4449..d4740c4048e4 100644 --- a/Documentation/usb/usb-serial.txt +++ b/Documentation/usb/usb-serial.txt @@ -139,6 +139,9 @@ Digi AccelePort Driver (plus a parallel port) and 4 port USB serial converters. The driver does NOT yet support the Digi AccelePort USB 8. + This driver works under SMP with the usb-uhci driver. It does not + work under SMP with the uhci driver. + The driver is generally working, though we still have a few more ioctls to implement and final testing and debugging to do. The paralled port on the USB 2 is supported as a serial to parallel converter; in other @@ -187,6 +190,12 @@ Empeg empeg-car Mark I/II Driver (empeg.c) This is an experimental driver to provide connectivity support for the client synchronization tools for an Empeg empeg-car mp3 player. + Tips: + + * Don't forget to create the device nodes for ttyUSB{0,1,2,...} + * modprobe empeg (modprobe is your friend) + * emptool --usb /dev/ttyUSB0 (or whatever you named your device node) + The driver is still pretty new, so some testing 'in the wild' would be helpful. :) diff --git a/MAINTAINERS b/MAINTAINERS index c42849b99e45..a6ae5d123e20 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1325,7 +1325,7 @@ M: pberger@brimson.com M: alborchers@steinerpoint.com L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net -S: Supported +S: Maintained USB SERIAL KEYSPAN DRIVER P: Hugh Blemings @@ -1351,6 +1351,13 @@ L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net S: Maintained +USB SERIAL EMPEG EMPEG-CAR MARK I/II DRIVER +P: Gary Brubaker +M: xavyer@ix.netcom.com +L: linux-usb-users@lists.sourceforge.net +L: linux-usb-devel@lists.sourceforge.net +S: Maintained + USB MASS STORAGE DRIVER P: Matthew Dharm M: mdharm-usb@one-eyed-alien.net diff --git a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S index 276c63368788..c52b170ecdca 100644 --- a/arch/i386/boot/setup.S +++ b/arch/i386/boot/setup.S @@ -631,43 +631,39 @@ end_move_self: # now we are at the right place # appropriate # that was painless, now we enable a20 - -# -# First, try the "fast A20 gate". -# - inb $0x92,%al - orb $0x02,%al # Fast A20 on - andb $0xfe,%al # Don't reset CPU! - outb %al,$0x92 - -# -# Now comes the tricky part: some machines don't have a KBC and thus -# would end up looping almost indefinitely here. HOWEVER, once we -# have done the first command write, we must not stop the sequence. -# Therefore, the first empty_8042 should check to see if the fast A20 -# did the trick and stop its probing at that stage; but subsequent ones -# must not do so. -# - movb $0x01,%dl # A20-sensitive call empty_8042 - jnz a20_wait # A20 already on? movb $0xD1, %al # command write outb %al, $0x64 - call empty_8042_no_a20_exit + call empty_8042 movb $0xDF, %al # A20 on outb %al, $0x60 - call empty_8042_no_a20_exit + call empty_8042 + +# +# You must preserve the other bits here. Otherwise embarrasing things +# like laptops powering off on boot happen. Corrected version by Kira +# Brown from Linux 2.2 +# + inb $0x92, %al # + orb $02, %al # "fast A20" version + outb %al, $0x92 # some chips have only this # wait until a20 really *is* enabled; it can take a fair amount of # time on certain systems; Toshiba Tecras are known to have this # problem. The memory location used here (0x200) is the int 0x80 # vector, which should be safe to use. + xorw %ax, %ax # segment 0x0000 + movw %ax, %fs + decw %ax # segment 0xffff (HMA) + movw %ax, %gs a20_wait: - call a20_test - jz a20_wait + incw %ax # unused memory location <0xfff0 + movw %ax, %fs:(0x200) # we use the "int 0x80" vector + cmpw %gs:(0x210), %ax # and its corresponding HMA addr + je a20_wait # loop until no longer aliased # make sure any possible coprocessor is properly reset.. xorw %ax, %ax @@ -830,25 +826,21 @@ bootsect_panic_mess: # Some machines have delusions that the keyboard buffer is always full # with no keyboard attached... # -# If %dl is nonzero on entry, terminate with ZF=0 if A20 becomes alive, -# otherwise terminate with ZF=1. +# If there is no keyboard controller, we will usually get 0xff +# to all the reads. With each IO taking a microsecond and +# a timeout of 100,000 iterations, this can take about half a +# second ("delay" == outb to port 0x80). That should be ok, +# and should also be plenty of time for a real keyboard controller +# to empty. +# -empty_8042_no_a20_exit: - xorb %dl,%dl # Not A20-sensitive empty_8042: pushl %ecx - movl $0x000FFFFF, %ecx + movl $100000, %ecx empty_8042_loop: decl %ecx - jz empty_8042_end_loop # ZF=1 - - # Always call the test routine to keep delays constant - call a20_test - jz ignore_a20 - and %dl,%dl - jnz empty_8042_end_loop # ZF=0 -ignore_a20: + jz empty_8042_end_loop call delay @@ -863,38 +855,10 @@ ignore_a20: no_output: testb $2, %al # is input buffer full? jnz empty_8042_loop # yes - loop - # ZF=1 - empty_8042_end_loop: popl %ecx ret -a20_test: - pushw %ax - pushw %cx - pushw %fs - pushw %gs - xorw %ax, %ax # segment 0x0000 - movw %ax, %fs - decw %ax # segment 0xffff (HMA) - movw %ax, %gs - movw 0x100,%cx - movw %fs:(0x200),%ax # So we keep cycling... - pushw %ax # Be extra paranoid... -a20_loop: - incw %ax # unused memory location <0xfff0 - movw %ax, %fs:(0x200) # we use the "int 0x80" vector - cmpw %gs:(0x210), %ax # and its corresponding HMA addr - jnz a20_ret # if ZF not set A20 is functional - loop a20_loop -a20_ret: - popw %fs:(0x200) - popw %gs - popw %fs - popw %cx - popw %ax - ret # if ZF set A20 is not operational - # Read the cmos clock. Return the seconds in al gettime: pushw %cx @@ -911,7 +875,6 @@ gettime: # Delay is needed after doing I/O delay: - outb %al,$0x80 # What the main kernel uses outb %al,$0x80 ret diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index fc54896f8e27..4179c8f13b36 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -220,66 +220,19 @@ void __init setup_local_APIC (void) BUG(); /* - * Set up LVT0, LVT1: - * - * set up through-local-APIC on the BP's LINT0. This is not - * strictly necessery in pure symmetric-IO mode, but sometimes - * we delegate interrupts to the 8259A. - */ - /* - * TODO: set up through-local-APIC from through-I/O-APIC? --macro - */ - value = apic_read(APIC_LVT0) & APIC_LVT_MASKED; - if (!smp_processor_id() && (pic_mode || !value)) { - value = APIC_DM_EXTINT; - printk("enabled ExtINT on CPU#%d\n", smp_processor_id()); - } else { - value = APIC_DM_EXTINT | APIC_LVT_MASKED; - printk("masked ExtINT on CPU#%d\n", smp_processor_id()); - } - apic_write_around(APIC_LVT0, value); - - /* - * only the BP should see the LINT1 NMI signal, obviously. + * Intel recommends to set DFR, LDR and TPR before enabling + * an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel + * document number 292116). So here it goes... */ - if (!smp_processor_id()) - value = APIC_DM_NMI; - else - value = APIC_DM_NMI | APIC_LVT_MASKED; - if (!APIC_INTEGRATED(ver)) /* 82489DX */ - value |= APIC_LVT_LEVEL_TRIGGER; - apic_write_around(APIC_LVT1, value); - - if (APIC_INTEGRATED(ver)) { /* !82489DX */ - maxlvt = get_maxlvt(); - if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ - apic_write(APIC_ESR, 0); - value = apic_read(APIC_ESR); - printk("ESR value before enabling vector: %08lx\n", value); - - value = ERROR_APIC_VECTOR; // enables sending errors - apic_write_around(APIC_LVTERR, value); - /* - * spec says clear errors after enabling vector. - */ - if (maxlvt > 3) - apic_write(APIC_ESR, 0); - value = apic_read(APIC_ESR); - printk("ESR value after enabling vector: %08lx\n", value); - } else - printk("No ESR for 82489DX.\n"); /* - * Set Task Priority to 'accept all'. We never change this - * later on. + * Put the APIC into flat delivery mode. + * Must be "all ones" explicitly for 82489DX. */ - value = apic_read(APIC_TASKPRI); - value &= ~APIC_TPRI_MASK; - apic_write_around(APIC_TASKPRI, value); + apic_write_around(APIC_DFR, 0xffffffff); /* - * Set up the logical destination ID and put the - * APIC into flat delivery mode. + * Set up the logical destination ID. */ value = apic_read(APIC_LDR); value &= ~APIC_LDR_MASK; @@ -287,9 +240,12 @@ void __init setup_local_APIC (void) apic_write_around(APIC_LDR, value); /* - * Must be "all ones" explicitly for 82489DX. + * Set Task Priority to 'accept all'. We never change this + * later on. */ - apic_write_around(APIC_DFR, 0xffffffff); + value = apic_read(APIC_TASKPRI); + value &= ~APIC_TPRI_MASK; + apic_write_around(APIC_TASKPRI, value); /* * Now that we are all set up, enable the APIC @@ -326,6 +282,56 @@ void __init setup_local_APIC (void) */ value |= SPURIOUS_APIC_VECTOR; apic_write_around(APIC_SPIV, value); + + /* + * Set up LVT0, LVT1: + * + * set up through-local-APIC on the BP's LINT0. This is not + * strictly necessery in pure symmetric-IO mode, but sometimes + * we delegate interrupts to the 8259A. + */ + /* + * TODO: set up through-local-APIC from through-I/O-APIC? --macro + */ + value = apic_read(APIC_LVT0) & APIC_LVT_MASKED; + if (!smp_processor_id() && (pic_mode || !value)) { + value = APIC_DM_EXTINT; + printk("enabled ExtINT on CPU#%d\n", smp_processor_id()); + } else { + value = APIC_DM_EXTINT | APIC_LVT_MASKED; + printk("masked ExtINT on CPU#%d\n", smp_processor_id()); + } + apic_write_around(APIC_LVT0, value); + + /* + * only the BP should see the LINT1 NMI signal, obviously. + */ + if (!smp_processor_id()) + value = APIC_DM_NMI; + else + value = APIC_DM_NMI | APIC_LVT_MASKED; + if (!APIC_INTEGRATED(ver)) /* 82489DX */ + value |= APIC_LVT_LEVEL_TRIGGER; + apic_write_around(APIC_LVT1, value); + + if (APIC_INTEGRATED(ver)) { /* !82489DX */ + maxlvt = get_maxlvt(); + if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ + apic_write(APIC_ESR, 0); + value = apic_read(APIC_ESR); + printk("ESR value before enabling vector: %08lx\n", value); + + value = ERROR_APIC_VECTOR; // enables sending errors + apic_write_around(APIC_LVTERR, value); + /* + * spec says clear errors after enabling vector. + */ + if (maxlvt > 3) + apic_write(APIC_ESR, 0); + value = apic_read(APIC_ESR); + printk("ESR value after enabling vector: %08lx\n", value); + } else + printk("No ESR for 82489DX.\n"); } void __init init_apic_mappings(void) diff --git a/arch/i386/kernel/pci-irq.c b/arch/i386/kernel/pci-irq.c index d9bd520ac541..58984fd080bc 100644 --- a/arch/i386/kernel/pci-irq.c +++ b/arch/i386/kernel/pci-irq.c @@ -424,9 +424,12 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs); mask &= pcibios_irq_mask; - /* Find the best IRQ to assign */ - newirq = 0; - if (assign) { + /* + * Find the best IRQ to assign: use the one + * reported by the device if possible. + */ + newirq = dev->irq; + if (!newirq && assign) { for (i = 0; i < 16; i++) { if (!(mask & (1 << i))) continue; @@ -436,13 +439,18 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) newirq = i; } } - DBG(" -> newirq=%d", newirq); } + DBG(" -> newirq=%d", newirq); /* Try to get current IRQ */ if (r->get && (irq = r->get(pirq_router_dev, d, pirq))) { DBG(" -> got IRQ %d\n", irq); msg = "Found"; + /* We refuse to override the dev->irq information. Give a warning! */ + if (dev->irq && dev->irq != irq) { + printk("IRQ routing conflict in pirq table! Try 'pci=autoirq'\n"); + return 0; + } } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { DBG(" -> assigning IRQ %d", newirq); if (r->set(pirq_router_dev, d, pirq, newirq)) { @@ -576,19 +584,17 @@ void pcibios_penalize_isa_irq(int irq) void pcibios_enable_irq(struct pci_dev *dev) { - if (!dev->irq) { - u8 pin; - pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); - if (pin && !pcibios_lookup_irq(dev, 1)) { - char *msg; - if (io_apic_assign_pci_irqs) - msg = " Probably buggy MP table."; - else if (pci_probe & PCI_BIOS_IRQ_SCAN) - msg = ""; - else - msg = " Please try using pci=biosirq."; - printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", - 'A' + pin - 1, dev->slot_name, msg); - } + u8 pin; + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) { + char *msg; + if (io_apic_assign_pci_irqs) + msg = " Probably buggy MP table."; + else if (pci_probe & PCI_BIOS_IRQ_SCAN) + msg = ""; + else + msg = " Please try using pci=biosirq."; + printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", + 'A' + pin - 1, dev->slot_name, msg); } } diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index eef85b801d3f..b0bbdb759c85 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -2172,7 +2172,7 @@ int get_cpuinfo(char * buffer) "fpu_exception\t: %s\n" "cpuid level\t: %d\n" "wp\t\t: %s\n" - "flags\t:", + "flags\t\t:", c->fdiv_bug ? "yes" : "no", c->hlt_works_ok ? "no" : "yes", c->f00f_bug ? "yes" : "no", diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index 36d88a6eddc9..f4f284a9adc7 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -13,6 +13,7 @@ * enhanced by Bjoern Brauel and Roman Hodek */ +#include #include #include #include diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index 844c83cbe6e0..52331d2333e1 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -1,4 +1,3 @@ -# $Id: Makefile,v 1.11 1999/10/17 19:55:22 harald Exp $ # # Makefile for MIPS-specific library files.. # diff --git a/arch/mips/lib/memcpy.S b/arch/mips/lib/memcpy.S index 4850b09cea0e..a7f1e43c868a 100644 --- a/arch/mips/lib/memcpy.S +++ b/arch/mips/lib/memcpy.S @@ -3,8 +3,6 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * $Id: memcpy.S,v 1.3 1998/07/10 01:14:49 ralf Exp $ - * * Unified implementation of memcpy, memmove and the __copy_user backend. * For __rmemcpy and memmove an exception is always a kernel bug, therefore * they're not protected. In order to keep the exception fixup routine @@ -410,6 +408,7 @@ LEAF(memmove) sltu t0, v0, a1 # dst + len < src -> non- bnez t0, __memcpy # overlapping, can use memcpy move v0, a0 /* return value */ + beqz a2, r_out END(memmove) LEAF(__rmemcpy) /* a0=dst a1=src a2=len */ diff --git a/arch/mips/lib/memset.S b/arch/mips/lib/memset.S index 32f175756c9f..0340cc2a143a 100644 --- a/arch/mips/lib/memset.S +++ b/arch/mips/lib/memset.S @@ -1,13 +1,9 @@ /* - * include/asm-mips/types.h - * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1998 by Ralf Baechle - * - * $Id: memset.S,v 1.2 1998/04/25 17:01:45 ralf Exp $ */ #include #include diff --git a/arch/mips64/lib/Makefile b/arch/mips64/lib/Makefile index 3e61ae79edeb..b8392d527caf 100644 --- a/arch/mips64/lib/Makefile +++ b/arch/mips64/lib/Makefile @@ -1,4 +1,3 @@ -# $Id: Makefile,v 1.2 1999/11/19 20:35:22 ralf Exp $ # # Makefile for MIPS-specific library files.. # diff --git a/arch/mips64/lib/memcpy.S b/arch/mips64/lib/memcpy.S index eb51e10692e7..494193c787c4 100644 --- a/arch/mips64/lib/memcpy.S +++ b/arch/mips64/lib/memcpy.S @@ -458,6 +458,7 @@ LEAF(memmove) sltu ta0, v0, a1 # dst + len < src -> non- bnez ta0, __memcpy # overlapping, can use memcpy move v0, a0 /* return value */ + beqz a2, r_out END(memmove) LEAF(__rmemcpy) /* a0=dst a1=src a2=len */ diff --git a/arch/mips64/lib/memset.S b/arch/mips64/lib/memset.S index 4a051cebff43..ca5218210af2 100644 --- a/arch/mips64/lib/memset.S +++ b/arch/mips64/lib/memset.S @@ -1,15 +1,16 @@ -/* $Id: memset.S,v 1.3 2000/01/15 23:48:55 ralf Exp $ - * +/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1998, 1999 by Ralf Baechle - * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 1998, 1999, 2000 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. */ #include #include #include +#include +#include #define EX(insn,reg,addr,handler) \ 9: insn reg, addr; \ @@ -34,8 +35,6 @@ * a1: char to fill with * a2: size of area to clear */ -#include -#include .set noreorder .align 5 LEAF(memset) diff --git a/arch/parisc/Makefile b/arch/parisc/Makefile new file mode 100644 index 000000000000..ec27df6b015a --- /dev/null +++ b/arch/parisc/Makefile @@ -0,0 +1,91 @@ +# +# parisc/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# Portions Copyright (C) 1999 The Puffin Group +# +# Modified for PA-RISC Linux by Paul Lahaie, Alex deVries, +# Mike Shaver, Helge Deller and Martin K. Petersen +# + +FINAL_LD=$(CROSS_COMPILE)ld --warn-common --warn-section-align + +CPP=$(CC) -E +OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S +LDFLAGS = +LINKFLAGS =-T $(TOPDIR)/arch/parisc/vmlinux.lds $(LDFLAGS) + +CFLAGS_PIPE := -pipe +CFLAGS_NSR := -fno-strength-reduce +CFLAGS := $(CFLAGS) -D__linux__ $(CFLAGS_PIPE) $(CFLAGS_NSR) + +# These should be on for older toolchains or SOM toolchains that don't +# enable them by default. +CFLAGS += -mno-space-regs -mfast-indirect-calls + +# If we become able to compile for specific platforms, this should be +# conditional on that. +CFLAGS += -mschedule=7200 + +# No fixed-point multiply +CFLAGS += -mdisable-fpregs + +HEAD = arch/parisc/kernel/head.o + +SUBDIRS := $(SUBDIRS) $(addprefix arch/parisc/, tools kernel mm lib hpux) +CORE_FILES := $(addprefix arch/parisc/, kernel/pdc_cons.o kernel/process.o \ + lib/lib.a mm/mm.o kernel/kernel.o hpux/hpux.o) \ + $(CORE_FILES) arch/parisc/kernel/init_task.o +LIBS := `$(CC) -print-libgcc-file-name` $(TOPDIR)/arch/parisc/lib/lib.a $(LIBS) + +ifdef CONFIG_MATH_EMULATION +SUBDIRS := $(SUBDIRS) arch/parisc/math-emu +DRIVERS := $(DRIVERS) arch/parisc/math-emu/math.a +endif + +ifdef CONFIG_KWDB +SUBDIRS := $(SUBDIRS) arch/parisc/kdb +DRIVERS := $(DRIVERS) arch/parisc/kdb/kdb.o + +arch/parisc/kdb: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/parisc/kdb +endif + +arch/parisc/kernel: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/parisc/kernel + +arch/parisc/mm: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/parisc/mm + +palo: vmlinux + export TOPDIR=`pwd`; export CONFIG_STI_CONSOLE=$(CONFIG_STI_CONSOLE); \ + unset STRIP LDFLAGS CPP CPPFLAGS AFLAGS CFLAGS CC LD; cd ../palo && make lifimage + +Image: palo + +Image-clean: + +ramdisk.o: + +zImage: palo + +bzImage: palo + +compressed: zImage + +install: + +archclean: + +archmrproper: + +archdep: diff --git a/arch/parisc/config.in b/arch/parisc/config.in new file mode 100644 index 000000000000..eed80181e1eb --- /dev/null +++ b/arch/parisc/config.in @@ -0,0 +1,208 @@ +# +# For a description of the syntax of this configuration file, +# see the Configure script. +# + +mainmenu_name "Linux Kernel Configuration" + +define_bool CONFIG_PARISC y +define_bool CONFIG_UID16 n + +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu + +mainmenu_option next_comment +comment 'General options' + +# bool 'Symmetric multi-processing support' CONFIG_SMP +define_bool CONFIG_SMP n + +bool 'Kernel Debugger support' CONFIG_KWDB +# define_bool CONFIG_KWDB n + +# bool 'GSC/Gecko bus support' CONFIG_GSC y +define_bool CONFIG_GSC y + +bool 'U2/Uturn I/O MMU' CONFIG_IOMMU_CCIO y +bool 'LASI I/O support' CONFIG_GSC_LASI y + +bool 'PCI bus support' CONFIG_PCI y + +if [ "$CONFIG_PCI" = "y" ]; then + bool 'GSCtoPCI/DINO PCI support' CONFIG_GSC_DINO y + bool 'LBA/Elroy PCI support' CONFIG_PCI_LBA n +fi + +if [ "$CONFIG_PCI_LBA" = "y" ]; then + define_bool CONFIG_IOSAPIC y + define_bool CONFIG_IOMMU_SBA y +fi + +# +# if [ "$CONFIG_PCI_EPIC" = "y" ]; then... +# + +endmenu + +mainmenu_option next_comment +comment 'Loadable module support' +bool 'Enable loadable module support' CONFIG_MODULES +if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS + bool 'Kernel module loader' CONFIG_KMOD +fi +endmenu + +mainmenu_option next_comment +comment 'General setup' + +bool 'Networking support' CONFIG_NET + +bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT +bool 'Sysctl support' CONFIG_SYSCTL +tristate 'Kernel support for SOM binaries' CONFIG_BINFMT_SOM +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA +fi + +endmenu + +##source drivers/parport/Config.in +mainmenu_option next_comment +comment 'Parallel port support' + +tristate 'Parallel port support' CONFIG_PARPORT +if [ "$CONFIG_PARPORT" != "n" ]; then + if [ "$CONFIG_PCI" = "y" ]; then + dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT + if [ "$CONFIG_PARPORT_PC" != "n" ]; then + bool ' Use FIFO/DMA if available' CONFIG_PARPORT_PC_FIFO + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' SuperIO chipset support (EXPERIMENTAL)' CONFIG_PARPORT_PC_SUPERIO + fi + fi + fi + if [ "$CONFIG_GSC_LASI" = "y" ]; then + dep_tristate ' LASI/ASP builtin parallel-port' CONFIG_PARPORT_GSC $CONFIG_PARPORT + else + define_tristate CONFIG_PARPORT_GSC n + fi + + # If exactly one hardware type is selected then parport will optimise away + # support for loading any others. Defeat this if the user is keen. + bool ' Support foreign hardware' CONFIG_PARPORT_OTHER + + bool ' IEEE 1284 transfer modes' CONFIG_PARPORT_1284 +fi +endmenu + + +source drivers/block/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +mainmenu_option next_comment +comment 'SCSI support' + +tristate 'SCSI support' CONFIG_SCSI + +if [ "$CONFIG_SCSI" != "n" ]; then + comment 'SCSI support type (disk, tape, CDrom)' + + dep_tristate 'SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI + if [ "$CONFIG_BLK_DEV_SD" != "n" ]; then + int 'Maximum number of SCSI disks that can be loaded as modules' CONFIG_SD_EXTRA_DEVS 40 + fi + + dep_tristate 'SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI + dep_tristate 'SCSI CDROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI + if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then + bool ' Enable vendor-specific extensions (for SCSI CDROM)' CONFIG_BLK_DEV_SR_VENDOR + int 'Maximum number of CDROM devices that can be loaded as modules' CONFIG_SR_EXTRA_DEVS 2 + fi + dep_tristate 'SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI + + comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' + bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN + bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS + + mainmenu_option next_comment + comment 'SCSI low-level drivers' + if [ "$CONFIG_GSC_LASI" = "y" ]; then + dep_tristate 'Lasi SCSI support' CONFIG_SCSI_LASI $CONFIG_SCSI + dep_tristate 'Zalon SCSI support' CONFIG_SCSI_ZALON $CONFIG_SCSI + fi + if [ "$CONFIG_PCI" = "y" ]; then + dep_tristate 'SYM53C8XX SCSI support' CONFIG_SCSI_SYM53C8XX $CONFIG_SCSI + fi + if [ "$CONFIG_SCSI_ZALON" != "n" -o "$CONFIG_SCSI_SYM53C8XX" != "n" ]; then + int ' default tagged command queue depth' CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS 8 + int ' maximum number of queued commands' CONFIG_SCSI_NCR53C8XX_MAX_TAGS 32 + int ' synchronous transfers frequency in MHz' CONFIG_SCSI_NCR53C8XX_SYNC 20 + bool ' enable profiling' CONFIG_SCSI_NCR53C8XX_PROFILE + bool ' use normal IO' CONFIG_SCSI_NCR53C8XX_IOMAPPED + fi + endmenu +fi +endmenu + +if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' + + bool 'Network device support' CONFIG_NETDEVICES + + if [ "$CONFIG_NETDEVICES" = "y" ]; then + if [ "$CONFIG_GSC_LASI" = "y" ]; then + tristate 'Lasi ethernet' CONFIG_LASI_82596 + fi + source drivers/net/Config.in + fi + endmenu +fi + +source drivers/char/Config.in + +source fs/Config.in + +mainmenu_option next_comment +comment 'Sound Drivers' +tristate 'Sound card support' CONFIG_SOUND +if [ "$CONFIG_SOUND" != "n" ]; then + source drivers/sound/Config.in +fi +endmenu + +if [ "$CONFIG_VT" = "y" ]; then + mainmenu_option next_comment + comment 'Console drivers' + source drivers/video/Config.in + +# bool 'IODC console' CONFIG_IODC_CONSOLE + bool 'STI console' CONFIG_STI_CONSOLE + if [ "$CONFIG_IODC_CONSOLE" = "n" ]; then + if [ "$CONFIG_GSC_PS2" = "y" ]; then + define_bool CONFIG_DUMMY_CONSOLE y + fi + fi + if [ "$CONFIG_STI_CONSOLE" = "y" ]; then + define_bool CONFIG_DUMMY_CONSOLE y + fi + endmenu +fi +# endmenu + +mainmenu_option next_comment +comment 'Kernel hacking' + +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +endmenu + diff --git a/arch/parisc/defconfig b/arch/parisc/defconfig new file mode 100644 index 000000000000..352fa78c04de --- /dev/null +++ b/arch/parisc/defconfig @@ -0,0 +1,363 @@ +# +# Automatically generated by make menuconfig: don't edit +# +CONFIG_PARISC=y +# CONFIG_UID16 is not set + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General options +# +# CONFIG_SMP is not set +# CONFIG_KWDB is not set +CONFIG_GSC=y +CONFIG_IOMMU_CCIO=y +CONFIG_GSC_LASI=y +CONFIG_PCI=y +CONFIG_GSC_DINO=y +CONFIG_PCI_LBA=y +CONFIG_IOSAPIC=y +CONFIG_IOMMU_SBA=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# General setup +# +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_BINFMT_SOM=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set + +# +# Parallel port support +# +CONFIG_PARPORT=y +# CONFIG_PARPORT_PC is not set +CONFIG_PARPORT_GSC=y +# CONFIG_PARPORT_OTHER is not set +# CONFIG_PARPORT_1284 is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT 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 + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# SCSI support +# +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_SD_EXTRA_DEVS=40 +CONFIG_CHR_DEV_ST=y +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_SR_EXTRA_DEVS=2 +CONFIG_CHR_DEV_SG=y +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set + +# +# SCSI low-level drivers +# +CONFIG_SCSI_LASI=y +CONFIG_SCSI_ZALON=y +CONFIG_SCSI_SYM53C8XX=y +CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 +CONFIG_SCSI_NCR53C8XX_SYNC=20 +# CONFIG_SCSI_NCR53C8XX_PROFILE is not set +# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y +CONFIG_LASI_82596=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +# CONFIG_DE4X5 is not set +CONFIG_TULIP=y +# CONFIG_DGRS is not set +# CONFIG_DM9102 is not set +# CONFIG_EEPRO100 is not set +# CONFIG_LNE390 is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_NE3210 is not set +# CONFIG_ES3210 is not set +# CONFIG_RTL8129 is not set +# CONFIG_8139TOO is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_WINBOND_840 is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_GSC_PS2=y +CONFIG_HIL=y +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +CONFIG_SERIAL_GSC=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_PRINTER=y +# CONFIG_LP_CONSOLE is not set +# CONFIG_PPDEV is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +CONFIG_GENRTC=y +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set +# CONFIG_NCPFS_NDS_DOMAINS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_NLS is not set + +# +# Sound Drivers +# +# CONFIG_SOUND is not set + +# +# Console drivers +# + +# +# Frame-buffer support +# +# CONFIG_FB is not set +# CONFIG_STI_CONSOLE is not set + +# +# Kernel hacking +# +CONFIG_MAGIC_SYSRQ=y diff --git a/arch/parisc/hpux/Makefile b/arch/parisc/hpux/Makefile new file mode 100644 index 000000000000..d946959bebc4 --- /dev/null +++ b/arch/parisc/hpux/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +all: hpux.o +O_TARGET = hpux.o +O_OBJS = entry_hpux.o gate.o wrappers.o fs.o ioctl.o sys_hpux.o + +.o.S: $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $*.S -o $*.o + +include $(TOPDIR)/Rules.make diff --git a/arch/parisc/hpux/entry_hpux.S b/arch/parisc/hpux/entry_hpux.S new file mode 100644 index 000000000000..165aeb6aff3e --- /dev/null +++ b/arch/parisc/hpux/entry_hpux.S @@ -0,0 +1,537 @@ +/* ----------------------------------------------------------------------------- + * + * Native PARISC/Linux Project (http://www.puffingroup.com/parisc) + * + * modified by Matthew Wilcox 1999-07-26 + */ + + +#define ASSEMBLY + +#include +#include +#include + + + .text + +#define ENTRY_NAME(_name_) .word _name_ + + .align 4 + .export hpux_call_table +hpux_call_table: + ENTRY_NAME(sys_ni_syscall) /* 0 */ + ENTRY_NAME(sys_exit) + ENTRY_NAME(hpux_fork_wrapper) + ENTRY_NAME(sys_read) + ENTRY_NAME(sys_write) + ENTRY_NAME(sys_open) /* 5 */ + ENTRY_NAME(sys_close) + ENTRY_NAME(hpux_wait) + ENTRY_NAME(sys_creat) + ENTRY_NAME(sys_link) + ENTRY_NAME(sys_unlink) /* 10 */ + ENTRY_NAME(hpux_execv_wrapper) + ENTRY_NAME(sys_chdir) + ENTRY_NAME(sys_time) + ENTRY_NAME(sys_mknod) + ENTRY_NAME(sys_chmod) /* 15 */ + ENTRY_NAME(sys_chown) + ENTRY_NAME(hpux_brk) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_lseek) + ENTRY_NAME(sys_getpid) /* 20 */ + ENTRY_NAME(hpux_mount) + ENTRY_NAME(sys_oldumount) + ENTRY_NAME(sys_setuid) + ENTRY_NAME(sys_getuid) + ENTRY_NAME(sys_stime) /* 25 */ + ENTRY_NAME(hpux_ptrace) + ENTRY_NAME(sys_alarm) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_pause) + ENTRY_NAME(sys_utime) /* 30 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_access) + ENTRY_NAME(hpux_nice) + ENTRY_NAME(sys_ni_syscall) /* 35 */ + ENTRY_NAME(sys_sync) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_newstat) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_newlstat) /* 40 */ + ENTRY_NAME(sys_dup) + ENTRY_NAME(hpux_pipe_wrapper) + ENTRY_NAME(sys_times) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 45 */ + ENTRY_NAME(sys_setgid) + ENTRY_NAME(sys_getgid) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 50 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(hpux_ioctl) + ENTRY_NAME(sys_ni_syscall) /* 55 */ + ENTRY_NAME(sys_symlink) + ENTRY_NAME(hpux_utssys) + ENTRY_NAME(sys_readlink) + ENTRY_NAME(hpux_execve_wrapper) + ENTRY_NAME(sys_umask) /* 60 */ + ENTRY_NAME(sys_chroot) + ENTRY_NAME(sys_fcntl) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 65 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(hpux_sbrk) + ENTRY_NAME(sys_ni_syscall) /* 70 */ + ENTRY_NAME(sys_mmap) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 75 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 80 */ + ENTRY_NAME(sys_getpgid) + ENTRY_NAME(sys_setpgid) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 85 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_dup2) /* 90 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_newfstat) + ENTRY_NAME(sys_select) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 95 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 100 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 105 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 110 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 115 */ + ENTRY_NAME(sys_gettimeofday) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 120 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_fchown) + ENTRY_NAME(sys_fchmod) + ENTRY_NAME(sys_ni_syscall) /* 125 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_rename) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 130 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(hpux_sysconf) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 135 */ + ENTRY_NAME(sys_mkdir) + ENTRY_NAME(sys_rmdir) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 140 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 145 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 150 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 155 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 160 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 165 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 170 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 175 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 180 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 185 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 190 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(hpux_getdomainname) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 195 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_waitpid) /* 200 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 205 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 210 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 215 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 220 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 225 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 230 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 235 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 240 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 245 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 250 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 255 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 260 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 265 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 270 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_fchdir) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_accept) /* 275 */ + ENTRY_NAME(sys_bind) + ENTRY_NAME(sys_connect) + ENTRY_NAME(sys_getpeername) + ENTRY_NAME(sys_getsockname) + ENTRY_NAME(sys_getsockopt) /* 280 */ + ENTRY_NAME(sys_listen) + ENTRY_NAME(sys_recv) + ENTRY_NAME(sys_recvfrom) + ENTRY_NAME(sys_recvmsg) + ENTRY_NAME(sys_send) /* 285 */ + ENTRY_NAME(sys_sendmsg) + ENTRY_NAME(sys_sendto) + ENTRY_NAME(sys_setsockopt) + ENTRY_NAME(sys_shutdown) + ENTRY_NAME(sys_socket) /* 290 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 295 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 300 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 305 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 310 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 315 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 320 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 325 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 330 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_lchown) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 335 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 340 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 345 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 350 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_nanosleep) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 355 */ + ENTRY_NAME(hpux_getdents) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 360 */ + ENTRY_NAME(hpux_fstat64) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 365 */ + ENTRY_NAME(hpux_lstat64) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(hpux_stat64) + ENTRY_NAME(sys_ni_syscall) /* 370 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 375 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 380 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 385 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 390 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 395 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 400 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 405 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 410 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 415 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 420 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 425 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 430 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 435 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 440 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 445 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 450 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 455 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 460 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 465 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 470 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 475 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 480 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 485 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 490 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 495 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 500 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 505 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) /* 510 */ + ENTRY_NAME(sys_ni_syscall) + ENTRY_NAME(sys_ni_syscall) +.end + diff --git a/arch/parisc/hpux/fs.c b/arch/parisc/hpux/fs.c new file mode 100644 index 000000000000..4957a05f8786 --- /dev/null +++ b/arch/parisc/hpux/fs.c @@ -0,0 +1,250 @@ +/* + * linux/arch/parisc/kernel/sys_hpux.c + * + * implements HPUX syscalls. + */ + +#include +#include +#include +#include +#include +#include +#include + +int hpux_execve(struct pt_regs *regs) +{ + int error; + char *filename; + + filename = getname((char *) regs->gr[26]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + + error = do_execve(filename, (char **) regs->gr[25], + (char **)regs->gr[24], regs); + + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); + +out: + return error; +} + +struct hpux_dirent { + long d_off_pad; /* we only have a 32-bit off_t */ + long d_off; + ino_t d_ino; + short d_reclen; + short d_namlen; + char d_name[1]; +}; + +struct getdents_callback { + struct hpux_dirent *current_dir; + struct hpux_dirent *previous; + int count; + int error; +}; + +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) + +static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino) +{ + struct hpux_dirent * dirent; + struct getdents_callback * buf = (struct getdents_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->current_dir; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(reclen, &dirent->d_reclen); + put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; +} + +#undef NAME_OFFSET +#undef ROUND_UP + +int hpux_getdents(unsigned int fd, struct hpux_dirent *dirent, unsigned int count) +{ + struct file * file; + struct dentry * dentry; + struct inode * inode; + struct hpux_dirent * lastdirent; + struct getdents_callback buf; + int error; + + lock_kernel(); + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + dentry = file->f_dentry; + if (!dentry) + goto out_putf; + + inode = dentry->d_inode; + if (!inode) + goto out_putf; + + buf.current_dir = dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out_putf; + + /* + * Get the inode's semaphore to prevent changes + * to the directory while we read it. + */ + down(&inode->i_sem); + error = file->f_op->readdir(file, &buf, filldir); + up(&inode->i_sem); + if (error < 0) + goto out_putf; + error = buf.error; + lastdirent = buf.previous; + if (lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = count - buf.count; + } + +out_putf: + fput(file); +out: + unlock_kernel(); + return error; +} + +int hpux_mount(const char *fs, const char *path, int mflag, + const char *fstype, const char *dataptr, int datalen) +{ + return -ENOSYS; +} + +static int cp_hpux_stat(struct inode * inode, struct hpux_stat64 * statbuf) +{ + struct hpux_stat64 tmp; + unsigned int blocks, indirect; + + memset(&tmp, 0, sizeof(tmp)); + tmp.st_dev = kdev_t_to_nr(inode->i_dev); + tmp.st_ino = inode->i_ino; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlink; + tmp.st_uid = inode->i_uid; + tmp.st_gid = inode->i_gid; + tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); + tmp.st_size = inode->i_size; + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; + +#define D_B 7 +#define I_B (BLOCK_SIZE / sizeof(unsigned short)) + + if (!inode->i_blksize) { + blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE; + if (blocks > D_B) { + indirect = (blocks - D_B + I_B - 1) / I_B; + blocks += indirect; + if (indirect > 1) { + indirect = (indirect - 1 + I_B - 1) / I_B; + blocks += indirect; + if (indirect > 1) + blocks++; + } + } + tmp.st_blocks = (BLOCK_SIZE / 512) * blocks; + tmp.st_blksize = BLOCK_SIZE; + } else { + tmp.st_blocks = inode->i_blocks; + tmp.st_blksize = inode->i_blksize; + } + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} + +/* + * Revalidate the inode. This is required for proper NFS attribute caching. + * Blatently copied wholesale from fs/stat.c + */ +static __inline__ int +do_revalidate(struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + if (inode->i_op && inode->i_op->revalidate) + return inode->i_op->revalidate(dentry); + return 0; +} + +long hpux_stat64(const char *path, struct hpux_stat64 *buf) +{ + struct nameidata nd; + int error; + + lock_kernel(); + error = user_path_walk(path, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_hpux_stat(nd.dentry->d_inode, buf); + path_release(&nd); + } + unlock_kernel(); + return error; +} + +long hpux_fstat64(unsigned int fd, struct hpux_stat64 *statbuf) +{ + struct file * f; + int err = -EBADF; + + lock_kernel(); + f = fget(fd); + if (f) { + struct dentry * dentry = f->f_dentry; + + err = do_revalidate(dentry); + if (!err) + err = cp_hpux_stat(dentry->d_inode, statbuf); + fput(f); + } + unlock_kernel(); + return err; +} + +long hpux_lstat64(char *filename, struct hpux_stat64 *statbuf) +{ + struct nameidata nd; + int error; + + lock_kernel(); + error = user_path_walk_link(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_hpux_stat(nd.dentry->d_inode, statbuf); + path_release(&nd); + } + unlock_kernel(); + return error; +} diff --git a/arch/parisc/hpux/gate.S b/arch/parisc/hpux/gate.S new file mode 100644 index 000000000000..5e0bf3c663ad --- /dev/null +++ b/arch/parisc/hpux/gate.S @@ -0,0 +1,74 @@ +/* ------------------------------------------------------------------------------ + * + * Linux/PARISC Project (http://www.thepuffingroup.com/parisc) + * + * System call entry code Copyright (c) Matthew Wilcox 1999 + * Licensed under the GNU GPL. + * thanks to Philipp Rumpf, Mike Shaver and various others + * sorry about the wall, puffin.. + */ + +#define __ASSEMBLY__ +#include +#include +#include +#include +#define __ASSEMBLY__ +#include /* for STREG/LDREG */ + + .text + + .import hpux_call_table + .import hpux_syscall_exit,code + .export hpux_gateway_page + + .align 4096 +hpux_gateway_page: + nop + mfsp %sr7,%r1 ;! we must set sr3 to the space + mtsp %r1,%sr3 ;! of the user before the gate +#ifdef __LP64__ +#warning NEEDS WORK for 64-bit +#endif + ldw -64(%r30), %r28 ;! 8th argument + ldw -60(%r30), %r19 ;! 7th argument + ldw -56(%r30), %r20 ;! 6th argument + ldw -52(%r30), %r21 ;! 5th argument + gate .+8, %r0 ;! become privileged + mtsp %r0,%sr4 ;! get kernel space into sr4 + mtsp %r0,%sr5 ;! get kernel space into sr5 + mtsp %r0,%sr6 ;! get kernel space into sr6 + mtsp %r0,%sr7 ;! get kernel space into sr7 + mfctl %cr30,%r1 ;! get the kernel task ptr + mtctl %r0,%cr30 ;! zero it (flag) + STREG %r30,TASK_PT_GR30(%r1) ;! preserve userspace sp + STREG %r2,TASK_PT_GR2(%r1) ;! preserve rp + STREG %r27,TASK_PT_GR27(%r1) ;! user dp + STREG %r31,TASK_PT_GR31(%r1) ;! preserve syscall return ptr + + loadgp ;! setup kernel dp + + ldo TASK_SZ_ALGN+64(%r1),%r30 ;! set up kernel stack + + stw %r21, -52(%r30) ;! 5th argument + stw %r20, -56(%r30) ;! 6th argument + stw %r19, -60(%r30) ;! 7th argument + stw %r28, -64(%r30) ;! 8th argument + + ldil L%hpux_call_table, %r21 + ldo R%hpux_call_table(%r21), %r21 + comiclr,>>= __NR_HPUX_syscalls, %r22, %r0 + b,n syscall_nosys + ldwx,s %r22(%r21), %r21 + ldil L%hpux_syscall_exit,%r2 + be 0(%sr7,%r21) + ldo R%hpux_syscall_exit(%r2),%r2 + +syscall_nosys: + ldil L%hpux_syscall_exit,%r1 + be R%hpux_syscall_exit(%sr7,%r1) + ldo -ENOSYS(%r0),%r28 + + .align 4096 + .export end_hpux_gateway_page +end_hpux_gateway_page: diff --git a/arch/parisc/hpux/ioctl.c b/arch/parisc/hpux/ioctl.c new file mode 100644 index 000000000000..9087123e1e6e --- /dev/null +++ b/arch/parisc/hpux/ioctl.c @@ -0,0 +1,63 @@ +/* + * linux/arch/parisc/hpux/ioctl.c + * + * implements some necessary HPUX ioctls. + */ + +/* + * Supported ioctls: + * TCGETA + * TCSETA + * TCSETAW + * TCSETAF + * TCSBRK + * TCXONC + * TCFLSH + * TIOCGWINSZ + * TIOCSWINSZ + * TIOCGPGRP + * TIOCSPGRP + */ + +#include +#include +#include +#include +#include + +int sys_ioctl(unsigned int, unsigned int, unsigned long); + +static int hpux_ioctl_t(int fd, unsigned long cmd, unsigned long arg) +{ + int result = -EOPNOTSUPP; + int nr = _IOC_NR(cmd); + switch (nr) { + case 106: + result = sys_ioctl(fd, TIOCSWINSZ, arg); + break; + case 107: + result = sys_ioctl(fd, TIOCGWINSZ, arg); + break; + } + return result; +} + +int hpux_ioctl(int fd, unsigned long cmd, unsigned long arg) +{ + int result = -EOPNOTSUPP; + int type = _IOC_TYPE(cmd); + switch (type) { + case 'T': + /* Our structures are now compatible with HPUX's */ + result = sys_ioctl(fd, cmd, arg); + break; + case 't': + result = hpux_ioctl_t(fd, cmd, arg); + break; + default: + /* If my mother ever sees this, I hope she disowns me. + * Take this out after NYLWE. */ + result = sys_ioctl(fd, cmd, arg); + } + return result; +} diff --git a/arch/parisc/hpux/sys_hpux.c b/arch/parisc/hpux/sys_hpux.c new file mode 100644 index 000000000000..e0c832e764c3 --- /dev/null +++ b/arch/parisc/hpux/sys_hpux.c @@ -0,0 +1,334 @@ +/* + * linux/arch/parisc/kernel/sys_hpux.c + * + * implements HPUX syscalls. + */ + +#include +#include +#include +#include +#include + +unsigned long sys_brk(unsigned long addr); + +unsigned long hpux_brk(unsigned long addr) +{ + /* Sigh. Looks like HP/UX libc relies on kernel bugs. */ + return sys_brk(addr + PAGE_SIZE); +} + +int hpux_sbrk(void) +{ + return -ENOSYS; +} + +/* Random other syscalls */ + +int hpux_nice(int priority_change) +{ + return -ENOSYS; +} + +int hpux_ptrace(void) +{ + return -ENOSYS; +} + +int hpux_wait(int *stat_loc) +{ + extern int sys_waitpid(int, int *, int); + return sys_waitpid(-1, stat_loc, 0); +} + +#define _SC_CPU_VERSION 10001 +#define _SC_OPEN_MAX 4 +#define CPU_PA_RISC1_1 0x210 + +int hpux_sysconf(int which) +{ + switch (which) { + case _SC_CPU_VERSION: + return CPU_PA_RISC1_1; + case _SC_OPEN_MAX: + return INT_MAX; + default: + return -EINVAL; + } +} + +/*****************************************************************************/ + +#define HPUX_UTSLEN 9 +#define HPUX_SNLEN 15 + +struct hpux_utsname { + char sysname[HPUX_UTSLEN]; + char nodename[HPUX_UTSLEN]; + char release[HPUX_UTSLEN]; + char version[HPUX_UTSLEN]; + char machine[HPUX_UTSLEN]; + char idnumber[HPUX_SNLEN]; +} ; + +struct hpux_ustat { + int32_t f_tfree; /* total free (daddr_t) */ + u_int32_t f_tinode; /* total inodes free (ino_t) */ + char f_fname[6]; /* filsys name */ + char f_fpack[6]; /* filsys pack name */ + u_int32_t f_blksize; /* filsys block size (int) */ +}; + +/* + * HPUX's utssys() call. It's a collection of miscellaneous functions, + * alas, so there's no nice way of splitting them up. + */ + +/* This function is called from hpux_utssys(); HP-UX implements + * ustat() as an option to utssys(). + * + * Now, struct ustat on HP-UX is exactly the same as on Linux, except + * that it contains one addition field on the end, int32_t f_blksize. + * So, we could have written this function to just call the Linux + * sys_ustat(), (defined in linux/fs/super.c), and then just + * added this additional field to the user's structure. But I figure + * if we're gonna be digging through filesystem structures to get + * this, we might as well just do the whole enchilada all in one go. + * + * So, most of this function is almost identical to sys_ustat(). + * I have placed comments at the few lines changed or added, to + * aid in porting forward if and when sys_ustat() is changed from + * its form in kernel 2.2.5. + */ +static int hpux_ustat(dev_t dev, struct hpux_ustat *ubuf) +{ + struct super_block *s; + struct hpux_ustat tmp; /* Changed to hpux_ustat */ + struct statfs sbuf; + int err = -EINVAL; + + lock_kernel(); + s = get_super(to_kdev_t(dev)); + if (s == NULL) + goto out; + err = vfs_statfs(s, &sbuf); + if (err) + goto out; + + memset(&tmp,0,sizeof(struct hpux_ustat)); /* Changed to hpux_ustat */ + + tmp.f_tfree = (int32_t)sbuf.f_bfree; + tmp.f_tinode = (u_int32_t)sbuf.f_ffree; + tmp.f_blksize = (u_int32_t)sbuf.f_bsize; /* Added this line */ + + /* Changed to hpux_ustat: */ + err = copy_to_user(ubuf,&tmp,sizeof(struct hpux_ustat)) ? -EFAULT : 0; +out: + unlock_kernel(); + return err; +} + + +/* This function is called from hpux_utssys(); HP-UX implements + * uname() as an option to utssys(). + * + * The form of this function is pretty much copied from sys_olduname(), + * defined in linux/arch/i386/kernel/sys_i386.c. + */ +/* TODO: Are these put_user calls OK? Should they pass an int? + * (I copied it from sys_i386.c like this.) + */ +static int hpux_uname(struct hpux_utsname *name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct hpux_utsname))) + return -EFAULT; + + down_read(&uts_sem); + + error = __copy_to_user(&name->sysname,&system_utsname.sysname,HPUX_UTSLEN-1); + error |= __put_user(0,name->sysname+HPUX_UTSLEN-1); + error |= __copy_to_user(&name->nodename,&system_utsname.nodename,HPUX_UTSLEN-1); + error |= __put_user(0,name->nodename+HPUX_UTSLEN-1); + error |= __copy_to_user(&name->release,&system_utsname.release,HPUX_UTSLEN-1); + error |= __put_user(0,name->release+HPUX_UTSLEN-1); + error |= __copy_to_user(&name->version,&system_utsname.version,HPUX_UTSLEN-1); + error |= __put_user(0,name->version+HPUX_UTSLEN-1); + error |= __copy_to_user(&name->machine,&system_utsname.machine,HPUX_UTSLEN-1); + error |= __put_user(0,name->machine+HPUX_UTSLEN-1); + + up_read(&uts_sem); + + /* HP-UX utsname has no domainname field. */ + + /* TODO: Implement idnumber!!! */ +#if 0 + error |= __put_user(0,name->idnumber); + error |= __put_user(0,name->idnumber+HPUX_SNLEN-1); +#endif + + error = error ? -EFAULT : 0; + + return error; +} + +int sys_sethostname(char *, int); +int sys_gethostname(char *, int); + +/* Note: HP-UX just uses the old suser() function to check perms + * in this system call. We'll use capable(CAP_SYS_ADMIN). + */ +int hpux_utssys(char *ubuf, int n, int type) +{ + int len; + int error; + switch( type ) { + case 0: + /* uname(): */ + return( hpux_uname( (struct hpux_utsname *)ubuf ) ); + break ; + case 1: + /* Obsolete (used to be umask().) */ + return -EFAULT ; + break ; + case 2: + /* ustat(): */ + return( hpux_ustat((dev_t)n, (struct hpux_ustat *)ubuf) ); + break ; + case 3: + /* setuname(): + * + * On linux (unlike HP-UX), utsname.nodename + * is the same as the hostname. + * + * sys_sethostname() is defined in linux/kernel/sys.c. + */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + /* Unlike Linux, HP-UX returns an error if n==0: */ + if ( n <= 0 ) + return -EINVAL ; + /* Unlike Linux, HP-UX truncates it if n is too big: */ + len = (n <= __NEW_UTS_LEN) ? n : __NEW_UTS_LEN ; + return( sys_sethostname(ubuf, len) ); + break ; + case 4: + /* sethostname(): + * + * sys_sethostname() is defined in linux/kernel/sys.c. + */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + /* Unlike Linux, HP-UX returns an error if n==0: */ + if ( n <= 0 ) + return -EINVAL ; + /* Unlike Linux, HP-UX truncates it if n is too big: */ + len = (n <= __NEW_UTS_LEN) ? n : __NEW_UTS_LEN ; + return( sys_sethostname(ubuf, len) ); + break ; + case 5: + /* gethostname(): + * + * sys_gethostname() is defined in linux/kernel/sys.c. + */ + /* Unlike Linux, HP-UX returns an error if n==0: */ + if ( n <= 0 ) + return -EINVAL ; + return( sys_gethostname(ubuf, n) ); + break ; + case 6: + /* Supposedly called from setuname() in libc. + * TODO: When and why is this called? + * Is it ever even called? + * + * This code should look a lot like sys_sethostname(), + * defined in linux/kernel/sys.c. If that gets updated, + * update this code similarly. + */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + /* Unlike Linux, HP-UX returns an error if n==0: */ + if ( n <= 0 ) + return -EINVAL ; + /* Unlike Linux, HP-UX truncates it if n is too big: */ + len = (n <= __NEW_UTS_LEN) ? n : __NEW_UTS_LEN ; + /**/ + /* TODO: print a warning about using this? */ + down_write(&uts_sem); + error = -EFAULT; + if (!copy_from_user(system_utsname.sysname, ubuf, len)) { + system_utsname.sysname[len] = 0; + error = 0; + } + up_write(&uts_sem); + return error; + break ; + case 7: + /* Sets utsname.release, if you're allowed. + * Undocumented. Used by swinstall to change the + * OS version, during OS updates. Yuck!!! + * + * This code should look a lot like sys_sethostname() + * in linux/kernel/sys.c. If that gets updated, update + * this code similarly. + */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + /* Unlike Linux, HP-UX returns an error if n==0: */ + if ( n <= 0 ) + return -EINVAL ; + /* Unlike Linux, HP-UX truncates it if n is too big: */ + len = (n <= __NEW_UTS_LEN) ? n : __NEW_UTS_LEN ; + /**/ + /* TODO: print a warning about this? */ + down_write(&uts_sem); + error = -EFAULT; + if (!copy_from_user(system_utsname.release, ubuf, len)) { + system_utsname.release[len] = 0; + error = 0; + } + up_write(&uts_sem); + return error; + break ; + default: + /* This system call returns -EFAULT if given an unknown type. + * Why not -EINVAL? I don't know, it's just not what they did. + */ + return -EFAULT ; + } +} + +int hpux_getdomainname(char *name, int len) +{ + int nlen; + int err = -EFAULT; + + down_read(&uts_sem); + + nlen = strlen(system_utsname.domainname) + 1; + + if (nlen < len) + len = nlen; + if(len > __NEW_UTS_LEN) + goto done; + if(copy_to_user(name, system_utsname.domainname, len)) + goto done; + err = 0; +done: + up_read(&uts_sem); + return err; + +} + +int hpux_pipe(int *kstack_fildes) +{ + int error; + + lock_kernel(); + error = do_pipe(kstack_fildes); + unlock_kernel(); + return error; +} diff --git a/arch/parisc/hpux/wrappers.S b/arch/parisc/hpux/wrappers.S new file mode 100644 index 000000000000..c150783d9950 --- /dev/null +++ b/arch/parisc/hpux/wrappers.S @@ -0,0 +1,244 @@ +/*------------------------------------------------------------------------------ + * Native PARISC/Linux Project (http://www.puffingroup.com/parisc) + * + * HP-UX System Call Wrapper routines and System Call Return Path + * + * Copyright (C) 2000 Hewlett-Packard (John Marvin) + * + * 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, 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. + */ + +#ifdef __LP64__ +#warning Must be changed for PA64 +#endif + +#include + + .level 1.1 + .text + +#define __ASSEMBLY__ +#include +#include + + /* These should probably go in a header file somewhere. + * They are duplicated in kernel/wrappers.S + * Possibly we should consider consolidating these + * register save/restore macros. + */ + .macro reg_save regs +#ifdef __LP64__ +#warning NEEDS WORK for 64-bit +#endif + stw %r3, PT_GR3(\regs) + stw %r4, PT_GR4(\regs) + stw %r5, PT_GR5(\regs) + stw %r6, PT_GR6(\regs) + stw %r7, PT_GR7(\regs) + stw %r8, PT_GR8(\regs) + stw %r9, PT_GR9(\regs) + stw %r10,PT_GR10(\regs) + stw %r11,PT_GR11(\regs) + stw %r12,PT_GR12(\regs) + stw %r13,PT_GR13(\regs) + stw %r14,PT_GR14(\regs) + stw %r15,PT_GR15(\regs) + stw %r16,PT_GR16(\regs) + stw %r17,PT_GR17(\regs) + stw %r18,PT_GR18(\regs) + .endm + + .macro reg_restore regs + ldw PT_GR3(\regs), %r3 + ldw PT_GR4(\regs), %r4 + ldw PT_GR5(\regs), %r5 + ldw PT_GR6(\regs), %r6 + ldw PT_GR7(\regs), %r7 + ldw PT_GR8(\regs), %r8 + ldw PT_GR9(\regs), %r9 + ldw PT_GR10(\regs),%r10 + ldw PT_GR11(\regs),%r11 + ldw PT_GR12(\regs),%r12 + ldw PT_GR13(\regs),%r13 + ldw PT_GR14(\regs),%r14 + ldw PT_GR15(\regs),%r15 + ldw PT_GR16(\regs),%r16 + ldw PT_GR17(\regs),%r17 + ldw PT_GR18(\regs),%r18 + .endm + + + .export hpux_fork_wrapper + .import sys_fork + +hpux_fork_wrapper: + ldo TASK_REGS-TASK_SZ_ALGN-64(%r30),%r1 ;! get pt regs + ;! pointer in task + reg_save %r1 + + stw %r2,-20(%r30) + ldo 64(%r30),%r30 + stw %r2,PT_GR19(%r1) ;! save for child + stw %r30,PT_GR20(%r1) ;! save for child + ldil L%child_return,%r3 + ldo R%child_return(%r3),%r3 + stw %r3,PT_GR21(%r1) ;! save for child + + ldw TASK_PT_GR30(%r1),%r25 + copy %r1,%r24 + bl sys_clone,%r2 + ldi SIGCHLD,%r26 + + ldw -84(%r30),%r2 +fork_return: + ldo -64(%r30),%r30 + ldo TASK_REGS-TASK_SZ_ALGN-64(%r30),%r1 ;! get pt regs + + reg_restore %r1 + + /* + * HP-UX wants pid (child gets parent pid, parent gets child pid) + * in r28 and a flag in r29 (r29 == 1 for child, 0 for parent). + * Linux fork returns 0 for child, pid for parent. Since HP-UX + * libc stub throws away parent pid and returns 0 for child, + * we'll just return 0 for parent pid now. Only applications + * that jump directly to the gateway page (not supported) will + * know the difference. We can fix this later if necessary. + */ + + ldo -1024(%r0),%r1 + comb,>>=,n %r28,%r1,fork_exit /* just let the syscall exit handle it */ + or,= %r28,%r0,%r0 + or,tr %r0,%r0,%r29 /* r28 <> 0, we are parent, set r29 to 0 */ + ldo 1(%r0),%r29 /* r28 == 0, we are child, set r29 to 1 */ + +fork_exit: + bv %r0(%r2) + nop + + /* Set the return value for the child */ + +child_return: + ldw TASK_PT_GR19-TASK_SZ_ALGN-128(%r30),%r2 + b fork_return + copy %r0,%r28 + + .export hpux_execve_wrapper + .export hpux_execv_wrapper + .import hpux_execve + +hpux_execv_wrapper: + copy %r0,%r24 /* NULL environment */ + +hpux_execve_wrapper: + + ldo TASK_REGS-TASK_SZ_ALGN-64(%r30),%r1 ;! get pt regs + + /* + * Do we need to save/restore r3-r18 here? + * I don't think so. why would new thread need old + * threads registers? + */ + + /* Store arg0, arg1 and arg2 so that hpux_execve will find them */ + + stw %r26,PT_GR26(%r1) + stw %r25,PT_GR25(%r1) + stw %r24,PT_GR24(%r1) + + stw %r2,-20(%r30) + ldo 64(%r30),%r30 + bl hpux_execve,%r2 + copy %r1,%arg0 + + ldo -64(%r30),%r30 + ldw -20(%r30),%r2 + + /* If exec succeeded we need to load the args */ + + ldo -1024(%r0),%r1 + comb,>>= %r28,%r1,exec_error + copy %r2,%r19 + ldo -TASK_SZ_ALGN-64(%r30),%r1 ;! get task ptr + ldw TASK_PT_GR26(%r1),%r26 + ldw TASK_PT_GR25(%r1),%r25 + ldw TASK_PT_GR24(%r1),%r24 + ldw TASK_PT_GR23(%r1),%r23 + copy %r0,%r2 /* Flag to syscall_exit not to clear args */ + +exec_error: + bv %r0(%r19) + nop + + .export hpux_pipe_wrapper + .import hpux_pipe + + /* HP-UX expects pipefd's returned in r28 & r29 */ + +hpux_pipe_wrapper: + stw %r2,-20(%r30) + ldo 64(%r30),%r30 + bl hpux_pipe,%r2 + ldo -56(%r30),%r26 /* pass local array to hpux_pipe */ + + + ldo -1024(%r0),%r1 + comb,>>= %r28,%r1,pipe_exit /* let syscall exit handle it */ + ldw -84(%r30),%r2 + + /* if success, load fd's from stack array */ + + ldw -56(%r30),%r28 + ldw -52(%r30),%r29 + +pipe_exit: + bv %r0(%r2) + ldo -64(%r30),%r30 + + .export hpux_syscall_exit + .import syscall_exit + +hpux_syscall_exit: + + /* + * + * HP-UX call return conventions: + * + * if error: + * r22 = 1 + * r28 = errno value + * r29 = secondary return value + * else + * r22 = 0 + * r28 = return value + * r29 = secondary return value + * + * For now, we'll just check to see if r28 is < (unsigned long)-1024 + * (to handle addresses > 2 Gb) and if so set r22 to zero. If not, + * we'll complement r28 and set r22 to 1. Wrappers will be + * needed for syscalls that care about the secondary return value. + * The wrapper may also need a way of avoiding the following code, + * but we'll deal with that when it becomes necessary. + */ + + ldo -1024(%r0),%r1 + comb,<< %r28,%r1,no_error + copy %r0,%r22 + subi 0,%r28,%r28 + ldo 1(%r0),%r22 + +no_error: + b syscall_exit + nop diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile new file mode 100644 index 000000000000..03ce8e58b178 --- /dev/null +++ b/arch/parisc/kernel/Makefile @@ -0,0 +1,52 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +all: kernel.o init_task.o pdc_cons.o process.o head.o +O_TARGET = kernel.o +O_OBJS = + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +obj-y += cache.o setup.o traps.o time.o irq.o \ + syscall.o entry.o sys_parisc.o pdc.o ptrace.o hardware.o \ + inventory.o drivers.o semaphore.o pa7300lc.o pci-dma.o \ + signal.o hpmc.o \ + real1.o real2.o led.o parisc_ksyms.o + +export-objs := parisc_ksyms.o + + +obj-$(CONFIG_PCI) += pci.o +obj-$(CONFIG_VT) += keyboard.o +obj-$(CONFIG_PCI_LBA) += lba_pci.o +# I/O SAPIC is also on IA64 platforms. +# The two could be merged into a common source some day. +obj-$(CONFIG_IOSAPIC) += iosapic.o +obj-$(CONFIG_IOMMU_SBA) += sba_iommu.o +# Only use one of them: ccio-rm-dma is for PCX-W systems *only* +# obj-$(CONFIG_IOMMU_CCIO) += ccio-rm-dma.o +obj-$(CONFIG_IOMMU_CCIO) += ccio-dma.o + +.o.S: $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $*.S -o $*.o + +# Translate to Rules.make lists. + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) + +include $(TOPDIR)/Rules.make diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c new file mode 100644 index 000000000000..713a8b39a526 --- /dev/null +++ b/arch/parisc/kernel/cache.c @@ -0,0 +1,253 @@ +/* $Id: cache.c,v 1.4 2000/01/25 00:11:38 prumpf Exp $ + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1999 Helge Deller (07-13-1999) + * Copyright (C) 1999 SuSE GmbH Nuernberg + * Copyright (C) 2000 Philipp Rumpf (prumpf@tux.org) + * + * Cache and TLB management + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +struct pdc_cache_info cache_info; +#ifndef __LP64__ +static struct pdc_btlb_info btlb_info; +#endif + + +void __flush_page_to_ram(unsigned long address) +{ + __flush_dcache_range(address, PAGE_SIZE); + __flush_icache_range(address, PAGE_SIZE); +} + + + +void flush_data_cache(void) +{ + register unsigned long base = cache_info.dc_base; + register unsigned long count = cache_info.dc_count; + register unsigned long loop = cache_info.dc_loop; + register unsigned long stride = cache_info.dc_stride; + register unsigned long addr; + register long i, j; + + for(i=0,addr=base; i>8, + btlb_info.fixed_range_info.num_i, + btlb_info.fixed_range_info.num_d, + btlb_info.fixed_range_info.num_comb, + btlb_info.variable_range_info.num_i, + btlb_info.variable_range_info.num_d, + btlb_info.variable_range_info.num_comb + ); + } +#endif + + return p - buffer; +} + + +void __init +cache_init(void) +{ + if(pdc_cache_info(&cache_info)<0) + panic("cache_init: pdc_cache_info failed"); + +#if 0 + printk("ic_size %lx dc_size %lx it_size %lx pdc_cache_info %d*long pdc_cache_cf %d\n", + cache_info.ic_size, + cache_info.dc_size, + cache_info.it_size, + sizeof (struct pdc_cache_info) / sizeof (long), + sizeof (struct pdc_cache_cf) + ); +#endif +#ifndef __LP64__ + if(pdc_btlb_info(&btlb_info)<0) { + memset(&btlb_info, 0, sizeof btlb_info); + } +#endif +} diff --git a/arch/parisc/kernel/ccio-dma.c b/arch/parisc/kernel/ccio-dma.c new file mode 100644 index 000000000000..2dcd325b4e7f --- /dev/null +++ b/arch/parisc/kernel/ccio-dma.c @@ -0,0 +1,1208 @@ +/* +** ccio-dma.c: +** DMA management routines for first generation cache-coherent machines. +** Program U2/Uturn in "Virtual Mode" and use the I/O MMU. +** +** (c) Copyright 2000 Grant Grundler +** (c) Copyright 2000 Ryan Bradetich +** (c) Copyright 2000 Hewlett-Packard Company +** +** 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. +** +** +** "Real Mode" operation refers to U2/Uturn chip operation. +** U2/Uturn were designed to perform coherency checks w/o using +** the I/O MMU - basically what x86 does. +** +** Philipp Rumpf has a "Real Mode" driver for PCX-W machines at: +** CVSROOT=:pserver:anonymous@198.186.203.37:/cvsroot/linux-parisc +** cvs -z3 co linux/arch/parisc/kernel/dma-rm.c +** +** I've rewritten his code to work under TPG's tree. See ccio-rm-dma.c. +** +** Drawbacks of using Real Mode are: +** o outbound DMA is slower - U2 won't prefetch data (GSC+ XQL signal). +** o Inbound DMA less efficient - U2 can't use DMA_FAST attribute. +** o Ability to do scatter/gather in HW is lost. +** o Doesn't work under PCX-U/U+ machines since they didn't follow +** the coherency design originally worked out. Only PCX-W does. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* for L1_CACHE_BYTES */ +#include +#include +#include + +#include +#include /* for gsc_writeN()... */ + +/* +** Choose "ccio" since that's what HP-UX calls it. +** Make it easier for folks to migrate from one to the other :^) +*/ +#define MODULE_NAME "ccio" + +/* +#define DEBUG_CCIO_RES +#define DEBUG_CCIO_RUN +#define DEBUG_CCIO_INIT +#define DUMP_RESMAP +*/ + +#include +#include /* for proc_runway_root */ + +#ifdef DEBUG_CCIO_INIT +#define DBG_INIT(x...) printk(x) +#else +#define DBG_INIT(x...) +#endif + +#ifdef DEBUG_CCIO_RUN +#define DBG_RUN(x...) printk(x) +#else +#define DBG_RUN(x...) +#endif + +#ifdef DEBUG_CCIO_RES +#define DBG_RES(x...) printk(x) +#else +#define DBG_RES(x...) +#endif + +#define CCIO_INLINE /* inline */ +#define WRITE_U32(value, addr) gsc_writel(value, (u32 *) (addr)) + +#define U2_IOA_RUNWAY 0x580 +#define U2_BC_GSC 0x501 +#define UTURN_IOA_RUNWAY 0x581 +#define UTURN_BC_GSC 0x502 +/* We *can't* support JAVA (T600). Venture there at your own risk. */ + +static void dump_resmap(void); + +static int ccio_driver_callback(struct hp_device *, struct pa_iodc_driver *); + +static struct pa_iodc_driver ccio_drivers_for[] = { + + {HPHW_IOA, U2_IOA_RUNWAY, 0x0, 0xb, 0, 0x10, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "U2 I/O MMU", (void *) ccio_driver_callback}, + + {HPHW_IOA, UTURN_IOA_RUNWAY, 0x0, 0xb, 0, 0x10, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "Uturn I/O MMU", (void *) ccio_driver_callback}, + +/* +** FIXME: The following claims the GSC bus port, not the IOA. +** And there are two busses below a single I/O TLB. +** +** These should go away once we have a real PA bus walk. +** Firmware wants to tell the PA bus walk code about the GSC ports +** since they are not "architected" PA I/O devices. Ie a PA bus walk +** wouldn't discover them. But the PA bus walk code could check +** the "fixed module table" to add such devices to an I/O Tree +** and proceed with the recursive, depth first bus walk. +*/ + {HPHW_BCPORT, U2_BC_GSC, 0x0, 0xc, 0, 0x10, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "U2 GSC+ BC", (void *) ccio_driver_callback}, + + {HPHW_BCPORT, UTURN_BC_GSC, 0x0, 0xc, 0, 0x10, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "Uturn GSC+ BC", (void *) ccio_driver_callback}, + + {0,0,0,0,0,0, + 0, + (char *) NULL, (char *) NULL, (void *) NULL } +}; + + +#define IS_U2(id) ( \ + (((id)->hw_type == HPHW_IOA) && ((id)->hversion == U2_IOA_RUNWAY)) || \ + (((id)->hw_type == HPHW_BCPORT) && ((id)->hversion == U2_BC_GSC)) \ +) + +#define IS_UTURN(id) ( \ + (((id)->hw_type == HPHW_IOA) && ((id)->hversion == UTURN_IOA_RUNWAY)) || \ + (((id)->hw_type == HPHW_BCPORT) && ((id)->hversion == UTURN_BC_GSC)) \ +) + + +#define IOA_NORMAL_MODE 0x00020080 /* IO_CONTROL to turn on CCIO */ +#define CMD_TLB_DIRECT_WRITE 35 /* IO_COMMAND for I/O TLB Writes */ +#define CMD_TLB_PURGE 33 /* IO_COMMAND to Purge I/O TLB entry */ + +struct ioa_registers { + /* Runway Supervisory Set */ + volatile int32_t unused1[12]; + volatile uint32_t io_command; /* Offset 12 */ + volatile uint32_t io_status; /* Offset 13 */ + volatile uint32_t io_control; /* Offset 14 */ + volatile int32_t unused2[1]; + + /* Runway Auxiliary Register Set */ + volatile uint32_t io_err_resp; /* Offset 0 */ + volatile uint32_t io_err_info; /* Offset 1 */ + volatile uint32_t io_err_req; /* Offset 2 */ + volatile uint32_t io_err_resp_hi; /* Offset 3 */ + volatile uint32_t io_tlb_entry_m; /* Offset 4 */ + volatile uint32_t io_tlb_entry_l; /* Offset 5 */ + volatile uint32_t unused3[1]; + volatile uint32_t io_pdir_base; /* Offset 7 */ + volatile uint32_t io_io_low_hv; /* Offset 8 */ + volatile uint32_t io_io_high_hv; /* Offset 9 */ + volatile uint32_t unused4[1]; + volatile uint32_t io_chain_id_mask; /* Offset 11 */ + volatile uint32_t unused5[2]; + volatile uint32_t io_io_low; /* Offset 14 */ + volatile uint32_t io_io_high; /* Offset 15 */ +}; + + +struct ccio_device { + struct ccio_device *next; /* list of LBA's in system */ + struct hp_device *iodc; /* data about dev from firmware */ + spinlock_t ccio_lock; + + struct ioa_registers *ccio_hpa; /* base address */ + u64 *pdir_base; /* physical base address */ + char *res_map; /* resource map, bit == pdir entry */ + + int res_hint; /* next available IOVP - circular search */ + int res_size; /* size of resource map in bytes */ + int chainid_shift; /* specify bit location of chain_id */ + int flags; /* state/functionality enabled */ +#ifdef DELAYED_RESOURCE_CNT + dma_addr_t res_delay[DELAYED_RESOURCE_CNT]; +#endif + + /* STUFF We don't need in performance path */ + int pdir_size; /* in bytes, determined by IOV Space size */ + int hw_rev; /* HW revision of chip */ +}; + + +/* Ratio of Host MEM to IOV Space size */ +static unsigned long ccio_mem_ratio = 4; +static struct ccio_device *ccio_list = NULL; + +static int ccio_proc_info(char *buffer, char **start, off_t offset, int length); +static unsigned long ccio_used_bytes = 0; +static unsigned long ccio_used_pages = 0; +static int ccio_cujo_bug = 0; + +static unsigned long ccio_alloc_size = 0; +static unsigned long ccio_free_size = 0; + +/************************************************************** +* +* I/O Pdir Resource Management +* +* Bits set in the resource map are in use. +* Each bit can represent a number of pages. +* LSbs represent lower addresses (IOVA's). +* +* This was was copied from sba_iommu.c. Don't try to unify +* the two resource managers unless a way to have different +* allocation policies is also adjusted. We'd like to avoid +* I/O TLB thrashing by having resource allocation policy +* match the I/O TLB replacement policy. +* +***************************************************************/ +#define PAGES_PER_RANGE 1 /* could increase this to 4 or 8 if needed */ +#define IOVP_SIZE PAGE_SIZE +#define IOVP_SHIFT PAGE_SHIFT +#define IOVP_MASK PAGE_MASK + +/* Convert from IOVP to IOVA and vice versa. */ +#define CCIO_IOVA(iovp,offset) ((iovp) | (offset)) +#define CCIO_IOVP(iova) ((iova) & ~(IOVP_SIZE-1) ) + +#define PDIR_INDEX(iovp) ((iovp)>>IOVP_SHIFT) +#define MKIOVP(pdir_idx) ((long)(pdir_idx) << IOVP_SHIFT) +#define MKIOVA(iovp,offset) (dma_addr_t)((long)iovp | (long)offset) + +/* CUJO20 KLUDGE start */ +#define CUJO_20_BITMASK 0x0ffff000 /* upper nibble is a don't care */ +#define CUJO_20_STEP 0x10000000 /* inc upper nibble */ +#define CUJO_20_BADPAGE1 0x01003000 /* pages that hpmc on raven U+ */ +#define CUJO_20_BADPAGE2 0x01607000 /* pages that hpmc on firehawk U+ */ +#define CUJO_20_BADHVERS 0x6821 /* low nibble 1 is cujo rev 2.0 */ +#define CUJO_RAVEN_LOC 0xf1000000UL /* cujo location on raven U+ */ +#define CUJO_FIREHAWK_LOC 0xf1604000UL /* cujo location on firehawk U+ */ +/* CUJO20 KLUDGE end */ + +/* +** Don't worry about the 150% average search length on a miss. +** If the search wraps around, and passes the res_hint, it will +** cause the kernel to panic anyhow. +*/ + +/* ioa->res_hint = idx + (size >> 3); \ */ + +#define CCIO_SEARCH_LOOP(ioa, idx, mask, size) \ + for(; res_ptr < res_end; ++res_ptr) \ + { \ + if(0 == ((*res_ptr) & mask)) { \ + *res_ptr |= mask; \ + idx = (int)((unsigned long)res_ptr - (unsigned long)ioa->res_map); \ + ioa->res_hint = 0;\ + goto resource_found; \ + } \ + } + +#define CCIO_FIND_FREE_MAPPING(ioa, idx, mask, size) { \ + u##size *res_ptr = (u##size *)&((ioa)->res_map[ioa->res_hint & ~((size >> 3) - 1)]); \ + u##size *res_end = (u##size *)&(ioa)->res_map[ioa->res_size]; \ + CCIO_SEARCH_LOOP(ioa, idx, mask, size); \ + res_ptr = (u##size *)&(ioa)->res_map[0]; \ + CCIO_SEARCH_LOOP(ioa, idx, mask, size); \ +} + +/* +** Find available bit in this ioa's resource map. +** Use a "circular" search: +** o Most IOVA's are "temporary" - avg search time should be small. +** o keep a history of what happened for debugging +** o KISS. +** +** Perf optimizations: +** o search for log2(size) bits at a time. +** o search for available resource bits using byte/word/whatever. +** o use different search for "large" (eg > 4 pages) or "very large" +** (eg > 16 pages) mappings. +*/ +static int +ccio_alloc_range(struct ccio_device *ioa, size_t size) +{ + int res_idx; + unsigned long mask, flags; + unsigned int pages_needed = size >> PAGE_SHIFT; + + ASSERT(pages_needed); + ASSERT((pages_needed * IOVP_SIZE) < DMA_CHUNK_SIZE); + ASSERT(pages_needed < (BITS_PER_LONG - IOVP_SHIFT)); + + mask = (unsigned long) -1L; + mask >>= BITS_PER_LONG - pages_needed; + + DBG_RES(__FUNCTION__ " size: %d pages_needed %d pages_mask 0x%08lx\n", + size, pages_needed, mask); + + spin_lock_irqsave(&ioa->ccio_lock, flags); + + /* + ** "seek and ye shall find"...praying never hurts either... + ** ggg sacrafices another 710 to the computer gods. + */ + + if(pages_needed <= 8) { + CCIO_FIND_FREE_MAPPING(ioa, res_idx, mask, 8); + } else if(pages_needed <= 16) { + CCIO_FIND_FREE_MAPPING(ioa, res_idx, mask, 16); + } else if(pages_needed <= 32) { + CCIO_FIND_FREE_MAPPING(ioa, res_idx, mask, 32); +#ifdef __LP64__ + } else if(pages_needed <= 64) { + CCIO_FIND_FREE_MAPPING(ioa, res_idx, mask, 64) +#endif + } else { + panic(__FILE__ ":" __FUNCTION__ "() Too many pages to map.\n"); + } + +#ifdef DUMP_RESMAP + dump_resmap(); +#endif + panic(__FILE__ ":" __FUNCTION__ "() I/O MMU is out of mapping resources\n"); + +resource_found: + + DBG_RES(__FUNCTION__ " res_idx %d mask 0x%08lx res_hint: %d\n", + res_idx, mask, ioa->res_hint); + + ccio_used_pages += pages_needed; + ccio_used_bytes += ((pages_needed >> 3) ? (pages_needed >> 3) : 1); + + spin_unlock_irqrestore(&ioa->ccio_lock, flags); + +#ifdef DUMP_RESMAP + dump_resmap(); +#endif + + /* + ** return the bit address (convert from byte to bit). + */ + return (res_idx << 3); +} + + +#define CCIO_FREE_MAPPINGS(ioa, idx, mask, size) \ + u##size *res_ptr = (u##size *)&((ioa)->res_map[idx + (((size >> 3) - 1) & ~((size >> 3) - 1))]); \ + ASSERT((*res_ptr & mask) == mask); \ + *res_ptr &= ~mask; + +/* +** clear bits in the ioa's resource map +*/ +static void +ccio_free_range(struct ccio_device *ioa, dma_addr_t iova, size_t size) +{ + unsigned long mask, flags; + unsigned long iovp = CCIO_IOVP(iova); + unsigned int res_idx = PDIR_INDEX(iovp)>>3; + unsigned int pages_mapped = (size >> IOVP_SHIFT) + !!(size & ~IOVP_MASK); + + ASSERT(pages_needed); + ASSERT((pages_needed * IOVP_SIZE) < DMA_CHUNK_SIZE); + ASSERT(pages_needed < (BITS_PER_LONG - IOVP_SHIFT)); + + mask = (unsigned long) -1L; + mask >>= BITS_PER_LONG - pages_mapped; + + DBG_RES(__FUNCTION__ " res_idx: %d size: %d pages_mapped %d mask 0x%08lx\n", + res_idx, size, pages_mapped, mask); + + spin_lock_irqsave(&ioa->ccio_lock, flags); + + if(pages_mapped <= 8) { + CCIO_FREE_MAPPINGS(ioa, res_idx, mask, 8); + } else if(pages_mapped <= 16) { + CCIO_FREE_MAPPINGS(ioa, res_idx, mask, 16); + } else if(pages_mapped <= 32) { + CCIO_FREE_MAPPINGS(ioa, res_idx, mask, 32); +#ifdef __LP64__ + } else if(pages_mapped <= 64) { + CCIO_FREE_MAPPINGS(ioa, res_idx, mask, 64); +#endif + } else { + panic(__FILE__ ":" __FUNCTION__ "() Too many pages to unmap.\n"); + } + + ccio_used_pages -= (pages_mapped ? pages_mapped : 1); + ccio_used_bytes -= ((pages_mapped >> 3) ? (pages_mapped >> 3) : 1); + + spin_unlock_irqrestore(&ioa->ccio_lock, flags); + +#ifdef DUMP_RESMAP + dump_resmap(); +#endif +} + + +/**************************************************************** +** +** CCIO dma_ops support routines +** +*****************************************************************/ + +typedef unsigned long space_t; +#define KERNEL_SPACE 0 + + +/* +** DMA "Page Type" and Hints +** o if SAFE_DMA isn't set, mapping is for FAST_DMA. SAFE_DMA should be +** set for subcacheline DMA transfers since we don't want to damage the +** other part of a cacheline. +** o SAFE_DMA must be set for "memory" allocated via pci_alloc_consistent(). +** This bit tells U2 to do R/M/W for partial cachelines. "Streaming" +** data can avoid this if the mapping covers full cache lines. +** o STOP_MOST is needed for atomicity across cachelines. +** Apperently only "some EISA devices" need this. +** Using CONFIG_ISA is hack. Only the IOA with EISA under it needs +** to use this hint iff the EISA devices needs this feature. +** According to the U2 ERS, STOP_MOST enabled pages hurt performance. +** o PREFETCH should *not* be set for cases like Multiple PCI devices +** behind GSCtoPCI (dino) bus converter. Only one cacheline per GSC +** device can be fetched and multiply DMA streams will thrash the +** prefetch buffer and burn memory bandwidth. See 6.7.3 "Prefetch Rules +** and Invalidation of Prefetch Entries". +** +** FIXME: the default hints need to be per GSC device - not global. +** +** HP-UX dorks: linux device driver programming model is totally different +** than HP-UX's. HP-UX always sets HINT_PREFETCH since it's drivers +** do special things to work on non-coherent platforms...linux has to +** be much more careful with this. +*/ +#define IOPDIR_VALID 0x01UL +#define HINT_SAFE_DMA 0x02UL /* used for pci_alloc_consistent() pages */ +#ifdef CONFIG_ISA /* EISA support really */ +#define HINT_STOP_MOST 0x04UL /* LSL support */ +#else +#define HINT_STOP_MOST 0x00UL /* only needed for "some EISA devices" */ +#endif +#define HINT_UDPATE_ENB 0x08UL /* not used/supported by U2 */ +#define HINT_PREFETCH 0x10UL /* for outbound pages which are not SAFE */ + + +/* +** Use direction (ie PCI_DMA_TODEVICE) to pick hint. +** ccio_alloc_consistent() depends on this to get SAFE_DMA +** when it passes in BIDIRECTIONAL flag. +*/ +static u32 hint_lookup[] = { + [PCI_DMA_BIDIRECTIONAL] HINT_STOP_MOST | HINT_SAFE_DMA | IOPDIR_VALID, + [PCI_DMA_TODEVICE] HINT_STOP_MOST | HINT_PREFETCH | IOPDIR_VALID, + [PCI_DMA_FROMDEVICE] HINT_STOP_MOST | IOPDIR_VALID, + [PCI_DMA_NONE] 0, /* not valid */ +}; + +/* +** Initialize an I/O Pdir entry +** +** Given a virtual address (vba, arg2) and space id, (sid, arg1), +** load the I/O PDIR entry pointed to by pdir_ptr (arg0). Each IO Pdir +** entry consists of 8 bytes as shown below (MSB == bit 0): +** +** +** WORD 0: +** +------+----------------+-----------------------------------------------+ +** | Phys | Virtual Index | Phys | +** | 0:3 | 0:11 | 4:19 | +** |4 bits| 12 bits | 16 bits | +** +------+----------------+-----------------------------------------------+ +** WORD 1: +** +-----------------------+-----------------------------------------------+ +** | Phys | Rsvd | Prefetch |Update |Rsvd |Lock |Safe |Valid | +** | 20:39 | | Enable |Enable | |Enable|DMA | | +** | 20 bits | 5 bits | 1 bit |1 bit |2 bits|1 bit |1 bit |1 bit | +** +-----------------------+-----------------------------------------------+ +** +** The virtual index field is filled with the results of the LCI +** (Load Coherence Index) instruction. The 8 bits used for the virtual +** index are bits 12:19 of the value returned by LCI. +*/ + +void CCIO_INLINE +ccio_io_pdir_entry(u64 *pdir_ptr, space_t sid, void * vba, unsigned long hints) +{ + register unsigned long pa = (volatile unsigned long) vba; + register unsigned long ci; /* coherent index */ + + /* We currently only support kernel addresses */ + ASSERT(sid == 0); + ASSERT(((unsigned long) vba & 0xf0000000UL) == 0xc0000000UL); + + mtsp(sid,1); + + /* + ** WORD 1 - low order word + ** "hints" parm includes the VALID bit! + ** "dep" clobbers the physical address offset bits as well. + */ + pa = virt_to_phys(vba); + asm volatile("depw %1,31,12,%0" : "+r" (pa) : "r" (hints)); + ((u32 *)pdir_ptr)[1] = (u32) pa; + + /* + ** WORD 0 - high order word + */ + +#ifdef __LP64__ + /* + ** get bits 12:15 of physical address + ** shift bits 16:31 of physical address + ** and deposit them + */ + asm volatile ("extrd,u %1,15,4,%0" : "=r" (ci) : "r" (pa)); + asm volatile ("extrd,u %1,31,16,%0" : "+r" (ci) : "r" (ci)); + asm volatile ("depd %1,35,4,%0" : "+r" (pa) : "r" (ci)); +#else + pa = 0; +#endif + /* + ** get CPU coherency index bits + ** Grab virtual index [0:11] + ** Deposit virt_idx bits into I/O PDIR word + */ + asm volatile ("lci 0(%%sr1, %1), %0" : "=r" (ci) : "r" (vba)); + asm volatile ("extru %1,19,12,%0" : "+r" (ci) : "r" (ci)); + asm volatile ("depw %1,15,12,%0" : "+r" (pa) : "r" (ci)); + + ((u32 *)pdir_ptr)[0] = (u32) pa; + + + /* FIXME: PCX_W platforms don't need FDC/SYNC. (eg C360) + ** PCX-U/U+ do. (eg C200/C240) + ** PCX-T'? Don't know. (eg C110 or similar K-class) + ** + ** See PDC_MODEL/option 0/SW_CAP word for "Non-coherent IO-PDIR bit". + ** Hopefully we can patch (NOP) these out at boot time somehow. + ** + ** "Since PCX-U employs an offset hash that is incompatible with + ** the real mode coherence index generation of U2, the PDIR entry + ** must be flushed to memory to retain coherence." + */ + asm volatile("fdc 0(%0)" : : "r" (pdir_ptr)); + asm volatile("sync"); +} + + +/* +** Remove stale entries from the I/O TLB. +** Need to do this whenever an entry in the PDIR is marked invalid. +*/ +static CCIO_INLINE void +ccio_clear_io_tlb( struct ccio_device *d, dma_addr_t iovp, size_t byte_cnt) +{ + u32 chain_size = 1 << d->chainid_shift; + + iovp &= ~(IOVP_SIZE-1); /* clear offset bits, just want pagenum */ + byte_cnt += chain_size; + + while (byte_cnt > chain_size) { + WRITE_U32(CMD_TLB_PURGE | iovp, &d->ccio_hpa->io_command); + iovp += chain_size; + byte_cnt -= chain_size; + } +} + + +/*********************************************************** + * + * Mark the I/O Pdir entries invalid and blow away the + * corresponding I/O TLB entries. + * + * FIXME: at some threshhold it might be "cheaper" to just blow + * away the entire I/O TLB instead of individual entries. + * + * FIXME: Uturn has 256 TLB entries. We don't need to purge every + * PDIR entry - just once for each possible TLB entry. + * (We do need to maker I/O PDIR entries invalid regardless). + ***********************************************************/ +static CCIO_INLINE void +ccio_mark_invalid(struct ccio_device *d, dma_addr_t iova, size_t byte_cnt) +{ + u32 iovp = (u32) CCIO_IOVP(iova); + size_t saved_byte_cnt; + + /* round up to nearest page size */ + saved_byte_cnt = byte_cnt = (byte_cnt + IOVP_SIZE - 1) & IOVP_MASK; + + while (byte_cnt > 0) { + /* invalidate one page at a time */ + unsigned int idx = PDIR_INDEX(iovp); + char *pdir_ptr = (char *) &(d->pdir_base[idx]); + + ASSERT( idx < (d->pdir_size/sizeof(u64))); + + pdir_ptr[7] = 0; /* clear only VALID bit */ + + /* + ** FIXME: PCX_W platforms don't need FDC/SYNC. (eg C360) + ** PCX-U/U+ do. (eg C200/C240) + ** See PDC_MODEL/option 0/SW_CAP for "Non-coherent IO-PDIR bit". + ** + ** Hopefully someone figures out how to patch (NOP) the + ** FDC/SYNC out at boot time. + */ + asm volatile("fdc 0(%0)" : : "r" (pdir_ptr[7])); + + iovp += IOVP_SIZE; + byte_cnt -= IOVP_SIZE; + } + + asm volatile("sync"); + ccio_clear_io_tlb(d, CCIO_IOVP(iova), saved_byte_cnt); +} + + +/**************************************************************** +** +** CCIO dma_ops +** +*****************************************************************/ + +void __init ccio_init(void) +{ + register_driver(ccio_drivers_for); +} + + +static int ccio_dma_supported( struct pci_dev *dev, dma_addr_t mask) +{ + if (dev == NULL) { + printk(MODULE_NAME ": EISA/ISA/et al not supported\n"); + BUG(); + return(0); + } + + dev->dma_mask = mask; /* save it */ + + /* only support 32-bit devices (ie PCI/GSC) */ + return((int) (mask >= 0xffffffffUL)); +} + +/* +** Dump a hex representation of the resource map. +*/ + +#ifdef DUMP_RESMAP +static +void dump_resmap() +{ + struct ccio_device *ioa = ccio_list; + unsigned long *res_ptr = (unsigned long *)ioa->res_map; + unsigned long i = 0; + + printk("res_map: "); + for(; i < (ioa->res_size / sizeof(unsigned long)); ++i, ++res_ptr) + printk("%08lx ", *res_ptr); + + printk("\n"); +} +#endif + +/* +** map_single returns a fully formed IOVA +*/ +static dma_addr_t ccio_map_single(struct pci_dev *dev, void *addr, size_t size, int direction) +{ + struct ccio_device *ioa = ccio_list; /* FIXME : see Multi-IOC below */ + dma_addr_t iovp; + dma_addr_t offset; + u64 *pdir_start; + unsigned long hint = hint_lookup[direction]; + int idx; + + ASSERT(size > 0); + + /* save offset bits */ + offset = ((dma_addr_t) addr) & ~IOVP_MASK; + + /* round up to nearest IOVP_SIZE */ + size = (size + offset + IOVP_SIZE - 1) & IOVP_MASK; + + idx = ccio_alloc_range(ioa, size); + iovp = (dma_addr_t) MKIOVP(idx); + + DBG_RUN(__FUNCTION__ " 0x%p -> 0x%lx", addr, (long) iovp | offset); + + pdir_start = &(ioa->pdir_base[idx]); + + /* If not cacheline aligned, force SAFE_DMA on the whole mess */ + if ((size % L1_CACHE_BYTES) || ((unsigned long) addr % L1_CACHE_BYTES)) + hint |= HINT_SAFE_DMA; + + /* round up to nearest IOVP_SIZE */ + size = (size + IOVP_SIZE - 1) & IOVP_MASK; + + while (size > 0) { + + ccio_io_pdir_entry(pdir_start, KERNEL_SPACE, addr, hint); + + DBG_RUN(" pdir %p %08x%08x\n", + pdir_start, + (u32) (((u32 *) pdir_start)[0]), + (u32) (((u32 *) pdir_start)[1]) + ); + addr += IOVP_SIZE; + size -= IOVP_SIZE; + pdir_start++; + } + /* form complete address */ + return CCIO_IOVA(iovp, offset); +} + + +static void ccio_unmap_single(struct pci_dev *dev, dma_addr_t iova, size_t size, int direction) +{ +#ifdef FIXME +/* Multi-IOC (ie N-class) : need to lookup IOC from dev +** o If we can't know about lba PCI data structs, that eliminates ->sysdata. +** o walking up pcidev->parent dead ends at elroy too +** o leaves hashing dev->bus->number into some lookup. +** (may only work for N-class) +*/ + struct ccio_device *ioa = dev->sysdata +#else + struct ccio_device *ioa = ccio_list; +#endif + dma_addr_t offset; + + offset = iova & ~IOVP_MASK; + + /* round up to nearest IOVP_SIZE */ + size = (size + offset + IOVP_SIZE - 1) & IOVP_MASK; + + /* Mask off offset */ + iova &= IOVP_MASK; + + DBG_RUN(__FUNCTION__ " iovp 0x%lx\n", (long) iova); + +#ifdef DELAYED_RESOURCE_CNT + if (ioa->saved_cnt < DELAYED_RESOURCE_CNT) { + ioa->saved_iova[ioa->saved_cnt] = iova; + ioa->saved_size[ioa->saved_cnt] = size; + ccio_saved_cnt++; + } else { + do { +#endif + ccio_mark_invalid(ioa, iova, size); + ccio_free_range(ioa, iova, size); + +#ifdef DELAYED_RESOURCE_CNT + d->saved_cnt--; + iova = ioa->saved_iova[ioa->saved_cnt]; + size = ioa->saved_size[ioa->saved_cnt]; + } while (ioa->saved_cnt) + } +#endif +} + + +static void * ccio_alloc_consistent (struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) +{ + void *ret; + unsigned long flags; + struct ccio_device *ioa = ccio_list; + + DBG_RUN(__FUNCTION__ " size 0x%x\n", size); + +#if 0 +/* GRANT Need to establish hierarchy for non-PCI devs as well +** and then provide matching gsc_map_xxx() functions for them as well. +*/ + if (!hwdev) { + /* only support PCI */ + *dma_handle = 0; + return 0; + } +#endif + spin_lock_irqsave(&ioa->ccio_lock, flags); + ccio_alloc_size += get_order(size); + spin_unlock_irqrestore(&ioa->ccio_lock, flags); + + ret = (void *) __get_free_pages(GFP_ATOMIC, get_order(size)); + + if (ret) { + memset(ret, 0, size); + *dma_handle = ccio_map_single(hwdev, ret, size, PCI_DMA_BIDIRECTIONAL); + } + DBG_RUN(__FUNCTION__ " ret %p\n", ret); + + return ret; +} + + +static void ccio_free_consistent (struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) +{ + unsigned long flags; + struct ccio_device *ioa = ccio_list; + + spin_lock_irqsave(&ioa->ccio_lock, flags); + ccio_free_size += get_order(size); + spin_unlock_irqrestore(&ioa->ccio_lock, flags); + + ccio_unmap_single(hwdev, dma_handle, size, 0); + free_pages((unsigned long) vaddr, get_order(size)); +} + + +static int ccio_map_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction) +{ + int tmp = nents; + + DBG_RUN(KERN_WARNING __FUNCTION__ " START\n"); + + /* KISS: map each buffer seperately. */ + while (nents) { + sg_dma_address(sglist) = ccio_map_single(dev, sglist->address, sglist->length, direction); + sg_dma_len(sglist) = sglist->length; + nents--; + sglist++; + } + + DBG_RUN(KERN_WARNING __FUNCTION__ " DONE\n"); + return tmp; +} + + +static void ccio_unmap_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction) +{ + DBG_RUN(KERN_WARNING __FUNCTION__ " : unmapping %d entries\n", nents); + while (nents) { + ccio_unmap_single(dev, sg_dma_address(sglist), sg_dma_len(sglist), direction); + nents--; + sglist++; + } + return; +} + + +static struct pci_dma_ops ccio_ops = { + ccio_dma_supported, + ccio_alloc_consistent, + ccio_free_consistent, + ccio_map_single, + ccio_unmap_single, + ccio_map_sg, + ccio_unmap_sg, + NULL, /* dma_sync_single : NOP for U2/Uturn */ + NULL, /* dma_sync_sg : ditto */ +}; + +#if 0 +/* GRANT - is this needed for U2 or not? */ + +/* +** Get the size of the I/O TLB for this I/O MMU. +** +** If spa_shift is non-zero (ie probably U2), +** then calculate the I/O TLB size using spa_shift. +** +** Otherwise we are supposed to get the IODC entry point ENTRY TLB +** and execute it. However, both U2 and Uturn firmware supplies spa_shift. +** I think only Java (K/D/R-class too?) systems don't do this. +*/ +static int +ccio_get_iotlb_size(struct hp_device *d) +{ + if(d->spa_shift == 0) { + panic(__FUNCTION__ ": Can't determine I/O TLB size.\n"); + } + return(1 << d->spa_shift); +} +#else + +/* Uturn supports 256 TLB entries */ +#define CCIO_CHAINID_SHIFT 8 +#define CCIO_CHAINID_MASK 0xff + +#endif /* 0 */ + + +/* +** Figure out how big the I/O PDIR should be and alloc it. +** Also sets variables which depend on pdir size. +*/ +static void +ccio_alloc_pdir(struct ccio_device *ioa) +{ + extern unsigned long mem_max; /* arch.../setup.c */ + + u32 iova_space_size = 0; + void * pdir_base; + int pdir_size, iov_order; + + /* + ** Determine IOVA Space size from memory size. + ** Using "mem_max" is a kluge. + ** + ** Ideally, PCI drivers would register the maximum number + ** of DMA they can have outstanding for each device they + ** own. Next best thing would be to guess how much DMA + ** can be outstanding based on PCI Class/sub-class. Both + ** methods still require some "extra" to support PCI + ** Hot-Plug/Removal of PCI cards. (aka PCI OLARD). + */ + /* limit IOVA space size to 1MB-1GB */ + if (mem_max < (ccio_mem_ratio*1024*1024)) { + iova_space_size = 1024*1024; +#ifdef __LP64__ + } else if (mem_max > (ccio_mem_ratio*512*1024*1024)) { + iova_space_size = 512*1024*1024; +#endif + } else { + iova_space_size = (u32) (mem_max/ccio_mem_ratio); + } + + /* + ** iova space must be log2() in size. + ** thus, pdir/res_map will also be log2(). + */ + + /* We could use larger page sizes in order to *decrease* the number + ** of mappings needed. (ie 8k pages means 1/2 the mappings). + ** + ** Note: Grant Grunder says "Using 8k I/O pages isn't trivial either + ** since the pages must also be physically contiguous - typically + ** this is the case under linux." + */ + + iov_order = get_order(iova_space_size); + ASSERT(iov_order <= (30 - IOVP_SHIFT)); /* iova_space_size <= 1GB */ + ASSERT(iov_order >= (20 - IOVP_SHIFT)); /* iova_space_size >= 1MB */ + iova_space_size = 1 << (iov_order + IOVP_SHIFT); + + ioa->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64); + + ASSERT(pdir_size < 4*1024*1024); /* max pdir size < 4MB */ + + /* Verify it's a power of two */ + ASSERT((1 << get_order(pdir_size)) == (pdir_size >> PAGE_SHIFT)); + + DBG_INIT(__FUNCTION__ " hpa 0x%p mem %dMB IOV %dMB (%d bits)\n PDIR size 0x%0x", + ioa->ccio_hpa, (int) (mem_max>>20), iova_space_size>>20, + iov_order + PAGE_SHIFT, pdir_size); + + ioa->pdir_base = + pdir_base = (void *) __get_free_pages(GFP_KERNEL, get_order(pdir_size)); + if (NULL == pdir_base) + { + panic(__FILE__ ":" __FUNCTION__ "() could not allocate I/O Page Table\n"); + } + memset(pdir_base, 0, pdir_size); + + ASSERT((((unsigned long) pdir_base) & PAGE_MASK) == (unsigned long) pdir_base); + + DBG_INIT(" base %p", pdir_base); + + /* + ** Chainid is the upper most bits of an IOVP used to determine + ** which TLB entry an IOVP will use. + */ + ioa->chainid_shift = get_order(iova_space_size)+PAGE_SHIFT-CCIO_CHAINID_SHIFT; + + DBG_INIT(" chainid_shift 0x%x\n", ioa->chainid_shift); +} + + +static void +ccio_hw_init(struct ccio_device *ioa) +{ + int i; + + /* + ** Initialize IOA hardware + */ + WRITE_U32(CCIO_CHAINID_MASK << ioa->chainid_shift, &ioa->ccio_hpa->io_chain_id_mask); + WRITE_U32(virt_to_phys(ioa->pdir_base), &ioa->ccio_hpa->io_pdir_base); + + + /* + ** Go to "Virtual Mode" + */ + WRITE_U32(IOA_NORMAL_MODE, &ioa->ccio_hpa->io_control); + + /* + ** Initialize all I/O TLB entries to 0 (Valid bit off). + */ + WRITE_U32(0, &ioa->ccio_hpa->io_tlb_entry_m); + WRITE_U32(0, &ioa->ccio_hpa->io_tlb_entry_l); + + for (i = 1 << CCIO_CHAINID_SHIFT; i ; i--) { + WRITE_U32((CMD_TLB_DIRECT_WRITE | (i << ioa->chainid_shift)), + &ioa->ccio_hpa->io_command); + } + +} + + +static void +ccio_resmap_init(struct ccio_device *ioa) +{ + u32 res_size; + + /* + ** Ok...we do more than just init resource map + */ + ioa->ccio_lock = SPIN_LOCK_UNLOCKED; + + ioa->res_hint = 16; /* next available IOVP - circular search */ + + /* resource map size dictated by pdir_size */ + res_size = ioa->pdir_size/sizeof(u64); /* entries */ + res_size >>= 3; /* convert bit count to byte count */ + DBG_INIT(__FUNCTION__ "() res_size 0x%x\n", res_size); + + ioa->res_size = res_size; + ioa->res_map = (char *) __get_free_pages(GFP_KERNEL, get_order(res_size)); + if (NULL == ioa->res_map) + { + panic(__FILE__ ":" __FUNCTION__ "() could not allocate resource map\n"); + } + memset(ioa->res_map, 0, res_size); +} + +/* CUJO20 KLUDGE start */ +static struct { + u16 hversion; + u8 spa; + u8 type; + u32 foo[3]; /* 16 bytes total */ +} cujo_iodc __attribute__ ((aligned (64))); +static unsigned long cujo_result[32] __attribute__ ((aligned (16))) = {0,0,0,0}; + +/* +** CUJO 2.0 incorrectly decodes a memory access for specific +** pages (every page at specific iotlb locations dependent +** upon where the cujo is flexed - diff on raven/firehawk. +** resulting in an hpmc and/or silent data corruption. +** Workaround is to prevent use of those I/O TLB entries +** by marking the suspect bitmap range entries as busy. +*/ +static void +ccio_cujo20_hack(struct ccio_device *ioa) +{ + unsigned long status; + unsigned int idx; + u8 *res_ptr = ioa->res_map; + u32 iovp=0x0; + unsigned long mask; + + status = pdc_iodc_read( &cujo_result, (void *) CUJO_RAVEN_LOC, 0, &cujo_iodc, 16); + if (status == 0) { + if (cujo_iodc.hversion==CUJO_20_BADHVERS) + iovp = CUJO_20_BADPAGE1; + } else { + status = pdc_iodc_read( &cujo_result, (void *) CUJO_FIREHAWK_LOC, 0, &cujo_iodc, 16); + if (status == 0) { + if (cujo_iodc.hversion==CUJO_20_BADHVERS) + iovp = CUJO_20_BADPAGE2; + } else { + /* not a defective system */ + return; + } + } + + printk(MODULE_NAME ": Cujo 2.0 bug needs a work around\n"); + ccio_cujo_bug = 1; + + /* + ** mark bit entries that match "bad page" + */ + idx = PDIR_INDEX(iovp)>>3; + mask = 0xff; + + while(idx * sizeof(u8) < ioa->res_size) { + res_ptr[idx] |= mask; + idx += (PDIR_INDEX(CUJO_20_STEP)>>3); + ccio_used_pages += 8; + ccio_used_bytes += 1; + } +} +/* CUJO20 KLUDGE end */ + +#ifdef CONFIG_PROC_FS +static int ccio_proc_info(char *buf, char **start, off_t offset, int len) +{ + unsigned long i = 0; + struct ccio_device *ioa = ccio_list; + unsigned long *res_ptr = (unsigned long *)ioa->res_map; + unsigned long total_pages = ioa->res_size << 3; /* 8 bits per byte */ + + sprintf(buf, "%s\nCujo 2.0 bug : %s\n", + parisc_getHWdescription(ioa->iodc->hw_type, ioa->iodc->hversion, + ioa->iodc->sversion), + (ccio_cujo_bug ? "yes" : "no")); + + sprintf(buf, "%sIO pdir size : %d bytes (%d entries)\n", + buf, ((ioa->res_size << 3) * sizeof(u64)), /* 8 bits per byte */ + ioa->res_size << 3); /* 8 bits per byte */ + + sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", + buf, ioa->res_size, ioa->res_size << 3); /* 8 bits per byte */ + + strcat(buf, " total: free: used: % used:\n"); + sprintf(buf, "%sblocks %8d %8ld %8ld %8ld%%\n", buf, ioa->res_size, + ioa->res_size - ccio_used_bytes, ccio_used_bytes, + (ccio_used_bytes * 100) / ioa->res_size); + + sprintf(buf, "%spages %8ld %8ld %8ld %8ld%%\n", buf, total_pages, + total_pages - ccio_used_pages, ccio_used_pages, + (ccio_used_pages * 100 / total_pages)); + + sprintf(buf, "%sconsistent %8ld %8ld\n", buf, + ccio_alloc_size, ccio_free_size); + + strcat(buf, "\nResource bitmap:\n"); + + for(; i < (ioa->res_size / sizeof(unsigned long)); ++i, ++res_ptr) + len += sprintf(buf, "%s%08lx ", buf, *res_ptr); + + strcat(buf, "\n"); + return strlen(buf); +} +#endif + +/* +** Determine if ccio should claim this chip (return 0) or not (return 1). +** If so, initialize the chip and tell other partners in crime they +** have work to do. +*/ +static int +ccio_driver_callback(struct hp_device *d, struct pa_iodc_driver *dri) +{ + struct ccio_device *ioa; + + printk("%s found %s at 0x%p\n", dri->name, dri->version, d->hpa); + + if (ccio_list) { + printk(MODULE_NAME ": already initialized one device\n"); + return(0); + } + + ioa = kmalloc(sizeof(struct ccio_device), GFP_KERNEL); + if (NULL == ioa) + { + printk(MODULE_NAME " - couldn't alloc ccio_device\n"); + return(1); + } + memset(ioa, 0, sizeof(struct ccio_device)); + + /* + ** ccio list is used mainly as a kluge to support a single instance. + ** Eventually, with core dumps, it'll be useful for debugging. + */ + ccio_list = ioa; + ioa->iodc = d; + +#if 1 +/* KLUGE: determine IOA hpa based on GSC port value. +** Needed until we have a PA bus walk. Can only discover IOA via +** walking the architected PA MMIO space as described by the I/O ACD. +** "Legacy" PA Firmware only tells us about unarchitected devices +** that can't be detected by PA/EISA/PCI bus walks. +*/ + switch((long) d->hpa) { + case 0xf3fbf000L: /* C110 IOA0 LBC (aka GSC port) */ + /* ccio_hpa same as C200 IOA0 */ + case 0xf203f000L: /* C180/C200/240/C360 IOA0 LBC (aka GSC port) */ + ioa->ccio_hpa = (struct ioa_registers *) 0xfff88000L; + break; + case 0xf103f000L: /* C180/C200/240/C360 IOA1 LBC (aka GSC port) */ + ioa->ccio_hpa = (struct ioa_registers *) 0xfff8A000L; + break; + default: + panic("ccio-dma.c doesn't know this GSC port Address!\n"); + break; + }; +#else + ioa->ccio_hpa = d->hpa; +#endif + + ccio_alloc_pdir(ioa); + ccio_hw_init(ioa); + ccio_resmap_init(ioa); + + /* CUJO20 KLUDGE start */ + ccio_cujo20_hack(ioa); + /* CUJO20 KLUDGE end */ + + hppa_dma_ops = &ccio_ops; + + create_proc_info_entry(MODULE_NAME, 0, proc_runway_root, ccio_proc_info); + return(0); +} + + + diff --git a/arch/parisc/kernel/ccio-rm-dma.c b/arch/parisc/kernel/ccio-rm-dma.c new file mode 100644 index 000000000000..25ec90c1bf32 --- /dev/null +++ b/arch/parisc/kernel/ccio-rm-dma.c @@ -0,0 +1,212 @@ +/* + * ccio-rm-dma.c: + * DMA management routines for first generation cache-coherent machines. + * "Real Mode" operation refers to U2/Uturn chip operation. The chip + * can perform coherency checks w/o using the I/O MMU. That's all we + * need until support for more than 4GB phys mem is needed. + * + * This is the trivial case - basically what x86 does. + * + * Drawbacks of using Real Mode are: + * o outbound DMA is slower since one isn't using the prefetching + * U2 can do for outbound DMA. + * o Ability to do scatter/gather in HW is also lost. + * o only known to work with PCX-W processor. (eg C360) + * (PCX-U/U+ are not coherent with U2 in real mode.) + * + * + * 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. + * + * + * Original version/author: + * CVSROOT=:pserver:anonymous@198.186.203.37:/cvsroot/linux-parisc + * cvs -z3 co linux/arch/parisc/kernel/dma-rm.c + * + * (C) Copyright 2000 Philipp Rumpf + * + * + * Adopted for The Puffin Group's parisc-linux port by Grant Grundler. + * (C) Copyright 2000 Grant Grundler + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* Only chose "ccio" since that's what HP-UX calls it.... +** Make it easier for folks to migrate from one to the other :^) +*/ +#define MODULE_NAME "ccio" + +#define U2_IOA_RUNWAY 0x580 +#define U2_BC_GSC 0x501 +#define UTURN_IOA_RUNWAY 0x581 +#define UTURN_BC_GSC 0x502 + +static int ccio_driver_callback(struct hp_device *, struct pa_iodc_driver *); + +static struct pa_iodc_driver ccio_drivers_for[] = { + + {HPHW_BCPORT, U2_BC_GSC, 0x0, 0xb, 0, 0x10, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "U2 I/O MMU", (void *) ccio_driver_callback}, + + {HPHW_BCPORT, UTURN_BC_GSC, 0x0, 0xb, 0, 0x10, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "Uturn I/O MMU", (void *) ccio_driver_callback}, + + {0,0,0,0,0,0, + 0, + (char *) NULL, (char *) NULL, (void *) NULL } +}; + + +#define IS_U2(id) ( \ + (((id)->hw_type == HPHW_IOA) && ((id)->hversion == U2_IOA_RUNWAY)) || \ + (((id)->hw_type == HPHW_BCPORT) && ((id)->hversion == U2_BC_GSC)) \ +) + +#define IS_UTURN(id) ( \ + (((id)->hw_type == HPHW_IOA) && ((id)->hversion == UTURN_IOA_RUNWAY)) || \ + (((id)->hw_type == HPHW_BCPORT) && ((id)->hversion == UTURN_BC_GSC)) \ +) + + +void __init ccio_init(void) +{ + register_driver(ccio_drivers_for); +} + + +static int ccio_dma_supported( struct pci_dev *dev, dma_addr_t mask) +{ + if (dev == NULL) { + printk(MODULE_NAME ": EISA/ISA/et al not supported\n"); + BUG(); + return(0); + } + + dev->dma_mask = mask; /* save it */ + + /* only support 32-bit devices (ie PCI/GSC) */ + return((int) (mask >= 0xffffffffUL)); +} + + +static void *ccio_alloc_consistent(struct pci_dev *dev, size_t size, + dma_addr_t *handle) +{ + void *ret; + + ret = (void *)__get_free_pages(GFP_ATOMIC, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *handle = virt_to_phys(ret); + } + return ret; +} + +static void ccio_free_consistent(struct pci_dev *dev, size_t size, + void *vaddr, dma_addr_t handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} + +static dma_addr_t ccio_map_single(struct pci_dev *dev, void *ptr, size_t size, + int direction) +{ + return virt_to_phys(ptr); +} + +static void ccio_unmap_single(struct pci_dev *dev, dma_addr_t dma_addr, + size_t size, int direction) +{ + /* Nothing to do */ +} + + +static int ccio_map_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction) +{ + int tmp = nents; + + /* KISS: map each buffer seperately. */ + while (nents) { + sg_dma_address(sglist) = ccio_map_single(dev, sglist->address, sglist->length, direction); + sg_dma_len(sglist) = sglist->length; + nents--; + sglist++; + } + + return tmp; +} + + +static void ccio_unmap_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction) +{ +#if 0 + while (nents) { + ccio_unmap_single(dev, sg_dma_address(sglist), sg_dma_len(sglist), direction); + nents--; + sglist++; + } + return; +#else + /* Do nothing (copied from current ccio_unmap_single() :^) */ +#endif +} + + +static struct pci_dma_ops ccio_ops = { + ccio_dma_supported, + ccio_alloc_consistent, + ccio_free_consistent, + ccio_map_single, + ccio_unmap_single, + ccio_map_sg, + ccio_unmap_sg, + NULL, /* dma_sync_single : NOP for U2 */ + NULL, /* dma_sync_sg : ditto */ + + +}; + + +/* +** Determine if u2 should claim this chip (return 0) or not (return 1). +** If so, initialize the chip and tell other partners in crime they +** have work to do. +*/ +static int +ccio_driver_callback(struct hp_device *d, struct pa_iodc_driver *dri) +{ + printk("%s found %s at 0x%p\n", dri->name, dri->version, d->hpa); + +/* +** FIXME - should check U2 registers to verify it's really running +** in "Real Mode". +*/ + +#if 0 +/* will need this for "Virtual Mode" operation */ + ccio_hw_init(ccio_dev); + ccio_common_init(ccio_dev); +#endif + hppa_dma_ops = &ccio_ops; + return 0; +} diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c new file mode 100644 index 000000000000..c2d9f825e40d --- /dev/null +++ b/arch/parisc/kernel/drivers.c @@ -0,0 +1,135 @@ +/* + +drivers.c + +Copyright (c) 1999 The Puffin Group + +This is a collection of routines intended to register all the devices +in a system, and register device drivers. + +*/ + +#include +#include +#include +#include +#include +#include +#include + + +extern struct hp_hardware *parisc_get_reference( + unsigned short hw_type, unsigned long hversion, + unsigned long sversion ); + + +/* I'm assuming there'll never be 64 devices. We should probably make + this more flexible. */ + +#define MAX_DEVICES 64 + +unsigned int num_devices = 0; + +struct hp_device devices[MAX_DEVICES]; + +static unsigned long pdc_result[32] __attribute__ ((aligned (16))) = {0,0,0,0}; +static u8 iodc_data[32] __attribute__ ((aligned (64))); + +/* + * XXX should we be using a locked array ? + */ + +int register_driver(struct pa_iodc_driver *driver) +{ + unsigned int i; + struct hp_device * device; + + for (;driver->check;driver++) { + + for (i=0;imanaged) continue; + + if ((driver->check & DRIVER_CHECK_HWTYPE) && + (driver->hw_type != device->hw_type)) + continue; + if ((driver->check & DRIVER_CHECK_HVERSION) && + (driver->hversion != device->hversion)) + continue; + if ((driver->check & DRIVER_CHECK_HVERSION_REV) && + (driver->hversion_rev != device->hversion_rev)) + continue; + if ((driver->check & DRIVER_CHECK_SVERSION) && + (driver->sversion != device->sversion)) + continue; + if ((driver->check & DRIVER_CHECK_SVERSION_REV) && + (driver->sversion_rev != device->sversion_rev)) + continue; + if ((driver->check & DRIVER_CHECK_OPT) && + (driver->opt != device->opt)) + continue; + if ( (*driver->callback)(device,driver) ==0) { + device->managed=1; + } else { + printk("Warning : device (%d, 0x%x, 0x%x, 0x%x, 0x%x) NOT claimed by %s %s\n", + device->hw_type, + device->hversion, device->hversion_rev, + device->sversion, device->sversion_rev, + driver->name, driver->version); + } + } + } + return 0; +} + + +struct hp_device * register_module(void *hpa) +{ + + struct hp_device * d; + int status; + + d = &devices[num_devices]; + status = pdc_iodc_read(&pdc_result,hpa,0,&iodc_data,32 ); + if (status !=PDC_RET_OK) { + /* There is no device here, so we'll skip it */ + return 0; + } + + d->hw_type = iodc_data[3]&0x1f; + d->hversion = (iodc_data[0]<<4)|((iodc_data[1]&0xf0)>>4); + d->sversion = + ((iodc_data[4]&0x0f)<<16)|(iodc_data[5]<<8)|(iodc_data[6]); + d->hversion_rev = iodc_data[1]&0x0f; + d->sversion_rev = iodc_data[4]>>4; + d->opt = iodc_data[7]; + d->hpa = hpa; + d->managed=0; + d->reference = parisc_get_reference(d->hw_type, d->hversion, + d->sversion); + + num_devices++; + + return d; +} + +void print_devices(char * buf) { + + int i; + struct hp_device *d; + printk("Found devices:\n"); + for (i=0;ireference) ? d->reference->name : "Unknown device", + d->hw_type,d->hpa, d->hversion, d->hversion_rev, + d->sversion, d->sversion_rev, d->opt); + + } + printk("That's a total of %d devices.\n",num_devices); +} + + diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S new file mode 100644 index 000000000000..437df8231cef --- /dev/null +++ b/arch/parisc/kernel/entry.S @@ -0,0 +1,1866 @@ +/*------------------------------------------------------------------------------ + * Native PARISC/Linux Project (http://www.puffingroup.com/parisc) + * + * kernel entry points (interruptions, system call wrappers) + * Copyright (C) 1999,2000 Philipp Rumpf + * Copyright (C) 1999 SuSE GmbH Nuernberg + * Copyright (C) 2000 Hewlett-Packard (John Marvin) + * Copyright (C) 1999 Hewlett-Packard (Frank Rowand) + * + * 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, 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 + +/* the following is the setup i think we should follow: + * whenever the CPU is interruptible, the following has to be true: + * CR30 is the kernel sp or 0 if we currently use the kernel stack + * CR31 is the kernel gp */ + +/* we have the following possibilities to act on an interruption: + * - handle in assembly and use shadowed registers only + * - save registers to kernel stack and handle in assembly or C */ + + .text + +#ifdef __LP64__ + .level 2.0w +#endif + +#define __ASSEMBLY__ +#include /* for LDREG/STREG defines */ +#include +#include +#include + +#ifdef __LP64__ +#define FRAME_SIZE 64 +#else +#define FRAME_SIZE 64 +#endif + + /* Switch to virtual mapping, trashing only %r1 */ + .macro virt_map rfi_type + mtsm %r0 + tovirt %r29 + tovirt %r30 + mfsp %sr7, %r1 + mtsp %r1, %sr3 + mtsp %r0, %sr4 + mtsp %r0, %sr5 + mtsp %r0, %sr6 + mtsp %r0, %sr7 + ldil L%KERNEL_PSW, %r1 + ldo R%KERNEL_PSW(%r1), %r1 + LDIL_FIXUP(%r1) + mtctl %r1, %cr22 + mtctl %r0, %cr17 + mtctl %r0, %cr17 + ldil L%.+28, %r1 + ldo R%.+24(%r1), %r1 + LDIL_FIXUP(%r1) + mtctl %r1, %cr18 + ldo 4(%r1), %r1 + mtctl %r1, %cr18 + \rfi_type + nop + .endm + + .macro get_stack + mfctl %cr30, %r1 + comib,=,n 0, %r1, 0f /* forward so predicted not taken */ + + /* we save the registers in the task struct */ + ldo TASK_REGS(%r1), %r29 + tophys %r29 + STREG %r30, PT_GR30(%r29) + STREG %r1, PT_CR30(%r29) + ldo TASK_SZ_ALGN(%r1), %r30 + b 1f /* unconditional so predicted taken */ + mtctl %r0,%cr30 +0: + /* we put a struct pt_regs on the stack and save the registers there */ + copy %r30,%r29 + ldo PT_SZ_ALGN(%r30),%r30 + tophys %r29 + STREG %r30,PT_GR30(%r29) + STREG %r0,PT_CR30(%r29) +1: + .endm + + .macro rest_stack regs + LDREG PT_CR30(\regs), %r1 + comib,=,n 0, %r1, 2f/* forward so predicted not taken */ + + /* we restore the registers out of the task struct */ + mtctl %r1, %cr30 + LDREG PT_GR1(\regs), %r1 + LDREG PT_GR30(\regs),%r30 + b 3f + LDREG PT_GR29(\regs),%r29 +2: + /* we take a struct pt_regs off the stack */ + LDREG PT_GR1(\regs), %r1 + LDREG PT_GR29(\regs), %r29 + ldo -PT_SZ_ALGN(%r30), %r30 +3: + .endm + +#ifdef OLD + /* fixme interruption handler */ + .macro def code + /* WARNING!!! THIS IS DEBUG CODE ONLY!!! */ + b unimplemented_64bitirq + ldi \code, %r1 + .align 32 + .endm + + /* Use def to enable break - KWDB wants em + * (calls traps.c:handle_interruption) */ + .macro pass_break code + +#else + /* default interruption handler + * (calls traps.c:handle_interruption) */ + .macro def code +#endif + mtctl %r29, %cr31 + mtctl %r1, %cr28 + ldi \code, %r1 + b intr_save + mtctl %r1, %cr29 + .align 32 + .endm + + /* Interrupt interruption handler + * (calls irq.c:do_irq_mask) */ + .macro extint code + mtctl %r29, %cr31 + mtctl %r1, %cr28 + mfctl %cr23, %r1 + mtctl %r1, %cr23 + b intr_extint + mtctl %r1, %cr29 + .align 32 + .endm + + .import os_hpmc, code + + /* HPMC handler */ + .macro hpmc code + nop /* must be a NOP, will be patched later */ + ldil L%PA(os_hpmc), %r3 + ldo R%PA(os_hpmc)(%r3), %r3 + bv,n 0(%r3) + nop + .word 0 /* checksum (will be patched) */ + .word PA(os_hpmc) /* address of handler */ + .word 0 /* length of handler */ + .endm + + /* + * Performance Note: Instructions will be moved up into + * this part of the code later on, once we are sure + * that the tlb miss handlers are close to final form. + */ + + /* Register definitions for tlb miss handler macros */ + + va = r8 /* virtual address for which the trap occured */ + spc = r24 /* space for which the trap occured */ + +#ifndef __LP64__ + + /* + * itlb miss interruption handler (parisc 1.1 - 32 bit) + */ + + .macro itlb_11 code + + mfctl %pcsq, spc + b itlb_miss_11 + mfctl %pcoq, va + + .align 32 + .endm +#endif + + /* + * itlb miss interruption handler (parisc 2.0) + */ + + .macro itlb_20 code + + mfctl %pcsq, spc +#ifdef __LP64__ + b itlb_miss_20w +#else + b itlb_miss_20 +#endif + mfctl %pcoq, va + + .align 32 + .endm + +#ifndef __LP64__ + /* + * naitlb miss interruption handler (parisc 1.1 - 32 bit) + * + * Note: naitlb misses will be treated + * as an ordinary itlb miss for now. + * However, note that naitlb misses + * have the faulting address in the + * IOR/ISR. + */ + + .macro naitlb_11 code + + mfctl %isr,spc + b itlb_miss_11 + mfctl %ior,va + /* FIXME: If user causes a naitlb miss, the priv level may not be in + * lower bits of va, where the itlb miss handler is expecting them + */ + + .align 32 + .endm +#endif + + /* + * naitlb miss interruption handler (parisc 2.0) + * + * Note: naitlb misses will be treated + * as an ordinary itlb miss for now. + * However, note that naitlb misses + * have the faulting address in the + * IOR/ISR. + */ + + .macro naitlb_20 code + + mfctl %isr,spc +#ifdef __LP64__ + b itlb_miss_20w +#else + b itlb_miss_20 +#endif + mfctl %ior,va + /* FIXME: If user causes a naitlb miss, the priv level may not be in + * lower bits of va, where the itlb miss handler is expecting them + */ + + .align 32 + .endm + +#ifndef __LP64__ + /* + * dtlb miss interruption handler (parisc 1.1 - 32 bit) + */ + + .macro dtlb_11 code + + mfctl %isr, spc + b dtlb_miss_11 + mfctl %ior, va + + .align 32 + .endm +#endif + + /* + * dtlb miss interruption handler (parisc 2.0) + */ + + .macro dtlb_20 code + + mfctl %isr, spc +#ifdef __LP64__ + b dtlb_miss_20w +#else + b dtlb_miss_20 +#endif + mfctl %ior, va + + .align 32 + .endm + +#ifndef __LP64__ + /* nadtlb miss interruption handler (parisc 1.1 - 32 bit) + * + * Note: nadtlb misses will be treated + * as an ordinary dtlb miss for now. + * + */ + + .macro nadtlb_11 code + + mfctl %isr,spc + b dtlb_miss_11 + mfctl %ior,va + + .align 32 + .endm +#endif + + /* nadtlb miss interruption handler (parisc 2.0) + * + * Note: nadtlb misses will be treated + * as an ordinary dtlb miss for now. + * + */ + + .macro nadtlb_20 code + + mfctl %isr,spc +#ifdef __LP64__ + b dtlb_miss_20w +#else + b dtlb_miss_20 +#endif + mfctl %ior,va + + .align 32 + .endm + +#ifndef __LP64__ + /* + * dirty bit trap interruption handler (parisc 1.1 - 32 bit) + */ + + .macro dbit_11 code + + mfctl %isr,spc + b dbit_trap_11 + mfctl %ior,va + + .align 32 + .endm +#endif + + /* + * dirty bit trap interruption handler (parisc 2.0) + */ + + .macro dbit_20 code + + mfctl %isr,spc +#ifdef __LP64__ + b dbit_trap_20w +#else + b dbit_trap_20 +#endif + mfctl %ior,va + + .align 32 + .endm + + /* + * Align fault_vector_20 on 4K boundary so that both + * fault_vector_11 and fault_vector_20 are on the + * same page. This is only necessary as long as we + * write protect the kernel text, which we may stop + * doing once we use large parge translations to cover + * the static part of the kernel address space. + */ + + + .export fault_vector_20 + + .align 4096 + +fault_vector_20: + /* First vector is invalid (0) */ + .ascii "cows can fly" + .byte 0 + .align 32 + + hpmc 1 + def 2 + def 3 + extint 4 + def 5 + itlb_20 6 + def 7 + def 8 + def 9 + def 10 + def 11 + def 12 + def 13 + def 14 + dtlb_20 15 + naitlb_20 16 + nadtlb_20 17 + def 18 + def 19 + dbit_20 20 + def 21 + def 22 + def 23 + def 24 + def 25 + def 26 + def 27 + def 28 + def 29 + def 30 + def 31 + +#ifndef __LP64__ + + .export fault_vector_11 + + .align 2048 + +fault_vector_11: + /* First vector is invalid (0) */ + .ascii "cows can fly" + .byte 0 + .align 32 + + hpmc 1 + def 2 + def 3 + extint 4 + def 5 + itlb_11 6 + def 7 + def 8 + def 9 + def 10 + def 11 + def 12 + def 13 + def 14 + dtlb_11 15 + naitlb_11 16 + nadtlb_11 17 + def 18 + def 19 + dbit_11 20 + def 21 + def 22 + def 23 + def 24 + def 25 + def 26 + def 27 + def 28 + def 29 + def 30 + def 31 + +#endif + + .import handle_interruption,code + .import handle_real_interruption,code + .import do_irq_mask,code + .import parisc_stopkernel,code + .import cpu_irq_region,data + + /* + * r26 = function to be called + * r25 = argument to pass in + * r24 = flags for do_fork() + * + * Kernel threads don't ever return, so they don't need + * a true register context. We just save away the arguments + * for copy_thread/ret_ to properly set up the child. + */ + +#define CLONE_VM 0x100 /* Must agree with */ + + .export __kernel_thread, code + .import do_fork +__kernel_thread: + STREG %r2, -RP_OFFSET(%r30) + + copy %r30, %r1 + ldo PT_SZ_ALGN(%r30),%r30 +#ifdef __LP64__ + /* Yo, function pointers in wide mode are little structs... -PB */ + /* XXX FIXME do we need to honor the fptr's %dp value too? */ + ldd 16(%r26), %r26 +#endif + STREG %r26, PT_GR26(%r1) /* Store function & argument for child */ + STREG %r25, PT_GR25(%r1) + ldo CLONE_VM(%r0), %r26 /* Force CLONE_VM since only init_mm */ + or %r26, %r24, %r26 /* will have kernel mappings. */ + copy %r0, %r25 + bl do_fork, %r2 + copy %r1, %r24 + + /* Parent Returns here */ + + ldo -PT_SZ_ALGN(%r30), %r30 + LDREG -RP_OFFSET(%r30), %r2 + bv %r0(%r2) + nop + + /* + * Child Returns here + * + * copy_thread moved args from temp save area set up above + * into task save area. + */ + + .export ret_from_kernel_thread +ret_from_kernel_thread: + + LDREG TASK_PT_GR26-TASK_SZ_ALGN(%r30), %r1 + LDREG TASK_PT_GR25-TASK_SZ_ALGN(%r30), %r26 + ble 0(%sr7, %r1) + copy %r31, %r2 + + b sys_exit + ldi 0, %r26 + + .import sys_execve, code + .export __execve, code +__execve: + copy %r2, %r15 + copy %r23, %r17 + copy %r30, %r16 + ldo PT_SZ_ALGN(%r30), %r30 + STREG %r26, PT_GR26(%r16) + STREG %r25, PT_GR25(%r16) + STREG %r24, PT_GR24(%r16) + bl sys_execve, %r2 + copy %r16, %r26 + + comib,<>,n 0,%r28,__execve_failed + + b intr_return + STREG %r17, PT_CR30(%r16) + +__execve_failed: + /* yes, this will trap and die. */ + copy %r15, %r2 + bv %r0(%r2) + nop + + .align 4 + + /* + * struct task_struct *_switch_to(struct task_struct *prev, + * struct task_struct *next) + * + * switch kernel stacks and return prev */ + .export _switch_to, code +_switch_to: + STREG %r2, -RP_OFFSET(%r30) + + callee_save + + ldil L%_switch_to_ret, %r2 + ldo R%_switch_to_ret(%r2), %r2 + LDIL_FIXUP(%r2) + + STREG %r2, TASK_PT_KPC(%r26) + LDREG TASK_PT_KPC(%r25), %r2 + + STREG %r30, TASK_PT_KSP(%r26) + LDREG TASK_PT_KSP(%r25), %r30 + + bv %r0(%r2) + nop + +_switch_to_ret: + mtctl %r0, %cr0 /* Needed for single stepping */ + callee_rest + + LDREG -RP_OFFSET(%r30), %r2 + bv %r0(%r2) + copy %r26, %r28 + + /* + * Common rfi return path for interruptions, kernel execve, and some + * syscalls. The sys_rt_sigreturn syscall will return via this path + * if the signal was received when the process was running; if the + * process was blocked on a syscall then the normal syscall_exit + * path is used. All syscalls for traced proceses exit via + * intr_restore. + * Note that the following code uses a "relied upon translation". See + * the parisc ACD for details. The ssm is necessary due to a PCXT bug. + */ + + .align 4096 + + .export syscall_exit_rfi +syscall_exit_rfi: + copy %r30,%r16 + /* FIXME! depi below has hardcoded dependency on kernel stack size */ + depi 0,31,14,%r16 /* get task pointer */ + ldo TASK_REGS(%r16),%r16 + /* Force iaoq to userspace, as the user has had access to our current + * context via sigcontext. + * XXX do we need any other protection here? + */ + LDREG PT_IAOQ0(%r16),%r19 + depi 3,31,2,%r19 + STREG %r19,PT_IAOQ0(%r16) + LDREG PT_IAOQ1(%r16),%r19 + depi 3,31,2,%r19 + STREG %r19,PT_IAOQ1(%r16) + +intr_return: + + /* Check for software interrupts */ + + .import irq_stat,data + + ldil L%irq_stat,%r19 + ldo R%irq_stat(%r19),%r19 + LDIL_FIXUP(%r19) + +#ifdef CONFIG_SMP + copy %r30,%r1 + /* FIXME! depi below has hardcoded dependency on kernel stack size */ + depi 0,31,14,%r1 /* get task pointer */ + ldw TASK_PROCESSOR(%r1),%r20 /* get cpu # - int */ +#if (IRQSTAT_SZ == 32) + dep %r20,26,27,%r20 /* shift left 5 bits */ +#else +#error IRQSTAT_SZ changed, fix dep +#endif /* IRQSTAT_SZ */ + add %r19,%r20,%r19 +#endif /* CONFIG_SMP */ + + ldw IRQSTAT_SI_ACTIVE(%r19),%r20 /* hardirq.h: unsigned int */ + ldw IRQSTAT_SI_MASK(%r19),%r19 /* hardirq.h: unsigned int */ + and %r19,%r20,%r20 + comib,<>,n 0,%r20,intr_do_softirq /* forward */ + +intr_check_resched: + + /* check for reschedule */ + copy %r30,%r1 + /* FIXME! depi below has hardcoded dependency on kernel stack size */ + depi 0,31,14,%r1 /* get task pointer */ + LDREG TASK_NEED_RESCHED(%r1),%r19 /* sched.h: long need_resched */ + comib,<>,n 0,%r19,intr_do_resched /* forward */ + +intr_check_sig: + /* As above */ + copy %r30,%r1 + depi 0,31,14,%r1 /* get task pointer */ + ldw TASK_SIGPENDING(%r1),%r19 /* sched.h: int sigpending */ + comib,<>,n 0,%r19,intr_do_signal /* forward */ + +intr_restore: + copy %r16, %r29 + ldo PT_FR31(%r29), %r29 + rest_fp %r29 + copy %r16, %r29 + rest_general %r29 + ssm 0,%r0 + nop + nop + nop + nop + nop + nop + nop + tophys %r29 + mtsm %r0 + rest_specials %r29 + rest_stack %r29 + rfi + nop + nop + nop + nop + nop + nop + nop + nop + + .import do_softirq,code +intr_do_softirq: + bl do_softirq,%r2 + nop + b intr_check_resched + nop + + .import schedule,code +intr_do_resched: + /* Only do reschedule if we are returning to user space */ + LDREG PT_SR7(%r16), %r20 + comib,= 0,%r20,intr_restore /* backward */ + nop + + bl schedule,%r2 + ssm PSW_SM_I, %r0 + + /* It's OK to leave I bit on */ + b intr_return /* start over if we got a resched */ + nop + + .import do_signal,code +intr_do_signal: + /* Only do signals if we are returning to user space */ + LDREG PT_SR7(%r16), %r20 + comib,= 0,%r20,intr_restore /* backward */ + nop + + copy %r0, %r24 /* unsigned long in_syscall */ + copy %r16, %r25 /* struct pt_regs *regs */ + ssm PSW_SM_I, %r0 + bl do_signal,%r2 + copy %r0, %r26 /* sigset_t *oldset = NULL */ + + b intr_restore + nop + + /* CR28 - saved GR1 + * CR29 - argument for do_irq_mask */ + + /* External interrupts */ +intr_extint: + get_stack + save_specials %r29 + virt_map rfi + save_general %r29 + + ldo PT_FR0(%r29), %r24 + save_fp %r24 + + loadgp + + copy %r29, %r24 /* arg2 is pt_regs */ + copy %r29, %r16 /* save pt_regs */ +#ifdef CONFIG_KWDB + copy %r29, %r3 /* KWDB - update frame pointer (gr3) */ +#endif + + /* sorry to put this cruft in the interrupt path */ + ldil L%cpu_irq_region, %r25 + ldo R%cpu_irq_region(%r25), %r25 + bl do_irq_mask,%r2 +#ifdef __LP64__ + LDIL_FIXUP(%r25) +#else + nop +#endif + + b intr_return + nop + + /* Generic interruptions (illegal insn, unaligned, page fault, etc) */ + + .export intr_save, code /* for os_hpmc */ + +intr_save: + get_stack + save_specials %r29 + + mfctl %cr20, %r1 + STREG %r1, PT_ISR(%r29) + mfctl %cr21, %r1 + STREG %r1, PT_IOR(%r29) + + virt_map rfi + save_general %r29 + + ldo PT_FR0(%r29), %r25 + save_fp %r25 + + loadgp + + copy %r29, %r25 /* arg1 is pt_regs */ +#ifdef CONFIG_KWDB + copy %r29, %r3 /* KWDB - update frame pointer (gr3) */ +#endif + + bl handle_interruption,%r2 + copy %r29, %r16 /* save pt_regs */ + + b intr_return + nop + + /* + * Note for all tlb miss handlers: + * + * cr24 contains a pointer to the kernel address space + * page directory. + * + * cr25 contains a pointer to the current user address + * space page directory. + * + * sr3 will contain the space id of the user address space + * of the current running thread while that thread is + * running in the kernel. + */ + + /* + * register number allocations. Note that these are all + * in the shadowed registers + */ + + t0 = r1 /* temporary register 0 */ + va = r8 /* virtual address for which the trap occured */ + t1 = r9 /* temporary register 1 */ + pte = r16 /* pte/phys page # */ + prot = r17 /* prot bits */ + spc = r24 /* space for which the trap occured */ + ptp = r25 /* page directory/page table pointer */ + +#ifdef __LP64__ + +dtlb_miss_20w: + + extrd,u spc,31,7,t1 /* adjust va */ + depd t1,31,7,va /* adjust va */ + depdi 0,31,7,spc /* adjust space */ + mfctl %cr25,ptp /* Assume user space miss */ + or,*<> %r0,spc,%r0 /* If it is user space, nullify */ + mfctl %cr24,ptp /* Load kernel pgd instead */ + extrd,u va,33,9,t1 /* Get pgd index */ + + mfsp %sr7,t0 /* Get current space */ + or,*= %r0,t0,%r0 /* If kernel, nullify following test */ + comb,<>,n t0,spc,dtlb_fault /* forward */ + + /* First level page table lookup */ + + ldd,s t1(ptp),ptp + extrd,u va,42,9,t0 /* get second-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,dtlb_fault + depdi 0,63,12,ptp /* clear prot bits */ + + /* Second level page table lookup */ + + ldd,s t0(ptp),ptp + extrd,u va,51,9,t0 /* get third-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,dtlb_fault + depdi 0,63,12,ptp /* clear prot bits */ + + /* Third level page table lookup */ + + shladd t0,3,ptp,ptp + ldi _PAGE_ACCESSED,t1 + ldd 0(ptp),pte + bb,>=,n pte,_PAGE_PRESENT_BIT,dtlb_fault + + /* Check whether the "accessed" bit was set, otherwise do so */ + + or t1,pte,t0 /* t0 has R bit set */ + and,*<> t1,pte,%r0 /* test and nullify if already set */ + std t0,0(ptp) /* write back pte */ + + copy spc,prot /* init prot with faulting space */ + + depd pte,8,7,prot + extrd,u,*= pte,_PAGE_NO_CACHE_BIT+32,1,r0 + depdi 1,12,1,prot + extrd,u,*= pte,_PAGE_USER_BIT+32,1,r0 + depdi 7,11,3,prot /* Set for user space (1 rsvd for read) */ + extrd,u,*= pte,_PAGE_GATEWAY_BIT+32,1,r0 + depdi 0,11,2,prot /* If Gateway, Set PL2 to 0 */ + + /* Get rid of prot bits and convert to page addr for idtlbt */ + + depdi 0,63,12,pte + extrd,u pte,56,32,pte + idtlbt %r16,%r17 + + rfir + nop +#else + +dtlb_miss_11: + mfctl %cr25,ptp /* Assume user space miss */ + or,<> %r0,spc,%r0 /* If it is user space, nullify */ + mfctl %cr24,ptp /* Load kernel pgd instead */ + extru va,9,10,t1 /* Get pgd index */ + + mfsp %sr7,t0 /* Get current space */ + or,= %r0,t0,%r0 /* If kernel, nullify following test */ + comb,<>,n t0,spc,dtlb_fault /* forward */ + + /* First level page table lookup */ + + ldwx,s t1(ptp),ptp + extru va,19,10,t0 /* get second-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,dtlb_fault + depi 0,31,12,ptp /* clear prot bits */ + + /* Second level page table lookup */ + + sh2addl t0,ptp,ptp + ldi _PAGE_ACCESSED,t1 + ldw 0(ptp),pte + bb,>=,n pte,_PAGE_PRESENT_BIT,dtlb_fault + + /* Check whether the "accessed" bit was set, otherwise do so */ + + or t1,pte,t0 /* t0 has R bit set */ + and,<> t1,pte,%r0 /* test and nullify if already set */ + stw t0,0(ptp) /* write back pte */ + + copy spc,prot /* init prot with faulting space */ + dep pte,8,7,prot + + extru,= pte,_PAGE_NO_CACHE_BIT,1,r0 + depi 1,12,1,prot + extru,= pte,_PAGE_USER_BIT,1,r0 + depi 7,11,3,prot /* Set for user space (1 rsvd for read) */ + extru,= pte,_PAGE_GATEWAY_BIT,1,r0 + depi 0,11,2,prot /* If Gateway, Set PL2 to 0 */ + + /* Get rid of prot bits and convert to page addr for idtlba */ + + depi 0,31,12,pte + extru pte,24,25,pte + + mfsp %sr1,t0 /* Save sr1 so we can use it in tlb inserts */ + mtsp spc,%sr1 + + idtlba pte,(%sr1,va) + idtlbp prot,(%sr1,va) + + mtsp t0, %sr1 /* Restore sr1 */ + + rfir + nop + +dtlb_miss_20: + .level 2.0 + + mfctl %cr25,ptp /* Assume user space miss */ + or,<> %r0,spc,%r0 /* If it is user space, nullify */ + mfctl %cr24,ptp /* Load kernel pgd instead */ + extru va,9,10,t1 /* Get pgd index */ + + mfsp %sr7,t0 /* Get current space */ + or,= %r0,t0,%r0 /* If kernel, nullify following test */ + comb,<>,n t0,spc,dtlb_fault /* forward */ + + /* First level page table lookup */ + + ldwx,s t1(ptp),ptp + extru va,19,10,t0 /* get second-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,dtlb_fault + depi 0,31,12,ptp /* clear prot bits */ + + /* Second level page table lookup */ + + sh2addl t0,ptp,ptp + ldi _PAGE_ACCESSED,t1 + ldw 0(ptp),pte + bb,>=,n pte,_PAGE_PRESENT_BIT,dtlb_fault + + /* Check whether the "accessed" bit was set, otherwise do so */ + + or t1,pte,t0 /* t0 has R bit set */ + and,<> t1,pte,%r0 /* test and nullify if already set */ + stw t0,0(ptp) /* write back pte */ + + copy spc,prot /* init prot with faulting space */ + + depd pte,8,7,prot + extrd,u,*= pte,_PAGE_NO_CACHE_BIT+32,1,r0 + depdi 1,12,1,prot + extrd,u,*= pte,_PAGE_USER_BIT+32,1,r0 + depdi 7,11,3,prot /* Set for user space (1 rsvd for read) */ + extrd,u,*= pte,_PAGE_GATEWAY_BIT+32,1,r0 + depdi 0,11,2,prot /* If Gateway, Set PL2 to 0 */ + + /* Get rid of prot bits and convert to page addr for idtlbt */ + + depdi 0,63,12,pte + extrd,u pte,56,25,pte + idtlbt %r16,%r17 + + .level 1.1 + + rfir + nop +#endif + +#ifdef __LP64__ +itlb_miss_20w: + + /* + * I miss is a little different, since we allow users to fault + * on the gateway page which is in the kernel address space. + */ + + extrd,u spc,31,7,t1 /* adjust va */ + depd t1,31,7,va /* adjust va */ + depdi 0,31,7,spc /* adjust space */ + cmpib,*= 0,spc,itlb_miss_kernel_20w + extrd,u va,33,9,t1 /* Get pgd index */ + + mfctl %cr25,ptp /* load user pgd */ + + mfsp %sr7,t0 /* Get current space */ + or,*= %r0,t0,%r0 /* If kernel, nullify following test */ + cmpb,*<>,n t0,spc,itlb_fault /* forward */ + + /* First level page table lookup */ + +itlb_miss_common_20w: + ldd,s t1(ptp),ptp + extrd,u va,42,9,t0 /* get second-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,itlb_fault + depdi 0,63,12,ptp /* clear prot bits */ + + /* Second level page table lookup */ + + ldd,s t0(ptp),ptp + extrd,u va,51,9,t0 /* get third-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,itlb_fault + depdi 0,63,12,ptp /* clear prot bits */ + + /* Third level page table lookup */ + + shladd t0,3,ptp,ptp + ldi _PAGE_ACCESSED,t1 + ldd 0(ptp),pte + bb,>=,n pte,_PAGE_PRESENT_BIT,itlb_fault + + /* Check whether the "accessed" bit was set, otherwise do so */ + + or t1,pte,t0 /* t0 has R bit set */ + and,*<> t1,pte,%r0 /* test and nullify if already set */ + std t0,0(ptp) /* write back pte */ + + copy spc,prot /* init prot with faulting space */ + + depd pte,8,7,prot + extrd,u,*= pte,_PAGE_NO_CACHE_BIT+32,1,r0 + depdi 1,12,1,prot + extrd,u,*= pte,_PAGE_USER_BIT+32,1,r0 + depdi 7,11,3,prot /* Set for user space (1 rsvd for read) */ + extrd,u,*= pte,_PAGE_GATEWAY_BIT+32,1,r0 + depdi 0,11,2,prot /* If Gateway, Set PL2 to 0 */ + + /* Get rid of prot bits and convert to page addr for iitlbt */ + + depdi 0,63,12,pte + extrd,u pte,56,32,pte + iitlbt %r16,%r17 + + rfir + nop + +itlb_miss_kernel_20w: + b itlb_miss_common_20w + mfctl %cr24,ptp /* Load kernel pgd */ +#else + +itlb_miss_11: + + /* + * I miss is a little different, since we allow users to fault + * on the gateway page which is in the kernel address space. + */ + + comib,= 0,spc,itlb_miss_kernel_11 + extru va,9,10,t1 /* Get pgd index */ + + mfctl %cr25,ptp /* load user pgd */ + + mfsp %sr7,t0 /* Get current space */ + or,= %r0,t0,%r0 /* If kernel, nullify following test */ + comb,<>,n t0,spc,itlb_fault /* forward */ + + /* First level page table lookup */ + +itlb_miss_common_11: + ldwx,s t1(ptp),ptp + extru va,19,10,t0 /* get second-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,itlb_fault + depi 0,31,12,ptp /* clear prot bits */ + + /* Second level page table lookup */ + + sh2addl t0,ptp,ptp + ldi _PAGE_ACCESSED,t1 + ldw 0(ptp),pte + bb,>=,n pte,_PAGE_PRESENT_BIT,itlb_fault + + /* Check whether the "accessed" bit was set, otherwise do so */ + + or t1,pte,t0 /* t0 has R bit set */ + and,<> t1,pte,%r0 /* test and nullify if already set */ + stw t0,0(ptp) /* write back pte */ + + copy spc,prot /* init prot with faulting space */ + dep pte,8,7,prot + + extru,= pte,_PAGE_NO_CACHE_BIT,1,r0 + depi 1,12,1,prot + extru,= pte,_PAGE_USER_BIT,1,r0 + depi 7,11,3,prot /* Set for user space (1 rsvd for read) */ + extru,= pte,_PAGE_GATEWAY_BIT,1,r0 + depi 0,11,2,prot /* If Gateway, Set PL2 to 0 */ + + /* Get rid of prot bits and convert to page addr for iitlba */ + + depi 0,31,12,pte + extru pte,24,25,pte + + mfsp %sr1,t0 /* Save sr1 so we can use it in tlb inserts */ + mtsp spc,%sr1 + + iitlba pte,(%sr1,va) + iitlbp prot,(%sr1,va) + + mtsp t0, %sr1 /* Restore sr1 */ + + rfir + nop + +itlb_miss_kernel_11: + b itlb_miss_common_11 + mfctl %cr24,ptp /* Load kernel pgd */ + +itlb_miss_20: + + /* + * I miss is a little different, since we allow users to fault + * on the gateway page which is in the kernel address space. + */ + + comib,= 0,spc,itlb_miss_kernel_20 + extru va,9,10,t1 /* Get pgd index */ + + mfctl %cr25,ptp /* load user pgd */ + + mfsp %sr7,t0 /* Get current space */ + or,= %r0,t0,%r0 /* If kernel, nullify following test */ + comb,<>,n t0,spc,itlb_fault /* forward */ + + /* First level page table lookup */ + +itlb_miss_common_20: + ldwx,s t1(ptp),ptp + extru va,19,10,t0 /* get second-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,itlb_fault + depi 0,31,12,ptp /* clear prot bits */ + + /* Second level page table lookup */ + + sh2addl t0,ptp,ptp + ldi _PAGE_ACCESSED,t1 + ldw 0(ptp),pte + bb,>=,n pte,_PAGE_PRESENT_BIT,itlb_fault + + /* Check whether the "accessed" bit was set, otherwise do so */ + + or t1,pte,t0 /* t0 has R bit set */ + and,<> t1,pte,%r0 /* test and nullify if already set */ + stw t0,0(ptp) /* write back pte */ + + copy spc,prot /* init prot with faulting space */ + + .level 2.0 + + depd pte,8,7,prot + extrd,u,*= pte,_PAGE_NO_CACHE_BIT+32,1,r0 + depdi 1,12,1,prot + extrd,u,*= pte,_PAGE_USER_BIT+32,1,r0 + depdi 7,11,3,prot /* Set for user space (1 rsvd for read) */ + extrd,u,*= pte,_PAGE_GATEWAY_BIT+32,1,r0 + depdi 0,11,2,prot /* If Gateway, Set PL2 to 0 */ + + /* Get rid of prot bits and convert to page addr for iitlbt */ + + depdi 0,63,12,pte + extrd,u pte,56,25,pte + iitlbt %r16,%r17 + .level 1.1 + + rfir + nop + + +itlb_miss_kernel_20: + b itlb_miss_common_20 + mfctl %cr24,ptp /* Load kernel pgd */ +#endif + +#ifdef __LP64__ + +dbit_trap_20w: + + extrd,u spc,31,7,t1 /* adjust va */ + depd t1,31,7,va /* adjust va */ + depdi 0,1,2,va /* adjust va */ + depdi 0,31,7,spc /* adjust space */ + mfctl %cr25,ptp /* Assume user space miss */ + or,*<> %r0,spc,%r0 /* If it is user space, nullify */ + mfctl %cr24,ptp /* Load kernel pgd instead */ + extrd,u va,33,9,t1 /* Get pgd index */ + + mfsp %sr7,t0 /* Get current space */ + or,*= %r0,t0,%r0 /* If kernel, nullify following test */ + comb,<>,n t0,spc,dbit_fault /* forward */ + + /* First level page table lookup */ + + ldd,s t1(ptp),ptp + extrd,u va,42,9,t0 /* get second-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,dbit_fault + depdi 0,63,12,ptp /* clear prot bits */ + + /* Second level page table lookup */ + + ldd,s t0(ptp),ptp + extrd,u va,51,9,t0 /* get third-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,dbit_fault + depdi 0,63,12,ptp /* clear prot bits */ + + /* Third level page table lookup */ + + shladd t0,3,ptp,ptp + ldi (_PAGE_ACCESSED|_PAGE_DIRTY),t1 + ldd 0(ptp),pte + bb,>=,n pte,_PAGE_PRESENT_BIT,dbit_fault + + /* Set Accessed and Dirty bits in the pte */ + + or t1,pte,pte + std pte,0(ptp) /* write back pte */ + + copy spc,prot /* init prot with faulting space */ + + depd pte,8,7,prot + extrd,u,*= pte,_PAGE_NO_CACHE_BIT+32,1,r0 + depdi 1,12,1,prot + extrd,u,*= pte,_PAGE_USER_BIT+32,1,r0 + depdi 7,11,3,prot /* Set for user space (1 rsvd for read) */ + extrd,u,*= pte,_PAGE_GATEWAY_BIT+32,1,r0 + depdi 0,11,2,prot /* If Gateway, Set PL2 to 0 */ + + /* Get rid of prot bits and convert to page addr for idtlbt */ + + depdi 0,63,12,pte + extrd,u pte,56,32,pte + idtlbt %r16,%r17 + + rfir + nop +#else + +dbit_trap_11: + mfctl %cr25,ptp /* Assume user space trap */ + or,<> %r0,spc,%r0 /* If it is user space, nullify */ + mfctl %cr24,ptp /* Load kernel pgd instead */ + extru va,9,10,t1 /* Get pgd index */ + + mfsp %sr7,t0 /* Get current space */ + or,= %r0,t0,%r0 /* If kernel, nullify following test */ + comb,<>,n t0,spc,dbit_fault /* forward */ + + /* First level page table lookup */ + + ldwx,s t1(ptp),ptp + extru va,19,10,t0 /* get second-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,dbit_fault + depi 0,31,12,ptp /* clear prot bits */ + + /* Second level page table lookup */ + + sh2addl t0,ptp,ptp + ldi (_PAGE_ACCESSED|_PAGE_DIRTY),t1 + ldw 0(ptp),pte + bb,>=,n pte,_PAGE_PRESENT_BIT,dbit_fault + + /* Set Accessed and Dirty bits in the pte */ + + or t1,pte,pte + stw pte,0(ptp) /* write back pte */ + + copy spc,prot /* init prot with faulting space */ + dep pte,8,7,prot + + extru,= pte,_PAGE_NO_CACHE_BIT,1,r0 + depi 1,12,1,prot + extru,= pte,_PAGE_USER_BIT,1,r0 + depi 7,11,3,prot /* Set for user space (1 rsvd for read) */ + extru,= pte,_PAGE_GATEWAY_BIT,1,r0 + depi 0,11,2,prot /* If Gateway, Set PL2 to 0 */ + + /* Get rid of prot bits and convert to page addr for idtlba */ + + depi 0,31,12,pte + extru pte,24,25,pte + + mfsp %sr1,t0 /* Save sr1 so we can use it in tlb inserts */ + mtsp spc,%sr1 + + idtlba pte,(%sr1,va) + idtlbp prot,(%sr1,va) + + mtsp t0, %sr1 /* Restore sr1 */ + + rfir + nop + +dbit_trap_20: + mfctl %cr25,ptp /* Assume user space trap */ + or,<> %r0,spc,%r0 /* If it is user space, nullify */ + mfctl %cr24,ptp /* Load kernel pgd instead */ + extru va,9,10,t1 /* Get pgd index */ + + mfsp %sr7,t0 /* Get current space */ + or,= %r0,t0,%r0 /* If kernel, nullify following test */ + comb,<>,n t0,spc,dbit_fault /* forward */ + + /* First level page table lookup */ + + ldwx,s t1(ptp),ptp + extru va,19,10,t0 /* get second-level index */ + bb,>=,n ptp,_PAGE_PRESENT_BIT,dbit_fault + depi 0,31,12,ptp /* clear prot bits */ + + /* Second level page table lookup */ + + sh2addl t0,ptp,ptp + ldi (_PAGE_ACCESSED|_PAGE_DIRTY),t1 + ldw 0(ptp),pte + bb,>=,n pte,_PAGE_PRESENT_BIT,dbit_fault + + /* Set Accessed and Dirty bits in the pte */ + + or t1,pte,pte + stw pte,0(ptp) /* write back pte */ + + copy spc,prot /* init prot with faulting space */ + + .level 2.0 + + depd pte,8,7,prot + extrd,u,*= pte,_PAGE_NO_CACHE_BIT+32,1,r0 + depdi 1,12,1,prot + extrd,u,*= pte,_PAGE_USER_BIT+32,1,r0 + depdi 7,11,3,prot /* Set for user space (1 rsvd for read) */ + extrd,u,*= pte,_PAGE_GATEWAY_BIT+32,1,r0 + depdi 0,11,2,prot /* If Gateway, Set PL2 to 0 */ + + /* Get rid of prot bits and convert to page addr for idtlbt */ + + depdi 0,63,12,pte + extrd,u pte,56,25,pte + idtlbt %r16,%r17 + + .level 1.1 + + rfir + nop +#endif + + .import handle_interruption,code + +kernel_bad_space: + b tlb_fault + ldi 31,%r1 /* Use an unused code */ + +dbit_fault: + b tlb_fault + ldi 20,%r1 + +itlb_fault: + b tlb_fault + ldi 6,%r1 + +dtlb_fault: + ldi 15,%r1 + + /* Fall Through */ + +tlb_fault: + mtctl %r1,%cr29 + mtctl %r29,%cr31 + + get_stack + save_specials %r29 /* Note this saves a trashed r1 */ + + SAVE_CR (%cr20, PT_ISR(%r29)) + SAVE_CR (%cr21, PT_IOR(%r29)) + + virt_map rfir + + STREG %r1,PT_GR1(%r29) /* save good value after rfir */ + + save_general %r29 + + ldo PT_FR0(%r29), %r25 + save_fp %r25 + + loadgp + + copy %r29, %r25 + + bl handle_interruption, %r2 + copy %r29, %r16 + + b intr_return + nop + + /* Register saving semantics for system calls: + + %r1 clobbered by system call macro in userspace + %r2 saved in PT_REGS by gateway page + %r3 - %r18 preserved by C code (saved by signal code) + %r19 - %r20 saved in PT_REGS by gateway page + %r21 - %r22 non-standard syscall args + stored in kernel stack by gateway page + %r23 - %r26 arg3-arg0, saved in PT_REGS by gateway page + %r27 - %r30 saved in PT_REGS by gateway page + %r31 syscall return pointer + */ + + /* Floating point registers (FIXME: what do we do with these?) + + %fr0 - %fr3 status/exception, not preserved + %fr4 - %fr7 arguments + %fr8 - %fr11 not preserved by C code + %fr12 - %fr21 preserved by C code + %fr22 - %fr31 not preserved by C code + */ + + .macro reg_save regs + STREG %r3, PT_GR3(\regs) + STREG %r4, PT_GR4(\regs) + STREG %r5, PT_GR5(\regs) + STREG %r6, PT_GR6(\regs) + STREG %r7, PT_GR7(\regs) + STREG %r8, PT_GR8(\regs) + STREG %r9, PT_GR9(\regs) + STREG %r10,PT_GR10(\regs) + STREG %r11,PT_GR11(\regs) + STREG %r12,PT_GR12(\regs) + STREG %r13,PT_GR13(\regs) + STREG %r14,PT_GR14(\regs) + STREG %r15,PT_GR15(\regs) + STREG %r16,PT_GR16(\regs) + STREG %r17,PT_GR17(\regs) + STREG %r18,PT_GR18(\regs) + .endm + + .macro reg_restore regs + LDREG PT_GR3(\regs), %r3 + LDREG PT_GR4(\regs), %r4 + LDREG PT_GR5(\regs), %r5 + LDREG PT_GR6(\regs), %r6 + LDREG PT_GR7(\regs), %r7 + LDREG PT_GR8(\regs), %r8 + LDREG PT_GR9(\regs), %r9 + LDREG PT_GR10(\regs),%r10 + LDREG PT_GR11(\regs),%r11 + LDREG PT_GR12(\regs),%r12 + LDREG PT_GR13(\regs),%r13 + LDREG PT_GR14(\regs),%r14 + LDREG PT_GR15(\regs),%r15 + LDREG PT_GR16(\regs),%r16 + LDREG PT_GR17(\regs),%r17 + LDREG PT_GR18(\regs),%r18 + .endm + + .export sys_fork_wrapper +sys_fork_wrapper: + ldo TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get pt regs */ + reg_save %r1 + + STREG %r2,-RP_OFFSET(%r30) + ldo FRAME_SIZE(%r30),%r30 + + /* These are call-clobbered registers and therefore + also syscall-clobbered (we hope). */ + STREG %r2,PT_GR19(%r1) /* save for child */ + STREG %r30,PT_GR20(%r1) + ldil L%child_return, %r3 + ldo R%child_return(%r3), %r3 + LDIL_FIXUP(%r3) + STREG %r3,PT_GR21(%r1) /* save for child */ + + LDREG PT_GR30(%r1),%r25 + copy %r1,%r24 + bl sys_clone,%r2 + ldi SIGCHLD,%r26 + + LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2 +wrapper_exit: + ldo -FRAME_SIZE(%r30),%r30 /* get the stackframe */ + ldo TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get pt regs */ + + reg_restore %r1 + + bv %r0(%r2) + nop + + /* Set the return value for the child */ +child_return: + LDREG TASK_PT_GR19-TASK_SZ_ALGN-128(%r30),%r2 + b wrapper_exit + copy %r0,%r28 + + + .export sys_clone_wrapper +sys_clone_wrapper: + ldo TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get pt regs */ + reg_save %r1 + + STREG %r2,-RP_OFFSET(%r30) + ldo FRAME_SIZE(%r30),%r30 + + STREG %r30,PT_GR20(%r1) + ldil L%child_return,%r3 + ldo R%child_return(%r3),%r3 + LDIL_FIXUP(%r3) + + bl sys_clone,%r2 + STREG %r3,PT_GR21(%r1) /* save for child */ + + b wrapper_exit + LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2 + + + .export sys_vfork_wrapper +sys_vfork_wrapper: + ldo TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get pt regs */ + reg_save %r1 + + STREG %r2,-RP_OFFSET(%r30) + ldo FRAME_SIZE(%r30),%r30 + + STREG %r30,PT_GR20(%r1) + ldil L%child_return,%r3 + ldo R%child_return(%r3),%r3 + LDIL_FIXUP(%r3) + STREG %r3,PT_GR21(%r1) /* save for child */ + + bl sys_vfork,%r2 + copy %r1,%r26 + + b wrapper_exit + LDREG -RP_OFFSET-FRAME_SIZE(%r30),%r2 + + + .macro execve_wrapper execve + ldo TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get pt regs */ + + /* + * Do we need to save/restore r3-r18 here? + * I don't think so. why would new thread need old + * threads registers? + */ + + /* %arg0 - %arg3 are already saved for us. */ + + STREG %r2,-RP_OFFSET(%r30) + ldo FRAME_SIZE(%r30),%r30 + bl \execve,%r2 + copy %r1,%arg0 + + ldo -FRAME_SIZE(%r30),%r30 + LDREG -RP_OFFSET(%r30),%r2 + + /* If exec succeeded we need to load the args */ + + ldo -1024(%r0),%r1 + comb,>>= %r28,%r1,error_\execve + copy %r2,%r19 + +error_\execve: + bv %r0(%r19) + nop + .endm + + .export sys_execve_wrapper + .import sys_execve + +sys_execve_wrapper: + execve_wrapper sys_execve + +#ifdef __LP64__ + .export sys32_execve_wrapper + .import sys32_execve + +sys32_execve_wrapper: + execve_wrapper sys32_execve +#endif + + .export sys_rt_sigreturn_wrapper +sys_rt_sigreturn_wrapper: + ldo TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30), %r26 + /* Don't save regs, we are going to restore them from sigcontext. */ + STREG %r2, -RP_OFFSET(%r30) + bl sys_rt_sigreturn,%r2 + ldo FRAME_SIZE(%r30), %r30 + + ldo -FRAME_SIZE(%r30), %r30 + LDREG -RP_OFFSET(%r30), %r2 + + /* FIXME: I think we need to restore a few more things here. */ + ldo TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get pt regs */ + reg_restore %r1 + + /* If the signal was received while the process was blocked on a + * syscall, then r2 will take us to syscall_exit; otherwise r2 will + * take us to syscall_exit_rfi and on to intr_return. + */ + bv %r0(%r2) + LDREG PT_GR28(%r1),%r28 /* reload original r28 for syscall_exit */ + + .export sys_sigaltstack_wrapper +sys_sigaltstack_wrapper: + /* Get the user stack pointer */ + LDREG -TASK_SZ_ALGN-FRAME_SIZE+TASK_PT_GR30(%r30), %r24 + STREG %r2, -RP_OFFSET(%r30) + bl do_sigaltstack,%r2 + ldo FRAME_SIZE(%r30), %r30 + + ldo -FRAME_SIZE(%r30), %r30 + LDREG -RP_OFFSET(%r30), %r2 + bv %r0(%r2) + nop + + .export sys_rt_sigsuspend_wrapper +sys_rt_sigsuspend_wrapper: + ldo TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30), %r24 + reg_save %r24 + + STREG %r2, -RP_OFFSET(%r30) + bl sys_rt_sigsuspend,%r2 + ldo FRAME_SIZE(%r30), %r30 + + ldo -FRAME_SIZE(%r30), %r30 + LDREG -RP_OFFSET(%r30), %r2 + + ldo TASK_REGS-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1 + reg_restore %r1 + + bv %r0(%r2) + nop + + .export syscall_exit +syscall_exit: + /* NOTE: HP-UX syscalls also come through here + after hpux_syscall_exit fixes up return + values. */ + /* NOTE: Not all syscalls exit this way. rt_sigreturn will exit + * via syscall_exit_rfi if the signal was received while the process + * was running. All traced processes will probably exit via + * syscall_exit_rfi in the future. + */ + + /* save return value now */ + + STREG %r28,TASK_PT_GR28-TASK_SZ_ALGN-FRAME_SIZE(%r30) + +syscall_check_bh: + +/* #ifdef NOTNOW */ + /* Check for software interrupts */ + + .import irq_stat,data + + ldil L%irq_stat,%r19 + ldo R%irq_stat(%r19),%r19 + LDIL_FIXUP(%r19) + +#ifdef CONFIG_SMP + /* sched.h: int processor */ + ldw TASK_PROCESSOR-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r20 /* get cpu # */ +#if (IRQSTAT_SZ == 32) + dep %r20,26,27,%r20 /* shift left 5 bits */ +#else +#error IRQSTAT_SZ changed, fix dep +#endif /* IRQSTAT_SZ */ + add %r19,%r20,%r19 +#endif /* CONFIG_SMP */ + + ldw IRQSTAT_SI_ACTIVE(%r19),%r20 /* hardirq.h: unsigned int */ + ldw IRQSTAT_SI_MASK(%r19),%r19 /* hardirq.h: unsigned int */ + and %r19,%r20,%r20 + comib,<>,n 0,%r20,syscall_do_softirq /* forward */ +/* #endif */ + + +syscall_check_resched: + + /* check for reschedule */ + + LDREG TASK_NEED_RESCHED-TASK_SZ_ALGN-FRAME_SIZE(%r30),%r19 /* long */ + comib,<>,n 0,%r19,syscall_do_resched /* forward */ + +syscall_check_sig: + ldo -TASK_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ + /* check for pending signals */ + ldw TASK_SIGPENDING(%r1),%r19 + comib,<>,n 0,%r19,syscall_do_signal /* forward */ + +syscall_restore: + /* disable interrupts while dicking with the kernel stack, */ + /* or life can become unpleasant */ + rsm PSW_SM_I, %r20 + LDREG TASK_PTRACE(%r1), %r19 /* Are we being ptraced? */ + bb,<,n %r19,31,syscall_restore_rfi + LDREG TASK_PT_GR20(%r1),%r19 + mtctl %r19, %cr27 + + LDREG TASK_PT_GR2(%r1),%r2 /* restore user rp */ + LDREG TASK_PT_GR21(%r1),%r21 + LDREG TASK_PT_GR22(%r1),%r22 + LDREG TASK_PT_GR23(%r1),%r23 + LDREG TASK_PT_GR24(%r1),%r24 + LDREG TASK_PT_GR25(%r1),%r25 + LDREG TASK_PT_GR26(%r1),%r26 + LDREG TASK_PT_GR27(%r1),%r27 /* restore user dp */ + LDREG TASK_PT_GR28(%r1),%r28 /* syscall return value */ + LDREG TASK_PT_GR29(%r1),%r29 + LDREG TASK_PT_GR30(%r1),%r30 /* restore user sp */ + LDREG TASK_PT_GR31(%r1),%r31 /* restore syscall rp */ + ldo TASK_PT_FR31(%r1),%r19 /* reload fpregs */ + rest_fp %r19 + LDREG TASK_PT_SAR(%r1),%r19 /* restore SAR */ + mtsar %r19 + LDREG TASK_PT_GR19(%r1),%r19 + + mtctl %r1,%cr30 /* intrhandler okay. */ + mfsp %sr3,%r1 /* Get users space id */ + mtsp %r1,%sr4 /* Restore sr4 */ + mtsp %r1,%sr5 /* Restore sr5 */ + mtsp %r1,%sr6 /* Restore sr6 */ + + depi 3,31,2,%r31 /* ensure return to user mode. */ + + mtsm %r20 /* restore irq state */ + mfctl %cr27,%r20 + + /* + * Due to a dependency in the tlb miss handlers on sr7, it + * is essential that sr7 get set in the delay slot. + */ + +#ifdef __LP64__ + + /* Note the be (and mtsp) is executed in narrow mode. This is OK + * for 32 bit processes, but won't work once we support 64 bit + * processes. + */ + + rsm PSW_SM_W, %r0 + be 0(%sr3,%r31) /* return to user space */ + mtsp %r1,%sr7 /* Restore sr7 */ +#else + be 0(%sr3,%r31) /* return to user space */ + mtsp %r1,%sr7 /* Restore sr7 */ +#endif + + /* We have to return via an RFI, so that PSW T and R bits can be set + * appropriately. + * This sets up pt_regs so we can return via intr_restore, which is not + * the most efficient way of doing things, but it works. + */ +syscall_restore_rfi: + ldo -1(%r0),%r2 /* Set recovery cntr to -1 */ + mtctl %r2,%cr0 /* for immediate trap */ + copy %r0,%r2 /* Create a reasonable PSW */ + /* XXX W bit??? */ + depi -1,13,1,%r2 + depi -1,28,1,%r2 + depi -1,30,1,%r2 + depi -1,31,1,%r2 + bb,<,n %r19,15,set_rbit /* PT_SINGLESTEP */ + bb,>=,n %r19,14,set_nobit /* PT_BLOCKSTEP, see ptrace.c */ +set_tbit: + depi -1,7,1,%r2 + b,n set_nobit +set_rbit: + depi -1,27,1,%r2 +set_nobit: + STREG %r2,TASK_PT_PSW(%r1) + STREG %r1,TASK_PT_CR30(%r1) + mfsp %sr0,%r2 + STREG %r2,TASK_PT_SR0(%r1) + mfsp %sr1,%r2 + STREG %r2,TASK_PT_SR1(%r1) + mfsp %sr2,%r2 + STREG %r2,TASK_PT_SR2(%r1) + mfsp %sr3,%r2 + STREG %r2,TASK_PT_SR3(%r1) + STREG %r2,TASK_PT_SR4(%r1) + STREG %r2,TASK_PT_SR5(%r1) + STREG %r2,TASK_PT_SR6(%r1) + STREG %r2,TASK_PT_SR7(%r1) + STREG %r2,TASK_PT_IASQ0(%r1) + STREG %r2,TASK_PT_IASQ1(%r1) + LDREG TASK_PT_GR31(%r1),%r2 + depi 3,31,2,%r2 /* ensure return to user mode. */ + STREG %r2,TASK_PT_IAOQ0(%r1) + ldo 4(%r2),%r2 + STREG %r2,TASK_PT_IAOQ1(%r1) + ldo TASK_REGS(%r1),%r25 + reg_save %r25 /* Save r3 to r18 */ + copy %r25,%r16 + b intr_restore + nop + + .import do_softirq,code +syscall_do_softirq: + bl do_softirq,%r2 + nop + b syscall_check_resched + ssm PSW_SM_I, %r0 /* do_softirq returns with I bit off */ + + .import schedule,code +syscall_do_resched: + bl schedule,%r2 + nop + b syscall_check_bh /* if resched, we start over again */ + nop + + .import do_signal,code +syscall_do_signal: + /* Save callee-save registers (for sigcontext). + FIXME: After this point the process structure should be + consistent with all the relevant state of the process + before the syscall. We need to verify this. */ + ldo TASK_REGS(%r1), %r25 /* struct pt_regs *regs */ + reg_save %r25 + + ldi 1, %r24 /* unsigned long in_syscall */ + + bl do_signal,%r2 + copy %r0, %r26 /* sigset_t *oldset = NULL */ + + ldo -TASK_SZ_ALGN-FRAME_SIZE(%r30), %r1 /* reload task ptr */ + ldo TASK_REGS(%r1), %r20 /* reload pt_regs */ + reg_restore %r20 + + b,n syscall_restore + +#ifdef __LP64__ +unimplemented_64bitirq: + ssm PSW_SM_Q+PSW_SM_I, %r0 + /* indicate that we had an interrupt */ + ldi 0x77, %r28 + ldi 0x77, %r29 + /* save interrupt registers in GRs for diagnosis */ + mfctl %cr17, %r17 + mfctl %cr18, %r18 + mfctl %cr19, %r19 + mfctl %cr20, %r20 + mfctl %cr21, %r21 + mfctl %cr22, %r22 + b,n . + nop +#endif diff --git a/arch/parisc/kernel/hardware.c b/arch/parisc/kernel/hardware.c new file mode 100644 index 000000000000..75d5c66950fc --- /dev/null +++ b/arch/parisc/kernel/hardware.c @@ -0,0 +1,1446 @@ +/* + * Hardware descriptions for HP 9000 based hardware, including + * system types, SCSI controllers, DMA controllers, HPPB controllers + * and lots more. + * + * Based on the document "PA-RISC 1.1 I/O Firmware Architecture + * Reference Specification", March 7, 1999, version 0.96. This + * is available at ?. + * + * Copyright 1999 by Alex deVries + * and copyright 1999 The Puffin Group Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include + +#define HPHW_NUM_TYPES 3431 + +static char * hw_type_name[16] = { + "Processor", + "Memory", + "B DMA", + "Obsolete", + "A DMA", + "A Direct", + "Obsolete", + "Bus Converter Port", + "HP CIO Adapter", + "Console", + "Foreign I/O Module", + "Bus Adapter", + "IOA (?)", + "Bus Bridge to Foreign Bus", + "HP Clothing: Fabric Component" +}; + +/* + * XXX Could this be __init ?? + */ + +static struct hp_hardware hp_hardware_list[] = { + {HPHW_NPROC,0x01,0x4,0x0,"Indigo (840, 930)"}, + {HPHW_NPROC,0x8,0x4,0x01,"Firefox(825,925)"}, + {HPHW_NPROC,0xA,0x4,0x01,"Top Gun (835,834,935,635)"}, + {HPHW_NPROC,0xB,0x4,0x01,"Technical Shogun (845, 645)"}, + {HPHW_NPROC,0xF,0x4,0x01,"Commercial Shogun (949)"}, + {HPHW_NPROC,0xC,0x4,0x01,"Cheetah (850, 950)"}, + {HPHW_NPROC,0x80,0x4,0x01,"Cheetah (950S)"}, + {HPHW_NPROC,0x81,0x4,0x01,"Jaguar (855, 955)"}, + {HPHW_NPROC,0x82,0x4,0x01,"Cougar (860, 960)"}, + {HPHW_NPROC,0x83,0x4,0x13,"Panther (865, 870, 980)"}, + {HPHW_NPROC,0x100,0x4,0x01,"Burgundy (810)"}, + {HPHW_NPROC,0x101,0x4,0x01,"SilverFox Low (822, 922)"}, + {HPHW_NPROC,0x102,0x4,0x01,"SilverFox High (832, 932)"}, + {HPHW_NPROC,0x103,0x4,0x01,"Lego, SilverLite (815, 808, 920)"}, + {HPHW_NPROC,0x104,0x4,0x03,"SilverBullet Low (842, 948)"}, + {HPHW_NPROC,0x105,0x4,0x03,"SilverBullet High (852, 958)"}, + {HPHW_NPROC,0x106,0x4,0x81,"Oboe"}, + {HPHW_NPROC,0x180,0x4,0x12,"Dragon"}, + {HPHW_NPROC,0x181,0x4,0x13,"Chimera (890, 990, 992)"}, + {HPHW_NPROC,0x182,0x4,0x91,"TNT 100 (891,T500)"}, + {HPHW_NPROC,0x183,0x4,0x91,"TNT 120 (892,T520)"}, + {HPHW_NPROC,0x184,0x4,0x91,"Jade 180 U (893,T540)"}, + {HPHW_NPROC,0x1FF,0x4,0x91,"Hitachi X Processor"}, + {HPHW_NPROC,0x200,0x4,0x81,"Cobra (720)"}, + {HPHW_NPROC,0x201,0x4,0x81,"Coral (750)"}, + {HPHW_NPROC,0x202,0x4,0x81,"King Cobra (730)"}, + {HPHW_NPROC,0x203,0x4,0x81,"Hardball (735/99)"}, + {HPHW_NPROC,0x204,0x4,0x81,"Coral II (755/99)"}, + {HPHW_NPROC,0x205,0x4,0x81,"Coral II (755/125)"}, + {HPHW_NPROC,0x205,0x4,0x91,"Snake Eagle "}, + {HPHW_NPROC,0x206,0x4,0x81,"Snake Cheetah (735/130)"}, + {HPHW_NPROC,0x280,0x4,0x81,"Nova Low (817, 827, 957, 957LX)"}, + {HPHW_NPROC,0x281,0x4,0x81,"Nova High (837, 847, 857, 967, 967LX)"}, + {HPHW_NPROC,0x282,0x4,0x81,"Nova8 (807, 917, 917LX, 927,927LX, 937, 937LX, 947,947LX)"}, + {HPHW_NPROC,0x283,0x4,0x81,"Nova64 (867, 877, 977)"}, + {HPHW_NPROC,0x284,0x4,0x81,"TNova (887, 897, 987)"}, + {HPHW_NPROC,0x285,0x4,0x81,"TNova64"}, + {HPHW_NPROC,0x286,0x4,0x91,"Hydra64 (Nova)"}, + {HPHW_NPROC,0x287,0x4,0x91,"Hydra96 (Nova)"}, + {HPHW_NPROC,0x288,0x4,0x81,"TNova96"}, + {HPHW_NPROC,0x300,0x4,0x81,"Bushmaster (710)"}, + {HPHW_NPROC,0x302,0x4,0x81,"Flounder (705)"}, + {HPHW_NPROC,0x310,0x4,0x81,"Scorpio (715/50)"}, + {HPHW_NPROC,0x311,0x4,0x81,"Scorpio Jr.(715/33)"}, + {HPHW_NPROC,0x312,0x4,0x81,"Strider-50 (715S/50)"}, + {HPHW_NPROC,0x313,0x4,0x81,"Strider-33 (715S/33)"}, + {HPHW_NPROC,0x314,0x4,0x81,"Trailways-50 (715T/50)"}, + {HPHW_NPROC,0x315,0x4,0x81,"Trailways-33 (715T/33)"}, + {HPHW_NPROC,0x316,0x4,0x81,"Scorpio Sr.(715/75)"}, + {HPHW_NPROC,0x317,0x4,0x81,"Scorpio 100 (715/100)"}, + {HPHW_NPROC,0x318,0x4,0x81,"Spectra (725/50)"}, + {HPHW_NPROC,0x319,0x4,0x81,"Spectra (725/75)"}, + {HPHW_NPROC,0x320,0x4,0x81,"Spectra (725/100)"}, + {HPHW_NPROC,0x401,0x4,0x81,"Pace (745i, 747i)"}, + {HPHW_NPROC,0x402,0x4,0x81,"Sidewinder (742i)"}, + {HPHW_NPROC,0x403,0x4,0x81,"Fast Pace"}, + {HPHW_NPROC,0x480,0x4,0x81,"Orville (E23)"}, + {HPHW_NPROC,0x481,0x4,0x81,"Wilbur (E25)"}, + {HPHW_NPROC,0x482,0x4,0x81,"WB-80 (E35)"}, + {HPHW_NPROC,0x483,0x4,0x81,"WB-96 (E45)"}, + {HPHW_NPROC,0x48,0x4,0x81,"UL Proc L-100 (811/D210,D310)"}, + {HPHW_NPROC,0x48,0x4,0x81,"UL Proc L-75 (801/D200)"}, + {HPHW_NPROC,0x501,0x4,0x81,"Merlin L2 132 (9000/778/B132L)"}, + {HPHW_NPROC,0x502,0x4,0x81,"Merlin L2 160 (9000/778/B160L)"}, + {HPHW_NPROC,0x503,0x4,0x81,"Merlin L2+ 132 (9000/778/B132L)"}, + {HPHW_NPROC,0x504,0x4,0x81,"Merlin L2+ 180 (9000/778/B180L)"}, + {HPHW_NPROC,0x505,0x4,0x81,"Raven L2 132 (9000/778/C132L)"}, + {HPHW_NPROC,0x506,0x4,0x81,"Raven L2 160 (9000/779/C160L)"}, + {HPHW_NPROC,0x507,0x4,0x81,"Raven L2 180 (9000/779/C180L)"}, + {HPHW_NPROC,0x508,0x4,0x81,"Raven L2 160 (9000/779/C160L)"}, + {HPHW_NPROC,0x509,0x4,0x81,"712/132 L2 Upgrade"}, + {HPHW_NPROC,0x50A,0x4,0x81,"712/160 L2 Upgrade"}, + {HPHW_NPROC,0x50B,0x4,0x81,"715/132 L2 Upgrade"}, + {HPHW_NPROC,0x50C,0x4,0x81,"715/160 L2 Upgrade"}, + {HPHW_NPROC,0x50D,0x4,0x81,"Rocky2 L2 120"}, + {HPHW_NPROC,0x50E,0x4,0x81,"Rocky2 L2 150"}, + {HPHW_NPROC,0x50F,0x4,0x81,"Anole L2 132 (744)"}, + {HPHW_NPROC,0x510,0x4,0x81,"Anole L2 165 (744)"}, + {HPHW_NPROC,0x511,0x4,0x81,"Kiji L2 132"}, + {HPHW_NPROC,0x512,0x4,0x81,"UL L2 132 (803/D220,D320)"}, + {HPHW_NPROC,0x513,0x4,0x81,"UL L2 160 (813/D220,D320)"}, + {HPHW_NPROC,0x514,0x4,0x81,"Merlin Jr L2 132"}, + {HPHW_NPROC,0x515,0x4,0x81,"Staccato L2 132"}, + {HPHW_NPROC,0x516,0x4,0x81,"Staccato L2 180 (A Class 180)"}, + {HPHW_NPROC,0x580,0x4,0x81,"KittyHawk DC2-100 (K100)"}, + {HPHW_NPROC,0x581,0x4,0x91,"KittyHawk DC3-120 (K210)"}, + {HPHW_NPROC,0x582,0x4,0x91,"KittyHawk DC3 100 (K400)"}, + {HPHW_NPROC,0x583,0x4,0x91,"KittyHawk DC3 120 (K410)"}, + {HPHW_NPROC,0x584,0x4,0x91,"LighteningHawk T120"}, + {HPHW_NPROC,0x585,0x4,0x91,"SkyHawk 100"}, + {HPHW_NPROC,0x586,0x4,0x91,"SkyHawk 120"}, + {HPHW_NPROC,0x587,0x4,0x81,"UL Proc 1-way T'120"}, + {HPHW_NPROC,0x588,0x4,0x91,"UL Proc 2-way T'120"}, + {HPHW_NPROC,0x589,0x4,0x81,"UL Proc 1-way T'100 (821/D250,D350)"}, + {HPHW_NPROC,0x58A,0x4,0x91,"UL Proc 2-way T'100 (831/D250,D350)"}, + {HPHW_NPROC,0x58B,0x4,0x91,"KittyHawk DC2 100 (K200)"}, + {HPHW_NPROC,0x58C,0x4,0x91,"ThunderHawk DC3- 120 1M (K220)"}, + {HPHW_NPROC,0x58D,0x4,0x91,"ThunderHawk DC3 120 1M (K420)"}, + {HPHW_NPROC,0x58E,0x4,0x81,"Raven 120 T'"}, + {HPHW_NPROC,0x58F,0x4,0x91,"Mohawk 160 U 1M DC3 (K450)"}, + {HPHW_NPROC,0x590,0x4,0x91,"Mohawk 180 U 1M DC3 (K460)"}, + {HPHW_NPROC,0x591,0x4,0x91,"Mohawk 200 U 1M DC3"}, + {HPHW_NPROC,0x592,0x4,0x81,"Raven 100 T'"}, + {HPHW_NPROC,0x593,0x4,0x91,"FireHawk 160 U"}, + {HPHW_NPROC,0x594,0x4,0x91,"FireHawk 180 U"}, + {HPHW_NPROC,0x595,0x4,0x91,"FireHawk 220 U"}, + {HPHW_NPROC,0x596,0x4,0x91,"FireHawk 240 U"}, + {HPHW_NPROC,0x597,0x4,0x91,"SPP2000 processor"}, + {HPHW_NPROC,0x598,0x4,0x81,"Raven U 230 (9000/780/C230)"}, + {HPHW_NPROC,0x599,0x4,0x81,"Raven U 240 (9000/780/C240)"}, + {HPHW_NPROC,0x59A,0x4,0x91,"Unlisted but reserved"}, + {HPHW_NPROC,0x59A,0x4,0x81,"Unlisted but reserved"}, + {HPHW_NPROC,0x59B,0x4,0x81,"Raven U 160 (9000/780/C160)"}, + {HPHW_NPROC,0x59D,0x4,0x81,"Raven U 200 (9000/780/C200)"}, + {HPHW_NPROC,0x59E,0x4,0x91,"ThunderHawk T' 120"}, + {HPHW_NPROC,0x59F,0x4,0x91,"Raven U 180+ (9000/780/\?\?\?\?)"}, + {HPHW_NPROC,0x5A0,0x4,0x81,"UL 1w T120 1MB/1MB (841/D260,D360)"}, + {HPHW_NPROC,0x5A1,0x4,0x91,"UL 2w T120 1MB/1MB (851/D260,D360)"}, + {HPHW_NPROC,0x5A2,0x4,0x81,"UL 1w U160 512K/512K (861/D270,D370)"}, + {HPHW_NPROC,0x5A3,0x4,0x91,"UL 2w U160 512K/512K (871/D270,D370)"}, + {HPHW_NPROC,0x5A4,0x4,0x91,"Mohawk 160 U 1M DC3- (K250)"}, + {HPHW_NPROC,0x5A5,0x4,0x91,"Mohawk 180 U 1M DC3- (K260)"}, + {HPHW_NPROC,0x5A6,0x4,0x91,"Mohawk 200 U 1M DC3-"}, + {HPHW_NPROC,0x5A7,0x4,0x81,"UL proc 1-way U160 1M/1M"}, + {HPHW_NPROC,0x5A8,0x4,0x91,"UL proc 2-way U160 1M/1M"}, + {HPHW_NPROC,0x5A9,0x4,0x81,"UL proc 1-way U180 1M/1M"}, + {HPHW_NPROC,0x5AA,0x4,0x91,"UL proc 2-way U180 1M/1M"}, + {HPHW_NPROC,0x5AB,0x4,0x91,"Obsolete"}, + {HPHW_NPROC,0x5AB,0x4,0x81,"Obsolete"}, + {HPHW_NPROC,0x5AC,0x4,0x91,"Obsolete"}, + {HPHW_NPROC,0x5AC,0x4,0x81,"Obsolete"}, + {HPHW_NPROC,0x5AD,0x4,0x91,"BraveHawk 180MHz DC3-"}, + {HPHW_NPROC,0x5AE,0x4,0x91,"BraveHawk 200MHz DC3- (898/K370)"}, + {HPHW_NPROC,0x5AF,0x4,0x91,"BraveHawk 220MHz DC3-"}, + {HPHW_NPROC,0x5B0,0x4,0x91,"BraveHawk 180MHz DC3"}, + {HPHW_NPROC,0x5B1,0x4,0x91,"BraveHawk 200MHz DC3 (899/K570)"}, + {HPHW_NPROC,0x5B2,0x4,0x91,"BraveHawk 220MHz DC3"}, + {HPHW_NPROC,0x5B3,0x4,0x91,"FireHawk 200"}, + {HPHW_NPROC,0x5B4,0x4,0x91,"SPP2500"}, + {HPHW_NPROC,0x5B5,0x4,0x91,"SummitHawk U+"}, + {HPHW_NPROC,0x5B6,0x4,0x91,"DragonHawk U+ 240 DC3"}, + {HPHW_NPROC,0x5B7,0x4,0x91,"DragonHawk U+ 240 DC3-"}, + {HPHW_NPROC,0x5B8,0x4,0x91,"SPP2250 240 MHz"}, + {HPHW_NPROC,0x5B9,0x4,0x81,"UL 1w U+/240 (350/550)"}, + {HPHW_NPROC,0x5BA,0x4,0x91,"UL 2w U+/240 (350/550)"}, + {HPHW_NPROC,0x5BB,0x4,0x81,"AllegroHigh W "}, + {HPHW_NPROC,0x5BC,0x4,0x91,"AllegroLow W"}, + {HPHW_NPROC,0x5BD,0x4,0x91,"Forte W 2-way"}, + {HPHW_NPROC,0x5BE,0x4,0x91,"Prelude W"}, + {HPHW_NPROC,0x5BF,0x4,0x91,"Forte W 4-way"}, + {HPHW_NPROC,0x5C0,0x4,0x91,"M2250"}, + {HPHW_NPROC,0x5C1,0x4,0x91,"M2500"}, + {HPHW_NPROC,0x5C2,0x4,0x91,"Sonata 440"}, + {HPHW_NPROC,0x5C3,0x4,0x91,"Sonata 360"}, + {HPHW_NPROC,0x5C4,0x4,0x91,"Rhapsody 440"}, + {HPHW_NPROC,0x5C5,0x4,0x91,"Rhapsody 360"}, + {HPHW_NPROC,0x5C6,0x4,0x91,"Raven W 360 (9000/780/\?\?\?\?)"}, + {HPHW_NPROC,0x5C7,0x4,0x91,"Halfdome W 440"}, + {HPHW_NPROC,0x5C8,0x4,0x81,"Lego 360 processor"}, + {HPHW_NPROC,0x5C9,0x4,0x91,"Rhapsody DC- 440"}, + {HPHW_NPROC,0x5CA,0x4,0x91,"Rhapsody DC- 360"}, + {HPHW_NPROC,0x5CB,0x4,0x91,"Crescendo 440"}, + {HPHW_NPROC,0x5FF,0x4,0x91,"Hitachi W"}, + {HPHW_NPROC,0x600,0x4,0x81,"Gecko (712/60)"}, + {HPHW_NPROC,0x601,0x4,0x81,"Gecko 80 (712/80)"}, + {HPHW_NPROC,0x602,0x4,0x81,"Gecko 100 (712/100)"}, + {HPHW_NPROC,0x603,0x4,0x81,"Anole 64 (743/64)"}, + {HPHW_NPROC,0x604,0x4,0x81,"Anole 100 (743/100)"}, + {HPHW_NPROC,0x605,0x4,0x81,"Gecko 120 (712/120)"}, + {HPHW_NPROC,0x606,0x4,0x81,"Gila 80"}, + {HPHW_NPROC,0x607,0x4,0x81,"Gila 100"}, + {HPHW_NPROC,0x608,0x4,0x81,"Gila 120"}, + {HPHW_NPROC,0x609,0x4,0x81,"Scorpio-L 80"}, + {HPHW_NPROC,0x60A,0x4,0x81,"Mirage Jr (715/64)"}, + {HPHW_NPROC,0x60B,0x4,0x81,"Mirage 100"}, + {HPHW_NPROC,0x60C,0x4,0x81,"Mirage 100+"}, + {HPHW_NPROC,0x60D,0x4,0x81,"Electra 100"}, + {HPHW_NPROC,0x60E,0x4,0x81,"Electra 120"}, + {HPHW_NPROC,0x610,0x4,0x81,"Scorpio-L 100"}, + {HPHW_NPROC,0x611,0x4,0x81,"Scorpio-L 120"}, + {HPHW_NPROC,0x612,0x4,0x81,"Spectra-L 80"}, + {HPHW_NPROC,0x613,0x4,0x81,"Spectra-L 100"}, + {HPHW_NPROC,0x614,0x4,0x81,"Spectra-L 120"}, + {HPHW_NPROC,0x615,0x4,0x81,"Piranha 100"}, + {HPHW_NPROC,0x616,0x4,0x81,"Piranha 120"}, + {HPHW_NPROC,0x617,0x4,0x81,"Jason 50"}, + {HPHW_NPROC,0x618,0x4,0x81,"Jason 100"}, + {HPHW_NPROC,0x619,0x4,0x81,"Mirage 80 "}, + {HPHW_NPROC,0x61A,0x4,0x81,"SAIC L-80"}, + {HPHW_NPROC,0x61B,0x4,0x81,"Rocky1 L-60"}, + {HPHW_NPROC,0x61C,0x4,0x81,"Anole T (743/T)"}, + {HPHW_NPROC,0x67E,0x4,0x81,"Hitachi Tiny 80"}, + {HPHW_NPROC,0x67F,0x4,0x81,"Hitachi Tiny 64"}, + {HPHW_NPROC,0x700,0x4,0x91,"NEC Aska Processor"}, + {HPHW_A_DIRECT, 0x004, 0x0000D, 0x00, "Arrakis MUX"}, + {HPHW_A_DIRECT, 0x005, 0x0000D, 0x00, "Dyun Kiuh MUX"}, + {HPHW_A_DIRECT, 0x006, 0x0000D, 0x00, "Baat Kiuh AP/MUX (40299B)"}, + {HPHW_A_DIRECT, 0x007, 0x0000D, 0x00, "Dino AP"}, + {HPHW_A_DIRECT, 0x009, 0x0000D, 0x00, "Solaris Direct Connect MUX (J2092A)"}, + {HPHW_A_DIRECT, 0x00A, 0x0000D, 0x00, "Solaris RS-422/423 MUX (J2093A)"}, + {HPHW_A_DIRECT, 0x00B, 0x0000D, 0x00, "Solaris RS-422/423 Quadriloops MUX"}, + {HPHW_A_DIRECT, 0x00C, 0x0000D, 0x00, "Solaris Modem MUX (J2094A)"}, + {HPHW_A_DIRECT, 0x00D, 0x0000D, 0x00, "Twins Direct Connect MUX"}, + {HPHW_A_DIRECT, 0x00E, 0x0000D, 0x00, "Twins Modem MUX"}, + {HPHW_A_DIRECT, 0x00F, 0x0000D, 0x00, "Nautilus RS-485"}, + {HPHW_A_DIRECT, 0x010, 0x0000D, 0x00, "UltraLight CAP/MUX"}, + {HPHW_A_DIRECT, 0x015, 0x0000D, 0x00, "Eole CAP/MUX"}, + {HPHW_A_DIRECT, 0x024, 0x0000D, 0x00, "Sahp Kiuh AP/MUX"}, + {HPHW_A_DIRECT, 0x034, 0x0000D, 0x00, "Sahp Kiuh Low AP/MUX"}, + {HPHW_A_DIRECT, 0x044, 0x0000D, 0x00, "Sahp Baat Kiuh AP/MUX"}, + {HPHW_A_DIRECT, 0x004, 0x0000E, 0x80, "Burgundy RS-232"}, + {HPHW_A_DIRECT, 0x005, 0x0000E, 0x80, "Silverfox RS-232"}, + {HPHW_A_DIRECT, 0x006, 0x0000E, 0x80, "Lego RS-232"}, + {HPHW_A_DIRECT, 0x004, 0x0000F, 0x00, "Peacock Graphics"}, + {HPHW_A_DIRECT, 0x004, 0x00014, 0x80, "Burgundy HIL"}, + {HPHW_A_DIRECT, 0x005, 0x00014, 0x80, "Peacock HIL"}, + {HPHW_A_DIRECT, 0x004, 0x00015, 0x80, "Leonardo"}, + {HPHW_A_DIRECT, 0x004, 0x00016, 0x80, "HP-PB HRM"}, + {HPHW_A_DIRECT, 0x004, 0x00017, 0x80, "HP-PB HRC"}, + {HPHW_A_DIRECT, 0x004, 0x0003A, 0x80, "Skunk Centronics (28655A)"}, + {HPHW_A_DIRECT, 0x024, 0x0003A, 0x80, "Sahp Kiuh Centronics"}, + {HPHW_A_DIRECT, 0x044, 0x0003A, 0x80, "Sahp Baat Kiuh Centronics"}, + {HPHW_A_DIRECT, 0x004, 0x0004E, 0x80, "AT&T DataKit (AMSO)"}, + {HPHW_A_DIRECT, 0x004, 0x0009B, 0x80, "Test&Meas GSC HPIB"}, + {HPHW_A_DIRECT, 0x004, 0x000A8, 0x00, "Rocky2-120 Front Keyboard"}, + {HPHW_A_DIRECT, 0x005, 0x000A8, 0x00, "Rocky2-150 Front Keyboard"}, + {HPHW_A_DIRECT, 0x004, 0x00101, 0x80, "Hitachi Console Module"}, + {HPHW_A_DIRECT, 0x004, 0x00102, 0x80, "Hitachi Boot Module"}, + {HPHW_A_DIRECT, 0x004, 0x00203, 0x80, "MELCO HBMLA MLAIT"}, + {HPHW_A_DIRECT, 0x004, 0x00208, 0x80, "MELCO HBDPC"}, + {HPHW_A_DIRECT, 0x004, 0x00300, 0x00, "DCI TWINAX TERM IO MUX"}, + {HPHW_A_DMA, 0x004, 0x00039, 0x80, "Skunk SCSI (28655A)"}, + {HPHW_A_DMA, 0x005, 0x00039, 0x80, "KittyHawk CSY Core SCSI"}, + {HPHW_A_DMA, 0x014, 0x00039, 0x80, "Diablo SCSI"}, + {HPHW_A_DMA, 0x024, 0x00039, 0x80, "Sahp Kiuh SCSI"}, + {HPHW_A_DMA, 0x034, 0x00039, 0x80, "Sahp Kiuh Low SCSI"}, + {HPHW_A_DMA, 0x044, 0x00039, 0x80, "Sahp Baat Kiuh SCSI"}, + {HPHW_A_DMA, 0x004, 0x0003B, 0x80, "Wizard SCSI"}, + {HPHW_A_DMA, 0x005, 0x0003B, 0x80, "KittyHawk CSY Core FW-SCSI"}, + {HPHW_A_DMA, 0x006, 0x0003B, 0x80, "Symbios EPIC FW-SCSI"}, + {HPHW_A_DMA, 0x004, 0x00040, 0x80, "HP-PB Shazam HPIB (28650A)"}, + {HPHW_A_DMA, 0x005, 0x00040, 0x80, "Burgundy HPIB"}, + {HPHW_A_DMA, 0x004, 0x00041, 0x80, "HP-PB HP-FL"}, + {HPHW_A_DMA, 0x004, 0x00042, 0x80, "HP-PB LoQuix HPIB (28650B)"}, + {HPHW_A_DMA, 0x004, 0x00043, 0x80, "HP-PB Crypt LoQuix"}, + {HPHW_A_DMA, 0x004, 0x00044, 0x80, "HP-PB Shazam GPIO (28651A)"}, + {HPHW_A_DMA, 0x004, 0x00045, 0x80, "HP-PB LoQuix GPIO"}, + {HPHW_A_DMA, 0x004, 0x00046, 0x80, "2-Port X.25 NIO_ACC (AMSO)"}, + {HPHW_A_DMA, 0x004, 0x00047, 0x80, "4-Port X.25 NIO_ACC (AMSO)"}, + {HPHW_A_DMA, 0x004, 0x0004B, 0x80, "LGB Control"}, + {HPHW_A_DMA, 0x004, 0x0004C, 0x80, "Martian RTI (AMSO)"}, + {HPHW_A_DMA, 0x004, 0x0004D, 0x80, "ACC Mux (AMSO)"}, + {HPHW_A_DMA, 0x004, 0x00050, 0x80, "Lanbrusca 802.3 (36967A)"}, + {HPHW_A_DMA, 0x004, 0x00056, 0x80, "HP-PB LoQuix FDDI"}, + {HPHW_A_DMA, 0x004, 0x00057, 0x80, "HP-PB LoQuix FDDI (28670A)"}, + {HPHW_A_DMA, 0x012, 0x00089, 0x80, "Barracuda Add-on FW-SCSI"}, + {HPHW_A_DMA, 0x013, 0x00089, 0x80, "Bluefish Add-on FW-SCSI"}, + {HPHW_A_DMA, 0x014, 0x00089, 0x80, "Shrike Add-on FW-SCSI"}, + {HPHW_A_DMA, 0x015, 0x00089, 0x80, "KittyHawk GSY Core FW-SCSI"}, + {HPHW_A_DMA, 0x017, 0x00089, 0x80, "Shrike Jade Add-on FW-SCSI (A3644A)"}, + {HPHW_A_DMA, 0x01F, 0x00089, 0x80, "SkyHawk 100/120 FW-SCSI"}, + {HPHW_A_DMA, 0x027, 0x00089, 0x80, "Piranha 100 FW-SCSI"}, + {HPHW_A_DMA, 0x032, 0x00089, 0x80, "Raven T' Core FW-SCSI"}, + {HPHW_A_DMA, 0x03d, 0x00089, 0x80, "Merlin 160 Core FW-SCSI"}, + {HPHW_A_DMA, 0x044, 0x00089, 0x80, "Mohawk Core FW-SCSI"}, + {HPHW_A_DMA, 0x051, 0x00089, 0x80, "Firehawk FW-SCSI"}, + {HPHW_A_DMA, 0x058, 0x00089, 0x80, "FireHawk 200 FW-SCSI"}, + {HPHW_A_DMA, 0x05C, 0x00089, 0x80, "SummitHawk 230 Ultra-SCSI"}, + {HPHW_A_DMA, 0x014, 0x00091, 0x80, "Baby Hugo Add-on Net FC (A3406A)"}, + {HPHW_A_DMA, 0x020, 0x00091, 0x80, "Baby Jade Add-on Net FC (A3638A)"}, + {HPHW_A_DMA, 0x004, 0x00092, 0x80, "GSC+ YLIASTER ATM"}, + {HPHW_A_DMA, 0x004, 0x00095, 0x80, "Hamlyn GSC+ Network Card"}, + {HPHW_A_DMA, 0x004, 0x00098, 0x80, "Lo-fat Emulator"}, + {HPHW_A_DMA, 0x004, 0x0009A, 0x80, "GSC+ Venus ATM"}, + {HPHW_A_DMA, 0x005, 0x0009A, 0x80, "GSC+ Samorobrive ATM"}, + {HPHW_A_DMA, 0x004, 0x0009D, 0x80, "HP HSC-PCI Cards"}, + {HPHW_A_DMA, 0x004, 0x0009E, 0x80, "Alaxis GSC+ 155Mb ATM"}, + {HPHW_A_DMA, 0x005, 0x0009E, 0x80, "Alaxis GSC+ 622Mb ATM"}, + {HPHW_A_DMA, 0x05C, 0x0009F, 0x80, "SummitHawk 230 USB"}, + {HPHW_A_DMA, 0x05C, 0x000A0, 0x80, "SummitHawk 230 100BaseT"}, + {HPHW_A_DMA, 0x015, 0x000A7, 0x80, "Baby Hugo Add-on mass FC (A3404A)"}, + {HPHW_A_DMA, 0x018, 0x000A7, 0x80, "Mombasa GS Add-on mass FC (A3591)"}, + {HPHW_A_DMA, 0x021, 0x000A7, 0x80, "Baby Jade Add-on mass FC (A3636A)"}, + {HPHW_A_DMA, 0x004, 0x00201, 0x80, "MELCO HCMAP"}, + {HPHW_A_DMA, 0x004, 0x00202, 0x80, "MELCO HBMLA MLAMA"}, + {HPHW_A_DMA, 0x004, 0x00205, 0x80, "MELCO HBRFU"}, + {HPHW_A_DMA, 0x004, 0x00380, 0x80, "Interphase NIO-FC"}, + {HPHW_A_DMA, 0x004, 0x00381, 0x80, "Interphase NIO-ATM"}, + {HPHW_A_DMA, 0x004, 0x00382, 0x80, "Interphase NIO-100BaseTX"}, + {HPHW_BA, 0x004, 0x00070, 0x0, "Cobra Core BA"}, + {HPHW_BA, 0x005, 0x00070, 0x0, "Coral Core BA"}, + {HPHW_BA, 0x006, 0x00070, 0x0, "Bushmaster Core BA"}, + {HPHW_BA, 0x007, 0x00070, 0x0, "Scorpio Core BA"}, + {HPHW_BA, 0x008, 0x00070, 0x0, "Flounder Core BA"}, + {HPHW_BA, 0x009, 0x00070, 0x0, "Outfield Core BA"}, + {HPHW_BA, 0x00A, 0x00070, 0x0, "CoralII Core BA"}, + {HPHW_BA, 0x00B, 0x00070, 0x0, "Scorpio Jr. Core BA"}, + {HPHW_BA, 0x00C, 0x00070, 0x0, "Strider-50 Core BA"}, + {HPHW_BA, 0x00D, 0x00070, 0x0, "Strider-33 Core BA"}, + {HPHW_BA, 0x00E, 0x00070, 0x0, "Trailways-50 Core BA"}, + {HPHW_BA, 0x00F, 0x00070, 0x0, "Trailways-33 Core BA"}, + {HPHW_BA, 0x010, 0x00070, 0x0, "Pace Core BA"}, + {HPHW_BA, 0x011, 0x00070, 0x0, "Sidewinder Core BA"}, + {HPHW_BA, 0x019, 0x00070, 0x0, "Scorpio Sr. Core BA"}, + {HPHW_BA, 0x020, 0x00070, 0x0, "Scorpio 100 Core BA"}, + {HPHW_BA, 0x021, 0x00070, 0x0, "Spectra 50 Core BA"}, + {HPHW_BA, 0x022, 0x00070, 0x0, "Spectra 75 Core BA"}, + {HPHW_BA, 0x023, 0x00070, 0x0, "Spectra 100 Core BA"}, + {HPHW_BA, 0x024, 0x00070, 0x0, "Fast Pace Core BA"}, + {HPHW_BA, 0x026, 0x00070, 0x0, "CoralII Jaguar Core BA"}, + {HPHW_BA, 0x004, 0x00076, 0x0, "Cobra EISA BA"}, + {HPHW_BA, 0x005, 0x00076, 0x0, "Coral EISA BA"}, + {HPHW_BA, 0x007, 0x00076, 0x0, "Scorpio EISA BA"}, + {HPHW_BA, 0x00A, 0x00076, 0x0, "CoralII EISA BA"}, + {HPHW_BA, 0x00B, 0x00076, 0x0, "Scorpio Jr. EISA BA"}, + {HPHW_BA, 0x00C, 0x00076, 0x0, "Strider-50 Core EISA"}, + {HPHW_BA, 0x00D, 0x00076, 0x0, "Strider-33 Core EISA"}, + {HPHW_BA, 0x00E, 0x00076, 0x0, "Trailways-50 Core EISA"}, + {HPHW_BA, 0x00F, 0x00076, 0x0, "Trailways-33 Core EISA"}, + {HPHW_BA, 0x010, 0x00076, 0x0, "Pace Core EISA"}, + {HPHW_BA, 0x019, 0x00076, 0x0, "Scorpio Sr. EISA BA"}, + {HPHW_BA, 0x020, 0x00076, 0x0, "Scorpio 100 EISA BA"}, + {HPHW_BA, 0x021, 0x00076, 0x0, "Spectra 50 EISA BA"}, + {HPHW_BA, 0x022, 0x00076, 0x0, "Spectra 75 EISA BA"}, + {HPHW_BA, 0x023, 0x00076, 0x0, "Spectra 100 EISA BA"}, + {HPHW_BA, 0x026, 0x00076, 0x0, "CoralII Jaguar EISA BA"}, + {HPHW_BA, 0x010, 0x00078, 0x0, "Pace VME BA"}, + {HPHW_BA, 0x011, 0x00078, 0x0, "Sidewinder VME BA"}, + {HPHW_BA, 0x01A, 0x00078, 0x0, "Anole 64 VME BA"}, + {HPHW_BA, 0x01B, 0x00078, 0x0, "Anole 100 VME BA"}, + {HPHW_BA, 0x024, 0x00078, 0x0, "Fast Pace VME BA"}, + {HPHW_BA, 0x034, 0x00078, 0x0, "Anole T VME BA"}, + {HPHW_BA, 0x04A, 0x00078, 0x0, "Anole L2 132 BME BA"}, + {HPHW_BA, 0x04C, 0x00078, 0x0, "Anole L2 165 VME BA"}, + {HPHW_BA, 0x011, 0x00081, 0x0, "WB-96 Core BA"}, + {HPHW_BA, 0x012, 0x00081, 0x0, "Orville UX Core BA"}, + {HPHW_BA, 0x013, 0x00081, 0x0, "Wilbur UX Core BA"}, + {HPHW_BA, 0x014, 0x00081, 0x0, "WB-80 Core BA"}, + {HPHW_BA, 0x015, 0x00081, 0x0, "KittyHawk GSY Core BA"}, + {HPHW_BA, 0x016, 0x00081, 0x0, "Gecko Core BA"}, + {HPHW_BA, 0x018, 0x00081, 0x0, "Gecko Optional BA"}, + {HPHW_BA, 0x01A, 0x00081, 0x0, "Anole 64 Core BA"}, + {HPHW_BA, 0x01B, 0x00081, 0x0, "Anole 100 Core BA"}, + {HPHW_BA, 0x01C, 0x00081, 0x0, "Gecko 80 Core BA"}, + {HPHW_BA, 0x01D, 0x00081, 0x0, "Gecko 100 Core BA"}, + {HPHW_BA, 0x01F, 0x00081, 0x0, "SkyHawk 100/120 Core BA"}, + {HPHW_BA, 0x027, 0x00081, 0x0, "Piranha 100 Core BA"}, + {HPHW_BA, 0x028, 0x00081, 0x0, "Mirage Jr Core BA"}, + {HPHW_BA, 0x029, 0x00081, 0x0, "Mirage Core BA"}, + {HPHW_BA, 0x02A, 0x00081, 0x0, "Electra Core BA"}, + {HPHW_BA, 0x02B, 0x00081, 0x0, "Mirage 80 Core BA"}, + {HPHW_BA, 0x02C, 0x00081, 0x0, "Mirage 100+ Core BA"}, + {HPHW_BA, 0x02E, 0x00081, 0x0, "UL 350 Lasi Core BA"}, + {HPHW_BA, 0x02F, 0x00081, 0x0, "UL 550 Lasi Core BA"}, + {HPHW_BA, 0x032, 0x00081, 0x0, "Raven T' Core BA"}, + {HPHW_BA, 0x033, 0x00081, 0x0, "Anole T Core BA"}, + {HPHW_BA, 0x034, 0x00081, 0x0, "SAIC L-80 Core BA"}, + {HPHW_BA, 0x035, 0x00081, 0x0, "PCX-L2 712/132 Core BA"}, + {HPHW_BA, 0x036, 0x00081, 0x0, "PCX-L2 712/160 Core BA"}, + {HPHW_BA, 0x03B, 0x00081, 0x0, "Raven U/L2 Core BA"}, + {HPHW_BA, 0x03C, 0x00081, 0x0, "Merlin 132 Core BA"}, + {HPHW_BA, 0x03D, 0x00081, 0x0, "Merlin 160 Core BA"}, + {HPHW_BA, 0x03E, 0x00081, 0x0, "Merlin+ 132 Core BA"}, + {HPHW_BA, 0x03F, 0x00081, 0x0, "Merlin+ 180 Core BA"}, + {HPHW_BA, 0x044, 0x00081, 0x0, "Mohawk Core BA"}, + {HPHW_BA, 0x045, 0x00081, 0x0, "Rocky1 Core BA"}, + {HPHW_BA, 0x046, 0x00081, 0x0, "Rocky2 120 Core BA"}, + {HPHW_BA, 0x047, 0x00081, 0x0, "Rocky2 150 Core BA"}, + {HPHW_BA, 0x04B, 0x00081, 0x0, "Anole L2 132 Core BA"}, + {HPHW_BA, 0x04D, 0x00081, 0x0, "Anole L2 165 Core BA"}, + {HPHW_BA, 0x04E, 0x00081, 0x0, "Kiji L2 132 Core BA"}, + {HPHW_BA, 0x050, 0x00081, 0x0, "Merlin Jr 132 Core BA"}, + {HPHW_BA, 0x051, 0x00081, 0x0, "Firehawk Core BA"}, + {HPHW_BA, 0x056, 0x00081, 0x0, "Raven+ w SE FWSCSI Core BA"}, + {HPHW_BA, 0x057, 0x00081, 0x0, "Raven+ w Diff FWSCSI Core BA"}, + {HPHW_BA, 0x058, 0x00081, 0x0, "FireHawk 200 Core BA"}, + {HPHW_BA, 0x05C, 0x00081, 0x0, "SummitHawk 230 Core BA"}, + {HPHW_BA, 0x05E, 0x00081, 0x0, "Staccato 132 Core BA"}, + {HPHW_BA, 0x05E, 0x00081, 0x0, "Staccato 180 Core BA"}, + {HPHW_BA, 0x05F, 0x00081, 0x0, "Staccato 180 Lasi"}, + {HPHW_BA, 0x800, 0x00081, 0x0, "Hitachi Tiny 64 Core BA"}, + {HPHW_BA, 0x801, 0x00081, 0x0, "Hitachi Tiny 80 Core BA"}, + {HPHW_BA, 0x004, 0x0008B, 0x0, "Anole Optional PCMCIA BA"}, + {HPHW_BA, 0x004, 0x0008E, 0x0, "GSC ITR Wax BA"}, + {HPHW_BA, 0x011, 0x0008E, 0x0, "SuperPace Wax BA"}, + {HPHW_BA, 0x012, 0x0008E, 0x0, "Mirage Jr Wax BA"}, + {HPHW_BA, 0x013, 0x0008E, 0x0, "Mirage Wax BA"}, + {HPHW_BA, 0x014, 0x0008E, 0x0, "Electra Wax BA"}, + {HPHW_BA, 0x017, 0x0008E, 0x0, "Raven Backplane Wax BA"}, + {HPHW_BA, 0x01E, 0x0008E, 0x0, "Raven T' Wax BA"}, + {HPHW_BA, 0x01F, 0x0008E, 0x0, "SkyHawk Wax BA"}, + {HPHW_BA, 0x023, 0x0008E, 0x0, "Rocky1 Wax BA"}, + {HPHW_BA, 0x02B, 0x0008E, 0x0, "Mirage 80 Wax BA"}, + {HPHW_BA, 0x02C, 0x0008E, 0x0, "Mirage 100+ Wax BA"}, + {HPHW_BA, 0x030, 0x0008E, 0x0, "UL 350 Core Wax BA"}, + {HPHW_BA, 0x031, 0x0008E, 0x0, "UL 550 Core Wax BA"}, + {HPHW_BA, 0x034, 0x0008E, 0x0, "SAIC L-80 Wax BA"}, + {HPHW_BA, 0x03A, 0x0008E, 0x0, "Merlin+ Wax BA"}, + {HPHW_BA, 0x040, 0x0008E, 0x0, "Merlin 132 Wax BA"}, + {HPHW_BA, 0x041, 0x0008E, 0x0, "Merlin 160 Wax BA"}, + {HPHW_BA, 0x043, 0x0008E, 0x0, "Merlin 132/160 Wax BA"}, + {HPHW_BA, 0x052, 0x0008E, 0x0, "Raven+ Hi Power Backplane w/EISA Wax BA"}, + {HPHW_BA, 0x054, 0x0008E, 0x0, "Raven+ Lo Power Backplane w/EISA Wax BA"}, + {HPHW_BA, 0x059, 0x0008E, 0x0, "FireHawk 200 Wax BA"}, + {HPHW_BA, 0x05A, 0x0008E, 0x0, "Raven+ L2 Backplane w/EISA Wax BA"}, + {HPHW_BA, 0x05D, 0x0008E, 0x0, "SummitHawk Wax BA"}, + {HPHW_BA, 0x800, 0x0008E, 0x0, "Hitachi Tiny 64 Wax BA"}, + {HPHW_BA, 0x801, 0x0008E, 0x0, "Hitachi Tiny 80 Wax BA"}, + {HPHW_BA, 0x011, 0x00090, 0x0, "SuperPace Wax EISA BA"}, + {HPHW_BA, 0x017, 0x00090, 0x0, "Raven Backplane Wax EISA BA"}, + {HPHW_BA, 0x01E, 0x00090, 0x0, "Raven T' Wax EISA BA"}, + {HPHW_BA, 0x01F, 0x00090, 0x0, "SkyHawk 100/120 Wax EISA BA"}, + {HPHW_BA, 0x027, 0x00090, 0x0, "Piranha 100 Wax EISA BA"}, + {HPHW_BA, 0x028, 0x00090, 0x0, "Mirage Jr Wax EISA BA"}, + {HPHW_BA, 0x029, 0x00090, 0x0, "Mirage Wax EISA BA"}, + {HPHW_BA, 0x02A, 0x00090, 0x0, "Electra Wax EISA BA"}, + {HPHW_BA, 0x02B, 0x00090, 0x0, "Mirage 80 Wax EISA BA"}, + {HPHW_BA, 0x02C, 0x00090, 0x0, "Mirage 100+ Wax EISA BA"}, + {HPHW_BA, 0x030, 0x00090, 0x0, "UL 350 Wax EISA BA"}, + {HPHW_BA, 0x031, 0x00090, 0x0, "UL 550 Wax EISA BA"}, + {HPHW_BA, 0x034, 0x00090, 0x0, "SAIC L-80 Wax EISA BA"}, + {HPHW_BA, 0x03A, 0x00090, 0x0, "Merlin+ Wax EISA BA"}, + {HPHW_BA, 0x040, 0x00090, 0x0, "Merlin 132 Wax EISA BA"}, + {HPHW_BA, 0x041, 0x00090, 0x0, "Merlin 160 Wax EISA BA"}, + {HPHW_BA, 0x043, 0x00090, 0x0, "Merlin 132/160 Wax EISA BA"}, + {HPHW_BA, 0x052, 0x00090, 0x0, "Raven Hi Power Backplane Wax EISA BA"}, + {HPHW_BA, 0x054, 0x00090, 0x0, "Raven Lo Power Backplane Wax EISA BA"}, + {HPHW_BA, 0x059, 0x00090, 0x0, "FireHawk 200 Wax EISA BA"}, + {HPHW_BA, 0x05A, 0x00090, 0x0, "Raven L2 Backplane Wax EISA BA"}, + {HPHW_BA, 0x05D, 0x00090, 0x0, "SummitHawk Wax EISA BA"}, + {HPHW_BA, 0x800, 0x00090, 0x0, "Hitachi Tiny 64 Wax EISA BA"}, + {HPHW_BA, 0x801, 0x00090, 0x0, "Hitachi Tiny 80 Wax EISA BA"}, + {HPHW_BA, 0x01A, 0x00093, 0x0, "Anole 64 TIMI BA"}, + {HPHW_BA, 0x01B, 0x00093, 0x0, "Anole 64 TIMI BA"}, + {HPHW_BA, 0x034, 0x00093, 0x0, "Anole T TIMI BA"}, + {HPHW_BA, 0x04A, 0x00093, 0x0, "Anole L2 132 TIMI BA"}, + {HPHW_BA, 0x04C, 0x00093, 0x0, "Anole L2 165 TIMI BA"}, + {HPHW_BA, 0x582, 0x000A5, 0x00, "Epic PCI Bridge"}, + {HPHW_BCPORT, 0x504, 0x00000, 0x00, "Phantom PseudoBC GSC+ Port"}, + {HPHW_BCPORT, 0x505, 0x00000, 0x00, "Phantom PseudoBC GSC+ Port"}, + {HPHW_BCPORT, 0x503, 0x0000C, 0x00, "Java BC GSC+ Port"}, + {HPHW_BCPORT, 0x57F, 0x0000C, 0x00, "Hitachi Ghostview GSC+ Port"}, + {HPHW_BCPORT, 0x501, 0x0000C, 0x00, "U2-IOA BC GSC+ Port"}, + {HPHW_BCPORT, 0x502, 0x0000C, 0x00, "Uturn-IOA BC GSC+ Port"}, + {HPHW_BCPORT, 0x780, 0x0000C, 0x00, "Astro BC Ropes Port"}, + {HPHW_BCPORT, 0x506, 0x0000C, 0x00, "NEC-IOS BC HSC Port"}, + {HPHW_BCPORT, 0x004, 0x0000C, 0x00, "Cheetah BC SMB Port"}, + {HPHW_BCPORT, 0x006, 0x0000C, 0x00, "Cheetah BC MID_BUS Port"}, + {HPHW_BCPORT, 0x005, 0x0000C, 0x00, "Condor BC MID_BUS Port"}, + {HPHW_BCPORT, 0x100, 0x0000C, 0x00, "Condor BC HP-PB Port"}, + {HPHW_BCPORT, 0x184, 0x0000C, 0x00, "Summit BC Port"}, + {HPHW_BCPORT, 0x101, 0x0000C, 0x00, "Summit BC HP-PB Port"}, + {HPHW_BCPORT, 0x102, 0x0000C, 0x00, "HP-PB Port (prefetch)"}, + {HPHW_BCPORT, 0x500, 0x0000C, 0x00, "Gecko BOA BC GSC+ Port"}, + {HPHW_BCPORT, 0x103, 0x0000C, 0x00, "Gecko BOA BC HP-PB Port"}, + {HPHW_BCPORT, 0x507, 0x0000C, 0x00, "Keyaki BC GSC+ Port"}, + {HPHW_BCPORT, 0x508, 0x0000C, 0x00, "Keyaki-DX BC GSC+ Port"}, + {HPHW_BCPORT, 0x584, 0x0000C, 0x10, "DEW BC Runway Port"}, + {HPHW_BCPORT, 0x800, 0x0000C, 0x10, "DEW BC Merced Port"}, + {HPHW_BCPORT, 0x801, 0x0000C, 0x10, "SMC Bus Interface Merced Bus0"}, + {HPHW_BCPORT, 0x802, 0x0000C, 0x10, "SMC Bus INterface Merced Bus1"}, + {HPHW_BCPORT, 0x803, 0x0000C, 0x10, "IKE I/O Bus Converter Merced Port"}, + {HPHW_BCPORT, 0x781, 0x0000C, 0x00, "IKE I/O Bus Converter Ropes Port"}, + {HPHW_BCPORT, 0x804, 0x0000C, 0x10, "REO I/O Bus Converter Merced Port"}, + {HPHW_BCPORT, 0x782, 0x0000C, 0x00, "REO I/O Bus Converter Ropes Port"}, + {HPHW_BRIDGE, 0x680, 0x0000A, 0x00, "Dino PCI Bridge"}, + {HPHW_BRIDGE, 0x682, 0x0000A, 0x00, "Cujo PCI Bridge"}, + {HPHW_BRIDGE, 0x782, 0x0000A, 0x00, "Elroy PCI Bridge"}, + {HPHW_BRIDGE, 0x583, 0x000A5, 0x00, "Saga PCI Bridge"}, + {HPHW_B_DMA, 0x004, 0x00018, 0x00, "Parallel I/O"}, + {HPHW_B_DMA, 0x004, 0x00019, 0x00, "Parallel RDB"}, + {HPHW_B_DMA, 0x004, 0x00020, 0x80, "MID_BUS PSI"}, + {HPHW_B_DMA, 0x004, 0x0002F, 0x80, "HP-PB Transit PSI (36960A)"}, + {HPHW_B_DMA, 0x008, 0x00051, 0x80, "HP-PB Transit 802.3"}, + {HPHW_B_DMA, 0x004, 0x00052, 0x80, "Miura LAN/Console (J2146A)"}, + {HPHW_B_DMA, 0x008, 0x00058, 0x80, "HP-PB Transit 802.4"}, + {HPHW_B_DMA, 0x005, 0x00060, 0x80, "KittyHawk CSY Core LAN/Console"}, + {HPHW_B_DMA, 0x014, 0x00060, 0x80, "Diablo LAN/Console"}, + {HPHW_B_DMA, 0x054, 0x00060, 0x80, "Countach LAN/Console"}, + {HPHW_B_DMA, 0x004, 0x00094, 0x80, "KittyHawk GSC+ Exerciser"}, + {HPHW_B_DMA, 0x004, 0x00100, 0x80, "HP-PB HF Interface"}, + {HPHW_B_DMA, 0x000, 0x00206, 0x80, "MELCO HMPHA"}, + {HPHW_B_DMA, 0x005, 0x00206, 0x80, "MELCO HMPHA_10"}, + {HPHW_B_DMA, 0x006, 0x00206, 0x80, "MELCO HMQHA"}, + {HPHW_B_DMA, 0x007, 0x00206, 0x80, "MELCO HMQHA_10"}, + {HPHW_B_DMA, 0x004, 0x207, 0x80, "MELCO HNDWA MDWS-70"}, + {HPHW_CIO, 0x004, 0x00010, 0x00, "VLSI CIO"}, + {HPHW_CIO, 0x005, 0x00010, 0x00, "Silverfox CIO"}, + {HPHW_CIO, 0x006, 0x00010, 0x00, "Emerald CIO"}, + {HPHW_CIO, 0x008, 0x00010, 0x00, "Discrete CIO"}, + {HPHW_CONSOLE, 0x004, 0x0001C, 0x00, "Cheetah console"}, + {HPHW_CONSOLE, 0x005, 0x0001C, 0x00, "Emerald console"}, + {HPHW_CONSOLE, 0x01A, 0x0001F, 0x00, "Jason/Anole 64 Null Console"}, + {HPHW_CONSOLE, 0x01B, 0x0001F, 0x00, "Jason/Anole 100 Null Console"}, + {HPHW_FABRIC, 0x004, 0x000AA, 0x80, "Halfdome DNA Central Agent"}, + {HPHW_FABRIC, 0x004, 0x000AB, 0x00, "Halfdome TOGO Fabric Crossbar"}, + {HPHW_FABRIC, 0x004, 0x000AC, 0x00, "Halfdome Sakura Fabric Router"}, + {HPHW_FIO, 0x025, 0x0002E, 0x80, "Armyknife Optional X.25"}, + {HPHW_FIO, 0x004, 0x0004F, 0x0, "8-Port X.25 EISA-ACC (AMSO)"}, + {HPHW_FIO, 0x004, 0x00071, 0x0, "Cobra Core SCSI"}, + {HPHW_FIO, 0x005, 0x00071, 0x0, "Coral Core SCSI"}, + {HPHW_FIO, 0x006, 0x00071, 0x0, "Bushmaster Core SCSI"}, + {HPHW_FIO, 0x007, 0x00071, 0x0, "Scorpio Core SCSI"}, + {HPHW_FIO, 0x008, 0x00071, 0x0, "Flounder Core SCSI"}, + {HPHW_FIO, 0x009, 0x00071, 0x0, "Outfield Core SCSI"}, + {HPHW_FIO, 0x00A, 0x00071, 0x0, "CoralII Core SCSI"}, + {HPHW_FIO, 0x00B, 0x00071, 0x0, "Scorpio Jr. Core SCSI"}, + {HPHW_FIO, 0x00C, 0x00071, 0x0, "Strider-50 Core SCSI"}, + {HPHW_FIO, 0x00D, 0x00071, 0x0, "Strider-33 Core SCSI"}, + {HPHW_FIO, 0x00E, 0x00071, 0x0, "Trailways-50 Core SCSI"}, + {HPHW_FIO, 0x00F, 0x00071, 0x0, "Trailways-33 Core SCSI"}, + {HPHW_FIO, 0x010, 0x00071, 0x0, "Pace Core SCSI"}, + {HPHW_FIO, 0x011, 0x00071, 0x0, "Sidewinder Core SCSI"}, + {HPHW_FIO, 0x019, 0x00071, 0x0, "Scorpio Sr. Core SCSI"}, + {HPHW_FIO, 0x020, 0x00071, 0x0, "Scorpio 100 Core SCSI"}, + {HPHW_FIO, 0x021, 0x00071, 0x0, "Spectra 50 Core SCSI"}, + {HPHW_FIO, 0x022, 0x00071, 0x0, "Spectra 75 Core SCSI"}, + {HPHW_FIO, 0x023, 0x00071, 0x0, "Spectra 100 Core SCSI"}, + {HPHW_FIO, 0x024, 0x00071, 0x0, "Fast Pace Core SCSI"}, + {HPHW_FIO, 0x026, 0x00071, 0x0, "CoralII Jaguar Core SCSI"}, + {HPHW_FIO, 0x004, 0x00072, 0x0, "Cobra Core LAN (802.3)"}, + {HPHW_FIO, 0x005, 0x00072, 0x0, "Coral Core LAN (802.3)"}, + {HPHW_FIO, 0x006, 0x00072, 0x0, "Bushmaster Core LAN (802.3)"}, + {HPHW_FIO, 0x007, 0x00072, 0x0, "Scorpio Core LAN (802.3)"}, + {HPHW_FIO, 0x008, 0x00072, 0x0, "Flounder Core LAN (802.3)"}, + {HPHW_FIO, 0x009, 0x00072, 0x0, "Outfield Core LAN (802.3)"}, + {HPHW_FIO, 0x00A, 0x00072, 0x0, "CoralII Core LAN (802.3)"}, + {HPHW_FIO, 0x00B, 0x00072, 0x0, "Scorpio Jr. Core LAN (802.3)"}, + {HPHW_FIO, 0x00C, 0x00072, 0x0, "Strider-50 Core LAN (802.3)"}, + {HPHW_FIO, 0x00D, 0x00072, 0x0, "Strider-33 Core LAN (802.3)"}, + {HPHW_FIO, 0x00E, 0x00072, 0x0, "Trailways-50 Core LAN (802.3)"}, + {HPHW_FIO, 0x00F, 0x00072, 0x0, "Trailways-33 Core LAN (802.3)"}, + {HPHW_FIO, 0x010, 0x00072, 0x0, "Pace Core Lan (802.3)"}, + {HPHW_FIO, 0x011, 0x00072, 0x0, "Sidewinder Core Lan (802.3)"}, + {HPHW_FIO, 0x019, 0x00072, 0x0, "Scorpio Sr. Core LAN (802.3)"}, + {HPHW_FIO, 0x020, 0x00072, 0x0, "Scorpio 100 Core LAN (802.3)"}, + {HPHW_FIO, 0x021, 0x00072, 0x0, "Spectra 50 Core LAN (802.3)"}, + {HPHW_FIO, 0x022, 0x00072, 0x0, "Spectra 75 Core LAN (802.3)"}, + {HPHW_FIO, 0x023, 0x00072, 0x0, "Spectra 100 Core LAN (802.3)"}, + {HPHW_FIO, 0x024, 0x00072, 0x0, "Fast Pace Core Lan (802.3)"}, + {HPHW_FIO, 0x026, 0x00072, 0x0, "CoralII Jaguar Core LAN (802.3)"}, + {HPHW_FIO, 0x004, 0x00073, 0x0, "Cobra Core HIL"}, + {HPHW_FIO, 0x005, 0x00073, 0x0, "Coral Core HIL"}, + {HPHW_FIO, 0x006, 0x00073, 0x0, "Bushmaster Core HIL"}, + {HPHW_FIO, 0x007, 0x00073, 0x0, "Scorpio Core HIL"}, + {HPHW_FIO, 0x008, 0x00073, 0x0, "Flounder Core HIL"}, + {HPHW_FIO, 0x009, 0x00073, 0x0, "Outfield Core HIL"}, + {HPHW_FIO, 0x00A, 0x00073, 0x0, "CoralII Core HIL"}, + {HPHW_FIO, 0x00B, 0x00073, 0x0, "Scorpio Jr. Core HIL"}, + {HPHW_FIO, 0x00C, 0x00073, 0x0, "Strider-50 Core HIL"}, + {HPHW_FIO, 0x00D, 0x00073, 0x0, "Strider-33 Core HIL"}, + {HPHW_FIO, 0x00E, 0x00073, 0x0, "Trailways-50 Core HIL"}, + {HPHW_FIO, 0x00F, 0x00073, 0x0, "Trailways-33 Core HIL"}, + {HPHW_FIO, 0x010, 0x00073, 0x0, "Pace Core HIL"}, + {HPHW_FIO, 0x011, 0x00073, 0xcc, "SuperPace Wax HIL"}, + {HPHW_FIO, 0x012, 0x00073, 0x0, "Mirage Jr Wax HIL"}, + {HPHW_FIO, 0x013, 0x00073, 0x0, "Mirage 100 Wax HIL"}, + {HPHW_FIO, 0x014, 0x00073, 0x0, "Electra Wax HIL"}, + {HPHW_FIO, 0x017, 0x00073, 0x0, "Raven Backplane Wax HIL"}, + {HPHW_FIO, 0x019, 0x00073, 0x0, "Scorpio Sr. Core HIL"}, + {HPHW_FIO, 0x01E, 0x00073, 0x0, "Raven T' Wax HIL"}, + {HPHW_FIO, 0x01F, 0x00073, 0x0, "SkyHawk 100/120 Wax HIL"}, + {HPHW_FIO, 0x020, 0x00073, 0x0, "Scorpio 100 Core HIL"}, + {HPHW_FIO, 0x021, 0x00073, 0x0, "Spectra 50 Core HIL"}, + {HPHW_FIO, 0x022, 0x00073, 0x0, "Spectra 75 Core HIL"}, + {HPHW_FIO, 0x023, 0x00073, 0x0, "Spectra 100 Core HIL"}, + {HPHW_FIO, 0x024, 0x00073, 0x0, "Fast Pace Core HIL"}, + {HPHW_FIO, 0x026, 0x00073, 0x0, "CoralII Jaguar Core HIL"}, + {HPHW_FIO, 0x02B, 0x00073, 0x0, "Mirage 80 Wax HIL"}, + {HPHW_FIO, 0x02C, 0x00073, 0x0, "Mirage 100+ Wax HIL"}, + {HPHW_FIO, 0x03A, 0x00073, 0x0, "Merlin+ Wax HIL"}, + {HPHW_FIO, 0x040, 0x00073, 0x0, "Merlin 132 Wax HIL"}, + {HPHW_FIO, 0x041, 0x00073, 0x0, "Merlin 160 Wax HIL"}, + {HPHW_FIO, 0x043, 0x00073, 0x0, "Merlin 132/160 Wax HIL"}, + {HPHW_FIO, 0x052, 0x00073, 0x0, "Raven+ Hi Power Backplane w/EISA Wax HIL"}, + {HPHW_FIO, 0x053, 0x00073, 0x0, "Raven+ Hi Power Backplane wo/EISA Wax HIL"}, + {HPHW_FIO, 0x054, 0x00073, 0x0, "Raven+ Lo Power Backplane w/EISA Wax HIL"}, + {HPHW_FIO, 0x055, 0x00073, 0x0, "Raven+ Lo Power Backplane wo/EISA Wax HIL"}, + {HPHW_FIO, 0x059, 0x00073, 0x0, "FireHawk 200 Wax HIL"}, + {HPHW_FIO, 0x05A, 0x00073, 0x0, "Raven+ L2 Backplane w/EISA Wax HIL"}, + {HPHW_FIO, 0x05B, 0x00073, 0x0, "Raven+ L2 Backplane wo/EISA Wax HIL"}, + {HPHW_FIO, 0x05D, 0x00073, 0x0, "SummitHawk Wax HIL"}, + {HPHW_FIO, 0x800, 0x00073, 0x0, "Hitachi Tiny 64 Wax HIL"}, + {HPHW_FIO, 0x801, 0x00073, 0x0, "Hitachi Tiny 80 Wax HIL"}, + {HPHW_FIO, 0x004, 0x00074, 0x0, "Cobra Core Centronics"}, + {HPHW_FIO, 0x005, 0x00074, 0x0, "Coral Core Centronics"}, + {HPHW_FIO, 0x006, 0x00074, 0x0, "Bushmaster Core Centronics"}, + {HPHW_FIO, 0x007, 0x00074, 0x0, "Scorpio Core Centronics"}, + {HPHW_FIO, 0x008, 0x00074, 0x0, "Flounder Core Centronics"}, + {HPHW_FIO, 0x009, 0x00074, 0x0, "Outfield Core Centronics"}, + {HPHW_FIO, 0x00A, 0x00074, 0x0, "CoralII Core Centronics"}, + {HPHW_FIO, 0x00B, 0x00074, 0x0, "Scorpio Jr. Core Centronics"}, + {HPHW_FIO, 0x00C, 0x00074, 0x0, "Strider-50 Core Centronics"}, + {HPHW_FIO, 0x00D, 0x00074, 0x0, "Strider-33 Core Centronics"}, + {HPHW_FIO, 0x00E, 0x00074, 0x0, "Trailways-50 Core Centronics"}, + {HPHW_FIO, 0x00F, 0x00074, 0x0, "Trailways-33 Core Centronics"}, + {HPHW_FIO, 0x010, 0x00074, 0x0, "Pace Core Centronics"}, + {HPHW_FIO, 0x011, 0x00074, 0x0, "Sidewinder Core Centronics"}, + {HPHW_FIO, 0x015, 0x00074, 0x0, "KittyHawk GSY Core Centronics"}, + {HPHW_FIO, 0x016, 0x00074, 0x0, "Gecko Core Centronics"}, + {HPHW_FIO, 0x019, 0x00074, 0x0, "Scorpio Sr. Core Centronics"}, + {HPHW_FIO, 0x01A, 0x00074, 0x0, "Anole 64 Core Centronics"}, + {HPHW_FIO, 0x01B, 0x00074, 0x0, "Anole 100 Core Centronics"}, + {HPHW_FIO, 0x01C, 0x00074, 0x0, "Gecko 80 Core Centronics"}, + {HPHW_FIO, 0x01D, 0x00074, 0x0, "Gecko 100 Core Centronics"}, + {HPHW_FIO, 0x01F, 0x00074, 0x0, "SkyHawk 100/120 Core Centronics"}, + {HPHW_FIO, 0x020, 0x00074, 0x0, "Scorpio 100 Core Centronics"}, + {HPHW_FIO, 0x021, 0x00074, 0x0, "Spectra 50 Core Centronics"}, + {HPHW_FIO, 0x022, 0x00074, 0x0, "Spectra 75 Core Centronics"}, + {HPHW_FIO, 0x023, 0x00074, 0x0, "Spectra 100 Core Centronics"}, + {HPHW_FIO, 0x024, 0x00074, 0x0, "Fast Pace Core Centronics"}, + {HPHW_FIO, 0x026, 0x00074, 0x0, "CoralII Jaguar Core Centronics"}, + {HPHW_FIO, 0x027, 0x00074, 0x0, "Piranha 100 Core Centronics"}, + {HPHW_FIO, 0x028, 0x00074, 0x0, "Mirage Jr Core Centronics"}, + {HPHW_FIO, 0x029, 0x00074, 0x0, "Mirage Core Centronics"}, + {HPHW_FIO, 0x02A, 0x00074, 0x0, "Electra Core Centronics"}, + {HPHW_FIO, 0x02B, 0x00074, 0x0, "Mirage 80 Core Centronics"}, + {HPHW_FIO, 0x02C, 0x00074, 0x0, "Mirage 100+ Core Centronics"}, + {HPHW_FIO, 0x02E, 0x00074, 0x0, "UL 350 Core Centronics"}, + {HPHW_FIO, 0x02F, 0x00074, 0x0, "UL 550 Core Centronics"}, + {HPHW_FIO, 0x032, 0x00074, 0x0, "Raven T' Core Centronics"}, + {HPHW_FIO, 0x033, 0x00074, 0x0, "Anole T Core Centronics"}, + {HPHW_FIO, 0x034, 0x00074, 0x0, "SAIC L-80 Core Centronics"}, + {HPHW_FIO, 0x035, 0x00074, 0x0, "PCX-L2 712/132 Core Centronics"}, + {HPHW_FIO, 0x036, 0x00074, 0x0, "PCX-L2 712/160 Core Centronics"}, + {HPHW_FIO, 0x03B, 0x00074, 0x0, "Raven U/L2 Core Centronics"}, + {HPHW_FIO, 0x03C, 0x00074, 0x0, "Merlin 132 Core Centronics"}, + {HPHW_FIO, 0x03D, 0x00074, 0x0, "Merlin 160 Core Centronics"}, + {HPHW_FIO, 0x03E, 0x00074, 0x0, "Merlin+ 132 Core Centronics"}, + {HPHW_FIO, 0x03F, 0x00074, 0x0, "Merlin+ 180 Core Centronics"}, + {HPHW_FIO, 0x044, 0x00074, 0x0, "Mohawk Core Centronics"}, + {HPHW_FIO, 0x045, 0x00074, 0x0, "Rocky1 Core Centronics"}, + {HPHW_FIO, 0x046, 0x00074, 0x0, "Rocky2 120 Core Centronics"}, + {HPHW_FIO, 0x047, 0x00074, 0x0, "Rocky2 150 Core Centronics"}, + {HPHW_FIO, 0x04B, 0x00074, 0x0, "Anole L2 132 Core Centronics"}, + {HPHW_FIO, 0x04D, 0x00074, 0x0, "Anole L2 165 Core Centronics"}, + {HPHW_FIO, 0x050, 0x00074, 0x0, "Merlin Jr 132 Core Centronics"}, + {HPHW_FIO, 0x051, 0x00074, 0x0, "Firehawk Core Centronics"}, + {HPHW_FIO, 0x056, 0x00074, 0x0, "Raven+ wSE FWSCSI Core Centronics"}, + {HPHW_FIO, 0x057, 0x00074, 0x0, "Raven+ wDiff FWSCSI Core Centronics"}, + {HPHW_FIO, 0x058, 0x00074, 0x0, "FireHawk 200 Core Centronics"}, + {HPHW_FIO, 0x05C, 0x00074, 0x0, "SummitHawk 230 Core Centronics"}, + {HPHW_FIO, 0x800, 0x00074, 0x0, "Hitachi Tiny 64 Core Centronics"}, + {HPHW_FIO, 0x801, 0x00074, 0x0, "Hitachi Tiny 80 Core Centronics"}, + {HPHW_FIO, 0x004, 0x00075, 0x0, "Cobra Core RS-232"}, + {HPHW_FIO, 0x005, 0x00075, 0x0, "Coral Core RS-232"}, + {HPHW_FIO, 0x006, 0x00075, 0x0, "Bushmaster Core RS-232"}, + {HPHW_FIO, 0x007, 0x00075, 0x0, "Scorpio Core RS-232"}, + {HPHW_FIO, 0x008, 0x00075, 0x0, "Flounder Core RS-232"}, + {HPHW_FIO, 0x009, 0x00075, 0x0, "Outfield Core RS-232"}, + {HPHW_FIO, 0x00A, 0x00075, 0x0, "CoralII Core RS-232"}, + {HPHW_FIO, 0x00B, 0x00075, 0x0, "Scorpio Jr. Core RS-232"}, + {HPHW_FIO, 0x00C, 0x00075, 0x0, "Strider-50 Core RS-232"}, + {HPHW_FIO, 0x00D, 0x00075, 0x0, "Strider-33 Core RS-232"}, + {HPHW_FIO, 0x00E, 0x00075, 0x0, "Trailways-50 Core RS-232"}, + {HPHW_FIO, 0x00F, 0x00075, 0x0, "Trailways-33 Core RS-232"}, + {HPHW_FIO, 0x010, 0x00075, 0x0, "Pace Core RS-232"}, + {HPHW_FIO, 0x011, 0x00075, 0x0, "Sidewinder Core RS-232"}, + {HPHW_FIO, 0x019, 0x00075, 0x0, "Scorpio Sr. Core RS-232"}, + {HPHW_FIO, 0x020, 0x00075, 0x0, "Scorpio 100 Core RS-232"}, + {HPHW_FIO, 0x021, 0x00075, 0x0, "Spectra 50 Core RS-232"}, + {HPHW_FIO, 0x022, 0x00075, 0x0, "Spectra 75 Core RS-232"}, + {HPHW_FIO, 0x023, 0x00075, 0x0, "Spectra 100 Core RS-232"}, + {HPHW_FIO, 0x024, 0x00075, 0x0, "Fast Pace Core RS-232"}, + {HPHW_FIO, 0x026, 0x00075, 0x0, "CoralII Jaguar Core RS-232"}, + {HPHW_FIO, 0x004, 0x00077, 0x0, "Coral SGC Graphics"}, + {HPHW_FIO, 0x005, 0x00077, 0x0, "Hyperdrive Optional Graphics"}, + {HPHW_FIO, 0x006, 0x00077, 0x0, "Stinger Optional Graphics"}, + {HPHW_FIO, 0x007, 0x00077, 0x0, "Scorpio Builtin Graphics"}, + {HPHW_FIO, 0x008, 0x00077, 0x0, "Anole Hyperdrive Optional Graphics"}, + {HPHW_FIO, 0x009, 0x00077, 0x0, "Thunder II graphics EISA form"}, + {HPHW_FIO, 0x00A, 0x00077, 0x0, "Thunder II graphics GSA form"}, + {HPHW_FIO, 0x00B, 0x00077, 0x0, "Scorpio Jr Builtin Graphics"}, + {HPHW_FIO, 0x00C, 0x00077, 0x0, "Strider-50 SSC Graphics"}, + {HPHW_FIO, 0x00D, 0x00077, 0x0, "Strider-33 SSC Graphics"}, + {HPHW_FIO, 0x00E, 0x00077, 0x0, "Trailways-50 SSC Graphics"}, + {HPHW_FIO, 0x00F, 0x00077, 0x0, "Trailways-33 SSC Graphics"}, + {HPHW_FIO, 0x010, 0x00077, 0x0, "Pace SGC Graphics"}, + {HPHW_FIO, 0x011, 0x00077, 0x0, "Mohawk Opt. 2D Graphics (Kid)"}, + {HPHW_FIO, 0x012, 0x00077, 0x0, "Raven Opt. 2D Graphics (Goat)"}, + {HPHW_FIO, 0x016, 0x00077, 0x0, "Lego 24 SCG Graphics"}, + {HPHW_FIO, 0x017, 0x00077, 0x0, "Lego 24Z SCG Graphics"}, + {HPHW_FIO, 0x018, 0x00077, 0x0, "Lego 48Z SCG Graphics"}, + {HPHW_FIO, 0x019, 0x00077, 0x0, "Scorpio Sr Builtin Graphics"}, + {HPHW_FIO, 0x020, 0x00077, 0x0, "Scorpio 100 Builtin Graphics"}, + {HPHW_FIO, 0x021, 0x00077, 0x0, "Spectra 50 Builtin Graphics"}, + {HPHW_FIO, 0x022, 0x00077, 0x0, "Spectra 75 Builtin Graphics"}, + {HPHW_FIO, 0x023, 0x00077, 0x0, "Spectra 100 Builtin Graphics"}, + {HPHW_FIO, 0x024, 0x00077, 0x0, "Fast Pace SGC Graphics"}, + {HPHW_FIO, 0x006, 0x0007A, 0x0, "Bushmaster Audio"}, + {HPHW_FIO, 0x008, 0x0007A, 0x0, "Flounder Audio"}, + {HPHW_FIO, 0x004, 0x0007B, 0x0, "UL Optional Audio"}, + {HPHW_FIO, 0x007, 0x0007B, 0x0, "Scorpio Audio"}, + {HPHW_FIO, 0x00B, 0x0007B, 0x0, "Scorpio Jr. Audio"}, + {HPHW_FIO, 0x00C, 0x0007B, 0x0, "Strider-50 Audio"}, + {HPHW_FIO, 0x00D, 0x0007B, 0x0, "Strider-33 Audio"}, + {HPHW_FIO, 0x00E, 0x0007B, 0x0, "Trailways-50 Audio"}, + {HPHW_FIO, 0x00F, 0x0007B, 0x0, "Trailways-33 Audio"}, + {HPHW_FIO, 0x016, 0x0007B, 0x0, "Gecko Audio"}, + {HPHW_FIO, 0x019, 0x0007B, 0x0, "Scorpio Sr. Audio"}, + {HPHW_FIO, 0x01A, 0x0007B, 0x0, "Anole 64 Audio"}, + {HPHW_FIO, 0x01B, 0x0007B, 0x0, "Anole 100 Audio"}, + {HPHW_FIO, 0x01C, 0x0007B, 0x0, "Gecko 80 Audio"}, + {HPHW_FIO, 0x01D, 0x0007B, 0x0, "Gecko 100 Audio"}, + {HPHW_FIO, 0x01F, 0x0007B, 0x0, "SkyHawk 100/120 Audio"}, + {HPHW_FIO, 0x020, 0x0007B, 0x0, "Scorpio 100 Audio"}, + {HPHW_FIO, 0x021, 0x0007B, 0x0, "Spectra 50 Audio"}, + {HPHW_FIO, 0x022, 0x0007B, 0x0, "Spectra 75 Audio"}, + {HPHW_FIO, 0x023, 0x0007B, 0x0, "Spectra 100 Audio"}, + {HPHW_FIO, 0x028, 0x0007B, 0x0, "Mirage Jr Audio"}, + {HPHW_FIO, 0x029, 0x0007B, 0x0, "Mirage Audio"}, + {HPHW_FIO, 0x02A, 0x0007B, 0x0, "Electra Audio"}, + {HPHW_FIO, 0x02B, 0x0007B, 0x0, "Mirage 80 Audio"}, + {HPHW_FIO, 0x02C, 0x0007B, 0x0, "Mirage 100+ Audio"}, + {HPHW_FIO, 0x032, 0x0007B, 0x0, "Raven T' Audio"}, + {HPHW_FIO, 0x034, 0x0007B, 0x0, "SAIC L-80 Audio"}, + {HPHW_FIO, 0x035, 0x0007B, 0x0, "PCX-L2 712/132 Core Audio"}, + {HPHW_FIO, 0x036, 0x0007B, 0x0, "PCX-L2 712/160 Core Audio"}, + {HPHW_FIO, 0x03B, 0x0007B, 0x0, "Raven U/L2 Core Audio"}, + {HPHW_FIO, 0x03C, 0x0007B, 0x0, "Merlin 132 Core Audio"}, + {HPHW_FIO, 0x03D, 0x0007B, 0x0, "Merlin 160 Core Audio"}, + {HPHW_FIO, 0x03E, 0x0007B, 0x0, "Merlin+ 132 Core Audio"}, + {HPHW_FIO, 0x03F, 0x0007B, 0x0, "Merlin+ 180 Core Audio"}, + {HPHW_FIO, 0x044, 0x0007B, 0x0, "Mohawk Core Audio"}, + {HPHW_FIO, 0x046, 0x0007B, 0x0, "Rocky2 120 Core Audio"}, + {HPHW_FIO, 0x047, 0x0007B, 0x0, "Rocky2 150 Core Audio"}, + {HPHW_FIO, 0x04B, 0x0007B, 0x0, "Anole L2 132 Core Audio"}, + {HPHW_FIO, 0x04D, 0x0007B, 0x0, "Anole L2 165 Core Audio"}, + {HPHW_FIO, 0x04E, 0x0007B, 0x0, "Kiji L2 132 Core Audio"}, + {HPHW_FIO, 0x050, 0x0007B, 0x0, "Merlin Jr 132 Core Audio"}, + {HPHW_FIO, 0x051, 0x0007B, 0x0, "Firehawk Audio"}, + {HPHW_FIO, 0x056, 0x0007B, 0x0, "Raven+ w SE FWSCSU Core Audio"}, + {HPHW_FIO, 0x057, 0x0007B, 0x0, "Raven+ w Diff FWSCSU Core Audio"}, + {HPHW_FIO, 0x058, 0x0007B, 0x0, "FireHawk 200 Audio"}, + {HPHW_FIO, 0x05C, 0x0007B, 0x0, "SummitHawk 230 Core Audio"}, + {HPHW_FIO, 0x800, 0x0007B, 0x0, "Hitachi Tiny 64 Audio"}, + {HPHW_FIO, 0x801, 0x0007B, 0x0, "Hitachi Tiny 80 Audio"}, + {HPHW_FIO, 0x009, 0x0007C, 0x0, "Outfield FW SCSI"}, + {HPHW_FIO, 0x00A, 0x0007C, 0x0, "CoralII FW SCSI"}, + {HPHW_FIO, 0x026, 0x0007C, 0x0, "CoralII Jaguar FW SCSI"}, + {HPHW_FIO, 0x009, 0x0007D, 0x0, "Outfield FDDI"}, + {HPHW_FIO, 0x00A, 0x0007D, 0x0, "CoralII FDDI"}, + {HPHW_FIO, 0x026, 0x0007D, 0x0, "CoralII Jaguar FDDI"}, + {HPHW_FIO, 0x010, 0x0007E, 0x0, "Pace Audio"}, + {HPHW_FIO, 0x024, 0x0007E, 0x0, "Fast Pace Audio"}, + {HPHW_FIO, 0x009, 0x0007F, 0x0, "Outfield Audio"}, + {HPHW_FIO, 0x00A, 0x0007F, 0x0, "CoralII Audio"}, + {HPHW_FIO, 0x026, 0x0007F, 0x0, "CoralII Jaguar Audio"}, + {HPHW_FIO, 0x010, 0x00080, 0x0, "Pace Core HPIB"}, + {HPHW_FIO, 0x024, 0x00080, 0x0, "Fast Pace Core HPIB"}, + {HPHW_FIO, 0x016, 0x00082, 0x0, "Gecko Core SCSI"}, + {HPHW_FIO, 0x01A, 0x00082, 0x0, "Anole 64 Core SCSI"}, + {HPHW_FIO, 0x01B, 0x00082, 0x0, "Anole 100 Core SCSI"}, + {HPHW_FIO, 0x01C, 0x00082, 0x0, "Gecko 80 Core SCSI"}, + {HPHW_FIO, 0x01D, 0x00082, 0x0, "Gecko 100 Core SCSI"}, + {HPHW_FIO, 0x01F, 0x00082, 0x0, "SkyHawk 100/120 Core SCSI"}, + {HPHW_FIO, 0x027, 0x00082, 0x0, "Piranha 100 Core SCSI"}, + {HPHW_FIO, 0x028, 0x00082, 0x0, "Mirage Jr Core SCSI"}, + {HPHW_FIO, 0x029, 0x00082, 0x0, "Mirage Core SCSI"}, + {HPHW_FIO, 0x02A, 0x00082, 0x0, "Electra Core SCSI"}, + {HPHW_FIO, 0x02B, 0x00082, 0x0, "Mirage 80 Core SCSI"}, + {HPHW_FIO, 0x02C, 0x00082, 0x0, "Mirage 100+ Core SCSI"}, + {HPHW_FIO, 0x02E, 0x00082, 0x0, "UL 350 Core SCSI"}, + {HPHW_FIO, 0x02F, 0x00082, 0x0, "UL 550 Core SCSI"}, + {HPHW_FIO, 0x032, 0x00082, 0x0, "Raven T' Core SCSI"}, + {HPHW_FIO, 0x033, 0x00082, 0x0, "Anole T Core SCSI"}, + {HPHW_FIO, 0x034, 0x00082, 0x0, "SAIC L-80 Core SCSI"}, + {HPHW_FIO, 0x035, 0x00082, 0x0, "PCX-L2 712/132 Core SCSI"}, + {HPHW_FIO, 0x036, 0x00082, 0x0, "PCX-L2 712/160 Core SCSI"}, + {HPHW_FIO, 0x03B, 0x00082, 0x0, "Raven U/L2 Core SCSI"}, + {HPHW_FIO, 0x03C, 0x00082, 0x0, "Merlin 132 Core SCSI"}, + {HPHW_FIO, 0x03D, 0x00082, 0x0, "Merlin 160 Core SCSI"}, + {HPHW_FIO, 0x03E, 0x00082, 0x0, "Merlin+ 132 Core SCSI"}, + {HPHW_FIO, 0x03F, 0x00082, 0x0, "Merlin+ 180 Core SCSI"}, + {HPHW_FIO, 0x044, 0x00082, 0x0, "Mohawk Core SCSI"}, + {HPHW_FIO, 0x045, 0x00082, 0x0, "Rocky1 Core SCSI"}, + {HPHW_FIO, 0x046, 0x00082, 0x0, "Rocky2 120 Core SCSI"}, + {HPHW_FIO, 0x047, 0x00082, 0x0, "Rocky2 150 Core SCSI"}, + {HPHW_FIO, 0x04B, 0x00082, 0x0, "Anole L2 132 Core SCSI"}, + {HPHW_FIO, 0x04D, 0x00082, 0x0, "Anole L2 165 Core SCSI"}, + {HPHW_FIO, 0x04E, 0x00082, 0x0, "Kiji L2 132 Core SCSI"}, + {HPHW_FIO, 0x050, 0x00082, 0x0, "Merlin Jr 132 Core SCSI"}, + {HPHW_FIO, 0x051, 0x00082, 0x0, "Firehawk Core SCSI"}, + {HPHW_FIO, 0x056, 0x00082, 0x0, "Raven+ w SE FWSCSI Core SCSI"}, + {HPHW_FIO, 0x057, 0x00082, 0x0, "Raven+ w Diff FWSCSI Core SCSI"}, + {HPHW_FIO, 0x058, 0x00082, 0x0, "FireHawk 200 Core SCSI"}, + {HPHW_FIO, 0x05C, 0x00082, 0x0, "SummitHawk 230 Core SCSI"}, + {HPHW_FIO, 0x05E, 0x00082, 0x0, "Staccato 132 Core SCSI"}, + {HPHW_FIO, 0x05F, 0x00082, 0x0, "Staccato 180 Core SCSI"}, + {HPHW_FIO, 0x800, 0x00082, 0x0, "Hitachi Tiny 64 Core SCSI"}, + {HPHW_FIO, 0x801, 0x00082, 0x0, "Hitachi Tiny 80 Core SCSI"}, + {HPHW_FIO, 0x016, 0x00083, 0x0, "Gecko Core PC Floppy"}, + {HPHW_FIO, 0x01C, 0x00083, 0x0, "Gecko 80 Core PC Floppy"}, + {HPHW_FIO, 0x01D, 0x00083, 0x0, "Gecko 100 Core PC Floppy"}, + {HPHW_FIO, 0x051, 0x00083, 0x0, "Firehawk Core PC Floppy"}, + {HPHW_FIO, 0x058, 0x00083, 0x0, "FireHawk 200 Core PC Floppy"}, + {HPHW_FIO, 0x027, 0x00083, 0x0, "Piranha 100 Core PC Floppy"}, + {HPHW_FIO, 0x028, 0x00083, 0x0, "Mirage Jr Core PC Floppy"}, + {HPHW_FIO, 0x029, 0x00083, 0x0, "Mirage Core PC Floppy"}, + {HPHW_FIO, 0x02A, 0x00083, 0x0, "Electra Core PC Floppy"}, + {HPHW_FIO, 0x02B, 0x00083, 0x0, "Mirage 80 Core PC Floppy"}, + {HPHW_FIO, 0x02C, 0x00083, 0x0, "Mirage 100+ Core PC Floppy"}, + {HPHW_FIO, 0x02E, 0x00083, 0x0, "UL 350 Core PC Floppy"}, + {HPHW_FIO, 0x02F, 0x00083, 0x0, "UL 550 Core PC Floppy"}, + {HPHW_FIO, 0x032, 0x00083, 0x0, "Raven T' Core PC Floppy"}, + {HPHW_FIO, 0x034, 0x00083, 0x0, "SAIC L-80 Core PC Floopy"}, + {HPHW_FIO, 0x035, 0x00083, 0x0, "PCX-L2 712/132 Core Floopy"}, + {HPHW_FIO, 0x036, 0x00083, 0x0, "PCX-L2 712/160 Core Floopy"}, + {HPHW_FIO, 0x03B, 0x00083, 0x0, "Raven U/L2 Core PC Floopy"}, + {HPHW_FIO, 0x03C, 0x00083, 0x0, "Merlin 132 Core PC Floopy"}, + {HPHW_FIO, 0x03D, 0x00083, 0x0, "Merlin 160 Core PC Floopy"}, + {HPHW_FIO, 0x03E, 0x00083, 0x0, "Merlin+ 132 Core PC Floopy"}, + {HPHW_FIO, 0x03F, 0x00083, 0x0, "Merlin+ 180 Core PC Floopy"}, + {HPHW_FIO, 0x045, 0x00083, 0x0, "Rocky1 Core PC Floopy"}, + {HPHW_FIO, 0x046, 0x00083, 0x0, "Rocky2 120 Core PC Floopy"}, + {HPHW_FIO, 0x047, 0x00083, 0x0, "Rocky2 150 Core PC Floopy"}, + {HPHW_FIO, 0x04E, 0x00083, 0x0, "Kiji L2 132 Core PC Floopy"}, + {HPHW_FIO, 0x050, 0x00083, 0x0, "Merlin Jr 132 Core PC Floopy"}, + {HPHW_FIO, 0x056, 0x00083, 0x0, "Raven+ w SE FWSCSI Core PC Floopy"}, + {HPHW_FIO, 0x057, 0x00083, 0x0, "Raven+ w Diff FWSCSI Core PC Floopy"}, + {HPHW_FIO, 0x800, 0x00083, 0x0, "Hitachi Tiny 64 Core PC Floopy"}, + {HPHW_FIO, 0x801, 0x00083, 0x0, "Hitachi Tiny 80 Core PC Floopy"}, + {HPHW_FIO, 0x015, 0x00084, 0x0, "KittyHawk GSY Core PC Keyboard"}, + {HPHW_FIO, 0x016, 0x00084, 0x0, "Gecko Core PC Keyboard"}, + {HPHW_FIO, 0x018, 0x00084, 0x0, "Gecko Optional PC Keyboard"}, + {HPHW_FIO, 0x01A, 0x00084, 0x0, "Anole 64 Core PC Keyboard"}, + {HPHW_FIO, 0x01B, 0x00084, 0x0, "Anole 100 Core PC Keyboard"}, + {HPHW_FIO, 0x01C, 0x00084, 0x0, "Gecko 80 Core PC Keyboard"}, + {HPHW_FIO, 0x01D, 0x00084, 0x0, "Gecko 100 Core PC Keyboard"}, + {HPHW_FIO, 0x01F, 0x00084, 0x0, "SkyHawk 100/120 Core PC Keyboard"}, + {HPHW_FIO, 0x027, 0x00084, 0x0, "Piranha 100 Core PC Keyboard"}, + {HPHW_FIO, 0x028, 0x00084, 0x0, "Mirage Jr Core PC Keyboard"}, + {HPHW_FIO, 0x029, 0x00084, 0x0, "Mirage Core PC Keyboard"}, + {HPHW_FIO, 0x02A, 0x00084, 0x0, "Electra Core PC Keyboard"}, + {HPHW_FIO, 0x02B, 0x00084, 0x0, "Mirage 80 Core PC Keyboard"}, + {HPHW_FIO, 0x02C, 0x00084, 0x0, "Mirage 100+ Core PC Keyboard"}, + {HPHW_FIO, 0x02E, 0x00084, 0x0, "UL 350 Core PC Keyboard"}, + {HPHW_FIO, 0x02F, 0x00084, 0x0, "UL 550 Core PC Keyboard"}, + {HPHW_FIO, 0x032, 0x00084, 0x0, "Raven T' Core PC Keyboard"}, + {HPHW_FIO, 0x033, 0x00084, 0x0, "Anole T Core PC Keyboard"}, + {HPHW_FIO, 0x034, 0x00084, 0x0, "SAIC L-80 Core PC Keyboard"}, + {HPHW_FIO, 0x035, 0x00084, 0x0, "PCX-L2 712/132 Core Keyboard"}, + {HPHW_FIO, 0x036, 0x00084, 0x0, "PCX-L2 712/160 Core Keyboard"}, + {HPHW_FIO, 0x03B, 0x00084, 0x0, "Raven U/L2 Core PC Keyboard"}, + {HPHW_FIO, 0x03C, 0x00084, 0x0, "Merlin 132 Core PC Keyboard"}, + {HPHW_FIO, 0x03D, 0x00084, 0x0, "Merlin 160 Core PC Keyboard"}, + {HPHW_FIO, 0x03E, 0x00084, 0x0, "Merlin+ 132 Core PC Keyboard"}, + {HPHW_FIO, 0x03F, 0x00084, 0x0, "Merlin+ 180 Core PC Keyboard"}, + {HPHW_FIO, 0x044, 0x00084, 0x0, "Mohawk Core PC Keyboard"}, + {HPHW_FIO, 0x045, 0x00084, 0x0, "Rocky1 Core PC Keyboard"}, + {HPHW_FIO, 0x046, 0x00084, 0x0, "Rocky2 120 Core PC Keyboard"}, + {HPHW_FIO, 0x047, 0x00084, 0x0, "Rocky2 150 Core PC Keyboard"}, + {HPHW_FIO, 0x048, 0x00084, 0x0, "Rocky2 120 Dino PC Keyboard"}, + {HPHW_FIO, 0x049, 0x00084, 0x0, "Rocky2 150 Dino PC Keyboard"}, + {HPHW_FIO, 0x04B, 0x00084, 0x0, "Anole L2 132 Core PC Keyboard"}, + {HPHW_FIO, 0x04D, 0x00084, 0x0, "Anole L2 165 Core PC Keyboard"}, + {HPHW_FIO, 0x04E, 0x00084, 0x0, "Kiji L2 132 Core PC Keyboard"}, + {HPHW_FIO, 0x050, 0x00084, 0x0, "Merlin Jr 132 Core PC Keyboard"}, + {HPHW_FIO, 0x051, 0x00084, 0x0, "Firehawk Core PC Keyboard"}, + {HPHW_FIO, 0x056, 0x00084, 0x0, "Raven+ w SE FWSCSI Core PC Keyboard"}, + {HPHW_FIO, 0x057, 0x00084, 0x0, "Raven+ w Diff FWSCSI Core PC Keyboard"}, + {HPHW_FIO, 0x058, 0x00084, 0x0, "FireHawk 200 Core PC Keyboard"}, + {HPHW_FIO, 0x05C, 0x00084, 0x0, "SummitHawk 230 Core PC Keyboard"}, + {HPHW_FIO, 0x800, 0x00084, 0x0, "Hitachi Tiny 64 Core PC Keyboard"}, + {HPHW_FIO, 0x801, 0x00084, 0x0, "Hitachi Tiny 80 Core PC Keyboard"}, + {HPHW_FIO, 0x004, 0x00085, 0x0, "Solo GSC Optional Graphics"}, + {HPHW_FIO, 0x005, 0x00085, 0x0, "Duet GSC Optional Graphics"}, + {HPHW_FIO, 0x008, 0x00085, 0x0, "Anole Artist Optional Graphics"}, + {HPHW_FIO, 0x010, 0x00085, 0x0, "Mirage 80 GSC Builtin Graphics"}, + {HPHW_FIO, 0x011, 0x00085, 0x0, "Mirage 100+ GSC Builtin Graphics"}, + {HPHW_FIO, 0x012, 0x00085, 0x0, "Mirage Jr GSC Builtin Graphics"}, + {HPHW_FIO, 0x013, 0x00085, 0x0, "Mirage GSC Builtin Graphics"}, + {HPHW_FIO, 0x014, 0x00085, 0x0, "Electra GSC Builtin Graphics"}, + {HPHW_FIO, 0x016, 0x00085, 0x0, "Gecko GSC Core Graphics"}, + {HPHW_FIO, 0x017, 0x00085, 0x0, "Gecko GSC Optional Graphics"}, + {HPHW_FIO, 0x01A, 0x00085, 0x0, "Anole 64 Artist Builtin Graphics"}, + {HPHW_FIO, 0x01B, 0x00085, 0x0, "Anole 100 Artist Builtin Graphics"}, + {HPHW_FIO, 0x01C, 0x00085, 0x0, "Gecko 80 GSC Core Graphics"}, + {HPHW_FIO, 0x01D, 0x00085, 0x0, "Gecko 100 GSC Core Graphics"}, + {HPHW_FIO, 0x032, 0x00085, 0x0, "Raven T' GSC Core Graphics"}, + {HPHW_FIO, 0x033, 0x00085, 0x0, "Anole T Artist Builtin Graphics"}, + {HPHW_FIO, 0x034, 0x00085, 0x0, "SAIC L-80 GSC Core Graphics"}, + {HPHW_FIO, 0x035, 0x00085, 0x0, "PCX-L2 712/132 Core Graphics"}, + {HPHW_FIO, 0x036, 0x00085, 0x0, "PCX-L2 712/160 Core Graphics"}, + {HPHW_FIO, 0x03B, 0x00085, 0x0, "Raven U/L2 Core Graphics"}, + {HPHW_FIO, 0x03C, 0x00085, 0x0, "Merlin 132 Core Graphics"}, + {HPHW_FIO, 0x03D, 0x00085, 0x0, "Merlin 160 Core Graphics"}, + {HPHW_FIO, 0x03E, 0x00085, 0x0, "Merlin+ 132 Core Graphics"}, + {HPHW_FIO, 0x03F, 0x00085, 0x0, "Merlin+ 180 Core Graphics"}, + {HPHW_FIO, 0x045, 0x00085, 0x0, "Rocky1 Core Graphics"}, + {HPHW_FIO, 0x046, 0x00085, 0x0, "Rocky2 120 Core Graphics"}, + {HPHW_FIO, 0x047, 0x00085, 0x0, "Rocky2 150 Core Graphics"}, + {HPHW_FIO, 0x04B, 0x00085, 0x0, "Anole L2 132 Core Graphics"}, + {HPHW_FIO, 0x04D, 0x00085, 0x0, "Anole L2 165 Core Graphics"}, + {HPHW_FIO, 0x04E, 0x00085, 0x0, "Kiji L2 132 Core Graphics"}, + {HPHW_FIO, 0x050, 0x00085, 0x0, "Merlin Jr 132 Core Graphics"}, + {HPHW_FIO, 0x056, 0x00085, 0x0, "Raven+ w SE FWSCSI Core Graphics"}, + {HPHW_FIO, 0x057, 0x00085, 0x0, "Raven+ w Diff FWSCSI Core Graphics"}, + {HPHW_FIO, 0x800, 0x00085, 0x0, "Hitachi Tiny 64 Core Graphics"}, + {HPHW_FIO, 0x801, 0x00085, 0x0, "Hitachi Tiny 80 Core Graphics"}, + {HPHW_FIO, 0x004, 0x00086, 0x0, "GSC IBM Token Ring"}, + {HPHW_FIO, 0x015, 0x00087, 0x0, "Gecko Optional ISDN"}, + {HPHW_FIO, 0x016, 0x00087, 0x0, "Gecko Core ISDN"}, + {HPHW_FIO, 0x01C, 0x00087, 0x0, "Gecko 80 Core ISDN"}, + {HPHW_FIO, 0x01D, 0x00087, 0x0, "Gecko 100 Core ISDN"}, + {HPHW_FIO, 0x010, 0x00088, 0x0, "Pace VME Networking"}, + {HPHW_FIO, 0x011, 0x00088, 0x0, "Sidewinder VME Networking"}, + {HPHW_FIO, 0x01A, 0x00088, 0x0, "Anole 64 VME Networking"}, + {HPHW_FIO, 0x01B, 0x00088, 0x0, "Anole 100 VME Networking"}, + {HPHW_FIO, 0x024, 0x00088, 0x0, "Fast Pace VME Networking"}, + {HPHW_FIO, 0x034, 0x00088, 0x0, "Anole T VME Networking"}, + {HPHW_FIO, 0x04A, 0x00088, 0x0, "Anole L2 132 VME Networking"}, + {HPHW_FIO, 0x04C, 0x00088, 0x0, "Anole L2 165 VME Networking"}, + {HPHW_FIO, 0x03B, 0x00089, 0x0, "Raven U/L2 Core FW-SCSI"}, + {HPHW_FIO, 0x011, 0x0008A, 0x0, "WB-96 Core Lan (802.3)"}, + {HPHW_FIO, 0x012, 0x0008A, 0x0, "Orville Core Lan (802.3)"}, + {HPHW_FIO, 0x013, 0x0008A, 0x0, "Wilbur Core Lan (802.3)"}, + {HPHW_FIO, 0x014, 0x0008A, 0x0, "WB-80 Core Lan (802.3)"}, + {HPHW_FIO, 0x015, 0x0008A, 0x0, "KittyHawk GSY Core Lan (802.3)"}, + {HPHW_FIO, 0x016, 0x0008A, 0x0, "Gecko Core Lan (802.3)"}, + {HPHW_FIO, 0x018, 0x0008A, 0x0, "Gecko Optional Lan (802.3)"}, + {HPHW_FIO, 0x01A, 0x0008A, 0x0, "Anole 64 Core Lan (802.3)"}, + {HPHW_FIO, 0x01B, 0x0008A, 0x0, "Anole 100 Core Lan (802.3)"}, + {HPHW_FIO, 0x01C, 0x0008A, 0x0, "Gecko 80 Core Lan (802.3)"}, + {HPHW_FIO, 0x01D, 0x0008A, 0x0, "Gecko 100 Core Lan (802.3)"}, + {HPHW_FIO, 0x01F, 0x0008A, 0x0, "SkyHawk 100/120 Core LAN (802.3)"}, + {HPHW_FIO, 0x027, 0x0008A, 0x0, "Piranha 100 Core LAN (802.3)"}, + {HPHW_FIO, 0x028, 0x0008A, 0x0, "Mirage Jr Core LAN (802.3)"}, + {HPHW_FIO, 0x029, 0x0008A, 0x0, "Mirage Core LAN (802.3)"}, + {HPHW_FIO, 0x02A, 0x0008A, 0x0, "Electra Core LAN (802.3)"}, + {HPHW_FIO, 0x02B, 0x0008A, 0x0, "Mirage 80 Core LAN (802.3)"}, + {HPHW_FIO, 0x02C, 0x0008A, 0x0, "Mirage 100+ Core LAN (802.3)"}, + {HPHW_FIO, 0x02E, 0x0008A, 0x0, "UL 350 Core LAN (802.3)"}, + {HPHW_FIO, 0x02F, 0x0008A, 0x0, "UL 350 Core LAN (802.3)"}, + {HPHW_FIO, 0x032, 0x0008A, 0x0, "Raven T' Core LAN (802.3)"}, + {HPHW_FIO, 0x033, 0x0008A, 0x0, "Anole T Core Lan (802.3)"}, + {HPHW_FIO, 0x034, 0x0008A, 0x0, "SAIC L-80 Core Lan (802.3)"}, + {HPHW_FIO, 0x035, 0x0008A, 0x0, "PCX-L2 712/132 Core Lan (802.3)"}, + {HPHW_FIO, 0x036, 0x0008A, 0x0, "PCX-L2 712/160 Core Lan (802.3)"}, + {HPHW_FIO, 0x03B, 0x0008A, 0x0, "Raven U/L2 Core Lan (802.3)"}, + {HPHW_FIO, 0x03C, 0x0008A, 0x0, "Merlin 132 Core Lan (802.3)"}, + {HPHW_FIO, 0x03D, 0x0008A, 0x0, "Merlin 160 Core Lan (802.3)"}, + {HPHW_FIO, 0x044, 0x0008A, 0x0, "Mohawk Core Lan (802.3)"}, + {HPHW_FIO, 0x045, 0x0008A, 0x0, "Rocky1 Core LAN (802.3)"}, + {HPHW_FIO, 0x046, 0x0008A, 0x0, "Rocky2 120 Core LAN (802.3)"}, + {HPHW_FIO, 0x047, 0x0008A, 0x0, "Rocky2 150 Core LAN (802.3)"}, + {HPHW_FIO, 0x04B, 0x0008A, 0x0, "Anole L2 132 Core LAN (802.3)"}, + {HPHW_FIO, 0x04D, 0x0008A, 0x0, "Anole L2 165 Core LAN (802.3)"}, + {HPHW_FIO, 0x04E, 0x0008A, 0x0, "Kiji L2 132 Core LAN (802.3)"}, + {HPHW_FIO, 0x050, 0x0008A, 0x0, "Merlin Jr 132 Core Lan (802.3)"}, + {HPHW_FIO, 0x058, 0x0008A, 0x0, "FireHawk 200 Core LAN (802.3)"}, + {HPHW_FIO, 0x800, 0x0008A, 0x0, "Hitachi Tiny 64 Core Lan (802.3)"}, + {HPHW_FIO, 0x801, 0x0008A, 0x0, "Hitachi Tiny 80 Core Lan (802.3)"}, + {HPHW_FIO, 0x004, 0x0008C, 0x0, "SkyHawk 100/120 Wax RS-232"}, + {HPHW_FIO, 0x005, 0x0008C, 0x0, "SAIC L-80 Wax RS-232"}, + {HPHW_FIO, 0x006, 0x0008C, 0x0, "Raven U/L2 Dino RS-232"}, + {HPHW_FIO, 0x007, 0x0008C, 0x0, "Dino RS-232"}, + {HPHW_FIO, 0x008, 0x0008C, 0x0, "Merlin 132 Dino RS-232"}, + {HPHW_FIO, 0x009, 0x0008C, 0x0, "Merlin 160 Dino RS-232"}, + {HPHW_FIO, 0x00A, 0x0008C, 0x0, "Merlin Jr 132 Dino RS-232"}, + {HPHW_FIO, 0x010, 0x0008C, 0x0, "Mirage 80 Wax RS-232"}, + {HPHW_FIO, 0x011, 0x0008C, 0x0, "Mirage 100+ Wax RS-232"}, + {HPHW_FIO, 0x012, 0x0008C, 0x0, "Mirage Jr Wax RS-232"}, + {HPHW_FIO, 0x013, 0x0008C, 0x0, "Mirage Wax RS-232"}, + {HPHW_FIO, 0x014, 0x0008C, 0x0, "Electra Wax RS-232"}, + {HPHW_FIO, 0x015, 0x0008C, 0x0, "KittyHawk GSY Core RS-232"}, + {HPHW_FIO, 0x016, 0x0008C, 0x0, "Gecko Core RS-232"}, + {HPHW_FIO, 0x017, 0x0008C, 0x0, "Raven Backplane RS-232"}, + {HPHW_FIO, 0x018, 0x0008C, 0x0, "Gecko Optional RS-232"}, + {HPHW_FIO, 0x019, 0x0008C, 0x0, "Merlin+ 180 Dino RS-232"}, + {HPHW_FIO, 0x01A, 0x0008C, 0x0, "Anole 64 Core RS-232"}, + {HPHW_FIO, 0x01B, 0x0008C, 0x0, "Anole 100 Core RS-232"}, + {HPHW_FIO, 0x01C, 0x0008C, 0x0, "Gecko 80 Core RS-232"}, + {HPHW_FIO, 0x01D, 0x0008C, 0x0, "Gecko 100 Core RS-232"}, + {HPHW_FIO, 0x01E, 0x0008C, 0x0, "Raven T' Wax RS-232"}, + {HPHW_FIO, 0x01F, 0x0008C, 0x0, "SkyHawk 100/120 Core RS-232"}, + {HPHW_FIO, 0x020, 0x0008C, 0x0, "Anole 64 Timi RS-232"}, + {HPHW_FIO, 0x021, 0x0008C, 0x0, "Anole 100 Timi RS-232"}, + {HPHW_FIO, 0x022, 0x0008C, 0x0, "Merlin+ 132 Dino RS-232"}, + {HPHW_FIO, 0x023, 0x0008C, 0x0, "Rocky1 Wax RS-232"}, + {HPHW_FIO, 0x025, 0x0008C, 0x0, "Armyknife Optional RS-232"}, + {HPHW_FIO, 0x026, 0x0008C, 0x0, "Piranha 100 Wax RS-232"}, + {HPHW_FIO, 0x027, 0x0008C, 0x0, "Piranha 100 Core RS-232"}, + {HPHW_FIO, 0x028, 0x0008C, 0x0, "Mirage Jr Core RS-232"}, + {HPHW_FIO, 0x029, 0x0008C, 0x0, "Mirage Core RS-232"}, + {HPHW_FIO, 0x02A, 0x0008C, 0x0, "Electra Core RS-232"}, + {HPHW_FIO, 0x02B, 0x0008C, 0x0, "Mirage 80 Core RS-232"}, + {HPHW_FIO, 0x02C, 0x0008C, 0x0, "Mirage 100+ Core RS-232"}, + {HPHW_FIO, 0x02E, 0x0008C, 0x0, "UL 350 Lasi Core RS-232"}, + {HPHW_FIO, 0x02F, 0x0008C, 0x0, "UL 550 Lasi Core RS-232"}, + {HPHW_FIO, 0x030, 0x0008C, 0x0, "UL 350 Wax Core RS-232"}, + {HPHW_FIO, 0x031, 0x0008C, 0x0, "UL 550 Wax Core RS-232"}, + {HPHW_FIO, 0x032, 0x0008C, 0x0, "Raven T' Lasi Core RS-232"}, + {HPHW_FIO, 0x033, 0x0008C, 0x0, "Anole T Core RS-232"}, + {HPHW_FIO, 0x034, 0x0008C, 0x0, "SAIC L-80 Core RS-232"}, + {HPHW_FIO, 0x035, 0x0008C, 0x0, "PCX-L2 712/132 Core RS-232"}, + {HPHW_FIO, 0x036, 0x0008C, 0x0, "PCX-L2 712/160 Core RS-232"}, + {HPHW_FIO, 0x03A, 0x0008C, 0x0, "Merlin+ Wax RS-232"}, + {HPHW_FIO, 0x03B, 0x0008C, 0x0, "Raven U/L2 Core RS-232"}, + {HPHW_FIO, 0x03C, 0x0008C, 0x0, "Merlin 132 Core RS-232"}, + {HPHW_FIO, 0x03D, 0x0008C, 0x0, "Merlin 160 Core RS-232"}, + {HPHW_FIO, 0x03E, 0x0008C, 0x0, "Merlin+ 132 Core RS-232"}, + {HPHW_FIO, 0x03F, 0x0008C, 0x0, "Merlin+ 180 Core RS-232"}, + {HPHW_FIO, 0x040, 0x0008C, 0x0, "Merlin 132 Wax RS-232"}, + {HPHW_FIO, 0x041, 0x0008C, 0x0, "Merlin 160 Wax RS-232"}, + {HPHW_FIO, 0x043, 0x0008C, 0x0, "Merlin 132/160 Wax RS-232"}, + {HPHW_FIO, 0x044, 0x0008C, 0x0, "Mohawk Core RS-232"}, + {HPHW_FIO, 0x045, 0x0008C, 0x0, "Rocky1 Core RS-232"}, + {HPHW_FIO, 0x046, 0x0008C, 0x0, "Rocky2 120 Core RS-232"}, + {HPHW_FIO, 0x047, 0x0008C, 0x0, "Rocky2 150 Core RS-232"}, + {HPHW_FIO, 0x048, 0x0008C, 0x0, "Rocky2 120 Dino RS-232"}, + {HPHW_FIO, 0x049, 0x0008C, 0x0, "Rocky2 150 Dino RS-232"}, + {HPHW_FIO, 0x04A, 0x0008C, 0x0, "Anole L2 132 TIMI RS-232"}, + {HPHW_FIO, 0x04B, 0x0008C, 0x0, "Anole L2 l32 Core RS-232"}, + {HPHW_FIO, 0x04C, 0x0008D, 0x0, "Anole L2 165 TIMI RS-232"}, + {HPHW_FIO, 0x04D, 0x0008C, 0x0, "Anole L2 165 Core RS-232"}, + {HPHW_FIO, 0x04E, 0x0008C, 0x0, "Kiji L2 132 Core RS-232"}, + {HPHW_FIO, 0x04F, 0x0008C, 0x0, "Kiji L2 132 Dino RS-232"}, + {HPHW_FIO, 0x050, 0x0008C, 0x0, "Merlin Jr 132 Core RS-232"}, + {HPHW_FIO, 0x051, 0x0008C, 0x0, "Firehawk Core RS-232"}, + {HPHW_FIO, 0x052, 0x0008C, 0x0, "Raven+ Hi Power Backplane w EISA RS-232"}, + {HPHW_FIO, 0x053, 0x0008C, 0x0, "Raven+ Hi Power Backplane w/o EISA RS-232"}, + {HPHW_FIO, 0x054, 0x0008C, 0x0, "Raven+ Lo Power Backplane w EISA RS-232"}, + {HPHW_FIO, 0x055, 0x0008C, 0x0, "Raven+ Lo Power Backplane w/o EISA RS-232"}, + {HPHW_FIO, 0x056, 0x0008C, 0x0, "Raven+ w SE FWSCSI Core RS-232"}, + {HPHW_FIO, 0x057, 0x0008C, 0x0, "Raven+ w Diff FWSCSI Core RS-232"}, + {HPHW_FIO, 0x058, 0x0008C, 0x0, "FireHawk 200 Core RS-232"}, + {HPHW_FIO, 0x059, 0x0008C, 0x0, "FireHawk 200 Wax RS-232"}, + {HPHW_FIO, 0x05A, 0x0008C, 0x0, "Raven+ L2 Backplane w EISA RS-232"}, + {HPHW_FIO, 0x05B, 0x0008C, 0x0, "Raven+ L2 Backplane w/o EISA RS-232"}, + {HPHW_FIO, 0x05D, 0x0008C, 0x0, "SummitHawk Dino RS-232"}, + {HPHW_FIO, 0x05E, 0x0008C, 0x0, "Staccato 132 Core LAN RS-232"}, + {HPHW_FIO, 0x05F, 0x0008C, 0x0, "Staccato 180 Core LAN RS-232"}, + {HPHW_FIO, 0x800, 0x0008C, 0x0, "Hitachi Tiny 64 Core RS-232"}, + {HPHW_FIO, 0x801, 0x0008C, 0x0, "Hitachi Tiny 80 Core RS-232"}, + {HPHW_FIO, 0x015, 0x0008D, 0x0, "Gecko Optional RJ-16"}, + {HPHW_FIO, 0x016, 0x0008D, 0x0, "Gecko Core RJ-16"}, + {HPHW_FIO, 0x01C, 0x0008D, 0x0, "Gecko 80 Core RJ-16"}, + {HPHW_FIO, 0x01D, 0x0008D, 0x0, "Gecko 100 Core RJ-16"}, + {HPHW_FIO, 0x004, 0x0008F, 0x0, "Anole Boot Rom"}, + {HPHW_FIO, 0x005, 0x0008F, 0x0, "Rocky1 Boot Rom"}, + {HPHW_FIO, 0x006, 0x0008F, 0x0, "Rocky2 120 Boot Rom"}, + {HPHW_FIO, 0x007, 0x0008F, 0x0, "Rocky2 150 Boot Rom"}, + {HPHW_FIO, 0x006, 0x00096, 0x0, "Raven U/L2 Dino PS2 Keyboard"}, + {HPHW_FIO, 0x007, 0x00096, 0x0, "Dino PS2 Keyboard"}, + {HPHW_FIO, 0x008, 0x00096, 0x0, "Merlin 132 Dino PS2 Keyboard"}, + {HPHW_FIO, 0x009, 0x00096, 0x0, "Merlin 160 Dino PS2 Keyboard"}, + {HPHW_FIO, 0x00A, 0x00096, 0x0, "Merlin Jr 132 Dino PS2 Keyboard"}, + {HPHW_FIO, 0x019, 0x00096, 0x0, "Merlin+ 180 Dino PS2 Keyboard"}, + {HPHW_FIO, 0x022, 0x00096, 0x0, "Merlin+ 132 Dino PS2 Keyboard"}, + {HPHW_FIO, 0x004, 0x00097, 0x0, "Cascade EISA 100VG lan"}, + {HPHW_FIO, 0x023, 0x00099, 0x0, "Rocky1 Wax HPIB"}, + {HPHW_FIO, 0x048, 0x00099, 0x0, "Rocky2 120 Clark/Dino HPIB"}, + {HPHW_FIO, 0x049, 0x00099, 0x0, "Rocky2 150 Clark/Dino HPIB"}, + {HPHW_FIO, 0x004, 0x000A1, 0x0, "SPP2000 Console TTY"}, + {HPHW_FIO, 0x004, 0x000A2, 0x0, "Forte Core PCI 10/100BT LAN"}, + {HPHW_FIO, 0x005, 0x000A2, 0x0, "AllegroLow PCI 10/100BT LAN"}, + {HPHW_FIO, 0x006, 0x000A2, 0x0, "AllegroHIgh Core PCI 10/100BT LAN"}, + {HPHW_FIO, 0x007, 0x000A2, 0x0, "PCI Plug-in Lan"}, + {HPHW_FIO, 0x00A, 0x000A2, 0x0, "Lego 360 Core PCI 10/100BT Lan"}, + {HPHW_FIO, 0x03E, 0x000A2, 0x0, "Merlin+ 132 Core PCI Lan"}, + {HPHW_FIO, 0x03F, 0x000A2, 0x0, "Merlin+ 180 Core PCI Lan"}, + {HPHW_FIO, 0x056, 0x000A2, 0x0, "Raven+ w SE FWSCSI Core PCI Lan"}, + {HPHW_FIO, 0x057, 0x000A2, 0x0, "Raven+ w Diff FWSCSI Core PCI Lan"}, + {HPHW_FIO, 0x05E, 0x000A2, 0x0, "Staccato 132 PCI Lan"}, + {HPHW_FIO, 0x05F, 0x000A2, 0x0, "Staccato 180 PCI Lan"}, + {HPHW_FIO, 0x004, 0x000A3, 0x0, "Forte Core PCI LVD Ultra2 SCSI"}, + {HPHW_FIO, 0x004, 0x000A3, 0x0, "Forte Core PCI SE UltraSCSI"}, + {HPHW_FIO, 0x004, 0x000A3, 0x0, "Forte Core PCI IDE/ATAPI CD-ROM"}, + {HPHW_FIO, 0x005, 0x000A3, 0x0, "AllegroLow Core PCI LVD Ultra2 SCSI"}, + {HPHW_FIO, 0x005, 0x000A3, 0x0, "AllegroLow Core PCI IDE/ATAPI CD-ROM"}, + {HPHW_FIO, 0x006, 0x000A3, 0x0, "AllegroHigh Core PCI LVD Ultra2 SCSI"}, + {HPHW_FIO, 0x006, 0x000A3, 0x0, "AllegroHigh Core PCI IDE/ATAPI CD-ROM"}, + {HPHW_FIO, 0x007, 0x000A3, 0x0, "PCI Plug-in Disk"}, + {HPHW_FIO, 0x008, 0x000A3, 0x0, "A5158A S FC Tachlite HBA"}, + {HPHW_FIO, 0x009, 0x000A3, 0x0, "A5157A D FC HBA"}, + {HPHW_FIO, 0x00A, 0x000A3, 0x0, "Lego 360 Core PCI LVD Ultra2 SCSI"}, + {HPHW_FIO, 0x00A, 0x000A3, 0x0, "Lego 360 Core PCI NSE UltraSCSI"}, + {HPHW_FIO, 0x00A, 0x000A3, 0x0, "Lego 360 Core PCI WSE UltraSCSI"}, + {HPHW_FIO, 0x00A, 0x000A3, 0x0, "Lego 360 Core PCI IDE/ATAPI CD-ROM"}, + {HPHW_FIO, 0x03E, 0x000A3, 0x0, "Merlin+ 132 Core SE FWSCSI PCI Disk"}, + {HPHW_FIO, 0x03F, 0x000A3, 0x0, "Merlin+ 180 Core SE FWSCSI PCI Disk"}, + {HPHW_FIO, 0x056, 0x000A3, 0x0, "Raven+ w SE FWSCSI Core PCI Disk"}, + {HPHW_FIO, 0x057, 0x000A3, 0x0, "Raven+ w Diff FWSCSI Core PCI Disk"}, + {HPHW_FIO, 0x004, 0x000A4, 0x0, "SPP2000 Core BA"}, + {HPHW_FIO, 0x004, 0x000A6, 0x0, "Sonic Ethernet 802.3 Card"}, + {HPHW_FIO, 0x004, 0x000A9, 0x00, "Forte Core PCI SuperIO RS-232"}, + {HPHW_FIO, 0x004, 0x000A9, 0x00, "Forte Core PCI USB KB"}, + {HPHW_FIO, 0x005, 0x000A9, 0x00, "AllegroLow Core PCI SuperIO RS-232"}, + {HPHW_FIO, 0x005, 0x000A9, 0x00, "AllegroLow Core PCI USB KB"}, + {HPHW_FIO, 0x006, 0x000A9, 0x00, "AllegroHigh Core PCI SuperIO RS-232"}, + {HPHW_FIO, 0x006, 0x000A9, 0x00, "AllegroHigh Core PCI USB KB"}, + {HPHW_FIO, 0x007, 0x000A9, 0x0, "Miscelaneous PCI Plug-in"}, + {HPHW_FIO, 0x00A, 0x000A9, 0x0, "Lego 360 Core PCI SuperIO RS-232"}, + {HPHW_FIO, 0x00A, 0x000A9, 0x0, "Lego 360 Core PCI USB KB"}, + {HPHW_FIO, 0x004, 0x00320, 0x0, "Metheus Frame Buffer"}, + {HPHW_FIO, 0x004, 0x00340, 0x0, "BARCO CX4500 VME Grphx Cnsl"}, + {HPHW_FIO, 0x004, 0x00360, 0x0, "Hughes TOG VME FDDI"}, + {HPHW_IOA, 0x185, 0x0000B, 0x00, "Java BC Summit Port"}, + {HPHW_IOA, 0x1FF, 0x0000B, 0x00, "Hitachi Ghostview Summit Port"}, + {HPHW_IOA, 0x580, 0x0000B, 0x10, "U2-IOA BC Runway Port"}, + {HPHW_IOA, 0x581, 0x0000B, 0x10, "Uturn-IOA BC Runway Port"}, + {HPHW_IOA, 0x582, 0x0000B, 0x10, "Astro BC Runway Port"}, + {HPHW_IOA, 0x700, 0x0000B, 0x00, "NEC-IOS BC System Bus Port"}, + {HPHW_MEMORY, 0x002, 0x00008, 0x00, "MID_BUS"}, + {HPHW_MEMORY, 0x00C, 0x00008, 0x08, "Kahlua 8MB"}, + {HPHW_MEMORY, 0x00D, 0x00008, 0x08, "Kahlua 4MB"}, + {HPHW_MEMORY, 0x00E, 0x00008, 0x08, "Tequila 16MB"}, + {HPHW_MEMORY, 0x00F, 0x00008, 0x08, "Tequila 32MB"}, + {HPHW_MEMORY, 0x040, 0x00008, 0x00, "Hitachi"}, + {HPHW_MEMORY, 0x004, 0x00009, 0x00, "Cheetah"}, + {HPHW_MEMORY, 0x005, 0x00009, 0x00, "Emerald"}, + {HPHW_MEMORY, 0x008, 0x00009, 0x00, "Indigo 3MB/5MB"}, + {HPHW_MEMORY, 0x00C, 0x00009, 0x00, "Indigo 8MB"}, + {HPHW_MEMORY, 0x00D, 0x00009, 0x00, "Paradise 4MB"}, + {HPHW_MEMORY, 0x00E, 0x00009, 0x00, "Burgundy Onboard"}, + {HPHW_MEMORY, 0x012, 0x00009, 0x00, "Indigo 12MB/20MB"}, + {HPHW_MEMORY, 0x013, 0x00009, 0x00, "Cobra"}, + {HPHW_MEMORY, 0x014, 0x00009, 0x00, "Nova"}, + {HPHW_MEMORY, 0x015, 0x00009, 0x00, "Coral"}, + {HPHW_MEMORY, 0x016, 0x00009, 0x00, "Bushmaster"}, + {HPHW_MEMORY, 0x017, 0x00009, 0x00, "Scorpio"}, + {HPHW_MEMORY, 0x018, 0x00009, 0x00, "Flounder"}, + {HPHW_MEMORY, 0x019, 0x00009, 0x00, "Hardball"}, + {HPHW_MEMORY, 0x01A, 0x00009, 0x00, "CoralII 99"}, + {HPHW_MEMORY, 0x01B, 0x00009, 0x00, "Scorpio Jr."}, + {HPHW_MEMORY, 0x01C, 0x00009, 0x00, "Strider-50 (715T)"}, + {HPHW_MEMORY, 0x01D, 0x00009, 0x00, "Strider-33 (707T)"}, + {HPHW_MEMORY, 0x01E, 0x00009, 0x00, "Trailways-50 (715S)"}, + {HPHW_MEMORY, 0x01F, 0x00009, 0x00, "Trailways-33 (707S)"}, + {HPHW_MEMORY, 0x020, 0x00009, 0x00, "Pace"}, + {HPHW_MEMORY, 0x021, 0x00009, 0x00, "Sidewinder"}, + {HPHW_MEMORY, 0x022, 0x00009, 0x00, "Orville"}, + {HPHW_MEMORY, 0x023, 0x00009, 0x00, "Wilbur"}, + {HPHW_MEMORY, 0x026, 0x00009, 0x00, "Gecko"}, + {HPHW_MEMORY, 0x027, 0x00009, 0x00, "Scorpio Sr."}, + {HPHW_MEMORY, 0x028, 0x00009, 0x00, "Scorpio 100"}, + {HPHW_MEMORY, 0x029, 0x00009, 0x00, "Spectra 50"}, + {HPHW_MEMORY, 0x02A, 0x00009, 0x00, "CoralII 132"}, + {HPHW_MEMORY, 0x02F, 0x00009, 0x00, "KittyHawk DC2-"}, + {HPHW_MEMORY, 0x030, 0x00009, 0x00, "Spectra 75"}, + {HPHW_MEMORY, 0x031, 0x00009, 0x00, "Spectra 100"}, + {HPHW_MEMORY, 0x032, 0x00009, 0x00, "KittyHawk DC3"}, + {HPHW_MEMORY, 0x033, 0x00009, 0x00, "Fast Pace"}, + {HPHW_MEMORY, 0x034, 0x00009, 0x00, "Snake Eagle"}, + {HPHW_MEMORY, 0x035, 0x00009, 0x00, "Anole 64"}, + {HPHW_MEMORY, 0x036, 0x00009, 0x00, "Anole 100"}, + {HPHW_MEMORY, 0x037, 0x00009, 0x00, "Snake Cheetah"}, + {HPHW_MEMORY, 0x038, 0x00009, 0x00, "Gecko 80"}, + {HPHW_MEMORY, 0x039, 0x00009, 0x00, "Gecko 100"}, + {HPHW_MEMORY, 0x03A, 0x00009, 0x00, "Gecko 120"}, + {HPHW_MEMORY, 0x03B, 0x00009, 0x00, "Gila 80"}, + {HPHW_MEMORY, 0x03C, 0x00009, 0x00, "Gila 100"}, + {HPHW_MEMORY, 0x03D, 0x00009, 0x00, "Gila 120"}, + {HPHW_MEMORY, 0x03E, 0x00009, 0x00, "Scorpio-L 80"}, + {HPHW_MEMORY, 0x03F, 0x00009, 0x00, "Scorpio-L 100"}, + {HPHW_MEMORY, 0x040, 0x00009, 0x00, "Scorpio-L 120"}, + {HPHW_MEMORY, 0x041, 0x00009, 0x00, "Spectra-L 80"}, + {HPHW_MEMORY, 0x042, 0x00009, 0x00, "Spectra-L 100"}, + {HPHW_MEMORY, 0x043, 0x00009, 0x00, "Spectra-L 120"}, + {HPHW_MEMORY, 0x044, 0x00009, 0x00, "Piranha 100"}, + {HPHW_MEMORY, 0x045, 0x00009, 0x00, "Piranha 120"}, + {HPHW_MEMORY, 0x046, 0x00009, 0x00, "Jason 50"}, + {HPHW_MEMORY, 0x047, 0x00009, 0x00, "Jason 100"}, + {HPHW_MEMORY, 0x049, 0x00009, 0x00, "SkyHawk 100/120"}, + {HPHW_MEMORY, 0x04A, 0x00009, 0x00, "Mirage Jr"}, + {HPHW_MEMORY, 0x04B, 0x00009, 0x00, "Mirage 100"}, + {HPHW_MEMORY, 0x04C, 0x00009, 0x00, "Mirage 100+"}, + {HPHW_MEMORY, 0x04D, 0x00009, 0x00, "Electra 100"}, + {HPHW_MEMORY, 0x04E, 0x00009, 0x00, "Electra 120"}, + {HPHW_MEMORY, 0x04F, 0x00009, 0x00, "Mirage 80"}, + {HPHW_MEMORY, 0x050, 0x00009, 0x00, "UL Proc 1 way T'100"}, + {HPHW_MEMORY, 0x051, 0x00009, 0x00, "UL Proc 1 way T'120"}, + {HPHW_MEMORY, 0x052, 0x00009, 0x00, "UL Proc 2 way T'100"}, + {HPHW_MEMORY, 0x053, 0x00009, 0x00, "KittyHawk DC3-"}, + {HPHW_MEMORY, 0x054, 0x00009, 0x00, "UL Proc 2 way T'120"}, + {HPHW_MEMORY, 0x055, 0x00009, 0x00, "Raven 120 mem"}, + {HPHW_MEMORY, 0x056, 0x00009, 0x00, "UL Proc L 75"}, + {HPHW_MEMORY, 0x057, 0x00009, 0x00, "UL Proc L 100"}, + {HPHW_MEMORY, 0x058, 0x00009, 0x00, "Anole T"}, + {HPHW_MEMORY, 0x059, 0x00009, 0x00, "SAIC L-80"}, + {HPHW_MEMORY, 0x05A, 0x00009, 0x00, "Merlin+ L2 180"}, + {HPHW_MEMORY, 0x05B, 0x00009, 0x00, "Raven U 200 2-way"}, + {HPHW_MEMORY, 0x05C, 0x00009, 0x00, "Raven U 180+"}, + {HPHW_MEMORY, 0x05D, 0x00009, 0x00, "Raven U 200"}, + {HPHW_MEMORY, 0x05E, 0x00009, 0x00, "Rocky2 150 Memory"}, + {HPHW_MEMORY, 0x08A, 0x00009, 0x00, "Staccato L2 132 Memory"}, + {HPHW_MEMORY, 0x08B, 0x00009, 0x00, "Staccato L2 180 Memory"}, + {HPHW_MEMORY, 0x05F, 0x00009, 0x00, "SPP2000 Memory"}, + {HPHW_MEMORY, 0x060, 0x00009, 0x00, "Merlin L2 132"}, + {HPHW_MEMORY, 0x061, 0x00009, 0x00, "Merlin+ L2 132"}, + {HPHW_MEMORY, 0x063, 0x00009, 0x00, "712/132 L2 Upgrade"}, + {HPHW_MEMORY, 0x064, 0x00009, 0x00, "712/160 L2 Upgrade"}, + {HPHW_MEMORY, 0x065, 0x00009, 0x00, "715/132 L2 Upgrade"}, + {HPHW_MEMORY, 0x066, 0x00009, 0x00, "715/160 L2 Upgrade"}, + {HPHW_MEMORY, 0x067, 0x00009, 0x00, "Merlin 160/ThunderHawk Memory"}, + {HPHW_MEMORY, 0x068, 0x00009, 0x00, "LightningHawk Memory"}, + {HPHW_MEMORY, 0x069, 0x00009, 0x00, "Rocky1 Memory"}, + {HPHW_MEMORY, 0x06A, 0x00009, 0x00, "Raven L2 132"}, + {HPHW_MEMORY, 0x06B, 0x00009, 0x00, "Raven L2 160"}, + {HPHW_MEMORY, 0x06C, 0x00009, 0x00, "Raven L2 187"}, + {HPHW_MEMORY, 0x06D, 0x00009, 0x00, "Raven L2 200"}, + {HPHW_MEMORY, 0x06E, 0x00009, 0x00, "Raven U 230"}, + {HPHW_MEMORY, 0x06F, 0x00009, 0x00, "Raven U 240"}, + {HPHW_MEMORY, 0x070, 0x00009, 0x00, "Rocky2 120 Memory"}, + {HPHW_MEMORY, 0x071, 0x00009, 0x00, "Raven U 160"}, + {HPHW_MEMORY, 0x072, 0x00009, 0x00, "Raven U 180"}, + {HPHW_MEMORY, 0x072, 0x00009, 0x00, "UL Proc 1 way T'120 1MB/1MB"}, + {HPHW_MEMORY, 0x073, 0x00009, 0x00, "UL Proc 2 way T'120 1MB/1MB"}, + {HPHW_MEMORY, 0x074, 0x00009, 0x00, "Anole L2 132 memory"}, + {HPHW_MEMORY, 0x075, 0x00009, 0x00, "Anole L2 165 memory"}, + {HPHW_MEMORY, 0x076, 0x00009, 0x00, "UL 1 way U160 512K/512K memory"}, + {HPHW_MEMORY, 0x077, 0x00009, 0x00, "UL 2 way U160 512K/512K memory"}, + {HPHW_MEMORY, 0x078, 0x00009, 0x00, "Kiji L2 132 memory"}, + {HPHW_MEMORY, 0x079, 0x00009, 0x00, "UL 1 way U160 1M/1M memory"}, + {HPHW_MEMORY, 0x07A, 0x00009, 0x00, "UL 2 way U160 1M/1M memory"}, + {HPHW_MEMORY, 0x07B, 0x00009, 0x00, "UL 1 way U180 1M/1M memory"}, + {HPHW_MEMORY, 0x07C, 0x00009, 0x00, "UL 2 way U180 1M/1M memory"}, + {HPHW_MEMORY, 0x07D, 0x00009, 0x00, "UL 1 way U240 U+ 2M/2M memory"}, + {HPHW_MEMORY, 0x07E, 0x00009, 0x00, "UL 2 way U240 U+ 2M/2M memory"}, + {HPHW_MEMORY, 0x07F, 0x00009, 0x00, "UL L2 132 memory"}, + {HPHW_MEMORY, 0x080, 0x00009, 0x00, "UL L2 160 memory"}, + {HPHW_MEMORY, 0x081, 0x00009, 0x00, "Merlin Jr 132 memory"}, + {HPHW_MEMORY, 0x082, 0x00009, 0x00, "FireHawk 200 Memory"}, + {HPHW_MEMORY, 0x083, 0x00009, 0x00, "SummitHawk Memory"}, + {HPHW_MEMORY, 0x084, 0x00009, 0x00, "Jade Upgrade Memory"}, + {HPHW_MEMORY, 0x085, 0x00009, 0x00, "SPP2500 Memory"}, + {HPHW_MEMORY, 0x086, 0x00009, 0x00, "AllegroHigh Memory"}, + {HPHW_MEMORY, 0x087, 0x00009, 0x00, "AllegroLow Memory"}, + {HPHW_MEMORY, 0x088, 0x00009, 0x00, "Forte 2w Memory"}, + {HPHW_MEMORY, 0x089, 0x00009, 0x00, "Forte 4w Memory"}, + {HPHW_MEMORY, 0x08A, 0x00009, 0x00, "Staccato L2 132 Memory"}, + {HPHW_MEMORY, 0x08B, 0x00009, 0x00, "Staccato L2 180 Memory"}, + {HPHW_MEMORY, 0x090, 0x00009, 0x00, "Prelude SMC Memory"}, + {HPHW_MEMORY, 0x091, 0x00009, 0x00, "Lego 360 Memory"}, + {HPHW_MEMORY, 0x7FF, 0x00009, 0x00, "NEC Aska memory"}, + {HPHW_MEMORY, 0x800, 0x00009, 0x00, "Hitachi Tiny 64"}, + {HPHW_MEMORY, 0x801, 0x00009, 0x00, "Hitachi Tiny 80"}, + {HPHW_MEMORY, 0x8FF, 0x00009, 0x00, "Hitachi X memory"}, + {HPHW_MEMORY, 0x091, 0x00009, 0x00, "M2250 Memory"}, + {HPHW_MEMORY, 0x092, 0x00009, 0x00, "M2500 Memory"}, + {HPHW_MEMORY, 0x093, 0x00009, 0x00, "Sonata 440 Memory"}, + {HPHW_MEMORY, 0x094, 0x00009, 0x00, "Sonata 360 Memory"}, + {HPHW_MEMORY, 0x095, 0x00009, 0x00, "Rhapsody 440 Memory"}, + {HPHW_MEMORY, 0x096, 0x00009, 0x00, "Rhapsody 360 Memory"}, + {HPHW_MEMORY, 0x097, 0x00009, 0x00, "Raven W 360 Memory"}, + {HPHW_MEMORY, 0x098, 0x00009, 0x00, "Halfdome W 440 Memory"}, + {HPHW_MEMORY, 0x099, 0x00009, 0x00, "Rhapsody DC- 440 Memory"}, + {HPHW_MEMORY, 0x09A, 0x00009, 0x00, "Rhapsody DC- 360 Memory"}, + {HPHW_MEMORY, 0x09B, 0x00009, 0x00, "Crescendo Memory"}, + {HPHW_OTHER, 0x004, 0x00030, 0x00, "Master"}, + {HPHW_OTHER, 0x004, 0x00034, 0x00, "Slave"}, + {HPHW_OTHER, 0x004, 0x00038, 0x00, "EDU"}, + {HPHW_OTHER, 0x004, 0x00049, 0x00, "LGB Control"}, + {0, } /* leave the last entry empty ! */ +}; + + +static struct hp_cpu_type_mask { + unsigned short model; + unsigned short mask; + enum cpu_type cpu; +} hp_cpu_type_mask_list[] = { + + { 0x0000, 0x0ff0, pcx }, /* 0x0000 - 0x000f */ + { 0x0048, 0x0ff0, pcxl }, /* 0x0040 - 0x004f */ + { 0x0080, 0x0ff0, pcx }, /* 0x0080 - 0x008f */ + { 0x0100, 0x0ff0, pcx }, /* 0x0100 - 0x010f */ + { 0x0182, 0x0ffe, pcx }, /* 0x0182 - 0x0183 */ + { 0x0182, 0x0ffe, pcxt }, /* 0x0182 - 0x0183 */ + { 0x0184, 0x0fff, pcxu }, /* 0x0184 - 0x0184 */ + { 0x0200, 0x0ffe, pcxs }, /* 0x0200 - 0x0201 */ + { 0x0202, 0x0fff, pcxs }, /* 0x0202 - 0x0202 */ + { 0x0203, 0x0fff, pcxt }, /* 0x0203 - 0x0203 */ + { 0x0204, 0x0ffc, pcxt }, /* 0x0204 - 0x0207 */ + { 0x0280, 0x0ffc, pcxs }, /* 0x0280 - 0x0283 */ + { 0x0284, 0x0ffc, pcxt }, /* 0x0284 - 0x0287 */ + { 0x0288, 0x0fff, pcxt }, /* 0x0288 - 0x0288 */ + { 0x0300, 0x0ffc, pcxs }, /* 0x0300 - 0x0303 */ + { 0x0310, 0x0ff0, pcxt }, /* 0x0310 - 0x031f */ + { 0x0320, 0x0ff0, pcxt }, /* 0x0320 - 0x032f */ + { 0x0400, 0x0ff0, pcxt }, /* 0x0400 - 0x040f */ + { 0x0480, 0x0ff0, pcxl }, /* 0x0480 - 0x048f */ + { 0x0500, 0x0ff0, pcxl2 }, /* 0x0500 - 0x050f */ + { 0x0510, 0x0ff0, pcxl2 }, /* 0x0510 - 0x051f */ + { 0x0580, 0x0ff8, pcxt_ }, /* 0x0580 - 0x0587 */ + { 0x0588, 0x0ffc, pcxt_ }, /* 0x0588 - 0x058b */ + { 0x058c, 0x0ffe, pcxt_ }, /* 0x058c - 0x058d */ + { 0x058e, 0x0fff, pcxt_ }, /* 0x058e - 0x058e */ + { 0x058f, 0x0fff, pcxu }, /* 0x058f - 0x058f */ + { 0x0590, 0x0ffe, pcxu }, /* 0x0590 - 0x0591 */ + { 0x0592, 0x0fff, pcxt_ }, /* 0x0592 - 0x0592 */ + { 0x0593, 0x0fff, pcxu }, /* 0x0593 - 0x0593 */ + { 0x0594, 0x0ffc, pcxu }, /* 0x0594 - 0x0597 */ + { 0x0598, 0x0ffc, pcxu }, /* 0x0598 - 0x059b */ + { 0x059c, 0x0ffe, pcxu_ }, /* 0x059c - 0x059d */ + { 0x059e, 0x0fff, pcxt_ }, /* 0x059e - 0x059e */ + { 0x059f, 0x0fff, pcxu }, /* 0x059f - 0x059f */ + { 0x05a0, 0x0ffe, pcxt_ }, /* 0x05a0 - 0x05a1 */ + { 0x05a2, 0x0ffe, pcxu }, /* 0x05a2 - 0x05a3 */ + { 0x05a4, 0x0ffc, pcxu }, /* 0x05a4 - 0x05a7 */ + { 0x05a8, 0x0ffc, pcxu }, /* 0x05a8 - 0x05ab */ + { 0x05ad, 0x0fff, pcxu_ }, /* 0x05ad - 0x05ad */ + { 0x05ae, 0x0ffe, pcxu_ }, /* 0x05ae - 0x05af */ + { 0x05b0, 0x0ffe, pcxu_ }, /* 0x05b0 - 0x05b1 */ + { 0x05b2, 0x0fff, pcxu_ }, /* 0x05b2 - 0x05b2 */ + { 0x05b3, 0x0fff, pcxu }, /* 0x05b3 - 0x05b3 */ + { 0x05b4, 0x0fff, pcxw }, /* 0x05b4 - 0x05b4 */ + { 0x05b5, 0x0fff, pcxu_ }, /* 0x05b5 - 0x05b5 */ + { 0x05b6, 0x0ffe, pcxu_ }, /* 0x05b6 - 0x05b7 */ + { 0x05b8, 0x0ffe, pcxu_ }, /* 0x05b8 - 0x05b9 */ + { 0x05ba, 0x0fff, pcxu_ }, /* 0x05ba - 0x05ba */ + { 0x05bb, 0x0fff, pcxw }, /* 0x05bb - 0x05bb */ + { 0x05bc, 0x0ffc, pcxw }, /* 0x05bc - 0x05bf */ + { 0x05c0, 0x0fc0, pcxw }, /* 0x05c0 - 0x05ff */ + { 0x0600, 0x0ff0, pcxl }, /* 0x0600 - 0x060f */ + { 0x0610, 0x0ff0, pcxl }, /* 0x0610 - 0x061f */ + { 0x0000, 0x0000, pcx } /* terminate table */ +}; + +char *cpu_name_version[][2] = { + [pcx] { "PA7000 (PCX)", "1.0" }, + [pcxs] { "PA7000 (PCX-S)", "1.1a" }, + [pcxt] { "PA7100 (PCX-T)", "1.1b" }, + [pcxt_] { "PA7200 (PCX-T')", "1.1c" }, + [pcxl] { "PA7100LC (PCX-L)", "1.1d" }, + [pcxl2] { "PA7300LC (PCX-L2)", "1.1e" }, + [pcxu] { "PA8000 (PCX-U)", "2.0" }, + [pcxu_] { "PA8200 (PCX-U+)", "2.0" }, + [pcxw] { "PA8500 (PCX-W)", "2.0" }, + [pcxw_] { "PA8600 (PCX-W+)", "2.0" } +}; + +char *parisc_getHWtype(unsigned short hw_type) +{ + if (hw_type <= HPHW_CIO) { + return hw_type_name[hw_type]; + } else { + return "Unknown Type"; + } +} + +char *parisc_getHWdescription(unsigned short hw_type, unsigned long hversion, + unsigned long sversion) +{ + struct hp_hardware *listptr; + + for (listptr = hp_hardware_list; listptr->name; listptr++) { + if ((listptr->hw_type==hw_type) && + (listptr->hversion==hversion) && + (listptr->sversion==sversion)){ + return listptr->name; + } + } + return "unknown device"; +} + + +/* Interpret hversion (ret[0]) from PDC_MODEL(4)/PDC_MODEL_INFO(0) */ +enum cpu_type parisc_get_cpu_type(unsigned long hversion) +{ + struct hp_cpu_type_mask *ptr; + unsigned short model = ((unsigned short) (hversion)) >> 4; + + for (ptr = hp_cpu_type_mask_list; 0 != ptr->mask; ptr++) { + if (ptr->model == (model & ptr->mask)) + return ptr->cpu; + } + panic("parisc_get_cpu_type() could not identify CPU type\n"); + + return pcx; /* not reached: */ +} + + +struct hp_hardware *parisc_get_reference(unsigned short hw_type, + unsigned long hversion, unsigned long sversion) +{ + struct hp_hardware *listptr = hp_hardware_list; + + for (listptr = hp_hardware_list; listptr->name; listptr++) { + if ((listptr->hw_type == hw_type) && + (listptr->hversion == hversion) && + (listptr->sversion == sversion)) { + return listptr; + } + } + return NULL; +} diff --git a/arch/parisc/kernel/head.S b/arch/parisc/kernel/head.S new file mode 100644 index 000000000000..ce45dd6088ba --- /dev/null +++ b/arch/parisc/kernel/head.S @@ -0,0 +1,156 @@ +/* + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1999 by Helge Deller + * Copyright 1999 SuSE GmbH (Philipp Rumpf) + * Copyright 1999 Philipp Rumpf (prumpf@tux.org) + * + * Initial Version 04-23-1999 by Helge Deller (helge.deller@ruhr-uni-bochum.de) + */ + + +#include +#include + +#define __ASSEMBLY__ +/********* +#include +*********/ +#include +#include + + + .level 1.1 + + .section .initcall.init + .align 4 + .export __initcall_start +__initcall_start: + .export __initcall_end +__initcall_end: + .export __setup_start +__setup_start: + .export __setup_end +__setup_end: + + .text + .align 4 + .import init_task_union,data + .import $global$ /* forward declaration */ + .import fault_vector_11,code /* IVA parisc 1.1 32 bit */ + .import fault_vector_20,code /* IVA parisc 2.0 32 bit */ + .import start_parisc,code /* then enable VM and go here */ + + .export stext + .export _stext,data /* Kernel want it this way! */ +_stext: +stext: + .proc + .callinfo + + /* Make sure sr4-sr7 are set to zero for the kernel address space */ + + mtsp %r0,%sr4 + mtsp %r0,%sr5 + mtsp %r0,%sr6 + mtsp %r0,%sr7 + + /* Initialize startup VM. Just map first 8 MB of memory */ + + ldil L%PA(pg0),%r1 + ldo R%PA(pg0)(%r1),%r1 + ldo _PAGE_TABLE(%r1),%r3 + ldil L%PA(swapper_pg_dir),%r4 + ldo R%PA(swapper_pg_dir)(%r4),%r4 + mtctl %r4,%cr24 /* Initialize kernel root pointer */ + mtctl %r4,%cr25 /* Initialize user root pointer */ + stw %r3,0xc00(%r4) /* Hardwired 0xc0000000 kernel vaddr start */ + ldo 0x1000(%r3),%r3 + stw %r3,0xc04(%r4) + ldo _PAGE_KERNEL(%r0),%r3 /* Hardwired 0x0 phys addr start */ +$pgt_fill_loop: + stwm %r3,4(%r1) + ldo 0x1000(%r3),%r3 + bb,>= %r3,8,$pgt_fill_loop + nop + + /* Initialize the global data pointer */ + ldil L%$global$,%dp + ldo R%$global$(%dp),%dp + + /* And the stack pointer, physical too */ + ldil L%init_task_union+TASK_SZ_ALGN,%sp + ldo R%init_task_union+TASK_SZ_ALGN(%sp),%sp + + /* we need this to take interruptions directly after the rfi below */ + /* (which we need for PA2.0 boxes) */ + mtctl %r0, %cr30 + + /* + * Set up our interrupt table. HPMCs might not work after this! + * + * We need to install the correct iva for PA1.1 or PA2.0. The + * following short sequence of instructions can determine this + * (without being illegal on a PA1.1 machine). + */ + + ldi 32,%r10 + mtctl %r10,%cr11 + .level 2.0 + mfctl,w %cr11,%r10 + .level 1.1 + comib,<>,n 0,%r10,$is_pa20 + ldil L%PA(fault_vector_11),%r10 + b $install_iva + ldo R%PA(fault_vector_11)(%r10),%r10 + +$is_pa20: + ldil L%PA(fault_vector_20),%r10 + ldo R%PA(fault_vector_20)(%r10),%r10 + +$install_iva: + mtctl %r10,%cr14 + + /* Disable (most) interruptions */ + mtsm %r0 + + /* kernel PSW: + * - no interruptions except for HPMC and TOC (which are handled by PDC) + * - Q bit set (IODC / PDC interruptions) + * - big-endian + * - virtually mapped + */ + + ldil L%KERNEL_PSW,%r10 + ldo R%KERNEL_PSW(%r10),%r10 + mtctl %r10,%ipsw + + /* Set the space pointers for the post-RFI world */ + mtctl %r0,%cr17 /* Clear two-level IIA Space Queue */ + mtctl %r0,%cr17 /* effectively setting kernel space. */ + + /* And the return address(es) too */ + ldil L%start_parisc,%r10 + ldo R%start_parisc(%r10),%r10 + mtctl %r10,%cr18 + ldo 4(%r10),%r10 + mtctl %r10,%cr18 + + /* Jump to hyperspace */ + rfi + nop + + .procend + + .data + + .align 4 + .export $global$,data + + .type $global$,@object + .size $global$,4 +$global$: + .word 0 diff --git a/arch/parisc/kernel/hpmc.S b/arch/parisc/kernel/hpmc.S new file mode 100644 index 000000000000..c77aee04e08e --- /dev/null +++ b/arch/parisc/kernel/hpmc.S @@ -0,0 +1,319 @@ +/* + * HPMC (High Priority Machine Check) handler. + * + * Copyright (C) 1999 Philipp Rumpf + * Copyright (C) 1999 Hewlett-Packard (Frank Rowand) + * Copyright (C) 2000 Hewlett-Packard (John Marvin) + * + * 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, 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 HPMC handler retrieves the HPMC pim data, resets IO and + * returns to the default trap handler with code set to 1 (HPMC). + * The default trap handler calls handle interruption, which + * does a stack and register dump. This at least allows kernel + * developers to get back to C code in virtual mode, where they + * have the option to examine and print values from memory that + * would help in debugging an HPMC caused by a software bug. + * + * There is more to do here: + * + * 1) On MP systems we need to synchronize processors + * before calling pdc/iodc. + * 2) We should be checking the system state and not + * returning to the fault handler if things are really + * bad. + * + */ + + .level 1.1 + .data + +#define __ASSEMBLY__ +#include +#include + + /* + * stack for os_hpmc, the HPMC handler. + * buffer for IODC procedures (for the HPMC handler). + * + * IODC requires 7K byte stack. That leaves 1K byte for os_hpmc. + */ + + .align 4096 +hpmc_stack: + .block 16384 + +#define HPMC_IODC_BUF_SIZE 0x8000 + + .align 4096 +hpmc_iodc_buf: + .block HPMC_IODC_BUF_SIZE + + .align 8 +hpmc_raddr: + .block 128 + +#define HPMC_PIM_DATA_SIZE 896 /* Enough to hold all architected 2.0 state */ + + .export hpmc_pim_data, data + .align 8 +hpmc_pim_data: + .block HPMC_PIM_DATA_SIZE + + .text + + .export os_hpmc, code + .import intr_save, code + +os_hpmc: + + /* + * registers modified: + * + * Using callee saves registers without saving them. The + * original values are in the pim dump if we need them. + * + * r2 (rp) return pointer + * r3 address of PDCE_PROC + * r4 scratch + * r5 scratch + * r23 (arg3) procedure arg + * r24 (arg2) procedure arg + * r25 (arg1) procedure arg + * r26 (arg0) procedure arg + * r30 (sp) stack pointer + * + * registers read: + * + * r26 contains address of PDCE_PROC on entry + * r28 (ret0) return value from procedure + */ + + copy arg0, %r3 /* save address of PDCE_PROC */ + + /* + * disable nested HPMCs + * + * Increment os_hpmc checksum to invalidate it. + * Do this before turning the PSW M bit off. + */ + + mfctl %cr14, %r4 + ldw 52(%r4),%r5 + addi 1,%r5,%r5 + stw %r5,52(%r4) + + /* MP_FIXME: synchronize all processors. */ + + /* Setup stack pointer. */ + + ldil L%PA(hpmc_stack),sp + ldo R%PA(hpmc_stack)(sp),sp + + ldo 128(sp),sp /* leave room for arguments */ + + /* + * Most PDC routines require that the M bit be off. + * So turn on the Q bit and turn off the M bit. + */ + + ldo 8(%r0),%r4 /* PSW Q on, PSW M off */ + mtctl %r4,ipsw + mtctl %r0,pcsq + mtctl %r0,pcsq + ldil L%PA(os_hpmc_1),%r4 + ldo R%PA(os_hpmc_1)(%r4),%r4 + mtctl %r4,pcoq + ldo 4(%r4),%r4 + mtctl %r4,pcoq + rfi + nop + +os_hpmc_1: + + /* Call PDC_PIM to get HPMC pim info */ + + /* + * Note that on some newer boxes, PDC_PIM must be called + * before PDC_IO if you want IO to be reset. PDC_PIM sets + * a flag that PDC_IO examines. + */ + + ldo PDC_PIM(%r0), arg0 + ldo PDC_PIM_HPMC(%r0),arg1 /* Transfer HPMC data */ + ldil L%PA(hpmc_raddr),arg2 + ldo R%PA(hpmc_raddr)(arg2),arg2 + ldil L%PA(hpmc_pim_data),arg3 + ldo R%PA(hpmc_pim_data)(arg3),arg3 + ldil L%HPMC_PIM_DATA_SIZE,%r4 + ldo R%HPMC_PIM_DATA_SIZE(%r4),%r4 + stw %r4,-52(sp) + + ldil L%PA(os_hpmc_2), rp + bv (r3) /* call pdce_proc */ + ldo R%PA(os_hpmc_2)(rp), rp + +os_hpmc_2: + comib,<> 0,ret0, os_hpmc_fail + + /* Reset IO by calling the hversion dependent PDC_IO routine */ + + ldo PDC_IO(%r0),arg0 + ldo 0(%r0),arg1 /* log IO errors */ + ldo 0(%r0),arg2 /* reserved */ + ldo 0(%r0),arg3 /* reserved */ + stw %r0,-52(sp) /* reserved */ + + ldil L%PA(os_hpmc_3),rp + bv (%r3) /* call pdce_proc */ + ldo R%PA(os_hpmc_3)(rp),rp + +os_hpmc_3: + + /* FIXME? Check for errors from PDC_IO (-1 might be OK) */ + + /* + * Initialize the IODC console device (HPA,SPA, path etc. + * are stored on page 0. + */ + + /* + * Load IODC into hpmc_iodc_buf by calling PDC_IODC. + * Note that PDC_IODC handles flushing the appropriate + * data and instruction cache lines. + */ + + ldo PDC_IODC(%r0),arg0 + ldo PDC_IODC_READ(%r0),arg1 + ldil L%PA(hpmc_raddr),arg2 + ldo R%PA(hpmc_raddr)(arg2),arg2 + ldw BOOT_CONSOLE_HPA_OFFSET(%r0),arg3 /* console hpa */ + ldo PDC_IODC_RI_INIT(%r0),%r4 + stw %r4,-52(sp) + ldil L%PA(hpmc_iodc_buf),%r4 + ldo R%PA(hpmc_iodc_buf)(%r4),%r4 + stw %r4,-56(sp) + ldil L%HPMC_IODC_BUF_SIZE,%r4 + ldo R%HPMC_IODC_BUF_SIZE(%r4),%r4 + stw %r4,-60(sp) + + ldil L%PA(os_hpmc_4),rp + bv (%r3) /* call pdce_proc */ + ldo R%PA(os_hpmc_4)(rp),rp + +os_hpmc_4: + comib,<> 0,ret0,os_hpmc_fail + + /* Call the entry init (just loaded by PDC_IODC) */ + + ldw BOOT_CONSOLE_HPA_OFFSET(%r0),arg0 /* console hpa */ + ldo ENTRY_INIT_MOD_DEV(%r0), arg1 + ldw BOOT_CONSOLE_SPA_OFFSET(%r0),arg2 /* console spa */ + depi 0,31,11,arg2 /* clear bits 21-31 */ + ldo BOOT_CONSOLE_PATH_OFFSET(%r0),arg3 /* console path */ + ldil L%PA(hpmc_raddr),%r4 + ldo R%PA(hpmc_raddr)(%r4),%r4 + stw %r4, -52(sp) + stw %r0, -56(sp) /* HV */ + stw %r0, -60(sp) /* HV */ + stw %r0, -64(sp) /* HV */ + stw %r0, -68(sp) /* lang, must be zero */ + + ldil L%PA(hpmc_iodc_buf),%r5 + ldo R%PA(hpmc_iodc_buf)(%r5),%r5 + ldil L%PA(os_hpmc_5),rp + bv (%r5) + ldo R%PA(os_hpmc_5)(rp),rp + +os_hpmc_5: + comib,<> 0,ret0,os_hpmc_fail + + /* Prepare to call intr_save */ + + /* + * Load kernel page directory (load into user also, since + * we don't intend to ever return to user land anyway) + */ + + ldil L%PA(swapper_pg_dir),%r4 + ldo R%PA(swapper_pg_dir)(%r4),%r4 + mtctl %r4,%cr24 /* Initialize kernel root pointer */ + mtctl %r4,%cr25 /* Initialize user root pointer */ + + /* Clear sr4-sr7 */ + + mtsp %r0, %sr4 + mtsp %r0, %sr5 + mtsp %r0, %sr6 + mtsp %r0, %sr7 + + tovirt %r30 /* make sp virtual */ + + rsm 8,%r0 /* Clear Q bit */ + ldi 1,%r1 + mtctl %r1,%cr29 /* Set trap code to "1" for HPMC */ + mtctl %r0,%cr30 /* Force interruptions to use hpmc stack */ + ldil L%PA(intr_save), %r1 + ldo R%PA(intr_save)(%r1), %r1 + be 0(%sr7,%r1) + nop + +os_hpmc_fail: + + /* + * Reset the system + * + * Some systems may lockup from a broadcast reset, so try the + * hversion PDC_BROADCAST_RESET() first. + * MP_FIXME: reset all processors if more than one central bus. + */ + + /* PDC_BROADCAST_RESET() */ + + ldo PDC_BROADCAST_RESET(%r0),arg0 + ldo 0(%r0),arg1 /* do reset */ + + ldil L%PA(os_hpmc_6),rp + bv (%r3) /* call pdce_proc */ + ldo R%PA(os_hpmc_6)(rp),rp + +os_hpmc_6: + + /* + * possible return values: + * -1 non-existent procedure + * -2 non-existent option + * -16 unaligned stack + * + * If call returned, do a broadcast reset. + */ + + ldil L%0xfffc0000,%r4 /* IO_BROADCAST */ + ldo 5(%r0),%r5 + stw %r5,48(%r4) /* CMD_RESET to IO_COMMAND offset */ + + b . + nop + + /* this label used to compute os_hpmc checksum */ + + .export os_hpmc_end, code + +os_hpmc_end: + + nop diff --git a/arch/parisc/kernel/init_task.c b/arch/parisc/kernel/init_task.c new file mode 100644 index 000000000000..a838548de02d --- /dev/null +++ b/arch/parisc/kernel/init_task.c @@ -0,0 +1,29 @@ +#include +#include +#include + +#include +#include +#include + +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial task structure. + * + * We need to make sure that this is 16384-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +union task_union init_task_union + __attribute__((section("init_task"), aligned(4096))) = { INIT_TASK(init_task_union.task) }; + +unsigned long swapper_pg_dir[PTRS_PER_PGD] __attribute__ ((aligned(4096))) = { 0, }; +#ifdef __LP64__ +unsigned long pmd0[PTRS_PER_PMD] __attribute__ ((aligned(4096))) = { 0, }; +#endif +unsigned long pg0[PT_INITIAL * PTRS_PER_PTE] __attribute__ ((aligned(4096))) = { 0, }; diff --git a/arch/parisc/kernel/inventory.c b/arch/parisc/kernel/inventory.c new file mode 100644 index 000000000000..f62538145295 --- /dev/null +++ b/arch/parisc/kernel/inventory.c @@ -0,0 +1,398 @@ + +/* Copyright (c) 1999 The Puffin Group */ +/* Written by David Kennedy and Alex deVries */ + +#include +#include +#include +#include +#include +#include + +/* +** Debug options +** DEBUG_PAT Dump details which PDC PAT provides about ranges/devices. +*/ +#undef DEBUG_PAT + +extern char *parisc_getHWtype(unsigned short hw_type); + +extern struct hp_device * register_module(void *hpa); +extern void print_devices(char * buf); + + +int pdc_hpa_processor(void *address); + +#ifndef __LP64__ +static u8 iodc_data[32] __attribute__ ((aligned (64))); +static struct pdc_model model __attribute__ ((aligned (8))); +#endif +static unsigned long pdc_result[32] __attribute__ ((aligned (8))) = {0,0,0,0}; +static struct pdc_hpa processor_hpa __attribute__ ((aligned (8))); +static struct pdc_system_map module_result __attribute__ ((aligned (8))); +static struct pdc_module_path module_path __attribute__ ((aligned (8))); + +#ifdef __LP64__ +#include + +int pdc_pat = 0; + +/* +** The module object is filled via PDC_PAT_CELL[Return Cell Module]. +** If a module is found, register module will get the IODC bytes via +** pdc_iodc_read() using the PA view of conf_base_addr for the hpa parameter. +** +** The IO view can be used by PDC_PAT_CELL[Return Cell Module] +** only for SBAs and LBAs. This view will cause an invalid +** argument error for all other cell module types. +** +*/ + +static int +pat_query_module( ulong pcell_loc, ulong mod_index) +{ + extern int num_devices; + extern struct hp_device devices[]; + + pdc_pat_cell_mod_maddr_block_t pa_pdc_cell; + struct hp_device * dev = &devices[num_devices]; + uint64_t temp; /* 64-bit scratch value */ + long status; /* PDC return value status */ + + /* return cell module (PA or Processor view) */ + status = pdc_pat_cell_module(& pdc_result, pcell_loc, mod_index, + PA_VIEW, & pa_pdc_cell); + + if (status != PDC_RET_OK) { + /* no more cell modules or error */ + return status; + } + + /* + ** save parameters in the hp_device + ** (The idea being the device driver will call pdc_pat_cell_module() + ** and store the results in it's own data structure.) + */ + dev->pcell_loc = pcell_loc; + dev->mod_index = mod_index; + + /* save generic info returned from the call */ + /* REVISIT: who is the consumer of this? not sure yet... */ + dev->mod_info = pa_pdc_cell.mod_info; /* pass to PAT_GET_ENTITY() */ + dev->pmod_loc = pa_pdc_cell.mod_location; + dev->mod_path = pa_pdc_cell.mod_path; + + temp = pa_pdc_cell.cba; + register_module((void *) PAT_GET_CBA(temp)); /* fills in dev->hpa */ + +#ifdef DEBUG_PAT + /* dump what we see so far... */ + switch (PAT_GET_ENTITY(dev->mod_info)) { + ulong i; + + case PAT_ENTITY_PROC: + printk ("PAT_ENTITY_PROC: id_eid 0x%lx\n", pa_pdc_cell.mod[0]); + break; + + case PAT_ENTITY_MEM: + printk ("PAT_ENTITY_MEM: amount 0x%lx min_gni_base 0x%lx min_gni_len 0x%lx\n", + pa_pdc_cell.mod[0], + pa_pdc_cell.mod[1], + pa_pdc_cell.mod[2]); + break; + case PAT_ENTITY_CA: + printk ("PAT_ENTITY_CA: %ld\n",pcell_loc); + break; + + case PAT_ENTITY_PBC: + printk ("PAT_ENTITY_PBC: "); + goto print_ranges; + + case PAT_ENTITY_SBA: + printk ("PAT_ENTITY_SBA: "); + goto print_ranges; + + case PAT_ENTITY_LBA: + printk ("PAT_ENTITY_LBA: "); + +print_ranges: + printk ("ranges %ld\n", pa_pdc_cell.mod[1]); + for (i = 0; i < pa_pdc_cell.mod[1]; i++) { + printk (" %ld: 0x%016lx 0x%016lx 0x%016lx\n", i, + pa_pdc_cell.mod[2+i*3], /* type */ + pa_pdc_cell.mod[3+i*3], /* start */ + pa_pdc_cell.mod[4+i*3]); /* finish (ie end) */ + } + printk("\n"); + break; + } +#endif /* DEBUG_PAT */ + return PDC_RET_OK; +} + + +static int do_pat_inventory(void) +{ + ulong mod_index=0; + int status; + ulong cell_num; + ulong pcell_loc; + + pdc_pat = (pdc_pat_cell_get_number(&pdc_result) == PDC_OK); + if (!pdc_pat) + { + return 0; + } + + cell_num = pdc_result[0]; /* Cell number call was made */ + + /* As of PDC PAT ARS 2.5, ret[1] is NOT architected! */ + pcell_loc = pdc_result[1]; /* Physical location of the cell */ + +#ifdef DEBUG_PAT + printk("CELL_GET_NUMBER: 0x%lx 0x%lx\n", cell_num, pcell_loc); +#endif + + status = pdc_pat_cell_num_to_loc(&pdc_result, cell_num); + if (status == PDC_BAD_OPTION) + { + /* Prelude (and it's successors: Lclass, A400/500) only + ** implement PDC_PAT_CELL sub-options 0 and 2. + ** "Home cook'n is best anyhow!" + */ + } else if (PDC_OK == status) { + /* so far only Halfdome supports this */ + pcell_loc = pdc_result[0]; + } else { + panic("WTF? CELL_GET_NUMBER give me invalid cell number?"); + } + + while (PDC_RET_OK == pat_query_module(pcell_loc, mod_index)) + { + mod_index++; + } + + return mod_index; +} +#endif /* __LP64__ */ + +static int do_newer_workstation_inventory(void) +{ + long status; + int i, num = 0; + + /* So the idea here is to simply try one SYSTEM_MAP call. If + that one works, great, otherwise do it another way */ + + status = pdc_system_map_find_mods(&module_result,&module_path,0); + + if (status == PDC_RET_OK) { + /* This is for newer non-PDC-PAT boxes */ + + printk("a newer box...\n"); + for(i=0, status=PDC_RET_OK; status != PDC_RET_NE_PROC && + status != PDC_RET_NE_MOD ;i++) { + + status = pdc_system_map_find_mods(&module_result,&module_path,i); + if (status == PDC_RET_OK) { + num++; + register_module(module_result.mod_addr); + } + } + } + + return (num > 0); +} + +#ifndef __LP64__ +static struct pdc_memory_map r_addr __attribute__ ((aligned (8))); + +static int really_do_oldhw_inventory(void) +{ + int i, mod, num = 0; + int status; + unsigned int hw_type; + unsigned int func; + + /* This is undocumented at the time of writing, but basically + we're setting up mod_path so that bc[0..4]=0xff, and step + through mod to get the "Path Structure for GSC Modules". If + it works, use the returned HPA and determine the hardware type. */ + + for (i=0;i<6;i++) module_path.bc[i]=0xff; + + for (mod=0;mod<16;mod++) { + char *stype = NULL; + + module_path.mod=mod; + status = pdc_mem_map_hpa(&r_addr, &module_path); + if (status!=PDC_RET_OK) continue; + + status = pdc_iodc_read(&pdc_result,(void *) r_addr.hpa, + 0, &iodc_data,32 ); + if (status!=PDC_RET_OK) continue; + hw_type = iodc_data[3]&0x1f; + + switch (hw_type) + { + case HPHW_NPROC: /* 0 */ + stype="Processor"; break; + + case HPHW_MEMORY: /* 1 */ + stype="Memory"; break; + + case HPHW_B_DMA: /* 2 */ + stype="Type B DMA"; break; + + case HPHW_A_DMA: /* 4 */ + stype="Type A DMA"; break; + + case HPHW_A_DIRECT: /* 5 */ + stype="Type A Direct"; break; + + case HPHW_BCPORT: /* 7 */ + stype="Bus Converter Port"; break; + + case HPHW_CONSOLE: /* 9 */ + stype="Console"; break; + + case HPHW_FIO: /* 10 - Graphics */ + stype="Foreign I/O (Graphics)"; break; + + case HPHW_BA: /* 11 - Bus Adapter */ + stype="Bus Adapter"; break; + + case HPHW_IOA: /* 12 */ + stype="I/O Adapter"; break; + + case HPHW_BRIDGE: /* 13 */ + stype="Bridge"; break; + + case HPHW_FABRIC: /* 14 */ + stype="Fabric"; break; + + case HPHW_FAULTY: /* 31 */ + stype="Faulty HW"; break; + + case HPHW_OTHER: /* 42 */ + default: + printk("Don't know this hw_type: %d\n", hw_type); + break; + } + + // This is kluged. But don't want to replicate code for + // most of the above cases. + if (stype) { +#ifdef DBG_PDC_QUERY + // parisc/kernel/drivers.c + extern int num_devices; + extern struct hp_device devices[]; + struct hp_hardware *h; +#endif + + status = pdc_mem_map_hpa(&r_addr, &module_path); + if (status==PDC_RET_OK && register_module((void *) r_addr.hpa) != NULL) + num++; + + + if (hw_type == HPHW_BA) { + /* Now, we're checking for devices for each + module. I seem to think that the + modules in question are Lasi (2), 2nd Lasi (6) + Wax (5). To do this, set bc[5]=0, and set + bc[4] to the module, and step through the + functions. */ + + for (i=0;i<4;i++) module_path.bc[i]=0xff; + module_path.bc[4]=mod; + for (func=0;func<16;func++) { + module_path.mod = func; + module_path.bc[5]=0; + status = pdc_mem_map_hpa(&r_addr, &module_path); + if (status!=PDC_RET_OK) continue; + if (register_module((void *) r_addr.hpa) != NULL) + num++; + } + } + // reset module_path.bc[] + for (i=0;i<6;i++) module_path.bc[i]=0xff; + + +#ifdef DBG_PDC_QUERY +// +// Let print_devices() dump everything which is registered. +// + h = devices[num_devices-1].reference; + + if (h) stype = h->name; + printk("Found %s at %d\n", stype, module_path.mod); +#endif + } + } + return num; +} + +static int +do_old_inventory(void) +{ + unsigned int bus_id; + long status; + int ok = 0; + + printk(" an older box...\n"); + + /* Here, we're going to check the model, and decide + if we should even bother trying. */ + + status = pdc_model_info(&model); + + bus_id = (model.hversion >> (4+7) ) &0x1f; + + /* Here, we're checking the HVERSION of the CPU. + We're only checking the 0th CPU, since it'll + be the same on an SMP box. */ + + switch (bus_id) { + case 0x4: /* 720, 730, 750, 735, 755 */ + case 0x6: /* 705, 710 */ + case 0x7: /* 715, 725 */ + case 0x8: /* 745, 747, 742 */ + case 0xA: /* 712 and similiar */ + case 0xC: /* 715/64, at least */ + + /* Do inventory using MEM_MAP */ + really_do_oldhw_inventory(); + ok = 1; + break; + default: /* Everything else */ + printk("This is a very very old machine, with a bus_id of 0x%x.\n",bus_id); + panic("This will probably never run Linux.\n"); + } + + return ok; +} + +#endif /* !__LP64__ */ + +void do_inventory(void){ + if((pdc_hpa_processor(&processor_hpa))<0){ + printk(KERN_INFO "Couldn't get the HPA of the processor.\n" ); + } + + printk("Searching for devices in PDC firmware... "); + printk("processor hpa 0x%lx\n", processor_hpa.hpa); + + if (!( + do_newer_workstation_inventory() +#ifdef __LP64__ + || do_pat_inventory() +#else /* __LP64__ */ + || do_old_inventory() +#endif /* __LP64__ */ + )) + { + panic("I can't get the hardware inventory on this machine"); + } + print_devices(NULL); +} + diff --git a/arch/parisc/kernel/iosapic.c b/arch/parisc/kernel/iosapic.c new file mode 100644 index 000000000000..c38fe0bce2eb --- /dev/null +++ b/arch/parisc/kernel/iosapic.c @@ -0,0 +1,1101 @@ +/* +** I/O Sapic Driver - PCI interrupt line support +** +** (c) Copyright 1999 Grant Grundler +** (c) Copyright 1999 Hewlett-Packard Company +** +** 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. +** +** The I/O sapic driver manages the Interrupt Redirection Table which is +** the control logic to convert PCI line based interrupts into a Message +** Signaled Interrupt (aka Transaction Based Interrupt, TBI). +** +** Acronyms +** -------- +** HPA Hard Physical Address (aka MMIO address) +** IRQ Interrupt ReQuest. Implies Line based interrupt. +** IRT Interrupt Routing Table (provided by PAT firmware) +** IRdT Interrupt Redirection Table. IRQ line to TXN ADDR/DATA +** table which is implemented in I/O SAPIC. +** ISR Interrupt Service Routine. aka Interrupt handler. +** MSI Message Signaled Interrupt. PCI 2.2 functionality. +** aka Transaction Based Interrupt (or TBI). +** PA Precision Architecture. HP's RISC architecture. +** RISC Reduced Instruction Set Computer. +** +** +** What's a Message Signalled Interrupt? +** ------------------------------------- +** MSI is a write transaction which targets a processor and is similar +** to a processor write to memory or MMIO. MSIs can be generated by I/O +** devices as well as processors and require *architecture* to work. +** +** PA only supports MSI. So I/O subsystems must either natively generate +** MSIs (e.g. GSC or HP-PB) or convert line based interrupts into MSIs +** (e.g. PCI and EISA). IA64 supports MSIs via a "local SAPIC" which +** acts on behalf of a processor. +** +** MSI allows any I/O device to interrupt any processor. This makes +** load balancing of the interrupt processing possible on an SMP platform. +** Interrupts are also ordered WRT to DMA data. It's possible on I/O +** coherent systems to completely eliminate PIO reads from the interrupt +** path. The device and driver must be designed and implemented to +** guarantee all DMA has been issued (issues about atomicity here) +** before the MSI is issued. I/O status can then safely be read from +** DMA'd data by the ISR. +** +** +** PA Firmware +** ----------- +** PA-RISC platforms have two fundementally different types of firmware. +** For PCI devices, "Legacy" PDC initializes the "INTERRUPT_LINE" register +** and BARs similar to a traditional PC BIOS. +** The newer "PAT" firmware supports PDC calls which return tables. +** PAT firmware only initializes PCI Console and Boot interface. +** With these tables, the OS can progam all other PCI devices. +** +** One such PAT PDC call returns the "Interrupt Routing Table" (IRT). +** The IRT maps each PCI slot's INTA-D "output" line to an I/O SAPIC +** input line. If the IRT is not available, this driver assumes +** INTERRUPT_LINE register has been programmed by firmware. The latter +** case also means online addition of PCI cards can NOT be supported +** even if HW support is present. +** +** All platforms with PAT firmware to date (Oct 1999) use one Interrupt +** Routing Table for the entire platform. +** +** Where's the iosapic? +** -------------------- +** I/O sapic is part of the "Core Electronics Complex". And on HP platforms +** it's integrated as part of the PCI bus adapter, "lba". So no bus walk +** will discover I/O Sapic. I/O Sapic driver learns about each device +** when lba driver advertises the presence of the I/O sapic by calling +** iosapic_register(). +** +** +** IRQ region notes +** ---------------- +** The data passed to iosapic_interrupt() is per IRQ line. +** Each IRQ line will get one txn_addr/data pair. Thus each IRQ region, +** will have several txn_addr/data pairs (up to 7 for current I/O SAPIC +** implementations). The IRQ region "sysdata" will NOT be directly passed +** to the interrupt handler like GSCtoPCI (dino.c). +** +** iosapic interrupt handler will NOT call do_irq_mask(). +** It doesn't need to read a bit mask to determine which IRQ line was pulled +** since it already knows based on vector_info passed to iosapic_interrupt(). +** +** One IRQ number represents both an IRQ line and a driver ISR. +** The I/O sapic driver can't manage shared IRQ lines because +** additional data besides the IRQ number must be passed via +** irq_region_ops. do_irq() and request_irq() must manage +** a sharing a bit in the mask. +** +** iosapic_interrupt() replaces do_irq_mask() and calls do_irq(). +** Which IRQ line was asserted is already known since each +** line has unique data associated with it. We could omit +** iosapic_interrupt() from the calling path if it did NOT need +** to write EOI. For unshared lines, it really doesn't. +** +** Unfortunately, can't optimize out EOI if IRQ line isn't "shared". +** N-class console "device" and some sort of heartbeat actually share +** one line though only one driver is registered......this was +** true for HP-UX at least. May not be true for parisc-linux. +** +** +** Overview of exported iosapic functions +** -------------------------------------- +** (caveat: code isn't finished yet - this is just the plan) +** +** iosapic_init: +** o initialize globals (lock, etc) +** o try to read IRT. Presence of IRT determines if this is +** a PAT platform or not. +** +** iosapic_register(): +** o create iosapic_info instance data structure +** o allocate vector_info array for this iosapic +** o initialize vector_info - read corresponding IRdT? +** +** iosapic_xlate_pin: (only called by fixup_irq for PAT platform) +** o intr_pin = read cfg (INTERRUPT_PIN); +** o if (device under PCI-PCI bridge) +** translate slot/pin +** +** iosapic_fixup_irq: +** o if PAT platform (IRT present) +** intr_pin = iosapic_xlate_pin(isi,pcidev): +** intr_line = find IRT entry(isi, PCI_SLOT(pcidev), intr_pin) +** save IRT entry into vector_info later +** write cfg INTERRUPT_LINE (with intr_line)? +** else +** intr_line = pcidev->irq +** IRT pointer = NULL +** endif +** o locate vector_info (needs: isi, intr_line) +** o allocate processor "irq" and get txn_addr/data +** o request_irq(processor_irq, iosapic_interrupt, vector_info,...) +** o pcidev->irq = isi->isi_region...base + intr_line; +** +** iosapic_interrupt: +** o call do_irq(vector->isi->irq_region, vector->irq_line, regs) +** o assume level triggered and write EOI +** +** iosapic_enable_irq: +** o clear any pending IRQ on that line +** o enable IRdT - call enable_irq(vector[line]->processor_irq) +** o write EOI in case line is already asserted. +** +** iosapic_disable_irq: +** o disable IRdT - call disable_irq(vector[line]->processor_irq) +** +** FIXME: mask/unmask +*/ + + +/* FIXME: determine which include files are really needed */ +#include +#include +#include +#include +#include /* pci cfg accessor functions */ +#include +#include +#include +#include /* irqaction */ +#include /* irq_region support */ + +#include /* get in-line asm for swab */ +#include +#include +#include +#include +#include +#include /* gsc_read/write functions */ + +#include +#include "./iosapic_private.h" + +#define MODULE_NAME "iosapic" + +/* "local" compile flags */ +#undef IOSAPIC_CALLBACK +#undef PCI_BRIDGE_FUNCS +#undef DEBUG_IOSAPIC +#undef DEBUG_IOSAPIC_IRT + + +#ifdef DEBUG_IOSAPIC +static char assert_buf[128]; + +static int +assert_failed (char *a, char *f, int l) +{ + sprintf(assert_buf, + "ASSERT(%s) failed!\nline %d in %s\n", + a, /* assertion text */ + l, /* line number */ + f); /* file name */ + panic(assert_buf); + return 0; +} + +#undef ASSERT +#define ASSERT(EX) { if (!(EX)) assert_failed(# EX, __FILE__, __LINE__); } + +#define DBG(x...) printk(x) + +#else /* DEBUG_IOSAPIC */ + +#define DBG(x...) +#define ASSERT(EX) + +#endif /* DEBUG_IOSAPIC */ + +#ifdef DEBUG_IOSAPIC_IRT +#define DBG_IRT(x...) printk(x) +#else +#define DBG_IRT(x...) +#endif + + +#define READ_U8(addr) gsc_readb(addr) +#define READ_U16(addr) le16_to_cpu(gsc_readw((u16 *) (addr))) +#define READ_U32(addr) le32_to_cpu(gsc_readl((u32 *) (addr))) +#define READ_REG16(addr) gsc_readw((u16 *) (addr)) +#define READ_REG32(addr) gsc_readl((u32 *) (addr)) +#define WRITE_U8(value, addr) gsc_writeb(value, addr) +#define WRITE_U16(value, addr) gsc_writew(cpu_to_le16(value), (u16 *) (addr)) +#define WRITE_U32(value, addr) gsc_writel(cpu_to_le32(value), (u32 *) (addr)) +#define WRITE_REG16(value, addr) gsc_writew(value, (u16 *) (addr)) +#define WRITE_REG32(value, addr) gsc_writel(value, (u32 *) (addr)) + + +#define IOSAPIC_REG_SELECT 0 +#define IOSAPIC_REG_WINDOW 0x10 +#define IOSAPIC_REG_EOI 0x40 + +#define IOSAPIC_REG_VERSION 0x1 + +#define IOSAPIC_IRDT_ENTRY(idx) (0x10+(idx)*2) +#define IOSAPIC_IRDT_ENTRY_HI(idx) (0x11+(idx)*2) + +/* +** FIXME: revisit which GFP flags we should really be using. +** GFP_KERNEL includes __GFP_WAIT flag and that may not +** be acceptable. Since this is boot time, we shouldn't have +** to wait ever and this code should (will?) never get called +** from the interrrupt context. +*/ +#define IOSAPIC_KALLOC(a_type, cnt) \ + (a_type *) kmalloc(sizeof(a_type)*(cnt), GFP_KERNEL) +#define IOSAPIC_FREE(addr, f_type, cnt) kfree((void *)addr) + + +#define IOSAPIC_LOCK(lck) spin_lock_irqsave(lck, irqflags) +#define IOSAPIC_UNLOCK(lck) spin_unlock_irqrestore(lck, irqflags) + + +#define IOSAPIC_VERSION_MASK 0x000000ff +#define IOSAPIC_VERSION_SHIFT 0x0 +#define IOSAPIC_VERSION(ver) \ + (int) ((ver & IOSAPIC_VERSION_MASK) >> IOSAPIC_VERSION_SHIFT) + +#define IOSAPIC_MAX_ENTRY_MASK 0x00ff0000 + +#define IOSAPIC_MAX_ENTRY_SHIFT 0x10 +#define IOSAPIC_IRDT_MAX_ENTRY(ver) \ + (int) ((ver&IOSAPIC_MAX_ENTRY_MASK) >> IOSAPIC_MAX_ENTRY_SHIFT) + +/* bits in the "low" I/O Sapic IRdT entry */ +#define IOSAPIC_IRDT_ENABLE 0x10000 +#define IOSAPIC_IRDT_PO_LOW 0x02000 +#define IOSAPIC_IRDT_LEVEL_TRIG 0x08000 +#define IOSAPIC_IRDT_MODE_LPRI 0x00100 + +/* bits in the "high" I/O Sapic IRdT entry */ +#define IOSAPIC_IRDT_ID_EID_SHIFT 0x10 + + + +#define IOSAPIC_EOI(eoi_addr, eoi_data) gsc_writel(eoi_data, eoi_addr) + +#if IOSAPIC_CALLBACK +/* +** Shouldn't use callback since SAPIC doesn't have an officially assigned +** H or S version numbers. Slight long term risk the number chosen would +** collide with something else. +** But benefit is cleaner lba/sapic interface. +** Might be worth it but for just use direct calls for now. +** +** Entry below is copied from lba driver. +** Only thing different is hw_type. +*/ +static struct pa_iodc_driver iosapic_driver_for[] = { + {HPHW_OTHER, 0x782, 0, 0x0000A, 0, 0x00, + DRIVER_CHECK_HWTYPE + DRIVER_CHECK_HVERSION + DRIVER_CHECK_SVERSION, + "I/O Sapic", "",(void *) iosapic_callback}, + {0,0,0,0,0,0, + 0, + (char *) NULL,(char *) NULL,(void *) NULL} +}; +#endif /* IOSAPIO_CALLBACK */ + + +static struct iosapic_info *iosapic_list; +static spinlock_t iosapic_lock; +static int iosapic_count; + + +/* +** REVISIT: future platforms may have more than one IRT. +** If so, the following three fields form a structure which +** then be linked into a list. Names are chosen to make searching +** for them easy - not necessarily accurate (eg "cell"). +** +** Alternative: iosapic_info could point to the IRT it's in. +** iosapic_register() could search a list of IRT's. +*/ +static struct irt_entry *irt_cell; +static size_t irt_num_entry; + + + +/* +** iosapic_load_irt +** +** The "Get PCI INT Routing Table Size" option returns the number of +** entries in the PCI interrupt routing table for the cell specified +** in the cell_number argument. The cell number must be for a cell +** within the caller's protection domain. +** +** The "Get PCI INT Routing Table" option returns, for the cell +** specified in the cell_number argument, the PCI interrupt routing +** table in the caller allocated memory pointed to by mem_addr. +** We assume the IRT only contains entries for I/O SAPIC and +** calculate the size based on the size of I/O sapic entries. +** +** The PCI interrupt routing table entry format is derived from the +** IA64 SAL Specification 2.4. The PCI interrupt routing table defines +** the routing of PCI interrupt signals between the PCI device output +** "pins" and the IO SAPICs' input "lines" (including core I/O PCI +** devices). This table does NOT include information for devices/slots +** behind PCI to PCI bridges. See PCI to PCI Bridge Architecture Spec. +** for the architected method of routing of IRQ's behind PPB's. +*/ + + +static int __init /* return number of entries as success/fail flag */ +iosapic_load_irt(unsigned long cell_num, struct irt_entry **irt) +{ + struct pdc_pat_io_num pdc_io_num; /* PAT PDC return block */ + long status; /* PDC return value status */ + struct irt_entry *table = NULL; /* start of interrupt routing tbl */ + unsigned long num_entries = 0UL; + + ASSERT(NULL != irt); + /* FIXME ASSERT(((&pdc_io_num) & (0x3f)) == 0); enforce 32-byte alignment */ + + /* Try PAT_PDC to get interrupt routing table size */ + DBG(KERN_DEBUG "calling get_irt_size\n"); + status = pdc_pat_get_irt_size( &pdc_io_num, cell_num); + DBG(KERN_DEBUG "get_irt_size: %ld\n", status); + + switch(status) { + + case PDC_RET_OK: /* PAT box. Proceed to get the IRT */ + + /* save the number of entries in the table */ + num_entries = pdc_io_num.num; + ASSERT(0UL != num_entries); + + /* + ** allocate memory for interrupt routing table + ** This interface isn't really right. We are assuming + ** the contents of the table are exclusively + ** for I/O sapic devices. + */ + table = IOSAPIC_KALLOC(struct irt_entry, num_entries); + if (table == NULL) { + printk(KERN_WARNING MODULE_NAME ": read_irt : can not alloc mem for IRT\n"); + return 0; + } + + /* get PCI INT routing table */ + status = pdc_pat_get_irt( (void *) table, cell_num); + DBG(KERN_DEBUG "pdc_pat_get_irt: %ld\n", status); + ASSERT(status == PDC_RET_OK); + break; + + case PDC_RET_NE_PROC: /* Not a PAT platform. Try PDC_PCI extensions */ + /* + ** C3000/J5000 (and similar) platforms with "legacy" PDC + ** will return exactly one IRT. + ** So if we have one, don't need to get it again. + */ + if (NULL != irt_cell) + break; + + status = pdc_pci_irt_size( (void *)&pdc_io_num, + /* elroy HPA (really a NOP) */ 0); + DBG(KERN_WARNING "pdc_pci_irt_size: %ld\n", status); + + if (PDC_RET_OK != status) { + /* Not a "legacy" system with I/O SAPIC either */ + return 0; + } + + num_entries = pdc_io_num.num; + ASSERT(0UL != num_entries); + + table = IOSAPIC_KALLOC(struct irt_entry, num_entries); + if (table == NULL) { + printk(KERN_WARNING MODULE_NAME ": read_irt : can not alloc mem for IRT\n"); + return 0; + } + + status = pdc_pci_irt( (void *) &pdc_io_num, + (void *) NULL, /* Elroy HPA - not used */ + (void *) table); + + ASSERT(PDC_RET_OK == status); + break; + + default: + printk(KERN_WARNING MODULE_NAME ": PDC_PAT_IO call failed with %ld\n", status); + break; + } + + /* return interrupt table address */ + *irt = table; + + +#ifdef DEBUG_IOSAPIC_IRT + { + struct irt_entry *p = table; + int i; + + printk(MODULE_NAME " Interrupt Routing Table (cell %ld)\n", cell_num); + printk(MODULE_NAME " start = 0x%p num_entries %ld entry_size %d\n", + table, + num_entries, + (int) sizeof(struct irt_entry)); + + for (i = 0 ; i < num_entries ; i++, p++) + { + printk(MODULE_NAME " %02x %02x %02x %02x %02x %02x %02x %02x %08x%08x\n", + p->entry_type, p->entry_length, p->interrupt_type, + p->polarity_trigger, p->src_bus_irq_devno, p->src_bus_id, + p->src_seg_id, p->dest_iosapic_intin, + ((u32 *) p)[2], + ((u32 *) p)[3] + ); + } + } +#endif /* DEBUG_IOSAPIC_IRT */ + + return num_entries; +} + + + +void __init +iosapic_init(void) +{ + /* init global data */ + iosapic_lock = SPIN_LOCK_UNLOCKED; + iosapic_list = (struct iosapic_info *) NULL; + iosapic_count = 0; + + DBG("iosapic_init()\n"); + + /* + ** get IRT for this cell. + */ + irt_num_entry = iosapic_load_irt(0L, &irt_cell); + if (0 == irt_num_entry) + irt_cell = NULL; /* old PDC w/o iosapic */ + +#ifdef IOSAPIC_CALLBACK + /* + ** When new I/O SAPICs are discovered, this callback + ** will get invoked. Implies lba driver will register + ** I/O Sapic as a device it "discovered" with faked + ** IODC data. + */ + register_driver(iosapic_driver_for); +#endif /* IOSAPIC_CALLBACK */ +} + + +/* +** Return the IRT entry in case we need to look something else up. +*/ +static struct irt_entry * +irt_find_irqline(struct iosapic_info *isi, u8 slot, u8 intr_pin) +{ + struct irt_entry *i = irt_cell; + int cnt; /* track how many entries we've looked at */ + u8 irq_devno = (slot << IRT_DEV_SHIFT) | (intr_pin-1); + + DBG_IRT("irt_find_irqline() SLOT %d pin %d\n", slot, intr_pin); + + for (cnt=0; cnt < irt_num_entry; cnt++, i++) { + + /* + ** Validate: entry_type, entry_length, interrupt_type + ** + ** Difference between validate vs compare is the former + ** should print debug info and is not expected to "fail" + ** on current platforms. + */ + if (i->entry_type != IRT_IOSAPIC_TYPE) { + DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d type %d\n", i, cnt, i->entry_type); + continue; + } + + if (i->entry_length != IRT_IOSAPIC_LENGTH) { + DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d length %d\n", i, cnt, i->entry_length); + continue; + } + + if (i->interrupt_type != IRT_VECTORED_INTR) { + DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d interrupt_type %d\n", i, cnt, i->interrupt_type); + continue; + } + + /* + ** Compare: dest_iosapic_addr, src_bus_irq_devno + */ + if (i->dest_iosapic_addr != (u64) ((long) isi->isi_hpa)) + continue; + + if ((i->src_bus_irq_devno & IRT_IRQ_DEVNO_MASK) != irq_devno) + continue; + + /* + ** Ignore: src_bus_id and rc_seg_id correlate with + ** iosapic_info->isi_hpa on HP platforms. + ** If needed, pass in "PFA" (aka config space addr) + ** instead of slot. + */ + + /* Found it! */ + return i; + } + + printk(KERN_WARNING MODULE_NAME ": 0x%p : no IRT entry for slot %d, pin %d\n", + isi->isi_hpa, slot, intr_pin); + return NULL; +} + + +/* +** xlate_pin() supports the skewing of IRQ lines done by subsidiary bridges. +** Legacy PDC already does this translation for us and stores it in INTR_LINE. +** +** PAT PDC needs to basically do what legacy PDC does: +** o read PIN +** o adjust PIN in case device is "behind" a PPB +** (eg 4-port 100BT and SCSI/LAN "Combo Card") +** o convert slot/pin to I/O SAPIC input line. +** +** HP platforms only support: +** o one level of skewing for any number of PPBs +** o only support PCI-PCI Bridges. +*/ +static struct irt_entry * +iosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev) +{ + u8 intr_pin, intr_slot; + + (void) pci_read_config_byte(pcidev, PCI_INTERRUPT_PIN, &intr_pin); + + DBG_IRT("iosapic_xlate_pin() SLOT %d pin %d\n", PCI_SLOT(pcidev->devfn), intr_pin); + + if (0 == intr_pin) + { + /* + ** The device does NOT support/use IRQ lines. + */ + return NULL; + } + + /* Check if pcidev behind a PPB */ + if (NULL != pcidev->bus->self) + { + /* Convert pcidev INTR_PIN into something we + ** can lookup in the IRT. + */ +#ifdef PCI_BRIDGE_FUNCS + /* + ** Proposal #1: + ** + ** call implementation specific translation function + ** This is architecturally "cleaner". HP-UX doesn't + ** support other secondary bus types (eg. E/ISA) directly. + ** May be needed for other processor (eg IA64) architectures + ** or by some ambitous soul who wants to watch TV. + */ + if (pci_bridge_funcs->xlate_intr_line) { + intr_pin = (*pci_bridge_funcs->xlate_intr_line)(pcidev); + } +#else /* PCI_BRIDGE_FUNCS */ + struct pci_bus *p = pcidev->bus; + /* + ** Proposal #2: + ** The "pin" is skewed ((pin + dev - 1) % 4). + ** + ** This isn't very clean since I/O SAPIC must assume: + ** - all platforms only have PCI busses. + ** - only PCI-PCI bridge (eg not PCI-EISA, PCI-PCMCIA) + ** - IRQ routing is only skewed once regardless of + ** the number of PPB's between iosapic and device. + ** (Bit3 expansion chassis follows this rule) + ** + ** Advantage is it's really easy to implement. + */ + intr_pin = ((intr_pin-1)+PCI_SLOT(pcidev->devfn)) % 4; + intr_pin++; /* convert back to INTA-D (1-4) */ +#endif /* PCI_BRIDGE_FUNCS */ + + /* + ** Locate the host slot the PPB nearest the Host bus + ** adapter. + */ + while (NULL != p->parent->self) + p = p->parent; + + intr_slot = PCI_SLOT(p->self->devfn); + } else { + intr_slot = PCI_SLOT(pcidev->devfn); + } + DBG_IRT("iosapic_xlate_pin: bus %d slot %d pin %d\n", + pcidev->bus->secondary, intr_slot, intr_pin); + + return irt_find_irqline(isi, intr_slot, intr_pin); +} + + +static void +iosapic_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct vector_info *vi = (struct vector_info *)dev_id; + extern void do_irq(struct irqaction *a, int i, struct pt_regs *p); + int irq_num = vi->vi_ios->isi_region->data.irqbase + vi->vi_irqline; + + DBG("iosapic_interrupt(): irq %d line %d eoi %p\n", irq, vi->vi_irqline, + vi->vi_eoi_addr); + +/* FIXME: Need to mask/unmask? processor IRQ is already masked... */ + do_irq(&vi->vi_ios->isi_region->action[vi->vi_irqline], irq_num, regs); + + /* + ** PCI only supports level triggered in order to share IRQ lines. + ** I/O SAPIC must always issue EOI. + */ + IOSAPIC_EOI(vi->vi_eoi_addr, vi->vi_eoi_data); +} + + +int +iosapic_fixup_irq(void *isi_obj, struct pci_dev *pcidev) +{ + struct iosapic_info *isi = (struct iosapic_info *)isi_obj; + struct irt_entry *irte = NULL; /* only used if PAT PDC */ + struct vector_info *vi; + int isi_line; /* line used by device */ + int tmp; + + if (NULL == isi) { + printk(KERN_WARNING MODULE_NAME ": 0x%p hpa not registered\n", isi->isi_hpa); + return(-1); + } + + /* lookup IRT entry for isi/slot/pin set */ + irte = iosapic_xlate_pin(isi, pcidev); + if (NULL == irte) { + return(-1); + } + DBG_IRT("iosapic_fixup_irq(): irte %p %x %x %x %x %x %x %x %x\n", + irte, + irte->entry_type, + irte->entry_length, + irte->polarity_trigger, + irte->src_bus_irq_devno, + irte->src_bus_id, + irte->src_seg_id, + irte->dest_iosapic_intin, + (u32) irte->dest_iosapic_addr); + isi_line = irte->dest_iosapic_intin; + + /* get vector info for this input line */ + ASSERT(NULL != isi->isi_vector); + vi = &(isi->isi_vector[isi_line]); + DBG_IRT("iosapic_fixup_irq: line %d vi 0x%p\n", isi_line, vi); + vi->vi_irte = irte; + + /* Allocate processor IRQ */ + vi->vi_txn_irq = txn_alloc_irq(); + +/* XXX/FIXME The txn_alloc_irq() code and related code should be moved +** to enable_irq(). That way we only allocate processor IRQ bits +** for devices that actually have drivers claiming them. +** Right now we assign an IRQ to every PCI device present regardless +** of whether it's used or not. +*/ + if (vi->vi_txn_irq < 0) + panic("I/O sapic: couldn't get TXN IRQ\n"); + + /* enable_irq() will use txn_* to program IRdT */ + vi->vi_txn_addr = txn_alloc_addr(vi->vi_txn_irq); + vi->vi_txn_data = txn_alloc_data(vi->vi_txn_irq, 8); + ASSERT(vi->vi_txn_data < 256); /* matches 8 above */ + + tmp = request_irq(vi->vi_txn_irq, iosapic_interrupt, 0, "iosapic", vi); + ASSERT(tmp == 0); + + vi->vi_eoi_addr = ((void *) isi->isi_hpa) + IOSAPIC_REG_EOI; + vi->vi_eoi_data = cpu_to_le32(vi->vi_irqline); + + ASSERT(NULL != isi->isi_region); + /* + ** pcidev->irq still needs to be virtualized. + */ + pcidev->irq = isi->isi_region->data.irqbase + isi_line; + + DBG_IRT("iosapic_fixup_irq() %d:%d %x %x line %d irq %d\n", PCI_SLOT(pcidev->devfn), + PCI_FUNC(pcidev->devfn), pcidev->vendor, pcidev->device, isi_line, pcidev->irq); + + return(pcidev->irq); +} + + +static void +iosapic_rd_irt_entry(struct vector_info *vi , u32 *dp0, u32 *dp1) +{ + struct iosapic_info *isp = vi->vi_ios; + u8 idx = vi->vi_irqline; + + /* point the window register to the lower word */ + WRITE_U32(IOSAPIC_IRDT_ENTRY(idx), isp->isi_hpa+IOSAPIC_REG_SELECT); + *dp0 = READ_U32(isp->isi_hpa+IOSAPIC_REG_WINDOW); + + /* point the window register to the higher word */ + WRITE_U32(IOSAPIC_IRDT_ENTRY_HI(idx), isp->isi_hpa+IOSAPIC_REG_SELECT); + *dp1 = READ_U32(isp->isi_hpa+IOSAPIC_REG_WINDOW); +} + + +static void +iosapic_wr_irt_entry(struct vector_info *vi, u32 dp0, u32 dp1) +{ + struct iosapic_info *isp = vi->vi_ios; + + ASSERT(NULL != isp); + ASSERT(NULL != isp->isi_hpa); + DBG_IRT("iosapic_wr_irt_entry(): irq %d hpa %p WINDOW %p 0x%x 0x%x\n", + vi->vi_irqline, + isp->isi_hpa, isp->isi_hpa+IOSAPIC_REG_WINDOW, + dp0, dp1); + + /* point the window register to the lower word */ + WRITE_U32(IOSAPIC_IRDT_ENTRY(vi->vi_irqline), isp->isi_hpa+IOSAPIC_REG_SELECT); + WRITE_U32( dp0, isp->isi_hpa+IOSAPIC_REG_WINDOW); + + /* Read the window register to flush the writes down to HW */ + dp0 = READ_U32(isp->isi_hpa+IOSAPIC_REG_WINDOW); + + /* point the window register to the higher word */ + WRITE_U32(IOSAPIC_IRDT_ENTRY_HI(vi->vi_irqline), isp->isi_hpa+IOSAPIC_REG_SELECT); + WRITE_U32( dp1, isp->isi_hpa+IOSAPIC_REG_WINDOW); + + /* Read the window register to flush the writes down to HW */ + dp1 = READ_U32(isp->isi_hpa+IOSAPIC_REG_WINDOW); +} + + +/* +** set_irt prepares the data (dp0, dp1) according to the vector_info +** and target cpu (id_eid). dp0/dp1 are then used to program I/O SAPIC +** IRdT for the given "vector" (aka IRQ line). +*/ +static void +iosapic_set_irt_data( struct vector_info *vi, u32 *dp0, u32 *dp1) +{ + u32 mode = 0; + struct irt_entry *p = vi->vi_irte; + ASSERT(NULL != vi->vi_irte); + + if ((p->polarity_trigger & IRT_PO_MASK) == IRT_ACTIVE_LO) + mode |= IOSAPIC_IRDT_PO_LOW; + + if (((p->polarity_trigger >> IRT_EL_SHIFT) & IRT_EL_MASK) == IRT_LEVEL_TRIG) + mode |= IOSAPIC_IRDT_LEVEL_TRIG; + + /* + ** IA64 REVISIT + ** PA doesn't support EXTINT or LPRIO bits. + */ + + ASSERT(vi->vi_txn_data); + *dp0 = mode | (u32) vi->vi_txn_data; + + /* + ** Extracting id_eid isn't a real clean way of getting it. + ** But the encoding is the same for both PA and IA64 platforms. + */ +#ifdef __LP64__ + if (pdc_pat) { + /* + ** PAT PDC just hands it to us "right". + ** vi_txn_addr comes from cpu_data[x].txn_addr. + */ + *dp1 = (u32) (vi->vi_txn_addr); + } else +#endif + { + /* + ** eg if base_addr == 0xfffa0000), + ** we want to get 0xa0ff0000. + ** + ** eid 0x0ff00000 -> 0x00ff0000 + ** id 0x000ff000 -> 0xff000000 + */ + *dp1 = (((u32)vi->vi_txn_addr & 0x0ff00000) >> 4) | + (((u32)vi->vi_txn_addr & 0x000ff000) << 12); + } + DBG_IRT("iosapic_set_irt_data(): 0x%x 0x%x\n", *dp0, *dp1); +} + + +static void +iosapic_disable_irq(void *irq_dev, int irq) +{ + ulong irqflags; + struct vector_info *vi = &(((struct vector_info *) irq_dev)[irq]); + u32 d0, d1; + + ASSERT(NULL != vi); + + IOSAPIC_LOCK(&iosapic_lock); + +#ifdef REVISIT_DESIGN_ISSUE +/* +** XXX/FIXME + +disable_irq()/enable_irq(): drawback of using IRQ as a "handle" + +Current disable_irq interface only allows the irq_region support routines +to manage sharing of "irq" objects. The problem is the disable_irq() +interface specifies which IRQ line needs to be disabled but does not +identify the particular ISR which needs to be disabled. IO sapic +(and similar code in Dino) can only support one handler per IRQ +since they don't further encode the meaning of the IRQ number. +irq_region support has to hide it's implementation of "shared IRQ" +behind a function call. + +Encoding the IRQ would be possible by I/O SAPIC but makes life really +complicated for the IRQ handler and not help performance. + +Need more info on how Linux supports shared IRQ lines on a PC. +*/ +#endif /* REVISIT_DESIGN_ISSUE */ + + iosapic_rd_irt_entry(vi, &d0, &d1); + d0 |= IOSAPIC_IRDT_ENABLE; + iosapic_wr_irt_entry(vi, d0, d1); + + IOSAPIC_UNLOCK(&iosapic_lock); + + /* disable ISR for parent */ + disable_irq(vi->vi_txn_irq); +} + + +static void +iosapic_enable_irq(void *dev, int irq) +{ + struct vector_info *vi = &(((struct vector_info *) dev)[irq]); + u32 d0, d1; + + ASSERT(NULL != vi); + ASSERT(NULL != vi->vi_irte); + + /* data is initialized by fixup_irq */ + ASSERT(0 < vi->vi_txn_irq); + ASSERT(0UL != vi->vi_txn_addr); + ASSERT(0UL != vi->vi_txn_data); + + iosapic_set_irt_data(vi, &d0, &d1); + iosapic_wr_irt_entry(vi, d0, d1); + + +#ifdef DEBUG_IOSAPIC_IRT +{ +u32 *t = (u32 *) ((ulong) vi->vi_eoi_addr & ~0xffUL); +printk("iosapic_enable_irq(): regs %p", vi->vi_eoi_addr); +while (t < vi->vi_eoi_addr) printk(" %x", READ_U32(t++)); +printk("\n"); +} + +printk("iosapic_enable_irq(): sel "); +{ + struct iosapic_info *isp = vi->vi_ios; + + for (d0=0x10; d0<0x1e; d0++) { + /* point the window register to the lower word */ + WRITE_U32(d0, isp->isi_hpa+IOSAPIC_REG_SELECT); + + /* read the word */ + d1 = READ_U32(isp->isi_hpa+IOSAPIC_REG_WINDOW); + printk(" %x", d1); + } +} +printk("\n"); +#endif + + /* + ** KLUGE: IRQ should not be asserted when Drivers enabling their IRQ. + ** PCI supports level triggered in order to share IRQ lines. + ** + ** Issueing I/O SAPIC an EOI causes an interrupt iff IRQ line is + ** asserted. + */ + IOSAPIC_EOI(vi->vi_eoi_addr, vi->vi_eoi_data); +} + + +static void +iosapic_mask_irq(void *dev, int irq) +{ + BUG(); +} + + +static void +iosapic_unmask_irq(void *dev, int irq) +{ + BUG(); +} + + +static struct irq_region_ops iosapic_irq_ops = { + iosapic_disable_irq, + iosapic_enable_irq, + iosapic_mask_irq, + iosapic_unmask_irq +}; + + +/* +** squirrel away the I/O Sapic Version +*/ +static unsigned int +iosapic_rd_version(struct iosapic_info *isi) +{ + ASSERT(isi); + ASSERT(isi->isi_hpa); + + /* point window to the version register */ + WRITE_U32(IOSAPIC_REG_VERSION, isi->isi_hpa+IOSAPIC_REG_SELECT); + + /* now read the version register */ + return (READ_U32(isi->isi_hpa+IOSAPIC_REG_WINDOW)); +} + + +#ifndef IOSAPIC_CALLBACK +/* +** iosapic_register() is the alternative to iosapic_driver_for(). +** (Only one or the other should be implemented.) +*/ + +/* +** iosapic_register() is called by "drivers" with an integrated I/O SAPIC. +** Caller must be certain they have an I/O SAPIC and know it's MMIO address. +** +** o allocate iosapic_info and add it to the list +** o read iosapic version and squirrel that away +** o read size of IRdT. +** o allocate and initialize isi_vector[] +** o allocate isi_region (registers region handlers) +*/ +void * +iosapic_register(void *hpa) +{ + struct iosapic_info *isi = NULL; + struct irt_entry *irte = irt_cell; + struct vector_info *vip; + int cnt; /* track how many entries we've looked at */ + + /* + ** Astro based platforms can't support PCI OLARD if they + ** implement the legacy PDC (not PAT). Though Legacy PDC + ** supports an IRT, LBA's with no device under them + ** are *not* listed in the IRT. + ** Search the IRT and ignore iosapic's which aren't + ** in the IRT. + */ + ASSERT(NULL != irte); /* always have built-in devices */ + for (cnt=0; cnt < irt_num_entry; cnt++, irte++) { + ASSERT(IRT_IOSAPIC_TYPE == irte->entry_type); + /* + ** We need sign extension of the hpa on 32-bit kernels. + ** The address in the IRT is *always* 64 bit and really + ** is an unsigned quantity (like all physical addresses). + */ + if (irte->dest_iosapic_addr == (s64) ((long) hpa)) + break; + } + + if (cnt >= irt_num_entry) + return (NULL); + + if ((isi = IOSAPIC_KALLOC(struct iosapic_info, 1)) == NULL) { + BUG(); + return (NULL); + } + + memset(isi, 0, sizeof(struct iosapic_info)); + + isi->isi_hpa = (unsigned char *) hpa; + isi->isi_version = iosapic_rd_version(isi); + isi->isi_num_vectors = IOSAPIC_IRDT_MAX_ENTRY(isi->isi_version) + 1; + + vip = isi->isi_vector = + IOSAPIC_KALLOC(struct vector_info, isi->isi_num_vectors); + + if (vip == NULL) { + IOSAPIC_FREE(isi, struct iosapic_info, 1); + return (NULL); + } + + memset(vip, 0, sizeof(struct vector_info) * isi->isi_num_vectors); + + /* + ** Initialize vector array + */ + for (cnt=0; cnt < isi->isi_num_vectors; cnt++, vip++) { + vip->vi_irqline = (unsigned char) cnt; + vip->vi_ios = isi; + } + + isi->isi_region = alloc_irq_region(isi->isi_num_vectors, + &iosapic_irq_ops, IRQ_REG_DIS|IRQ_REG_MASK, + "I/O Sapic", (void *) isi->isi_vector); + + ASSERT(NULL != isi->isi_region); + return ((void *) isi); +} +#endif /* !IOSAPIC_CALLBACK */ + + + +#ifdef DEBUG_IOSAPIC + +static void +iosapic_prt_irt(void *irt, long num_entry) +{ + unsigned int i, *irp = (unsigned int *) irt; + + ASSERT(NULL != irt); + + printk(KERN_DEBUG MODULE_NAME ": Interrupt Routing Table (%lx entries)\n", num_entry); + + for (i=0; ivi_irqline, vi); + printk(KERN_DEBUG "\t\tvi_status: %.4x\n", vi->vi_status); + printk(KERN_DEBUG "\t\tvi_txn_irq: %d\n", vi->vi_txn_irq); + printk(KERN_DEBUG "\t\tvi_txn_addr: %lx\n", vi->vi_txn_addr); + printk(KERN_DEBUG "\t\tvi_txn_data: %lx\n", vi->vi_txn_data); + printk(KERN_DEBUG "\t\tvi_eoi_addr: %p\n", vi->vi_eoi_addr); + printk(KERN_DEBUG "\t\tvi_eoi_data: %x\n", vi->vi_eoi_data); +} + + +static void +iosapic_prt_isi(struct iosapic_info *isi) +{ + ASSERT(NULL != isi); + printk(KERN_DEBUG MODULE_NAME ": io_sapic_info at %p\n", isi); + printk(KERN_DEBUG "\t\tisi_hpa: %p\n", isi->isi_hpa); + printk(KERN_DEBUG "\t\tisi_satus: %x\n", isi->isi_status); + printk(KERN_DEBUG "\t\tisi_version: %x\n", isi->isi_version); + printk(KERN_DEBUG "\t\tisi_vector: %p\n", isi->isi_vector); +} +#endif /* DEBUG_IOSAPIC */ diff --git a/arch/parisc/kernel/iosapic_private.h b/arch/parisc/kernel/iosapic_private.h new file mode 100644 index 000000000000..f310501c470a --- /dev/null +++ b/arch/parisc/kernel/iosapic_private.h @@ -0,0 +1,165 @@ +/* +** This file is private to iosapic driver. +** If stuff needs to be used by another driver, move it to a common file. +** +** WARNING: fields most data structures here are ordered to make sure +** they pack nicely for 64-bit compilation. (ie sizeof(long) == 8) +*/ + + +/* +** Interrupt Routing Stuff +** ----------------------- +** The interrupt routing table consists of entries derived from +** MP Specification Draft 1.5. There is one interrupt routing +** table per cell. N- and L-class consist of a single cell. +*/ +struct irt_entry { + + /* Entry Type 139 identifies an I/O SAPIC interrupt entry */ + u8 entry_type; + + /* Entry Length 16 indicates entry is 16 bytes long */ + u8 entry_length; + + /* + ** Interrupt Type of 0 indicates a vectored interrupt, + ** all other values are reserved + */ + u8 interrupt_type; + + /* + ** PO and EL + ** Polarity of SAPIC I/O input signals: + ** 00 = Reserved + ** 01 = Active high + ** 10 = Reserved + ** 11 = Active low + ** Trigger mode of SAPIC I/O input signals: + ** 00 = Reserved + ** 01 = Edge-triggered + ** 10 = Reserved + ** 11 = Level-triggered + */ + u8 polarity_trigger; + + /* + ** IRQ and DEVNO + ** irq identifies PCI interrupt signal where + ** 0x0 corresponds to INT_A#, + ** 0x1 corresponds to INT_B#, + ** 0x2 corresponds to INT_C# + ** 0x3 corresponds to INT_D# + ** PCI device number where interrupt originates + */ + u8 src_bus_irq_devno; + + /* Source Bus ID identifies the bus where interrupt signal comes from */ + u8 src_bus_id; + + /* + ** Segment ID is unique across a protection domain and + ** identifies a segment of PCI buses (reserved in + ** MP Specification Draft 1.5) + */ + u8 src_seg_id; + + /* + ** Destination I/O SAPIC INTIN# identifies the INTIN n pin + ** to which the signal is connected + */ + u8 dest_iosapic_intin; + + /* + ** Destination I/O SAPIC Address identifies the I/O SAPIC + ** to which the signal is connected + */ + u64 dest_iosapic_addr; +}; + +#define IRT_IOSAPIC_TYPE 139 +#define IRT_IOSAPIC_LENGTH 16 + +#define IRT_VECTORED_INTR 0 + +#define IRT_PO_MASK 0x3 +#define IRT_ACTIVE_HI 1 +#define IRT_ACTIVE_LO 3 + +#define IRT_EL_MASK 0x3 +#define IRT_EL_SHIFT 2 +#define IRT_EDGE_TRIG 1 +#define IRT_LEVEL_TRIG 3 + +#define IRT_IRQ_MASK 0x3 +#define IRT_DEV_MASK 0x1f +#define IRT_DEV_SHIFT 2 + +#define IRT_IRQ_DEVNO_MASK ((IRT_DEV_MASK << IRT_DEV_SHIFT) | IRT_IRQ_MASK) + +#ifdef SUPPORT_MULTI_CELL +struct iosapic_irt { + struct iosapic_irt *irt_next; /* next routing table */ + struct irt_entry *irt_base; /* intr routing table address */ + size_t irte_count; /* number of entries in the table */ + size_t irte_size; /* size (bytes) of each entry */ +}; +#endif + +struct vector_info { + struct iosapic_info *vi_ios; /* I/O SAPIC this vector is on */ + struct irt_entry *vi_irte; /* IRT entry */ + u32 *vi_eoi_addr; /* precalculate EOI reg address */ + u32 vi_eoi_data; /* IA64: ? PA: swapped txn_data */ + u8 vi_status; /* status/flags */ + u8 vi_irqline; /* INTINn(IRQ) */ + int vi_txn_irq; /* virtual IRQ number for processor */ + ulong vi_txn_addr; /* IA64: id_eid PA: partial HPA */ + ulong vi_txn_data; /* IA64: vector PA: EIR bit */ +}; + + +struct iosapic_info { + struct iosapic_info *isi_next; /* list of I/O SAPIC */ + volatile void *isi_hpa; /* physical base address */ + struct irq_region *isi_region; /* each I/O SAPIC is one region */ + struct vector_info *isi_vector; /* IRdT (IRQ line) array */ + int isi_num_vectors; /* size of IRdT array */ + int isi_status; /* status/flags */ + unsigned int isi_version; /* DEBUG: data fr version reg */ +}; + + + +#ifdef __IA64__ +/* +** PA risc does NOT have any local sapics. IA64 does. +** PIB (Processor Interrupt Block) is handled by Astro or Dew (Stretch CEC). +** +** PA: Get id_eid from IRT and hardcode PIB to 0xfeeNNNN0 +** Emulate the data on PAT platforms. +*/ +struct local_sapic_info { + struct local_sapic_info *lsi_next; /* point to next CPU info */ + int *lsi_cpu_id; /* point to logical CPU id */ + unsigned long *lsi_id_eid; /* point to IA-64 CPU id */ + int *lsi_status; /* point to CPU status */ + void *lsi_private; /* point to special info */ +}; + +/* +** "root" data structure which ties everything together. +** Should always be able to start with sapic_root and locate +** the desired information. +*/ +struct sapic_info { + struct sapic_info *si_next; /* info is per cell */ + int si_cellid; /* cell id */ + unsigned int si_status; /* status */ + char *si_pib_base; /* intr blk base address */ + local_sapic_info_t *si_local_info; + io_sapic_info_t *si_io_info; + extint_info_t *si_extint_info;/* External Intr info */ +}; +#endif + diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c new file mode 100644 index 000000000000..0fdf3b08377f --- /dev/null +++ b/arch/parisc/kernel/irq.c @@ -0,0 +1,537 @@ +/* $Id: irq.c,v 1.8 2000/02/08 02:01:17 grundler Exp $ + * + * Code to handle x86 style IRQs plus some generic interrupt stuff. + * + * This is not in any way SMP-clean. + * + * Copyright (C) 1992 Linus Torvalds + * Copyright (C) 1994, 1995, 1996, 1997, 1998 Ralf Baechle + * Copyright (C) 1999 SuSE GmbH (Author: Philipp Rumpf, prumpf@tux.org) + * Copyright (C) 2000 Hewlett Packard Corp (Co-Author: Grant Grundler, grundler@cup.hp.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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#undef DEBUG_IRQ + +extern void timer_interrupt(int, void *, struct pt_regs *); +extern void ipi_interrupt(int, void *, struct pt_regs *); + +#ifdef DEBUG_IRQ +#define DBG_IRQ(x...) printk(x) +#else /* DEBUG_IRQ */ +#define DBG_IRQ(x...) +#endif /* DEBUG_IRQ */ + +#define EIEM_MASK(irq) (1L<<(MAX_CPU_IRQ-IRQ_OFFSET(irq))) +#define CLEAR_EIEM_BIT(irq) set_eiem(get_eiem() & ~EIEM_MASK(irq)) +#define SET_EIEM_BIT(irq) set_eiem(get_eiem() | EIEM_MASK(irq)) + +static void disable_cpu_irq(void *unused, int irq) +{ + CLEAR_EIEM_BIT(irq); +} + +static void enable_cpu_irq(void *unused, int irq) +{ + unsigned long mask = EIEM_MASK(irq); + + mtctl(mask, 23); + SET_EIEM_BIT(irq); +} + +static struct irqaction cpu_irq_actions[IRQ_PER_REGION] = { + [IRQ_OFFSET(TIMER_IRQ)] { timer_interrupt, 0, 0, "timer", NULL, NULL }, + [IRQ_OFFSET(IPI_IRQ)] { ipi_interrupt, 0, 0, "IPI", NULL, NULL }, +}; + +struct irq_region cpu_irq_region = { + { disable_cpu_irq, enable_cpu_irq, NULL, NULL }, + { &cpu_data[0], "PA-PIC", IRQ_REG_MASK|IRQ_REG_DIS, IRQ_FROM_REGION(CPU_IRQ_REGION)}, + cpu_irq_actions +}; + +struct irq_region *irq_region[NR_IRQ_REGS] = { + [ 0 ] NULL, /* abuse will data page fault (aka code 15) */ + [ CPU_IRQ_REGION ] &cpu_irq_region, +}; + + + +/* we special-case the real IRQs here, which feels right given the relatively + * high cost of indirect calls. If anyone is bored enough to benchmark this + * and find out whether I am right, feel free to. prumpf */ + +static inline void mask_irq(int irq) +{ + struct irq_region *region; + +#ifdef DEBUG_IRQ + if (irq != TIMER_IRQ) +#endif + DBG_IRQ("mask_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)); + + if(IRQ_REGION(irq) != CPU_IRQ_REGION) { + region = irq_region[IRQ_REGION(irq)]; + if(region->data.flags & IRQ_REG_MASK) + region->ops.mask_irq(region->data.dev, IRQ_OFFSET(irq)); + } else { + CLEAR_EIEM_BIT(irq); + } +} + +static inline void unmask_irq(int irq) +{ + struct irq_region *region; + +#ifdef DEBUG_IRQ + if (irq != TIMER_IRQ) +#endif + DBG_IRQ("unmask_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)); + + if(IRQ_REGION(irq) != CPU_IRQ_REGION) { + region = irq_region[IRQ_REGION(irq)]; + if(region->data.flags & IRQ_REG_MASK) + region->ops.unmask_irq(region->data.dev, IRQ_OFFSET(irq)); + } else { + SET_EIEM_BIT(irq); + } +} + +void disable_irq(int irq) +{ + struct irq_region *region; + +#ifdef DEBUG_IRQ + if (irq != TIMER_IRQ) +#endif + DBG_IRQ("disable_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)); + region = irq_region[IRQ_REGION(irq)]; + + if(region->data.flags & IRQ_REG_DIS) + region->ops.disable_irq(region->data.dev, IRQ_OFFSET(irq)); + else + BUG(); +} + +void enable_irq(int irq) +{ + struct irq_region *region; + +#ifdef DEBUG_IRQ + if (irq != TIMER_IRQ) +#endif + DBG_IRQ("enable_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)); + region = irq_region[IRQ_REGION(irq)]; + + if(region->data.flags & IRQ_REG_DIS) + region->ops.enable_irq(region->data.dev, IRQ_OFFSET(irq)); + else + BUG(); +} + +int get_irq_list(char *buf) +{ +#ifdef CONFIG_PROC_FS + char *p = buf; + int i, j; + int regnr, irq_no; + struct irq_region *region; + struct irqaction *action, *mainaction; + + p += sprintf(p, " "); + for (j=0; jaction) + continue; + + mainaction = region->action; + + for (i = 0; i <= MAX_CPU_IRQ; i++) { + action = mainaction++; + if (!action || !action->name) + continue; + + irq_no = IRQ_FROM_REGION(regnr) + i; + + p += sprintf(p, "%3d: ", irq_no); +#ifndef CONFIG_SMP + p += sprintf(p, "%10u ", kstat_irqs(irq_no)); +#else + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + kstat.irqs[cpu_logical_map(j)][irq_no]); +#endif + p += sprintf(p, " %14s", + region->data.name ? region->data.name : "N/A"); + p += sprintf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + p += sprintf(p, ", %s", action->name); + *p++ = '\n'; + } + } + + p += sprintf(p, "\n"); +#if CONFIG_SMP + p += sprintf(p, "LOC: "); + for (j = 0; j < smp_num_cpus; j++) + p += sprintf(p, "%10u ", + apic_timer_irqs[cpu_logical_map(j)]); + p += sprintf(p, "\n"); +#endif + + return p - buf; + +#else /* CONFIG_PROC_FS */ + + return 0; + +#endif /* CONFIG_PROC_FS */ +} + + + +/* +** The following form a "set": Virtual IRQ, Transaction Address, Trans Data. +** Respectively, these map to IRQ region+EIRR, Processor HPA, EIRR bit. +** +** To use txn_XXX() interfaces, get a Virtual IRQ first. +** Then use that to get the Transaction address and data. +*/ + +int +txn_alloc_irq(void) +{ + int irq; + + /* never return irq 0 cause that's the interval timer */ + for(irq=1; irq<=MAX_CPU_IRQ; irq++) { + if(cpu_irq_region.action[irq].handler == NULL) { + return (IRQ_FROM_REGION(CPU_IRQ_REGION) + irq); + } + } + + /* unlikely, but be prepared */ + return -1; +} + +int +txn_claim_irq(int irq) +{ + if (irq_region[IRQ_REGION(irq)]->action[IRQ_OFFSET(irq)].handler ==NULL) + { + return irq; + } + + /* unlikely, but be prepared */ + return -1; +} + +unsigned long +txn_alloc_addr(int virt_irq) +{ + struct cpuinfo_parisc *dev = (struct cpuinfo_parisc *) (irq_region[IRQ_REGION(virt_irq)]->data.dev); + + if (0==dev) { + printk(KERN_ERR "txn_alloc_addr(0x%x): CPU IRQ region? dev %p\n", + virt_irq,dev); + return(0UL); + } + return (dev->txn_addr); +} + + +/* +** The alloc process needs to accept a parameter to accomodate limitations +** of the HW/SW which use these bits: +** Legacy PA I/O (GSC/NIO): 5 bits (architected EIM register) +** V-class (EPIC): 6 bits +** N/L-class/A500: 8 bits (iosapic) +** PCI 2.2 MSI: 16 bits (I think) +** Existing PCI devices: 32-bits (NCR c720/ATM/GigE/HyperFabric) +** +** On the service provider side: +** o PA 1.1 (and PA2.0 narrow mode) 5-bits (width of EIR register) +** o PA 2.0 wide mode 6-bits (per processor) +** o IA64 8-bits (0-256 total) +** +** So a Legacy PA I/O device on a PA 2.0 box can't use all +** the bits supported by the processor...and the N/L-class +** I/O subsystem supports more bits than PA2.0 has. The first +** case is the problem. +*/ +unsigned int +txn_alloc_data(int virt_irq, unsigned int bits_wide) +{ + /* XXX FIXME : bits_wide indicates how wide the transaction + ** data is allowed to be...we may need a different virt_irq + ** if this one won't work. Another reason to index virtual + ** irq's into a table which can manage CPU/IRQ bit seperately. + */ + if (IRQ_OFFSET(virt_irq) > (1 << (bits_wide -1))) + { + panic("Sorry -- didn't allocate valid IRQ for this device\n"); + } + + return(IRQ_OFFSET(virt_irq)); +} + + +/* FIXME: SMP, flags, bottom halves, rest */ +void do_irq(struct irqaction *action, int irq, struct pt_regs * regs) +{ + int cpu = smp_processor_id(); + + irq_enter(cpu, irq); + +#ifdef DEBUG_IRQ + if (irq != TIMER_IRQ) +#endif + DBG_IRQ("do_irq(%d) %d+%d\n", irq, IRQ_REGION(irq), IRQ_OFFSET(irq)); + if (action->handler == NULL) + printk(KERN_ERR "No handler for interrupt %d !\n", irq); + + for(; action && action->handler; action = action->next) { + action->handler(irq, action->dev_id, regs); + } + + irq_exit(cpu, irq); + + /* don't need to care about unmasking and stuff */ + do_softirq(); +} + +void do_irq_mask(unsigned long mask, struct irq_region *region, struct pt_regs *regs) +{ + unsigned long bit; + int irq; + int cpu = smp_processor_id(); + +#ifdef DEBUG_IRQ + if (mask != (1L << MAX_CPU_IRQ)) + printk("do_irq_mask %08lx %p %p\n", mask, region, regs); +#endif + + for(bit=(1L<>=1, irq++) { + int irq_num; + if(!(bit&mask)) + continue; + + irq_num = region->data.irqbase + irq; + + ++kstat.irqs[cpu][IRQ_FROM_REGION(CPU_IRQ_REGION) | irq]; + if (IRQ_REGION(irq_num) != CPU_IRQ_REGION) + ++kstat.irqs[cpu][irq_num]; + + mask_irq(irq_num); + do_irq(®ion->action[irq], irq_num, regs); + unmask_irq(irq_num); + } +} + +static inline int alloc_irqregion(void) +{ + int irqreg; + + for(irqreg=1; irqreg<=(NR_IRQ_REGS); irqreg++) { + if(irq_region[irqreg] == NULL) + return irqreg; + } + + return 0; +} + +struct irq_region *alloc_irq_region( + int count, struct irq_region_ops *ops, unsigned long flags, + const char *name, void *dev) +{ + struct irq_region *region; + int index; + + index = alloc_irqregion(); + + if((IRQ_REGION(count-1))) + return NULL; + + if (count < IRQ_PER_REGION) { + DBG_IRQ("alloc_irq_region() using minimum of %d irq lines for %s (%d)\n", + IRQ_PER_REGION, name, count); + count = IRQ_PER_REGION; + } + + if(flags & IRQ_REG_MASK) + if(!(ops->mask_irq && ops->unmask_irq)) + return NULL; + + if(flags & IRQ_REG_DIS) + if(!(ops->disable_irq && ops->enable_irq)) + return NULL; + + if((irq_region[index])) + return NULL; + + region = kmalloc(sizeof *region, GFP_ATOMIC); + if(!region) + return NULL; + + region->action = kmalloc(sizeof *region->action * count, GFP_ATOMIC); + if(!region->action) { + kfree(region); + return NULL; + } + memset(region->action, 0, sizeof *region->action * count); + + region->ops = *ops; + region->data.irqbase = IRQ_FROM_REGION(index); + region->data.flags = flags; + region->data.name = name; + region->data.dev = dev; + + irq_region[index] = region; + + return irq_region[index]; +} + + + +/* FIXME: SMP, flags, bottom halves, rest */ + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + struct irqaction * action; + +#if 0 + printk(KERN_INFO "request_irq(%d, %p, 0x%lx, %s, %p)\n",irq, handler, irqflags, devname, dev_id); +#endif + if(!handler) { + printk(KERN_ERR "request_irq(%d,...): Augh! No handler for irq!\n", + irq); + return -EINVAL; + } + + if ((IRQ_REGION(irq) == 0) || irq_region[IRQ_REGION(irq)] == NULL) { + /* + ** Bug catcher for drivers which use "char" or u8 for + ** the IRQ number. They lose the region number which + ** is in pcidev->irq (an int). + */ + printk(KERN_ERR "%p (%s?) called request_irq with an invalid irq %d\n", + __builtin_return_address(0), devname, irq); + return -EINVAL; + } + + action = &irq_region[IRQ_REGION(irq)]->action[IRQ_OFFSET(irq)]; + + if(action->handler) { + while(action->next) + action = action->next; + + action->next = kmalloc(sizeof *action, GFP_ATOMIC); + + action = action->next; + } + + if(!action) { + printk(KERN_ERR "request_irq():Augh! No action!\n") ; + return -ENOMEM; + } + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + enable_irq(irq); + return 0; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + struct irqaction *action, **p; + + action = &irq_region[IRQ_REGION(irq)]->action[IRQ_OFFSET(irq)]; + + if(action->dev_id == dev_id) { + if(action->next == NULL) + action->handler = NULL; + else + memcpy(action, action->next, sizeof *action); + + return; + } + + p = &action->next; + action = action->next; + + for (; (action = *p) != NULL; p = &action->next) { + if (action->dev_id != dev_id) + continue; + + /* Found it - now free it */ + *p = action->next; + kfree(action); + + return; + } + + printk(KERN_ERR "Trying to free free IRQ%d\n",irq); +} + +unsigned long probe_irq_on (void) +{ + return 0; +} + +int probe_irq_off (unsigned long irqs) +{ + return 0; +} + + +void __init init_IRQ(void) +{ +} + +void init_irq_proc(void) +{ +} diff --git a/arch/parisc/kernel/keyboard.c b/arch/parisc/kernel/keyboard.c new file mode 100644 index 000000000000..05044db496bf --- /dev/null +++ b/arch/parisc/kernel/keyboard.c @@ -0,0 +1,82 @@ +/* + * linux/arch/parisc/kernel/keyboard.c + * + * Alex deVries + * Copyright (1999) The Puffin Group + * Mostly rewritten by Philipp Rumpf + * Copyright 2000 Philipp Rumpf + */ + +#include +#include + +static int def_setkeycode(unsigned int x, unsigned int y) +{ + return 0; +} + +static int def_getkeycode(unsigned int x) +{ + return 0; +} + +static int def_translate(unsigned char scancode, unsigned char *keycode, + char raw) +{ + *keycode = scancode; + + return 1; +} + +static char def_unexpected_up(unsigned char c) +{ + return 128; +} + +static void def_leds(unsigned char leds) +{ +} + +static void def_init_hw(void) +{ +} + +static char def_sysrq_xlate[NR_KEYS]; + +static struct kbd_ops def_kbd_ops = { + setkeycode: def_setkeycode, + getkeycode: def_getkeycode, + translate: def_translate, + unexpected_up: def_unexpected_up, + leds: def_leds, + init_hw: def_init_hw, + + sysrq_key: 0xff, + sysrq_xlate: def_sysrq_xlate, +}; + +struct kbd_ops *kbd_ops = &def_kbd_ops; + +void register_kbd_ops(struct kbd_ops *ops) +{ + if(ops->setkeycode) + kbd_ops->setkeycode = ops->setkeycode; + + if(ops->getkeycode) + kbd_ops->getkeycode = ops->getkeycode; + + if(ops->translate) + kbd_ops->translate = ops->translate; + + if(ops->unexpected_up) + kbd_ops->unexpected_up = ops->unexpected_up; + + if(ops->leds) + kbd_ops->leds = ops->leds; + + if(ops->init_hw) + kbd_ops->init_hw = ops->init_hw; + + kbd_ops->sysrq_key = ops->sysrq_key; + kbd_ops->sysrq_xlate = ops->sysrq_xlate; +} diff --git a/arch/parisc/kernel/lasimap.map b/arch/parisc/kernel/lasimap.map new file mode 100644 index 000000000000..2a9ee223c054 --- /dev/null +++ b/arch/parisc/kernel/lasimap.map @@ -0,0 +1,322 @@ +# HP 712 kernel keymap. This uses 7 modifier combinations. + +keymaps 0-2,4-5,8,12 +# ie, plain, Shift, AltGr, Control, Control+Shift, Alt and Control+Alt + + +# Change the above line into +# keymaps 0-2,4-6,8,12 +# in case you want the entries +# altgr control keycode 83 = Boot +# altgr control keycode 111 = Boot +# below. +# +# In fact AltGr is used very little, and one more keymap can +# be saved by mapping AltGr to Alt (and adapting a few entries): +# keycode 100 = Alt +# +keycode 1 = F9 F19 Console_21 + control keycode 1 = F9 + alt keycode 1 = Console_9 + control alt keycode 1 = Console_9 +keycode 2 = +keycode 3 = F5 F15 Console_17 + control keycode 3 = F5 + alt keycode 3 = Console_5 + control alt keycode 3 = Console_5 +keycode 4 = F3 F13 Console_15 + control keycode 4 = F3 + alt keycode 4 = Console_3 + control alt keycode 4 = Console_3 +keycode 5 = F1 F11 Console_13 + control keycode 5 = F1 + alt keycode 5 = Console_1 + control alt keycode 5 = Console_1 +keycode 6 = F2 F12 Console_14 + control keycode 6 = F2 + alt keycode 6 = Console_2 + control alt keycode 6 = Console_2 +keycode 7 = F12 F12 Console_24 + control keycode 7 = F12 + alt keycode 7 = Console_12 + control alt keycode 7 = Console_12 +keycode 8 = +keycode 9 = F10 F20 Console_22 + control keycode 9 = F10 + alt keycode 9 = Console_10 + control alt keycode 9 = Console_10 +keycode 10 = F8 F18 Console_20 + control keycode 10 = F8 + alt keycode 10 = Console_8 + control alt keycode 10 = Console_8 +keycode 11 = F6 F16 Console_18 + control keycode 11 = F6 + alt keycode 11 = Console_6 + control alt keycode 11 = Console_6 +keycode 12 = F4 F14 Console_16 + control keycode 12 = F4 + alt keycode 12 = Console_4 + control alt keycode 12 = Console_4 +keycode 13 = Tab Tab + alt keycode 13 = Meta_Tab +keycode 14 = grave asciitilde + control keycode 14 = nul + alt keycode 14 = Meta_grave +keycode 15 = +keycode 16 = +keycode 17 = Alt +keycode 18 = Shift +keycode 19 = +keycode 20 = Control +keycode 21 = q +keycode 22 = one exclam exclam +keycode 23 = +keycode 24 = +keycode 25 = +keycode 26 = z +keycode 27 = s +keycode 28 = a + altgr keycode 28 = Hex_A +keycode 29 = w +keycode 30 = two at at +keycode 31 = +keycode 32 = +keycode 33 = c + altgr keycode 46 = Hex_C +keycode 34 = x +keycode 35 = d + altgr keycode 35 = Hex_D +keycode 36 = e + altgr keycode 36 = Hex_E +keycode 37 = four dollar +keycode 38 = three numbersign +keycode 39 = +keycode 40 = +keycode 41 = +keycode 42 = v +keycode 43 = f + altgr keycode 43 = Hex_F +keycode 44 = t +keycode 45 = r +keycode 46 = five percent +keycode 47 = +keycode 48 = +keycode 49 = n +keycode 50 = b + altgr keycode 50 = Hex_B +keycode 51 = h +keycode 52 = g +keycode 53 = y +keycode 54 = six asciicircum +keycode 55 = +keycode 56 = +keycode 57 = +keycode 58 = m +keycode 59 = j +keycode 60 = u +keycode 61 = seven ampersand +keycode 62 = eight asterisk asterisk +keycode 63 = +keycode 64 = +keycode 65 = comma less + alt keycode 65 = Meta_comma +keycode 66 = k +keycode 67 = i +keycode 68 = o +keycode 69 = zero parenright bracketright +keycode 70 = nine parenleft bracketleft +keycode 71 = +keycode 72 = +keycode 73 = period greater + control keycode 73 = Compose + alt keycode 73 = Meta_period +keycode 74 = slash question + control keycode 74 = Delete + alt keycode 53 = Meta_slash +keycode 75 = l +keycode 76 = semicolon colon + alt keycode 39 = Meta_semicolon +keycode 77 = p +keycode 78 = minus underscore +keycode 79 = +keycode 80 = +keycode 81 = +keycode 82 = apostrophe quotedbl + control keycode 82 = Control_g + alt keycode 40 = Meta_apostrophe +keycode 83 = +keycode 84 = bracketleft braceleft + control keycode 84 = Escape + alt keycode 26 = Meta_bracketleft +keycode 85 = equal plus +keycode 86 = +keycode 87 = +keycode 88 = Caps_Lock +keycode 88 = +keycode 89 = +keycode 89 = +keycode 89 = +keycode 90 = Return + alt keycode 90 = Meta_Control_m +keycode 91 = bracketright braceright asciitilde + control keycode 91 = Control_bracketright + alt keycode 91 = Meta_bracketright +keycode 92 = +keycode 93 = backslash bar + control keycode 43 = Control_backslash + alt keycode 43 = Meta_backslash +keycode 94 = +keycode 95 = +keycode 96 = +keycode 97 = +keycode 98 = +keycode 99 = +keycode 100 = +keycode 101 = +keycode 102 = BackSpace +keycode 103 = +keycode 104 = +keycode 105 = KP_1 + alt keycode 105 = Ascii_1 + altgr keycode 105 = Hex_1 +keycode 106 = +keycode 107 = KP_4 + alt keycode 107 = Ascii_4 + altgr keycode 107 = Hex_4 +keycode 108 = KP_7 + alt keycode 108 = Ascii_7 + altgr keycode 108 = Hex_7 +keycode 109 = +keycode 110 = +keycode 111 = +keycode 112 = KP_0 + alt keycode 82 = Ascii_0 + altgr keycode 82 = Hex_0 +keycode 113 = KP_Period +keycode 114 = KP_2 + alt keycode 114 = Ascii_2 + altgr keycode 114 = Hex_2 +keycode 115 = KP_5 + alt keycode 115 = Ascii_5 + altgr keycode 115 = Hex_5 +keycode 116 = KP_6 + alt keycode 116 = Ascii_6 + altgr keycode 116 = Hex_6 +keycode 117 = KP_8 + alt keycode 117 = Ascii_8 + altgr keycode 117 = Hex_8 +keycode 118 = Escape +keycode 119 = +keycode 120 = F11 +keycode 121 = KP_Add +keycode 122 = KP_3 + alt keycode 122 = Ascii_3 + altgr keycode 122 = Hex_3 +keycode 123 = KP_Subtract +keycode 124 = KP_Multiply +keycode 125 = KP_9 + alt keycode 125 = Ascii_9 + altgr keycode 125 = Hex_9 +keycode 126 = +# 131!! +keycode 127 = F7 F17 Console_19 + control keycode 127 = F7 + alt keycode 127 = Console_7 + control alt keycode 127 = Console_7 + +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +string Find = "\033[1~" +string Insert = "\033[2~" +string Remove = "\033[3~" +string Select = "\033[4~" +string Prior = "\033[5~" +string Next = "\033[6~" +string Macro = "\033[M" +string Pause = "\033[P" +compose '`' 'A' to 'À' +compose '`' 'a' to 'à' +compose '\'' 'A' to 'Á' +compose '\'' 'a' to 'á' +compose '^' 'A' to 'Â' +compose '^' 'a' to 'â' +compose '~' 'A' to 'Ã' +compose '~' 'a' to 'ã' +compose '"' 'A' to 'Ä' +compose '"' 'a' to 'ä' +compose 'O' 'A' to 'Å' +compose 'o' 'a' to 'å' +compose '0' 'A' to 'Å' +compose '0' 'a' to 'å' +compose 'A' 'A' to 'Å' +compose 'a' 'a' to 'å' +compose 'A' 'E' to 'Æ' +compose 'a' 'e' to 'æ' +compose ',' 'C' to 'Ç' +compose ',' 'c' to 'ç' +compose '`' 'E' to 'È' +compose '`' 'e' to 'è' +compose '\'' 'E' to 'É' +compose '\'' 'e' to 'é' +compose '^' 'E' to 'Ê' +compose '^' 'e' to 'ê' +compose '"' 'E' to 'Ë' +compose '"' 'e' to 'ë' +compose '`' 'I' to 'Ì' +compose '`' 'i' to 'ì' +compose '\'' 'I' to 'Í' +compose '\'' 'i' to 'í' +compose '^' 'I' to 'Î' +compose '^' 'i' to 'î' +compose '"' 'I' to 'Ï' +compose '"' 'i' to 'ï' +compose '-' 'D' to 'Ð' +compose '-' 'd' to 'ð' +compose '~' 'N' to 'Ñ' +compose '~' 'n' to 'ñ' +compose '`' 'O' to 'Ò' +compose '`' 'o' to 'ò' +compose '\'' 'O' to 'Ó' +compose '\'' 'o' to 'ó' +compose '^' 'O' to 'Ô' +compose '^' 'o' to 'ô' +compose '~' 'O' to 'Õ' +compose '~' 'o' to 'õ' +compose '"' 'O' to 'Ö' +compose '"' 'o' to 'ö' +compose '/' 'O' to 'Ø' +compose '/' 'o' to 'ø' +compose '`' 'U' to 'Ù' +compose '`' 'u' to 'ù' +compose '\'' 'U' to 'Ú' +compose '\'' 'u' to 'ú' +compose '^' 'U' to 'Û' +compose '^' 'u' to 'û' +compose '"' 'U' to 'Ü' +compose '"' 'u' to 'ü' +compose '\'' 'Y' to 'Ý' +compose '\'' 'y' to 'ý' +compose 'T' 'H' to 'Þ' +compose 't' 'h' to 'þ' +compose 's' 's' to 'ß' +compose '"' 'y' to 'ÿ' +compose 's' 'z' to 'ß' +compose 'i' 'j' to 'ÿ' diff --git a/arch/parisc/kernel/lba_pci.c b/arch/parisc/kernel/lba_pci.c new file mode 100644 index 000000000000..b81e6b2e2a1b --- /dev/null +++ b/arch/parisc/kernel/lba_pci.c @@ -0,0 +1,1347 @@ +/* +** PCI Lower Bus Adapter (LBA) manager +** +** (c) Copyright 1999,2000 Grant Grundler +** (c) Copyright 1999,2000 Hewlett-Packard Company +** +** 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 module primarily provides access to PCI bus (config/IOport +** spaces) on platforms with an SBA/LBA chipset. A/B/C/J/L/N-class +** with 4 digit model numbers - eg C3000 (and A400...sigh). +** +** LBA driver isn't as simple as the Dino driver because: +** (a) this chip has substantial bug fixes between revisions +** (Only one Dino bug has a software workaround :^( ) +** (b) has more options which we don't (yet) support (DMA hints, OLARD) +** (c) IRQ support lives in the I/O SAPIC driver (not with PCI driver) +** (d) play nicely with both PAT and "Legacy" PA-RISC firmware (PDC). +** (dino only deals with "Legacy" PDC) +** +** LBA driver passes the I/O SAPIC HPA to the I/O SAPIC driver. +** (I/O SAPIC is integratd in the LBA chip). +** +** FIXME: Add support to SBA and LBA drivers for DMA hint sets +** FIXME: Add support for PCI card hot-plug (OLARD). +*/ + +#include +#include +#include +#include +#include +#include /* for __init and __devinit */ +#include +#include +#include +#include + +#include +#include /* for struct irq_region support */ +#include +#include +#include +#include +#include + +#include /* for register_driver() stuff */ +#include /* for iosapic_register() */ +#include /* gsc_read/write stuff */ + + +#ifndef TRUE +#define TRUE (1 == 1) +#define FALSE (1 == 0) +#endif + +#undef DEBUG_LBA /* general stuff */ +#undef DEBUG_LBA_PORT /* debug I/O Port access */ +#undef DEBUG_LBA_CFG /* debug Config Space Access (ie PCI Bus walk) */ +#undef DEBUG_LBA_PAT /* debug PCI Resource Mgt code - PDC PAT only */ + +#ifdef DEBUG_LBA +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#ifdef DEBUG_LBA_PORT +#define DBG_PORT(x...) printk(x) +#else +#define DBG_PORT(x...) +#endif + +#ifdef DEBUG_LBA_CFG +#define DBG_CFG(x...) printk(x) +#else +#define DBG_CFG(x...) +#endif + +#ifdef DEBUG_LBA_PAT +#define DBG_PAT(x...) printk(x) +#else +#define DBG_PAT(x...) +#endif + +/* +** Config accessor functions only pass in the 8-bit bus number and not +** the 8-bit "PCI Segment" number. Each LBA will be assigned a PCI bus +** number based on what firmware wrote into the scratch register. +** +** The "secondary" bus number is set to this before calling +** pci_register_ops(). If any PPB's are present, the scan will +** discover them and update the "secondary" and "subordinate" +** fields in the pci_bus structure. +** +** Changes in the configuration *may* result in a different +** bus number for each LBA depending on what firmware does. +*/ + +#define MODULE_NAME "lba" + +static int lba_driver_callback(struct hp_device *, struct pa_iodc_driver *); + + +static struct pa_iodc_driver lba_drivers_for[]= { + + {HPHW_BRIDGE, 0x782, 0x0, 0xa, 0,0, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "tbd", (void *) lba_driver_callback}, + + {0,0,0,0,0,0, + 0, + (char *) NULL, (char *) NULL, (void *) NULL} +}; + + +#define LBA_FUNC_ID 0x0000 /* function id */ +#define LBA_FCLASS 0x0008 /* function class, bist, header, rev... */ +#define LBA_CAPABLE 0x0030 /* capabilities register */ + +#define LBA_PCI_CFG_ADDR 0x0040 /* poke CFG address here */ +#define LBA_PCI_CFG_DATA 0x0048 /* read or write data here */ + +#define LBA_PMC_MTLT 0x0050 /* Firmware sets this - read only. */ +#define LBA_FW_SCRATCH 0x0058 /* Firmware writes the PCI bus number here. */ +#define LBA_ERROR_ADDR 0x0070 /* On error, address gets logged here */ + +#define LBA_ARB_MASK 0x0080 /* bit 0 enable arbitration. PAT/PDC enables */ +#define LBA_ARB_PRI 0x0088 /* firmware sets this. */ +#define LBA_ARB_MODE 0x0090 /* firmware sets this. */ +#define LBA_ARB_MTLT 0x0098 /* firmware sets this. */ + +#define LBA_MOD_ID 0x0100 /* Module ID. PDC_PAT_CELL reports 4 */ + +#define LBA_STAT_CTL 0x0108 /* Status & Control */ +#define HF_ENABLE 0x40 /* enable HF mode (default is -1 mode) */ + +#define LBA_LMMIO_BASE 0x0200 /* < 4GB I/O address range */ +#define LBA_LMMIO_MASK 0x0208 + +#define LBA_GMMIO_BASE 0x0210 /* > 4GB I/O address range */ +#define LBA_GMMIO_MASK 0x0218 + +#define LBA_WLMMIO_BASE 0x0220 /* All < 4GB ranges under the same *SBA* */ +#define LBA_WLMMIO_MASK 0x0228 + +#define LBA_WGMMIO_BASE 0x0230 /* All > 4GB ranges under the same *SBA* */ +#define LBA_WGMMIO_MASK 0x0238 + +#define LBA_IOS_BASE 0x0240 /* I/O port space for this LBA */ +#define LBA_IOS_MASK 0x0248 + +#define LBA_ELMMIO_BASE 0x0250 /* Extra LMMIO range */ +#define LBA_ELMMIO_MASK 0x0258 + +#define LBA_EIOS_BASE 0x0260 /* Extra I/O port space */ +#define LBA_EIOS_MASK 0x0268 + +#define LBA_DMA_CTL 0x0278 /* firmware sets this */ + +/* RESET: ignore DMA stuff until we can measure performance */ +#define LBA_IBASE 0x0300 /* DMA support */ +#define LBA_IMASK 0x0308 +#define LBA_HINT_CFG 0x0310 +#define LBA_HINT_BASE 0x0380 /* 14 registers at every 8 bytes. */ + +/* ERROR regs are needed for config cycle kluges */ +#define LBA_ERROR_CONFIG 0x0680 +#define LBA_ERROR_STATUS 0x0688 + +#define LBA_IOSAPIC_BASE 0x800 /* Offset of IRQ logic */ + +/* non-postable I/O port space, densely packed */ +#ifdef __LP64__ +#define LBA_ASTRO_PORT_BASE (0xfffffffffee00000UL) +#else +#define LBA_ASTRO_PORT_BASE (0xfee00000UL) +#endif + + +/* +** lba_device: Per instance Elroy data structure +*/ +struct lba_device { + struct pci_hba_data hba; + + spinlock_t lba_lock; + void *iosapic_obj; + +#ifdef __LP64__ + unsigned long lmmio_base; /* PA_VIEW - fixup MEM addresses */ + unsigned long gmmio_base; /* PA_VIEW - Not used (yet) */ + unsigned long iop_base; /* PA_VIEW - for IO port accessor funcs */ +#endif + + int flags; /* state/functionality enabled */ + int hw_rev; /* HW revision of chip */ +}; + + +static u32 lba_t32; + +/* +** lba "flags" +*/ +#define LBA_FLAG_NO_DMA_DURING_CFG 0x01 +#define LBA_FLAG_SKIP_PROBE 0x10 + +/* Tape Release 4 == hw_rev 5 */ +#define LBA_TR4PLUS(d) ((d)->hw_rev > 0x4) +#define LBA_DMA_DURING_CFG_DISABLED(d) ((d)->flags & LBA_FLAG_NO_DMA_DURING_CFG) +#define LBA_SKIP_PROBE(d) ((d)->flags & LBA_FLAG_SKIP_PROBE) + + +/* Looks nice and keeps the compiler happy */ +#define LBA_DEV(d) ((struct lba_device *) (d)) + + +/* +** Only allow 8 subsidiary busses per LBA +** Problem is the PCI bus numbering is globally shared. +*/ +#define LBA_MAX_NUM_BUSES 8 + +/************************************ + * LBA register read and write support + * + * BE WARNED: register writes are posted. + * (ie follow writes which must reach HW with a read) + */ +#define READ_U8(addr) gsc_readb(addr) +#define READ_U16(addr) gsc_readw((u16 *) (addr)) +#define READ_U32(addr) gsc_readl((u32 *) (addr)) +#define WRITE_U8(value, addr) gsc_writeb(value, addr) +#define WRITE_U16(value, addr) gsc_writew(value, (u16 *) (addr)) +#define WRITE_U32(value, addr) gsc_writel(value, (u32 *) (addr)) + +#define READ_REG8(addr) gsc_readb(addr) +#define READ_REG16(addr) le16_to_cpu(gsc_readw((u16 *) (addr))) +#define READ_REG32(addr) le32_to_cpu(gsc_readl((u32 *) (addr))) +#define WRITE_REG8(value, addr) gsc_writeb(value, addr) +#define WRITE_REG16(value, addr) gsc_writew(cpu_to_le16(value), (u16 *) (addr)) +#define WRITE_REG32(value, addr) gsc_writel(cpu_to_le32(value), (u32 *) (addr)) + + +#define LBA_CFG_TOK(bus,dfn) ((u32) ((bus)<<16 | (dfn)<<8)) +#define LBA_CFG_BUS(tok) ((u8) ((tok)>>16)) +#define LBA_CFG_DEV(tok) ((u8) ((tok)>>11) & 0x1f) +#define LBA_CFG_FUNC(tok) ((u8) ((tok)>>8 ) & 0x7) + + +#ifdef DEBUG_LBA +/* Extract LBA (Rope) number from HPA */ +#define LBA_NUM(x) ((((uintptr_t) x) >> 13) & 0xf) +#endif /* DEBUG_LBA */ + +#ifdef __LP64__ +/* PDC_PAT */ +static unsigned long pdc_result[32] __attribute__ ((aligned (8))) = {0,0,0,0}; +#endif + +/* +** One time initialization to let the world know the LBA was found. +** This is the only routine which is NOT static. +** Must be called exactly once before pci_init(). +*/ +void __init lba_init(void) +{ + register_driver(lba_drivers_for); +} + + +static void +lba_dump_res(struct resource *r, int d) +{ + int i; + + if (NULL == r) + return; + + printk("(%p)", r->parent); + for (i = d; i ; --i) printk(" "); + printk("%p [%lx,%lx]/%x\n", r, r->start, r->end, (int) r->flags); + lba_dump_res(r->child, d+2); + lba_dump_res(r->sibling, d); +} + + +/* +** LBA rev 2.0, 2.1, 2.2, and 3.0 bus walks require a complex +** workaround for cfg cycles: +** -- preserve LBA state +** -- LBA_FLAG_NO_DMA_DURING_CFG workaround +** -- turn on smart mode +** -- probe with config writes before doing config reads +** -- check ERROR_STATUS +** -- clear ERROR_STATUS +** -- restore LBA state +** +** The workaround is only used for device discovery. +*/ + +static int +lba_device_present( u8 bus, u8 dfn, struct lba_device *d) +{ + u8 first_bus = d->hba.hba_bus->secondary; + u8 last_sub_bus = d->hba.hba_bus->subordinate; +#if 0 +/* FIXME - see below in this function */ + u8 dev = PCI_SLOT(dfn); + u8 func = PCI_FUNC(dfn); +#endif + + ASSERT(bus >= first_bus); + ASSERT(bus <= last_sub_bus); + ASSERT((bus - first_bus) < LBA_MAX_NUM_BUSES); + + if ((bus < first_bus) || + (bus > last_sub_bus) || + ((bus - first_bus) >= LBA_MAX_NUM_BUSES)) + { + /* devices that fall into any of these cases won't get claimed */ + return(FALSE); + } + +#if 0 +/* +** FIXME: Need to implement code to fill the devices bitmap based +** on contents of the local pci_bus tree "data base". +** pci_register_ops() walks the bus for us and builds the tree. +** For now, always do the config cycle. +*/ + bus -= first_bus; + + return (((d->devices[bus][dev]) >> func) & 0x1); +#else + return TRUE; +#endif +} + + + +#define LBA_CFG_SETUP(d, tok) { \ + /* Save contents of error config register. */ \ + error_config = READ_REG32(d->hba.base_addr + LBA_ERROR_CONFIG); \ +\ + /* Save contents of status control register. */ \ + status_control = READ_REG32(d->hba.base_addr + LBA_STAT_CTL); \ +\ + /* For LBA rev 2.0, 2.1, 2.2, and 3.0, we must disable DMA \ + ** arbitration for full bus walks. \ + */ \ + if (LBA_DMA_DURING_CFG_DISABLED(d)) { \ + /* Save contents of arb mask register. */ \ + arb_mask = READ_REG32(d->hba.base_addr + LBA_ARB_MASK); \ +\ + /* \ + * Turn off all device arbitration bits (i.e. everything \ + * except arbitration enable bit). \ + */ \ + WRITE_REG32(0x1, d->hba.base_addr + LBA_ARB_MASK); \ + } \ +\ + /* \ + * Set the smart mode bit so that master aborts don't cause \ + * LBA to go into PCI fatal mode (required). \ + */ \ + WRITE_REG32(error_config | 0x20, d->hba.base_addr + LBA_ERROR_CONFIG); \ +} + + +#define LBA_CFG_PROBE(d, tok) { \ + /* \ + * Setup Vendor ID write and read back the address register \ + * to make sure that LBA is the bus master. \ + */ \ + WRITE_REG32(tok | PCI_VENDOR_ID, (d)->hba.base_addr + LBA_PCI_CFG_ADDR);\ + /* \ + * Read address register to ensure that LBA is the bus master, \ + * which implies that DMA traffic has stopped when DMA arb is off. \ + */ \ + lba_t32 = READ_REG32((d)->hba.base_addr + LBA_PCI_CFG_ADDR); \ + /* \ + * Generate a cfg write cycle (will have no affect on \ + * Vendor ID register since read-only). \ + */ \ + WRITE_REG32(~0, (d)->hba.base_addr + LBA_PCI_CFG_DATA); \ + /* \ + * Make sure write has completed before proceeding further, \ + * i.e. before setting clear enable. \ + */ \ + lba_t32 = READ_REG32((d)->hba.base_addr + LBA_PCI_CFG_ADDR); \ +} + + +/* + * HPREVISIT: + * -- Can't tell if config cycle got the error. + * + * OV bit is broken until rev 4.0, so can't use OV bit and + * LBA_ERROR_LOG_ADDR to tell if error belongs to config cycle. + * + * As of rev 4.0, no longer need the error check. + * + * -- Even if we could tell, we still want to return -1 + * for **ANY** error (not just master abort). + * + * -- Only clear non-fatal errors (we don't want to bring + * LBA out of pci-fatal mode). + * + * Actually, there is still a race in which + * we could be clearing a fatal error. We will + * live with this during our real mode bus walk + * until rev 4.0 (no driver activity during + * real mode bus walk). The real mode bus walk + * has race conditions concerning the use of + * smart mode as well. + */ + +#define LBA_MASTER_ABORT_ERROR 0xc +#define LBA_FATAL_ERROR 0x10 + +#define LBA_CFG_MASTER_ABORT_CHECK(d, base, tok, error) { \ + u32 error_status = 0; \ + /* \ + * Set clear enable (CE) bit. Unset by HW when new \ + * errors are logged -- LBA HW ERS section 14.3.3). \ + */ \ + WRITE_REG32(status_control | 0x20, base + LBA_STAT_CTL); \ + error_status = READ_REG32(base + LBA_ERROR_STATUS); \ + if ((error_status & 0x1f) != 0) { \ + /* \ + * Fail the config read request. \ + */ \ + error = 1; \ + if ((error_status & LBA_FATAL_ERROR) == 0) { \ + /* \ + * Clear error status (if fatal bit not set) by setting \ + * clear error log bit (CL). \ + */ \ + WRITE_REG32(status_control | 0x10, base + LBA_STAT_CTL); \ + } \ + } \ +} + +#define LBA_CFG_TR4_ADDR_SETUP(d, addr) \ + WRITE_REG32(((addr) & ~3), (d)->hba.base_addr + LBA_PCI_CFG_ADDR) + +#define LBA_CFG_ADDR_SETUP(d, addr) { \ + WRITE_REG32(((addr) & ~3), (d)->hba.base_addr + LBA_PCI_CFG_ADDR); \ + /* \ + * HPREVISIT: \ + * -- Potentially could skip this once DMA bug fixed. \ + * \ + * Read address register to ensure that LBA is the bus master, \ + * which implies that DMA traffic has stopped when DMA arb is off. \ + */ \ + lba_t32 = READ_REG32((d)->hba.base_addr + LBA_PCI_CFG_ADDR); \ +} + + +#define LBA_CFG_RESTORE(d, base) { \ + /* \ + * Restore status control register (turn off clear enable). \ + */ \ + WRITE_REG32(status_control, base + LBA_STAT_CTL); \ + /* \ + * Restore error config register (turn off smart mode). \ + */ \ + WRITE_REG32(error_config, base + LBA_ERROR_CONFIG); \ + if (LBA_DMA_DURING_CFG_DISABLED(d)) { \ + /* \ + * Restore arb mask register (reenables DMA arbitration). \ + */ \ + WRITE_REG32(arb_mask, base + LBA_ARB_MASK); \ + } \ +} + + + +static unsigned int +lba_rd_cfg( struct lba_device *d, u32 tok, u8 reg, u32 size) +{ + u32 data = ~0; + int error = 0; + u32 arb_mask = 0; /* used by LBA_CFG_SETUP/RESTORE */ + u32 error_config = 0; /* used by LBA_CFG_SETUP/RESTORE */ + u32 status_control = 0; /* used by LBA_CFG_SETUP/RESTORE */ + + ASSERT((size == sizeof(u8)) || + (size == sizeof(u16)) || + (size == sizeof(u32))); + + if ((size != sizeof(u8)) && + (size != sizeof(u16)) && + (size != sizeof(u32))) { + return(data); + } + + LBA_CFG_SETUP(d, tok); + LBA_CFG_PROBE(d, tok); + LBA_CFG_MASTER_ABORT_CHECK(d, d->hba.base_addr, tok, error); + if (!error) { + LBA_CFG_ADDR_SETUP(d, tok | reg); + switch (size) { + case sizeof(u8): + data = (u32) READ_REG8(d->hba.base_addr + LBA_PCI_CFG_DATA + (reg & 3)); + break; + case sizeof(u16): + data = (u32) READ_REG16(d->hba.base_addr + LBA_PCI_CFG_DATA + (reg & 2)); + break; + case sizeof(u32): + data = READ_REG32(d->hba.base_addr + LBA_PCI_CFG_DATA); + break; + default: + break; /* leave data as -1 */ + } + } + LBA_CFG_RESTORE(d, d->hba.base_addr); + return(data); +} + + + +#define LBA_CFG_RD(size, mask) \ +static int lba_cfg_read##size (struct pci_dev *dev, int pos, u##size *data) \ +{ \ + struct lba_device *d = LBA_DEV(dev->bus->sysdata); \ + u32 local_bus = (dev->bus->parent == NULL) ? 0 : dev->bus->secondary; \ + u32 tok = LBA_CFG_TOK(local_bus,dev->devfn); \ + \ + if ((!LBA_TR4PLUS(d)) && (!LBA_SKIP_PROBE(d))) { \ + /* original - Generate config cycle on broken elroy \ + with risk we will miss PCI bus errors. */ \ + *data = (u##size) lba_rd_cfg(d, tok, pos, sizeof(u##size)); \ + DBG_CFG(KERN_DEBUG "%s(%s+%2x) -> 0x%x (a)\n", __FUNCTION__, dev->slot_name, pos, *data); \ + return(*data == (u##size) -1); \ + } \ + \ + if (LBA_SKIP_PROBE(d) && (!lba_device_present(dev->bus->secondary, dev->devfn, d))) \ + { \ + DBG_CFG(KERN_DEBUG "%s(%s+%2x) -> -1 (b)\n", __FUNCTION__, dev->slot_name, pos, *data); \ + /* either don't want to look or know device isn't present. */ \ + *data = (u##size) -1; \ + return(0); \ + } \ + \ + /* Basic Algorithm \ + ** Should only get here on fully working LBA rev. \ + ** This is how simple the code should have been. \ + */ \ + LBA_CFG_TR4_ADDR_SETUP(d, tok | pos); \ + *data = READ_REG##size(d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & mask));\ + DBG_CFG(KERN_DEBUG "%s(%s+%2x) -> 0x%x (c)\n", __FUNCTION__, dev->slot_name, pos, *data);\ + return(*data == (u##size) -1); \ +} + +LBA_CFG_RD( 8, 3) +LBA_CFG_RD(16, 2) +LBA_CFG_RD(32, 0) + + + +static void +lba_wr_cfg( struct lba_device *d, u32 tok, u8 reg, u32 data, u32 size) +{ + int error = 0; + u32 arb_mask = 0; + u32 error_config = 0; + u32 status_control = 0; + + ASSERT((size == sizeof(u8)) || + (size == sizeof(u16)) || + (size == sizeof(u32))); + + if ((size != sizeof(u8)) && + (size != sizeof(u16)) && + (size != sizeof(u32))) { + return; + } + + LBA_CFG_SETUP(d, tok); + LBA_CFG_ADDR_SETUP(d, tok | reg); + switch (size) { + case sizeof(u8): + WRITE_REG8((u8) data, d->hba.base_addr + LBA_PCI_CFG_DATA + (reg&3)); + break; + case sizeof(u16): + WRITE_REG16((u8) data, d->hba.base_addr + LBA_PCI_CFG_DATA +(reg&2)); + break; + case sizeof(u32): + WRITE_REG32(data, d->hba.base_addr + LBA_PCI_CFG_DATA); + break; + default: + break; + } + LBA_CFG_MASTER_ABORT_CHECK(d, d->hba.base_addr, tok, error); + LBA_CFG_RESTORE(d, d->hba.base_addr); +} + + +/* + * LBA 4.0 config write code implements non-postable semantics + * by doing a read of CONFIG ADDR after the write. + */ + +#define LBA_CFG_WR(size, mask) \ +static int lba_cfg_write##size (struct pci_dev *dev, int pos, u##size data) \ +{ \ + struct lba_device *d = LBA_DEV(dev->bus->sysdata); \ + u32 local_bus = (dev->bus->parent == NULL) ? 0 : dev->bus->secondary; \ + u32 tok = LBA_CFG_TOK(local_bus,dev->devfn); \ + \ + ASSERT((tok & 0xff) == 0); \ + ASSERT(pos < 0x100); \ + \ + if ((!LBA_TR4PLUS(d)) && (!LBA_SKIP_PROBE(d))) { \ + /* Original Workaround */ \ + lba_wr_cfg(d, tok, pos, (u32) data, sizeof(u##size)); \ + DBG_CFG(KERN_DEBUG "%s(%s+%2x) = 0x%x (a)\n", __FUNCTION__, dev->slot_name, pos, data); \ + return 0; \ + } \ + \ + if (LBA_SKIP_PROBE(d) && (!lba_device_present(dev->bus->secondary, dev->devfn, d))) { \ + DBG_CFG(KERN_DEBUG "%s(%s+%2x) = 0x%x (b)\n", __FUNCTION__, dev->slot_name, pos, data); \ + return 1; /* New Workaround */ \ + } \ + \ + DBG_CFG(KERN_DEBUG "%s(%s+%2x) = 0x%x (c)\n", __FUNCTION__, dev->slot_name, pos, data); \ + /* Basic Algorithm */ \ + LBA_CFG_TR4_ADDR_SETUP(d, tok | pos); \ + WRITE_REG##size(data, d->hba.base_addr + LBA_PCI_CFG_DATA + (pos & mask)); \ + lba_t32 = READ_REG32(d->hba.base_addr + LBA_PCI_CFG_ADDR); \ + return 0; \ +} + + +LBA_CFG_WR( 8, 3) +LBA_CFG_WR(16, 2) +LBA_CFG_WR(32, 0) + +static struct pci_ops lba_cfg_ops = { + lba_cfg_read8, lba_cfg_read16, lba_cfg_read32, + lba_cfg_write8, lba_cfg_write16, lba_cfg_write32 + +}; + + + +static void +lba_bios_init(void) +{ + DBG(KERN_DEBUG MODULE_NAME ": lba_bios_init\n"); +} + + +#ifdef __LP64__ + +/* +** Determine if a device is already configured. +** If so, reserve it resources. +** +** Read PCI cfg command register and see if I/O or MMIO is enabled. +** PAT has to enable the devices it's using. +** +** Note: resources are fixed up before we try to claim them. +*/ +static void +lba_claim_dev_resources(struct pci_dev *dev) +{ + u16 cmd; + int i, srch_flags; + + (void) lba_cfg_read16(dev, PCI_COMMAND, &cmd); + + srch_flags = (cmd & PCI_COMMAND_IO) ? IORESOURCE_IO : 0; + if (cmd & PCI_COMMAND_MEMORY) + srch_flags |= IORESOURCE_MEM; + + if (!srch_flags) + return; + + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + if (dev->resource[i].flags & srch_flags) { + pci_claim_resource(dev, i); + DBG(" claimed %s %d [%lx,%lx]/%x\n", + dev->slot_name, i, + dev->resource[i].start, + dev->resource[i].end, + (int) dev->resource[i].flags + ); + } + } +} +#endif + + +/* +** The algorithm is generic code. +** But it needs to access local data structures to get the IRQ base. +** Could make this a "pci_fixup_irq(bus, region)" but not sure +** it's worth it. +** +** Called by do_pci_scan_bus() immediately after each PCI bus is walked. +** Resources aren't allocated until recursive buswalk below HBA is completed. +*/ +static void +lba_fixup_bus(struct pci_bus *bus) +{ + struct list_head *ln; + struct pci_dev *dev; + u16 fbb_enable = PCI_STATUS_FAST_BACK; + u16 status; + struct lba_device *ldev = LBA_DEV(bus->sysdata); +#ifdef __LP64__ + int i; +#endif + DBG("lba_fixup_bus(0x%p) bus %d sysdata 0x%p\n", + bus, bus->secondary, bus->sysdata); + + /* + ** Properly Setup MMIO resources for this bus. + ** pci_alloc_primary_bus() mangles this. + */ + if (NULL == bus->self) { + int err; + + DBG("lba_fixup_bus() %s [%lx/%lx]/%x\n", + ldev->hba.io_space.name, + ldev->hba.io_space.start, + ldev->hba.io_space.end, + (int) ldev->hba.io_space.flags); + DBG("lba_fixup_bus() %s [%lx/%lx]/%x\n", + ldev->hba.mem_space.name, + ldev->hba.mem_space.start, + ldev->hba.mem_space.end, + (int) ldev->hba.mem_space.flags); + + err = request_resource(&ioport_resource, &(ldev->hba.io_space)); + if (err < 0) { + BUG(); + lba_dump_res(&ioport_resource, 2); + } + err = request_resource(&iomem_resource, &(ldev->hba.mem_space)); + if (err < 0) { + BUG(); + lba_dump_res(&iomem_resource, 2); + } + + bus->resource[0] = &(ldev->hba.io_space); + bus->resource[1] = &(ldev->hba.mem_space); + } + + list_for_each(ln, &bus->devices) { + + dev = pci_dev_b(ln); + +#ifdef __LP64__ + /* + ** 0-5 are the "standard PCI regions" + ** (see comments near PCI_NUM_RESOURCES in include/linux/pci.h) + */ + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + struct resource *res = &(dev->resource[i]); + + if (res->flags & IORESOURCE_MEM) { + /* "Globalize" PCI address */ + res->start |= ldev->lmmio_base; + res->end |= ldev->lmmio_base; + } + } +#endif + + /* + ** If one device does not support FBB transfers, + ** No one on the bus can be allowed to use them. + */ + (void) lba_cfg_read16(dev, PCI_STATUS, &status); + fbb_enable &= status; + +#ifdef __LP64__ + if (pdc_pat) { + /* Claim resources for PDC's devices */ + lba_claim_dev_resources(dev); + } +#endif /* __LP64__ */ + + /* + ** P2PB's have no IRQs. ignore them. + */ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) + continue; + + /* Adjust INTERRUPT_LINE for this dev */ + iosapic_fixup_irq(LBA_DEV(bus->sysdata)->iosapic_obj, dev); + } + +#if 0 +/* FIXME/REVISIT - finish figuring out to set FBB on both +** pbus_set_ranges() clobbers PCI_BRIDGE_CONTROL. +** Can't fixup here anyway....garr... +*/ + if (fbb_enable) { + if (bus->self) { + u8 control; + /* enable on PPB */ + (void) lba_cfg_read8(bus->self, PCI_BRIDGE_CONTROL, &control); + (void) lba_cfg_write8(bus->self, PCI_BRIDGE_CONTROL, control | PCI_STATUS_FAST_BACK); + + } else { + /* enable on LBA */ + } + fbb_enable = PCI_COMMAND_FAST_BACK; + } + + /* Lastly enable FBB/PERR/SERR on all devices too */ + list_for_each(ln, &bus->devices) { + (void) lba_cfg_read16(dev, PCI_COMMAND, &status); + status |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | fbb_enable; + (void) lba_cfg_write16(dev, PCI_COMMAND, status); + } +#endif +} + + +struct pci_bios_ops lba_bios_ops = { + lba_bios_init, + lba_fixup_bus /* void lba_fixup_bus(struct pci_bus *bus) */ +}; + + + + +/******************************************************* +** +** LBA Sprockets "I/O Port" Space Accessor Functions +** +** This set of accessor functions is intended for use with +** "legacy firmware" (ie Sprockets on Allegro/Forte boxes). +** +** Many PCI devices don't require use of I/O port space (eg Tulip, +** NCR720) since they export the same registers to both MMIO and +** I/O port space. In general I/O port space is slower than +** MMIO since drivers are designed so PIO writes can be posted. +** +********************************************************/ + +#define LBA_PORT_IN(size, mask) \ +static u##size lba_astro_in##size (struct pci_hba_data *d, u16 addr) \ +{ \ + u##size t; \ + ASSERT(bus != NULL); \ + DBG_PORT(KERN_DEBUG "%s(0x%p, 0x%x) ->", __FUNCTION__, bus, addr); \ + t = READ_REG##size(LBA_ASTRO_PORT_BASE + addr); \ + DBG_PORT(" 0x%x\n", t); \ + return (t); \ +} + +LBA_PORT_IN( 8, 3) +LBA_PORT_IN(16, 2) +LBA_PORT_IN(32, 0) + + + +/* +** BUG X4107: Ordering broken - DMA RD return can bypass PIO WR +** +** Fixed in Elroy 2.2. The READ_U32(..., LBA_FUNC_ID) below is +** guarantee non-postable completion semantics - not avoid X4107. +** The READ_U32 only guarantees the write data gets to elroy but +** out to the PCI bus. We can't read stuff from I/O port space +** since we don't know what has side-effects. Attempting to read +** from configuration space would be suicidal given the number of +** bugs in that elroy functionality. +** +** Description: +** DMA read results can improperly pass PIO writes (X4107). The +** result of this bug is that if a processor modifies a location in +** memory after having issued PIO writes, the PIO writes are not +** guaranteed to be completed before a PCI device is allowed to see +** the modified data in a DMA read. +** +** Note that IKE bug X3719 in TR1 IKEs will result in the same +** symptom. +** +** Workaround: +** The workaround for this bug is to always follow a PIO write with +** a PIO read to the same bus before starting DMA on that PCI bus. +** +*/ +#define LBA_PORT_OUT(size, mask) \ +static void lba_astro_out##size (struct pci_hba_data *d, u16 addr, u##size val) \ +{ \ + ASSERT(bus != NULL); \ + DBG_PORT(KERN_DEBUG "%s(0x%p, 0x%x, 0x%x)\n", __FUNCTION__, d, addr, val); \ + WRITE_REG##size(val, LBA_ASTRO_PORT_BASE + addr); \ + if (LBA_DEV(d)->hw_rev < 3) \ + lba_t32 = READ_U32(d->base_addr + LBA_FUNC_ID); \ +} + +LBA_PORT_OUT( 8, 3) +LBA_PORT_OUT(16, 2) +LBA_PORT_OUT(32, 0) + + +static struct pci_port_ops lba_astro_port_ops = { + lba_astro_in8, lba_astro_in16, lba_astro_in32, + lba_astro_out8, lba_astro_out16, lba_astro_out32 +}; + + +#ifdef __LP64__ + +#define PIOP_TO_GMMIO(lba, addr) \ + ((lba)->iop_base + (((addr)&0xFFFC)<<10) + ((addr)&3)) + +/******************************************************* +** +** LBA PAT "I/O Port" Space Accessor Functions +** +** This set of accessor functions is intended for use with +** "PAT PDC" firmware (ie Prelude/Rhapsody/Piranha boxes). +** +** This uses the PIOP space located in the first 64MB of GMMIO. +** Each rope gets a full 64*KB* (ie 4 bytes per page) this way. +** bits 1:0 stay the same. bits 15:2 become 25:12. +** Then add the base and we can generate an I/O Port cycle. +********************************************************/ +#undef LBA_PORT_IN +#define LBA_PORT_IN(size, mask) \ +static u##size lba_pat_in##size (struct pci_hba_data *l, u16 addr) \ +{ \ + u##size t; \ + ASSERT(bus != NULL); \ + DBG_PORT(KERN_DEBUG "%s(0x%p, 0x%x) ->", __FUNCTION__, l, addr); \ + t = READ_REG##size(PIOP_TO_GMMIO(LBA_DEV(l), addr)); \ + DBG_PORT(" 0x%x\n", t); \ + return (t); \ +} + +LBA_PORT_IN( 8, 3) +LBA_PORT_IN(16, 2) +LBA_PORT_IN(32, 0) + + +#undef LBA_PORT_OUT +#define LBA_PORT_OUT(size, mask) \ +static void lba_pat_out##size (struct pci_hba_data *l, u16 addr, u##size val) \ +{ \ + void *where = (void *) PIOP_TO_GMMIO(LBA_DEV(l), addr); \ + ASSERT(bus != NULL); \ + DBG_PORT(KERN_DEBUG "%s(0x%p, 0x%x, 0x%x)\n", __FUNCTION__, l, addr, val); \ + WRITE_REG##size(val, where); \ + /* flush the I/O down to the elroy at least */ \ + lba_t32 = READ_U32(l->base_addr + LBA_FUNC_ID); \ +} + +LBA_PORT_OUT( 8, 3) +LBA_PORT_OUT(16, 2) +LBA_PORT_OUT(32, 0) + + +static struct pci_port_ops lba_pat_port_ops = { + lba_pat_in8, lba_pat_in16, lba_pat_in32, + lba_pat_out8, lba_pat_out16, lba_pat_out32 +}; + + + +/* +** make range information from PDC available to PCI subsystem. +** We make the PDC call here in order to get the PCI bus range +** numbers. The rest will get forwarded in pcibios_fixup_bus(). +** We don't have a struct pci_bus assigned to us yet. +*/ +static void +lba_pat_resources( struct hp_device *d, struct lba_device *lba_dev) +{ + pdc_pat_cell_mod_maddr_block_t pa_pdc_cell; /* PA_VIEW */ +#ifdef DONT_NEED_THIS_FOR_ASTRO + pdc_pat_cell_mod_maddr_block_t io_pdc_cell; /* IO_VIEW */ + long io_count; +#endif + long status; /* PDC return status */ + long pa_count; + int i; + + /* return cell module (IO view) */ + status = pdc_pat_cell_module(& pdc_result, d->pcell_loc, d->mod_index, + PA_VIEW, & pa_pdc_cell); + pa_count = pa_pdc_cell.mod[1]; + +#ifdef DONT_NEED_THIS_FOR_ASTRO + status |= pdc_pat_cell_module(& pdc_result, d->pcell_loc, d->mod_index, + IO_VIEW, & io_pdc_cell); + io_count = io_pdc_cell.mod[1]; +#endif + + /* We've already done this once for device discovery...*/ + if (status != PDC_RET_OK) { + panic("pdc_pat_cell_module() call failed for LBA!\n"); + } + + if (PAT_GET_ENTITY(pa_pdc_cell.mod_info) != PAT_ENTITY_LBA) { + panic("pdc_pat_cell_module() entity returned != PAT_ENTITY_LBA!\n"); + } + + /* + ** Inspect the resources PAT tells us about + */ + for (i = 0; i < pa_count; i++) { + struct { + unsigned long type; + unsigned long start; + unsigned long end; /* aka finish */ + } *p; + struct resource *r; + + p = (void *) &(pa_pdc_cell.mod[2+i*3]); + + /* Convert the PAT range data to PCI "struct resource" */ + switch(p->type & 0xff) { + case PAT_PBNUM: + lba_dev->hba.bus_num.start = p->start; + lba_dev->hba.bus_num.end = p->end; + break; + case PAT_LMMIO: + /* used to fix up pre-initialized MEM BARs */ + lba_dev->lmmio_base = p->start; + + r = &(lba_dev->hba.mem_space); + r->name = "LBA LMMIO"; + r->start = p->start; + r->end = p->end; + r->flags = IORESOURCE_MEM; + r->parent = r->sibling = r->child = NULL; + break; + case PAT_GMMIO: + printk(KERN_WARNING MODULE_NAME + " range[%d] : ignoring GMMIO (0x%lx)\n", + i, p->start); + lba_dev->gmmio_base = p->start; + break; + case PAT_NPIOP: + printk(KERN_WARNING MODULE_NAME + " range[%d] : ignoring NPIOP (0x%lx)\n", + i, p->start); + break; + case PAT_PIOP: + /* + ** Postable I/O port space is per PCI host adapter. + */ + + /* save base of 64MB PIOP region */ + lba_dev->iop_base = p->start; + + r = &(lba_dev->hba.io_space); + r->name = "LBA I/O Port"; + r->start = lba_dev->hba.hba_num << 16; + r->end = r->start + 0xffffUL; + r->flags = IORESOURCE_IO; + r->parent = r->sibling = r->child = NULL; + break; + default: + printk(KERN_WARNING MODULE_NAME + " range[%d] : unknown pat range type (0x%lx)\n", + i, p->type & 0xff); + break; + } + } +} +#endif /* __LP64__ */ + + +static void +lba_legacy_resources( struct hp_device *d, struct lba_device *lba_dev) +{ + int lba_num; + struct resource *r; +#ifdef __LP64__ + /* + ** Used to sign extend instead BAR values are only 32-bit. + ** 64-bit BARs have the upper 32-bit's zero'd by firmware. + ** "Sprockets" PDC initializes for 32-bit OS. + */ + lba_dev->lmmio_base = 0xffffffff00000000UL; +#endif + + /* + ** With "legacy" firmware, the lowest byte of FW_SCRATCH + ** represents bus->secondary and the second byte represents + ** bus->subsidiary (i.e. highest PPB programmed by firmware). + ** PCI bus walk *should* end up with the same result. + ** FIXME: But we don't have sanity checks in PCI or LBA. + */ + lba_num = READ_REG32(d->hpa + LBA_FW_SCRATCH); + r = &(lba_dev->hba.bus_num); + r->name = "LBA PCI Busses"; + r->start = lba_num & 0xff; + r->end = (lba_num>>8) & 0xff; + + /* Set up local PCI Bus resources - we don't really need + ** them for Legacy boxes but it's nice to see in /proc. + */ + r = &(lba_dev->hba.mem_space); + r->name = "LBA PCI LMMIO"; + r->flags = IORESOURCE_MEM; + r->start = READ_REG32(d->hpa + LBA_LMMIO_BASE); + r->end = r->start + ~ (READ_REG32(d->hpa + LBA_LMMIO_MASK)); + + r = &(lba_dev->hba.io_space); + r->name = "LBA PCI I/O Ports"; + r->flags = IORESOURCE_IO; + r->start = READ_REG32(d->hpa + LBA_IOS_BASE); + r->end = r->start + (READ_REG32(d->hpa + LBA_IOS_MASK) ^ 0xffff); + + lba_num = lba_dev->hba.hba_num << 16; + r->start |= lba_num; + r->end |= lba_num; +} + + +/************************************************************************** +** +** LBA initialization code (HW and SW) +** +** o identify LBA chip itself +** o initialize LBA chip modes (HardFail) +** o FIXME: initialize DMA hints for reasonable defaults +** o enable configuration functions +** o call pci_register_ops() to discover devs (fixup/fixup_bus get invoked) +** +**************************************************************************/ + +static void +lba_hw_init(struct lba_device *d) +{ + u32 stat; + + /* Set HF mode as the default (vs. -1 mode). */ + stat = READ_REG32(d->hba.base_addr + LBA_STAT_CTL); + WRITE_REG32(stat | HF_ENABLE, d->hba.base_addr + LBA_STAT_CTL); + + /* + ** FIXME: Hint registers are programmed with default hint + ** values by firmware. Hints should be sane even if we + ** can't reprogram them the way drivers want. + */ +} + + + +static void +lba_common_init(struct lba_device *lba_dev) +{ + pci_bios = &lba_bios_ops; + pcibios_register_hba((struct pci_hba_data *)lba_dev); + lba_dev->lba_lock = SPIN_LOCK_UNLOCKED; + + /* + ** Set flags which depend on hw_rev + */ + if (!LBA_TR4PLUS(lba_dev)) { + lba_dev->flags |= LBA_FLAG_NO_DMA_DURING_CFG; + } +} + + + +/* +** Determine if lba should claim this chip (return 0) or not (return 1). +** If so, initialize the chip and tell other partners in crime they +** have work to do. +*/ +static __init int +lba_driver_callback(struct hp_device *d, struct pa_iodc_driver *dri) +{ + struct lba_device *lba_dev; + struct pci_bus *lba_bus; + u32 func_class; + void *tmp_obj; + + /* from drivers/pci/setup-bus.c */ + extern void __init pbus_set_ranges(struct pci_bus *, struct pbus_set_ranges_data *); + + /* Read HW Rev First */ + func_class = READ_REG32(d->hpa + LBA_FCLASS); + func_class &= 0xf; + + switch (func_class) { + case 0: dri->version = "TR1.0"; break; + case 1: dri->version = "TR2.0"; break; + case 2: dri->version = "TR2.1"; break; + case 3: dri->version = "TR2.2"; break; + case 4: dri->version = "TR3.0"; break; + case 5: dri->version = "TR4.0"; break; + default: dri->version = "TR4+"; + } + + printk("%s version %s (0x%x) found at 0x%p\n", dri->name, dri->version, func_class & 0xf, d->hpa); + + /* Just in case we find some prototypes... */ + if (func_class < 2) { + printk(KERN_WARNING "Can't support LBA older than TR2.1 " + "- continuing under adversity.\n"); + } + + /* + ** Tell I/O SAPIC driver we have a IRQ handler/region. + */ + tmp_obj = iosapic_register(d->hpa+LBA_IOSAPIC_BASE); + if (NULL == tmp_obj) { + /* iosapic may have failed. But more likely the + ** slot isn't occupied and thus has no IRT entries. + ** iosapic_register looks for this iosapic in the IRT + ** before bothering to allocating data structures + ** we don't need. + */ + DBG(KERN_WARNING MODULE_NAME ": iosapic_register says not used\n"); + return (1); + } + + lba_dev = kmalloc(sizeof(struct lba_device), GFP_KERNEL); + if (NULL == lba_dev) + { + printk("lba_init_chip - couldn't alloc lba_device\n"); + return(1); + } + + memset(lba_dev, 0, sizeof(struct lba_device)); + + + /* ---------- First : initialize data we already have --------- */ + + /* + ** Need hw_rev to adjust configuration space behavior. + ** LBA_TR4PLUS macro uses hw_rev field. + */ + lba_dev->hw_rev = func_class; + + lba_dev->hba.base_addr = d->hpa; /* faster access */ + lba_dev->iosapic_obj = tmp_obj; /* save interrupt handle */ + + /* ------------ Second : initialize common stuff ---------- */ + lba_common_init(lba_dev); + lba_hw_init(lba_dev); + + /* ---------- Third : setup I/O Port and MMIO resources --------- */ +#ifdef __LP64__ + + if (pdc_pat) { + /* PDC PAT firmware uses PIOP region of GMMIO space. */ + pci_port = &lba_pat_port_ops; + + /* Go ask PDC PAT what resources this LBA has */ + lba_pat_resources(d, lba_dev); + + } else { +#endif + /* Sprockets PDC uses NPIOP region */ + pci_port = &lba_astro_port_ops; + + /* Poke the chip a bit for /proc output */ + lba_legacy_resources(d, lba_dev); +#ifdef __LP64__ + } +#endif + + /* + ** Tell PCI support another PCI bus was found. + ** Walks PCI bus for us too. + */ + lba_bus = lba_dev->hba.hba_bus = + pci_scan_bus( lba_dev->hba.bus_num.start, &lba_cfg_ops, (void *) lba_dev); + +#ifdef __LP64__ + if (pdc_pat) { + + /* determine window sizes needed by PCI-PCI bridges */ + DBG_PAT("LBA pcibios_size_bridge()\n"); + pcibios_size_bridge(lba_bus, NULL); + + /* assign resources to un-initialized devices */ + DBG_PAT("LBA pcibios_assign_unassigned_resources()\n"); + pcibios_assign_unassigned_resources(lba_bus); + +#ifdef DEBUG_LBA_PAT + DBG_PAT("\nLBA PIOP resource tree\n"); + lba_dump_res(&lba_dev->hba.io_space, 2); + DBG_PAT("\nLBA LMMIO resource tree\n"); + lba_dump_res(&lba_dev->hba.mem_space, 2); +#endif + + /* program *all* PCI-PCI bridge range registers */ + DBG_PAT("LBA pbus_set_ranges()\n"); + pbus_set_ranges(lba_bus, NULL); + } +#endif /* __LP64__ */ + + /* + ** Once PCI register ops has walked the bus, access to config + ** space is restricted. Avoids master aborts on config cycles. + ** Early LBA revs go fatal on *any* master abort. + */ + if (!LBA_TR4PLUS(lba_dev)) { + lba_dev->flags |= LBA_FLAG_SKIP_PROBE; + } + + /* Whew! Finally done! Tell services we got this one covered. */ + return 0; +} + + +/* +** Initialize the IBASE/IMASK registers for LBA (Elroy). +** Only called from sba_iommu.c initialization sequence. +*/ +void lba_init_iregs(void *sba_hpa, u32 ibase, u32 imask) +{ + extern struct pci_hba_data *hba_list; /* arch/parisc/kernel/pci.c */ + struct pci_hba_data *lba; + + imask <<= 2; /* adjust for hints - 2 more bits */ + + ASSERT((ibase & 0x003fffff) == 0); + ASSERT((imask & 0x003fffff) == 0); + + /* FIXME: sba_hpa is intended to search some table to + ** determine which LBA's belong to the caller's SBA. + ** IS_ASTRO: just assume only one SBA for now. + */ + ASSERT(NULL != hba_list); + DBG(KERN_DEBUG "%s() ibase 0x%x imask 0x%x\n", __FUNCTION__, ibase, imask); + + for (lba = hba_list; NULL != lba; lba = lba->next) { + DBG(KERN_DEBUG "%s() base_addr %p\n", __FUNCTION__, lba->base_addr); + WRITE_REG32( imask, lba->base_addr + LBA_IMASK); + WRITE_REG32( ibase, lba->base_addr + LBA_IBASE); + } + DBG(KERN_DEBUG "%s() done\n", __FUNCTION__); +} + diff --git a/arch/parisc/kernel/led.c b/arch/parisc/kernel/led.c new file mode 100644 index 000000000000..6e6ee7768824 --- /dev/null +++ b/arch/parisc/kernel/led.c @@ -0,0 +1,450 @@ +/* + * Chassis LCD/LED driver for HP-PARISC workstations + * + * (c) Copyright 2000 Red Hat Software + * (c) Copyright 2000 Helge Deller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* HZ */ +#include + + +/* define to disable all LED functions */ +#undef DISABLE_LEDS + + +#define CPU_HVERSION ((boot_cpu_data.hversion >> 4) & 0x0FFF) + + +struct lcd_block { + unsigned char command; /* stores the command byte */ + unsigned char on; /* value for turning LED on */ + unsigned char off; /* value for turning LED off */ +}; + +/* Structure returned by PDC_RETURN_CHASSIS_INFO */ +struct pdc_chassis_lcd_info_ret_block { + unsigned long model:16; /* DISPLAY_MODEL_XXXX (see below) */ + unsigned long lcd_width:16; /* width of the LCD in chars (DISPLAY_MODEL_LCD only) */ + char *lcd_cmd_reg_addr; /* ptr to LCD cmd-register & data ptr for LED */ + char *lcd_data_reg_addr; /* ptr to LCD data-register (LCD only) */ + unsigned int min_cmd_delay; /* delay in uS after cmd-write (LCD only) */ + unsigned char reset_cmd1; /* command #1 for writing LCD string (LCD only) */ + unsigned char reset_cmd2; /* command #2 for writing LCD string (LCD only) */ + unsigned char act_enable; /* 0 = no activity (LCD only) */ + struct lcd_block heartbeat; + struct lcd_block disk_io; + struct lcd_block lan_rcv; + struct lcd_block lan_tx; + char _pad; +}; + +/* values for pdc_chassis_lcd_info_ret_block.model: */ +#define DISPLAY_MODEL_LCD 0 /* KittyHawk LED or LCD */ +#define DISPLAY_MODEL_NONE 1 /* no LED or LCD */ +#define DISPLAY_MODEL_LASI 2 /* LASI style 8 bit LED */ +#define DISPLAY_MODEL_OLD_ASP 0x7F /* faked: ASP style 8 x 1 bit LED (only very old ASP versions) */ + + +/* LCD_CMD and LCD_DATA for KittyHawk machines */ +#ifdef __LP64__ +#define KITTYHAWK_LCD_CMD 0xfffffffff0190000L +#else +#define KITTYHAWK_LCD_CMD 0xf0190000 +#endif +#define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD + 1) + + +/* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's */ +static struct pdc_chassis_lcd_info_ret_block +lcd_info __attribute__((aligned(8))) = +{ + model:DISPLAY_MODEL_LCD, + lcd_width:16, + lcd_cmd_reg_addr:(char *) KITTYHAWK_LCD_CMD, + lcd_data_reg_addr:(char *) KITTYHAWK_LCD_DATA, + min_cmd_delay:40, + reset_cmd1:0x80, + reset_cmd2:0xc0, +}; + + +/* direct access to some of the lcd_info variables */ +#define LCD_CMD_REG lcd_info.lcd_cmd_reg_addr +#define LCD_DATA_REG lcd_info.lcd_data_reg_addr +#define LED_DATA_REG lcd_info.lcd_cmd_reg_addr /* LASI & ASP only */ + + + + +/* + ** + ** led_ASP_driver() + ** + */ +#define LED_DATA 0x01 /* data to shift (0:on 1:off) */ +#define LED_STROBE 0x02 /* strobe to clock data */ +static void led_ASP_driver(unsigned char leds) +{ + int i; + + leds = ~leds; + for (i = 0; i < 8; i++) { + unsigned char value; + value = (leds & 0x80) >> 7; + gsc_writeb( value, LED_DATA_REG ); + gsc_writeb( value | LED_STROBE, LED_DATA_REG ); + leds <<= 1; + } +} + + +/* + ** + ** led_LASI_driver() + ** + */ +static void led_LASI_driver(unsigned char leds) +{ + leds = ~leds; + gsc_writeb( leds, LED_DATA_REG ); +} + + +/* + ** + ** led_LCD_driver() + ** + ** The logic of the LCD driver is, that we write at every interrupt + ** only to one of LCD_CMD_REG _or_ LCD_DATA_REG - registers. + ** That way we don't need to let this interrupt routine busywait + ** the "min_cmd_delay", since idlewaiting in an interrupt-routine is + ** allways a BAD IDEA ! + ** + ** TODO: check the value of "min_cmd_delay" against the value of HZ. + ** + */ + +static void led_LCD_driver(unsigned char leds) +{ + static int last_index; /* 0:heartbeat, 1:disk, 2:lan_in, 3:lan_out */ + static int last_was_cmd;/* 0: CMD was written last, 1: DATA was last */ + struct lcd_block *block_ptr; + int value; + + // leds = ~leds; /* needed ? */ + + switch (last_index) { + case 0: block_ptr = &lcd_info.heartbeat; + value = leds & LED_HEARTBEAT; + break; + case 1: block_ptr = &lcd_info.disk_io; + value = leds & LED_DISK_IO; + break; + case 2: block_ptr = &lcd_info.lan_rcv; + value = leds & LED_LAN_RCV; + break; + case 3: block_ptr = &lcd_info.lan_tx; + value = leds & LED_LAN_TX; + break; + default: /* should never happen: */ + BUG(); + return; + } + + if (last_was_cmd) { + /* write the value to the LCD data port */ + gsc_writeb( value ? block_ptr->on : block_ptr->off, LCD_DATA_REG ); + } else { + /* write the command-byte to the LCD command register */ + gsc_writeb( block_ptr->command, LCD_CMD_REG ); + } + + /* now update the vars for the next interrupt iteration */ + if (++last_was_cmd == 2) { + last_was_cmd = 0; + if (++last_index == 4) + last_index = 0; + } +} + + + +static char currentleds; /* stores current value of the LEDs */ + +static void (*led_func_ptr) (unsigned char); /* ptr to LCD/LED-specific function */ + +/* + ** led_interrupt_func() + ** + ** is called at every timer interrupt from time.c, + ** updates the chassis LCD/LED + */ + +#define HEARTBEAT_LEN (HZ/16) + +void led_interrupt_func(void) +{ +#ifndef DISABLE_LEDS + static int count; + static int lastleds = -1; + static int nr; + + /* exit, if not initialized */ + if (!led_func_ptr) + return; + + /* increment the local counter */ + if (count == (HZ-1)) + count = 0; + else + count++; + + /* calculate the Heartbeat */ + if ((count % (HZ/2)) < HEARTBEAT_LEN) + currentleds |= LED_HEARTBEAT; + else + currentleds &= ~LED_HEARTBEAT; + + /* roll LEDs 0..2 */ + if (count == 0) { + if (nr++ >= 2) + nr = 0; + currentleds &= ~7; + currentleds |= (1 << nr); + } + + /* now update the LEDs */ + if (currentleds != lastleds) { + led_func_ptr(currentleds); + lastleds = currentleds; + } +#endif +} + + +/* + ** register_led_driver() + ** + ** All information in lcd_info needs to be set up prior + ** calling this function. + */ + +static void __init register_led_driver(void) +{ +#ifndef DISABLE_LEDS + switch (lcd_info.model) { + case DISPLAY_MODEL_LCD: + printk(KERN_INFO "LCD display at (%p,%p)\n", + LCD_CMD_REG , LCD_DATA_REG); + led_func_ptr = led_LCD_driver; + break; + + case DISPLAY_MODEL_LASI: + printk(KERN_INFO "LED display at %p\n", + LED_DATA_REG); + led_func_ptr = led_LASI_driver; + break; + + case DISPLAY_MODEL_OLD_ASP: + printk(KERN_INFO "LED (ASP-style) display at %p\n", + LED_DATA_REG); + led_func_ptr = led_ASP_driver; + break; + + default: + printk(KERN_ERR "%s: Wrong LCD/LED model %d !\n", + __FUNCTION__, lcd_info.model); + return; + } +#endif +} + +/* + * XXX - could this move to lasi.c ?? + */ + +/* + ** lasi_led_init() + ** + ** lasi_led_init() is called from lasi.c with the base hpa + ** of the lasi controller chip. + ** Since Mirage and Electra machines use a different LED + ** address register, we need to check for these machines + ** explicitly. + */ + +#ifdef CONFIG_GSC_LASI +void __init lasi_led_init(unsigned long lasi_hpa) +{ + if (lcd_info.model != DISPLAY_MODEL_NONE || + lasi_hpa == 0) + return; + + printk("%s: CPU_HVERSION %x\n", __FUNCTION__, CPU_HVERSION); + + /* Mirage and Electra machines need special offsets */ + switch (CPU_HVERSION) { + case 0x60A: /* Mirage Jr (715/64) */ + case 0x60B: /* Mirage 100 */ + case 0x60C: /* Mirage 100+ */ + case 0x60D: /* Electra 100 */ + case 0x60E: /* Electra 120 */ + LED_DATA_REG = (char *) (lasi_hpa - 0x00020000); + break; + default: + LED_DATA_REG = (char *) (lasi_hpa + 0x0000C000); + break; + } /* switch() */ + + lcd_info.model = DISPLAY_MODEL_LASI; + register_led_driver(); +} +#endif + + +/* + ** asp_led_init() + ** + ** asp_led_init() is called from asp.c with the ptr + ** to the LED display. + */ + +#ifdef CONFIG_GSC_LASI +void __init asp_led_init(unsigned long led_ptr) +{ + if (lcd_info.model != DISPLAY_MODEL_NONE || + led_ptr == 0) + return; + + lcd_info.model = DISPLAY_MODEL_OLD_ASP; + LED_DATA_REG = (char *) led_ptr; + + register_led_driver(); +} + +#endif + + + +/* + ** register_led_regions() + ** + ** Simple function, which registers the LCD/LED regions for /procfs. + ** At bootup - where the initialisation of the LCD/LED normally happens - + ** not all internal structures of request_region() are properly set up, + ** so that we delay the registration until busdevice.c is executed. + ** + */ + +void __init register_led_regions(void) +{ + switch (lcd_info.model) { + case DISPLAY_MODEL_LCD: + request_region((unsigned long)LCD_CMD_REG, 1, "lcd_cmd"); + request_region((unsigned long)LCD_DATA_REG, 1, "lcd_data"); + break; + case DISPLAY_MODEL_LASI: + case DISPLAY_MODEL_OLD_ASP: + request_region((unsigned long)LED_DATA_REG, 1, "led_data"); + break; + } +} + + + +/* + ** led_init() + ** + ** led_init() is called very early in the bootup-process from setup.c + ** and asks the PDC for an usable chassis LCD or LED. + ** If the PDC doesn't return any info, then the LED + ** is detected by lasi.c or asp.c and registered with the + ** above functions lasi_led_init() or asp_led_init(). + ** KittyHawk machines have often a buggy PDC, so that + ** we explicitly check for those machines here. + */ + +int __init led_init(void) +{ +#ifndef DISABLE_LEDS + long pdc_result[32]; + + printk("%s: CPU_HVERSION %x\n", __FUNCTION__, CPU_HVERSION); + + /* Work around the buggy PDC of KittyHawk-machines */ + switch (CPU_HVERSION) { + case 0x580: /* KittyHawk DC2-100 (K100) */ + case 0x581: /* KittyHawk DC3-120 (K210) */ + case 0x582: /* KittyHawk DC3 100 (K400) */ + case 0x583: /* KittyHawk DC3 120 (K410) */ + case 0x58B: /* KittyHawk DC2 100 (K200) */ + printk("%s: KittyHawk-Machine found !!\n", __FUNCTION__); + goto found; /* use the preinitialized values of lcd_info */ + + default: + break; + } + + /* initialize pdc_result, so we can check the return values of pdc_chassis_info() */ + pdc_result[0] = pdc_result[1] = 0; + + if (pdc_chassis_info(&pdc_result, &lcd_info, sizeof(lcd_info)) == PDC_OK) { + printk("%s: chassis info: model %d, ret0=%d, ret1=%d\n", + __FUNCTION__, lcd_info.model, pdc_result[0], pdc_result[1]); + + /* check the results. Some machines have a buggy PDC */ + if (pdc_result[0] <= 0 || pdc_result[0] != pdc_result[1]) + goto not_found; + + switch (lcd_info.model) { + case DISPLAY_MODEL_LCD: /* LCD display */ + if (pdc_result[0] != sizeof(struct pdc_chassis_lcd_info_ret_block) + && pdc_result[0] != sizeof(struct pdc_chassis_lcd_info_ret_block) - 1) + goto not_found; + printk("%s: min_cmd_delay = %d uS\n", + __FUNCTION__, lcd_info.min_cmd_delay); + break; + + case DISPLAY_MODEL_NONE: /* no LED or LCD available */ + goto not_found; + + case DISPLAY_MODEL_LASI: /* Lasi style 8 bit LED display */ + if (pdc_result[0] != 8 && pdc_result[0] != 32) + goto not_found; + break; + + default: + printk(KERN_WARNING "Unknown LCD/LED model %d\n", + lcd_info.model); + goto not_found; + } /* switch() */ + +found: + /* register the LCD/LED driver */ + register_led_driver(); + return 0; + + } /* if() */ + +not_found: + lcd_info.model = DISPLAY_MODEL_NONE; + return 1; +#endif +} diff --git a/arch/parisc/kernel/pa7300lc.c b/arch/parisc/kernel/pa7300lc.c new file mode 100644 index 000000000000..307e4b2fdae3 --- /dev/null +++ b/arch/parisc/kernel/pa7300lc.c @@ -0,0 +1,54 @@ +/* + * linux/arch/parisc/kernel/pa7300lc.c + * - PA7300LC-specific functions + * + * Copyright (C) 2000 Philipp Rumpf */ + +#include +#include +#include +#include +#include + +/* CPU register indices */ + +#define MIOC_STATUS 0xf040 +#define MIOC_CONTROL 0xf080 +#define MDERRADD 0xf0e0 +#define DMAERR 0xf0e8 +#define DIOERR 0xf0ec +#define HIDMAMEM 0xf0f4 + +/* read CPU Diagnose register index */ +static u32 diag_read(int index) +{ + return 0; +} + +/* this returns the HPA of the CPU it was called on */ +static u32 cpu_hpa(void) +{ + return 0xfffb0000; +} + +static void pa7300lc_lpmc(int code, struct pt_regs *regs) +{ + u32 hpa; + printk(KERN_WARNING "LPMC on CPU %d\n", smp_processor_id()); + + show_regs(regs); + + hpa = cpu_hpa(); + printk(KERN_WARNING + "MIOC_CONTROL %08x\n" "MIOC_STATUS %08x\n" + "MDERRADD %08x\n" "DMAERR %08x\n" + "DIOERR %08x\n" "HIDMAMEM %08x\n", + gsc_readl(hpa+MIOC_CONTROL), gsc_readl(hpa+MIOC_STATUS), + gsc_readl(hpa+MDERRADD), gsc_readl(hpa+DMAERR), + gsc_readl(hpa+DIOERR), gsc_readl(hpa+HIDMAMEM)); +} + +void pa7300lc_init(void) +{ + cpu_lpmc = pa7300lc_lpmc; +} diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c new file mode 100644 index 000000000000..2b4dbd74ca94 --- /dev/null +++ b/arch/parisc/kernel/parisc_ksyms.c @@ -0,0 +1,138 @@ +/* + * Architecture-specific kernel symbols + */ + +#include +#include +#include + +#include +EXPORT_SYMBOL_NOVERS(memscan); +EXPORT_SYMBOL_NOVERS(memset); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL_NOVERS(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strncmp); +EXPORT_SYMBOL(strncpy); +EXPORT_SYMBOL(strtok); + +#include +EXPORT_SYMBOL(hppa_dma_ops); + +#include +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); + +#include +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(boot_cpu_data); + +#ifdef CONFIG_SMP +EXPORT_SYMBOL(synchronize_irq); + +#include +EXPORT_SYMBOL(kernel_flag); + +#include +EXPORT_SYMBOL(__global_sti); +EXPORT_SYMBOL(__global_cli); +EXPORT_SYMBOL(__global_save_flags); +EXPORT_SYMBOL(__global_restore_flags); + +#endif + +#include +EXPORT_SYMBOL(lcopy_to_user); +EXPORT_SYMBOL(lcopy_from_user); + +/* Needed so insmod can set dp value */ + +extern int data_start; + +EXPORT_SYMBOL_NOVERS(data_start); + +#include +EXPORT_SYMBOL(_gsc_writeb); +EXPORT_SYMBOL(_gsc_writew); +EXPORT_SYMBOL(_gsc_writel); +EXPORT_SYMBOL(_gsc_readb); +EXPORT_SYMBOL(_gsc_readw); +EXPORT_SYMBOL(_gsc_readl); +EXPORT_SYMBOL(busdevice_alloc_irq); +EXPORT_SYMBOL(register_driver); +EXPORT_SYMBOL(gsc_alloc_irq); +EXPORT_SYMBOL(pdc_iodc_read); + +extern void $$divI(void); +extern void $$divU(void); +extern void $$remI(void); +extern void $$remU(void); +extern void $$mulI(void); +extern void $$mulU(void); +extern void $$divU_3(void); +extern void $$divU_5(void); +extern void $$divU_6(void); +extern void $$divU_9(void); +extern void $$divU_10(void); +extern void $$divU_12(void); +extern void $$divU_7(void); +extern void $$divU_14(void); +extern void $$divU_15(void); +extern void $$divI_3(void); +extern void $$divI_5(void); +extern void $$divI_6(void); +extern void $$divI_7(void); +extern void $$divI_9(void); +extern void $$divI_10(void); +extern void $$divI_12(void); +extern void $$divI_14(void); +extern void $$divI_15(void); + +EXPORT_SYMBOL_NOVERS($$divI); +EXPORT_SYMBOL_NOVERS($$divU); +EXPORT_SYMBOL_NOVERS($$remI); +EXPORT_SYMBOL_NOVERS($$remU); +EXPORT_SYMBOL_NOVERS($$mulI); +EXPORT_SYMBOL_NOVERS($$mulU); +EXPORT_SYMBOL_NOVERS($$divU_3); +EXPORT_SYMBOL_NOVERS($$divU_5); +EXPORT_SYMBOL_NOVERS($$divU_6); +EXPORT_SYMBOL_NOVERS($$divU_9); +EXPORT_SYMBOL_NOVERS($$divU_10); +EXPORT_SYMBOL_NOVERS($$divU_12); +EXPORT_SYMBOL_NOVERS($$divU_7); +EXPORT_SYMBOL_NOVERS($$divU_14); +EXPORT_SYMBOL_NOVERS($$divU_15); +EXPORT_SYMBOL_NOVERS($$divI_3); +EXPORT_SYMBOL_NOVERS($$divI_5); +EXPORT_SYMBOL_NOVERS($$divI_6); +EXPORT_SYMBOL_NOVERS($$divI_7); +EXPORT_SYMBOL_NOVERS($$divI_9); +EXPORT_SYMBOL_NOVERS($$divI_10); +EXPORT_SYMBOL_NOVERS($$divI_12); +EXPORT_SYMBOL_NOVERS($$divI_14); +EXPORT_SYMBOL_NOVERS($$divI_15); + +extern void __ashrdi3(void); + +EXPORT_SYMBOL_NOVERS(__ashrdi3); + +#ifdef __LP64__ +extern void __divdi3(void); +extern void __udivdi3(void); + +EXPORT_SYMBOL_NOVERS(__divdi3); +EXPORT_SYMBOL_NOVERS(__udivdi3); +#endif + +#ifndef __LP64__ +extern void $$dyncall(void); +EXPORT_SYMBOL_NOVERS($$dyncall); +#endif + diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c new file mode 100644 index 000000000000..88a9ddc775f2 --- /dev/null +++ b/arch/parisc/kernel/pci-dma.c @@ -0,0 +1,547 @@ +/* +** Dynamic DMA mapping support. +** See Documentation/DMA-mapping.txt for interface definitions. +** +** (c) Copyright 1999,2000 Hewlett-Packard Company +** (c) Copyright 2000 Grant Grundler +** (c) Copyright 2000 Philipp Rumpf +** (c) Copyright 2000 John Marvin +** +** This implementation is for PA-RISC platforms that do not support +** I/O TLBs (aka DMA address translation hardware). +** +** "leveraged" from 2.3.47: arch/ia64/kernel/pci-dma.c. +** (I assume it's from David Mosberger-Tang but there was no Copyright) +** +** AFAIK, all PA7100LC and PA7300LC platforms can use this code. +** All PA2.0 machines but V-class can alias xxx_alloc_consistent() +** to use regular cacheable memory. +** +** - ggg +*/ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include /* get_order */ +#include /* for DMA_CHUNK_SIZE */ + +#include + +static struct proc_dir_entry * proc_gsc_root = NULL; +static int pcxl_proc_info(char *buffer, char **start, off_t offset, int length); +static unsigned long pcxl_used_bytes = 0; +static unsigned long pcxl_used_pages = 0; + +extern unsigned long pcxl_dma_start; /* Start of pcxl dma mapping area */ +static spinlock_t pcxl_res_lock; +static char *pcxl_res_map; +static int pcxl_res_hint; +static int pcxl_res_size; + +#ifdef DEBUG_PCXL_RESOURCE +#define DBG_RES(x...) printk(x) +#else +#define DBG_RES(x...) +#endif + + +/* +** Dump a hex representation of the resource map. +*/ + +#ifdef DUMP_RESMAP +static +void dump_resmap(void) +{ + u_long *res_ptr = (unsigned long *)pcxl_res_map; + u_long i = 0; + + printk("res_map: "); + for(; i < (pcxl_res_size / sizeof(unsigned long)); ++i, ++res_ptr) + printk("%08lx ", *res_ptr); + + printk("\n"); +} +#else +static inline void dump_resmap(void) {;} +#endif + +static int pa11_dma_supported( struct pci_dev *dev, dma_addr_t mask) +{ + return 1; +} + +static inline int map_pte_uncached(pte_t * pte, + unsigned long vaddr, + unsigned long size, unsigned long *paddr_ptr) +{ + unsigned long end; + unsigned long orig_vaddr = vaddr; + + vaddr &= ~PMD_MASK; + end = vaddr + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + if (!pte_none(*pte)) + printk(KERN_ERR "map_pte_uncached: page already exists\n"); + set_pte(pte, __mk_pte(*paddr_ptr, PAGE_KERNEL_UNC)); + pdtlb_kernel(orig_vaddr); + vaddr += PAGE_SIZE; + orig_vaddr += PAGE_SIZE; + (*paddr_ptr) += PAGE_SIZE; + pte++; + } while (vaddr < end); + return 0; +} + +static inline int map_pmd_uncached(pmd_t * pmd, unsigned long vaddr, + unsigned long size, unsigned long *paddr_ptr) +{ + unsigned long end; + unsigned long orig_vaddr = vaddr; + + vaddr &= ~PGDIR_MASK; + end = vaddr + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + do { + pte_t * pte = pte_alloc_kernel(pmd, vaddr); + if (!pte) + return -ENOMEM; + if (map_pte_uncached(pte, orig_vaddr, end - vaddr, paddr_ptr)) + return -ENOMEM; + vaddr = (vaddr + PMD_SIZE) & PMD_MASK; + orig_vaddr += PMD_SIZE; + pmd++; + } while (vaddr < end); + return 0; +} + +static inline int map_uncached_pages(unsigned long vaddr, unsigned long size, + unsigned long paddr) +{ + pgd_t * dir; + unsigned long end = vaddr + size; + + dir = pgd_offset_k(vaddr); + do { + pmd_t *pmd; + + pmd = pmd_alloc_kernel(dir, vaddr); + if (!pmd) + return -ENOMEM; + if (map_pmd_uncached(pmd, vaddr, end - vaddr, &paddr)) + return -ENOMEM; + vaddr = vaddr + PGDIR_SIZE; + dir++; + } while (vaddr && (vaddr < end)); + return 0; +} + +static inline void unmap_uncached_pte(pmd_t * pmd, unsigned long vaddr, + unsigned long size) +{ + pte_t * pte; + unsigned long end; + unsigned long orig_vaddr = vaddr; + + if (pmd_none(*pmd)) + return; + if (pmd_bad(*pmd)) { + pmd_ERROR(*pmd); + pmd_clear(pmd); + return; + } + pte = pte_offset(pmd, vaddr); + vaddr &= ~PMD_MASK; + end = vaddr + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + pte_t page = *pte; + pte_clear(pte); + pdtlb_kernel(orig_vaddr); + vaddr += PAGE_SIZE; + orig_vaddr += PAGE_SIZE; + pte++; + if (pte_none(page) || pte_present(page)) + continue; + printk(KERN_CRIT "Whee.. Swapped out page in kernel page table\n"); + } while (vaddr < end); +} + +static inline void unmap_uncached_pmd(pgd_t * dir, unsigned long vaddr, + unsigned long size) +{ + pmd_t * pmd; + unsigned long end; + unsigned long orig_vaddr = vaddr; + + if (pgd_none(*dir)) + return; + if (pgd_bad(*dir)) { + pgd_ERROR(*dir); + pgd_clear(dir); + return; + } + pmd = pmd_offset(dir, vaddr); + vaddr &= ~PGDIR_MASK; + end = vaddr + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + do { + unmap_uncached_pte(pmd, orig_vaddr, end - vaddr); + vaddr = (vaddr + PMD_SIZE) & PMD_MASK; + orig_vaddr += PMD_SIZE; + pmd++; + } while (vaddr < end); +} + +static void unmap_uncached_pages(unsigned long vaddr, unsigned long size) +{ + pgd_t * dir; + unsigned long end = vaddr + size; + + dir = pgd_offset_k(vaddr); + do { + unmap_uncached_pmd(dir, vaddr, end - vaddr); + vaddr = vaddr + PGDIR_SIZE; + dir++; + } while (vaddr && (vaddr < end)); +} + +#define PCXL_SEARCH_LOOP(idx, mask, size) \ + for(; res_ptr < res_end; ++res_ptr) \ + { \ + if(0 == ((*res_ptr) & mask)) { \ + *res_ptr |= mask; \ + idx = (int)((u_long)res_ptr - (u_long)pcxl_res_map); \ + pcxl_res_hint = idx + (size >> 3); \ + goto resource_found; \ + } \ + } + +#define PCXL_FIND_FREE_MAPPING(idx, mask, size) { \ + u##size *res_ptr = (u##size *)&(pcxl_res_map[pcxl_res_hint & ~((size >> 3) - 1)]); \ + u##size *res_end = (u##size *)&pcxl_res_map[pcxl_res_size]; \ + PCXL_SEARCH_LOOP(idx, mask, size); \ + res_ptr = (u##size *)&pcxl_res_map[0]; \ + PCXL_SEARCH_LOOP(idx, mask, size); \ +} + +unsigned long +pcxl_alloc_range(size_t size) +{ + int res_idx; + u_long mask, flags; + unsigned int pages_needed = size >> PAGE_SHIFT; + + ASSERT(pages_needed); + ASSERT((pages_needed * PAGE_SIZE) < DMA_CHUNK_SIZE); + ASSERT(pages_needed < (BITS_PER_LONG - PAGE_SHIFT)); + + mask = (u_long) -1L; + mask >>= BITS_PER_LONG - pages_needed; + + DBG_RES("pcxl_alloc_range() size: %d pages_needed %d pages_mask 0x%08lx\n", + size, pages_needed, mask); + + spin_lock_irqsave(&pcxl_res_lock, flags); + + if(pages_needed <= 8) { + PCXL_FIND_FREE_MAPPING(res_idx, mask, 8); + } else if(pages_needed <= 16) { + PCXL_FIND_FREE_MAPPING(res_idx, mask, 16); + } else if(pages_needed <= 32) { + PCXL_FIND_FREE_MAPPING(res_idx, mask, 32); + } else { + panic(__FILE__ ": pcxl_alloc_range() Too many pages to map.\n"); + } + + dump_resmap(); + panic(__FILE__ ": pcxl_alloc_range() out of dma mapping resources\n"); + +resource_found: + + DBG_RES("pcxl_alloc_range() res_idx %d mask 0x%08lx res_hint: %d\n", + res_idx, mask, pcxl_res_hint); + + pcxl_used_pages += pages_needed; + pcxl_used_bytes += ((pages_needed >> 3) ? (pages_needed >> 3) : 1); + + spin_unlock_irqrestore(&pcxl_res_lock, flags); + + dump_resmap(); + + /* + ** return the corresponding vaddr in the pcxl dma map + */ + return (pcxl_dma_start + (res_idx << (PAGE_SHIFT + 3))); +} + +#define PCXL_FREE_MAPPINGS(idx, m, size) \ + u##size *res_ptr = (u##size *)&(pcxl_res_map[(idx) + (((size >> 3) - 1) & (~((size >> 3) - 1)))]); \ + ASSERT((*res_ptr & m) == m); \ + *res_ptr &= ~m; + +/* +** clear bits in the pcxl resource map +*/ +static void +pcxl_free_range(unsigned long vaddr, size_t size) +{ + u_long mask, flags; + unsigned int res_idx = (vaddr - pcxl_dma_start) >> (PAGE_SHIFT + 3); + unsigned int pages_mapped = size >> PAGE_SHIFT; + + ASSERT(pages_mapped); + ASSERT((pages_mapped * PAGE_SIZE) < DMA_CHUNK_SIZE); + ASSERT(pages_mapped < (BITS_PER_LONG - PAGE_SHIFT)); + + mask = (u_long) -1L; + mask >>= BITS_PER_LONG - pages_mapped; + + DBG_RES("pcxl_free_range() res_idx: %d size: %d pages_mapped %d mask 0x%08lx\n", + res_idx, size, pages_mapped, mask); + + spin_lock_irqsave(&pcxl_res_lock, flags); + + if(pages_mapped <= 8) { + PCXL_FREE_MAPPINGS(res_idx, mask, 8); + } else if(pages_mapped <= 16) { + PCXL_FREE_MAPPINGS(res_idx, mask, 16); + } else if(pages_mapped <= 32) { + PCXL_FREE_MAPPINGS(res_idx, mask, 32); + } else { + panic(__FILE__ ": pcxl_free_range() Too many pages to unmap.\n"); + } + + pcxl_used_pages -= (pages_mapped ? pages_mapped : 1); + pcxl_used_bytes -= ((pages_mapped >> 3) ? (pages_mapped >> 3) : 1); + + spin_unlock_irqrestore(&pcxl_res_lock, flags); + + dump_resmap(); +} + +static int __init +pcxl_dma_init(void) +{ + if (pcxl_dma_start == 0) + return 0; + + spin_lock_init(&pcxl_res_lock); + pcxl_res_size = PCXL_DMA_MAP_SIZE >> (PAGE_SHIFT + 3); + pcxl_res_hint = 0; + pcxl_res_map = (char *)__get_free_pages(GFP_KERNEL, + get_order(pcxl_res_size)); + + proc_gsc_root = proc_mkdir("gsc", 0); + create_proc_info_entry("dino", 0, proc_gsc_root, pcxl_proc_info); + return 0; +} + +__initcall(pcxl_dma_init); + +static void * pa11_dma_alloc_consistent (struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) +{ + unsigned long vaddr; + unsigned long paddr; + int order; + + order = get_order(size); + size = 1 << (order + PAGE_SHIFT); + vaddr = pcxl_alloc_range(size); + paddr = __get_free_pages(GFP_ATOMIC, order); + flush_kernel_dcache_range(paddr, size); + paddr = __pa(paddr); + map_uncached_pages(vaddr, size, paddr); + *dma_handle = (dma_addr_t) paddr; + +#if 0 +/* This probably isn't needed to support EISA cards. +** ISA cards will certainly only support 24-bit DMA addressing. +** Not clear if we can, want, or need to support ISA. +*/ + if (!hwdev || hwdev->dma_mask != 0xffffffff) + gfp |= GFP_DMA; +#endif + return (void *)vaddr; +} + +static void pa11_dma_free_consistent (struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) +{ + int order; + + order = get_order(size); + size = 1 << (order + PAGE_SHIFT); + unmap_uncached_pages((unsigned long)vaddr, size); + pcxl_free_range((unsigned long)vaddr, size); + free_pages((unsigned long)__va(dma_handle), order); +} + +static dma_addr_t pa11_dma_map_single(struct pci_dev *dev, void *addr, size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) { + printk(KERN_ERR "pa11_dma_map_single(PCI_DMA_NONE) called by %p\n", __builtin_return_address(0)); + BUG(); + } + + flush_kernel_dcache_range((unsigned long) addr, size); + return virt_to_phys(addr); +} + +static void pa11_dma_unmap_single(struct pci_dev *dev, dma_addr_t dma_handle, size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) { + printk(KERN_ERR "pa11_dma_unmap_single(PCI_DMA_NONE) called by %p\n", __builtin_return_address(0)); + BUG(); + } + + if (direction == PCI_DMA_TODEVICE) + return; + + /* + * For PCI_DMA_FROMDEVICE this flush is not necessary for the + * simple map/unmap case. However, it IS necessary if if + * pci_dma_sync_single has been called and the buffer reused. + */ + + flush_kernel_dcache_range((unsigned long) phys_to_virt(dma_handle), size); + return; +} + +static int pa11_dma_map_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction) +{ + int i; + + if (direction == PCI_DMA_NONE) + BUG(); + + for (i = 0; i < nents; i++, sglist++ ) { + sg_dma_address(sglist) = (dma_addr_t) virt_to_phys(sglist->address); + sg_dma_len(sglist) = sglist->length; + flush_kernel_dcache_range((unsigned long)sglist->address, + sglist->length); + } + return nents; +} + +static void pa11_dma_unmap_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction) +{ + int i; + + if (direction == PCI_DMA_NONE) + BUG(); + + if (direction == PCI_DMA_TODEVICE) + return; + + /* once we do combining we'll need to use phys_to_virt(sg_dma_address(sglist)) */ + + for (i = 0; i < nents; i++, sglist++ ) + flush_kernel_dcache_range((unsigned long) sglist->address, sglist->length); + return; +} + +static void pa11_dma_sync_single(struct pci_dev *dev, dma_addr_t dma_handle, size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + + flush_kernel_dcache_range((unsigned long) phys_to_virt(dma_handle), size); +} + +static void pa11_dma_sync_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction) +{ + int i; + + /* once we do combining we'll need to use phys_to_virt(sg_dma_address(sglist)) */ + + for (i = 0; i < nents; i++, sglist++ ) + flush_kernel_dcache_range((unsigned long) sglist->address, sglist->length); +} + +struct pci_dma_ops pcxl_dma_ops = { + pa11_dma_supported, /* dma_support */ + pa11_dma_alloc_consistent, + pa11_dma_free_consistent, + pa11_dma_map_single, /* map_single */ + pa11_dma_unmap_single, /* unmap_single */ + pa11_dma_map_sg, /* map_sg */ + pa11_dma_unmap_sg, /* unmap_sg */ + pa11_dma_sync_single, /* dma_sync_single */ + pa11_dma_sync_sg /* dma_sync_sg */ +}; + +static void *fail_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + return NULL; +} + +static void fail_free_consistent(struct pci_dev *dev, size_t size, + void *vaddr, dma_addr_t iova) +{ + return; +} + +struct pci_dma_ops pcx_dma_ops = { + pa11_dma_supported, /* dma_support */ + fail_alloc_consistent, + fail_free_consistent, + pa11_dma_map_single, /* map_single */ + pa11_dma_unmap_single, /* unmap_single */ + pa11_dma_map_sg, /* map_sg */ + pa11_dma_unmap_sg, /* unmap_sg */ + pa11_dma_sync_single, /* dma_sync_single */ + pa11_dma_sync_sg /* dma_sync_sg */ +}; + +struct pci_dma_ops *hppa_dma_ops; + +static int pcxl_proc_info(char *buf, char **start, off_t offset, int len) +{ + u_long i = 0; + unsigned long *res_ptr = (u_long *)pcxl_res_map; + unsigned long total_pages = pcxl_res_size << 3; /* 8 bits per byte */ + + sprintf(buf, "\nDMA Mapping Area size : %d bytes (%d pages)\n", + PCXL_DMA_MAP_SIZE, + (pcxl_res_size << 3) ); /* 1 bit per page */ + + sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", + buf, pcxl_res_size, pcxl_res_size << 3); /* 8 bits per byte */ + + strcat(buf, " total: free: used: % used:\n"); + sprintf(buf, "%sblocks %8d %8ld %8ld %8ld%%\n", buf, pcxl_res_size, + pcxl_res_size - pcxl_used_bytes, pcxl_used_bytes, + (pcxl_used_bytes * 100) / pcxl_res_size); + + sprintf(buf, "%spages %8ld %8ld %8ld %8ld%%\n", buf, total_pages, + total_pages - pcxl_used_pages, pcxl_used_pages, + (pcxl_used_pages * 100 / total_pages)); + + strcat(buf, "\nResource bitmap:"); + + for(; i < (pcxl_res_size / sizeof(u_long)); ++i, ++res_ptr) { + if ((i & 7) == 0) + strcat(buf,"\n "); + sprintf(buf, "%s %08lx", buf, *res_ptr); + } + strcat(buf, "\n"); + return strlen(buf); +} + diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c new file mode 100644 index 000000000000..ad9d9d41fce3 --- /dev/null +++ b/arch/parisc/kernel/pci.c @@ -0,0 +1,534 @@ +/* $Id: pci.c,v 1.6 2000/01/29 00:12:05 grundler Exp $ + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1997, 1998 Ralf Baechle + * Copyright (C) 1999 SuSE GmbH + * Copyright (C) 1999 Hewlett-Packard Company + * Copyright (C) 1999, 2000 Grant Grundler + */ +#include +#include +#include /* for __init and __devinit */ +#include +#include +#include /* for memcpy() */ + +#include + +#ifdef CONFIG_PCI + +#undef DEBUG_RESOURCES + +#ifdef DEBUG_RESOURCES +#define DBG_RES(x...) printk(x) +#else +#define DBG_RES(x...) +#endif + +/* To be used as: mdelay(pci_post_reset_delay); +** +** post_reset is the time the kernel should stall to prevent anyone from +** accessing the PCI bus once #RESET is de-asserted. +** PCI spec somewhere says 1 second but with multi-PCI bus systems, +** this makes the boot time much longer than necessary. +** 20ms seems to work for all the HP PCI implementations to date. +*/ +int pci_post_reset_delay = 50; + +struct pci_port_ops *pci_port; +struct pci_bios_ops *pci_bios; + +struct pci_hba_data *hba_list = NULL; +int hba_count = 0; + +/* +** parisc_pci_hba used by pci_port->in/out() ops to lookup bus data. +*/ +#define PCI_HBA_MAX 32 +static struct pci_hba_data *parisc_pci_hba[PCI_HBA_MAX]; + + +/******************************************************************** +** +** I/O port space support +** +*********************************************************************/ + +#define PCI_PORT_HBA(a) ((a)>>16) +#define PCI_PORT_ADDR(a) ((a) & 0xffffUL) + +/* KLUGE : inb needs to be defined differently for PCI devices than +** for other bus interfaces. Doing this at runtime sucks but is the +** only way one driver binary can support devices on different bus types. +** +*/ + +#define PCI_PORT_IN(type, size) \ +u##size in##type (int addr) \ +{ \ + int b = PCI_PORT_HBA(addr); \ + u##size d = (u##size) -1; \ + ASSERT(pci_port); /* make sure services are defined */ \ + ASSERT(parisc_pci_hba[b]); /* make sure ioaddr are "fixed up" */ \ + if (parisc_pci_hba[b] == NULL) { \ + printk(KERN_WARNING "\nPCI Host Bus Adapter %d not registered. in" #size "(0x%x) returning -1\n", b, addr); \ + } else { \ + d = pci_port->in##type(parisc_pci_hba[b], PCI_PORT_ADDR(addr)); \ + } \ + return d; \ +} + +PCI_PORT_IN(b, 8) +PCI_PORT_IN(w, 16) +PCI_PORT_IN(l, 32) + + +#define PCI_PORT_OUT(type, size) \ +void out##type (u##size d, int addr) \ +{ \ + int b = PCI_PORT_HBA(addr); \ + ASSERT(pci_port); \ + pci_port->out##type(parisc_pci_hba[b], PCI_PORT_ADDR(addr), d); \ +} + +PCI_PORT_OUT(b, 8) +PCI_PORT_OUT(w, 16) +PCI_PORT_OUT(l, 32) + + + +/* + * BIOS32 replacement. + */ +void pcibios_init(void) +{ + ASSERT(pci_bios != NULL); + + if (pci_bios) + { + if (pci_bios->init) { + (*pci_bios->init)(); + } else { + printk(KERN_WARNING "pci_bios != NULL but init() is!\n"); + } + } +} + + +/* Called from pci_do_scan_bus() *after* walking a bus but before walking PPBs. */ +void pcibios_fixup_bus(struct pci_bus *bus) +{ + ASSERT(pci_bios != NULL); + + /* If this is a bridge, get the current bases */ + if (bus->self) { + pci_read_bridge_bases(bus); + } + + if (pci_bios) { + if (pci_bios->fixup_bus) { + (*pci_bios->fixup_bus)(bus); + } else { + printk(KERN_WARNING "pci_bios != NULL but fixup_bus() is!\n"); + } + } +} + + +char *pcibios_setup(char *str) +{ + return str; +} + +#endif /* defined(CONFIG_PCI) */ + + + +/* ------------------------------------------------------------------- +** linux-2.4: NEW STUFF +** -------------------- +*/ + +/* +** Used in drivers/pci/quirks.c +*/ +struct pci_fixup pcibios_fixups[] = { {0} }; + + +/* +** called by drivers/pci/setup.c:pdev_fixup_irq() +*/ +void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) +{ +/* +** updates IRQ_LINE cfg register to reflect PCI-PCI bridge skewing. +** +** Calling path for Alpha is: +** alpha/kernel/pci.c:common_init_pci(swizzle_func, pci_map_irq_func ) +** drivers/pci/setup.c:pci_fixup_irqs() +** drivers/pci/setup.c:pci_fixup_irq() (for each PCI device) +** invoke swizzle and map functions +** alpha/kernel/pci.c:pcibios_update_irq() +** +** Don't need this for PA legacy PDC systems. +** +** On PAT PDC systems, We only support one "swizzle" for any number +** of PCI-PCI bridges deep. That's how bit3 PCI expansion chassis +** are implemented. The IRQ lines are "skewed" for all devices but +** *NOT* routed through the PCI-PCI bridge. Ie any device "0" will +** share an IRQ line. Legacy PDC is expecting this IRQ line routing +** as well. +** +** Unfortunately, PCI spec allows the IRQ lines to be routed +** around the PCI bridge as long as the IRQ lines are skewed +** based on the device number...... +** +** Lastly, dino.c might be able to use pci_fixup_irq() to +** support RS-232 and PS/2 children. Not sure how but it's +** something to think about. +*/ +} + + +/* ------------------------------------ +** +** Program one BAR in PCI config space. +** +** ------------------------------------ +** PAT PDC systems need this routine. PA legacy PDC does not. +** +** Used by alpha/arm: +** alpha/kernel/pci.c:common_init_pci() +** (or arm/kernel/pci.c:pcibios_init()) +** drivers/pci/setup.c:pci_assign_unassigned_resources() +** drivers/pci/setup.c:pdev_assign_unassigned_resources() +** arch//kernel/pci.c:pcibios_update_resource() +** +** When BAR's are configured by linux, this routine +** will update configuration space with the "normalized" +** address. "root" indicates where the range starts and res +** is some portion of that range. +** +** For all PA-RISC systems except V-class, root->start would be zero. +** +** PAT PDC can tell us which MMIO ranges are available or already in use. +** I/O port space and such are not memory mapped anyway for PA-Risc. +*/ +void __devinit +pcibios_update_resource( + struct pci_dev *dev, + struct resource *root, + struct resource *res, + int barnum + ) +{ + int where; + u32 barval = 0; + + DBG_RES("pcibios_update_resource(%s, ..., %d) [%lx,%lx]/%x\n", + dev->slot_name, + barnum, res->start, res->end, (int) res->flags); + + if (barnum >= PCI_BRIDGE_RESOURCES) { + /* handled in pbus_set_ranges_data() */ + return; + } + + if (barnum == PCI_ROM_RESOURCE) { + where = PCI_ROM_ADDRESS; + } else { + /* 0-5 standard PCI "regions" */ + where = PCI_BASE_ADDRESS_0 + (barnum * 4); + } + + if (res->flags & IORESOURCE_IO) { + barval = PCI_PORT_ADDR(res->start); + } else if (res->flags & IORESOURCE_MEM) { + /* This should work for VCLASS too */ + barval = res->start & 0xffffffffUL; + } else { + panic("pcibios_update_resource() WTF? flags not IO or MEM"); + } + + pci_write_config_dword(dev, where, barval); + +/* XXX FIXME - Elroy does support 64-bit (dual cycle) addressing. +** But at least one device (Symbios 53c896) which has 64-bit BAR +** doesn't actually work right with dual cycle addresses. +** So ignore the whole mess for now. +*/ + + if ((res->flags & (PCI_BASE_ADDRESS_SPACE + | PCI_BASE_ADDRESS_MEM_TYPE_MASK)) + == (PCI_BASE_ADDRESS_SPACE_MEMORY + | PCI_BASE_ADDRESS_MEM_TYPE_64)) { + pci_write_config_dword(dev, where+4, 0); + printk(KERN_WARNING "PCI: dev %s type 64-bit\n", dev->name); + } +} + +/* +** Called by pci_set_master() - a driver interface. +** +** Legacy PDC guarantees to set: +** Map Memory BAR's into PA IO space. +** Map Expansion ROM BAR into one common PA IO space per bus. +** Map IO BAR's into PCI IO space. +** Command (see below) +** Cache Line Size +** Latency Timer +** Interrupt Line +** PPB: secondary latency timer, io/mmio base/limit, +** bus numbers, bridge control +** +*/ +void +pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + if (lat >= 16) return; + + /* + ** HP generally has fewer devices on the bus than other architectures. + */ + printk("PCIBIOS: Setting latency timer of %s to 128\n", dev->slot_name); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x80); +} + + +/* +** called by drivers/pci/setup-res.c:pbus_set_ranges(). +*/ +void pcibios_fixup_pbus_ranges( + struct pci_bus *bus, + struct pbus_set_ranges_data *ranges + ) +{ + /* + ** I/O space may see busnumbers here. Something + ** in the form of 0xbbxxxx where bb is the bus num + ** and xxxx is the I/O port space address. + ** Remaining address translation are done in the + ** PCI Host adapter specific code - ie dino_out8. + */ + ranges->io_start = PCI_PORT_ADDR(ranges->io_start); + ranges->io_end = PCI_PORT_ADDR(ranges->io_end); + + DBG_RES("pcibios_fixup_pbus_ranges(%02x, [%lx,%lx %lx,%lx])\n", bus->number, + ranges->io_start, ranges->io_end, + ranges->mem_start, ranges->mem_end); +} + +#define MAX(val1, val2) ((val1) > (val2) ? (val1) : (val2)) + + +/* +** pcibios align resources() is called everytime generic PCI code +** wants to generate a new address. The process of looking for +** an available address, each candidate is first "aligned" and +** then checked if the resource is available until a match is found. +** +** Since we are just checking candidates, don't use any fields other +** than res->start. +*/ +void __devinit +pcibios_align_resource(void *data, struct resource *res, unsigned long size) +{ + unsigned long mask, align; + + DBG_RES("pcibios_align_resource(%s, (%p) [%lx,%lx]/%x, 0x%lx)\n", + ((struct pci_dev *) data)->slot_name, + res->parent, res->start, res->end, (int) res->flags, size); + + /* has resource already been aligned/assigned? */ + if (res->parent) + return; + + /* If it's not IO, then it's gotta be MEM */ + align = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; + + /* Align to largest of MIN or input size */ + mask = MAX(size, align) - 1; + res->start += mask; + res->start &= ~mask; + + /* + ** WARNING : caller is expected to update "end" field. + ** We can't since it might really represent the *size*. + ** The difference is "end = start + size" vs "end += size". + */ +} + + +#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +void __devinit +pcibios_size_bridge(struct pci_bus *bus, struct pbus_set_ranges_data *outer) +{ + struct pbus_set_ranges_data inner; + struct pci_dev *dev; + struct pci_dev *bridge = bus->self; + struct list_head *ln; + + /* set reasonable default "window" for pcibios_align_resource */ + inner.io_start = inner.io_end = 0; + inner.mem_start = inner.mem_end = 0; + + /* Collect information about how our direct children are layed out. */ + for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) { + int i; + dev = pci_dev_b(ln); + + /* Skip bridges here - we'll catch them below */ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) + continue; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource res; + unsigned long size; + + if (dev->resource[i].flags == 0) + continue; + + memcpy(&res, &dev->resource[i], sizeof(res)); + size = res.end - res.start + 1; + + if (res.flags & IORESOURCE_IO) { + res.start = inner.io_end; + pcibios_align_resource(dev, &res, size); + inner.io_end += res.start + size; + } else if (res.flags & IORESOURCE_MEM) { + res.start = inner.mem_end; + pcibios_align_resource(dev, &res, size); + inner.mem_end = res.start + size; + } + + DBG_RES(" %s inner size %lx/%x IO %lx MEM %lx\n", + dev->slot_name, + size, res.flags, inner.io_end, inner.mem_end); + } + } + + /* And for all of the subordinate busses. */ + for (ln=bus->children.next; ln != &bus->children; ln=ln->next) + pcibios_size_bridge(pci_bus_b(ln), &inner); + + /* turn the ending locations into sizes (subtract start) */ + inner.io_end -= inner.io_start - 1; + inner.mem_end -= inner.mem_start - 1; + + /* Align the sizes up by bridge rules */ + inner.io_end = ROUND_UP(inner.io_end, 4*1024) - 1; + inner.mem_end = ROUND_UP(inner.mem_end, 1*1024*1024) - 1; + + /* PPB - PCI bridge Device will normaller also have "outer" != NULL. */ + if (bridge) { + /* Adjust the bus' allocation requirements */ + /* PPB's pci device Bridge resources */ + + bus->resource[0] = &bridge->resource[PCI_BRIDGE_RESOURCES]; + bus->resource[1] = &bridge->resource[PCI_BRIDGE_RESOURCES + 1]; + + bus->resource[0]->start = bus->resource[1]->start = 0; + bus->resource[0]->parent= bus->resource[1]->parent = NULL; + + bus->resource[0]->end = inner.io_end; + bus->resource[0]->flags = IORESOURCE_IO; + + bus->resource[1]->end = inner.mem_end; + bus->resource[1]->flags = IORESOURCE_MEM; + } + + /* adjust parent's resource requirements */ + if (outer) { + outer->io_end = ROUND_UP(outer->io_end, 4*1024); + outer->io_end += inner.io_end; + + outer->mem_end = ROUND_UP(outer->mem_end, 1*1024*1024); + outer->mem_end += inner.mem_end; + } +} + +#undef ROUND_UP + + +int __devinit +pcibios_enable_device(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + + /* + ** The various platform PDC's (aka "BIOS" for PCs) don't + ** enable all the same bits. We just make sure they are here. + */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + + /* + ** See if any resources have been allocated + */ + for (idx=0; idx<6; idx++) { + struct resource *r = &dev->resource[idx]; + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + + /* + ** System error and Parity Error reporting are enabled by default. + ** Devices that do NOT want those behaviors should clear them + ** (eg PCI graphics, possibly networking). + ** Interfaces like SCSI certainly should not. We want the + ** system to crash if a system or parity error is detected. + ** At least until the device driver can recover from such an error. + */ + cmd |= (PCI_COMMAND_SERR | PCI_COMMAND_PARITY); + + if (cmd != old_cmd) { + printk("PCIBIOS: Enabling device %s (%04x -> %04x)\n", + dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + + return 0; +} + + +void __devinit +pcibios_assign_unassigned_resources(struct pci_bus *bus) +{ + struct list_head *ln; + + for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) + { + pdev_assign_unassigned_resources(pci_dev_b(ln)); + } + + /* And for all of the sub-busses. */ + for (ln=bus->children.next; ln != &bus->children; ln=ln->next) + pcibios_assign_unassigned_resources(pci_bus_b(ln)); + +} + +/* +** PARISC specific (unfortunately) +*/ +void pcibios_register_hba(struct pci_hba_data *hba) +{ + hba->next = hba_list; + hba_list = hba; + + ASSERT(hba_count < PCI_HBA_MAX); + + /* + ** pci_port->in/out() uses parisc_pci_hba to lookup parameter. + */ + parisc_pci_hba[hba_count] = hba; + hba->hba_num = hba_count++; +} diff --git a/arch/parisc/kernel/pdc.c b/arch/parisc/kernel/pdc.c new file mode 100644 index 000000000000..4fa4327c0f1d --- /dev/null +++ b/arch/parisc/kernel/pdc.c @@ -0,0 +1,217 @@ +/* arch/parisc/kernel/pdc.c - safe pdc access routines + * + * Copyright 1999 SuSE GmbH Nuernberg (Philipp Rumpf, prumpf@tux.org) + * portions Copyright 1999 The Puffin Group, (Alex deVries, David Kennedy) + * + * only these routines should be used out of the real kernel (i.e. everything + * using virtual addresses) for obvious reasons */ + +/* I think it would be in everyone's best interest to follow this + * guidelines when writing PDC wrappers: + * + * - the name of the pdc wrapper should match one of the macros + * used for the first two arguments + * - don't use caps for random parts of the name + * - use ASSERT_ALIGN to ensure the aligment of the arguments is + * correct + * - use __pa() to convert virtual (kernel) pointers to physical + * ones. + * - the name of the struct used for pdc return values should equal + * one of the macros used for the first two arguments to the + * corresponding PDC call + * - keep the order of arguments + * - don't be smart (setting trailing NUL bytes for strings, return + * something useful even if the call failed) unless you are sure + * it's not going to affect functionality or performance + * + * Example: + * int pdc_cache_info(struct pdc_cache_info *cache_info ) + * { + * ASSERT_ALIGN(cache_info, 8); + * + * return mem_pdc_call(PDC_CACHE,PDC_CACHE_INFO,__pa(cache_info),0); + * } + * prumpf 991016 + */ + +#include +#include + +#include +#include +#include +#include + + +#define ASSERT_ALIGN(ptr, align) \ + do { if(((unsigned long)(ptr)) & (align-1)) { \ + printk("PDC: %s:%d %s() called with " \ + "unaligned argument from %p", __FILE__, __LINE__, \ + __FUNCTION__, __builtin_return_address(0)); \ + \ + return -1; \ + } } while(0) + +/* verify address can be accessed without an HPMC */ +int pdc_add_valid(void *address) +{ + ASSERT_ALIGN(address, 4); + + return mem_pdc_call(PDC_ADD_VALID, PDC_ADD_VALID_VERIFY, (unsigned long)address); +} + +#if 0 +int pdc_chassis_warn(struct pdc_chassis_warn *address) +{ + ASSERT_ALIGN(address, 4); + + return mem_pdc_call(PDC_CHASSIS, PDC_CHASSIS_WARN, __pa(address), 0); +} +#endif + +int pdc_chassis_disp(unsigned long disp) +{ + return mem_pdc_call(PDC_CHASSIS, PDC_CHASSIS_DISP, disp); +} + +int pdc_chassis_info(void *pdc_result, void *chassis_info, unsigned long len) +{ + ASSERT_ALIGN(pdc_result, 4); + ASSERT_ALIGN(chassis_info, 4); + return mem_pdc_call(PDC_CHASSIS,PDC_RETURN_CHASSIS_INFO, + __pa(pdc_result), __pa(chassis_info), len); +} + +int pdc_hpa_processor(void *address) +{ + /* We're using 0 for the last parameter just to make sure. + It's actually HVERSION dependant. And remember, life is + hard without a backspace. */ + ASSERT_ALIGN(address, 4); + + return mem_pdc_call(PDC_HPA, PDC_HPA_PROCESSOR, __pa(address),0); +} + +#if 0 +int pdc_hpa_modules(void *address) +{ + return mem_pdc_call(PDC_HPA, PDC_HPA_MODULES, address); +} +#endif + +int pdc_iodc_read(void *address, void * hpa, unsigned int index, + void * iodc_data, unsigned int iodc_data_size) +{ + ASSERT_ALIGN(address, 4); + ASSERT_ALIGN(iodc_data, 8); + return mem_pdc_call(PDC_IODC, PDC_IODC_READ, + __pa(address), hpa, index, __pa(iodc_data), iodc_data_size); +} + + +int pdc_system_map_find_mods(void *pdc_mod_info, + void *mod_path, int index) +{ + return mem_pdc_call(PDC_SYSTEM_MAP, PDC_FIND_MODULE, + __pa(pdc_mod_info), __pa(mod_path), (long)index); +} + + +int pdc_model_info(struct pdc_model *model) { + ASSERT_ALIGN(model, 8); + return mem_pdc_call(PDC_MODEL,PDC_MODEL_INFO,__pa(model),0); +} + +/* get system model name from PDC ROM (e.g. 9000/715 or 9000/778/B160L) */ +int pdc_model_sysmodel(char * name) +{ + struct pdc_model_sysmodel sys_model; + int retval; + + ASSERT_ALIGN(&sys_model, 8); + ASSERT_ALIGN(name, 4); + + sys_model.mod_len = 0; + retval = mem_pdc_call(PDC_MODEL,PDC_MODEL_SYSMODEL,__pa(&sys_model), + OS_ID_HPUX,__pa(name)); + + if (retval == PDC_RET_OK) + name[sys_model.mod_len] = '\0'; /* add trailing '\0' */ + else + name[0] = 0; + + return retval; +} + +/* id: 0 = cpu revision, 1 = boot-rom-version */ +int pdc_model_versions(struct pdc_model_cpuid *cpu_id, int id) { + return mem_pdc_call(PDC_MODEL,PDC_MODEL_VERSIONS,__pa(cpu_id),id); +} + +int pdc_model_cpuid(struct pdc_model_cpuid *cpu_id) { + cpu_id->cpuid = 0; /* preset zero (call maybe not implemented!) */ + return mem_pdc_call(PDC_MODEL,6,__pa(cpu_id),0); /* 6="return CPU ID" */ +} + +int pdc_cache_info(struct pdc_cache_info *cache_info) { + ASSERT_ALIGN(cache_info, 8); + + return mem_pdc_call(PDC_CACHE,PDC_CACHE_INFO,__pa(cache_info),0); +} + +#ifndef __LP64__ +int pdc_btlb_info( struct pdc_btlb_info *btlb ) { + int status; + status = mem_pdc_call(PDC_BLOCK_TLB,PDC_BTLB_INFO,__pa(btlb),0); + if (status<0) btlb->max_size = 0; + return status; +} + +int pdc_mem_map_hpa(void *r_addr, void *mod_path) { + return mem_pdc_call(PDC_MEM_MAP,PDC_MEM_MAP_HPA, + __pa(r_addr),__pa(mod_path)); +} + +int pdc_lan_station_id(char *lan_addr, void *net_hpa) { + struct pdc_lan_station_id id; + unsigned char *addr; + + if (mem_pdc_call(PDC_LAN_STATION_ID, PDC_LAN_STATION_ID_READ, + __pa(&id), net_hpa) < 0) + addr = 0; /* FIXME: else read MAC from NVRAM */ + else + addr = id.addr; + if (addr) + memmove( lan_addr, addr, PDC_LAN_STATION_ID_SIZE); + else + memset( lan_addr, 0, PDC_LAN_STATION_ID_SIZE); + return (addr != 0); +} +#endif + + +/* Similar to PDC_PAT stuff in pdcpat.c - but added for Forte/Allegro boxes */ +int pdc_pci_irt_size(void *r_addr, void *hpa) +{ + return mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL_SIZE, + __pa(r_addr), hpa); + +} + +int pdc_pci_irt(void *r_addr, void *hpa, void *tbl) +{ + return mem_pdc_call(PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL, + __pa(r_addr), hpa, __pa(tbl)); +} + +/* access the TOD clock */ +int pdc_tod_read(struct pdc_tod *tod) +{ + ASSERT_ALIGN(tod, 8); + return mem_pdc_call(PDC_TOD, PDC_TOD_READ, __pa(tod), 0); +} + +int pdc_tod_set(unsigned long sec, unsigned long usec) +{ + return mem_pdc_call(PDC_TOD, PDC_TOD_WRITE, sec, usec); +} diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c new file mode 100644 index 000000000000..f2d45862bfd4 --- /dev/null +++ b/arch/parisc/kernel/pdc_cons.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for iodc_call() proto and friends */ +#include + +static int __attribute__((aligned(8))) iodc_retbuf[32]; +static char __attribute__((aligned(64))) iodc_dbuf[4096]; + +/* + * pdc_putc: + * Console character print using IODC. + * + * Note that only these special chars are architected for console IODC io: + * BEL, BS, CR, and LF. Others are passed through. + * Since the HP console requires CR+LF to perform a 'newline', we translate + * "\n" to "\r\n". + */ + +static int posx; /* for simple TAB-Simulation... */ + +/* XXX Should we spinlock posx usage */ + +void pdc_putc(unsigned char c) +{ + unsigned int n; + unsigned long flags; + + switch (c) { + case '\n': + iodc_dbuf[0] = '\r'; + iodc_dbuf[1] = '\n'; + n = 2; + posx = 0; + break; + case '\t': + pdc_putc(' '); + while (posx & 7) /* expand TAB */ + pdc_putc(' '); + return; /* return since IODC can't handle this */ + case '\b': + posx-=2; /* BS */ + default: + iodc_dbuf[0] = c; + n = 1; + posx++; + break; + } + { + real32_call(PAGE0->mem_cons.iodc_io, + (unsigned long)PAGE0->mem_cons.hpa, ENTRY_IO_COUT, + PAGE0->mem_cons.spa, __pa(PAGE0->mem_cons.dp.layers), + __pa(iodc_retbuf), 0, __pa(iodc_dbuf), n, 0); + } +} + +static void pdc_console_write(struct console *co, const char *s, unsigned count) +{ + while(count--) + pdc_putc(*s++); +} + +int pdc_console_wait_key(struct console *co) +{ + int ch = 'X'; + int status; + + /* Bail if no console input device. */ + if (!PAGE0->mem_kbd.iodc_io) + return 0; + + /* wait for a keyboard (rs232)-input */ + do { + unsigned long flags; + + save_flags(flags); + cli(); + status = real32_call(PAGE0->mem_kbd.iodc_io, + (unsigned long)PAGE0->mem_kbd.hpa, ENTRY_IO_CIN, + PAGE0->mem_kbd.spa, __pa(PAGE0->mem_kbd.dp.layers), + __pa(iodc_retbuf), 0, __pa(iodc_dbuf), 1, 0); + restore_flags(flags); + ch = *iodc_dbuf; /* save the character directly to ch */ + } while (*iodc_retbuf == 0); /* wait for a key */ + return ch; +} + +int pdc_getc(void) +{ + return pdc_console_wait_key(NULL); +} + +static int pdc_console_setup(struct console *co, char *options) +{ + return 0; +} + +static struct console pdc_cons = { + name: "ttyB", + write: pdc_console_write, + read: NULL, + device: NULL, + wait_key: pdc_console_wait_key, + unblank: NULL, + setup: pdc_console_setup, + flags: CON_PRINTBUFFER|CON_ENABLED, // |CON_CONSDEV, + index: -1, +}; + +static int pdc_console_initialized; + +void pdc_console_init(void) +{ + if (pdc_console_initialized) + return; + ++pdc_console_initialized; + + /* If the console is duplex then copy the COUT parameters to CIN. */ + if (PAGE0->mem_cons.cl_class == CL_DUPLEX) + memcpy(&PAGE0->mem_kbd, &PAGE0->mem_cons, sizeof(PAGE0->mem_cons)); + + pdc_console_write(0, "PDC Console Initialized\n", 24); + /* register the pdc console */ + register_console(&pdc_cons); +} + + +/* Unregister the pdc console with the printk console layer */ +void pdc_console_die(void) +{ + printk("Switching from PDC console\n"); + if (!pdc_console_initialized) + return; + --pdc_console_initialized; + +#ifdef CONFIG_VT_CONSOLE + { + /* fixme (needed?): Wait for console-tasklet to finish !*/ + extern struct tasklet_struct console_tasklet; + tasklet_schedule(&console_tasklet); + } +#endif + + unregister_console(&pdc_cons); +} + + +/* + * Used for emergencies. Currently only used if an HPMC occurs. If an + * HPMC occurs, it is possible that the current console may not be + * properly initialed after the PDC IO reset. This routine unregisters all + * of the current consoles, reinitializes the pdc console and + * registers it. + */ + +void pdc_console_restart(void) +{ + struct console *console; + extern int log_size; + + if (pdc_console_initialized) + return; + + while ((console = console_drivers) != (struct console *)0) + unregister_console(console_drivers); + + log_size = 0; + pdc_console_init(); + printk("Switched to PDC console\n"); + return; +} + diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c new file mode 100644 index 000000000000..4094638daad3 --- /dev/null +++ b/arch/parisc/kernel/process.c @@ -0,0 +1,254 @@ +/* + * linux/arch/parisc/kernel/process.c + * based on the work for i386 + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#define __KERNEL_SYSCALLS__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED; + +#ifdef __LP64__ +/* The 64-bit code should work equally well in 32-bit land but I didn't + * want to take the time to confirm that. -PB + */ +extern unsigned int ret_from_kernel_thread; +#else +asmlinkage void ret_from_kernel_thread(void) __asm__("ret_from_kernel_thread"); +#endif + + +int hlt_counter=0; + +void disable_hlt(void) +{ + hlt_counter++; +} + +void enable_hlt(void) +{ + hlt_counter--; +} + +/* + * The idle thread. There's no useful work to be + * done, so just try to conserve power and have a + * low exit latency (ie sit in a loop waiting for + * somebody to say that they'd like to reschedule) + */ +void cpu_idle(void) +{ + /* endless idle loop with no priority at all */ + init_idle(); + current->nice = 20; + current->counter = -100; + + while (1) { + while (!current->need_resched) { + } + schedule(); + check_pgt_cache(); + } +} + +void __init reboot_setup(char *str, int *ints) +{ +} + +struct notifier_block *mach_notifier; + +void machine_restart(char *ptr) +{ + notifier_call_chain(&mach_notifier, MACH_RESTART, ptr); +} + +void machine_halt(void) +{ + notifier_call_chain(&mach_notifier, MACH_HALT, NULL); +} + +void machine_power_on(void) +{ + notifier_call_chain(&mach_notifier, MACH_POWER_ON, NULL); +} + +void machine_power_off(void) +{ + notifier_call_chain(&mach_notifier, MACH_POWER_OFF, NULL); +} + + +void machine_heartbeat(void) +{ +} + + +/* + * Create a kernel thread + */ + +extern pid_t __kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); +pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + + /* + * FIXME: Once we are sure we don't need any debug here, + * kernel_thread can become a #define. + */ + + return __kernel_thread(fn, arg, flags); +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ +} + +void flush_thread(void) +{ + set_fs(USER_DS); +} + +void release_thread(struct task_struct *dead_task) +{ +} + +/* + * Fill in the FPU structure for a core dump. + */ +int dump_fpu (struct pt_regs * regs, elf_fpregset_t *r) +{ + memcpy(r, regs->fr, sizeof *r); + return 1; +} + +/* Note that "fork()" is implemented in terms of clone, with + parameters (SIGCHLD, regs->gr[30], regs). */ +int +sys_clone(unsigned long clone_flags, unsigned long usp, + struct pt_regs *regs) +{ + return do_fork(clone_flags, usp, regs, 0); +} + +int +sys_vfork(struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, + regs->gr[30], regs, 0); +} + +int +copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, /* in ia64 this is "user_stack_size" */ + struct task_struct * p, struct pt_regs * pregs) +{ + struct pt_regs * cregs = &(p->thread.regs); + long ksp; + + *cregs = *pregs; + + /* Set the return value for the child. Note that this is not + actually restored by the syscall exit path, but we put it + here for consistency in case of signals. */ + cregs->gr[28] = 0; /* child */ + + /* + * We need to differentiate between a user fork and a + * kernel fork. We can't use user_mode, because the + * the syscall path doesn't save iaoq. Right now + * We rely on the fact that kernel_thread passes + * in zero for usp. + */ + if (usp == 0) { + /* Kernel Thread */ + ksp = (((unsigned long)(p)) + TASK_SZ_ALGN); + cregs->ksp = ksp; /* always return to kernel */ +#ifdef __LP64__ + cregs->kpc = (unsigned long) &ret_from_kernel_thread; +#else + cregs->kpc = (unsigned long) ret_from_kernel_thread; +#endif + + /* + * Copy function and argument to be called from + * ret_from_kernel_thread. + */ + cregs->gr[26] = pregs->gr[26]; + cregs->gr[25] = pregs->gr[25]; + + } else { + /* User Thread: + * + * Use same stack depth as parent when in wrapper + * + * Note that the fork wrappers are responsible + * for setting gr[20] and gr[21]. + */ + + cregs->ksp = ((unsigned long)(p)) + + (pregs->gr[20] & (INIT_TASK_SIZE - 1)); + cregs->kpc = pregs->gr[21]; + } + + return 0; +} + +/* + * sys_execve() executes a new program. + */ + +asmlinkage int sys_execve(struct pt_regs *regs) +{ + int error; + char *filename; + + filename = getname((char *) regs->gr[26]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, (char **) regs->gr[25], + (char **) regs->gr[24], regs); + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); +out: + + return error; +} diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c new file mode 100644 index 000000000000..176b4386ac4f --- /dev/null +++ b/arch/parisc/kernel/ptrace.c @@ -0,0 +1,305 @@ +/* + * Kernel support for the ptrace() and syscall tracing interfaces. + * + * Copyright (C) 2000 Hewlett-Packard Co, Linuxcare Inc. + * Copyright (C) 2000 Matthew Wilcox + * Copyright (C) 2000 David Huggins-Daines + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* These are used in entry.S, syscall_restore_rfi. We need to record the + * current stepping mode somewhere other than in PSW, because there is no + * concept of saving and restoring the users PSW over a syscall. We choose + * to use these two bits in task->ptrace. These bits must not clash with + * any PT_* defined in include/linux/sched.h, and must match with the bit + * tests in entry.S + */ +#define PT_SINGLESTEP 0x10000 +#define PT_BLOCKSTEP 0x20000 + +long sys_ptrace(long request, pid_t pid, long addr, long data) +{ + struct task_struct *child; + long ret; + + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + ret = -EPERM; + if (pid == 1) /* no messing around with init! */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + if (child == current) + goto out_tsk; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) + goto out_tsk; + /* the same process cannot be attached many times */ + if (child->ptrace & PT_PTRACED) + goto out_tsk; + child->ptrace |= PT_PTRACED; + if (child->p_pptr != current) { + unsigned long flags; + + write_lock_irqsave(&tasklist_lock, flags); + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); + } + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + + switch (request) { + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + goto out_tsk; + ret = put_user(tmp,(unsigned long *) data); + goto out_tsk; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + goto out_tsk; + ret = -EIO; + goto out_tsk; + + /* Read the word at location addr in the USER area. This will need + to change when the kernel no longer saves all regs on a syscall. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || (unsigned long) addr >= sizeof(struct pt_regs)) + goto out_tsk; + + tmp = *(unsigned long *) ((char *) task_regs(child) + addr); + ret = put_user(tmp, (unsigned long *) data); + goto out_tsk; + } + + /* Write the word at location addr in the USER area. This will need + to change when the kernel no longer saves all regs on a syscall. + FIXME. There is a problem at the moment in that r3-r18 are only + saved if the process is ptraced on syscall entry, and even then + those values are overwritten by actual register values on syscall + exit. */ + case PTRACE_POKEUSR: + ret = -EIO; + if ((addr & 3) || (unsigned long) addr >= sizeof(struct pt_regs)) + goto out_tsk; + /* XXX This test probably needs adjusting. We probably want to + * allow writes to some bits of PSW, and may want to block writes + * to (some) space registers. Some register values written here + * may be ignored in entry.S:syscall_restore_rfi; e.g. iaoq is + * written with r31/r31+4, and not with the values in pt_regs. + */ + /* Allow writing of gr1-gr31, fr*, sr*, iasq*, iaoq*, sar */ + if (addr == PT_PSW || (addr > PT_IAOQ1 && addr != PT_SAR)) + goto out_tsk; + + *(unsigned long *) ((char *) task_regs(child) + addr) = data; + ret = 0; + goto out_tsk; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: + ret = -EIO; + if ((unsigned long) data > _NSIG) + goto out_tsk; + child->ptrace &= ~(PT_SINGLESTEP|PT_BLOCKSTEP); + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + goto out_wake_notrap; + + case PTRACE_KILL: + /* + * make the child exit. Best I can do is send it a + * sigkill. perhaps it should be put in the status + * that it wants to exit. + */ + if (child->state == TASK_ZOMBIE) /* already dead */ + goto out_tsk; + child->exit_code = SIGKILL; + goto out_wake_notrap; + + case PTRACE_SINGLEBLOCK: + ret = -EIO; + if ((unsigned long) data > _NSIG) + goto out_tsk; + child->ptrace &= ~(PT_TRACESYS|PT_SINGLESTEP); + child->ptrace |= PT_BLOCKSTEP; + child->exit_code = data; + + /* Enable taken branch trap. */ + pa_psw(child)->r = 0; + pa_psw(child)->t = 1; + pa_psw(child)->h = 0; + pa_psw(child)->l = 0; + goto out_wake; + + case PTRACE_SINGLESTEP: + ret = -EIO; + if ((unsigned long) data > _NSIG) + goto out_tsk; + child->ptrace &= ~(PT_TRACESYS|PT_BLOCKSTEP); + child->ptrace |= PT_SINGLESTEP; + child->exit_code = data; + + if (pa_psw(child)->n) { + struct siginfo si; + + /* Nullified, just crank over the queue. */ + task_regs(child)->iaoq[0] = task_regs(child)->iaoq[1]; + task_regs(child)->iasq[0] = task_regs(child)->iasq[1]; + task_regs(child)->iaoq[1] = task_regs(child)->iaoq[0] + 4; + pa_psw(child)->n = 0; + pa_psw(child)->x = 0; + pa_psw(child)->y = 0; + pa_psw(child)->z = 0; + pa_psw(child)->b = 0; + pa_psw(child)->r = 0; + pa_psw(child)->t = 0; + pa_psw(child)->h = 0; + pa_psw(child)->l = 0; + /* Don't wake up the child, but let the + parent know something happened. */ + si.si_code = TRAP_TRACE; + si.si_addr = (void *) (task_regs(child)->iaoq[0] & ~3); + si.si_signo = SIGTRAP; + si.si_errno = 0; + force_sig_info(SIGTRAP, &si, child); + //notify_parent(child, SIGCHLD); + //ret = 0; + goto out_wake; + } + + /* Enable recovery counter traps. The recovery counter + * itself will be set to zero on a task switch. If the + * task is suspended on a syscall then the syscall return + * path will overwrite the recovery counter with a suitable + * value such that it traps once back in user space. We + * disable interrupts in the childs PSW here also, to avoid + * interrupts while the recovery counter is decrementing. + */ + pa_psw(child)->r = 1; + pa_psw(child)->t = 0; + pa_psw(child)->h = 0; + pa_psw(child)->l = 0; + /* give it a chance to run. */ + goto out_wake; + + case PTRACE_DETACH: + ret = -EIO; + if ((unsigned long) data > _NSIG) + goto out_tsk; + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS|PT_SINGLESTEP|PT_BLOCKSTEP); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + goto out_wake_notrap; + + default: + ret = -EIO; + goto out_tsk; + } + +out_wake_notrap: + /* make sure the trap bits are not set */ + pa_psw(child)->r = 0; + pa_psw(child)->t = 0; + pa_psw(child)->h = 0; + pa_psw(child)->l = 0; +out_wake: + wake_up_process(child); + ret = 0; +out_tsk: + free_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +void syscall_trace(void) +{ + if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) != + (PT_PTRACED|PT_TRACESYS)) + return; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/parisc/kernel/real1.c b/arch/parisc/kernel/real1.c new file mode 100644 index 000000000000..2bb26bc88d52 --- /dev/null +++ b/arch/parisc/kernel/real1.c @@ -0,0 +1,154 @@ +/* + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000 Hewlett Packard (Paul Bame bame@puffin.external.hp.com) + * + * most of these calls might reasonably be moved to ../kernel -PB + * + * The basic principle is to construct a stack frame in C then call + * some assembly which adopts that stack, does some rfi magic, may + * switch wide/narrow mode, and calls the routine described by the + * 'fn' parameter WHICH IS NOT A FUNCTION POINTER!!!!!!!!!!!!!!!! + */ +#include +#include +#include +#include /* for __pa() */ +#include + +static spinlock_t pdc_lock = SPIN_LOCK_UNLOCKED; + +/***************** 32-bit real-mode calls ***********/ +/* The struct below is used + * to overlay real_stack (real2.S), preparing a 32-bit call frame. + * real32_call_asm() then uses this stack in narrow real mode + */ + +struct narrow_stack { + /* use int, not long which is 64 bits */ + unsigned int arg13; + unsigned int arg12; + unsigned int arg11; + unsigned int arg10; + unsigned int arg9; + unsigned int arg8; + unsigned int arg7; + unsigned int arg6; + unsigned int arg5; + unsigned int arg4; + unsigned int arg3; + unsigned int arg2; + unsigned int arg1; + unsigned int arg0; + unsigned int frame_marker[8]; + unsigned int sp; + /* in reality, there's nearly 8k of stack after this */ +}; + +long +real32_call(unsigned long fn, ...) +{ + unsigned long r; + va_list args; + unsigned long flags; + extern struct narrow_stack real_stack; + extern unsigned long real32_call_asm(unsigned int *, + unsigned int *, unsigned int); + + va_start(args, fn); + real_stack.arg0 = va_arg(args, unsigned int); + real_stack.arg1 = va_arg(args, unsigned int); + real_stack.arg2 = va_arg(args, unsigned int); + real_stack.arg3 = va_arg(args, unsigned int); + real_stack.arg4 = va_arg(args, unsigned int); + real_stack.arg5 = va_arg(args, unsigned int); + real_stack.arg6 = va_arg(args, unsigned int); + real_stack.arg7 = va_arg(args, unsigned int); + real_stack.arg8 = va_arg(args, unsigned int); + real_stack.arg9 = va_arg(args, unsigned int); + real_stack.arg10 = va_arg(args, unsigned int); + real_stack.arg11 = va_arg(args, unsigned int); + real_stack.arg12 = va_arg(args, unsigned int); + real_stack.arg13 = va_arg(args, unsigned int); + va_end(args); + + if (fn == 0) { + /* mem_pdc call */ + fn = PAGE0->mem_pdc; + } + + spin_lock_irqsave(&pdc_lock, flags); + r = real32_call_asm(&real_stack.sp, &real_stack.arg0, fn); + spin_unlock_irqrestore(&pdc_lock, flags); + + return r; +} + +#ifdef __LP64__ +/***************** 64-bit real-mode calls ***********/ + +struct wide_stack { + unsigned long arg0; + unsigned long arg1; + unsigned long arg2; + unsigned long arg3; + unsigned long arg4; + unsigned long arg5; + unsigned long arg6; + unsigned long arg7; + unsigned long arg8; + unsigned long arg9; + unsigned long arg10; + unsigned long arg11; + unsigned long arg12; + unsigned long arg13; + unsigned long frame_marker[2]; /* rp, previous sp */ + unsigned long sp; + /* in reality, there's nearly 8k of stack after this */ +}; + +long +real64_call(unsigned long fn, ...) +{ + unsigned long r; + va_list args; + unsigned long flags; + extern struct wide_stack real_stack; + extern unsigned long real64_call_asm(unsigned long *, + unsigned long *, unsigned long); + + va_start(args, fn); + real_stack.arg0 = va_arg(args, unsigned long); + real_stack.arg1 = va_arg(args, unsigned long); + real_stack.arg2 = va_arg(args, unsigned long); + real_stack.arg3 = va_arg(args, unsigned long); + real_stack.arg4 = va_arg(args, unsigned long); + real_stack.arg5 = va_arg(args, unsigned long); + real_stack.arg6 = va_arg(args, unsigned long); + real_stack.arg7 = va_arg(args, unsigned long); + real_stack.arg8 = va_arg(args, unsigned long); + real_stack.arg9 = va_arg(args, unsigned long); + real_stack.arg10 = va_arg(args, unsigned long); + real_stack.arg11 = va_arg(args, unsigned long); + real_stack.arg12 = va_arg(args, unsigned long); + real_stack.arg13 = va_arg(args, unsigned long); + va_end(args); + + if (fn == 0) { + /* mem_pdc call */ + fn = PAGE0->mem_pdc_hi; + fn <<= 32; + fn |= PAGE0->mem_pdc; + } + + spin_lock_irqsave(&pdc_lock, flags); + r = real64_call_asm(&real_stack.sp, &real_stack.arg0, fn); + spin_unlock_irqrestore(&pdc_lock, flags); + + return r; +} + +#endif diff --git a/arch/parisc/kernel/real2.S b/arch/parisc/kernel/real2.S new file mode 100644 index 000000000000..eb1425ec4b9f --- /dev/null +++ b/arch/parisc/kernel/real2.S @@ -0,0 +1,274 @@ +/* + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000 Hewlett Packard (Paul Bame bame@puffin.external.hp.com) + * + */ +#define __ASSEMBLY__ +#include +#include + + .section .bss + .export real_stack + .align 64 +real_stack: + .block 8192 + +#ifdef __LP64__ +# define REG_SZ 8 +#else +# define REG_SZ 4 +#endif + +#define N_SAVED_REGS 9 + +save_cr_space: + .block REG_SZ * N_SAVED_REGS + + +/************************ 32-bit real-mode calls ***********************/ +/* This can be called in both narrow and wide kernels */ + .text + .export real32_call_asm + /* unsigned long real32_call_asm(unsigned int *sp, + * unsigned int *arg0p, + * unsigned int iodc_fn) + * sp is value of stack pointer to adopt before calling PDC (virt) + * arg0p points to where saved arg values may be found + * iodc_fn is the IODC function to call + */ +real32_call_asm: + STREG %rp, -RP_OFFSET(%sp) /* save RP */ +#ifdef __LP64__ + callee_save + ldo 2*REG_SZ(%sp), %sp /* room for a couple more saves */ + STREG %r27, -1*REG_SZ(%sp) + STREG %r29, -2*REG_SZ(%sp) +#endif + STREG %sp, -REG_SZ(%arg0) /* save SP on real-mode stack */ + copy %arg0, %sp /* adopt the real-mode SP */ + + /* save iodc_fn */ + copy %arg2, %r31 + + /* load up the arg registers from the saved arg area */ + /* 32-bit calling convention passes first 4 args in registers */ + ldw 0(%arg1), %arg0 /* note overwriting arg0 */ + ldw -8(%arg1), %arg2 + ldw -12(%arg1), %arg3 + ldw -4(%arg1), %arg1 /* obviously must do this one last! */ + + tophys %sp + + b,l rfi_virt2real,%r2 + nop + + b,l save_control_regs,%r2 /* modifies r1, r2, r28 */ + nop + +#ifdef __LP64__ + rsm PSW_SM_W, %r0 /* go narrow */ +#endif + + ldil L%PA(ric_ret), %r2 + ldo R%PA(ric_ret)(%r2), %r2 + bv 0(%r31) + nop +ric_ret: +#ifdef __LP64__ + ssm PSW_SM_W, %r0 /* go wide */ +#endif + /* restore CRs before going virtual in case we page fault */ + b,l restore_control_regs, %r2 /* modifies r1, r2, r26 */ + nop + + b,l rfi_real2virt,%r2 + nop + + tovirt %sp + LDREG -REG_SZ(%sp), %sp /* restore SP */ +#ifdef __LP64__ + LDREG -1*REG_SZ(%sp), %r27 + LDREG -2*REG_SZ(%sp), %r29 + ldo -2*REG_SZ(%sp), %sp + callee_rest +#endif + LDREG -RP_OFFSET(%sp), %rp /* restore RP */ + bv 0(%rp) + nop + + +# define PUSH_CR(r, where) mfctl r, %r1 ! STREG,ma %r1, REG_SZ(where) +# define POP_CR(r, where) LDREG,mb -REG_SZ(where), %r1 ! mtctl %r1, r + + .text +save_control_regs: + load32 PA(save_cr_space), %r28 + PUSH_CR(%cr24, %r28) + PUSH_CR(%cr25, %r28) + PUSH_CR(%cr26, %r28) + PUSH_CR(%cr27, %r28) + PUSH_CR(%cr28, %r28) + PUSH_CR(%cr29, %r28) + PUSH_CR(%cr30, %r28) + PUSH_CR(%cr31, %r28) + PUSH_CR(%cr15, %r28) + bv 0(%r2) + nop + +restore_control_regs: + load32 PA(save_cr_space + (N_SAVED_REGS * REG_SZ)), %r26 + POP_CR(%cr15, %r26) + POP_CR(%cr31, %r26) + POP_CR(%cr30, %r26) + POP_CR(%cr29, %r26) + POP_CR(%cr28, %r26) + POP_CR(%cr27, %r26) + POP_CR(%cr26, %r26) + POP_CR(%cr25, %r26) + POP_CR(%cr24, %r26) + bv 0(%r2) + nop + +/* rfi_virt2real() and rfi_real2virt() could perhaps be adapted for + * more general-purpose use by the several places which need RFIs + */ + .align 128 + .text +rfi_virt2real: + /* switch to real mode... */ + ssm 0,0 /* See "relied upon translation" */ + nop /* comment in interruption.S */ + nop + nop + nop + nop + nop + nop + nop + + mtsm 0 /* disable interruptions */ + mtctl 0, %cr17 /* space 0 */ + mtctl 0, %cr17 + load32 PA(rfi_v2r_1), %r1 + mtctl %r1, %cr18 + ldo 4(%r1), %r1 + mtctl %r1, %cr18 + load32 PDC_PSW, %r1 + mtctl %r1, %cr22 + rfi + + nop + nop + nop + nop + nop + nop + nop + nop +rfi_v2r_1: + tophys %r2 + bv 0(%r2) + nop + + .text + .align 128 +rfi_real2virt: + ssm 0,0 /* See "relied upon translation" */ + nop /* comment in interruption.S */ + nop + nop + nop + nop + nop + nop + nop + + mtsm 0 /* disable interruptions */ + mtctl 0, %cr17 /* space 0 */ + mtctl 0, %cr17 + load32 (rfi_r2v_1), %r1 + mtctl %r1, %cr18 + ldo 4(%r1), %r1 + mtctl %r1, %cr18 + load32 KERNEL_PSW, %r1 + mtctl %r1, %cr22 + rfi + + nop + nop + nop + nop + nop + nop + nop + nop +rfi_r2v_1: + tovirt %r2 + bv 0(%r2) + nop + +#ifdef __LP64__ + +/************************ 64-bit real-mode calls ***********************/ +/* This is only usable in wide kernels right now and will probably stay so */ + .text + .export real64_call_asm + /* unsigned long real64_call_asm(unsigned long *sp, + * unsigned long *arg0p, + * unsigned long fn) + * sp is value of stack pointer to adopt before calling PDC (virt) + * arg0p points to where saved arg values may be found + * iodc_fn is the IODC function to call + */ +real64_call_asm: + std %rp, -0x10(%sp) /* save RP */ + std %sp, -8(%arg0) /* save SP on real-mode stack */ + copy %arg0, %sp /* adopt the real-mode SP */ + + /* save fn */ + copy %arg2, %r31 + + /* set up the new ap */ + ldo 64(%arg1), %r29 + + /* load up the arg registers from the saved arg area */ + /* 32-bit calling convention passes first 4 args in registers */ + ldd 0*REG_SZ(%arg1), %arg0 /* note overwriting arg0 */ + ldd 2*REG_SZ(%arg1), %arg2 + ldd 3*REG_SZ(%arg1), %arg3 + ldd 4*REG_SZ(%arg1), %r22 + ldd 5*REG_SZ(%arg1), %r21 + ldd 6*REG_SZ(%arg1), %r20 + ldd 7*REG_SZ(%arg1), %r19 + ldd 1*REG_SZ(%arg1), %arg1 /* do this one last! */ + + tophys %sp + + b,l rfi_virt2real,%r2 + nop + + b,l save_control_regs,%r2 /* modifies r1, r2, r28 */ + nop + + load32 PA(r64_ret), %r2 + bv 0(%r31) + nop +r64_ret: + /* restore CRs before going virtual in case we page fault */ + b,l restore_control_regs, %r2 /* modifies r1, r2, r26 */ + nop + + b,l rfi_real2virt,%r2 + nop + + tovirt %sp + ldd -8(%sp), %sp /* restore SP */ + ldd -0x10(%sp), %rp /* restore RP */ + bv 0(%rp) + nop + +#endif diff --git a/arch/parisc/kernel/sba_iommu.c b/arch/parisc/kernel/sba_iommu.c new file mode 100644 index 000000000000..b7b5414788c0 --- /dev/null +++ b/arch/parisc/kernel/sba_iommu.c @@ -0,0 +1,1752 @@ +/* +** System Bus Adapter (SBA) I/O MMU manager +** +** (c) Copyright 2000 Grant Grundler +** (c) Copyright 2000 Hewlett-Packard Company +** +** Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code) +** +** 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 module initializes the IOC (I/O Controller) found on B1000/C3000/ +** J5000/J7000/N-class/L-class machines and their successors. +** +** FIXME: Multi-IOC support missing - depends on hp_device data +** FIXME: add DMA hint support programming in both sba and lba modules. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#define PCI_DEBUG /* for ASSERT */ +#include +#undef PCI_DEBUG + +#include +#include +#include /* for DMA_CHUNK_SIZE */ + +#include /* for register_driver() stuff */ +#include /* FIXME: for gsc_read/gsc_write */ + +#include +#include /* for proc_runway_root */ + + +#define MODULE_NAME "SBA" + +/* +** The number of debug flags is a clue - this code is fragile. +** Don't even think about messing with it unless you have +** plenty of 710's to sacrafice to the computer gods. :^) +*/ +#undef DEBUG_SBA_INIT +#undef DEBUG_SBA_RUN +#undef DEBUG_SBA_RUN_SG +#undef DEBUG_SBA_RESOURCE +#undef ASSERT_PDIR_SANITY +#undef DEBUG_LARGE_SG_ENTRIES + +#if 1 +#define SBA_INLINE +#else +#define SBA_INLINE __inline__ +#endif + +#ifdef DEBUG_SBA_INIT +#define DBG_INIT(x...) printk(x) +#else +#define DBG_INIT(x...) +#endif + +#ifdef DEBUG_SBA_RUN +#define DBG_RUN(x...) printk(x) +#else +#define DBG_RUN(x...) +#endif + +#ifdef DEBUG_SBA_RUN_SG +#define DBG_RUN_SG(x...) printk(x) +#else +#define DBG_RUN_SG(x...) +#endif + + +#ifdef DEBUG_SBA_RESOURCE +#define DBG_RES(x...) printk(x) +#else +#define DBG_RES(x...) +#endif + +/* +** The number of pdir entries to "free" before issueing +** a read to PCOM register to flush out PCOM writes. +** Interacts with allocation granularity (ie 4 or 8 entries +** allocated and free'd/purged at a time might make this +** less interesting). +*/ +#if 0 +#define DELAYED_RESOURCE_CNT 16 +#else +#undef DELAYED_RESOURCE_CNT +#endif + +#define DEFAULT_DMA_HINT_REG 0 + +#define ASTRO_RUNWAY_PORT 0x582 +#define ASTRO_ROPES_PORT 0x780 + +#define IKE_MERCED_PORT 0x803 +#define IKE_ROPES_PORT 0x781 + +int sba_driver_callback(struct hp_device *, struct pa_iodc_driver *); + +static struct pa_iodc_driver sba_drivers_for[] = { + +/* FIXME: why is SVERSION checked? */ + + {HPHW_IOA, ASTRO_RUNWAY_PORT, 0x0, 0xb, 0, 0x10, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "I/O MMU", (void *) sba_driver_callback}, + + {HPHW_BCPORT, ASTRO_ROPES_PORT, 0x0, 0xb, 0, 0x10, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "I/O MMU", (void *) sba_driver_callback}, + +#if 0 +/* FIXME : N-class! Use a different "callback"? */ + {HPHW_BCPORT, IKE_MERCED_PORT, 0x0, 0xb, 0, 0x10, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "I/O MMU", (void *) sba_driver_callback}, + + {HPHW_BCPORT, IKE_ROPES_PORT, 0x0, 0xb, 0, 0x10, + DRIVER_CHECK_HVERSION + + DRIVER_CHECK_SVERSION + DRIVER_CHECK_HWTYPE, + MODULE_NAME, "I/O MMU", (void *) sba_driver_callback}, +#endif + + {0,0,0,0,0,0, + 0, + (char *) NULL, (char *) NULL, (void *) NULL } +}; + + +#define SBA_FUNC_ID 0x0000 /* function id */ +#define SBA_FCLASS 0x0008 /* function class, bist, header, rev... */ + +#define IS_ASTRO(id) ( \ + (((id)->hw_type == HPHW_IOA) && ((id)->hversion == ASTRO_RUNWAY_PORT)) || \ + (((id)->hw_type == HPHW_BCPORT) && ((id)->hversion == ASTRO_ROPES_PORT)) \ +) + +#define CONFIG_FUNC_SIZE 4096 /* SBA configuration function reg set */ + +#define ASTRO_IOC_OFFSET 0x20000 +/* Ike's IOC's occupy functions 2 and 3 (not 0 and 1) */ +#define IKE_IOC_OFFSET(p) ((p+2)*CONFIG_FUNC_SIZE) + +#define IOC_CTRL 0x8 /* IOC_CTRL offset */ +#define IOC_CTRL_TE (0x1 << 0) /* TOC Enable */ +#define IOC_CTRL_RM (0x1 << 8) /* Real Mode */ +#define IOC_CTRL_NC (0x1 << 9) /* Non Coherent Mode */ + +#define MAX_IOC 2 /* per Ike. Astro only has 1 */ + + +/* +** Offsets into MBIB (Function 0 on Ike and hopefully Astro) +** Firmware programs this stuff. Don't touch it. +*/ +#define IOS_DIST_BASE 0x390 +#define IOS_DIST_MASK 0x398 +#define IOS_DIST_ROUTE 0x3A0 + +#define IOS_DIRECT_BASE 0x3C0 +#define IOS_DIRECT_MASK 0x3C8 +#define IOS_DIRECT_ROUTE 0x3D0 + +/* +** Offsets into I/O TLB (Function 2 and 3 on Ike) +*/ +#define ROPE0_CTL 0x200 /* "regbus pci0" */ +#define ROPE1_CTL 0x208 +#define ROPE2_CTL 0x210 +#define ROPE3_CTL 0x218 +#define ROPE4_CTL 0x220 +#define ROPE5_CTL 0x228 +#define ROPE6_CTL 0x230 +#define ROPE7_CTL 0x238 + +#define HF_ENABLE 0x40 + + +#define IOC_IBASE 0x300 /* IO TLB */ +#define IOC_IMASK 0x308 +#define IOC_PCOM 0x310 +#define IOC_TCNFG 0x318 +#define IOC_PDIR_BASE 0x320 + +#define IOC_IOVA_SPACE_BASE 0 /* IOVA ranges start at 0 */ + +/* +** IOC supports 4/8/16/64KB page sizes (see TCNFG register) +** It's safer (avoid memory corruption) to keep DMA page mappings +** equivalently sized to VM PAGE_SIZE. +** +** We really can't avoid generating a new mapping for each +** page since the Virtual Coherence Index has to be generated +** and updated for each page. +** +** IOVP_SIZE could only be greater than PAGE_SIZE if we are +** confident the drivers really only touch the next physical +** page iff that driver instance owns it. +*/ +#define IOVP_SIZE PAGE_SIZE +#define IOVP_SHIFT PAGE_SHIFT +#define IOVP_MASK PAGE_MASK + +#define SBA_PERF_CFG 0x708 /* Performance Counter stuff */ +#define SBA_PERF_MASK1 0x718 +#define SBA_PERF_MASK2 0x730 + + +/* +** Offsets into PCI Performance Counters (functions 12 and 13) +** Controlled by PERF registers in function 2 & 3 respectively. +*/ +#define SBA_PERF_CNT1 0x200 +#define SBA_PERF_CNT2 0x208 +#define SBA_PERF_CNT3 0x210 + + +struct ioc { + char *ioc_hpa; /* I/O MMU base address */ + char *res_map; /* resource map, bit == pdir entry */ + u64 *pdir_base; /* physical base address */ + + unsigned long *res_hint; /* next available IOVP - circular search */ + unsigned int res_bitshift; /* from the LEFT! */ + unsigned int res_size; /* size of resource map in bytes */ + unsigned int hint_shift_pdir; + spinlock_t res_lock; + unsigned long hint_mask_pdir; /* bits used for DMA hints */ +#ifdef DELAYED_RESOURCE_CNT + dma_addr_t res_delay[DELAYED_RESOURCE_CNT]; +#endif + +#ifdef CONFIG_PROC_FS +#define SBA_SEARCH_SAMPLE 0x100 + unsigned long avg_search[SBA_SEARCH_SAMPLE]; + unsigned long avg_idx; /* current index into avg_search */ + unsigned long used_pages; + unsigned long msingle_calls; + unsigned long msingle_pages; + unsigned long msg_calls; + unsigned long msg_pages; + unsigned long usingle_calls; + unsigned long usingle_pages; + unsigned long usg_calls; + unsigned long usg_pages; +#endif + + /* STUFF We don't need in performance path */ + unsigned int pdir_size; /* in bytes, determined by IOV Space size */ + unsigned long ibase; /* pdir IOV Space base - shared w/lba_pci */ + unsigned long imask; /* pdir IOV Space mask - shared w/lba_pci */ +}; + +struct sba_device { + struct sba_device *next; /* list of LBA's in system */ + struct hp_device *iodc; /* data about dev from firmware */ + char *sba_hpa; /* base address */ + spinlock_t sba_lock; + unsigned int flags; /* state/functionality enabled */ + unsigned int hw_rev; /* HW revision of chip */ + + unsigned int num_ioc; /* number of on-board IOC's */ + struct ioc ioc[MAX_IOC]; +}; + + +static struct sba_device *sba_list; +static int sba_count; + +/* Ratio of Host MEM to IOV Space size */ +static unsigned long sba_mem_ratio = 4; + +/* Looks nice and keeps the compiler happy */ +#define SBA_DEV(d) ((struct sba_device *) (d)) + + +#define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1)) + + +/************************************ +** SBA register read and write support +** +** BE WARNED: register writes are posted. +** (ie follow writes which must reach HW with a read) +*/ +#define READ_U8(addr) gsc_readb(addr) +#define READ_U16(addr) gsc_readw((u16 *) (addr)) +#define READ_U32(addr) gsc_readl((u32 *) (addr)) +#define WRITE_U8(value, addr) gsc_writeb(value, addr) +#define WRITE_U16(value, addr) gsc_writew(value, (u16 *) (addr)) +#define WRITE_U32(value, addr) gsc_writel(value, (u32 *) (addr)) + +#define READ_REG8(addr) gsc_readb(addr) +#define READ_REG16(addr) le16_to_cpu(gsc_readw((u16 *) (addr))) +#define READ_REG32(addr) le32_to_cpu(gsc_readl((u32 *) (addr))) +#define READ_REG64(addr) le64_to_cpu(gsc_readq((u64 *) (addr))) +#define WRITE_REG8(value, addr) gsc_writeb(value, addr) +#define WRITE_REG16(value, addr) gsc_writew(cpu_to_le16(value), (u16 *) (addr)) +#define WRITE_REG32(value, addr) gsc_writel(cpu_to_le32(value), (u32 *) (addr)) +#define WRITE_REG64(value, addr) gsc_writeq(cpu_to_le64(value), (u64 *) (addr)) + +#ifdef DEBUG_SBA_INIT + +static void +sba_dump_ranges(char *hpa) +{ + printk("SBA at 0x%p\n", hpa); + printk("IOS_DIST_BASE : %08x %08x\n", + READ_REG32(hpa+IOS_DIST_BASE+4), + READ_REG32(hpa+IOS_DIST_BASE)); + printk("IOS_DIST_MASK : %08x %08x\n", + READ_REG32(hpa+IOS_DIST_MASK+4), + READ_REG32(hpa+IOS_DIST_MASK)); + printk("IOS_DIST_ROUTE : %08x %08x\n", + READ_REG32(hpa+IOS_DIST_ROUTE+4), + READ_REG32(hpa+IOS_DIST_ROUTE)); + printk("\n"); + printk("IOS_DIRECT_BASE : %08x %08x\n", + READ_REG32(hpa+IOS_DIRECT_BASE+4), + READ_REG32(hpa+IOS_DIRECT_BASE)); + printk("IOS_DIRECT_MASK : %08x %08x\n", + READ_REG32(hpa+IOS_DIRECT_MASK+4), + READ_REG32(hpa+IOS_DIRECT_MASK)); + printk("IOS_DIRECT_ROUTE: %08x %08x\n", + READ_REG32(hpa+IOS_DIRECT_ROUTE+4), + READ_REG32(hpa+IOS_DIRECT_ROUTE)); +} + +static void +sba_dump_tlb(char *hpa) +{ + printk("IO TLB at 0x%p\n", hpa); + printk("IOC_IBASE : %08x %08x\n", + READ_REG32(hpa+IOC_IBASE+4), + READ_REG32(hpa+IOC_IBASE)); + printk("IOC_IMASK : %08x %08x\n", + READ_REG32(hpa+IOC_IMASK+4), + READ_REG32(hpa+IOC_IMASK)); + printk("IOC_TCNFG : %08x %08x\n", + READ_REG32(hpa+IOC_TCNFG+4), + READ_REG32(hpa+IOC_TCNFG)); + printk("IOC_PDIR_BASE: %08x %08x\n", + READ_REG32(hpa+IOC_PDIR_BASE+4), + READ_REG32(hpa+IOC_PDIR_BASE)); + printk("\n"); +} +#endif + + +#ifdef ASSERT_PDIR_SANITY + +static void +sba_dump_pdir_entry(struct ioc *ioc, char *msg, uint pide) +{ + /* start printing from lowest pde in rval */ + u64 *ptr = &(ioc->pdir_base[pide & (~0U * BITS_PER_LONG)]); + unsigned long *rptr = (unsigned long *) &(ioc->res_map[(pide >>3) & ~(sizeof(unsigned long) - 1)]); + uint rcnt; + + printk("SBA: %s rp %p bit %d rval 0x%lx\n", + msg, + rptr, pide & (BITS_PER_LONG - 1), *rptr); + + rcnt = 0; + while (rcnt < BITS_PER_LONG) { + printk("%s %2d %p %016Lx\n", + (rcnt == (pide & (BITS_PER_LONG - 1))) + ? " -->" : " ", + rcnt, ptr, *ptr ); + rcnt++; + ptr++; + } + printk(msg); +} + + +/* Verify the resource map and pdir state is consistent */ +static int +sba_check_pdir(struct ioc *ioc, char *msg) +{ + u32 *rptr_end = (u32 *) &(ioc->res_map[ioc->res_size]); + u32 *rptr = (u32 *) ioc->res_map; /* resource map ptr */ + u64 *pptr = ioc->pdir_base; /* pdir ptr */ + uint pide = 0; + + while (rptr < rptr_end) { + u32 rval = *rptr; + int rcnt = 32; /* number of bits we might check */ + + while (rcnt) { + /* Get last byte and highest bit from that */ + u32 pde = ((u32) (((char *)pptr)[7])) << 24; + if ((rval ^ pde) & 0x80000000) + { + /* + ** BUMMER! -- res_map != pdir -- + ** Dump rval and matching pdir entries + */ + sba_dump_pdir_entry(ioc, msg, pide); + return(1); + } + rcnt--; + rval <<= 1; /* try the next bit */ + pptr++; + pide++; + } + rptr++; /* look at next word of res_map */ + } + /* It'd be nice if we always got here :^) */ + return 0; +} + + +static void +sba_dump_sg( struct ioc *ioc, struct scatterlist *startsg, int nents) +{ + while (nents-- > 0) { + printk(" %d : %08lx/%05x %p/%05x\n", + nents, + (unsigned long) sg_dma_address(startsg), + sg_dma_len(startsg), + startsg->address, startsg->length); + startsg++; + } +} + +#endif /* ASSERT_PDIR_SANITY */ + + + +/* +** One time initialization to let the world know the LBA was found. +** This is the only routine which is NOT static. +** Must be called exactly once before pci_init(). +*/ +void __init +sba_init(void) +{ + sba_list = (struct sba_device *) NULL; + sba_count = 0; + +#ifdef DEBUG_SBA_INIT + sba_dump_ranges((char *) 0xFED00000L); +#endif + + register_driver(sba_drivers_for); +} + + + +/************************************************************** +* +* I/O Pdir Resource Management +* +* Bits set in the resource map are in use. +* Each bit can represent a number of pages. +* LSbs represent lower addresses (IOVA's). +* +***************************************************************/ +#define PAGES_PER_RANGE 1 /* could increase this to 4 or 8 if needed */ + +/* Convert from IOVP to IOVA and vice versa. */ +#define SBA_IOVA(ioc,iovp,offset,hint_reg) ((iovp) | (offset) | ((hint_reg)<<(ioc->hint_shift_pdir))) +#define SBA_IOVP(ioc,iova) ((iova) & ioc->hint_mask_pdir) + +/* FIXME : review these macros to verify correctness and usage */ +#define PDIR_INDEX(iovp) ((iovp)>>IOVP_SHIFT) +#define MKIOVP(dma_hint,pide) (dma_addr_t)((long)(dma_hint) | ((long)(pide) << IOVP_SHIFT)) +#define MKIOVA(iovp,offset) (dma_addr_t)((long)iovp | (long)offset) + +#define RESMAP_MASK(n) (~0UL << (BITS_PER_LONG - (n))) +#define RESMAP_IDX_MASK (sizeof(unsigned long) - 1) + + +/* +** Perf optimizations: +** o search for log2(size) bits at a time. +** +** Search should use register width as "stride" to search the res_map. +*/ + +static SBA_INLINE unsigned long +sba_search_bitmap(struct ioc *ioc, unsigned long bits_wanted) +{ + unsigned long *res_ptr = ioc->res_hint; + unsigned long *res_end = (unsigned long *) &(ioc->res_map[ioc->res_size]); + unsigned long pide = ~0UL; + + ASSERT(((unsigned long) ioc->res_hint & (sizeof(unsigned long) - 1UL)) == 0); + ASSERT(res_ptr < res_end); + if (bits_wanted > (BITS_PER_LONG/2)) { + /* Search word at a time - no mask needed */ + for(; res_ptr < res_end; ++res_ptr) { + if (*res_ptr == 0) { + *res_ptr = RESMAP_MASK(bits_wanted); + pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map); + pide <<= 3; /* convert to bit address */ + ASSERT(0 != pide); + break; + } + } + /* point to the next word on next pass */ + res_ptr++; + ioc->res_bitshift = 0; + } else { + /* + ** Search the resource bit map on well-aligned values. + ** "o" is the alignment. + ** We need the alignment to invalidate I/O TLB using + ** SBA HW features in the unmap path. + */ + unsigned long o = 1 << get_order(bits_wanted << PAGE_SHIFT); + uint bitshiftcnt = ROUNDUP(ioc->res_bitshift, o); + unsigned long mask; + + if (bitshiftcnt >= BITS_PER_LONG) { + bitshiftcnt = 0; + res_ptr++; + } + mask = RESMAP_MASK(bits_wanted) >> bitshiftcnt; + + DBG_RES("sba_search_bitmap() o %ld %p", o, res_ptr); + while(res_ptr < res_end) + { + DBG_RES(" %p %lx %lx\n", res_ptr, mask, *res_ptr); + ASSERT(0 != mask); + if(0 == ((*res_ptr) & mask)) { + *res_ptr |= mask; /* mark resources busy! */ + pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map); + pide <<= 3; /* convert to bit address */ + pide += bitshiftcnt; + ASSERT(0 != pide); + break; + } + mask >>= o; + bitshiftcnt += o; + if (0 == mask) { + mask = RESMAP_MASK(bits_wanted); + bitshiftcnt=0; + res_ptr++; + } + } + /* look in the same word on the next pass */ + ioc->res_bitshift = bitshiftcnt + bits_wanted; + } + + /* wrapped ? */ + ioc->res_hint = (res_end == res_ptr) ? (unsigned long *) ioc->res_map : res_ptr; + return (pide); +} + + +static int +sba_alloc_range(struct ioc *ioc, size_t size) +{ + unsigned int pages_needed = size >> IOVP_SHIFT; +#ifdef CONFIG_PROC_FS + unsigned long cr_start = mfctl(16); +#endif + unsigned long pide; + + ASSERT(pages_needed); + ASSERT((pages_needed * IOVP_SIZE) < DMA_CHUNK_SIZE); + ASSERT(pages_needed < BITS_PER_LONG); + ASSERT(0 == (size & ~IOVP_MASK)); + + /* + ** "seek and ye shall find"...praying never hurts either... + ** ggg sacrifices another 710 to the computer gods. + */ + + pide = sba_search_bitmap(ioc, pages_needed); + if (pide >= (ioc->res_size << 3)) { + pide = sba_search_bitmap(ioc, pages_needed); + if (pide >= (ioc->res_size << 3)) + panic(__FILE__ ": I/O MMU @ %p is out of mapping resources\n", ioc->ioc_hpa); + } + +#ifdef ASSERT_PDIR_SANITY + /* verify the first enable bit is clear */ + if(0x00 != ((u8 *) ioc->pdir_base)[pide*sizeof(u64) + 7]) { + sba_dump_pdir_entry(ioc, "sba_search_bitmap() botched it?", pide); + } +#endif + + DBG_RES("sba_alloc_range(%x) %d -> %lx hint %x/%x\n", + size, pages_needed, pide, + (uint) ((unsigned long) ioc->res_hint - (unsigned long) ioc->res_map), + ioc->res_bitshift ); + +#ifdef CONFIG_PROC_FS + { + unsigned long cr_end = mfctl(16); + unsigned long tmp = cr_end - cr_start; + /* check for roll over */ + cr_start = (cr_end < cr_start) ? -(tmp) : (tmp); + } + ioc->avg_search[ioc->avg_idx++] = cr_start; + ioc->avg_idx &= SBA_SEARCH_SAMPLE - 1; + + ioc->used_pages += pages_needed; +#endif + + return (pide); +} + + +/* +** clear bits in the ioc's resource map +*/ +static SBA_INLINE void +sba_free_range(struct ioc *ioc, dma_addr_t iova, size_t size) +{ + unsigned long iovp = SBA_IOVP(ioc, iova); + unsigned int pide = PDIR_INDEX(iovp); + unsigned int ridx = pide >> 3; /* convert bit to byte address */ + unsigned long *res_ptr = (unsigned long *) &((ioc)->res_map[ridx & ~RESMAP_IDX_MASK]); + + int bits_not_wanted = size >> IOVP_SHIFT; + + /* 3-bits "bit" address plus 2 (or 3) bits for "byte" == bit in word */ + unsigned long m = RESMAP_MASK(bits_not_wanted) >> (pide & (BITS_PER_LONG - 1)); + + DBG_RES("sba_free_range( ,%x,%x) %x/%lx %x %p %lx\n", + (uint) iova, size, + bits_not_wanted, m, pide, res_ptr, *res_ptr); + +#ifdef CONFIG_PROC_FS + ioc->used_pages -= bits_not_wanted; +#endif + + ASSERT(m != 0); + ASSERT(bits_not_wanted); + ASSERT((bits_not_wanted * IOVP_SIZE) < DMA_CHUNK_SIZE); + ASSERT(bits_not_wanted < BITS_PER_LONG); + ASSERT((*res_ptr & m) == m); /* verify same bits are set */ + *res_ptr &= ~m; +} + + +/************************************************************** +* +* "Dynamic DMA Mapping" support (aka "Coherent I/O") +* +***************************************************************/ + +#define SBA_DMA_HINT(ioc, val) ((val) << (ioc)->hint_shift_pdir) + + +typedef unsigned long space_t; +#define KERNEL_SPACE 0 + +/* +* SBA Mapping Routine +* +* Given a virtual address (vba, arg2) and space id, (sid, arg1) +* sba_io_pdir_entry() loads the I/O PDIR entry pointed to by +* pdir_ptr (arg0). Each IO Pdir entry consists of 8 bytes as +* shown below (MSB == bit 0): +* +* 0 19 51 55 63 +* +-+---------------------+----------------------------------+----+--------+ +* |V| U | PPN[43:12] | U | VI | +* +-+---------------------+----------------------------------+----+--------+ +* +* V == Valid Bit +* U == Unused +* PPN == Physical Page Number +* VI == Virtual Index (aka Coherent Index) +* +* The physical address fields are filled with the results of the LPA +* instruction. The virtual index field is filled with the results of +* of the LCI (Load Coherence Index) instruction. The 8 bits used for +* the virtual index are bits 12:19 of the value returned by LCI. +* +* We need to pre-swap the bytes since PCX-W is Big Endian. +*/ + +void SBA_INLINE +sba_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba) +{ + u64 pa; /* physical address */ + register unsigned ci; /* coherent index */ + + /* We currently only support kernel addresses */ + ASSERT(sid == 0); + ASSERT(((unsigned long) vba & 0xc0000000UL) == 0xc0000000UL); + + pa = virt_to_phys(vba); + pa &= ~4095ULL; /* clear out offset bits */ + + mtsp(sid,1); + asm("lci 0(%%sr1, %1), %0" : "=r" (ci) : "r" (vba)); + pa |= (ci >> 12) & 0xff; /* move CI (8 bits) into lowest byte */ + + pa |= 0x8000000000000000ULL; /* set "valid" bit */ + *pdir_ptr = cpu_to_le64(pa); /* swap and store into I/O Pdir */ +} + + +/*********************************************************** + * The Ike PCOM (Purge Command Register) is to purge + * stale entries in the IO TLB when unmapping entries. + * + * The PCOM register supports purging of multiple pages, with a minium + * of 1 page and a maximum of 2GB. Hardware requires the address be + * aligned to the size of the range being purged. The size of the range + * must be a power of 2. + ***********************************************************/ +static SBA_INLINE void +sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt) +{ + u32 iovp = (u32) SBA_IOVP(ioc,iova); + + /* Even though this is a big-endian machine, the entries + ** in the iopdir are swapped. That's why we clear the byte + ** at +7 instead of at +0. + */ + int off = PDIR_INDEX(iovp)*sizeof(u64)+7; + + /* Must be non-zero and rounded up */ + ASSERT(byte_cnt > 0); + ASSERT(0 == (byte_cnt & ~IOVP_MASK)); + +#ifdef ASSERT_PDIR_SANITY + /* Assert first pdir entry is set */ + if (0x80 != (((u8 *) ioc->pdir_base)[off])) { + sba_dump_pdir_entry(ioc,"sba_mark_invalid()", PDIR_INDEX(iovp)); + } +#endif + + if (byte_cnt <= IOVP_SIZE) + { + ASSERT( off < ioc->pdir_size); + + iovp |= IOVP_SHIFT; /* set "size" field for PCOM */ + + /* + ** clear I/O PDIR entry "valid" bit + ** Do NOT clear the rest - save it for debugging. + ** We should only clear bits that have previously + ** been enabled. + */ + ((u8 *)(ioc->pdir_base))[off] = 0; + } else { + u32 t = get_order(byte_cnt) + PAGE_SHIFT; + + iovp |= t; + ASSERT(t <= 31); /* 2GB! Max value of "size" field */ + + do { + /* verify this pdir entry is enabled */ + ASSERT(0x80 == (((u8 *) ioc->pdir_base)[off] & 0x80)); + /* clear I/O Pdir entry "valid" bit first */ + ((u8 *)(ioc->pdir_base))[off] = 0; + off += sizeof(u64); + byte_cnt -= IOVP_SIZE; + } while (byte_cnt > 0); + } + + WRITE_REG32(iovp, ioc->ioc_hpa+IOC_PCOM); +} + +static int +sba_dma_supported( struct pci_dev *dev, dma_addr_t mask) +{ + if (dev == NULL) { + printk(MODULE_NAME ": EISA/ISA/et al not supported\n"); + BUG(); + return(0); + } + + dev->dma_mask = mask; /* save it */ + + /* only support PCI devices */ + return((int) (mask >= 0xffffffff)); +} + + +/* +** map_single returns a fully formed IOVA +*/ +static dma_addr_t +sba_map_single(struct pci_dev *dev, void *addr, size_t size, int direction) +{ + struct ioc *ioc = &sba_list->ioc[0]; /* FIXME : see Multi-IOC below */ + unsigned long flags; + dma_addr_t iovp; + dma_addr_t offset; + u64 *pdir_start; + int pide; + + ASSERT(size > 0); + + /* save offset bits */ + offset = ((dma_addr_t) addr) & ~IOVP_MASK; + + /* round up to nearest IOVP_SIZE */ + size = (size + offset + ~IOVP_MASK) & IOVP_MASK; + + spin_lock_irqsave(&ioc->res_lock, flags); +#ifdef ASSERT_PDIR_SANITY + sba_check_pdir(ioc,"Check before sba_map_single()"); +#endif + +#ifdef CONFIG_PROC_FS + ioc->msingle_calls++; + ioc->msingle_pages += size >> IOVP_SHIFT; +#endif + pide = sba_alloc_range(ioc, size); + iovp = (dma_addr_t) pide << IOVP_SHIFT; + + DBG_RUN("sba_map_single() 0x%p -> 0x%lx", addr, (long) iovp | offset); + + pdir_start = &(ioc->pdir_base[pide]); + + while (size > 0) { + ASSERT(((u8 *)pdir_start)[7] == 0); /* verify availability */ + sba_io_pdir_entry(pdir_start, KERNEL_SPACE, (unsigned long) addr); + + DBG_RUN(" pdir 0x%p %02x%02x%02x%02x%02x%02x%02x%02x\n", + pdir_start, + (u8) (((u8 *) pdir_start)[7]), + (u8) (((u8 *) pdir_start)[6]), + (u8) (((u8 *) pdir_start)[5]), + (u8) (((u8 *) pdir_start)[4]), + (u8) (((u8 *) pdir_start)[3]), + (u8) (((u8 *) pdir_start)[2]), + (u8) (((u8 *) pdir_start)[1]), + (u8) (((u8 *) pdir_start)[0]) + ); + + addr += IOVP_SIZE; + size -= IOVP_SIZE; + pdir_start++; + } + /* form complete address */ +#ifdef ASSERT_PDIR_SANITY + sba_check_pdir(ioc,"Check after sba_map_single()"); +#endif + spin_unlock_irqrestore(&ioc->res_lock, flags); + return SBA_IOVA(ioc, iovp, offset, DEFAULT_DMA_HINT_REG); +} + + +static void +sba_unmap_single(struct pci_dev *dev, dma_addr_t iova, size_t size, int direction) +{ +#ifdef FIXME +/* Multi-IOC (ie N-class) : need to lookup IOC from dev +** o If we can't know about lba PCI data structs, that eliminates ->sysdata. +** o walking up pcidev->parent dead ends at elroy too +** o leaves hashing dev->bus->number into some lookup. +** (may only work for N-class) +** o use (struct pci_hba) and put fields in there for DMA. +** (ioc and per device dma_hint.) +** +** Last one seems the clearest and most promising. +** sba_dma_supported() fill in those fields when the driver queries +** the system for support. +*/ + struct ioc *ioc = (struct ioc *) ((struct pci_hba *) (dev->sysdata))->dma_data; +#else + struct ioc *ioc = &sba_list->ioc[0]; +#endif + + unsigned long flags; + dma_addr_t offset; + offset = iova & ~IOVP_MASK; + + DBG_RUN("%s() iovp 0x%lx/%x\n", __FUNCTION__, (long) iova, size); + + iova ^= offset; /* clear offset bits */ + size += offset; + size = ROUNDUP(size, IOVP_SIZE); + + ASSERT(0 != iova); + + spin_lock_irqsave(&ioc->res_lock, flags); +#ifdef CONFIG_PROC_FS + ioc->usingle_calls++; + ioc->usingle_pages += size >> IOVP_SHIFT; +#endif +#ifdef DELAYED_RESOURCE_CNT + if (ioc->saved_cnt < DELAYED_RESOURCE_CNT) { + ioc->saved_iova[ioc->saved_cnt] = iova; + ioc->saved_size[ioc->saved_cnt] = size; + ioc_saved_cnt++; + } else { + do { +#endif + sba_mark_invalid(ioc, iova, size); + sba_free_range(ioc, iova, size); + +#ifdef DELAYED_RESOURCE_CNT + ioc->saved_cnt--; + iova = ioc->saved_iova[ioc->saved_cnt]; + size = ioc->saved_size[ioc->saved_cnt]; + } while (ioc->saved_cnt) + + /* flush purges */ + (void) (volatile) READ_REG32(ioc->ioc_hpa+IOC_PCOM); + } +#else + /* flush purges */ + READ_REG32(ioc->ioc_hpa+IOC_PCOM); +#endif + spin_unlock_irqrestore(&ioc->res_lock, flags); +} + + +static void * +sba_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) +{ + void *ret; + + if (!hwdev) { + /* only support PCI */ + *dma_handle = 0; + return 0; + } + + ret = (void *) __get_free_pages(GFP_ATOMIC, get_order(size)); + + if (ret) { + memset(ret, 0, size); + *dma_handle = sba_map_single(hwdev, ret, size, 0); + } + + return ret; +} + + +static void +sba_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) +{ + sba_unmap_single(hwdev, dma_handle, size, 0); + free_pages((unsigned long) vaddr, get_order(size)); +} + +/* +** Two address ranges are "virtually contiguous" iff: +** 1) end of prev == start of next, or... append case +** 3) end of next == start of prev prepend case +** +** and they are DMA contiguous *iff*: +** 2) end of prev and start of next are both on a page boundry +** +** (shift left is a quick trick to mask off upper bits) +*/ +#define DMA_CONTIG(__X, __Y) \ + (((((unsigned long) __X) | ((unsigned long) __Y)) << (BITS_PER_LONG - PAGE_SHIFT)) == 0UL) + +/* +** Assumption is two transactions are mutually exclusive. +** ie both go to different parts of memory. +** If both are true, then both transaction are on the same page. +*/ +#define DMA_SAME_PAGE(s1,e1,s2,e2) \ + ( ((((s1) ^ (s2)) >> PAGE_SHIFT) == 0) \ + && ((((e1) ^ (e2)) >> PAGE_SHIFT) == 0) ) + +/* +** Since 0 is a valid pdir_base index value, can't use that +** to determine if a value is valid or not. Use a flag to indicate +** the SG list entry contains a valid pdir index. +*/ +#define PIDE_FLAG 0x80000000UL + +#ifdef DEBUG_LARGE_SG_ENTRIES +int dump_run_sg = 0; +#endif + +static SBA_INLINE int +sba_fill_pdir( + struct ioc *ioc, + struct scatterlist *startsg, + int nents) +{ + struct scatterlist *dma_sg = startsg; /* pointer to current DMA */ + int n_mappings = 0; + u64 *pdirp = 0; + unsigned long dma_offset = 0; + + dma_sg--; + while (nents-- > 0) { + int cnt = sg_dma_len(startsg); + sg_dma_len(startsg) = 0; + +#ifdef DEBUG_LARGE_SG_ENTRIES + if (dump_run_sg) + printk(" %d : %08lx/%05x %p/%05x\n", + nents, + (unsigned long) sg_dma_address(startsg), cnt, + startsg->address, startsg->length + ); +#else + DBG_RUN_SG(" %d : %08lx/%05x %p/%05x\n", + nents, + (unsigned long) sg_dma_address(startsg), cnt, + startsg->address, startsg->length + ); +#endif + /* + ** Look for the start of a new DMA stream + */ + if (sg_dma_address(startsg) & PIDE_FLAG) { + u32 pide = sg_dma_address(startsg) & ~PIDE_FLAG; + dma_offset = (unsigned long) pide & ~IOVP_MASK; + pide >>= IOVP_SHIFT; + pdirp = &(ioc->pdir_base[pide]); + sg_dma_address(startsg) = 0; + ++dma_sg; + sg_dma_address(dma_sg) = (pide << IOVP_SHIFT) + dma_offset; + n_mappings++; + } + + /* + ** Look for a VCONTIG chunk + */ + if (cnt) { + unsigned long vaddr = (unsigned long) startsg->address; + ASSERT(pdirp); + + sg_dma_len(dma_sg) += cnt; + cnt += dma_offset; + dma_offset=0; /* only want offset on first chunk */ + cnt = ROUNDUP(cnt, IOVP_SIZE); +#ifdef CONFIG_PROC_FS + ioc->msg_pages += cnt >> IOVP_SHIFT; +#endif + do { + sba_io_pdir_entry(pdirp, KERNEL_SPACE, vaddr); + vaddr += IOVP_SIZE; + cnt -= IOVP_SIZE; + pdirp++; + } while (cnt > 0); + } + startsg++; + } +#ifdef DEBUG_LARGE_SG_ENTRIES + dump_run_sg = 0; +#endif + return(n_mappings); +} + + + +/* +** First pass is to walk the SG list and determine where the breaks are +** in the DMA stream. Allocates PDIR entries but does not fill them. +** Returns the number of DMA chunks. +** +** Doing the fill seperate from the coalescing/allocation keeps the +** code simpler. Future enhancement could make one pass through +** the sglist do both. +*/ +static SBA_INLINE int +sba_coalesce_chunks( struct ioc *ioc, + struct scatterlist *startsg, + int nents) +{ + int n_mappings = 0; + + while (nents > 0) { + struct scatterlist *dma_sg; /* next DMA stream head */ + unsigned long dma_offset, dma_len; /* start/len of DMA stream */ + struct scatterlist *chunksg; /* virtually contig chunk head */ + unsigned long chunk_addr, chunk_len; /* start/len of VCONTIG chunk */ + + /* + ** Prepare for first/next DMA stream + */ + dma_sg = chunksg = startsg; + dma_len = chunk_len = startsg->length; + chunk_addr = (unsigned long) startsg->address; + dma_offset = 0UL; + + /* + ** This loop terminates one iteration "early" since + ** it's always looking one "ahead". + */ + while (--nents > 0) { + /* ptr to coalesce prev and next */ + struct scatterlist *prev_sg = startsg; + unsigned long prev_end = (unsigned long) prev_sg->address + prev_sg->length; + unsigned long current_end; + + /* PARANOID: clear entries */ + sg_dma_address(startsg) = 0; + sg_dma_len(startsg) = 0; + + /* Now start looking ahead */ + startsg++; + current_end = (unsigned long) startsg->address + startsg->length; + + /* + ** First look for virtually contiguous blocks. + ** PARISC needs this since it's cache is virtually + ** indexed and we need the associated virtual + ** address for each I/O address we map. + ** + ** 1) can we *prepend* the next transaction? + */ + if (current_end == (unsigned long) prev_sg->address) + { + /* prepend : get new offset */ + chunksg = startsg; + chunk_addr = (unsigned long) prev_sg->address; + chunk_len += startsg->length; + dma_len += startsg->length; + continue; + } + + /* + ** 2) or append the next transaction? + */ + if (prev_end == (unsigned long) startsg->address) + { + chunk_len += startsg->length; + dma_len += startsg->length; + continue; + } + +#ifdef DEBUG_LARGE_SG_ENTRIES + dump_run_sg = (chunk_len > IOVP_SIZE); +#endif + /* + ** Not virtually contigous. + ** Terminate prev chunk. + ** Start a new chunk. + ** + ** Once we start a new VCONTIG chunk, the offset + ** can't change. And we need the offset from the first + ** chunk - not the last one. Ergo Successive chunks + ** must start on page boundaries and dove tail + ** with it's predecessor. + */ + sg_dma_len(prev_sg) = chunk_len; + + chunk_len = startsg->length; + dma_offset |= (chunk_addr & ~IOVP_MASK); + ASSERT((0 == (chunk_addr & ~IOVP_MASK)) || + (dma_offset == (chunk_addr & ~IOVP_MASK))); + +#if 0 + /* + ** 4) do the chunks end/start on page boundaries? + ** Easier than 3 since no offsets are involved. + */ + if (DMA_CONTIG(prev_end, startsg->address)) + { + /* + ** Yes. + ** Reset chunk ptr. + */ + chunksg = startsg; + chunk_addr = (unsigned long) startsg->address; + + continue; + } else +#endif + { + break; + } + } + + /* + ** End of DMA Stream + ** Terminate chunk. + ** Allocate space for DMA stream. + */ + sg_dma_len(startsg) = chunk_len; + dma_len = (dma_len + dma_offset + ~IOVP_MASK) & IOVP_MASK; + sg_dma_address(dma_sg) = + PIDE_FLAG + | (sba_alloc_range(ioc, dma_len) << IOVP_SHIFT) + | dma_offset; + n_mappings++; + } + + return n_mappings; +} + + +/* +** And this algorithm still generally only ends up coalescing entries +** that happens to be on the same page due to how sglists are assembled. +*/ +static int +sba_map_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction) +{ + struct ioc *ioc = &sba_list->ioc[0]; /* FIXME : see Multi-IOC below */ + int coalesced, filled = 0; + unsigned long flags; + + DBG_RUN_SG("%s() START %d entries\n", __FUNCTION__, nents); + + /* Fast path single entry scatterlists. */ + if (nents == 1) { + sg_dma_address(sglist)= sba_map_single(dev, sglist->address, + sglist->length, direction); + sg_dma_len(sglist)= sglist->length; + return 1; + } + + spin_lock_irqsave(&ioc->res_lock, flags); + +#ifdef ASSERT_PDIR_SANITY + if (sba_check_pdir(ioc,"Check before sba_map_sg()")) + { + sba_dump_sg(ioc, sglist, nents); + panic("Check before sba_map_sg()"); + } +#endif + +#ifdef CONFIG_PROC_FS + ioc->msg_calls++; +#endif + + /* + ** First coalesce the chunks and allocate I/O pdir space + ** + ** If this is one DMA stream, we can properly map using the + ** correct virtual address associated with each DMA page. + ** w/o this association, we wouldn't have coherent DMA! + ** Access to the virtual address is what forces a two pass algorithm. + */ + coalesced = sba_coalesce_chunks(ioc, sglist, nents); + + /* + ** Program the I/O Pdir + ** + ** map the virtual addresses to the I/O Pdir + ** o dma_address will contain the pdir index + ** o dma_len will contain the number of bytes to map + ** o address contains the virtual address. + */ + filled = sba_fill_pdir(ioc, sglist, nents); + +#ifdef ASSERT_PDIR_SANITY + if (sba_check_pdir(ioc,"Check after sba_map_sg()")) + { + sba_dump_sg(ioc, sglist, nents); + panic("Check after sba_map_sg()\n"); + } +#endif + + spin_unlock_irqrestore(&ioc->res_lock, flags); + + ASSERT(coalesced == filled); + DBG_RUN_SG("%s() DONE %d mappings\n", __FUNCTION__, filled); + + return filled; +} + + +static void +sba_unmap_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction) +{ + struct ioc *ioc = &sba_list->ioc[0]; /* FIXME : see Multi-IOC below */ +#ifdef ASSERT_PDIR_SANITY + unsigned long flags; +#endif + + DBG_RUN_SG("%s() START %d entries, %p,%x\n", + __FUNCTION__, nents, sglist->address, sglist->length); + +#ifdef CONFIG_PROC_FS + ioc->usg_calls++; +#endif + +#ifdef ASSERT_PDIR_SANITY + spin_lock_irqsave(&ioc->res_lock, flags); + sba_check_pdir(ioc,"Check before sba_unmap_sg()"); + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + + while (sg_dma_len(sglist) && nents--) { + +#ifdef CONFIG_PROC_FS + ioc->usg_pages += sg_dma_len(sglist) >> PAGE_SHIFT; +#endif + sba_unmap_single(dev, sg_dma_address(sglist), sg_dma_len(sglist), direction); + ++sglist; + } + + DBG_RUN_SG("%s() DONE (nents %d)\n", __FUNCTION__, nents); + +#ifdef ASSERT_PDIR_SANITY + spin_lock_irqsave(&ioc->res_lock, flags); + sba_check_pdir(ioc,"Check after sba_unmap_sg()"); + spin_unlock_irqrestore(&ioc->res_lock, flags); +#endif + +} + +static struct pci_dma_ops sba_ops = { + sba_dma_supported, + sba_alloc_consistent, /* allocate cacheable host mem */ + sba_free_consistent, /* release cacheable host mem */ + sba_map_single, + sba_unmap_single, + sba_map_sg, + sba_unmap_sg, + NULL, /* dma_sync_single */ + NULL /* dma_sync_sg */ +}; + + +/************************************************************************** +** +** SBA PAT PDC support +** +** o call pdc_pat_cell_module() +** o store ranges in PCI "resource" structures +** +**************************************************************************/ + +static void +sba_get_pat_resources(struct sba_device *sba_dev) +{ +#if 0 +/* +** TODO/REVISIT/FIXME: support for directed ranges requires calls to +** PAT PDC to program the SBA/LBA directed range registers...this +** burden may fall on the LBA code since it directly supports the +** PCI subsystem. It's not clear yet. - ggg +*/ +PAT_MOD(mod)->mod_info.mod_pages = PAT_GET_MOD_PAGES(temp); + FIXME : ??? +PAT_MOD(mod)->mod_info.dvi = PAT_GET_DVI(temp); + Tells where the dvi bits are located in the address. +PAT_MOD(mod)->mod_info.ioc = PAT_GET_IOC(temp); + FIXME : ??? +#endif +} + + +/************************************************************** +* +* Initialization and claim +* +***************************************************************/ + + +static void +sba_ioc_init(struct ioc *ioc) +{ + extern unsigned long mem_max; /* arch.../setup.c */ + extern void lba_init_iregs(void *, u32, u32); /* arch.../lba_pci.c */ + + u32 iova_space_size, iova_space_mask; + void * pdir_base; + int pdir_size, iov_order; + + /* + ** Determine IOVA Space size from memory size. + ** Using "mem_max" is a kluge. + ** + ** Ideally, PCI drivers would register the maximum number + ** of DMA they can have outstanding for each device they + ** own. Next best thing would be to guess how much DMA + ** can be outstanding based on PCI Class/sub-class. Both + ** methods still require some "extra" to support PCI + ** Hot-Plug/Removal of PCI cards. (aka PCI OLARD). + ** + ** While we have 32-bits "IOVA" space, top two 2 bits are used + ** for DMA hints - ergo only 30 bits max. + */ + /* limit IOVA space size to 1MB-1GB */ + if (mem_max < (sba_mem_ratio*1024*1024)) { + iova_space_size = 1024*1024; +#ifdef __LP64__ + } else if (mem_max > (sba_mem_ratio*512*1024*1024)) { + iova_space_size = 512*1024*1024; +#endif + } else { + iova_space_size = (u32) (mem_max/sba_mem_ratio); + } + + /* + ** iova space must be log2() in size. + ** thus, pdir/res_map will also be log2(). + */ + iov_order = get_order(iova_space_size >> (IOVP_SHIFT-PAGE_SHIFT)); + ASSERT(iov_order <= (30 - IOVP_SHIFT)); /* iova_space_size <= 1GB */ + ASSERT(iov_order >= (20 - IOVP_SHIFT)); /* iova_space_size >= 1MB */ + iova_space_size = 1 << (iov_order + IOVP_SHIFT); + + ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64); + + ASSERT(pdir_size < 4*1024*1024); /* max pdir size < 4MB */ + + /* Verify it's a power of two */ + ASSERT((1 << get_order(pdir_size)) == (pdir_size >> PAGE_SHIFT)); + + DBG_INIT("%s() hpa 0x%p mem %dMBIOV %dMB (%d bits) PDIR size 0x%0x", + __FUNCTION__, ioc->ioc_hpa, (int) (mem_max>>20), + iova_space_size>>20, iov_order + PAGE_SHIFT, pdir_size); + + /* FIXME : DMA HINTs not used */ + ioc->hint_shift_pdir = iov_order + PAGE_SHIFT; + ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT)); + + ioc->pdir_base = + pdir_base = (void *) __get_free_pages(GFP_KERNEL, get_order(pdir_size)); + if (NULL == pdir_base) + { + panic(__FILE__ ":%s() could not allocate I/O Page Table\n", __FUNCTION__); + } + memset(pdir_base, 0, pdir_size); + + DBG_INIT("sba_ioc_init() pdir %p size %x hint_shift_pdir %x hint_mask_pdir %lx\n", + pdir_base, pdir_size, + ioc->hint_shift_pdir, ioc->hint_mask_pdir); + + ASSERT((((unsigned long) pdir_base) & PAGE_MASK) == (unsigned long) pdir_base); + WRITE_REG64(virt_to_phys(pdir_base), (u64 *)(ioc->ioc_hpa+IOC_PDIR_BASE)); + + DBG_INIT(" base %p\n", pdir_base); + + /* build IMASK for IOC and Elroy */ + iova_space_mask = 0xffffffff; + iova_space_mask <<= (iov_order + PAGE_SHIFT); + + /* + ** On C3000 w/512MB mem, HP-UX 10.20 reports: + ** ibase=0, imask=0xFE000000, size=0x2000000. + */ + ioc->ibase = IOC_IOVA_SPACE_BASE | 1; /* bit 0 == enable bit */ + ioc->imask = iova_space_mask; /* save it */ + + DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n", __FUNCTION__, + ioc->ibase, ioc->imask); + + /* + ** FIXME: Hint registers are programmed with default hint + ** values during boot, so hints should be sane even if we + ** can't reprogram them the way drivers want. + */ + + /* + ** setup Elroy IBASE/IMASK registers as well. + */ + lba_init_iregs(ioc->ioc_hpa, ioc->ibase, ioc->imask); + + /* + ** Program the IOC's ibase and enable IOVA translation + */ + WRITE_REG32(ioc->ibase, ioc->ioc_hpa+IOC_IBASE); + WRITE_REG32(ioc->imask, ioc->ioc_hpa+IOC_IMASK); + + /* Set I/O PDIR Page size to 4K */ + WRITE_REG32(0, ioc->ioc_hpa+IOC_TCNFG); + + /* + ** Clear I/O TLB of any possible entries. + ** (Yes. This is a it paranoid...but so what) + */ + WRITE_REG32(0 | 31, ioc->ioc_hpa+IOC_PCOM); + + DBG_INIT("%s() DONE\n", __FUNCTION__); +} + + + +/************************************************************************** +** +** SBA initialization code (HW and SW) +** +** o identify SBA chip itself +** o initialize SBA chip modes (HardFail) +** o initialize SBA chip modes (HardFail) +** o FIXME: initialize DMA hints for reasonable defaults +** +**************************************************************************/ + +static void +sba_hw_init(struct sba_device *sba_dev) +{ + int i; + int num_ioc; + u32 ioc_ctl; + + ioc_ctl = READ_REG32(sba_dev->sba_hpa+IOC_CTRL); + DBG_INIT("%s() hpa 0x%p ioc_ctl 0x%x ->", __FUNCTION__, sba_dev->sba_hpa, ioc_ctl ); + ioc_ctl &= ~(IOC_CTRL_RM | IOC_CTRL_NC); + ASSERT(ioc_ctl & IOC_CTRL_TE); /* astro: firmware enables this */ + + WRITE_REG32(ioc_ctl, sba_dev->sba_hpa+IOC_CTRL); + +#ifdef SBA_DEBUG_INIT + ioc_ctl = READ_REG32(sba_dev->sba_hpa+IOC_CTRL); + DBG_INIT(" 0x%x\n", ioc_ctl ); +#endif + + if (IS_ASTRO(sba_dev->iodc)) { + /* PAT_PDC (L-class) also reports the same goofy base */ + sba_dev->ioc[0].ioc_hpa = (char *) ASTRO_IOC_OFFSET; + num_ioc = 1; + } else { + sba_dev->ioc[0].ioc_hpa = sba_dev->ioc[1].ioc_hpa = 0; + num_ioc = 2; + } + + sba_dev->num_ioc = num_ioc; + for( i = 0; i < num_ioc; i++) + { + (unsigned long) sba_dev->ioc[i].ioc_hpa += (unsigned long) sba_dev->sba_hpa + IKE_IOC_OFFSET(i); + + /* + ** Make sure the box crashes if we get any errors on a rope. + */ + WRITE_REG32(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE0_CTL); + WRITE_REG32(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE1_CTL); + WRITE_REG32(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE2_CTL); + WRITE_REG32(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE3_CTL); + WRITE_REG32(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE4_CTL); + WRITE_REG32(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE5_CTL); + WRITE_REG32(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE6_CTL); + WRITE_REG32(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE7_CTL); + + /* flush out the writes */ + READ_REG32(sba_dev->ioc[i].ioc_hpa + ROPE7_CTL); + + sba_ioc_init(&(sba_dev->ioc[i])); + } +} + +static void +sba_common_init(struct sba_device *sba_dev) +{ + int i; + + /* add this one to the head of the list (order doesn't matter) + ** This will be useful for debugging - especially if we get coredumps + */ + sba_dev->next = sba_list; + sba_list = sba_dev; + sba_count++; + + for(i=0; i< sba_dev->num_ioc; i++) { + int res_size; +#ifdef CONFIG_DMB_TRAP + extern void iterate_pages(unsigned long , unsigned long , + void (*)(pte_t * , unsigned long), + unsigned long ); + void set_data_memory_break(pte_t * , unsigned long); +#endif + /* resource map size dictated by pdir_size */ + res_size = sba_dev->ioc[i].pdir_size/sizeof(u64); /* entries */ + res_size >>= 3; /* convert bit count to byte count */ + DBG_INIT("%s() res_size 0x%x\n", __FUNCTION__, res_size); + + sba_dev->ioc[i].res_size = res_size; + sba_dev->ioc[i].res_map = (char *) __get_free_pages(GFP_KERNEL, get_order(res_size)); + +#ifdef CONFIG_DMB_TRAP + iterate_pages( sba_dev->ioc[i].res_map, res_size, + set_data_memory_break, 0); +#endif + + if (NULL == sba_dev->ioc[i].res_map) + { + panic(__FILE__ ":%s() could not allocate resource map\n", __FUNCTION__ ); + } + + memset(sba_dev->ioc[i].res_map, 0, res_size); + /* next available IOVP - circular search */ + sba_dev->ioc[i].res_hint = (unsigned long *) + &(sba_dev->ioc[i].res_map[L1_CACHE_BYTES]); + +#ifdef ASSERT_PDIR_SANITY + /* Mark first bit busy - ie no IOVA 0 */ + sba_dev->ioc[i].res_map[0] = 0x80; + sba_dev->ioc[i].pdir_base[0] = 0xeeffc0addbba0080ULL; +#endif + +#ifdef CONFIG_DMB_TRAP + iterate_pages( sba_dev->ioc[i].res_map, res_size, + set_data_memory_break, 0); + iterate_pages( sba_dev->ioc[i].pdir_base, sba_dev->ioc[i].pdir_size, + set_data_memory_break, 0); +#endif + + DBG_INIT("sba_common_init() %d res_map %x %p\n", + i, res_size, sba_dev->ioc[i].res_map); + } + + sba_dev->sba_lock = SPIN_LOCK_UNLOCKED; +} + +#ifdef CONFIG_PROC_FS +static int sba_proc_info(char *buf, char **start, off_t offset, int len) +{ + struct sba_device *sba_dev = sba_list; +/* FIXME: Multi-IOC support broken! */ + struct ioc *ioc = &sba_dev->ioc[0]; + int total_pages = (int) (ioc->res_size << 3); /* 8 bits per byte */ + unsigned long i = 0, avg = 0, min, max; + + sprintf(buf, "%s rev %d.%d\n", + parisc_getHWdescription(sba_dev->iodc->hw_type, + sba_dev->iodc->hversion, sba_dev->iodc->sversion), + (sba_dev->hw_rev & 0x7) + 1, + (sba_dev->hw_rev & 0x18) >> 3 + ); + sprintf(buf, "%sIO PDIR size : %d bytes (%d entries)\n", + buf, + ((ioc->res_size << 3) * sizeof(u64)), /* 8 bits per byte */ + total_pages); /* 8 bits per byte */ + + sprintf(buf, "%sIO PDIR entries : %ld free %ld used (%d%%)\n", buf, + total_pages - ioc->used_pages, ioc->used_pages, + (int) (ioc->used_pages * 100 / total_pages)); + + sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", + buf, ioc->res_size, ioc->res_size << 3); /* 8 bits per byte */ + + min = max = ioc->avg_search[0]; + for (i = 0; i < SBA_SEARCH_SAMPLE; i++) { + avg += ioc->avg_search[i]; + if (ioc->avg_search[i] > max) max = ioc->avg_search[i]; + if (ioc->avg_search[i] < min) min = ioc->avg_search[i]; + } + avg /= SBA_SEARCH_SAMPLE; + sprintf(buf, "%s Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n", + buf, min, avg, max); + + sprintf(buf, "%spci_map_single(): %8ld calls %8ld pages (avg %d/1000)\n", + buf, ioc->msingle_calls, ioc->msingle_pages, + (int) ((ioc->msingle_pages * 1000)/ioc->msingle_calls)); + + /* KLUGE - unmap_sg calls unmap_single for each mapped page */ + min = ioc->usingle_calls - ioc->usg_calls; + max = ioc->usingle_pages - ioc->usg_pages; + sprintf(buf, "%spci_unmap_single: %8ld calls %8ld pages (avg %d/1000)\n", + buf, min, max, + (int) ((max * 1000)/min)); + + sprintf(buf, "%spci_map_sg() : %8ld calls %8ld pages (avg %d/1000)\n", + buf, ioc->msg_calls, ioc->msg_pages, + (int) ((ioc->msg_pages * 1000)/ioc->msg_calls)); + + sprintf(buf, "%spci_unmap_sg() : %8ld calls %8ld pages (avg %d/1000)\n", + buf, ioc->usg_calls, ioc->usg_pages, + (int) ((ioc->usg_pages * 1000)/ioc->usg_calls)); + + return strlen(buf); +} + +static int +sba_resource_map(char *buf, char **start, off_t offset, int len) +{ + struct sba_device *sba_dev = sba_list; + struct ioc *ioc = &sba_dev->ioc[0]; + unsigned long *res_ptr = (unsigned long *)ioc->res_map; + int i; + + for(i = 0; i < (ioc->res_size / sizeof(unsigned long)); ++i, ++res_ptr) { + if ((i & 7) == 0) + strcat(buf,"\n "); + sprintf(buf, "%s %08lx", buf, *res_ptr); + } + strcat(buf, "\n"); + + return strlen(buf); +} +#endif + +/* +** Determine if lba should claim this chip (return 0) or not (return 1). +** If so, initialize the chip and tell other partners in crime they +** have work to do. +*/ +int +sba_driver_callback(struct hp_device *d, struct pa_iodc_driver *dri) +{ + struct sba_device *sba_dev; + u32 func_class; + int i; + + if (IS_ASTRO(d)) { + static char astro_rev[]="Astro ?.?"; + + /* Read HW Rev First */ + func_class = READ_REG32(d->hpa); + + astro_rev[6] = '1' + (char) (func_class & 0x7); + astro_rev[8] = '0' + (char) ((func_class & 0x18) >> 3); + dri->version = astro_rev; + } else { + static char ike_rev[]="Ike rev ?"; + + /* Read HW Rev First */ + func_class = READ_REG32(d->hpa + SBA_FCLASS); + + ike_rev[8] = '0' + (char) (func_class & 0xff); + dri->version = ike_rev; + } + + printk("%s found %s at 0x%p\n", dri->name, dri->version, d->hpa); + + sba_dev = kmalloc(sizeof(struct sba_device), GFP_KERNEL); + if (NULL == sba_dev) + { + printk(MODULE_NAME " - couldn't alloc sba_device\n"); + return(1); + } + memset(sba_dev, 0, sizeof(struct sba_device)); + for(i=0; iioc[i].res_lock)); + + + sba_dev->hw_rev = func_class; + sba_dev->iodc = d; + sba_dev->sba_hpa = d->hpa; /* faster access */ + + sba_get_pat_resources(sba_dev); + sba_hw_init(sba_dev); + sba_common_init(sba_dev); + + hppa_dma_ops = &sba_ops; + +#ifdef CONFIG_PROC_FS + if (IS_ASTRO(d)) { + create_proc_info_entry("Astro", 0, proc_runway_root, sba_proc_info); + } else { + create_proc_info_entry("Ike", 0, proc_runway_root, sba_proc_info); + } + create_proc_info_entry("bitmap", 0, proc_runway_root, sba_resource_map); +#endif + return 0; +} diff --git a/arch/parisc/kernel/semaphore.c b/arch/parisc/kernel/semaphore.c new file mode 100644 index 000000000000..81156de7dd82 --- /dev/null +++ b/arch/parisc/kernel/semaphore.c @@ -0,0 +1,239 @@ +/* + * Just taken from alpha implementation. + * This can't work well, perhaps. + */ +/* + * Generic semaphore code. Buyer beware. Do your own + * specific changes in + */ + +#include +#include + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to sleep, while the "waking" variable is + * incremented when the "up()" code goes to wake up waiting + * processes. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * waking_non_zero() (from asm/semaphore.h) must execute + * atomically. + * + * When __up() is called, the count was negative before + * incrementing it, and we need to wake up somebody. + * + * This routine adds one to the count of processes that need to + * wake up and exit. ALL waiting processes actually wake up but + * only the one that gets to the "waking" field first will gate + * through and acquire the semaphore. The others will go back + * to sleep. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in + * where we want to avoid any extra jumps and calls. + */ +void __up(struct semaphore *sem) +{ + wake_one_more(sem); + wake_up(&sem->wait); +} + +/* + * Perform the "down" function. Return zero for semaphore acquired, + * return negative for signalled out of the function. + * + * If called from __down, the return is ignored and the wait loop is + * not interruptible. This means that a task waiting on a semaphore + * using "down()" cannot be killed until someone does an "up()" on + * the semaphore. + * + * If called from __down_interruptible, the return value gets checked + * upon return. If the return value is negative then the task continues + * with the negative value in the return register (it can be tested by + * the caller). + * + * Either form may be used in conjunction with "up()". + * + */ + + +#define DOWN_HEAD(task_state) \ + \ + \ + current->state = (task_state); \ + add_wait_queue(&sem->wait, &wait); \ + \ + /* \ + * Ok, we're set up. sem->count is known to be less than zero \ + * so we must wait. \ + * \ + * We can let go the lock for purposes of waiting. \ + * We re-acquire it after awaking so as to protect \ + * all semaphore operations. \ + * \ + * If "up()" is called before we call waking_non_zero() then \ + * we will catch it right away. If it is called later then \ + * we will have to go through a wakeup cycle to catch it. \ + * \ + * Multiple waiters contend for the semaphore lock to see \ + * who gets to gate through and who has to wait some more. \ + */ \ + for (;;) { + +#define DOWN_TAIL(task_state) \ + current->state = (task_state); \ + } \ + current->state = TASK_RUNNING; \ + remove_wait_queue(&sem->wait, &wait); + +void __down(struct semaphore * sem) +{ + DECLARE_WAITQUEUE(wait, current); + + DOWN_HEAD(TASK_UNINTERRUPTIBLE) + if (waking_non_zero(sem)) + break; + schedule(); + DOWN_TAIL(TASK_UNINTERRUPTIBLE) +} + +int __down_interruptible(struct semaphore * sem) +{ + DECLARE_WAITQUEUE(wait, current); + int ret = 0; + + DOWN_HEAD(TASK_INTERRUPTIBLE) + + ret = waking_non_zero_interruptible(sem, current); + if (ret) + { + if (ret == 1) + /* ret != 0 only if we get interrupted -arca */ + ret = 0; + break; + } + schedule(); + DOWN_TAIL(TASK_INTERRUPTIBLE) + return ret; +} + +int __down_trylock(struct semaphore * sem) +{ + return waking_non_zero_trylock(sem); +} + + +/* Wait for the lock to become unbiased. Readers + * are non-exclusive. =) + */ +void down_read_failed(struct rw_semaphore *sem) +{ + DECLARE_WAITQUEUE(wait, current); + + __up_read(sem); /* this takes care of granting the lock */ + + add_wait_queue(&sem->wait, &wait); + + while (atomic_read(&sem->count) < 0) { + set_task_state(current, TASK_UNINTERRUPTIBLE); + if (atomic_read(&sem->count) >= 0) + break; + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + current->state = TASK_RUNNING; +} + +void down_read_failed_biased(struct rw_semaphore *sem) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&sem->wait, &wait); /* put ourselves at the head of the list */ + + for (;;) { + if (sem->read_bias_granted && xchg(&sem->read_bias_granted, 0)) + break; + set_task_state(current, TASK_UNINTERRUPTIBLE); + if (!sem->read_bias_granted) + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + current->state = TASK_RUNNING; +} + + +/* Wait for the lock to become unbiased. Since we're + * a writer, we'll make ourselves exclusive. + */ +void down_write_failed(struct rw_semaphore *sem) +{ + DECLARE_WAITQUEUE(wait, current); + + __up_write(sem); /* this takes care of granting the lock */ + + add_wait_queue_exclusive(&sem->wait, &wait); + + while (atomic_read(&sem->count) < 0) { + set_task_state(current, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + if (atomic_read(&sem->count) >= 0) + break; /* we must attempt to aquire or bias the lock */ + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + current->state = TASK_RUNNING; +} + +void down_write_failed_biased(struct rw_semaphore *sem) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue_exclusive(&sem->write_bias_wait, &wait); /* put ourselves at the end of the list */ + + for (;;) { + if (sem->write_bias_granted && xchg(&sem->write_bias_granted, 0)) + break; + set_task_state(current, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + if (!sem->write_bias_granted) + schedule(); + } + + remove_wait_queue(&sem->write_bias_wait, &wait); + current->state = TASK_RUNNING; + + /* if the lock is currently unbiased, awaken the sleepers + * FIXME: this wakes up the readers early in a bit of a + * stampede -> bad! + */ + if (atomic_read(&sem->count) >= 0) + wake_up(&sem->wait); +} + + +/* Called when someone has done an up that transitioned from + * negative to non-negative, meaning that the lock has been + * granted to whomever owned the bias. + */ +void rwsem_wake_readers(struct rw_semaphore *sem) +{ + if (xchg(&sem->read_bias_granted, 1)) + BUG(); + wake_up(&sem->wait); +} + +void rwsem_wake_writer(struct rw_semaphore *sem) +{ + if (xchg(&sem->write_bias_granted, 1)) + BUG(); + wake_up(&sem->write_bias_wait); +} diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c new file mode 100644 index 000000000000..ec8f3cf36818 --- /dev/null +++ b/arch/parisc/kernel/setup.c @@ -0,0 +1,616 @@ +/* $Id: setup.c,v 1.8 2000/02/02 04:42:38 prumpf Exp $ + * + * Initial setup-routines for HP 9000 based hardware. + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Modifications for PA-RISC (C) 1999 Helge Deller + * Modifications copyright 1999 SuSE GmbH (Philipp Rumpf) + * Modifications copyright 2000 Martin K. Petersen + * Modifications copyright 2000 Philipp Rumpf + * + * Initial PA-RISC Version: 04-23-1999 by Helge Deller + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* for register_driver() stuff */ +#include +#include +#include +#include +#include +#include +#include /* for pa7300lc_init() proto */ + +#include /* for struct irq_region */ +#include /* for mem_pdc_call() proto */ +#include /* for PA_VIEW PDC_PAT_CPU_GET_NUMBER etc */ + +#include +#include /* for get_cache_info() proto */ + +#define COMMAND_LINE_SIZE 1024 +char saved_command_line[COMMAND_LINE_SIZE]; + +/* +** KLUGE ALERT! +** +** We *really* should be using a combination of request_resource() +** and request_region()! But request_region() requires kmalloc since +** returns a new struct resource. And kmalloc just isn't available +** until after mem_init() is called from start_kernel(). +** +** FIXME: assume contiguous memory initially. +** Additional chunks of memory might be added to sysram_resource.sibling. +*/ +static struct resource sysrom_resource = { + name: "System ROM", start: 0x0f0000000UL, end: 0x0f00fffffUL, + flags: IORESOURCE_BUSY | IORESOURCE_MEM, + parent: &iomem_resource, sibling: NULL, child: NULL }; + +static struct resource pdcdata_resource; + +static struct resource sysram_resource = { + name: "System RAM", start: 0UL, end: ~0UL /* bogus */, + flags: IORESOURCE_MEM, + parent: &iomem_resource, sibling: &sysrom_resource, child: &pdcdata_resource}; + +extern char _text; /* start of kernel code, defined by linker */ +extern int data_start; +extern char _edata; /* end of data, begin BSS, defined by linker */ +extern char _end; /* end of BSS, defined by linker */ + +static struct resource data_resource = { + name: "kernel Data", start: virt_to_phys(&data_start), end: virt_to_phys(&_end)-1, + flags: IORESOURCE_BUSY | IORESOURCE_MEM, + parent: &sysram_resource, sibling: NULL, child: NULL}; + +static struct resource code_resource = { + name: "Kernel Code", start: virt_to_phys(&_text), end: virt_to_phys(&data_start)-1, + flags: IORESOURCE_BUSY | IORESOURCE_MEM, + parent: &sysram_resource, sibling: &data_resource, child: NULL}; + +static struct resource pdcdata_resource = { + name: "PDC data (Page Zero)", start: 0, end: 0x9ff, + flags: IORESOURCE_BUSY | IORESOURCE_MEM, + parent: &sysram_resource, sibling: &code_resource, child: NULL}; + + + + +struct system_cpuinfo_parisc boot_cpu_data; +struct cpuinfo_parisc cpu_data[NR_CPUS]; + +extern void do_inventory(void); +extern void cache_init(void); +extern struct hp_device * register_module(void *hpa); + +static int cpu_driver_callback(struct hp_device *, struct pa_iodc_driver *); + +static struct pa_iodc_driver cpu_drivers_for[] = { + {HPHW_NPROC, 0x0, 0x0, 0x0, 0, 0, + DRIVER_CHECK_HWTYPE, + "CPU", "PARISC", (void *) cpu_driver_callback}, + {0,0,0,0,0,0, + 0, + (char *) NULL, (char *) NULL, (void *) NULL } +}; + +static long fallback_cpu_hpa[] = { 0xfffa0000L, 0xfffbe000L, 0x0 }; + + +/* +** PARISC CPU driver - claim "device" and initialize CPU data structures. +** +** Consolidate per CPU initialization into (mostly) one module. +** Monarch CPU will initialize boot_cpu_data which shouldn't +** change once the system has booted. +** +** The callback *should* do per-instance initialization of +** everything including the monarch. Some of the code that's +** in setup.c:start_parisc() should migrate here and start_parisc() +** should "register_driver(cpu_driver_for)" before calling +** do_inventory(). +** +** The goal of consolidating CPU initialization into one place is +** to make sure all CPU's get initialized the same way. +** It would be nice if the even the manarch through the exact same code path. +** (up to rendevous at least). +*/ +#undef ASSERT +#define ASSERT(expr) \ + if(!(expr)) { \ + printk( "\n" __FILE__ ":%d: Assertion " #expr " failed!\n",__LINE__); \ + panic(#expr); \ + } + +static int +cpu_driver_callback(struct hp_device *d, struct pa_iodc_driver *dri) +{ +#ifdef __LP64__ + extern int pdc_pat; /* arch/parisc/kernel/inventory.c */ + static unsigned long pdc_result[32] __attribute__ ((aligned (8))) = {0,0,0,0}; +#endif + struct cpuinfo_parisc *p; + +#ifndef CONFIG_SMP + if (boot_cpu_data.cpu_count > 0) { + printk(KERN_INFO "CONFIG_SMP disabled - not claiming addional CPUs\n"); + return(1); + } +#endif + + p = &cpu_data[boot_cpu_data.cpu_count]; + boot_cpu_data.cpu_count++; + +/* TODO: Enable FP regs - done early in start_parisc() now */ + + /* initialize counters */ + memset(p, 0, sizeof(struct cpuinfo_parisc)); + + p->hpa = (unsigned long) d->hpa; /* save CPU hpa */ + +#ifdef __LP64__ + if (pdc_pat) { + ulong status; + pdc_pat_cell_mod_maddr_block_t pa_pdc_cell; + + status = pdc_pat_cell_module(& pdc_result, d->pcell_loc, + d->mod_index, PA_VIEW, & pa_pdc_cell); + + ASSERT(PDC_RET_OK == status); + + /* verify it's the same as what do_pat_inventory() found */ + ASSERT(d->mod_info == pa_pdc_cell.mod_info); + ASSERT(d->pmod_loc == pa_pdc_cell.mod_location); + ASSERT(d->mod_path == pa_pdc_cell.mod_path); + + p->txn_addr = pa_pdc_cell.mod[0]; /* id_eid for IO sapic */ + + /* get the cpu number */ + status = mem_pdc_call( PDC_PAT_CPU, PDC_PAT_CPU_GET_NUMBER, + __pa(& pdc_result), d->hpa); + + ASSERT(PDC_RET_OK == status); + + p->cpuid = pdc_result[0]; + + } else +#endif + { + p->txn_addr = (unsigned long) d->hpa; /* for normal parisc */ + + /* logical CPU ID and update global counter */ + p->cpuid = boot_cpu_data.cpu_count - 1; + } + + /* + ** itimer and ipi IRQ handlers are statically initialized in + ** arch/parisc/kernel/irq.c + */ + p->region = irq_region[IRQ_FROM_REGION(CPU_IRQ_REGION)]; + + return(0); +} + + +void __xchg_called_with_bad_pointer(void) +{ + printk(KERN_EMERG "xchg() called with bad pointer !\n"); +} + + +/* Some versions of IODC don't list the CPU, and since we don't walk + * the bus yet, we have to probe for processors at well known hpa + * addresses. + */ + +void __init register_fallback_cpu (void) +{ + struct hp_device *d = NULL; + int i = 0; + +#ifdef CONFIG_SMP +#error "Revisit CPU fallback addresses for SMP (Assuming bus walk hasn't been implemented)" +#endif + printk ("No CPUs reported by firmware - probing...\n"); + + while (fallback_cpu_hpa[i]) { + + d = register_module ((void *) fallback_cpu_hpa[i]); + + if (d > 0) { + printk ("Found CPU at %lx\n", fallback_cpu_hpa[i]); + cpu_driver_callback (d, 0); + return; + } + + i++; + } + + panic ("No CPUs found. System halted.\n"); + return; +} + + +/* + * Get CPU information and store it in the boot_cpu_data structure. */ +void __init collect_boot_cpu_data(void) +{ + memset(&boot_cpu_data,0,sizeof(boot_cpu_data)); + + boot_cpu_data.cpu_hz = 100 * PAGE0->mem_10msec; /* Hz of this PARISC */ + + /* get CPU-Model Information... */ +#define p ((unsigned long *)&boot_cpu_data.pdc.model) + if(pdc_model_info(&boot_cpu_data.pdc.model)==0) + printk("model %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]); +#undef p + + if(pdc_model_versions(&boot_cpu_data.pdc.versions, 0)==0) + printk("vers %08lx\n", boot_cpu_data.pdc.versions.cpuid); + + if(pdc_model_cpuid(&boot_cpu_data.pdc.cpuid)==0) + printk("cpuid %08lx\n", boot_cpu_data.pdc.cpuid.cpuid); + + printk("CPUID vers %ld rev %ld\n", + (boot_cpu_data.pdc.cpuid.cpuid >> 5) & 127, + boot_cpu_data.pdc.cpuid.cpuid & 31); + + if (pdc_model_sysmodel(boot_cpu_data.pdc.sys_model_name)==0) + printk("model %s\n",boot_cpu_data.pdc.sys_model_name); + + boot_cpu_data.model_name = parisc_getHWdescription(HPHW_NPROC, + boot_cpu_data.pdc.model.hversion>>4, + boot_cpu_data.pdc.model.sversion>>8); + + boot_cpu_data.hversion = boot_cpu_data.pdc.model.hversion; + boot_cpu_data.sversion = boot_cpu_data.pdc.model.sversion; + + boot_cpu_data.cpu_type = + parisc_get_cpu_type(boot_cpu_data.pdc.model.hversion); + + boot_cpu_data.cpu_name = cpu_name_version[boot_cpu_data.cpu_type][0]; + boot_cpu_data.family_name = cpu_name_version[boot_cpu_data.cpu_type][1]; +} + + +#ifdef __LP64__ +#define COMMAND_GLOBAL 0xfffffffffffe0030UL +#else +#define COMMAND_GLOBAL 0xfffe0030 +#endif + +#define CMD_RESET 5 /* reset any module */ + +/* +** The Wright Brothers and Gecko systems have a H/W problem +** (Lasi...'nuf said) may cause a broadcast reset to lockup +** the system. An HVERSION dependent PDC call was developed +** to perform a "safe", platform specific broadcast reset instead +** of kludging up all the code. +** +** Older machines which do not implement PDC_BROADCAST_RESET will +** return (with an error) and the regular broadcast reset can be +** issued. Obviously, if the PDC does implement PDC_BROADCAST_RESET +** the PDC call will not return (the system will be reset). +*/ +static int +reset_parisc(struct notifier_block *self, unsigned long command, void *ptr) +{ + printk("%s: %s(cmd=%lu)\n", __FILE__, __FUNCTION__, command); + + switch(command) { + case MACH_RESTART: +#ifdef FASTBOOT_SELFTEST_SUPPORT + /* + ** If user has modified the Firmware Selftest Bitmap, + ** run the tests specified in the bitmap after the + ** system is rebooted w/PDC_DO_RESET. + ** + ** ftc_bitmap = 0x1AUL "Skip destructive memory tests" + ** + ** Using "directed resets" at each processor with the MEM_TOC + ** vector cleared will also avoid running destructive + ** memory self tests. (Not implemented yet) + */ + if (ftc_bitmap) { + mem_pdc_call( PDC_BROADCAST_RESET, + PDC_DO_FIRM_TEST_RESET, PDC_FIRM_TEST_MAGIC, + ftc_bitmap); + } +#endif + + /* "Normal" system reset */ + (void) mem_pdc_call(PDC_BROADCAST_RESET, PDC_DO_RESET, + 0L, 0L, 0L); + + /* Nope...box should reset with just CMD_RESET now */ + gsc_writel(CMD_RESET, COMMAND_GLOBAL); + + /* Wait for RESET to lay us to rest. */ + while (1) ; + + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block parisc_block = { reset_parisc, NULL, 0 }; + + +/* start_parisc() will be called from head.S to setup our new memory_start + and actually start our kernel ! + Memory-Layout is: + - Kernel-Image (code+data+BSS) + - Stack (stack-size see below!, stack-setup-code is in head.S) + - memory_start at end of stack.. +*/ + +unsigned long mem_start, mem_max; +unsigned long start_pfn, max_pfn; +extern asmlinkage void __init start_kernel(void); + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + +void __init start_parisc(unsigned arg0, unsigned arg1, + unsigned arg2, unsigned arg3) +{ + register unsigned long ccr; + unsigned long memory_start; + + /* Clear BSS */ + + { + char *p = &_edata, *q = &_end; + + while (p < q) { + *p++ = 0; + } + } + + + pdc_console_init(); + +#ifdef __LP64__ + printk("The 64-bit Kernel has started...\n"); +#else + printk("The 32-bit Kernel has started...\n"); +#endif + + /* + ** Enable FP coprocessor + ** + ** REVISIT: ccr should be set by PDC_COPROC results to support PA1.0. + ** Hardcoding works for PA1.1 processors. + ** + ** REVISIT: this could be done in the "code 22" trap handler. + ** (frowands idea - that way we know which processes need FP + ** registers saved on the interrupt stack.) + ** + ** NEWS FLASH: wide kernels need FP coprocessor enabled to handle + ** formatted printing of %lx for example (double divides I think) + */ + ccr = 0xc0; + mtctl(ccr, 10); + printk("Enabled FP coprocessor\n"); + +#ifdef __LP64__ + printk( "If this is the LAST MESSAGE YOU SEE, you're probably using\n" + "32-bit millicode by mistake.\n"); +#endif + + memory_start = (unsigned long) &_end; + memory_start = (memory_start + PAGE_SIZE) & PAGE_MASK; + printk("Free memory starts at: 0x%lx\n", memory_start); + + /* Collect stuff passed in from the boot loader */ + printk(KERN_WARNING "%s(0x%x,0x%x,0x%x,0x%x)\n", + __FUNCTION__, arg0, arg1, arg2, arg3); + + /* arg0 is free-mem start, arg1 is ptr to command line */ + if (arg0 < 64) { + /* called from hpux boot loader */ + saved_command_line[0] = '\0'; + } else { + strcpy(saved_command_line, (char *)__va(arg1)); + printk("PALO command line: '%s'\nPALO initrd %x-%x\n", + saved_command_line, arg2, arg3); + +#ifdef CONFIG_BLK_DEV_INITRD + if (arg2 != 0) /* did palo pass us a ramdisk? */ + { + initrd_start = (unsigned long)__va(arg2); + initrd_end = (unsigned long)__va(arg3); + } +#endif + } + + mem_start = __pa(memory_start); +#define MAX_MEM (512*1024*1024) + mem_max = (PAGE0->imm_max_mem > MAX_MEM ? MAX_MEM : PAGE0->imm_max_mem); + + collect_boot_cpu_data(); + + /* initialize the LCD/LED after boot_cpu_data is available ! */ + led_init(); /* LCD/LED initialization */ + + do_inventory(); /* probe for hardware */ + register_driver(cpu_drivers_for); /* claim all the CPUs */ + + if (boot_cpu_data.cpu_count == 0) + register_fallback_cpu(); + + printk("CPU(s): %d x %s at %d.%06d MHz\n", + boot_cpu_data.cpu_count, + boot_cpu_data.cpu_name, + boot_cpu_data.cpu_hz / 1000000, + boot_cpu_data.cpu_hz % 1000000 ); + + switch (boot_cpu_data.cpu_type) { + case pcx: + case pcxs: + case pcxt: + hppa_dma_ops = &pcx_dma_ops; + break; + case pcxl2: + pa7300lc_init(); + case pcxl: /* falls through */ + hppa_dma_ops = &pcxl_dma_ops; + break; + default: + break; + } + +#if 1 + /* KLUGE! this really belongs in kernel/resource.c! */ + iomem_resource.end = ~0UL; +#endif + sysram_resource.end = mem_max - 1; + notifier_chain_register(&mach_notifier, &parisc_block); + start_kernel(); /* now back to arch-generic code... */ +} + +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size; + unsigned long start_pfn; + unsigned long mem_free; + + *cmdline_p = saved_command_line; + + /* initialize bootmem */ + + start_pfn = PFN_UP(mem_start); + max_pfn = PFN_DOWN(mem_max); + + bootmap_size = init_bootmem(start_pfn, max_pfn); + + mem_start += bootmap_size; + mem_free = mem_max - mem_start; + + /* free_bootmem handles rounding nicely */ + printk("free_bootmem(0x%lx, 0x%lx)\n", (unsigned long)mem_start, + (unsigned long)mem_free); + free_bootmem(mem_start, mem_free); + +#ifdef CONFIG_BLK_DEV_INITRD + printk("initrd: %08x-%08x\n", (int) initrd_start, (int) initrd_end); + + if (initrd_end != 0) { + initrd_below_start_ok = 1; + reserve_bootmem(__pa(initrd_start), initrd_end - initrd_start); + } +#endif + + cache_init(); + + paging_init(); + + if((unsigned long)&init_task_union&(INIT_TASK_SIZE - 1)) { + printk("init_task_union not aligned. Please recompile the kernel after changing the first line in arch/parisc/kernel/init_task.c from \n\"#define PAD 0\" to\n\"#define PAD 1\" or vice versa\n"); + for(;;); + } + + +#ifdef CONFIG_SERIAL_CONSOLE + /* nothing */ +#elif CONFIG_VT +#if defined(CONFIG_STI_CONSOLE) + conswitchp = &dummy_con; /* we use take_over_console() later ! */ +#elif defined(CONFIG_IODC_CONSOLE) + conswitchp = &prom_con; /* it's currently really "prom_con" */ +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif + +} + +#ifdef CONFIG_PROC_FS +/* + * Get CPU information for use by procfs. + */ + +int get_cpuinfo(char *buffer) +{ + char *p = buffer; + int n; + + for(n=0; n + * Copyright (C) 2000 Linuxcare, Inc. + * + * Based on the ia64, i386, and alpha versions. + * + * Like the IA-64, we are a recent enough port (we are *starting* + * with glibc2.2) that we do not need to support the old non-realtime + * Linux signals. Therefore we don't. HP/UX signals will go in + * arch/parisc/hpux/signal.c when we figure out how to do them. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +extern long sys_wait4 (int, int *, int, struct rusage *); +int do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall); + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* + * If you change siginfo_t structure, please be sure + * this code is fixed accordingly. It should never + * copy any pad contained in the structure to avoid + * security leaks, but must copy the generic 3 ints + * plus the relevant union member. + */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + /* FIXME: should we put the interruption code here? */ + case __SI_POLL >> 16: + err |= __put_user(from->si_addr, &to->si_addr); + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + err |= __put_user(from->si_pid, &to->si_pid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +#ifdef __LP64__ +#include "sys32.h" +#endif + +asmlinkage int +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, struct pt_regs *regs) +{ + sigset_t saveset, newset; +#ifdef __LP64__ + /* XXX FIXME -- assumes 32-bit user app! */ + sigset_t32 newset32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t32)) + return -EINVAL; + + if (copy_from_user(&newset32, (sigset_t32 *)unewset, sizeof(newset32))) + return -EFAULT; + + newset.sig[0] = newset32.sig[0] | ((unsigned long)newset32.sig[1] << 32); +#else + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; +#endif + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gr[28] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs, 1)) + return -EINTR; + } +} + +/* + * Do a signal return - restore sigcontext. + */ + +struct rt_sigframe { + unsigned int tramp[4]; + struct siginfo info; + struct ucontext uc; +}; + +/* Trampoline for calling rt_sigreturn() */ +#define INSN_LDI_R25_0 0x34190000 /* ldi 0,%r25 (in_syscall=0) */ +#define INSN_LDI_R25_1 0x34190002 /* ldi 1,%r25 (in_syscall=1) */ +#define INSN_LDI_R20 0x3414015a /* ldi __NR_rt_sigreturn,%r20 */ +#define INSN_BLE_SR2_R0 0xe4008200 /* be,l 0x100(%sr2,%r0),%sr0,%r31 */ +#define INSN_NOP 0x80000240 /* nop */ +/* For debugging */ +#define INSN_DIE_HORRIBLY 0x68000ccc /* stw %r0,0x666(%sr0,%r0) */ + +/* + * The 32-bit ABI wants at least 48 bytes for a function call frame: + * 16 bytes for arg0-arg3, and 32 bytes for magic (the only part of + * which Linux/parisc uses is sp-20 for the saved return pointer...) + * Then, the stack pointer must be rounded to a cache line (64 bytes). + */ +#define PARISC_RT_SIGFRAME_SIZE \ + (((sizeof(struct rt_sigframe) + 48) + 63) & -64) + +static long +restore_sigcontext(struct sigcontext *sc, struct pt_regs *regs) +{ + long err = 0; + + err |= __copy_from_user(regs->gr, sc->sc_gr, sizeof(regs->gr)); + err |= __copy_from_user(regs->fr, sc->sc_fr, sizeof(regs->fr)); + err |= __copy_from_user(regs->iaoq, sc->sc_iaoq, sizeof(regs->iaoq)); + err |= __copy_from_user(regs->iasq, sc->sc_iasq, sizeof(regs->iasq)); + err |= __get_user(regs->sar, &sc->sc_sar); + +#if DEBUG_SIG + printk("restore_sigcontext: r28 is %ld\n", regs->gr[28]); +#endif + return err; +} + +void +sys_rt_sigreturn(struct pt_regs *regs, int in_syscall) +{ + struct rt_sigframe *frame; + struct siginfo si; + sigset_t set; + unsigned long usp = regs->gr[30]; + + /* Unwind the user stack to get the rt_sigframe structure. */ + frame = (struct rt_sigframe *) + (usp - PARISC_RT_SIGFRAME_SIZE); +#if DEBUG_SIG + printk("in sys_rt_sigreturn, frame is %p\n", frame); +#endif + + /* Verify that it's a good sigcontext before using it */ + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto give_sigsegv; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto give_sigsegv; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + /* Good thing we saved the old gr[30], eh? */ + if (restore_sigcontext(&frame->uc.uc_mcontext, regs)) + goto give_sigsegv; + +#if DEBUG_SIG + printk("usp: %#08lx stack %p", + usp, &frame->uc.uc_stack); +#endif + + /* I don't know why everyone else assumes they can call this + with a pointer to a stack_t on the kernel stack. That + makes no sense. Anyway we'll do it like m68k, since we + also are using segmentation in the same way as them. */ + if (do_sigaltstack(&frame->uc.uc_stack, NULL, usp) == -EFAULT) + goto give_sigsegv; + + /* If we are on the syscall path IAOQ will not be restored, and + * if we are on the interrupt path we must not corrupt gr31. + */ + if (in_syscall) + regs->gr[31] = regs->iaoq[0]; +#if DEBUG_SIG + printk("returning to %#lx\n", regs->iaoq[0]); + printk("in sys_rt_sigreturn:\n"); + show_regs(regs); +#endif + return; + +give_sigsegv: +#if DEBUG_SIG + printk("fuckup in sys_rt_sigreturn, sending SIGSEGV\n"); +#endif + si.si_signo = SIGSEGV; + si.si_errno = 0; + si.si_code = SI_KERNEL; + si.si_pid = current->pid; + si.si_uid = current->uid; + si.si_addr = &frame->uc; + force_sig_info(SIGSEGV, &si, current); + return; +} + +/* + * Set up a signal frame. + */ + +static inline void * +get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) +{ + if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + + return (void *) sp; /* Stacks grow up. Fun. */ +} + +static long +setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, int in_syscall) + +{ + unsigned long flags = 0; + long err = 0; + + if (on_sig_stack((unsigned long) sc)) + flags |= PARISC_SC_FLAG_ONSTACK; + if (in_syscall) { + flags |= PARISC_SC_FLAG_IN_SYSCALL; + /* regs->iaoq is undefined in the syscall return path */ + err |= __put_user(regs->gr[31], &sc->sc_iaoq[0]); + err |= __put_user(regs->gr[31]+4, &sc->sc_iaoq[1]); +#if DEBUG_SIG + printk("setup_sigcontext: iaoq %#lx/%#lx\n", regs->gr[31], regs->gr[31]); +#endif + } else { + err |= __copy_to_user(sc->sc_iaoq, regs->iaoq, sizeof(regs->iaoq)); + err |= __copy_to_user(sc->sc_iasq, regs->iasq, sizeof(regs->iasq)); +#if DEBUG_SIG + printk("setup_sigcontext: iaoq %#lx/%#lx\n", regs->iaoq[0], regs->iaoq[1]); +#endif + } + + err |= __put_user(flags, &sc->sc_flags); + err |= __copy_to_user(sc->sc_gr, regs->gr, sizeof(regs->gr)); + err |= __copy_to_user(sc->sc_fr, regs->fr, sizeof(regs->fr)); + err |= __put_user(regs->sar, &sc->sc_sar); +#if DEBUG_SIG + printk("setup_sigcontext: r28 is %ld\n", regs->gr[28]); +#endif + + return err; +} + +static long +setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs, int in_syscall) +{ + struct rt_sigframe *frame; + unsigned long rp, usp, haddr; + struct siginfo si; + int err = 0; + + usp = regs->gr[30]; + /* access_ok is broken, so do a simplistic "are we stomping on + kernel space" assertion. */ + if (usp > PAGE_OFFSET) { + printk("setup_rt_frame: called on kernel space (usp=%#lx), NOW YOU MUST DIE!!!\n", + usp); + show_regs(regs); + while(1); + } + + frame = get_sigframe(ka, usp, sizeof(*frame)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + +#if DEBUG_SIG + printk("setup_rt_frame 1: frame %p info %p\n", frame, info); +#endif + + err |= __copy_to_user(&frame->info, info, sizeof(siginfo_t)); + err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= __put_user(sas_ss_flags(regs->gr[30]), + &frame->uc.uc_stack.ss_flags); + err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, in_syscall); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + err |= __put_user(in_syscall ? INSN_LDI_R25_1 : INSN_LDI_R25_0, + &frame->tramp[0]); + err |= __put_user(INSN_LDI_R20, &frame->tramp[1]); + err |= __put_user(INSN_BLE_SR2_R0, &frame->tramp[2]); + err |= __put_user(INSN_NOP, &frame->tramp[3]); + +#if DEBUG_SIG + /* Assert that we're flushing in the correct space... */ + { + int sid; + asm ("mfsp %%sr3,%0" : "=r" (sid)); + printk("flushing 64 bytes at space %#x offset %p\n", + sid, frame->tramp); + } +#endif + +#if CACHE_FLUSHING_IS_NOT_BROKEN + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[4]); +#else + /* It should *always* be cache line-aligned, but the compiler + sometimes screws up. */ + asm volatile("fdc 0(%%sr3,%0)\n\t" + "fdc %1(%%sr3,%0)\n\t" + "sync\n\t" + "fic 0(%%sr3,%0)\n\t" + "fic %1(%%sr3,%0)\n\t" + "sync\n\t" + : : "r" (frame->tramp), "r" (L1_CACHE_BYTES)); +#endif + rp = (unsigned long) frame->tramp; + + if (err) + goto give_sigsegv; + +#ifdef __LP64__ +/* Much more has to happen with signals than this -- but it'll at least */ +/* provide a pointer to some places which definitely need a look. */ +#define HACK unsigned int +#else +#define HACK unsigned long +#endif + haddr = (HACK) ka->sa.sa_handler; + /* ARGH! Fucking brain damage. You don't want to know. */ + if (haddr & 2) { + HACK *plabel; + HACK ltp; + + plabel = (HACK *) (haddr & ~3); + err |= __get_user(haddr, plabel); + err |= __get_user(ltp, plabel + 1); + if (err) + goto give_sigsegv; + regs->gr[19] = ltp; + } + + /* The syscall return path will create IAOQ values from r31. + */ + if (in_syscall) + regs->gr[31] = (HACK) haddr; + else { + regs->iaoq[0] = (HACK) haddr | 3; + regs->iaoq[1] = regs->iaoq[0] + 4; + } + + regs->gr[2] = rp; /* userland return pointer */ + regs->gr[26] = sig; /* signal number */ + regs->gr[25] = (HACK) &frame->info; /* siginfo pointer */ + regs->gr[24] = (HACK) &frame->uc; /* ucontext pointer */ +#if DEBUG_SIG + printk("making sigreturn frame: %#lx + %#lx = %#lx\n", + regs->gr[30], PARISC_RT_SIGFRAME_SIZE, + regs->gr[30] + PARISC_RT_SIGFRAME_SIZE); +#endif + /* Raise the user stack pointer to make a proper call frame. */ + regs->gr[30] = ((HACK) frame + PARISC_RT_SIGFRAME_SIZE); + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): frame=0x%p sp=%#lx iaoq=%#lx/%#lx rp=%#lx\n", + current->comm, current->pid, frame, regs->gr[30], + regs->iaoq[0], regs->iaoq[1], rp); +#endif + + return 1; + +give_sigsegv: +#if DEBUG_SIG + printk("fuckup in setup_rt_frame, sending SIGSEGV\n"); +#endif + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + si.si_signo = SIGSEGV; + si.si_errno = 0; + si.si_code = SI_KERNEL; + si.si_pid = current->pid; + si.si_uid = current->uid; + si.si_addr = frame; + force_sig_info(SIGSEGV, &si, current); + return 0; +} + +/* + * OK, we're invoking a handler. + */ + +static long +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs *regs, int in_syscall) +{ +#if DEBUG_SIG + printk("handle_signal(sig=%ld, ka=%p, info=%p, oldset=%p, regs=%p)\n", + sig, ka, info, oldset, regs); +#endif + /* Set up the stack frame */ + if (!setup_rt_frame(sig, ka, info, oldset, regs, in_syscall)) + return 0; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } + return 1; +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * We need to be able to restore the syscall arguments (r21-r26) to + * restart syscalls. Thus, the syscall path should save them in the + * pt_regs structure (it's okay to do so since they are caller-save + * registers). As noted below, the syscall number gets restored for + * us due to the magic of delayed branching. + */ +asmlinkage int +do_signal(sigset_t *oldset, struct pt_regs *regs, int in_syscall) +{ + siginfo_t info; + struct k_sigaction *ka; + +#if DEBUG_SIG + printk("do_signal(oldset=0x%p, regs=0x%p, sr7 %#lx, pending %d, in_syscall=%d\n", + oldset, regs, regs->sr[7], current->sigpending, in_syscall); +#endif + /* Everyone else checks to see if they are in kernel mode at + this point and exits if that's the case. I'm not sure why + we would be called in that case, but for some reason we + are. */ + + if (!oldset) + oldset = ¤t->blocked; + +#if DEBUG_SIG + printk("do_signal: oldset %08lx:%08lx\n", oldset->sig[0], oldset->sig[1]); +#endif + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); +#if DEBUG_SIG + printk("do_signal: signr=%ld, pid=%d\n", signr, current->pid); +#endif + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + set_current_state(TASK_STOPPED); + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; +#if DEBUG_SIG + printk("sa_handler is %lx\n", ka->sa.sa_handler); +#endif + if ((unsigned long) ka->sa.sa_handler == (unsigned long) SIG_IGN) { + if (signr != SIGCHLD) + continue; + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if ((unsigned long) ka->sa.sa_handler == (unsigned long) SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + set_current_state(TASK_STOPPED); + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (signr == SIGQUIT) /* Userspace debugging */ + show_regs(regs); + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + lock_kernel(); + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Restart a system call if necessary. */ + if (in_syscall) { + /* Check the return code */ + switch (regs->gr[28]) { + case -ERESTARTNOHAND: +#if DEBUG_SIG + printk("ERESTARTNOHAND: returning -EINTR\n"); +#endif + regs->gr[28] = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { +#if DEBUG_SIG + printk("ERESTARTSYS: putting -EINTR\n"); +#endif + regs->gr[28] = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + /* A syscall is just a branch, so all + we have to do is fiddle the return + pointer. */ + regs->gr[31] -= 8; /* delayed branching */ + /* Preserve original r28. */ + regs->gr[28] = regs->orig_r28; + break; + } + } + /* Whee! Actually deliver the signal. If the + delivery failed, we need to continue to iterate in + this loop so we can deliver the SIGSEGV... */ + if (handle_signal(signr, ka, &info, oldset, regs, in_syscall)) { +#if DEBUG_SIG + printk("Exiting do_signal (success), regs->gr[28] = %ld\n", regs->gr[28]); +#endif + return 1; + } + } + + /* Did we come from a system call? */ + if (in_syscall) { + /* Restart the system call - no handlers present */ + if (regs->gr[28] == -ERESTARTNOHAND || + regs->gr[28] == -ERESTARTSYS || + regs->gr[28] == -ERESTARTNOINTR) { + /* Hooray for delayed branching. We don't + have to restore %r20 (the system call + number) because it gets loaded in the delay + slot of the branch external instruction. */ + regs->gr[31] -= 8; + /* Preserve original r28. */ + regs->gr[28] = regs->orig_r28; + } + } +#if DEBUG_SIG + printk("Exiting do_signal (not delivered), regs->gr[28] = %ld\n", regs->gr[28]); +#endif + return 0; +} diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c new file mode 100644 index 000000000000..85aca70afc1d --- /dev/null +++ b/arch/parisc/kernel/sys_parisc.c @@ -0,0 +1,88 @@ +/* + * linux/arch/parisc/kernel/sys_parisc.c + * + * this implements the missing syscalls. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* for some reason, "old_readdir" is the only syscall which does not begin + * with "sys_", which breaks the ENTRY_* macros in syscall.S so I "fixed" + * it here. + */ + +int sys_old_readdir(unsigned int fd, void *dirent, unsigned int count) +{ + return old_readdir(fd, dirent, count); +} + +int sys_pipe(int *fildes) +{ + int fd[2]; + int error; + + lock_kernel(); + error = do_pipe(fd); + unlock_kernel(); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +int sys_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, unsigned long fd, + unsigned long offset) +{ + struct file * file = NULL; + int error; + + down(¤t->mm->mmap_sem); + lock_kernel(); + if (!(flags & MAP_ANONYMOUS)) { + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + } + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + error = do_mmap(file, addr, len, prot, flags, offset); + if (file != NULL) + fput(file); +out: + unlock_kernel(); + up(¤t->mm->mmap_sem); + return error; +} + +int sys_ioperm(unsigned long from, unsigned long num, int on) +{ + return -ENOSYS; +} + +long sys_shmat_wrapper(int shmid, void *shmaddr, int shmflag) +{ + extern int sys_shmat(int shmid, char *shmaddr, int shmflg, + unsigned long * raddr); + unsigned long raddr; + int r; + + r = sys_shmat(shmid, shmaddr, shmflag, &raddr); + if (r < 0) + return r; + return raddr; +} diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S new file mode 100644 index 000000000000..bfa75747149c --- /dev/null +++ b/arch/parisc/kernel/syscall.S @@ -0,0 +1,563 @@ +/* + * Linux/PARISC Project (http://www.thepuffingroup.com/parisc) + * + * System call entry code Copyright (c) Matthew Wilcox 1999 + * Licensed under the GNU GPL. + * thanks to Philipp Rumpf, Mike Shaver and various others + * sorry about the wall, puffin.. + */ + +#include +#include +#include +#include + +#define __ASSEMBLY__ +#include +#include +#include + +#ifdef __LP64__ + .level 2.0w +#else + .level 1.1 +#endif + .text + + .import syscall_exit,code + .import syscall_exit_rfi,code + .export linux_gateway_page + + /* Linux gateway page is aliased to virtual page 0 in the kernel + * address space. Since it is a gateway page it cannot be + * dereferenced, so null pointers will still fault. We start + * the actual entry point at 0x100. We put break instructions + * at the beginning of the page to trap null indirect function + * pointers. + */ + + .align 4096 +linux_gateway_page: + + break 0,0 + + .align 256 +linux_gateway_entry: + mfsp %sr7,%r1 /* we must set sr3 to the space */ + mtsp %r1,%sr3 /* of the user before the gate */ + gate .+8, %r0 /* become privileged */ + mtsp %r0,%sr4 /* get kernel space into sr4 */ + mtsp %r0,%sr5 /* get kernel space into sr5 */ + mtsp %r0,%sr6 /* get kernel space into sr6 */ + mtsp %r0,%sr7 /* get kernel space into sr7 */ +#ifdef __LP64__ + /* for now we can *always* set the W bit on entry to the syscall + * since we don't support wide userland processes. We could + * also save the current SM other than in r0 and restore it on + * exit from the syscall, and also use that value to know + * whether to do narrow or wide syscalls. -PB + */ + ssm PSW_SM_W, %r0 +#endif + mtctl %r28,%cr27 + rsm PSW_I, %r28 /* no ints for a bit */ + mfctl %cr30,%r1 /* get the kernel task ptr */ + mtctl %r0,%cr30 /* zero it (flag) */ + + /* Save some registers for sigcontext and potential task + switch (see entry.S for the details of which ones are + saved/restored) */ + STREG %r2, TASK_PT_GR2(%r1) /* preserve rp */ + STREG %r19, TASK_PT_GR19(%r1) + STREG %r20, TASK_PT_GR20(%r1) + STREG %r21, TASK_PT_GR21(%r1) + STREG %r22, TASK_PT_GR22(%r1) + STREG %r23, TASK_PT_GR23(%r1) /* 4th argument */ + STREG %r24, TASK_PT_GR24(%r1) /* 3rd argument */ + STREG %r25, TASK_PT_GR25(%r1) /* 2nd argument */ + STREG %r26, TASK_PT_GR26(%r1) /* 1st argument */ + STREG %r27, TASK_PT_GR27(%r1) /* user dp */ + mfctl %cr27,%r19 + STREG %r19, TASK_PT_GR28(%r1) /* return value 0 */ + STREG %r19, TASK_PT_ORIG_R28(%r1) /* return value 0 (saved for signals) */ + STREG %r29, TASK_PT_GR29(%r1) /* return value 1 */ + STREG %r30, TASK_PT_GR30(%r1) /* preserve userspace sp */ + STREG %r31, TASK_PT_GR31(%r1) /* preserve syscall return ptr */ + + ldo TASK_PT_FR0(%r1), %r27 /* save fpregs from the kernel */ + save_fp %r27 /* or potential task switch */ + + mfctl %cr11, %r27 /* i.e. SAR */ + STREG %r27, TASK_PT_SAR(%r1) + + loadgp + + ldo TASK_SZ_ALGN+64(%r1),%r30 /* set up kernel stack */ + +#ifndef __LP64__ + /* no need to save these on stack because in wide mode the first 8 + * args are passed in registers */ + stw %r22, -52(%r30) /* 5th argument */ + stw %r21, -56(%r30) /* 6th argument */ +#endif + + /* for some unknown reason, task_struct.ptrace is an unsigned long so use LDREG */ + LDREG TASK_PTRACE(%r1), %r19 /* Are we being ptraced? */ + mtsm %r28 /* irqs back */ + + bb,<,n %r19, 31, .Ltracesys /* must match PT_PTRACE bit */ + + /* Note! We cannot use the syscall table that is mapped + nearby since the gateway page is mapped execute-only. */ + + ldil L%sys_call_table, %r1 + ldo R%sys_call_table(%r1), %r19 + LDIL_FIXUP(%r19) + + comiclr,>>= __NR_Linux_syscalls, %r20, %r0 + b,n .Lsyscall_nosys + +#ifdef __LP64__ + ldd,s %r20(%r19), %r19 +#else + ldwx,s %r20(%r19), %r19 +#endif + /* If this is a sys_rt_sigreturn call, and the signal was received + * when not in_syscall, then we want to return via syscall_exit_rfi, + * not syscall_exit. Signal no. in r20, in_syscall in r25 (see + * trampoline code in signal.c). + */ + ldi __NR_rt_sigreturn,%r2 + comb,= %r2,%r20,.Lrt_sigreturn +.Lin_syscall: + ldil L%syscall_exit,%r2 + LDIL_FIXUP(%r2) + be 0(%sr7,%r19) + ldo R%syscall_exit(%r2),%r2 +.Lrt_sigreturn: + comib,<> 0,%r25,.Lin_syscall + ldil L%syscall_exit_rfi,%r2 + LDIL_FIXUP(%r2) + be 0(%sr7,%r19) + ldo R%syscall_exit_rfi(%r2),%r2 + + /* Note! Because we are not running where we were linked, any + calls to functions external to this file must be indirect. To + be safe, we apply the opposite rule to functions within this + file, with local labels given to them to ensure correctness. */ + +.Lsyscall_nosys: +syscall_nosys: + ldil L%syscall_exit,%r1 + LDIL_FIXUP(%r1) + be R%syscall_exit(%sr7,%r1) + ldo -ENOSYS(%r0),%r28 /* set errno */ + + +/* Warning! This trace code is a virtual duplicate of the code above so be + * sure to maintain both! */ +.Ltracesys: +tracesys: + /* Need to save more registers so the debugger can see where we + * are. + */ + ldo -TASK_SZ_ALGN-64(%r30),%r1 /* get task ptr */ + ssm 0,%r2 /* Lower 8 bits only!! */ + STREG %r2,TASK_PT_PSW(%r1) + STREG %r1,TASK_PT_CR30(%r1) + mfsp %sr0,%r2 + STREG %r2,TASK_PT_SR0(%r1) + mfsp %sr1,%r2 + STREG %r2,TASK_PT_SR1(%r1) + mfsp %sr2,%r2 + STREG %r2,TASK_PT_SR2(%r1) + mfsp %sr3,%r2 + STREG %r2,TASK_PT_SR3(%r1) + STREG %r2,TASK_PT_SR4(%r1) + STREG %r2,TASK_PT_SR5(%r1) + STREG %r2,TASK_PT_SR6(%r1) + STREG %r2,TASK_PT_SR7(%r1) + STREG %r2,TASK_PT_IASQ0(%r1) + STREG %r2,TASK_PT_IASQ1(%r1) + LDREG TASK_PT_GR31(%r1),%r2 + STREG %r2,TASK_PT_IAOQ0(%r1) + ldo 4(%r2),%r2 + STREG %r2,TASK_PT_IAOQ1(%r1) + ldo TASK_REGS(%r1),%r2 + /* reg_save %r2 */ + STREG %r3,PT_GR3(%r2) + STREG %r4,PT_GR4(%r2) + STREG %r5,PT_GR5(%r2) + STREG %r6,PT_GR6(%r2) + STREG %r7,PT_GR7(%r2) + STREG %r8,PT_GR8(%r2) + STREG %r9,PT_GR9(%r2) + STREG %r10,PT_GR10(%r2) + STREG %r11,PT_GR11(%r2) + STREG %r12,PT_GR12(%r2) + STREG %r13,PT_GR13(%r2) + STREG %r14,PT_GR14(%r2) + STREG %r15,PT_GR15(%r2) + STREG %r16,PT_GR16(%r2) + STREG %r17,PT_GR17(%r2) + STREG %r18,PT_GR18(%r2) + /* Finished saving things for the debugger */ + + ldil L%syscall_trace,%r1 + LDIL_FIXUP(%r1) + ldil L%tracesys_next,%r2 + LDIL_FIXUP(%r2) + be R%syscall_trace(%sr7,%r1) + ldo R%tracesys_next(%r2),%r2 + +tracesys_next: + ldil L%sys_call_table,%r1 + LDIL_FIXUP(%r1) + ldo R%sys_call_table(%r1), %r19 + + ldo -TASK_SZ_ALGN-64(%r30),%r1 /* get task ptr */ + LDREG TASK_PT_GR20(%r1), %r20 + LDREG TASK_PT_GR26(%r1), %r26 /* Restore the users args */ + LDREG TASK_PT_GR25(%r1), %r25 + LDREG TASK_PT_GR24(%r1), %r24 + LDREG TASK_PT_GR23(%r1), %r23 +#ifdef __LP64__ + LDREG TASK_PT_GR22(%r1), %r22 + LDREG TASK_PT_GR21(%r1), %r21 +#endif + + comiclr,>>= __NR_Linux_syscalls, %r20, %r0 + b,n .Lsyscall_nosys + +#ifdef __LP64__ + ldd,s %r20(%r19), %r19 +#else + ldwx,s %r20(%r19), %r19 +#endif + /* If this is a sys_rt_sigreturn call, and the signal was received + * when not in_syscall, then we want to return via syscall_exit_rfi, + * not syscall_exit. Signal no. in r20, in_syscall in r25 (see + * trampoline code in signal.c). + */ + ldi __NR_rt_sigreturn,%r2 + comb,= %r2,%r20,.Ltrace_rt_sigreturn +.Ltrace_in_syscall: + ldil L%tracesys_exit,%r2 + LDIL_FIXUP(%r2) + be 0(%sr7,%r19) + ldo R%tracesys_exit(%r2),%r2 + + /* Do *not* call this function on the gateway page, because it + makes a direct call to syscall_trace. */ + +tracesys_exit: + ldo -TASK_SZ_ALGN-64(%r30),%r1 /* get task ptr */ + bl syscall_trace, %r2 + STREG %r28,TASK_PT_GR28(%r1) /* save return value now */ + ldo -TASK_SZ_ALGN-64(%r30),%r1 /* get task ptr */ + LDREG TASK_PT_GR28(%r1), %r28 /* Restore return val. */ + + ldil L%syscall_exit,%r1 + LDIL_FIXUP(%r1) + be,n R%syscall_exit(%sr7,%r1) + +.Ltrace_rt_sigreturn: + comib,<> 0,%r25,.Ltrace_in_syscall + ldil L%tracesys_sigexit,%r2 + LDIL_FIXUP(%r2) + be 0(%sr7,%r19) + ldo R%tracesys_sigexit(%r2),%r2 + +tracesys_sigexit: + ldo -TASK_SZ_ALGN-64(%r30),%r1 /* get task ptr */ + bl syscall_trace, %r2 + nop + + ldil L%syscall_exit_rfi,%r1 + LDIL_FIXUP(%r1) + be,n R%syscall_exit_rfi(%sr7,%r1) + +#ifdef __LP64__ +/* Use ENTRY_SAME for 32-bit syscalls which are the same on wide and + * narrow palinux. Use ENTRY_DIFF for those where a 32-bit specific + * implementation is required on wide palinux. + */ +#define ENTRY_SAME(_name_) .dword sys_##_name_ +#define ENTRY_DIFF(_name_) .dword sys32_##_name_ +#define ENTRY_UHOH(_name_) .dword sys32_unimplemented +#else +#define ENTRY_SAME(_name_) .word sys_##_name_ +#define ENTRY_DIFF(_name_) .word sys_##_name_ +#define ENTRY_UHOH(_name_) .word sys_##_name_ +#endif + + .align 8 + .export sys_call_table +.Lsys_call_table: +sys_call_table: + ENTRY_SAME(ni_syscall) /* 0 - old "setup()" system call*/ + ENTRY_SAME(exit) + ENTRY_SAME(fork_wrapper) + ENTRY_SAME(read) + ENTRY_SAME(write) + ENTRY_SAME(open) /* 5 */ + ENTRY_SAME(close) + ENTRY_SAME(waitpid) + ENTRY_SAME(creat) + ENTRY_SAME(link) + ENTRY_SAME(unlink) /* 10 */ + ENTRY_DIFF(execve_wrapper) + ENTRY_SAME(chdir) + /* See comments in kernel/time.c!!! Maybe we don't need this? */ + ENTRY_DIFF(time) + ENTRY_SAME(mknod) + ENTRY_SAME(chmod) /* 15 */ + ENTRY_SAME(lchown) + ENTRY_SAME(socket) + /* struct stat is MAYBE identical wide and narrow ?? */ + ENTRY_DIFF(newstat) + ENTRY_SAME(lseek) + ENTRY_SAME(getpid) /* 20 */ + /* the 'void * data' parameter may need re-packing in wide */ + ENTRY_DIFF(mount) + /* concerned about struct sockaddr in wide/narrow */ + /* ---> I think sockaddr is OK unless the compiler packs the struct */ + /* differently to align the char array */ + ENTRY_SAME(bind) + ENTRY_SAME(setuid) + ENTRY_SAME(getuid) + ENTRY_SAME(stime) /* 25 */ + ENTRY_SAME(ptrace) + ENTRY_SAME(alarm) + /* see stat comment */ + ENTRY_DIFF(newfstat) + ENTRY_SAME(pause) + /* struct utimbuf uses time_t which might vary */ + ENTRY_DIFF(utime) /* 30 */ + /* struct sockaddr... */ + ENTRY_SAME(connect) + ENTRY_SAME(listen) + ENTRY_SAME(access) + ENTRY_SAME(nice) + /* struct sockaddr... */ + ENTRY_SAME(accept) /* 35 */ + ENTRY_SAME(sync) + ENTRY_SAME(kill) + ENTRY_SAME(rename) + ENTRY_SAME(mkdir) + ENTRY_SAME(rmdir) /* 40 */ + ENTRY_SAME(dup) + ENTRY_SAME(pipe) + ENTRY_DIFF(times) + /* struct sockaddr... */ + ENTRY_SAME(getsockname) + /* it seems possible brk() could return a >4G pointer... */ + ENTRY_SAME(brk) /* 45 */ + ENTRY_SAME(setgid) + ENTRY_SAME(getgid) + ENTRY_SAME(signal) + ENTRY_SAME(geteuid) + ENTRY_SAME(getegid) /* 50 */ + ENTRY_SAME(acct) + ENTRY_SAME(umount) + /* struct sockaddr... */ + ENTRY_SAME(getpeername) + /* This one's a huge ugly mess */ + ENTRY_DIFF(ioctl) + /* struct flock? */ + ENTRY_DIFF(fcntl) /* 55 */ + ENTRY_SAME(socketpair) + ENTRY_SAME(setpgid) + ENTRY_SAME(send) + ENTRY_SAME(newuname) + ENTRY_SAME(umask) /* 60 */ + ENTRY_SAME(chroot) + ENTRY_SAME(ustat) + ENTRY_SAME(dup2) + ENTRY_SAME(getppid) + ENTRY_SAME(getpgrp) /* 65 */ + ENTRY_SAME(setsid) + ENTRY_SAME(pivot_root) + /* I don't like this */ + ENTRY_UHOH(sgetmask) + ENTRY_UHOH(ssetmask) + ENTRY_SAME(setreuid) /* 70 */ + ENTRY_SAME(setregid) + ENTRY_SAME(mincore) + ENTRY_DIFF(sigpending) + ENTRY_SAME(sethostname) + /* Following 3 have linux-common-code structs containing longs -( */ + ENTRY_DIFF(setrlimit) /* 75 */ + ENTRY_DIFF(getrlimit) + ENTRY_DIFF(getrusage) + /* struct timeval and timezone are maybe?? consistent wide and narrow */ + ENTRY_SAME(gettimeofday) + ENTRY_SAME(settimeofday) + ENTRY_SAME(getgroups) /* 80 */ + ENTRY_SAME(setgroups) + /* struct socketaddr... */ + ENTRY_SAME(sendto) + ENTRY_SAME(symlink) + /* see stat comment */ + ENTRY_DIFF(newlstat) + ENTRY_SAME(readlink) /* 85 */ + /* suspect we'll need some work for narrow shlibs on wide kernel */ + ENTRY_UHOH(uselib) + ENTRY_SAME(swapon) + ENTRY_SAME(reboot) + /* argh! struct dirent contains a long */ + ENTRY_UHOH(old_readdir) + /* I'm not certain about off_t... */ + ENTRY_SAME(mmap) /* 90 */ + ENTRY_SAME(munmap) + ENTRY_SAME(truncate) + ENTRY_SAME(ftruncate) + ENTRY_SAME(fchmod) + ENTRY_SAME(fchown) /* 95 */ + ENTRY_SAME(getpriority) + ENTRY_SAME(setpriority) + ENTRY_SAME(recv) + ENTRY_DIFF(statfs) + ENTRY_DIFF(fstatfs) /* 100 */ + ENTRY_SAME(ni_syscall) + /* don't think hppa glibc even provides an entry pt for this + * so disable for now */ + ENTRY_UHOH(socketcall) + ENTRY_SAME(syslog) + /* even though manpage says struct timeval contains longs, ours has + * time_t and suseconds_t -- both of which are safe wide/narrow */ + ENTRY_SAME(setitimer) + ENTRY_SAME(getitimer) /* 105 */ + ENTRY_SAME(capget) + ENTRY_SAME(capset) + ENTRY_SAME(pread) + ENTRY_SAME(pwrite) + ENTRY_SAME(getcwd) /* 110 */ + ENTRY_SAME(vhangup) + ENTRY_SAME(ni_syscall) + ENTRY_SAME(vfork_wrapper) + /* struct rusage contains longs... */ + ENTRY_DIFF(wait4) + ENTRY_SAME(swapoff) /* 115 */ + /* struct sysinfo contains longs */ + ENTRY_SAME(sysinfo) + ENTRY_SAME(shutdown) + ENTRY_SAME(fsync) + ENTRY_SAME(madvise) + ENTRY_SAME(clone_wrapper) /* 120 */ + ENTRY_SAME(setdomainname) + ENTRY_SAME(sendfile) + /* struct sockaddr... */ + ENTRY_SAME(recvfrom) + /* struct timex contains longs */ + ENTRY_UHOH(adjtimex) + ENTRY_SAME(mprotect) /* 125 */ + /* old_sigset_t forced to 32 bits. Beware glibc sigset_t */ + ENTRY_DIFF(sigprocmask) + ENTRY_SAME(create_module) + /* struct module contains longs, but insmod builds a 64 bit struct + * if running under a 64 bit kernel */ + ENTRY_SAME(init_module) + ENTRY_SAME(delete_module) + /* struct kernel_sym contains a long. Linus never heard of size_t? */ + ENTRY_DIFF(get_kernel_syms) /* 130 */ + ENTRY_SAME(quotactl) + ENTRY_SAME(getpgid) + ENTRY_SAME(fchdir) + /* bdflush(func, addr) where func has least-significant-bit set means + * addr is a pointer to long :-( */ + ENTRY_UHOH(bdflush) + ENTRY_SAME(sysfs) /* 135 */ + ENTRY_SAME(personality) + ENTRY_SAME(ni_syscall) /* for afs_syscall */ + ENTRY_SAME(setfsuid) + ENTRY_SAME(setfsgid) + /* I think this might work */ + ENTRY_SAME(llseek) /* 140 */ + /* struct linux_dirent has longs, like 'unsigned long d_ino' which + * almost definitely should be 'ino_t d_ino' but it's too late now */ + ENTRY_DIFF(getdents) + /* it is POSSIBLE that select will be OK because even though fd_set + * contains longs, the macros and sizes are clever. */ + ENTRY_SAME(select) + ENTRY_SAME(flock) + ENTRY_SAME(msync) + /* struct iovec contains pointers */ + ENTRY_UHOH(readv) /* 145 */ + ENTRY_UHOH(writev) + ENTRY_SAME(getsid) + ENTRY_SAME(fdatasync) + /* struct __sysctl_args is a mess */ + ENTRY_DIFF(sysctl) + ENTRY_SAME(mlock) /* 150 */ + ENTRY_SAME(munlock) + ENTRY_SAME(mlockall) + ENTRY_SAME(munlockall) + /* struct sched_param is ok for now */ + ENTRY_SAME(sched_setparam) + ENTRY_SAME(sched_getparam) /* 155 */ + ENTRY_SAME(sched_setscheduler) + ENTRY_SAME(sched_getscheduler) + ENTRY_SAME(sched_yield) + ENTRY_SAME(sched_get_priority_max) + ENTRY_SAME(sched_get_priority_min) /* 160 */ + /* These 2 would've worked if someone had defined struct timespec + * carefully, like timeval for example (which is about the same). + * Unfortunately it contains a long :-( */ + ENTRY_DIFF(sched_rr_get_interval) + ENTRY_DIFF(nanosleep) + ENTRY_SAME(mremap) + ENTRY_SAME(setresuid) + ENTRY_SAME(getresuid) /* 165 */ + /* might work, but in general signals need a thorough review */ + ENTRY_UHOH(sigaltstack_wrapper) + /* struct passed back to user can contain long symbol values */ + ENTRY_DIFF(query_module) + ENTRY_SAME(poll) + /* structs contain pointers and an in_addr... */ + ENTRY_UHOH(nfsservctl) + ENTRY_SAME(setresgid) /* 170 */ + ENTRY_SAME(getresgid) + ENTRY_SAME(prctl) + /* signals need a careful review */ + ENTRY_SAME(rt_sigreturn_wrapper) + ENTRY_DIFF(rt_sigaction) + ENTRY_DIFF(rt_sigprocmask) /* 175 */ + ENTRY_DIFF(rt_sigpending) + ENTRY_UHOH(rt_sigtimedwait) + ENTRY_UHOH(rt_sigqueueinfo) + ENTRY_SAME(rt_sigsuspend_wrapper) /* not really SAME -- see the code */ + ENTRY_SAME(chown) /* 180 */ + /* *sockopt() might work... */ + ENTRY_SAME(setsockopt) + ENTRY_SAME(getsockopt) + /* struct msghdr contains pointers... */ + ENTRY_UHOH(sendmsg) + ENTRY_UHOH(recvmsg) + ENTRY_SAME(semop) /* 185 */ + ENTRY_SAME(semget) + /* needs a more careful review */ + ENTRY_UHOH(semctl) + /* struct msgbuf contains a long */ + ENTRY_UHOH(msgsnd) + ENTRY_UHOH(msgrcv) + ENTRY_SAME(msgget) /* 190 */ + /* struct msqid_ds contains pointers */ + ENTRY_UHOH(msgctl) + ENTRY_SAME(shmat_wrapper) + ENTRY_SAME(shmdt) + ENTRY_SAME(shmget) + /***************/ + /* struct shmid_ds contains pointers */ + ENTRY_UHOH(shmctl) /* 195 */ + ENTRY_SAME(ni_syscall) /* streams1 */ + ENTRY_SAME(ni_syscall) /* streams2 */ + +.end + + /* Make sure nothing else is placed on this page */ + + .align 4096 + .export end_linux_gateway_page +end_linux_gateway_page: + diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c new file mode 100644 index 000000000000..c9d3e5034320 --- /dev/null +++ b/arch/parisc/kernel/time.c @@ -0,0 +1,99 @@ +/* + * linux/arch/arm/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Modifications for ARM (C) 1994, 1995, 1996,1997 Russell King + * Copyright (C) 1999 SuSE GmbH, (Philipp Rumpf, prumpf@tux.org) + * + * 1994-07-02 Alan Modra + * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime + * 1998-12-20 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +extern rwlock_t xtime_lock; + +static int timer_value; +static int timer_delta; +static struct pdc_tod tod_data __attribute__((aligned(8))); + +void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int old; + int lost = 0; + int cr16; + + old = timer_value; + + cr16 = mfctl(16); + while((timer_value - cr16) < (timer_delta / 2)) { + timer_value += timer_delta; + lost++; + } + + mtctl(timer_value ,16); + + do_timer(regs); + + led_interrupt_func(); +} + +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + + read_lock_irqsave(&xtime_lock, flags); + tv->tv_sec = xtime.tv_sec; + tv->tv_usec = xtime.tv_usec; + read_unlock_irqrestore(&xtime_lock, flags); + +} + +void do_settimeofday(struct timeval *tv) +{ + write_lock_irq(&xtime_lock); + xtime.tv_sec = tv->tv_sec; + xtime.tv_usec = tv->tv_usec; + write_unlock_irq(&xtime_lock); +} + +void __init time_init(void) +{ + timer_delta = (100 * PAGE0->mem_10msec) / HZ; + + /* make the first timer interrupt go off in one second */ + timer_value = mfctl(16) + (HZ * timer_delta); + mtctl(timer_value, 16); + + + if(pdc_tod_read(&tod_data) == 0) { + xtime.tv_sec = tod_data.tod_sec; + xtime.tv_usec = tod_data.tod_usec; + } else { + printk(KERN_ERR "Error reading tod clock\n"); + xtime.tv_sec = 0; + xtime.tv_usec = 0; + } + +} + diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c new file mode 100644 index 000000000000..b61591b55913 --- /dev/null +++ b/arch/parisc/kernel/traps.c @@ -0,0 +1,896 @@ +/* + * linux/arch/parisc/traps.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1999, 2000 Philipp Rumpf + */ + +/* + * 'Traps.c' handles hardware traps and faults after we have saved some + * state in 'asm.s'. + */ + +#include /* for CONFIG_KWDB */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_KWDB +#include /* for BI2_KGDB_GDB */ +#include /* for __() */ +#include /* for struct save_state */ +#include /* for pt_regs_to_ssp and ssp_to_pt_regs */ +#include /* for I_BRK_INST */ +#endif /* CONFIG_KWDB */ + + +static inline void console_verbose(void) +{ + extern int console_loglevel; + console_loglevel = 15; +} + + +void page_exception(void); + +/* + * These constants are for searching for possible module text + * segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is + * a guess of how much space is likely to be vmalloced. + */ +#define VMALLOC_OFFSET (8*1024*1024) +#define MODULE_RANGE (8*1024*1024) + +int kstack_depth_to_print = 24; + +static void printbinary(unsigned long x, int nbits) +{ + unsigned long mask = 1UL << (nbits - 1); + while (mask != 0) { + printk(mask & x ? "1" : "0"); + mask >>= 1; + } +} + +void show_regs(struct pt_regs *regs) +{ + int i; +#ifdef __LP64__ +#define RFMT " %016lx" +#else +#define RFMT " %08lx" +#endif + + printk("\n"); /* don't want to have that pretty register dump messed up */ + + printk(" YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI\nPSW: "); + printbinary(regs->gr[0], 32); + printk("\n"); + + for (i = 0; i < 32; i += 4) { + int j; + printk("r%d-%d\t", i, i + 3); + for (j = 0; j < 4; j++) { + printk(RFMT, i + j == 0 ? 0 : regs->gr[i + j]); + } + printk("\n"); + } + + for (i = 0; i < 8; i += 4) { + int j; + printk("sr%d-%d\t", i, i + 4); + for (j = 0; j < 4; j++) { + printk(RFMT, regs->sr[i + j]); + } + printk("\n"); + } + +#if REDICULOUSLY_VERBOSE + for (i = 0; i < 32; i++) { + printk("FR%2d : %016lx ", i, regs->fr[i]); + if ((i & 1) == 1) + printk("\n"); + } +#endif + + printk("\nIASQ:" RFMT RFMT " IAOQ:" RFMT RFMT "\n", + regs->iasq[0], regs->iasq[1], regs->iaoq[0], regs->iaoq[1]); + printk(" IIR: %08lx ISR:" RFMT " IOR:" RFMT "\nORIG_R28:" RFMT + "\n", regs->iir, regs->isr, regs->ior, regs->orig_r28); +} + +void +die_if_kernel (char *str, struct pt_regs *regs, long err) +{ + if (user_mode(regs)) { +#if 1 + if (err == 0) + return; /* STFU */ + + /* XXX for debugging only */ + printk ("!!die_if_kernel: %s(%d): %s %ld\n", + current->comm, current->pid, str, err); + show_regs(regs); +#endif + return; + } + + printk("%s[%d]: %s %ld\n", current->comm, current->pid, str, err); + + show_regs(regs); + + /* Wot's wrong wif bein' racy? */ + if (current->thread.flags & PARISC_KERNEL_DEATH) { + printk("die_if_kernel recursion detected.\n"); + sti(); + while (1); + } + current->thread.flags |= PARISC_KERNEL_DEATH; + do_exit(SIGSEGV); +} + +asmlinkage void cache_flush_denied(struct pt_regs * regs, long error_code) +{ +} + +asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) +{ +} + +#ifndef CONFIG_MATH_EMULATION + +asmlinkage void math_emulate(long arg) +{ +} + +#endif /* CONFIG_MATH_EMULATION */ + +int syscall_ipi(int (*syscall) (struct pt_regs *), struct pt_regs *regs) +{ + return syscall(regs); +} + +struct { + int retval; + + int (*func) (void *, struct pt_regs *); + void * data; +} ipi_action[NR_CPUS]; + +void ipi_interrupt(int irq, void *unused, struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + + if(!ipi_action[cpu].func) + BUG(); + + ipi_action[cpu].retval = + ipi_action[cpu].func(ipi_action[cpu].data, regs); +} + +/* gdb uses break 4,8 */ +#define GDB_BREAK_INSN 0x10004 +void handle_gdb_break(struct pt_regs *regs, int wot) +{ + struct siginfo si; + + si.si_code = wot; + si.si_addr = (void *) (regs->iaoq[0] & ~3); + si.si_signo = SIGTRAP; + si.si_errno = 0; + force_sig_info(SIGTRAP, &si, current); +} + +void handle_break(unsigned iir, struct pt_regs *regs) +{ + struct siginfo si; +#ifdef CONFIG_KWDB + struct save_state ssp; +#endif /* CONFIG_KWDB */ + + flush_all_caches(); + switch(iir) { + case 0x00: + /* show registers, halt */ + cli(); + printk("break 0,0: pid=%d command='%s'\n", + current->pid, current->comm); + die_if_kernel("Breakpoint", regs, 0); + show_regs(regs); + si.si_code = TRAP_BRKPT; + si.si_addr = (void *) (regs->iaoq[0] & ~3); + si.si_signo = SIGTRAP; + force_sig_info(SIGTRAP, &si, current); + break; + + case GDB_BREAK_INSN: + die_if_kernel("Breakpoint", regs, 0); + handle_gdb_break(regs, TRAP_BRKPT); + break; + +#ifdef CONFIG_KWDB + + case KGDB_BREAK_INSN: + mtctl(0, 15); + pt_regs_to_ssp(regs, &ssp); + kgdb_trap(I_BRK_INST, &ssp, 1); + ssp_to_pt_regs(&ssp, regs); + break; + + case KGDB_INIT_BREAK_INSN: + mtctl(0, 15); + pt_regs_to_ssp(regs, &ssp); + kgdb_trap(I_BRK_INST, &ssp, 1); + ssp_to_pt_regs(&ssp, regs); + + /* Advance pcoq to skip break */ + regs->iaoq[0] = regs->iaoq[1]; + regs->iaoq[1] += 4; + break; + +#endif /* CONFIG_KWDB */ + + default: + set_eiem(0); + printk("break %#08x: pid=%d command='%s'\n", + iir, current->pid, current->comm); + show_regs(regs); + si.si_signo = SIGTRAP; + si.si_code = TRAP_BRKPT; + si.si_addr = (void *) (regs->iaoq[0] & ~3); + force_sig_info(SIGTRAP, &si, current); + return; + } +} + +/* Format of the floating-point exception registers. */ +struct exc_reg { + unsigned int exception : 6; + unsigned int ei : 26; +}; + +/* Macros for grabbing bits of the instruction format from the 'ei' + field above. */ +/* Major opcode 0c and 0e */ +#define FP0CE_UID(i) (((i) >> 6) & 3) +#define FP0CE_CLASS(i) (((i) >> 9) & 3) +#define FP0CE_SUBOP(i) (((i) >> 13) & 7) +#define FP0CE_SUBOP1(i) (((i) >> 15) & 7) /* Class 1 subopcode */ +#define FP0C_FORMAT(i) (((i) >> 11) & 3) +#define FP0E_FORMAT(i) (((i) >> 11) & 1) + +/* Major opcode 0c, uid 2 (performance monitoring) */ +#define FPPM_SUBOP(i) (((i) >> 9) & 0x1f) + +/* Major opcode 2e (fused operations). */ +#define FP2E_SUBOP(i) (((i) >> 5) & 1) +#define FP2E_FORMAT(i) (((i) >> 11) & 1) + +/* Major opcode 26 (FMPYSUB) */ +/* Major opcode 06 (FMPYADD) */ +#define FPx6_FORMAT(i) ((i) & 0x1f) + +/* Flags and enable bits of the status word. */ +#define FPSW_FLAGS(w) ((w) >> 27) +#define FPSW_ENABLE(w) ((w) & 0x1f) +#define FPSW_V (1<<4) +#define FPSW_Z (1<<3) +#define FPSW_O (1<<2) +#define FPSW_U (1<<1) +#define FPSW_I (1<<0) + +/* Emulate a floating point instruction if necessary and possible + (this will be moved elsewhere eventually). Return zero if + successful or if emulation was not required, -1 if the instruction + is actually illegal or unimplemented. The status word passed as + the first parameter will be modified to signal exceptions, if + any. */ + +/* FIXME!!! This is really incomplete and, at the moment, most + illegal FP instructions will simply act as no-ops. Obviously that + is *not* what we want. Also we don't even try to handle exception + types other than the 'unimplemented' ones. */ +int +fp_emul_insn(u32 *sw, struct exc_reg exc, struct pt_regs *regs) +{ + switch (exc.exception) { + case 0x3: /* Unimplemented, opcode 06 */ + break; + case 0x9: /* Unimplemented, opcode 0c */ + /* We do not support quadword operations, end of + story. There's no support for them in GCC. */ + if (FP0C_FORMAT(exc.ei) == 3) + return -1; /* SIGILL */ + /* Fall through. */ + case 0xa: /* Unimplemented, opcode 0e */ + if (FP0CE_CLASS(exc.ei) == 1) { + /* FCNV instructions of various sorts. */ + } else { + if (FP0CE_CLASS(exc.ei == 0) + && FP0CE_SUBOP(exc.ei == 5)) { + /* FRND instructions should be + emulated, at some point, I + guess. */ + return -1; /* SIGILL */ + } + } + break; + case 0x23: /* Unimplemented, opcode 26 */ + break; + case 0x2b: /* Unimplemented, opcode 2e */ + break; + case 0x1: /* Unimplemented, opcode 0e/0c */ + /* FIXME: How the hell are we supposed to tell which + opcode it is? */ + break; + default: + return -1; /* Punt */ + } + + return 0; +} + +/* Handle a floating point exception. Return zero if the faulting + instruction can be completed successfully. */ +int +handle_fpe(struct pt_regs *regs) +{ + struct siginfo si; + union { + struct fpsw { + /* flag bits */ + unsigned int fv : 1; + unsigned int fz : 1; + unsigned int fo : 1; + unsigned int fu : 1; + unsigned int fi : 1; + + unsigned int c : 1; + unsigned int pad1 : 4; + unsigned int cq : 11; + unsigned int rm : 2; + unsigned int pad2 : 2; + unsigned int t : 1; + unsigned int d : 1; + + /* enable bits */ + unsigned int ev : 1; + unsigned int ez : 1; + unsigned int eo : 1; + unsigned int eu : 1; + unsigned int ei : 1; + } status; + u32 word; + } sw; + struct exc_reg excepts[7]; + unsigned int code = 0; + unsigned int throw; + + /* Status word = FR0L. */ + memcpy(&sw, regs->fr, sizeof(sw)); + /* Exception words = FR0R-FR3R. */ + memcpy(excepts, ((char *) regs->fr) + 4, sizeof(excepts)); + + /* This is all CPU dependent. Since there is no public + documentation on the PA2.0 processors we will just assume + everything is like the 7100/7100LC/7300LC for now. + + Specifically: All exceptions are marked as "unimplemented" + in the exception word, and the only exception word used is + excepts[1]. */ + + /* Try to emulate the instruction. Also determine if it is + really an illegal instruction in the process. + + FIXME: fp_emul_insn() only checks for the "unimplemented" + exceptions at the moment. So this may break horribly on + PA2.0, where we may want to also check to see if we should + just send SIGFPE (or maybe not, let's see the documentation + first...) */ + if (fp_emul_insn(&sw.word, excepts[1], regs) == -1) + goto send_sigill; + + /* Take the intersection of the flag bits in the FPSW and the + enable bits in the FPSW. */ + throw = FPSW_FLAGS(sw.word) & FPSW_ENABLE(sw.word); + + /* Concoct an appropriate si_code. Of course we don't know + what to do if multiple exceptions were enabled and multiple + flags were set. Maybe that's why HP/UX doesn't implement + feenableexcept(). */ + + if (throw == 0) + goto success; /* Duh. */ + else if (throw & FPSW_V) + code = FPE_FLTINV; + else if (throw & FPSW_Z) + code = FPE_FLTDIV; + else if (throw & FPSW_O) + code = FPE_FLTOVF; + else if (throw & FPSW_U) + code = FPE_FLTUND; + else if (throw & FPSW_I) + code = FPE_FLTRES; + +#if 1 /* Debugging... */ + printk("Unemulated floating point exception, pid=%d (%s)\n", + current->pid, current->comm); + show_regs(regs); + { + int i; + printk("FP Status: %08x\n", sw.word); + printk("FP Exceptions:\n"); + for (i = 0; i < 7; i++) { + printk("\tExcept%d: exception %03x insn %06x\n", + i, excepts[i].exception, excepts[i].ei); + } + } +#endif + + /* FIXME: Should we clear the flag bits, T bit, and exception + registers here? */ + + si.si_signo = SIGFPE; + si.si_errno = 0; + si.si_code = code; + si.si_addr = (void *) regs->iaoq[0]; + force_sig_info(SIGFPE, &si, current); + return -1; + + send_sigill: + si.si_signo = SIGILL; + si.si_errno = 0; + si.si_code = ILL_COPROC; + si.si_addr = (void *) regs->iaoq[0]; + force_sig_info(SIGILL, &si, current); + return -1; + + success: + /* We absolutely have to clear the T bit and exception + registers to allow the process to recover. Otherwise every + subsequent floating point instruction will trap. */ + sw.status.t = 0; + memset(excepts, 0, sizeof(excepts)); + + memcpy(regs->fr, &sw, sizeof(sw)); + memcpy(((char *) regs->fr) + 4,excepts , sizeof(excepts)); + return 0; +} + +int handle_toc(void) +{ + return 0; +} + +void default_trap(int code, struct pt_regs *regs) +{ + printk("Trap %d on CPU %d\n", code, smp_processor_id()); + + show_regs(regs); +} + +void (*cpu_lpmc) (int code, struct pt_regs *regs) = default_trap; + + +#ifdef CONFIG_KWDB +int +debug_call (void) { + printk ("Debug call.\n"); + return 0; +} + +int +debug_call_leaf (void) { + return 0; +} +#endif /* CONFIG_KWDB */ + +extern void do_page_fault(struct pt_regs *, int, unsigned long); +extern void parisc_terminate(char *, struct pt_regs *, int, unsigned long); +extern void transfer_pim_to_trap_frame(struct pt_regs *); +extern void pdc_console_restart(void); + +void handle_interruption(int code, struct pt_regs *regs) +{ + unsigned long fault_address = 0; + unsigned long fault_space = 0; + struct siginfo si; +#ifdef CONFIG_KWDB + struct save_state ssp; +#endif /* CONFIG_KWDB */ + + if (code == 1) + pdc_console_restart(); /* switch back to pdc if HPMC */ + else + sti(); + +#ifdef __LP64__ + + /* + * FIXME: + * For 32 bit processes we don't want the b bits (bits 0 & 1) + * in the ior. This is more appropriately handled in the tlb + * miss handlers. Changes need to be made to support addresses + * >32 bits for 64 bit processes. + */ + + regs->ior &= 0x3FFFFFFFFFFFFFFFUL; +#endif + +#if 0 + printk("interrupted with code %d, regs %p\n", code, regs); + show_regs(regs); +#endif + + switch(code) { + case 1: + parisc_terminate("High Priority Machine Check (HPMC)",regs,code,0); + /* NOT REACHED */ + case 3: /* Recovery counter trap */ + regs->gr[0] &= ~PSW_R; + if (regs->iasq[0]) + handle_gdb_break(regs, TRAP_TRACE); + /* else this must be the start of a syscall - just let it + * run. + */ + return; + + case 5: + flush_all_caches(); + cpu_lpmc(5, regs); + return; + + case 6: + fault_address = regs->iaoq[0]; + fault_space = regs->iasq[0]; + break; + + case 9: /* Break Instruction */ + handle_break(regs->iir,regs); + return; + + case 14: + /* Assist Exception Trap, i.e. floating point exception. */ + die_if_kernel("Floating point exception", regs, 0); /* quiet */ + handle_fpe(regs); + return; + case 15: + case 16: /* Non-Access TLB miss faulting address is in IOR */ + case 17: + case 26: + fault_address = regs->ior; + fault_space = regs->isr; + + if (code == 26 && fault_space == 0) + parisc_terminate("Data access rights fault in kernel",regs,code,fault_address); + break; + + case 19: + regs->gr[0] |= PSW_X; /* So we can single-step over the trap */ + /* fall thru */ + case 21: + handle_gdb_break(regs, TRAP_HWBKPT); + return; + + case 25: /* Taken branch trap */ + regs->gr[0] &= ~PSW_T; + if (regs->iasq[0]) + handle_gdb_break(regs, TRAP_BRANCH); + /* else this must be the start of a syscall - just let it + * run. + */ + return; + +#if 0 /* def CONFIG_KWDB */ + case I_TAKEN_BR: /* 25 */ + mtctl(0, 15); + pt_regs_to_ssp(regs, &ssp); + kgdb_trap(I_TAKEN_BR, &ssp, 1); + ssp_to_pt_regs(&ssp, regs); + break; +#endif /* CONFIG_KWDB */ + + case 8: + die_if_kernel("Illegal instruction", regs, code); + si.si_code = ILL_ILLOPC; + goto give_sigill; + + case 10: + die_if_kernel("Priviledged operation - shouldn't happen!", regs, code); + si.si_code = ILL_PRVOPC; + goto give_sigill; + case 11: + die_if_kernel("Priviledged register - shouldn't happen!", regs, code); + si.si_code = ILL_PRVREG; + give_sigill: + si.si_signo = SIGILL; + si.si_errno = 0; + si.si_addr = (void *) regs->iaoq[0]; + force_sig_info(SIGILL, &si, current); + return; + + case 28: /* Unaligned just causes SIGBUS for now */ + die_if_kernel("Unaligned data reference", regs, code); + si.si_code = BUS_ADRALN; + si.si_signo = SIGBUS; + si.si_errno = 0; + si.si_addr = (void *) regs->ior; + force_sig_info(SIGBUS, &si, current); + return; + + default: + if (user_mode(regs)) { + printk("\nhandle_interruption() pid=%d command='%s'\n", + current->pid, current->comm); + show_regs(regs); + /* SIGBUS, for lack of a better one. */ + si.si_signo = SIGBUS; + si.si_code = BUS_OBJERR; + si.si_errno = 0; + si.si_addr = (void *) regs->ior; + force_sig_info(SIGBUS, &si, current); + return; + } + parisc_terminate("Unexpected Interruption!",regs,code,0); + /* NOT REACHED */ + } + + if (user_mode(regs)) { + if (fault_space != regs->sr[7]) { + if (fault_space == 0) + printk("User Fault on Kernel Space "); + else /* this case should never happen, but whatever... */ + printk("User Fault (long pointer) "); + printk("pid=%d command='%s'\n", current->pid, current->comm); + show_regs(regs); + si.si_signo = SIGSEGV; + si.si_errno = 0; + si.si_code = SEGV_MAPERR; + si.si_addr = (void *) regs->ior; + force_sig_info(SIGSEGV, &si, current); + return; + } + } + else { + + /* + * The kernel should never fault on its own address space. + */ + + if (fault_space == 0) + parisc_terminate("Kernel Fault",regs,code,fault_address); + } + +#ifdef CONFIG_KWDB + debug_call_leaf (); +#endif /* CONFIG_KWDB */ + + do_page_fault(regs, code, fault_address); + + /* + * This should not be necessary. + * However, we do not currently + * implement flush_page_to_ram. + * + * The problem is that if we just + * brought in some code through the + * D-cache, the I-cache may not see + * it since it hasn't been flushed + * to ram. + */ + +/* flush_all_caches(); */ + +#if 0 + printk("returning %p\n", regs); +/* show_regs(regs); */ +#endif + + return; + +} + +void show_stack(unsigned long sp) +{ +#if 1 + if ((sp & 0xc0000000UL) == 0xc0000000UL) { + + __u32 *stackptr; + __u32 *dumpptr; + + /* Stack Dump! */ + + stackptr = (__u32 *)sp; + dumpptr = (__u32 *)(sp & ~(INIT_TASK_SIZE - 1)); + printk("\nDumping Stack from %p to %p:\n",dumpptr,stackptr); + while (dumpptr < stackptr) { + printk("%04x %08x %08x %08x %08x %08x %08x %08x %08x\n", + ((__u32)dumpptr) & 0xffff, + dumpptr[0], dumpptr[1], dumpptr[2], dumpptr[3], + dumpptr[4], dumpptr[5], dumpptr[6], dumpptr[7]); + dumpptr += 8; + } + } +#endif +} + + +void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long offset) +{ + set_eiem(0); + cli(); + + if (code == 1) + transfer_pim_to_trap_frame(regs); + +#if 1 + show_stack(regs->gr[30]); +#endif + + printk("\n%s: Code=%d regs=%p (Addr=%08lx)\n",msg,code,regs,offset); + show_regs(regs); + + for(;;) + ; +} + +void transfer_pim_to_trap_frame(struct pt_regs *regs) +{ + register int i; + extern unsigned int hpmc_pim_data[]; + struct pdc_hpmc_pim_11 *pim_narrow; + struct pdc_hpmc_pim_20 *pim_wide; + + if (boot_cpu_data.cpu_type >= pcxu) { + + pim_wide = (struct pdc_hpmc_pim_20 *)hpmc_pim_data; + + /* + * Note: The following code will probably generate a + * bunch of truncation error warnings from the compiler. + * Could be handled with an ifdef, but perhaps there + * is a better way. + */ + + regs->gr[0] = pim_wide->cr[22]; + + for (i = 1; i < 32; i++) + regs->gr[i] = pim_wide->gr[i]; + + for (i = 0; i < 32; i++) + regs->fr[i] = pim_wide->fr[i]; + + for (i = 0; i < 8; i++) + regs->sr[i] = pim_wide->sr[i]; + + regs->iasq[0] = pim_wide->cr[17]; + regs->iasq[1] = pim_wide->iasq_back; + regs->iaoq[0] = pim_wide->cr[18]; + regs->iaoq[1] = pim_wide->iaoq_back; + + regs->cr30 = pim_wide->cr[30]; + regs->sar = pim_wide->cr[11]; + regs->iir = pim_wide->cr[19]; + regs->isr = pim_wide->cr[20]; + regs->ior = pim_wide->cr[21]; + } + else { + pim_narrow = (struct pdc_hpmc_pim_11 *)hpmc_pim_data; + + regs->gr[0] = pim_narrow->cr[22]; + + for (i = 1; i < 32; i++) + regs->gr[i] = pim_narrow->gr[i]; + + for (i = 0; i < 32; i++) + regs->fr[i] = pim_narrow->fr[i]; + + for (i = 0; i < 8; i++) + regs->sr[i] = pim_narrow->sr[i]; + + regs->iasq[0] = pim_narrow->cr[17]; + regs->iasq[1] = pim_narrow->iasq_back; + regs->iaoq[0] = pim_narrow->cr[18]; + regs->iaoq[1] = pim_narrow->iaoq_back; + + regs->cr30 = pim_narrow->cr[30]; + regs->sar = pim_narrow->cr[11]; + regs->iir = pim_narrow->cr[19]; + regs->isr = pim_narrow->cr[20]; + regs->ior = pim_narrow->cr[21]; + } + + /* + * The following fields only have meaning if we came through + * another path. So just zero them here. + */ + + regs->ksp = 0; + regs->kpc = 0; + regs->orig_r28 = 0; +} + +int __init check_ivt(void *iva) +{ + int i; + u32 check = 0; + u32 *ivap; + u32 *hpmcp; + u32 length; + extern void os_hpmc(void); + extern void os_hpmc_end(void); + + if(strcmp((char *)iva, "cows can fly")) + return -1; + + ivap = (u32 *)iva; + + for (i = 0; i < 8; i++) + *ivap++ = 0; + + /* Compute Checksum for HPMC handler */ + + length = (u32)((unsigned long)os_hpmc_end - (unsigned long)os_hpmc); + ivap[7] = length; + + hpmcp = (u32 *)os_hpmc; + + for(i=0; i= pcxu) + iva = (void *) &fault_vector_20; + else +#ifdef __LP64__ + panic("Can't boot 64-bit OS on PA1.1 processor!"); +#else + iva = (void *) &fault_vector_11; +#endif + + if(check_ivt(iva)) + panic("IVT invalid"); + + mtctl(0, 30); + mtctl(90000000, 16); + set_eiem(-1L); + mtctl(-1L, 23); + asm volatile ("rsm 0,%0" : "=r" (eiem)); +} diff --git a/arch/parisc/lib/Makefile b/arch/parisc/lib/Makefile new file mode 100644 index 000000000000..9f0670667621 --- /dev/null +++ b/arch/parisc/lib/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for parisc-specific library files.. +# + + +L_TARGET = lib.a +L_OBJS = lusercopy.o bitops.o checksum.o + + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o + +include $(TOPDIR)/Rules.make diff --git a/arch/parisc/lib/bitops.c b/arch/parisc/lib/bitops.c new file mode 100644 index 000000000000..37797eec5c9b --- /dev/null +++ b/arch/parisc/lib/bitops.c @@ -0,0 +1,59 @@ +/* atomic.c: atomic operations which got too long to be inlined all over + * the place. + * + * Copyright 1999 Philipp Rumpf (prumpf@tux.org */ + +#include +#include +#include +#include + +#ifdef CONFIG_SMP +spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] = { + [0 ... (ATOMIC_HASH_SIZE-1)] = SPIN_LOCK_UNLOCKED +}; +#endif + +spinlock_t __atomic_lock = SPIN_LOCK_UNLOCKED; + +#ifndef __LP64__ +unsigned long __xchg(unsigned long x, unsigned long *ptr, int size) +{ + unsigned long temp, flags; + + if (size != sizeof x) { + printk("__xchg called with bad pointer\n"); + } + spin_lock_irqsave(&__atomic_lock, flags); + temp = *ptr; + *ptr = x; + spin_unlock_irqrestore(&__atomic_lock, flags); + return temp; +} +#else +unsigned long __xchg(unsigned long x, unsigned long *ptr, int size) +{ + unsigned long temp, flags; + unsigned int *ptr32; + + if (size == 8) { +try_long: + spin_lock_irqsave(&__atomic_lock, flags); + temp = *ptr; + *ptr = x; + spin_unlock_irqrestore(&__atomic_lock, flags); + return temp; + } + if (size == 4) { + ptr32 = (unsigned int *)ptr; + spin_lock_irqsave(&__atomic_lock, flags); + temp = (unsigned long)*ptr32; + *ptr32 = (unsigned int)x; + spin_unlock_irqrestore(&__atomic_lock, flags); + return temp; + } + + printk("__xchg called with bad pointer\n"); + goto try_long; +} +#endif diff --git a/arch/parisc/lib/checksum.c b/arch/parisc/lib/checksum.c new file mode 100644 index 000000000000..fa02027dd78f --- /dev/null +++ b/arch/parisc/lib/checksum.c @@ -0,0 +1,130 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * MIPS specific IP/TCP/UDP checksumming routines + * + * Authors: Ralf Baechle, + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * 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. + * + * $Id: checksum.c,v 1.3 1997/12/01 17:57:34 ralf Exp $ + */ +#include +#include +#include +#include +#include + +static inline unsigned short from32to16(unsigned int x) +{ + /* 32 bits --> 16 bits + carry */ + x = (x & 0xffff) + (x >> 16); + /* 16 bits + carry --> 16 bits including carry */ + x = (x & 0xffff) + (x >> 16); + return (unsigned short)x; +} + +static inline unsigned int do_csum(const unsigned char * buff, int len) +{ + int odd, count; + unsigned int result = 0; + + if (len <= 0) + goto out; + odd = 1 & (unsigned long) buff; + if (odd) { + result = be16_to_cpu(*buff); + len--; + buff++; + } + count = len >> 1; /* nr of 16-bit words.. */ + if (count) { + if (2 & (unsigned long) buff) { + result += *(unsigned short *) buff; + count--; + len -= 2; + buff += 2; + } + count >>= 1; /* nr of 32-bit words.. */ + if (count) { + unsigned int carry = 0; + do { + unsigned int w = *(unsigned int *) buff; + count--; + buff += 4; + result += carry; + result += w; + carry = (w > result); + } while (count); + result += carry; + result = (result & 0xffff) + (result >> 16); + } + if (len & 2) { + result += *(unsigned short *) buff; + buff += 2; + } + } + if (len & 1) + result += le16_to_cpu(*buff); + result = from32to16(result); + if (odd) + result = ((result >> 8) & 0xff) | ((result & 0xff) << 8); +out: + return result; +} + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ +unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum) +{ + unsigned int result = do_csum(buff, len); + + /* add in old sum, and carry.. */ + result += sum; + if(sum > result) + result += 1; + return result; +} + +/* + * copy while checksumming, otherwise like csum_partial + */ +unsigned int csum_partial_copy(const char *src, char *dst, + int len, unsigned int sum) +{ + /* + * It's 2:30 am and I don't feel like doing it real ... + * This is lots slower than the real thing (tm) + */ + sum = csum_partial(src, len, sum); + memcpy(dst, src, len); + + return sum; +} + +/* + * Copy from userspace and compute checksum. If we catch an exception + * then zero the rest of the buffer. + */ +unsigned int csum_partial_copy_from_user (const char *src, char *dst, + int len, unsigned int sum, + int *err_ptr) +{ + int missing; + + missing = copy_from_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(dst, len, sum); +} diff --git a/arch/parisc/lib/lusercopy.S b/arch/parisc/lib/lusercopy.S new file mode 100644 index 000000000000..b4b88b53c0fa --- /dev/null +++ b/arch/parisc/lib/lusercopy.S @@ -0,0 +1,242 @@ +/*------------------------------------------------------------------------------ + * Native PARISC/Linux Project (http://www.puffingroup.com/parisc) + * + * Assembly Language User Access Routines + * Copyright (C) 2000 Hewlett-Packard (John Marvin) + * + * 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, 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. + */ + +/* + * These routines still have plenty of room for optimization + * (word & doubleword load/store, dual issue, store hints, etc.). + */ + +/* + * The following routines assume that space register 3 (sr3) contains + * the space id associated with the current users address space. + */ + + + .level 1.1 + .text + +#include +#include + + /* + * get_sr gets the appropriate space value into + * sr1 for kernel/user space access, depending + * on the flag stored in the task structure. + */ + + /* FIXME! depi below has hardcoded idea of kernel stack size */ + + .macro get_sr + copy %r30,%r1 ;! Get task structure + depi 0,31,14,%r1 ;! into r1 + ldw TASK_SEGMENT(%r1),%r22 + mfsp %sr3,%r1 + or,<> %r22,%r0,%r0 + copy %r0,%r1 + mtsp %r1,%sr1 + .endm + + /* + * unsigned long + * lcopy_to_user(void *to, const void *from, unsigned long n) + * + * Returns 0 for success. + * otherwise, returns number of bytes not transferred. + */ + + .export lcopy_to_user,code +lcopy_to_user: + .proc + .callinfo NO_CALLS + .entry + comib,=,n 0,%r24,$lctu_done + get_sr +$lctu_loop: + ldbs,ma 1(%r25),%r1 + addib,<> -1,%r24,$lctu_loop +1: stbs,ma %r1,1(%sr1,%r26) +$lctu_done: + bv %r0(%r2) + copy %r24,%r28 + .exit + +2: b $lctu_done + ldo 1(%r24),%r24 + + .section __ex_table,"a" + .word 1b,(2b-1b) + .previous + + .procend + + /* + * unsigned long + * lcopy_from_user(void *to, const void *from, unsigned long n) + * + * Returns 0 for success. + * otherwise, returns number of bytes not transferred. + * + * NOTE: This routine will also zero any bytes in the + * destination that were not copied due to a fault. + * + */ + + .export lcopy_from_user,code +lcopy_from_user: + .proc + .callinfo NO_CALLS + .entry + comib,=,n 0,%r24,$lcfu_done + get_sr +$lcfu_loop: +1: ldbs,ma 1(%sr1,%r25),%r1 + addib,<> -1,%r24,$lcfu_loop + stbs,ma %r1,1(%r26) +$lcfu_done: + bv %r0(%r2) + copy %r24,%r28 + .exit + +2: copy %r24,%r23 +$lcfu_zero_loop: + addib,<> -1,%r23,$lcfu_zero_loop + stbs,ma %r0,1(%r26) + b $lcfu_done + nop + + .section __ex_table,"a" + .word 1b,(2b-1b) + .previous + + .procend + + /* + * long lstrncpy_from_user(char *dst, const char *src, long n) + * + * Returns -EFAULT if exception before terminator, + * N if the entire buffer filled, + * otherwise strlen + 1 (i.e. includes zero byte) + */ + + .export lstrncpy_from_user,code +lstrncpy_from_user: + .proc + .callinfo NO_CALLS + .entry + comib,= 0,%r24,$lsfu_done + copy %r26,%r23 + get_sr +1: ldbs,ma 1(%sr1,%r25),%r1 +$lsfu_loop: + stbs,ma %r1,1(%r26) + comib,=,n 0,%r1,$lsfu_done + addib,<>,n -1,%r24,$lsfu_loop +2: ldbs,ma 1(%sr1,%r25),%r1 +$lsfu_done: + sub %r26,%r23,%r28 +$lsfu_exit: + bv %r0(%r2) + nop + .exit + +3: b $lsfu_exit + ldi -EFAULT,%r28 + + .section __ex_table,"a" + .word 1b,(3b-1b) + .word 2b,(2b-1b) + .previous + + .procend + + /* + * unsigned long lclear_user(void *to, unsigned long n) + * + * Returns 0 for success. + * otherwise, returns number of bytes not transferred. + */ + + .export lclear_user,code +lclear_user: + .proc + .callinfo NO_CALLS + .entry + comib,=,n 0,%r25,$lclu_done + get_sr +$lclu_loop: + addib,<> -1,%r25,$lclu_loop +1: stbs,ma %r0,1(%sr1,%r26) + +$lclu_done: + bv %r0(%r2) + copy %r25,%r28 + .exit + +2: b $lclu_done + ldo 1(%r25),%r25 + + .section __ex_table,"a" + .word 1b,(2b-1b) + .previous + + .procend + + /* + * long lstrnlen_user(char *s, long n) + * + * Returns 0 if exception before zero byte or reaching N, + * N+1 if N would be exceeded, + * else strlen + 1 (i.e. includes zero byte). + */ + + .export lstrnlen_user,code +lstrnlen_user: + .proc + .callinfo NO_CALLS + .entry + comib,= 0,%r25,$lslen_nzero + copy %r26,%r24 + get_sr +1: ldbs,ma 1(%sr1,%r26),%r1 +$lslen_loop: + comib,=,n 0,%r1,$lslen_done + addib,<> -1,%r25,$lslen_loop +2: ldbs,ma 1(%sr1,%r26),%r1 +$lslen_done: + bv %r0(%r2) + sub %r26,%r24,%r28 + .exit + +$lslen_nzero: + b $lslen_done + ldo 1(%r26),%r26 /* special case for N == 0 */ + +3: b $lslen_done + copy %r24,%r26 /* reset r26 so 0 is returned on fault */ + + .section __ex_table,"a" + .word 1b,(3b-1b) + .word 2b,(2b-1b) + .previous + + .procend + + .end diff --git a/arch/parisc/mm/Makefile b/arch/parisc/mm/Makefile new file mode 100644 index 000000000000..819872a7f968 --- /dev/null +++ b/arch/parisc/mm/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the linux parisc-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := mm.o +O_OBJS := init.o fault.o kmap.o extable.o + +include $(TOPDIR)/Rules.make diff --git a/arch/parisc/mm/extable.c b/arch/parisc/mm/extable.c new file mode 100644 index 000000000000..7056889342a8 --- /dev/null +++ b/arch/parisc/mm/extable.c @@ -0,0 +1,69 @@ +/* + * Kernel exception handling table support. Derived from arch/i386/mm/extable.c. + * + * Copyright (C) 2000 Hewlett-Packard Co + * Copyright (C) 2000 John Marvin (jsm@fc.hp.com) + */ + +#include +#include +#include +#include +#include + + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline const struct exception_table_entry * +search_one_table (const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long addr) +{ + /* Abort early if the search value is out of range. */ + + if ((addr < first->addr) || (addr > last->addr)) + return 0; + + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = first + ((last - first)/2); + diff = mid->addr - addr; + + if (diff == 0) + return mid; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + + return 0; +} + +const struct exception_table_entry * +search_exception_table (unsigned long addr) +{ +#ifndef CONFIG_MODULE + /* There is only the kernel to search. */ + return search_one_table(__start___ex_table, + __stop___ex_table - 1, + addr); +#else + struct exception_table_entry *ret; + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + + for (mp = module_list; mp ; mp = mp->next) { + if (!mp->ex_table_start) + continue; + ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, + addr); + if (ret) + return ret; + } + return 0; +#endif +} diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c new file mode 100644 index 000000000000..68d4614ecf2f --- /dev/null +++ b/arch/parisc/mm/fault.c @@ -0,0 +1,283 @@ +/* $Id: fault.c,v 1.5 2000/01/26 16:20:29 jsm Exp $ + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * + * Copyright (C) 1995, 1996, 1997, 1998 by Ralf Baechle + * Copyright 1999 SuSE GmbH (Philipp Rumpf, prumpf@tux.org) + * Copyright 1999 Hewlett Packard Co. + * + */ + +#include +#include +#include +#include + +#include + + +/* Defines for parisc_acctyp() */ +#define READ 0 +#define WRITE 1 + +/* Various important other fields */ +#define bit22set(x) (x & 0x00000200) +#define bits23_25set(x) (x & 0x000001c0) +#define isGraphicsFlushRead(x) ((x & 0xfc003fdf) == 0x04001a80) + /* extended opcode is 0x6a */ + +#define BITSSET 0x1c0 /* for identifying LDCW */ + +/* + * parisc_acctyp(unsigned int inst) -- + * Given a PA-RISC memory access instruction, determine if the + * the instruction would perform a memory read or memory write + * operation. + * + * This function assumes that the given instruction is a memory access + * instruction (i.e. you should really only call it if you know that + * the instruction has generated some sort of a memory access fault). + * + * Returns: + * VM_READ if read operation + * VM_WRITE if write operation + * VM_EXEC if execute operation + */ +static unsigned long +parisc_acctyp(unsigned long code, unsigned int inst) +{ + if (code == 6 || code == 16) + return VM_EXEC; + + switch (inst & 0xf0000000) { + case 0x40000000: /* load */ + case 0x50000000: /* new load */ + return VM_READ; + + case 0x60000000: /* store */ + case 0x70000000: /* new store */ + return VM_WRITE; + + case 0x20000000: /* coproc */ + case 0x30000000: /* coproc2 */ + if (bit22set(inst)) + return VM_WRITE; + + case 0x0: /* indexed/memory management */ + if (bit22set(inst)) { + /* + * Check for the 'Graphics Flush Read' instruction. + * It resembles an FDC instruction, except for bits + * 20 and 21. Any combination other than zero will + * utilize the block mover functionality on some + * older PA-RISC platforms. The case where a block + * move is performed from VM to graphics IO space + * should be treated as a READ. + * + * The significance of bits 20,21 in the FDC + * instruction is: + * + * 00 Flush data cache (normal instruction behavior) + * 01 Graphics flush write (IO space -> VM) + * 10 Graphics flush read (VM -> IO space) + * 11 Graphics flush read/write (VM <-> IO space) + */ + if (isGraphicsFlushRead(inst)) + return VM_READ; + return VM_WRITE; + } else { + /* + * Check for LDCWX and LDCWS (semaphore instructions). + * If bits 23 through 25 are all 1's it is one of + * the above two instructions and is a write. + * + * Note: With the limited bits we are looking at, + * this will also catch PROBEW and PROBEWI. However, + * these should never get in here because they don't + * generate exceptions of the type: + * Data TLB miss fault/data page fault + * Data memory protection trap + */ + if (bits23_25set(inst) == BITSSET) + return VM_WRITE; + } + return VM_READ; /* Default */ + } + return VM_READ; /* Default */ +} + +#undef bit22set +#undef bits23_25set +#undef isGraphicsFlushRead +#undef BITSSET + +/* This is similar to expand_stack(), except that it is for stacks + * that grow upwards. + */ + +static inline int expand_stackup(struct vm_area_struct * vma, unsigned long address) +{ + unsigned long grow; + + address += 4 + PAGE_SIZE - 1; + address &= PAGE_MASK; + grow = (address - vma->vm_end) >> PAGE_SHIFT; + if (address - vma->vm_start > current->rlim[RLIMIT_STACK].rlim_cur || + ((vma->vm_mm->total_vm + grow) << PAGE_SHIFT) > current->rlim[RLIMIT_AS].rlim_cur) + return -ENOMEM; + vma->vm_end = address; + vma->vm_mm->total_vm += grow; + if (vma->vm_flags & VM_LOCKED) + vma->vm_mm->locked_vm += grow; + return 0; +} + + +/* This is similar to find_vma(), except that it understands that stacks + * grow up rather than down. + * XXX Optimise by making use of cache and avl tree as per find_vma(). + */ + +struct vm_area_struct * pa_find_vma(struct mm_struct * mm, unsigned long addr) +{ + struct vm_area_struct *vma = NULL; + + if (mm) { + vma = mm->mmap; + if (!vma || addr < vma->vm_start) + return NULL; + while (vma->vm_next && addr >= vma->vm_next->vm_start) + vma = vma->vm_next; + } + return vma; +} + + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + */ +extern void parisc_terminate(char *, struct pt_regs *, int, unsigned long); + +void do_page_fault(struct pt_regs *regs, unsigned long code, + unsigned long address) +{ + struct vm_area_struct * vma; + struct task_struct *tsk = current; + struct mm_struct *mm = tsk->mm; + const struct exception_table_entry *fix; + unsigned long acc_type; + + if (in_interrupt() || !mm) + goto no_context; + + down(&mm->mmap_sem); + vma = pa_find_vma(mm, address); + if (!vma) + goto bad_area; + if (address < vma->vm_end) + goto good_area; + if (!(vma->vm_flags & VM_GROWSUP) || expand_stackup(vma, address)) + goto bad_area; +/* + * Ok, we have a good vm_area for this memory access. We still need to + * check the access permissions. + */ + +good_area: + + acc_type = parisc_acctyp(code,regs->iir); + + if ((vma->vm_flags & acc_type) != acc_type) + goto bad_area; + + /* + * If for any reason at all we couldn't handle the fault, make + * sure we exit gracefully rather than endlessly redo the + * fault. + */ + + switch (handle_mm_fault(mm, vma, address, (acc_type & VM_WRITE) != 0)) { + case 1: + ++current->min_flt; + break; + case 2: + ++current->maj_flt; + break; + case 0: + /* + * We ran out of memory, or some other thing happened + * to us that made us unable to handle the page fault + * gracefully. + */ + goto bad_area; + default: + goto out_of_memory; + } + up(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + */ +bad_area: + up(&mm->mmap_sem); + + if (user_mode(regs)) { + struct siginfo si; + + printk("\ndo_page_fault() pid=%d command='%s'\n", + tsk->pid, tsk->comm); + show_regs(regs); + /* FIXME: actually we need to get the signo and code correct */ + si.si_signo = SIGSEGV; + si.si_errno = 0; + si.si_code = SEGV_MAPERR; + si.si_addr = (void *) address; + force_sig_info(SIGSEGV, &si, current); + return; + } + +no_context: + + if (!user_mode(regs)) { + + fix = search_exception_table(regs->iaoq[0]); + + if (fix) { + + if (fix->skip & 1) + regs->gr[8] = -EFAULT; + if (fix->skip & 2) + regs->gr[9] = 0; + + regs->iaoq[0] += ((fix->skip) & ~3); + + /* + * NOTE: In some cases the faulting instruction + * may be in the delay slot of a branch. We + * don't want to take the branch, so we don't + * increment iaoq[1], instead we set it to be + * iaoq[0]+4, and clear the B bit in the PSW + */ + + regs->iaoq[1] = regs->iaoq[0] + 4; + regs->gr[0] &= ~PSW_B; /* IPSW in gr[0] */ + + return; + } + } + + parisc_terminate("Bad Address (null pointer deref?)",regs,code,address); + + out_of_memory: + up(&mm->mmap_sem); + printk("VM: killing process %s\n", current->comm); + if (user_mode(regs)) + do_exit(SIGKILL); + goto no_context; +} diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c new file mode 100644 index 000000000000..c3e16c463832 --- /dev/null +++ b/arch/parisc/mm/init.c @@ -0,0 +1,479 @@ +/* + * linux/arch/parisc/mm/init.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright 1999 SuSE GmbH + * changed by Philipp Rumpf + * Copyright 1999 Philipp Rumpf (prumpf@tux.org) + * + */ + +#include + +#include +#include +#include +#include +#include /* for hppa_dma_ops and pcxl_dma_ops */ +#include +#include + +#include + +static unsigned long totalram_pages; +extern unsigned long max_pfn, mem_max; + +void free_initmem(void) { +} + +/* + * Just an arbitrary offset to serve as a "hole" between mapping areas + * (between top of physical memory and a potential pcxl dma mapping + * area, and below the vmalloc mapping area). + * + * The current 32K value just means that there will be a 32K "hole" + * between mapping areas. That means that any out-of-bounds memory + * accesses will hopefully be caught. The vmalloc() routines leaves + * a hole of 4kB between each vmalloced area for the same reason. + */ + +#define VM_MAP_OFFSET (32*1024) +#define SET_MAP_OFFSET(x) ((void *)(((unsigned long)(x) + VM_MAP_OFFSET) \ + & ~(VM_MAP_OFFSET-1))) + +void *vmalloc_start; +unsigned long pcxl_dma_start; + +void __init mem_init(void) +{ + max_mapnr = num_physpages = max_low_pfn; + high_memory = __va(max_low_pfn * PAGE_SIZE); + + totalram_pages += free_all_bootmem(); + printk("Memory: %luk available\n", totalram_pages << (PAGE_SHIFT-10)); + + if (hppa_dma_ops == &pcxl_dma_ops) { + pcxl_dma_start = (unsigned long)SET_MAP_OFFSET(high_memory); + vmalloc_start = SET_MAP_OFFSET(pcxl_dma_start + PCXL_DMA_MAP_SIZE); + } + else { + pcxl_dma_start = 0; + vmalloc_start = SET_MAP_OFFSET(high_memory); + } +} + +void __bad_pgd(pgd_t *pgd) +{ + printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd)); + pgd_val(*pgd) = _PAGE_TABLE + __pa(BAD_PAGETABLE); +} + +void __bad_pmd(pmd_t *pmd) +{ + printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); + pmd_val(*pmd) = _PAGE_TABLE + __pa(BAD_PAGETABLE); +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) __get_free_page(GFP_KERNEL); + + if (pmd_none(*pmd)) { + if (pte) { + clear_page(pte); + pmd_val(*pmd) = _PAGE_TABLE + __pa((unsigned long)pte); + return pte + offset; + } + pmd_val(*pmd) = _PAGE_TABLE + __pa(BAD_PAGETABLE); + return NULL; + } + + free_page((unsigned long)pte); + + if (pmd_bad(*pmd)) { + __bad_pmd(pmd); + return NULL; + } + + return (pte_t *) pmd_page(*pmd) + offset; +} + +int do_check_pgt_cache(int low, int high) +{ + return 0; +} + +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving an inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +pte_t * __bad_pagetable(void) +{ + return (pte_t *) NULL; +} + +unsigned long *empty_zero_page; +unsigned long *empty_bad_page; + +pte_t __bad_page(void) +{ + return *(pte_t *)NULL; +} + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0, cached = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (!atomic_read(&mem_map[i].count)) + free++; + else + shared += atomic_read(&mem_map[i].count) - 1; + } + printk("%d pages of RAM\n",total); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + show_buffers(); +} + +void set_pte_phys (unsigned long vaddr, unsigned long phys) +{ +} + + +/* + * pagetable_init() sets up the page tables + * + * Note that gateway_init() places the Linux gateway page at page 0. + * Since gateway pages cannot be dereferenced this has the desirable + * side effect of trapping those pesky NULL-reference errors in the + * kernel. + */ +static void __init pagetable_init(void) +{ + pgd_t *pg_dir; + pmd_t *pmd; + pte_t *pg_table; + unsigned long tmp1; + unsigned long tmp2; + unsigned long address; + unsigned long ro_start; + unsigned long ro_end; + unsigned long fv_addr; + extern const int stext; + extern int data_start; + extern const unsigned long fault_vector_20; + + ro_start = __pa((unsigned long)&stext); + ro_end = __pa((unsigned long)&data_start); + fv_addr = __pa((unsigned long)&fault_vector_20) & PAGE_MASK; + + printk("pagetable_init\n"); + + /* Map whole memory from PAGE_OFFSET */ + pg_dir = (pgd_t *)swapper_pg_dir + USER_PGD_PTRS; + + address = 0; + while (address < mem_max) { + /* XXX: BTLB should be done here */ + +#if PTRS_PER_PMD == 1 + pmd = (pmd_t *)__pa(pg_dir); +#else + pmd = (pmd_t *) (PAGE_MASK & pgd_val(*pg_dir)); + + /* + * pmd is physical at this point + */ + + if (!pmd) { + pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE); + pmd = (pmd_t *) __pa(pmd); + } + + pgd_val(*pg_dir) = _PAGE_TABLE | (unsigned long) pmd; +#endif + pg_dir++; + + /* now change pmd to kernel virtual addresses */ + + pmd = (pmd_t *) __va(pmd); + for (tmp1 = 0 ; tmp1 < PTRS_PER_PMD ; tmp1++,pmd++) { + + /* + * pg_table is physical at this point + */ + + pg_table = (pte_t *) (PAGE_MASK & pmd_val(*pmd)); + if (!pg_table) { + pg_table = (pte_t *) + alloc_bootmem_low_pages(PAGE_SIZE); + pg_table = (pte_t *) __pa(pg_table); + } + + pmd_val(*pmd) = _PAGE_TABLE | + (unsigned long) pg_table; + + /* now change pg_table to kernel virtual addresses */ + + pg_table = (pte_t *) __va(pg_table); + for (tmp2=0; tmp2 < PTRS_PER_PTE; tmp2++,pg_table++) { + pte_t pte; + +#if !defined(CONFIG_KWDB) && !defined(CONFIG_STI_CONSOLE) +#warning STI console should explicitly allocate executable pages but does not +/* KWDB needs to write kernel text when setting break points. +** +** The right thing to do seems like KWDB modify only the pte which +** has a break point on it...otherwise we might mask worse bugs. +*/ + if (address >= ro_start && address < ro_end + && address != fv_addr) + pte = __mk_pte(address, PAGE_KERNEL_RO); + else +#endif + pte = __mk_pte(address, PAGE_KERNEL); + + if (address >= mem_max) + pte_val(pte) = 0; + + set_pte(pg_table, pte); + + address += PAGE_SIZE; + } + + if (address >= mem_max) + break; + } + } + + empty_zero_page = alloc_bootmem_pages(PAGE_SIZE); + memset(empty_zero_page, 0, PAGE_SIZE); +} + +unsigned long gateway_pgd_offset; +unsigned long gateway_pgd_entry; + +static void __init gateway_init(void) +{ + unsigned long hpux_gateway_page_addr; + unsigned long linux_gateway_page_addr; + pgd_t *pg_dir; + pmd_t *pmd_base; + pmd_t *pmd; + pte_t *pg_table_base; + pte_t *pg_table; + /* FIXME: These are 'const' in order to trick the compiler + into not treating them as DP-relative data. */ + extern void * const hpux_gateway_page; + extern void * const linux_gateway_page; + pte_t pte; + + hpux_gateway_page_addr = HPUX_GATEWAY_ADDR & PAGE_MASK; + linux_gateway_page_addr = LINUX_GATEWAY_ADDR & PAGE_MASK; + + gateway_pgd_offset = hpux_gateway_page_addr >> PGDIR_SHIFT; + + /* + * Setup Linux Gateway page. + * + * The Linux gateway page will reside in kernel space (on virtual + * page 0), so it doesn't need to be aliased into user space. + */ + + pg_dir = (pgd_t *)swapper_pg_dir; + +#if PTRS_PER_PMD == 1 + pmd_base = (pmd_t *)pg_dir; + pmd = pmd_base + + ((linux_gateway_page_addr) >> PGDIR_SHIFT); + +#else + pmd_base = (pmd_t *) alloc_bootmem_pages(PAGE_SIZE); + pgd_val(*(pg_dir + (linux_gateway_page_addr >> PGDIR_SHIFT))) = + _PAGE_TABLE | __pa(pmd_base); + + pmd = pmd_base + + ((linux_gateway_page_addr & (PMD_MASK) & (PGDIR_SIZE - 1)) >> + PMD_SHIFT); +#endif + + pg_table_base = (pte_t *) alloc_bootmem_pages(PAGE_SIZE); + + pmd_val(*pmd) = _PAGE_TABLE | __pa(pg_table_base); + + pte = __mk_pte(__pa(&linux_gateway_page), PAGE_GATEWAY); + + pg_table = pg_table_base + + ((linux_gateway_page_addr & (PAGE_MASK) & (PMD_SIZE - 1)) >> + PAGE_SHIFT); + + set_pte(pg_table,pte); + + /* + * Setup HP-UX gateway page. + * This page will be aliased into each user address space. + */ + + pg_table_base = (pte_t *) alloc_bootmem_pages(PAGE_SIZE); + + pte = __mk_pte(__pa(&hpux_gateway_page), PAGE_GATEWAY); + pg_table = pg_table_base + + ((hpux_gateway_page_addr & (PAGE_MASK) & (PMD_SIZE - 1)) >> + PAGE_SHIFT); + + set_pte(pg_table,pte); + + +#if PTRS_PER_PMD == 1 + pmd_base = (pmd_t *)pg_table_base; +#else + pmd_base = (pmd_t *) alloc_bootmem_pages(PAGE_SIZE); + pmd = pmd_base + + ((hpux_gateway_page_addr & (PMD_MASK) & (PGDIR_SIZE - 1)) >> + PMD_SHIFT); + pmd_val(*pmd) = _PAGE_TABLE | __pa(pg_table_base); +#endif + + gateway_pgd_entry = _PAGE_TABLE | __pa(pmd_base); + + /* + * We will be aliasing the HP-UX gateway page into all HP-UX + * user spaces at the same address (not counting the space register + * value) that will be equivalently mapped as long as space register + * hashing is disabled. It will be a problem if anyone touches + * the gateway pages at its "kernel" address, since that is + * NOT equivalently mapped. We'll flush the caches at this + * point, just in case some code has touched those addresses + * previous to this, but all bets are off if they get touched + * after this point. + */ + + flush_all_caches(); + + return; +} + +void __init paging_init(void) +{ + pagetable_init(); + gateway_init(); + + { + unsigned long zones_size[MAX_NR_ZONES] = { max_pfn/2, max_pfn/2, }; + + free_area_init(zones_size); + } +} + +#define NR_SPACE_IDS 8192 + +static unsigned long space_id[NR_SPACE_IDS / (8 * sizeof(long))]; +static unsigned long space_id_index; +static unsigned long free_space_ids = NR_SPACE_IDS; + +/* + * XXX: We should probably unfold the set_bit / test_bit / clear_bit + * locking out of these two functions and have a single spinlock on the + * space_id data structures. + * + * Don't bother. This is all going to be significantly changed in the + * very near future. + */ + +#define SPACEID_SHIFT (PAGE_SHIFT + (PT_NLEVELS)*(PAGE_SHIFT - PT_NLEVELS) - 32) + +unsigned long alloc_sid(void) +{ + unsigned long index; + + if (free_space_ids == 0) + BUG(); + + free_space_ids--; + + do { + index = find_next_zero_bit(space_id, NR_SPACE_IDS, space_id_index); + } while(test_and_set_bit(index, space_id)); + + space_id_index = index; + + return index << SPACEID_SHIFT; +} + +void free_sid(unsigned long spaceid) +{ + unsigned long index = spaceid >> SPACEID_SHIFT; + if (index < 0) + BUG(); + + clear_bit(index, space_id); + + if (space_id_index > index) { + space_id_index = index; + } + free_space_ids++; +} + +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ +#if 0 + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(mem_map + MAP_NR(start)); + set_page_count(mem_map+MAP_NR(start), 1); + free_page(start); + totalram_pages++; + } + printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); +#endif +} +#endif + +void si_meminfo(struct sysinfo *val) +{ + int i; + + i = max_mapnr; + val->totalram = totalram_pages; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); +#if 0 + while (i-- > 0) { + if (PageReserved(mem_map+i)) + continue; + val->totalram++; + if (!atomic_read(&mem_map[i].count)) + continue; + val->sharedram += atomic_read(&mem_map[i].count) - 1; + } + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; +#endif + val->totalhigh = 0; + val->freehigh = 0; + return; +} diff --git a/arch/parisc/mm/kmap.c b/arch/parisc/mm/kmap.c new file mode 100644 index 000000000000..686d540d9d3f --- /dev/null +++ b/arch/parisc/mm/kmap.c @@ -0,0 +1,143 @@ +/* +** Stolen mostly from arch/parisc/kernel/pci-dma.c +*/ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include /* get_order */ + +#undef flush_cache_all +#define flush_cache_all flush_all_caches + +typedef void (*pte_iterator_t) (pte_t * pte, unsigned long arg); + +#if 0 +/* XXX This routine could be used with iterate_page() to replace + * unmap_uncached_page() and save a little code space but I didn't + * do that since I'm not certain whether this is the right path. -PB + */ +static void unmap_cached_pte(pte_t * pte, unsigned long arg) +{ + pte_t page = *pte; + pte_clear(pte); + if (!pte_none(page)) { + if (pte_present(page)) { + unsigned long map_nr = pte_pagenr(page); + if (map_nr < max_mapnr) + __free_page(mem_map + map_nr); + } else { + printk(KERN_CRIT + "Whee.. Swapped out page in kernel page table\n"); + } + } +} +#endif + +/* These two routines should probably check a few things... */ +static void set_uncached(pte_t * pte, unsigned long arg) +{ + pte_val(*pte) |= _PAGE_NO_CACHE; +} + +static void set_cached(pte_t * pte, unsigned long arg) +{ + pte_val(*pte) &= ~_PAGE_NO_CACHE; +} + +static inline void iterate_pte(pmd_t * pmd, unsigned long address, + unsigned long size, pte_iterator_t op, + unsigned long arg) +{ + pte_t *pte; + unsigned long end; + + if (pmd_none(*pmd)) + return; + if (pmd_bad(*pmd)) { + pmd_ERROR(*pmd); + pmd_clear(pmd); + return; + } + pte = pte_offset(pmd, address); + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + op(pte, arg); + address += PAGE_SIZE; + pte++; + } while (address < end); +} + +static inline void iterate_pmd(pgd_t * dir, unsigned long address, + unsigned long size, pte_iterator_t op, + unsigned long arg) +{ + pmd_t *pmd; + unsigned long end; + + if (pgd_none(*dir)) + return; + if (pgd_bad(*dir)) { + pgd_ERROR(*dir); + pgd_clear(dir); + return; + } + pmd = pmd_offset(dir, address); + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + do { + iterate_pte(pmd, address, end - address, op, arg); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address < end); +} + +static void iterate_pages(unsigned long address, unsigned long size, + pte_iterator_t op, unsigned long arg) +{ + pgd_t *dir; + unsigned long end = address + size; + + dir = pgd_offset_k(address); + flush_cache_all(); + do { + iterate_pmd(dir, address, end - address, op, arg); + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (address && (address < end)); + flush_tlb_all(); +} + +void +kernel_set_cachemode(unsigned long vaddr, unsigned long size, int what) +{ + switch (what) { + case IOMAP_FULL_CACHING: + iterate_pages(vaddr, size, set_cached, 0); + flush_tlb_range(&init_mm, vaddr, size); + break; + case IOMAP_NOCACHE_SER: + iterate_pages(vaddr, size, set_uncached, 0); + flush_tlb_range(&init_mm, vaddr, size); + break; + default: + printk(KERN_CRIT + "kernel_set_cachemode mode %d not understood\n", + what); + break; + } +} diff --git a/arch/parisc/mm/pa11.c b/arch/parisc/mm/pa11.c new file mode 100644 index 000000000000..42f0d57f6278 --- /dev/null +++ b/arch/parisc/mm/pa11.c @@ -0,0 +1,170 @@ +/* $Id: pa11.c,v 1.1 1999/03/17 01:05:41 pjlahaie Exp $ + * + * pa11.c: PA 1.1 specific mmu/cache code. + * + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern unsigned long mips_tlb_entries; + +/* page functions */ +void pa11_clear_page(unsigned long page) +{ +} + +static void pa11_copy_page(unsigned long to, unsigned long from) +{ +} + +/* Cache operations. */ +static inline void pa11_flush_cache_all(void) { } +static void pa11_flush_cache_mm(struct mm_struct *mm) { } +static void pa11_flush_cache_range(struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ +} + +static void pa11_flush_cache_page(struct vm_area_struct *vma, + unsigned long page) +{ +} + +static void pa11_flush_page_to_ram(unsigned long page) +{ +} + +static void pa11_flush_cache_sigtramp(unsigned long page) +{ +} + +/* TLB operations. */ +static inline void pa11_flush_tlb_all(void) +{ + unsigned long flags; + int entry; + + save_and_cli(flags); +/* Here we will need to flush all the TLBs */ + restore_flags(flags); +} + +static void pa11_flush_tlb_mm(struct mm_struct *mm) +{ +/* This is what the MIPS does.. Is it the right thing for PA-RISC? */ + if(mm == current->mm) + pa11_flush_tlb_all(); +} + +static void pa11_flush_tlb_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + if(mm == current->mm) + pa11_flush_tlb_all(); +} + +static void pa11_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + if(vma->vm_mm == current->mm) + pa11_flush_tlb_all(); +} + +static void pa11_load_pgd(unsigned long pg_dir) +{ + unsigned long flags; + /* We need to do the right thing here */ +} + +/* + * Initialize new page directory with pointers to invalid ptes + */ +static void pa11_pgd_init(unsigned long page) +{ + unsigned long dummy1, dummy2; + +} + +static void pa11_update_mmu_cache(struct vm_area_struct * vma, + unsigned long address, pte_t pte) +{ + pa11_flush_tlb_page(vma, address); +} + +static void pa11_show_regs(struct pt_regs * regs) +{ + /* + * Saved main processor registers + */ + printk("$0 : %08x %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + 0, (unsigned long) regs->regs[1], (unsigned long) regs->regs[2], + (unsigned long) regs->regs[3], (unsigned long) regs->regs[4], + (unsigned long) regs->regs[5], (unsigned long) regs->regs[6], + (unsigned long) regs->regs[7]); + printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + (unsigned long) regs->regs[8], (unsigned long) regs->regs[9], + (unsigned long) regs->regs[10], (unsigned long) regs->regs[11], + (unsigned long) regs->regs[12], (unsigned long) regs->regs[13], + (unsigned long) regs->regs[14], (unsigned long) regs->regs[15]); + printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + (unsigned long) regs->regs[16], (unsigned long) regs->regs[17], + (unsigned long) regs->regs[18], (unsigned long) regs->regs[19], + (unsigned long) regs->regs[20], (unsigned long) regs->regs[21], + (unsigned long) regs->regs[22], (unsigned long) regs->regs[23]); + printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx\n", + (unsigned long) regs->regs[24], (unsigned long) regs->regs[25], + (unsigned long) regs->regs[28], (unsigned long) regs->regs[29], + (unsigned long) regs->regs[30], (unsigned long) regs->regs[31]); + + /* + * Saved cp0 registers + */ + printk("epc : %08lx\nStatus: %08x\nCause : %08x\n", + (unsigned long) regs->cp0_epc, (unsigned int) regs->cp0_status, + (unsigned int) regs->cp0_cause); +} + +static int pa11_user_mode(struct pt_regs *regs) +{ + /* Return user mode stuff?? */ +} + +__initfunc(void ld_mmu_pa11(void)) +{ + + /* Taken directly from the MIPS arch.. Lots of bad things here */ + clear_page = pa11_clear_page; + copy_page = pa11_copy_page; + + flush_cache_all = pa11_flush_cache_all; + flush_cache_mm = pa11_flush_cache_mm; + flush_cache_range = pa11_flush_cache_range; + flush_cache_page = pa11_flush_cache_page; + flush_cache_sigtramp = pa11_flush_cache_sigtramp; + flush_page_to_ram = pa11_flush_page_to_ram; + + flush_tlb_all = pa11_flush_tlb_all; + flush_tlb_mm = pa11_flush_tlb_mm; + flush_tlb_range = pa11_flush_tlb_range; + flush_tlb_page = pa11_flush_tlb_page; + pa11_asid_setup(); + + load_pgd = pa11_load_pgd; + pgd_init = pa11_pgd_init; + update_mmu_cache = pa11_update_mmu_cache; + + show_regs = pa11_show_regs; + + add_wired_entry = pa11_add_wired_entry; + + user_mode = pa11_user_mode; + flush_tlb_all(); +} diff --git a/arch/parisc/mm/pa20.c b/arch/parisc/mm/pa20.c new file mode 100644 index 000000000000..cbc0343a0113 --- /dev/null +++ b/arch/parisc/mm/pa20.c @@ -0,0 +1,170 @@ +/* $Id: pa20.c,v 1.1 1999/03/17 01:05:41 pjlahaie Exp $ + * + * pa20.c: PA 2.0 specific mmu/cache code. + * + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern unsigned long mips_tlb_entries; + +/* page functions */ +void pa20_clear_page(unsigned long page) +{ +} + +static void pa20_copy_page(unsigned long to, unsigned long from) +{ +} + +/* Cache operations. */ +static inline void pa20_flush_cache_all(void) { } +static void pa20_flush_cache_mm(struct mm_struct *mm) { } +static void pa20_flush_cache_range(struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ +} + +static void pa20_flush_cache_page(struct vm_area_struct *vma, + unsigned long page) +{ +} + +static void pa20_flush_page_to_ram(unsigned long page) +{ +} + +static void pa20_flush_cache_sigtramp(unsigned long page) +{ +} + +/* TLB operations. */ +static inline void pa20_flush_tlb_all(void) +{ + unsigned long flags; + int entry; + + save_and_cli(flags); +/* Here we will need to flush all the TLBs */ + restore_flags(flags); +} + +static void pa20_flush_tlb_mm(struct mm_struct *mm) +{ +/* This is what the MIPS does.. Is it the right thing for PA-RISC? */ + if(mm == current->mm) + pa20_flush_tlb_all(); +} + +static void pa20_flush_tlb_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + if(mm == current->mm) + pa20_flush_tlb_all(); +} + +static void pa20_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + if(vma->vm_mm == current->mm) + pa20_flush_tlb_all(); +} + +static void pa20_load_pgd(unsigned long pg_dir) +{ + unsigned long flags; + /* We need to do the right thing here */ +} + +/* + * Initialize new page directory with pointers to invalid ptes + */ +static void pa20_pgd_init(unsigned long page) +{ + unsigned long dummy1, dummy2; + +} + +static void pa20_update_mmu_cache(struct vm_area_struct * vma, + unsigned long address, pte_t pte) +{ + pa20_flush_tlb_page(vma, address); +} + +static void pa20_show_regs(struct pt_regs * regs) +{ + /* + * Saved main processor registers + */ + printk("$0 : %08x %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + 0, (unsigned long) regs->regs[1], (unsigned long) regs->regs[2], + (unsigned long) regs->regs[3], (unsigned long) regs->regs[4], + (unsigned long) regs->regs[5], (unsigned long) regs->regs[6], + (unsigned long) regs->regs[7]); + printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + (unsigned long) regs->regs[8], (unsigned long) regs->regs[9], + (unsigned long) regs->regs[10], (unsigned long) regs->regs[11], + (unsigned long) regs->regs[12], (unsigned long) regs->regs[13], + (unsigned long) regs->regs[14], (unsigned long) regs->regs[15]); + printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + (unsigned long) regs->regs[16], (unsigned long) regs->regs[17], + (unsigned long) regs->regs[18], (unsigned long) regs->regs[19], + (unsigned long) regs->regs[20], (unsigned long) regs->regs[21], + (unsigned long) regs->regs[22], (unsigned long) regs->regs[23]); + printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx\n", + (unsigned long) regs->regs[24], (unsigned long) regs->regs[25], + (unsigned long) regs->regs[28], (unsigned long) regs->regs[29], + (unsigned long) regs->regs[30], (unsigned long) regs->regs[31]); + + /* + * Saved cp0 registers + */ + printk("epc : %08lx\nStatus: %08x\nCause : %08x\n", + (unsigned long) regs->cp0_epc, (unsigned int) regs->cp0_status, + (unsigned int) regs->cp0_cause); +} + +static int pa20_user_mode(struct pt_regs *regs) +{ + /* Return user mode stuff?? */ +} + +__initfunc(void ld_mmu_pa20(void)) +{ + + /* Taken directly from the MIPS arch.. Lots of bad things here */ + clear_page = pa20_clear_page; + copy_page = pa20_copy_page; + + flush_cache_all = pa20_flush_cache_all; + flush_cache_mm = pa20_flush_cache_mm; + flush_cache_range = pa20_flush_cache_range; + flush_cache_page = pa20_flush_cache_page; + flush_cache_sigtramp = pa20_flush_cache_sigtramp; + flush_page_to_ram = pa20_flush_page_to_ram; + + flush_tlb_all = pa20_flush_tlb_all; + flush_tlb_mm = pa20_flush_tlb_mm; + flush_tlb_range = pa20_flush_tlb_range; + flush_tlb_page = pa20_flush_tlb_page; + pa20_asid_setup(); + + load_pgd = pa20_load_pgd; + pgd_init = pa20_pgd_init; + update_mmu_cache = pa20_update_mmu_cache; + + show_regs = pa20_show_regs; + + add_wired_entry = pa20_add_wired_entry; + + user_mode = pa20_user_mode; + flush_tlb_all(); +} diff --git a/arch/parisc/tools/Makefile b/arch/parisc/tools/Makefile new file mode 100644 index 000000000000..54fad5647e8b --- /dev/null +++ b/arch/parisc/tools/Makefile @@ -0,0 +1,28 @@ +# Makefile for MIPS kernel build tools. +# +# Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) +# Copyright (C) 1997 Ralf Baechle (ralf@gnu.ai.mit.edu) +# +# $Id: Makefile,v 1.3 1999/09/29 05:19:56 grundler Exp $ +# +TARGET := $(TOPDIR)/include/asm-$(ARCH)/offset.h + +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +all: $(TARGET) + +$(TARGET): offset.h + cmp -s $^ $@ || (cp $^ $(TARGET).new && mv $(TARGET).new $(TARGET)) + +offset.h: offset.s + sed -n '/^@@@/s///p' $^ >$@ + +offset.s: offset.c + +clean: + rm -f offset.[hs] $(TARGET).new + +include $(TOPDIR)/Rules.make diff --git a/arch/parisc/tools/offset.c b/arch/parisc/tools/offset.c new file mode 100644 index 000000000000..167087f7e807 --- /dev/null +++ b/arch/parisc/tools/offset.c @@ -0,0 +1,326 @@ +/* $Id: offset.c,v 1.2 2000/01/31 13:42:59 jsm Exp $ + * + * offset.c: Calculate pt_regs and task_struct offsets. + * + * Copyright (C) 1996 David S. Miller + * Made portable by Ralf Baechle + * Adapted to parisc by Philipp Rumpf, (C) 1999 SuSE GmbH Nuernberg */ + +#include +#include +#include + +#include +#include +#include + +#define text(t) __asm__("\n@@@" t) +#define _offset(type, member) (&(((type *)NULL)->member)) + +#define offset(string, ptr, member) \ + __asm__("\n@@@" string "%0" : : "i" (_offset(ptr, member))) +#define size(string, size) \ + __asm__("\n@@@" string "%0" : : "i" (sizeof(size))) +#define align(x,y) (((x)+(2*(y))-1)-(((x)+(y)-1)%(y))) +#define size_align(string, size, algn) \ + __asm__("\n@@@" string "%0" : : "i" \ + align(sizeof(size),algn)) +#define linefeed text("") + +text("/* DO NOT TOUCH, AUTOGENERATED BY OFFSET.C */"); +linefeed; +text("#ifndef _PARISC_OFFSET_H"); +text("#define _PARISC_OFFSET_H"); +linefeed; + +void output_task_ptreg_defines(void) +{ + text("/* PA-RISC task pt_regs offsets. */"); + offset("#define TASK_REGS ", struct task_struct, thread.regs); + offset("#define TASK_PT_PSW ", struct task_struct, thread.regs.gr[ 0]); + offset("#define TASK_PT_GR1 ", struct task_struct, thread.regs.gr[ 1]); + offset("#define TASK_PT_GR2 ", struct task_struct, thread.regs.gr[ 2]); + offset("#define TASK_PT_GR3 ", struct task_struct, thread.regs.gr[ 3]); + offset("#define TASK_PT_GR4 ", struct task_struct, thread.regs.gr[ 4]); + offset("#define TASK_PT_GR5 ", struct task_struct, thread.regs.gr[ 5]); + offset("#define TASK_PT_GR6 ", struct task_struct, thread.regs.gr[ 6]); + offset("#define TASK_PT_GR7 ", struct task_struct, thread.regs.gr[ 7]); + offset("#define TASK_PT_GR8 ", struct task_struct, thread.regs.gr[ 8]); + offset("#define TASK_PT_GR9 ", struct task_struct, thread.regs.gr[ 9]); + offset("#define TASK_PT_GR10 ", struct task_struct, thread.regs.gr[10]); + offset("#define TASK_PT_GR11 ", struct task_struct, thread.regs.gr[11]); + offset("#define TASK_PT_GR12 ", struct task_struct, thread.regs.gr[12]); + offset("#define TASK_PT_GR13 ", struct task_struct, thread.regs.gr[13]); + offset("#define TASK_PT_GR14 ", struct task_struct, thread.regs.gr[14]); + offset("#define TASK_PT_GR15 ", struct task_struct, thread.regs.gr[15]); + offset("#define TASK_PT_GR16 ", struct task_struct, thread.regs.gr[16]); + offset("#define TASK_PT_GR17 ", struct task_struct, thread.regs.gr[17]); + offset("#define TASK_PT_GR18 ", struct task_struct, thread.regs.gr[18]); + offset("#define TASK_PT_GR19 ", struct task_struct, thread.regs.gr[19]); + offset("#define TASK_PT_GR20 ", struct task_struct, thread.regs.gr[20]); + offset("#define TASK_PT_GR21 ", struct task_struct, thread.regs.gr[21]); + offset("#define TASK_PT_GR22 ", struct task_struct, thread.regs.gr[22]); + offset("#define TASK_PT_GR23 ", struct task_struct, thread.regs.gr[23]); + offset("#define TASK_PT_GR24 ", struct task_struct, thread.regs.gr[24]); + offset("#define TASK_PT_GR25 ", struct task_struct, thread.regs.gr[25]); + offset("#define TASK_PT_GR26 ", struct task_struct, thread.regs.gr[26]); + offset("#define TASK_PT_GR27 ", struct task_struct, thread.regs.gr[27]); + offset("#define TASK_PT_GR28 ", struct task_struct, thread.regs.gr[28]); + offset("#define TASK_PT_GR29 ", struct task_struct, thread.regs.gr[29]); + offset("#define TASK_PT_GR30 ", struct task_struct, thread.regs.gr[30]); + offset("#define TASK_PT_GR31 ", struct task_struct, thread.regs.gr[31]); + offset("#define TASK_PT_FR0 ", struct task_struct, thread.regs.fr[ 0]); + offset("#define TASK_PT_FR1 ", struct task_struct, thread.regs.fr[ 1]); + offset("#define TASK_PT_FR2 ", struct task_struct, thread.regs.fr[ 2]); + offset("#define TASK_PT_FR3 ", struct task_struct, thread.regs.fr[ 3]); + offset("#define TASK_PT_FR4 ", struct task_struct, thread.regs.fr[ 4]); + offset("#define TASK_PT_FR5 ", struct task_struct, thread.regs.fr[ 5]); + offset("#define TASK_PT_FR6 ", struct task_struct, thread.regs.fr[ 6]); + offset("#define TASK_PT_FR7 ", struct task_struct, thread.regs.fr[ 7]); + offset("#define TASK_PT_FR8 ", struct task_struct, thread.regs.fr[ 8]); + offset("#define TASK_PT_FR9 ", struct task_struct, thread.regs.fr[ 9]); + offset("#define TASK_PT_FR10 ", struct task_struct, thread.regs.fr[10]); + offset("#define TASK_PT_FR11 ", struct task_struct, thread.regs.fr[11]); + offset("#define TASK_PT_FR12 ", struct task_struct, thread.regs.fr[12]); + offset("#define TASK_PT_FR13 ", struct task_struct, thread.regs.fr[13]); + offset("#define TASK_PT_FR14 ", struct task_struct, thread.regs.fr[14]); + offset("#define TASK_PT_FR15 ", struct task_struct, thread.regs.fr[15]); + offset("#define TASK_PT_FR16 ", struct task_struct, thread.regs.fr[16]); + offset("#define TASK_PT_FR17 ", struct task_struct, thread.regs.fr[17]); + offset("#define TASK_PT_FR18 ", struct task_struct, thread.regs.fr[18]); + offset("#define TASK_PT_FR19 ", struct task_struct, thread.regs.fr[19]); + offset("#define TASK_PT_FR20 ", struct task_struct, thread.regs.fr[20]); + offset("#define TASK_PT_FR21 ", struct task_struct, thread.regs.fr[21]); + offset("#define TASK_PT_FR22 ", struct task_struct, thread.regs.fr[22]); + offset("#define TASK_PT_FR23 ", struct task_struct, thread.regs.fr[23]); + offset("#define TASK_PT_FR24 ", struct task_struct, thread.regs.fr[24]); + offset("#define TASK_PT_FR25 ", struct task_struct, thread.regs.fr[25]); + offset("#define TASK_PT_FR26 ", struct task_struct, thread.regs.fr[26]); + offset("#define TASK_PT_FR27 ", struct task_struct, thread.regs.fr[27]); + offset("#define TASK_PT_FR28 ", struct task_struct, thread.regs.fr[28]); + offset("#define TASK_PT_FR29 ", struct task_struct, thread.regs.fr[29]); + offset("#define TASK_PT_FR30 ", struct task_struct, thread.regs.fr[30]); + offset("#define TASK_PT_FR31 ", struct task_struct, thread.regs.fr[31]); + offset("#define TASK_PT_SR0 ", struct task_struct, thread.regs.sr[ 0]); + offset("#define TASK_PT_SR1 ", struct task_struct, thread.regs.sr[ 1]); + offset("#define TASK_PT_SR2 ", struct task_struct, thread.regs.sr[ 2]); + offset("#define TASK_PT_SR3 ", struct task_struct, thread.regs.sr[ 3]); + offset("#define TASK_PT_SR4 ", struct task_struct, thread.regs.sr[ 4]); + offset("#define TASK_PT_SR5 ", struct task_struct, thread.regs.sr[ 5]); + offset("#define TASK_PT_SR6 ", struct task_struct, thread.regs.sr[ 6]); + offset("#define TASK_PT_SR7 ", struct task_struct, thread.regs.sr[ 7]); + offset("#define TASK_PT_IASQ0 ", struct task_struct, thread.regs.iasq[0]); + offset("#define TASK_PT_IASQ1 ", struct task_struct, thread.regs.iasq[1]); + offset("#define TASK_PT_IAOQ0 ", struct task_struct, thread.regs.iaoq[0]); + offset("#define TASK_PT_IAOQ1 ", struct task_struct, thread.regs.iaoq[1]); + offset("#define TASK_PT_CR24 ", struct task_struct, thread.regs.cr24); + offset("#define TASK_PT_CR25 ", struct task_struct, thread.regs.cr25); + offset("#define TASK_PT_CR26 ", struct task_struct, thread.regs.cr26); + offset("#define TASK_PT_CR27 ", struct task_struct, thread.regs.cr27); + offset("#define TASK_PT_CR30 ", struct task_struct, thread.regs.cr30); + offset("#define TASK_PT_ORIG_R28 ", struct task_struct, thread.regs.orig_r28); + offset("#define TASK_PT_KSP ", struct task_struct, thread.regs.ksp); + offset("#define TASK_PT_KPC ", struct task_struct, thread.regs.kpc); + offset("#define TASK_PT_SAR ", struct task_struct, thread.regs.sar); + offset("#define TASK_PT_CR11 ", struct task_struct, thread.regs.sar); + offset("#define TASK_PT_IIR ", struct task_struct, thread.regs.iir); + offset("#define TASK_PT_ISR ", struct task_struct, thread.regs.isr); + offset("#define TASK_PT_IOR ", struct task_struct, thread.regs.ior); + offset("#define TASK_PT_CR_PID0 ", struct task_struct, thread.regs.cr_pid[0]); + offset("#define TASK_PT_CR_PID1 ", struct task_struct, thread.regs.cr_pid[1]); + offset("#define TASK_PT_CR_PID2 ", struct task_struct, thread.regs.cr_pid[2]); + offset("#define TASK_PT_CR_PID3 ", struct task_struct, thread.regs.cr_pid[3]); + size("#define TASK_SZ ", struct task_struct); + size_align("#define TASK_SZ_ALGN ", struct task_struct, 64); + linefeed; +} + +void output_ptreg_defines(void) +{ + text("/* PA-RISC pt_regs offsets. */"); + offset("#define PT_PSW ", struct pt_regs, gr[ 0]); + offset("#define PT_GR1 ", struct pt_regs, gr[ 1]); + offset("#define PT_GR2 ", struct pt_regs, gr[ 2]); + offset("#define PT_GR3 ", struct pt_regs, gr[ 3]); + offset("#define PT_GR4 ", struct pt_regs, gr[ 4]); + offset("#define PT_GR5 ", struct pt_regs, gr[ 5]); + offset("#define PT_GR6 ", struct pt_regs, gr[ 6]); + offset("#define PT_GR7 ", struct pt_regs, gr[ 7]); + offset("#define PT_GR8 ", struct pt_regs, gr[ 8]); + offset("#define PT_GR9 ", struct pt_regs, gr[ 9]); + offset("#define PT_GR10 ", struct pt_regs, gr[10]); + offset("#define PT_GR11 ", struct pt_regs, gr[11]); + offset("#define PT_GR12 ", struct pt_regs, gr[12]); + offset("#define PT_GR13 ", struct pt_regs, gr[13]); + offset("#define PT_GR14 ", struct pt_regs, gr[14]); + offset("#define PT_GR15 ", struct pt_regs, gr[15]); + offset("#define PT_GR16 ", struct pt_regs, gr[16]); + offset("#define PT_GR17 ", struct pt_regs, gr[17]); + offset("#define PT_GR18 ", struct pt_regs, gr[18]); + offset("#define PT_GR19 ", struct pt_regs, gr[19]); + offset("#define PT_GR20 ", struct pt_regs, gr[20]); + offset("#define PT_GR21 ", struct pt_regs, gr[21]); + offset("#define PT_GR22 ", struct pt_regs, gr[22]); + offset("#define PT_GR23 ", struct pt_regs, gr[23]); + offset("#define PT_GR24 ", struct pt_regs, gr[24]); + offset("#define PT_GR25 ", struct pt_regs, gr[25]); + offset("#define PT_GR26 ", struct pt_regs, gr[26]); + offset("#define PT_GR27 ", struct pt_regs, gr[27]); + offset("#define PT_GR28 ", struct pt_regs, gr[28]); + offset("#define PT_GR29 ", struct pt_regs, gr[29]); + offset("#define PT_GR30 ", struct pt_regs, gr[30]); + offset("#define PT_GR31 ", struct pt_regs, gr[31]); + offset("#define PT_FR0 ", struct pt_regs, fr[ 0]); + offset("#define PT_FR1 ", struct pt_regs, fr[ 1]); + offset("#define PT_FR2 ", struct pt_regs, fr[ 2]); + offset("#define PT_FR3 ", struct pt_regs, fr[ 3]); + offset("#define PT_FR4 ", struct pt_regs, fr[ 4]); + offset("#define PT_FR5 ", struct pt_regs, fr[ 5]); + offset("#define PT_FR6 ", struct pt_regs, fr[ 6]); + offset("#define PT_FR7 ", struct pt_regs, fr[ 7]); + offset("#define PT_FR8 ", struct pt_regs, fr[ 8]); + offset("#define PT_FR9 ", struct pt_regs, fr[ 9]); + offset("#define PT_FR10 ", struct pt_regs, fr[10]); + offset("#define PT_FR11 ", struct pt_regs, fr[11]); + offset("#define PT_FR12 ", struct pt_regs, fr[12]); + offset("#define PT_FR13 ", struct pt_regs, fr[13]); + offset("#define PT_FR14 ", struct pt_regs, fr[14]); + offset("#define PT_FR15 ", struct pt_regs, fr[15]); + offset("#define PT_FR16 ", struct pt_regs, fr[16]); + offset("#define PT_FR17 ", struct pt_regs, fr[17]); + offset("#define PT_FR18 ", struct pt_regs, fr[18]); + offset("#define PT_FR19 ", struct pt_regs, fr[19]); + offset("#define PT_FR20 ", struct pt_regs, fr[20]); + offset("#define PT_FR21 ", struct pt_regs, fr[21]); + offset("#define PT_FR22 ", struct pt_regs, fr[22]); + offset("#define PT_FR23 ", struct pt_regs, fr[23]); + offset("#define PT_FR24 ", struct pt_regs, fr[24]); + offset("#define PT_FR25 ", struct pt_regs, fr[25]); + offset("#define PT_FR26 ", struct pt_regs, fr[26]); + offset("#define PT_FR27 ", struct pt_regs, fr[27]); + offset("#define PT_FR28 ", struct pt_regs, fr[28]); + offset("#define PT_FR29 ", struct pt_regs, fr[29]); + offset("#define PT_FR30 ", struct pt_regs, fr[30]); + offset("#define PT_FR31 ", struct pt_regs, fr[31]); + offset("#define PT_SR0 ", struct pt_regs, sr[ 0]); + offset("#define PT_SR1 ", struct pt_regs, sr[ 1]); + offset("#define PT_SR2 ", struct pt_regs, sr[ 2]); + offset("#define PT_SR3 ", struct pt_regs, sr[ 3]); + offset("#define PT_SR4 ", struct pt_regs, sr[ 4]); + offset("#define PT_SR5 ", struct pt_regs, sr[ 5]); + offset("#define PT_SR6 ", struct pt_regs, sr[ 6]); + offset("#define PT_SR7 ", struct pt_regs, sr[ 7]); + offset("#define PT_IASQ0 ", struct pt_regs, iasq[0]); + offset("#define PT_IASQ1 ", struct pt_regs, iasq[1]); + offset("#define PT_IAOQ0 ", struct pt_regs, iaoq[0]); + offset("#define PT_IAOQ1 ", struct pt_regs, iaoq[1]); + offset("#define PT_CR24 ", struct pt_regs, cr24); + offset("#define PT_CR25 ", struct pt_regs, cr25); + offset("#define PT_CR26 ", struct pt_regs, cr26); + offset("#define PT_CR27 ", struct pt_regs, cr27); + offset("#define PT_CR30 ", struct pt_regs, cr30); + offset("#define PT_ORIG_R28 ", struct pt_regs, orig_r28); + offset("#define PT_KSP ", struct pt_regs, ksp); + offset("#define PT_KPC ", struct pt_regs, kpc); + offset("#define PT_SAR ", struct pt_regs, sar); + offset("#define PT_CR11 ", struct pt_regs, sar); + offset("#define PT_IIR ", struct pt_regs, iir); + offset("#define PT_ISR ", struct pt_regs, isr); + offset("#define PT_IOR ", struct pt_regs, ior); + offset("#define PT_CR_PID0 ", struct pt_regs, cr_pid[0]); + offset("#define PT_CR_PID1 ", struct pt_regs, cr_pid[1]); + offset("#define PT_CR_PID2 ", struct pt_regs, cr_pid[2]); + offset("#define PT_CR_PID3 ", struct pt_regs, cr_pid[3]); + size("#define PT_SIZE ", struct pt_regs); + size_align("#define PT_SZ_ALGN ", struct pt_regs, 64); + linefeed; +} + +void output_task_defines(void) +{ + text("/* PARISC task_struct offsets. */"); + offset("#define TASK_STATE ", struct task_struct, state); + offset("#define TASK_FLAGS ", struct task_struct, flags); + offset("#define TASK_SIGPENDING ", struct task_struct, sigpending); + offset("#define TASK_SEGMENT ", struct task_struct, addr_limit); + offset("#define TASK_NEED_RESCHED ", struct task_struct, need_resched); + offset("#define TASK_COUNTER ", struct task_struct, counter); + offset("#define TASK_PTRACE ", struct task_struct, ptrace); + offset("#define TASK_NICE ", struct task_struct, nice); + offset("#define TASK_MM ", struct task_struct, mm); + offset("#define TASK_PROCESSOR ", struct task_struct, processor); + size ("#define TASK_SZ ", struct task_struct); + size_align("#define TASK_SZ_ALGN ", struct task_struct, 64); + linefeed; +} + +void output_irq_stat_defines(void) +{ + text("/* PARISC irq_cpustat_t offsets. */"); + offset("#define IRQSTAT_SI_ACTIVE ", irq_cpustat_t, __softirq_active); + offset("#define IRQSTAT_SI_MASK ", irq_cpustat_t, __softirq_mask); + size ("#define IRQSTAT_SZ ", irq_cpustat_t); + linefeed; +} + +#ifdef PRUMPF_HAD_MORE_TIME +void output_thread_defines(void) +{ + text("/* PARISC specific thread_struct offsets. */"); + offset("#define THREAD_REG16 ", struct task_struct, thread.reg16); + offset("#define THREAD_REG17 ", struct task_struct, thread.reg17); + offset("#define THREAD_REG18 ", struct task_struct, thread.reg18); + offset("#define THREAD_REG19 ", struct task_struct, thread.reg19); + offset("#define THREAD_REG20 ", struct task_struct, thread.reg20); + offset("#define THREAD_REG21 ", struct task_struct, thread.reg21); + offset("#define THREAD_REG22 ", struct task_struct, thread.reg22); + offset("#define THREAD_REG23 ", struct task_struct, thread.reg23); + offset("#define THREAD_REG29 ", struct task_struct, thread.reg29); + offset("#define THREAD_REG30 ", struct task_struct, thread.reg30); + offset("#define THREAD_REG31 ", struct task_struct, thread.reg31); + offset("#define THREAD_STATUS ", struct task_struct, thread.cp0_status); + offset("#define THREAD_FPU ", struct task_struct, thread.fpu); + offset("#define THREAD_BVADDR ", struct task_struct, thread.cp0_badvaddr); + offset("#define THREAD_BUADDR ", struct task_struct, thread.cp0_baduaddr); + offset("#define THREAD_ECODE ", struct task_struct, thread.error_code); + offset("#define THREAD_TRAPNO ", struct task_struct, thread.trap_no); + offset("#define THREAD_PGDIR ", struct task_struct, thread.pg_dir); + offset("#define THREAD_MFLAGS ", struct task_struct, thread.mflags); + offset("#define THREAD_CURDS ", struct task_struct, thread.current_ds); + offset("#define THREAD_TRAMP ", struct task_struct, thread.irix_trampoline); + offset("#define THREAD_OLDCTX ", struct task_struct, thread.irix_oldctx); + linefeed; +} + +void output_mm_defines(void) +{ + text("/* Linux mm_struct offsets. */"); + offset("#define MM_COUNT ", struct mm_struct, count); + offset("#define MM_PGD ", struct mm_struct, pgd); + offset("#define MM_CONTEXT ", struct mm_struct, context); + linefeed; +} + +void output_sc_defines(void) +{ + text("/* Linux sigcontext offsets. */"); + offset("#define SC_REGMASK ", struct sigcontext, sc_regmask); + offset("#define SC_STATUS ", struct sigcontext, sc_status); + offset("#define SC_PC ", struct sigcontext, sc_pc); + offset("#define SC_REGS ", struct sigcontext, sc_regs); + offset("#define SC_FPREGS ", struct sigcontext, sc_fpregs); + offset("#define SC_OWNEDFP ", struct sigcontext, sc_ownedfp); + offset("#define SC_FPC_CSR ", struct sigcontext, sc_fpc_csr); + offset("#define SC_FPC_EIR ", struct sigcontext, sc_fpc_eir); + offset("#define SC_SSFLAGS ", struct sigcontext, sc_ssflags); + offset("#define SC_MDHI ", struct sigcontext, sc_mdhi); + offset("#define SC_MDLO ", struct sigcontext, sc_mdlo); + offset("#define SC_CAUSE ", struct sigcontext, sc_cause); + offset("#define SC_BADVADDR ", struct sigcontext, sc_badvaddr); + offset("#define SC_SIGSET ", struct sigcontext, sc_sigset); + linefeed; +} + +#endif + +text("#endif /* !(_PARISC_OFFSET_H) */"); diff --git a/arch/parisc/vmlinux.lds b/arch/parisc/vmlinux.lds new file mode 100644 index 000000000000..268fc59ce865 --- /dev/null +++ b/arch/parisc/vmlinux.lds @@ -0,0 +1,79 @@ +/* ld script to make hppa Linux kernel */ +OUTPUT_FORMAT("elf32-hppa") +OUTPUT_ARCH(hppa) +ENTRY(_stext) +SECTIONS +{ + +/* right now use 0x10000/0x11000, later when we don't use Console and + * Boot-Device IODC, we will change this to 0x8000 !!! + */ + + . = 0xc0100000; +/* . = 0x10000; */ + + _text = .; /* Text and read-only data */ + .text BLOCK(16) : { + *(.text*) + *(.PARISC.unwind) + *(.fixup) + *(.lock.text) /* out-of-line lock text */ + *(.gnu.warning) + } = 0 + + . = ALIGN(16); + .rodata : { *(.rodata) } + .kstrtab : { *(.kstrtab) } + + _etext = .; /* End of text section */ + + .data BLOCK(8192) : { /* Data without special */ + data_start = .; + *(.data) + } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + + + . = ALIGN(16384); + __init_begin = .; + .init.text : { *(.init.text) } + .init.data : { *(.init.data) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + __init_end = .; + + + init_task BLOCK(16384) : { *(init_task) } /* The initial task and kernel stack */ + + _edata = .; /* End of data section */ + + + .bss : { *(.bss) *(COMMON) } /* BSS */ + + + _end = . ; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .note 0 : { *(.note) } + +} diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index 96c5df387029..0ae7379a9873 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -317,7 +317,7 @@ static volatile int fdc_busy = 0; static DECLARE_WAIT_QUEUE_HEAD(fdc_wait); static DECLARE_WAIT_QUEUE_HEAD(format_wait); -static unsigned int changed_floppies = 0xff, fake_change = 0; +static unsigned long changed_floppies = 0xff, fake_change = 0; #define CHECK_CHANGE_DELAY HZ/2 #define FD_MOTOR_OFF_DELAY (3*HZ) diff --git a/drivers/ide/q40ide.c b/drivers/ide/q40ide.c index 4582d45563e0..cbc99d25f266 100644 --- a/drivers/ide/q40ide.c +++ b/drivers/ide/q40ide.c @@ -10,7 +10,6 @@ * */ -#include #include #include #include diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 22f63442cb3c..73846f660d73 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -234,7 +234,6 @@ #include #include #include -#include #include "capiutil.h" #include "capicmd.h" #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 499970ea202a..b7812b830029 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1298,7 +1298,7 @@ HiSax_reportcard(int cardnr, int sel) printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n", (ulong) & HiSax_reportcard); printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs); - printk(KERN_DEBUG "HiSax: HW_Flags %x bc0 flg %x bc1 flg %x\n", + printk(KERN_DEBUG "HiSax: HW_Flags %lx bc0 flg %lx bc1 flg %lx\n", cs->HW_Flags, cs->bcs[0].Flag, cs->bcs[1].Flag); printk(KERN_DEBUG "HiSax: bcs 0 mode %d ch%d\n", cs->bcs[0].mode, cs->bcs[0].channel); diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h index 97211bc4cf33..22cf911b316e 100644 --- a/drivers/isdn/hisax/hisax.h +++ b/drivers/isdn/hisax/hisax.h @@ -202,7 +202,7 @@ struct Layer1 { void *hardware; struct BCState *bcs; struct PStack **stlistp; - int Flags; + long Flags; struct FsmInst l1m; struct FsmTimer timer; void (*l1l2) (struct PStack *, int, void *); @@ -241,7 +241,7 @@ struct Layer2 { int tei; int sap; int maxlen; - unsigned int flag; + unsigned long flag; unsigned int vs, va, vr; int rc; unsigned int window; @@ -366,7 +366,7 @@ struct w6692B_hw { }; struct isar_reg { - unsigned int Flags; + unsigned long Flags; volatile u_char bstat; volatile u_char iis; volatile u_char cmsb; @@ -484,7 +484,7 @@ struct amd7930_hw { struct BCState { int channel; int mode; - int Flag; + long Flag; /* long req'd for set_bit --RR */ struct IsdnCardState *cs; int tx_cnt; /* B-Channel transmit counter */ struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ @@ -529,7 +529,8 @@ struct Channel { int data_open; struct l3_process *proc; setup_parm setup; /* from isdnif.h numbers and Serviceindicator */ - int Flags; /* for remembering action done in l4 */ + long Flags; /* for remembering action done in l4 */ + /* long req'd for set_bit --RR */ int leased; }; @@ -866,7 +867,7 @@ struct IsdnCardState { int protocol; unsigned int irq; unsigned long irq_flags; - int HW_Flags; + long HW_Flags; int *busy_flag; int chanlimit; /* limited number of B-chans to use */ int logecho; /* log echo if supported by card */ @@ -933,7 +934,7 @@ struct IsdnCardState { int rcvidx; struct sk_buff *tx_skb; int tx_cnt; - int event; + long event; struct tq_struct tqueue; struct timer_list dbusytimer; #ifdef ERROR_STATISTIC diff --git a/drivers/isdn/hisax/l3ni1.c b/drivers/isdn/hisax/l3ni1.c index 46274ae342d6..8f4ccf4215a4 100644 --- a/drivers/isdn/hisax/l3ni1.c +++ b/drivers/isdn/hisax/l3ni1.c @@ -24,7 +24,6 @@ #include "isdnl3.h" #include "l3ni1.h" #include -#include extern char *HiSax_getrev(const char *revision); const char *ni1_revision = "$Revision: 2.5 $"; diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c index 6f0bfe3be757..9dede4757836 100644 --- a/drivers/isdn/hisax/netjet.c +++ b/drivers/isdn/hisax/netjet.c @@ -11,7 +11,6 @@ */ #define __NO_VERSION__ -#include #include #include "hisax.h" #include "isac.h" diff --git a/drivers/md/xor.c b/drivers/md/xor.c index f58463ebc9be..b9b1cefe9e94 100644 --- a/drivers/md/xor.c +++ b/drivers/md/xor.c @@ -16,7 +16,6 @@ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #define BH_TRACE 0 #include #include diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index da052eff57e6..5a9c4575c4fb 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -14,6 +14,7 @@ * */ +#include #include #include #include diff --git a/drivers/net/3c503.c b/drivers/net/3c503.c index 6db67d817941..0a6609eccc0d 100644 --- a/drivers/net/3c503.c +++ b/drivers/net/3c503.c @@ -88,7 +88,7 @@ el2_probe(struct net_device *dev) int base_addr = dev->base_addr; SET_MODULE_OWNER(dev); - + if (base_addr > 0x1ff) /* Check a single specified location. */ return el2_probe1(dev, base_addr); else if (base_addr != 0) /* Don't probe at all. */ @@ -319,6 +319,7 @@ out: static int el2_open(struct net_device *dev) { + int retval = -EAGAIN; if (dev->irq < 2) { int irqlist[] = {5, 9, 3, 4, 0}; @@ -331,18 +332,19 @@ el2_open(struct net_device *dev) unsigned long cookie = probe_irq_on(); outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR); outb_p(0x00, E33G_IDCFR); - if (*irqp == probe_irq_off(cookie) /* It's a good IRQ line! */ - && request_irq (dev->irq = *irqp, ei_interrupt, 0, ei_status.name, dev) == 0) + if (*irqp == probe_irq_off(cookie) /* It's a good IRQ line! */ + && ((retval = request_irq(dev->irq = *irqp, + ei_interrupt, 0, dev->name, dev)) == 0)) break; } } while (*++irqp); if (*irqp == 0) { outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */ - return -EAGAIN; + return retval; } } else { - if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev)) { - return -EAGAIN; + if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev))) { + return retval; } } diff --git a/drivers/net/3c505.h b/drivers/net/3c505.h index 1cbf41fae9e1..23714facc7fc 100644 --- a/drivers/net/3c505.h +++ b/drivers/net/3c505.h @@ -284,7 +284,7 @@ typedef struct { /* flags */ unsigned long send_pcb_semaphore; - unsigned int dmaing; + unsigned long dmaing; unsigned long busy; unsigned int rx_active; /* number of receive PCBs */ diff --git a/drivers/net/aironet4500.h b/drivers/net/aironet4500.h index 92f2dd4c144d..6a8956e85a53 100644 --- a/drivers/net/aironet4500.h +++ b/drivers/net/aironet4500.h @@ -1487,7 +1487,7 @@ struct awc_private { volatile int ejected; volatile int bh_running; volatile int bh_active; - volatile int tx_chain_active; + volatile long tx_chain_active; volatile u16 enabled_interrupts; volatile u16 waiting_interrupts; volatile int interrupt_count; diff --git a/drivers/net/arlan.c b/drivers/net/arlan.c index cec860fe0a52..aee2bcbddf0e 100644 --- a/drivers/net/arlan.c +++ b/drivers/net/arlan.c @@ -205,7 +205,7 @@ int arlan_command(struct net_device *dev, int command_p) priv->card_polling_interval = 1; if (arlan_debug & ARLAN_DEBUG_CHAIN_LOCKS) - printk(KERN_DEBUG "arlan_command, %lx lock %x commandByte %x waiting %x incoming %x \n", + printk(KERN_DEBUG "arlan_command, %lx lock %lx commandByte %x waiting %x incoming %x \n", jiffies, priv->command_lock, READSHMB(arlan->commandByte), priv->waiting_command_mask, command_p); diff --git a/drivers/net/arlan.h b/drivers/net/arlan.h index e28982bdb929..0f3d5da3e6aa 100644 --- a/drivers/net/arlan.h +++ b/drivers/net/arlan.h @@ -388,7 +388,7 @@ struct arlan_private { volatile int tx_chain_active; volatile int timer_chain_active; volatile int interrupt_ack_requested; - volatile int command_lock; + volatile long command_lock; volatile int rx_command_needed; volatile int tx_command_needed; volatile int waiting_command_mask; @@ -398,8 +398,8 @@ struct arlan_private { volatile int under_reset; volatile int under_config; volatile int rx_command_given; - volatile int tx_command_given; - volatile int interrupt_processing_active; + volatile long tx_command_given; + volatile long interrupt_processing_active; volatile long long last_tx_time; volatile long long last_rx_time; volatile long long last_rx_int_ack_time; diff --git a/drivers/net/bagetlance.c b/drivers/net/bagetlance.c index 9df9a4b1a6bf..916f2e857bec 100644 --- a/drivers/net/bagetlance.c +++ b/drivers/net/bagetlance.c @@ -224,9 +224,9 @@ struct lance_private { /* copy function */ void *(*memcpy_f)( void *, const void *, size_t ); struct net_device_stats stats; -/* These two must be ints for set_bit() */ - int tx_full; - int lock; +/* These two must be longs for set_bit() */ + long tx_full; + long lock; }; /* I/O register access macros */ diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c index a947845d81e4..2de40294430f 100644 --- a/drivers/net/dgrs.c +++ b/drivers/net/dgrs.c @@ -130,6 +130,14 @@ typedef unsigned int bool; #include "dgrs_asstruct.h" #include "dgrs_bcomm.h" +#if LINUX_VERSION_CODE >= 0x20400 +static struct pci_device_id dgrs_pci_tbl[] __initdata = { + { SE6_PCI_VENDOR_ID, SE6_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, dgrs_pci_tbl); +#endif /* LINUX_VERSION_CODE >= 0x20400 */ + /* * Firmware. Compiled separately for local compilation, * but #included for Linux distribution. @@ -788,9 +796,6 @@ static int dgrs_open( struct net_device *dev ) { netif_start_queue(dev); - - MOD_INC_USE_COUNT; - return (0); } @@ -800,9 +805,6 @@ dgrs_open( struct net_device *dev ) static int dgrs_close( struct net_device *dev ) { netif_stop_queue(dev); - - MOD_DEC_USE_COUNT; - return (0); } @@ -1268,6 +1270,7 @@ dgrs_found_device( priv->devtbl[0] = dev; dev->init = dgrs_probe1; + SET_MODULE_OWNER(dev); ether_setup(dev); priv->next_dev = dgrs_root_dev; dgrs_root_dev = dev; @@ -1303,6 +1306,7 @@ dgrs_found_device( privN->chan = i+1; priv->devtbl[i] = devN; devN->init = dgrs_initclone; + SET_MODULE_OWNER(dev); ether_setup(devN); privN->next_dev = dgrs_root_dev; dgrs_root_dev = devN; diff --git a/drivers/net/dmfe.c b/drivers/net/dmfe.c index c344722000fe..481b28700ff0 100644 --- a/drivers/net/dmfe.c +++ b/drivers/net/dmfe.c @@ -393,6 +393,7 @@ static int __init dmfe_init_one (struct pci_dev *pdev, dev = init_etherdev(NULL, sizeof(*db)); if (dev == NULL) goto err_out; + SET_MODULE_OWNER(dev); /* IO range check */ if (!request_region(pci_iobase, CHK_IO_SIZE(pdev, dev_rev), dev->name)) { @@ -461,12 +462,14 @@ static void __exit dmfe_remove_one (struct pci_dev *pdev) static int dmfe_open(struct net_device *dev) { + int ret; struct dmfe_board_info *db = dev->priv; DMFE_DBUG(0, "dmfe_open", 0); - if (request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev)) - return -EAGAIN; + ret = request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev); + if (ret) + return ret; /* Allocated Tx/Rx descriptor memory */ db->desc_pool_ptr = kmalloc(sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, GFP_KERNEL | GFP_DMA); @@ -511,9 +514,6 @@ static int dmfe_open(struct net_device *dev) /* Initilize DM910X board */ dmfe_init_dm910x(dev); - /* Active System Interface */ - MOD_INC_USE_COUNT; - /* set and active a timer process */ init_timer(&db->timer); db->timer.expires = DMFE_TIMER_WUT; @@ -657,7 +657,7 @@ static int dmfe_stop(struct net_device *dev) DELAY_5US; /* deleted timer */ - del_timer(&db->timer); + del_timer_sync(&db->timer); /* free interrupt */ free_irq(dev->irq, dev); @@ -669,8 +669,6 @@ static int dmfe_stop(struct net_device *dev) kfree(db->desc_pool_ptr); kfree(db->buf_pool_ptr); - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/e2100.c b/drivers/net/e2100.c index 3d632145cb74..680f043eb1c7 100644 --- a/drivers/net/e2100.c +++ b/drivers/net/e2100.c @@ -96,7 +96,7 @@ extern inline void mem_off(short port) #define E21_TX_START_PG E21_RX_STOP_PG /* First page of TX buffer */ int e2100_probe(struct net_device *dev); -int e21_probe1(struct net_device *dev, int ioaddr); +static int e21_probe1(struct net_device *dev, int ioaddr); static int e21_open(struct net_device *dev); static void e21_reset_8390(struct net_device *dev); @@ -122,6 +122,8 @@ int __init e2100_probe(struct net_device *dev) int *port; int base_addr = dev->base_addr; + SET_MODULE_OWNER(dev); + if (base_addr > 0x1ff) /* Check a single specified location. */ return e21_probe1(dev, base_addr); else if (base_addr != 0) /* Don't probe at all. */ @@ -134,14 +136,14 @@ int __init e2100_probe(struct net_device *dev) return -ENODEV; } -int __init e21_probe1(struct net_device *dev, int ioaddr) +static int __init e21_probe1(struct net_device *dev, int ioaddr) { int i, status, retval; unsigned char *station_addr = dev->dev_addr; static unsigned version_printed = 0; - if (!request_region(ioaddr, E21_IO_EXTENT, "e2100")) - return -ENODEV; + if (!request_region(ioaddr, E21_IO_EXTENT, dev->name)) + return -EBUSY; /* First check the station address for the Ctron prefix. */ if (inb(ioaddr + E21_SAPROM + 0) != 0x00 @@ -255,10 +257,10 @@ static int e21_open(struct net_device *dev) { short ioaddr = dev->base_addr; + int retval; - if (request_irq(dev->irq, ei_interrupt, 0, "e2100", dev)) { - return -EBUSY; - } + if ((retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev))) + return retval; /* Set the interrupt line and memory base on the hardware. */ inb(ioaddr + E21_IRQ_LOW); @@ -270,7 +272,6 @@ e21_open(struct net_device *dev) outb(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7)); ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -369,8 +370,6 @@ e21_close(struct net_device *dev) really bad things happen if it isn't. */ mem_off(ioaddr); - MOD_DEC_USE_COUNT; - return 0; } diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index cff64211b0c6..6943b7f873cc 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -533,8 +533,9 @@ static unsigned eeprom_reg = EEPROM_REG_PRO; int __init eepro_probe(struct net_device *dev) { int i; - int base_addr = dev ? dev->base_addr : 0; + int base_addr = dev->base_addr; + SET_MODULE_OWNER(dev); #ifdef PnPWakeup /* XXXX for multiple cards should this only be run once? */ @@ -643,7 +644,7 @@ void printEEPROMInfo(short ioaddr, struct net_device *dev) probes on the ISA bus. A good device probe avoids doing writes, and verifies that the correct device exists and functions. */ -int eepro_probe1(struct net_device *dev, short ioaddr) +static int eepro_probe1(struct net_device *dev, short ioaddr) { unsigned short station_addr[6], id, counter; int i,j, irqMask; @@ -1078,7 +1079,6 @@ static int eepro_open(struct net_device *dev) /* enabling rx */ eepro_en_rx(ioaddr); - MOD_INC_USE_COUNT; return 0; } @@ -1234,7 +1234,6 @@ static int eepro_close(struct net_device *dev) /* Update the statistics here. What statistics? */ - MOD_DEC_USE_COUNT; return 0; } @@ -1732,7 +1731,7 @@ eepro_transmit_interrupt(struct net_device *dev) static struct net_device dev_eepro[MAX_EEPRO]; static int io[MAX_EEPRO]; -static int irq[MAX_EEPRO] = { [0 ... MAX_EEPRO-1] = 0 }; +static int irq[MAX_EEPRO]; static int mem[MAX_EEPRO] = { /* Size of the rx buffer in KB */ [0 ... MAX_EEPRO-1] = RCV_DEFAULT_RAM/1024 }; diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c index 5f1f697bc549..102704b0cd87 100644 --- a/drivers/net/eexpress.c +++ b/drivers/net/eexpress.c @@ -344,6 +344,8 @@ int __init express_probe(struct net_device *dev) static unsigned short ports[] = { 0x300,0x310,0x270,0x320,0x340,0 }; unsigned short ioaddr = dev->base_addr; + SET_MODULE_OWNER(dev); + dev->if_port = 0xff; /* not set */ #ifdef CONFIG_MCA @@ -420,7 +422,7 @@ int __init express_probe(struct net_device *dev) static int eexp_open(struct net_device *dev) { - int irq = dev->irq; + int ret; unsigned short ioaddr = dev->base_addr; struct net_local *lp = (struct net_local *)dev->priv; @@ -428,11 +430,11 @@ static int eexp_open(struct net_device *dev) printk(KERN_DEBUG "%s: eexp_open()\n", dev->name); #endif - if (!irq || !irqrmap[irq]) + if (!dev->irq || !irqrmap[dev->irq]) return -ENXIO; - if (request_irq(irq,&eexp_irq,0,"EtherExpress",dev)) - return -EAGAIN; + ret = request_irq(dev->irq,&eexp_irq,0,dev->name,dev); + if (ret) return ret; request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress"); request_region(ioaddr+0x4000, 16, "EtherExpress shadow"); @@ -445,7 +447,6 @@ static int eexp_open(struct net_device *dev) } eexp_hw_init586(dev); - MOD_INC_USE_COUNT; netif_start_queue(dev); #if NET_DEBUG > 6 printk(KERN_DEBUG "%s: leaving eexp_open()\n", dev->name); @@ -477,7 +478,6 @@ static int eexp_close(struct net_device *dev) release_region(ioaddr+0x8000, 16); release_region(ioaddr+0xc000, 16); - MOD_DEC_USE_COUNT; return 0; } @@ -1625,14 +1625,9 @@ eexp_set_multicast(struct net_device *dev) #define EEXP_MAX_CARDS 4 /* max number of cards to support */ -static struct net_device dev_eexp[EEXP_MAX_CARDS] = -{ - { "", - 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, express_probe }, -}; - -static int irq[EEXP_MAX_CARDS] = {0, }; -static int io[EEXP_MAX_CARDS] = {0, }; +static struct net_device dev_eexp[EEXP_MAX_CARDS]; +static int irq[EEXP_MAX_CARDS]; +static int io[EEXP_MAX_CARDS]; MODULE_PARM(io, "1-" __MODULE_STRING(EEXP_MAX_CARDS) "i"); MODULE_PARM(irq, "1-" __MODULE_STRING(EEXP_MAX_CARDS) "i"); @@ -1649,6 +1644,7 @@ int init_module(void) struct net_device *dev = &dev_eexp[this_dev]; dev->irq = irq[this_dev]; dev->base_addr = io[this_dev]; + dev->init = express_probe; if (io[this_dev] == 0) { if (this_dev) break; printk(KERN_NOTICE "eexpress.c: Module autoprobe not recommended, give io=xx.\n"); diff --git a/drivers/net/es3210.c b/drivers/net/es3210.c index 86a09b4d4834..0c4c241ac4d4 100644 --- a/drivers/net/es3210.c +++ b/drivers/net/es3210.c @@ -62,7 +62,7 @@ static const char *version = #include "8390.h" int es_probe(struct net_device *dev); -int es_probe1(struct net_device *dev, int ioaddr); +static int es_probe1(struct net_device *dev, int ioaddr); static int es_open(struct net_device *dev); static int es_close(struct net_device *dev); @@ -128,6 +128,8 @@ int __init es_probe(struct net_device *dev) { unsigned short ioaddr = dev->base_addr; + SET_MODULE_OWNER(dev); + if (ioaddr > 0x1ff) /* Check a single specified location. */ return es_probe1(dev, ioaddr); else if (ioaddr > 0) /* Don't probe at all. */ @@ -148,7 +150,7 @@ int __init es_probe(struct net_device *dev) return -ENODEV; } -int __init es_probe1(struct net_device *dev, int ioaddr) +static int __init es_probe1(struct net_device *dev, int ioaddr) { int i, retval; unsigned long eisa_id; @@ -357,9 +359,6 @@ static void es_block_output(struct net_device *dev, int count, static int es_open(struct net_device *dev) { ei_open(dev); - - MOD_INC_USE_COUNT; - return 0; } @@ -370,24 +369,13 @@ static int es_close(struct net_device *dev) printk("%s: Shutting down ethercard.\n", dev->name); ei_close(dev); - - MOD_DEC_USE_COUNT; - return 0; } #ifdef MODULE #define MAX_ES_CARDS 4 /* Max number of ES3210 cards per module */ #define NAMELEN 8 /* # of chars for storing dev->name */ -static struct net_device dev_es3210[MAX_ES_CARDS] = { - { - "", /* device name is inserted by net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, NULL - }, -}; - +static struct net_device dev_es3210[MAX_ES_CARDS]; static int io[MAX_ES_CARDS]; static int irq[MAX_ES_CARDS]; static int mem[MAX_ES_CARDS]; diff --git a/drivers/net/hplance.c b/drivers/net/hplance.c index 63fdd734bccc..d80e5c9c0449 100644 --- a/drivers/net/hplance.c +++ b/drivers/net/hplance.c @@ -116,6 +116,7 @@ static int __init hplance_init(struct net_device *dev, int scode) return -ENOMEM; memset(dev->priv, 0, sizeof(struct hplance_private)); #endif + SET_MODULE_OWNER(dev); printk("%s: HP LANCE; select code %d, addr", dev->name, scode); @@ -212,7 +213,6 @@ static int hplance_open(struct net_device *dev) /* enable interrupts at board level. */ writeb(LE_IE, &(hpregs->status)); - MOD_INC_USE_COUNT; return 0; } @@ -222,7 +222,6 @@ static int hplance_close(struct net_device *dev) struct hplance_reg *hpregs = (struct hplance_reg *)lp->base; writeb(0,&(hpregs->status)); /* disable interrupts at boardlevel */ lance_close(dev); - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/lance.c b/drivers/net/lance.c index 6a67457046b4..4351442911fc 100644 --- a/drivers/net/lance.c +++ b/drivers/net/lance.c @@ -37,7 +37,6 @@ static const char *version = "lance.c:v1.15ac 1999/11/13 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n"; -#include #include #include #include diff --git a/drivers/net/lasi_82596.c b/drivers/net/lasi_82596.c new file mode 100644 index 000000000000..049ebccb78a4 --- /dev/null +++ b/drivers/net/lasi_82596.c @@ -0,0 +1,1550 @@ +/* lasi_82596.c -- driver for the intel 82596 ethernet controller, as + munged into HPPA boxen . + + This driver is based upon 82596.c, original credits are below... + but there were too many hoops which HP wants jumped through to + keep this code in there in a sane manner. + + 3 primary sources of the mess -- + 1) hppa needs *lots* of cacheline flushing to keep this kind of + MMIO running. + + 2) The 82596 needs to see all of its pointers as their physical + address. Thus virt_to_bus/bus_to_virt are *everywhere*. + + 3) The implementation HP is using seems to be significantly pickier + about when and how the command and RX units are started. some + command ordering was changed. + + Examination of the mach driver leads one to believe that there + might be a saner way to pull this off... anyone who feels like a + full rewrite can be my guest. + + Split 02/13/2000 Sam Creasey (sammy@oh.verio.com) + + 02/01/2000 Initial modifications for parisc by Helge Deller (deller@gmx.de) + 03/02/2000 changes for better/correct(?) cache-flushing (deller) +*/ + +/* 82596.c: A generic 82596 ethernet driver for linux. */ +/* + Based on Apricot.c + Written 1994 by Mark Evans. + This driver is for the Apricot 82596 bus-master interface + + Modularised 12/94 Mark Evans + + + Modified to support the 82596 ethernet chips on 680x0 VME boards. + by Richard Hirst + Renamed to be 82596.c + + 980825: Changed to receive directly in to sk_buffs which are + allocated at open() time. Eliminates copy on incoming frames + (small ones are still copied). Shared data now held in a + non-cached page, so we can run on 68060 in copyback mode. + + TBD: + * look at deferring rx frames rather than discarding (as per tulip) + * handle tx ring full as per tulip + * performace test to tune rx_copybreak + + Most of my modifications relate to the braindead big-endian + implementation by Intel. When the i596 is operating in + 'big-endian' mode, it thinks a 32 bit value of 0x12345678 + should be stored as 0x56781234. This is a real pain, when + you have linked lists which are shared by the 680x0 and the + i596. + + Driver skeleton + Written 1993 by Donald Becker. + Copyright 1993 United States Government as represented by the Director, + National Security Agency. This software may only be used and distributed + according to the terms of the GNU Public License as modified by SRC, + incorporated herein by reference. + + The author may be reached as becker@super.org or + C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 + + */ + +static const char *version = "82596.c $Revision: 1.14 $\n"; + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* DEBUG flags + */ + +#define DEB_INIT 0x0001 +#define DEB_PROBE 0x0002 +#define DEB_SERIOUS 0x0004 +#define DEB_ERRORS 0x0008 +#define DEB_MULTI 0x0010 +#define DEB_TDR 0x0020 +#define DEB_OPEN 0x0040 +#define DEB_RESET 0x0080 +#define DEB_ADDCMD 0x0100 +#define DEB_STATUS 0x0200 +#define DEB_STARTTX 0x0400 +#define DEB_RXADDR 0x0800 +#define DEB_TXADDR 0x1000 +#define DEB_RXFRAME 0x2000 +#define DEB_INTS 0x4000 +#define DEB_STRUCT 0x8000 +#define DEB_ANY 0xffff + + +#define DEB(x,y) if (i596_debug & (x)) y + + +#define CHECK_WBACK(addr,len) \ + do { if (!dma_consistent) dma_cache_wback((unsigned long)addr,len); } while (0) + +#define CHECK_INV(addr,len) \ + do { if (!dma_consistent) dma_cache_inv((unsigned long)addr,len); } while(0) + +#define CHECK_WBACK_INV(addr,len) \ + do { if (!dma_consistent) dma_cache_wback_inv((unsigned long)addr,len); } while (0) + + +#define PA_I82596_RESET 0 /* Offsets relative to LASI-LAN-Addr.*/ +#define PA_CPU_PORT_L_ACCESS 4 +#define PA_CHANNEL_ATTENTION 8 + + +/* + * Define various macros for Channel Attention, word swapping etc., dependent + * on architecture. MVME and BVME are 680x0 based, otherwise it is Intel. + */ + +#ifdef __BIG_ENDIAN +#define WSWAPrfd(x) ((struct i596_rfd *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPrbd(x) ((struct i596_rbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPiscp(x) ((struct i596_iscp *)(((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPscb(x) ((struct i596_scb *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPcmd(x) ((struct i596_cmd *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPtbd(x) ((struct i596_tbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define WSWAPchar(x) ((char *) (((u32)(x)<<16) | ((((u32)(x)))>>16))) +#define ISCP_BUSY 0x00010000 +#define MACH_IS_APRICOT 0 +#else +#define WSWAPrfd(x) ((struct i596_rfd *)(x)) +#define WSWAPrbd(x) ((struct i596_rbd *)(x)) +#define WSWAPiscp(x) ((struct i596_iscp *)(x)) +#define WSWAPscb(x) ((struct i596_scb *)(x)) +#define WSWAPcmd(x) ((struct i596_cmd *)(x)) +#define WSWAPtbd(x) ((struct i596_tbd *)(x)) +#define WSWAPchar(x) ((char *)(x)) +#define ISCP_BUSY 0x0001 +#define MACH_IS_APRICOT 1 +#endif + +/* + * The MPU_PORT command allows direct access to the 82596. With PORT access + * the following commands are available (p5-18). The 32-bit port command + * must be word-swapped with the most significant word written first. + * This only applies to VME boards. + */ +#define PORT_RESET 0x00 /* reset 82596 */ +#define PORT_SELFTEST 0x01 /* selftest */ +#define PORT_ALTSCP 0x02 /* alternate SCB address */ +#define PORT_ALTDUMP 0x03 /* Alternate DUMP address */ + +static int i596_debug = (DEB_SERIOUS|DEB_PROBE); + +MODULE_AUTHOR("Richard Hirst"); +MODULE_DESCRIPTION("i82596 driver"); +MODULE_PARM(i596_debug, "i"); + + +/* Copy frames shorter than rx_copybreak, otherwise pass on up in + * a full sized sk_buff. Value of 100 stolen from tulip.c (!alpha). + */ +static int rx_copybreak = 100; + +#define PKT_BUF_SZ 1536 +#define MAX_MC_CNT 64 + +#define I596_TOTAL_SIZE 17 + +#define I596_NULL ((void *)0xffffffff) + +#define CMD_EOL 0x8000 /* The last command of the list, stop. */ +#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ +#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ + +#define CMD_FLEX 0x0008 /* Enable flexible memory model */ + +enum commands { + CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, + CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7 +}; + +#define STAT_C 0x8000 /* Set to 0 after execution */ +#define STAT_B 0x4000 /* Command being executed */ +#define STAT_OK 0x2000 /* Command executed ok */ +#define STAT_A 0x1000 /* Command aborted */ + +#define CUC_START 0x0100 +#define CUC_RESUME 0x0200 +#define CUC_SUSPEND 0x0300 +#define CUC_ABORT 0x0400 +#define RX_START 0x0010 +#define RX_RESUME 0x0020 +#define RX_SUSPEND 0x0030 +#define RX_ABORT 0x0040 + +#define TX_TIMEOUT 5 + +#define OPT_SWAP_PORT 0x0001 /* Need to wordswp on the MPU port */ + + +struct i596_reg { + unsigned short porthi; + unsigned short portlo; + unsigned long ca; +}; + +#define EOF 0x8000 +#define SIZE_MASK 0x3fff + +struct i596_tbd { + unsigned short size; + unsigned short pad; + struct i596_tbd *next; + char *data; + long cache_pad[5]; /* Total 32 bytes... */ +}; + +/* The command structure has two 'next' pointers; v_next is the address of + * the next command as seen by the CPU, b_next is the address of the next + * command as seen by the 82596. The b_next pointer, as used by the 82596 + * always references the status field of the next command, rather than the + * v_next field, because the 82596 is unaware of v_next. It may seem more + * logical to put v_next at the end of the structure, but we cannot do that + * because the 82596 expects other fields to be there, depending on command + * type. + */ + +struct i596_cmd { + struct i596_cmd *v_next; /* Address from CPUs viewpoint */ + unsigned short status; + unsigned short command; + struct i596_cmd *b_next; /* Address from i596 viewpoint */ +}; + +struct tx_cmd { + struct i596_cmd cmd; + struct i596_tbd *tbd; + unsigned short size; + unsigned short pad; + struct sk_buff *skb; /* So we can free it after tx */ + dma_addr_t dma_addr; + long cache_pad[1]; /* Total 32 bytes... */ +}; + +struct tdr_cmd { + struct i596_cmd cmd; + unsigned short status; + unsigned short pad; +}; + +struct mc_cmd { + struct i596_cmd cmd; + short mc_cnt; + char mc_addrs[MAX_MC_CNT*6]; +}; + +struct sa_cmd { + struct i596_cmd cmd; + char eth_addr[8]; +}; + +struct cf_cmd { + struct i596_cmd cmd; + char i596_config[16]; +}; + +struct i596_rfd { + unsigned short stat; + unsigned short cmd; + struct i596_rfd *b_next; /* Address from i596 viewpoint */ + struct i596_rbd *rbd; + unsigned short count; + unsigned short size; + struct i596_rfd *v_next; /* Address from CPUs viewpoint */ + struct i596_rfd *v_prev; + long cache_pad[2]; /* Total 32 bytes... */ +}; + +struct i596_rbd { + unsigned short count; + unsigned short zero1; + struct i596_rbd *b_next; + unsigned char *b_data; /* Address from i596 viewpoint */ + unsigned short size; + unsigned short zero2; + struct sk_buff *skb; + struct i596_rbd *v_next; + struct i596_rbd *b_addr; /* This rbd addr from i596 view */ + unsigned char *v_data; /* Address from CPUs viewpoint */ + /* Total 32 bytes... */ +}; + +/* These values as chosen so struct i596_private fits in one page... */ + +#define TX_RING_SIZE 32 +#define RX_RING_SIZE 16 + +struct i596_scb { + unsigned short status; + unsigned short command; + struct i596_cmd *cmd; + struct i596_rfd *rfd; + unsigned long crc_err; + unsigned long align_err; + unsigned long resource_err; + unsigned long over_err; + unsigned long rcvdt_err; + unsigned long short_err; + unsigned short t_on; + unsigned short t_off; +}; + +struct i596_iscp { + unsigned long stat; + struct i596_scb *scb; +}; + +struct i596_scp { + unsigned long sysbus; + unsigned long pad; + struct i596_iscp *iscp; +}; + +struct i596_private { + volatile struct i596_scp scp __attribute__((aligned(32))); + volatile struct i596_iscp iscp __attribute__((aligned(32))); + volatile struct i596_scb scb __attribute__((aligned(32))); + struct sa_cmd sa_cmd __attribute__((aligned(32))); + struct cf_cmd cf_cmd __attribute__((aligned(32))); + struct tdr_cmd tdr_cmd __attribute__((aligned(32))); + struct mc_cmd mc_cmd __attribute__((aligned(32))); + struct i596_rfd rfds[RX_RING_SIZE] __attribute__((aligned(32))); + struct i596_rbd rbds[RX_RING_SIZE] __attribute__((aligned(32))); + struct tx_cmd tx_cmds[TX_RING_SIZE] __attribute__((aligned(32))); + struct i596_tbd tbds[TX_RING_SIZE] __attribute__((aligned(32))); + unsigned long stat; + int last_restart; + struct i596_rfd *rfd_head; + struct i596_rbd *rbd_head; + struct i596_cmd *cmd_tail; + struct i596_cmd *cmd_head; + int cmd_backlog; + unsigned long last_cmd; + struct net_device_stats stats; + int next_tx_cmd; + int options; + spinlock_t lock; + dma_addr_t dma_addr; +}; + +char init_setup[] = +{ + 0x8E, /* length, prefetch on */ + 0xC8, /* fifo to 8, monitor off */ + 0x80, /* don't save bad frames */ + 0x2E, /* No source address insertion, 8 byte preamble */ + 0x00, /* priority and backoff defaults */ + 0x60, /* interframe spacing */ + 0x00, /* slot time LSB */ + 0xf2, /* slot time and retries */ + 0x00, /* promiscuous mode */ + 0x00, /* collision detect */ + 0x40, /* minimum frame length */ + 0xff, + 0x00, + 0x7f /* *multi IA */ }; + +static int dma_consistent = 1; /* Zero if pci_alloc_consistent() fails */ + +static int i596_open(struct net_device *dev); +static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void i596_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int i596_close(struct net_device *dev); +static struct net_device_stats *i596_get_stats(struct net_device *dev); +static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd); +static void i596_tx_timeout (struct net_device *dev); +static void print_eth(unsigned char *buf, char *str); +static void set_multicast_list(struct net_device *dev); + +static int rx_ring_size = RX_RING_SIZE; +static int ticks_limit = 100; +static int max_cmd_backlog = TX_RING_SIZE-1; + + +static inline void CA(struct net_device *dev) +{ + gsc_writel(0, (void*)(dev->base_addr + PA_CHANNEL_ATTENTION)); +} + + +static inline void MPU_PORT(struct net_device *dev, int c, volatile void *x) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + + u32 v = (u32) (c) | (u32) (x); + + if (lp->options & OPT_SWAP_PORT) + v = ((u32) (v) << 16) | ((u32) (v) >> 16); + + gsc_writel(v & 0xffff, (void*)(dev->base_addr + PA_CPU_PORT_L_ACCESS)); + udelay(1); + gsc_writel(v >> 16, (void*)(dev->base_addr + PA_CPU_PORT_L_ACCESS)); +} + + +static inline int wait_istat(struct net_device *dev, struct i596_private *lp, int delcnt, char *str) +{ + CHECK_INV(&(lp->iscp), sizeof(struct i596_iscp)); + while (--delcnt && lp->iscp.stat) { + udelay(10); + CHECK_INV(&(lp->iscp), sizeof(struct i596_iscp)); + } + if (!delcnt) { + printk("%s: %s, iscp.stat %04lx, didn't clear\n", + dev->name, str, lp->iscp.stat); + return -1; + } + else + return 0; +} + + +static inline int wait_cmd(struct net_device *dev, struct i596_private *lp, int delcnt, char *str) +{ + CHECK_INV(&(lp->scb), sizeof(struct i596_scb)); + while (--delcnt && lp->scb.command) { + udelay(10); + CHECK_INV(&(lp->scb), sizeof(struct i596_scb)); + } + if (!delcnt) { + printk("%s: %s, status %4.4x, cmd %4.4x.\n", + dev->name, str, lp->scb.status, lp->scb.command); + return -1; + } + else + return 0; +} + + +static void i596_display_data(struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + struct i596_cmd *cmd; + struct i596_rfd *rfd; + struct i596_rbd *rbd; + + printk("lp and scp at %p, .sysbus = %08lx, .iscp = %p\n", + &lp->scp, lp->scp.sysbus, lp->scp.iscp); + printk("iscp at %p, iscp.stat = %08lx, .scb = %p\n", + &lp->iscp, lp->iscp.stat, lp->iscp.scb); + printk("scb at %p, scb.status = %04x, .command = %04x," + " .cmd = %p, .rfd = %p\n", + &lp->scb, lp->scb.status, lp->scb.command, + lp->scb.cmd, lp->scb.rfd); + printk(" errors: crc %lx, align %lx, resource %lx," + " over %lx, rcvdt %lx, short %lx\n", + lp->scb.crc_err, lp->scb.align_err, lp->scb.resource_err, + lp->scb.over_err, lp->scb.rcvdt_err, lp->scb.short_err); + cmd = lp->cmd_head; + while (cmd != I596_NULL) { + printk("cmd at %p, .status = %04x, .command = %04x, .b_next = %p\n", + cmd, cmd->status, cmd->command, cmd->b_next); + cmd = cmd->v_next; + } + rfd = lp->rfd_head; + printk("rfd_head = %p\n", rfd); + do { + printk (" %p .stat %04x, .cmd %04x, b_next %p, rbd %p," + " count %04x\n", + rfd, rfd->stat, rfd->cmd, rfd->b_next, rfd->rbd, + rfd->count); + rfd = rfd->v_next; + } while (rfd != lp->rfd_head); + rbd = lp->rbd_head; + printk("rbd_head = %p\n", rbd); + do { + printk(" %p .count %04x, b_next %p, b_data %p, size %04x\n", + rbd, rbd->count, rbd->b_next, rbd->b_data, rbd->size); + rbd = rbd->v_next; + } while (rbd != lp->rbd_head); + CHECK_INV(lp, sizeof(struct i596_private)); +} + + +#if defined(ENABLE_MVME16x_NET) || defined(ENABLE_BVME6000_NET) +static void i596_error(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000; + + pcc2[0x28] = 1; + pcc2[0x2b] = 0x1d; + printk("%s: Error interrupt\n", dev->name); + i596_display_data(dev); +} +#endif + +#define virt_to_dma(lp,v) ((char *)(v)-(char *)(lp)+(char *)((lp)->dma_addr)) + +static inline void init_rx_bufs(struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *)dev->priv; + int i; + struct i596_rfd *rfd; + struct i596_rbd *rbd; + + /* First build the Receive Buffer Descriptor List */ + + for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) { + dma_addr_t dma_addr; + struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ + 4); + + if (skb == NULL) + panic("82596: alloc_skb() failed"); + skb_reserve(skb, 2); + dma_addr = pci_map_single(NULL, skb->tail,PKT_BUF_SZ, + PCI_DMA_FROMDEVICE); + skb->dev = dev; + rbd->v_next = rbd+1; + rbd->b_next = WSWAPrbd(virt_to_dma(lp,rbd+1)); + rbd->b_addr = WSWAPrbd(virt_to_dma(lp,rbd)); + rbd->skb = skb; + rbd->v_data = skb->tail; + rbd->b_data = WSWAPchar(dma_addr); + rbd->size = PKT_BUF_SZ; + } + lp->rbd_head = lp->rbds; + rbd = lp->rbds + rx_ring_size - 1; + rbd->v_next = lp->rbds; + rbd->b_next = WSWAPrbd(virt_to_dma(lp,lp->rbds)); + + /* Now build the Receive Frame Descriptor List */ + + for (i = 0, rfd = lp->rfds; i < rx_ring_size; i++, rfd++) { + rfd->rbd = I596_NULL; + rfd->v_next = rfd+1; + rfd->v_prev = rfd-1; + rfd->b_next = WSWAPrfd(virt_to_dma(lp,rfd+1)); + rfd->cmd = CMD_FLEX; + } + lp->rfd_head = lp->rfds; + lp->scb.rfd = WSWAPrfd(virt_to_dma(lp,lp->rfds)); + rfd = lp->rfds; + rfd->rbd = lp->rbd_head; + rfd->v_prev = lp->rfds + rx_ring_size - 1; + rfd = lp->rfds + rx_ring_size - 1; + rfd->v_next = lp->rfds; + rfd->b_next = WSWAPrfd(virt_to_dma(lp,lp->rfds)); + rfd->cmd = CMD_EOL|CMD_FLEX; + + CHECK_WBACK_INV(lp, sizeof(struct i596_private)); +} + +static inline void remove_rx_bufs(struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *)dev->priv; + struct i596_rbd *rbd; + int i; + + for (i = 0, rbd = lp->rbds; i < rx_ring_size; i++, rbd++) { + if (rbd->skb == NULL) + break; + pci_unmap_single(NULL,(dma_addr_t)WSWAPchar(rbd->b_data), PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + dev_kfree_skb(rbd->skb); + } +} + + +static void rebuild_rx_bufs(struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + int i; + + /* Ensure rx frame/buffer descriptors are tidy */ + + for (i = 0; i < rx_ring_size; i++) { + lp->rfds[i].rbd = I596_NULL; + lp->rfds[i].cmd = CMD_FLEX; + } + lp->rfds[rx_ring_size-1].cmd = CMD_EOL|CMD_FLEX; + lp->rfd_head = lp->rfds; + lp->scb.rfd = WSWAPrfd(virt_to_dma(lp,lp->rfds)); + lp->rbd_head = lp->rbds; + lp->rfds[0].rbd = WSWAPrbd(virt_to_dma(lp,lp->rbds)); + + CHECK_WBACK_INV(lp, sizeof(struct i596_private)); +} + + +static int init_i596_mem(struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + unsigned long flags; + + disable_irq(dev->irq); /* disable IRQs from LAN */ + DEB(DEB_INIT, + printk("RESET 82596 port: %08lX (with IRQ%d disabled)\n", + dev->base_addr + PA_I82596_RESET, + dev->irq)); + + gsc_writel(0, (void*)(dev->base_addr + PA_I82596_RESET)); /* Hard Reset */ + udelay(100); /* Wait 100us - seems to help */ + + /* change the scp address */ + + lp->last_cmd = jiffies; + + + lp->scp.sysbus = 0x0000006c; + lp->scp.iscp = WSWAPiscp(virt_to_dma(lp,&(lp->iscp))); + lp->iscp.scb = WSWAPscb(virt_to_dma(lp,&(lp->scb))); + lp->iscp.stat = ISCP_BUSY; + lp->cmd_backlog = 0; + + lp->cmd_head = lp->scb.cmd = I596_NULL; + + DEB(DEB_INIT,printk("%s: starting i82596.\n", dev->name)); + + CHECK_WBACK(&(lp->scp), sizeof(struct i596_scp)); + CHECK_WBACK(&(lp->iscp), sizeof(struct i596_iscp)); + + MPU_PORT(dev, PORT_ALTSCP, (void *)virt_to_dma(lp,&lp->scp)); + + CA(dev); + + if (wait_istat(dev,lp,1000,"initialization timed out")) + goto failed; + DEB(DEB_INIT,printk("%s: i82596 initialization successful\n", dev->name)); + + /* Ensure rx frame/buffer descriptors are tidy */ + rebuild_rx_bufs(dev); + + lp->scb.command = 0; + CHECK_WBACK(&(lp->scb), sizeof(struct i596_scb)); + + enable_irq(dev->irq); /* enable IRQs from LAN */ + + DEB(DEB_INIT,printk("%s: queuing CmdConfigure\n", dev->name)); + memcpy(lp->cf_cmd.i596_config, init_setup, 14); + lp->cf_cmd.cmd.command = CmdConfigure; + CHECK_WBACK(&(lp->cf_cmd), sizeof(struct cf_cmd)); + i596_add_cmd(dev, &lp->cf_cmd.cmd); + + DEB(DEB_INIT,printk("%s: queuing CmdSASetup\n", dev->name)); + memcpy(lp->sa_cmd.eth_addr, dev->dev_addr, 6); + lp->sa_cmd.cmd.command = CmdSASetup; + CHECK_WBACK(&(lp->sa_cmd), sizeof(struct sa_cmd)); + i596_add_cmd(dev, &lp->sa_cmd.cmd); + + DEB(DEB_INIT,printk("%s: queuing CmdTDR\n", dev->name)); + lp->tdr_cmd.cmd.command = CmdTDR; + CHECK_WBACK(&(lp->tdr_cmd), sizeof(struct tdr_cmd)); + i596_add_cmd(dev, &lp->tdr_cmd.cmd); + + spin_lock_irqsave (&lp->lock, flags); + + if (wait_cmd(dev,lp,1000,"timed out waiting to issue RX_START")) { + spin_unlock_irqrestore (&lp->lock, flags); + goto failed; + } + DEB(DEB_INIT,printk("%s: Issuing RX_START\n", dev->name)); + lp->scb.command = RX_START; + lp->scb.rfd = WSWAPrfd(virt_to_dma(lp,lp->rfds)); + CHECK_WBACK(&(lp->scb), sizeof(struct i596_scb)); + + CA(dev); + + spin_unlock_irqrestore (&lp->lock, flags); + + if (wait_cmd(dev,lp,1000,"RX_START not processed")) + goto failed; + DEB(DEB_INIT,printk("%s: Receive unit started OK\n", dev->name)); + + return 0; + +failed: + printk("%s: Failed to initialise 82596\n", dev->name); + MPU_PORT(dev, PORT_RESET, 0); + return -1; +} + + +static inline int i596_rx(struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *)dev->priv; + struct i596_rfd *rfd; + struct i596_rbd *rbd; + int frames = 0; + + DEB(DEB_RXFRAME,printk ("i596_rx(), rfd_head %p, rbd_head %p\n", + lp->rfd_head, lp->rbd_head)); + + + rfd = lp->rfd_head; /* Ref next frame to check */ + + CHECK_INV(rfd, sizeof(struct i596_rfd)); + while ((rfd->stat) & STAT_C) { /* Loop while complete frames */ + if (rfd->rbd == I596_NULL) + rbd = I596_NULL; + else if (rfd->rbd == lp->rbd_head->b_addr) { + rbd = lp->rbd_head; + CHECK_INV(rbd, sizeof(struct i596_rbd)); + } + else { + printk("%s: rbd chain broken!\n", dev->name); + /* XXX Now what? */ + rbd = I596_NULL; + } + DEB(DEB_RXFRAME, printk(" rfd %p, rfd.rbd %p, rfd.stat %04x\n", + rfd, rfd->rbd, rfd->stat)); + + if (rbd != I596_NULL && ((rfd->stat) & STAT_OK)) { + /* a good frame */ + int pkt_len = rbd->count & 0x3fff; + struct sk_buff *skb = rbd->skb; + int rx_in_place = 0; + + DEB(DEB_RXADDR,print_eth(rbd->v_data, "received")); + frames++; + + /* Check if the packet is long enough to just accept + * without copying to a properly sized skbuff. + */ + + if (pkt_len > rx_copybreak) { + struct sk_buff *newskb; + dma_addr_t dma_addr; + + pci_unmap_single(NULL,(dma_addr_t)WSWAPchar(rbd->b_data), PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + /* Get fresh skbuff to replace filled one. */ + newskb = dev_alloc_skb(PKT_BUF_SZ + 4); + if (newskb == NULL) { + skb = NULL; /* drop pkt */ + goto memory_squeeze; + } + skb_reserve(newskb, 2); + + /* Pass up the skb already on the Rx ring. */ + skb_put(skb, pkt_len); + rx_in_place = 1; + rbd->skb = newskb; + newskb->dev = dev; + dma_addr = pci_map_single(NULL, newskb->tail, PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + rbd->v_data = newskb->tail; + rbd->b_data = WSWAPchar(dma_addr); + CHECK_WBACK_INV(rbd, sizeof(struct i596_rbd)); + } + else + skb = dev_alloc_skb(pkt_len + 2); +memory_squeeze: + if (skb == NULL) { + /* XXX tulip.c can defer packets here!! */ + printk ("%s: i596_rx Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + } + else { + skb->dev = dev; + if (!rx_in_place) { + /* 16 byte align the data fields */ + pci_dma_sync_single(NULL, (dma_addr_t)WSWAPchar(rbd->b_data), PKT_BUF_SZ, PCI_DMA_FROMDEVICE); + skb_reserve(skb, 2); + memcpy(skb_put(skb,pkt_len), rbd->v_data, pkt_len); + } + skb->len = pkt_len; + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + lp->stats.rx_packets++; + lp->stats.rx_bytes+=pkt_len; + } + } + else { + DEB(DEB_ERRORS, printk("%s: Error, rfd.stat = 0x%04x\n", + dev->name, rfd->stat)); + lp->stats.rx_errors++; + if ((rfd->stat) & 0x0001) + lp->stats.collisions++; + if ((rfd->stat) & 0x0080) + lp->stats.rx_length_errors++; + if ((rfd->stat) & 0x0100) + lp->stats.rx_over_errors++; + if ((rfd->stat) & 0x0200) + lp->stats.rx_fifo_errors++; + if ((rfd->stat) & 0x0400) + lp->stats.rx_frame_errors++; + if ((rfd->stat) & 0x0800) + lp->stats.rx_crc_errors++; + if ((rfd->stat) & 0x1000) + lp->stats.rx_length_errors++; + } + + /* Clear the buffer descriptor count and EOF + F flags */ + + if (rbd != I596_NULL && (rbd->count & 0x4000)) { + rbd->count = 0; + lp->rbd_head = rbd->v_next; + CHECK_WBACK_INV(rbd, sizeof(struct i596_rbd)); + } + + /* Tidy the frame descriptor, marking it as end of list */ + + rfd->rbd = I596_NULL; + rfd->stat = 0; + rfd->cmd = CMD_EOL|CMD_FLEX; + rfd->count = 0; + + /* Remove end-of-list from old end descriptor */ + + rfd->v_prev->cmd = CMD_FLEX; + + /* Update record of next frame descriptor to process */ + + lp->scb.rfd = rfd->b_next; + lp->rfd_head = rfd->v_next; + CHECK_WBACK_INV(rfd->v_prev, sizeof(struct i596_rfd)); + CHECK_WBACK_INV(rfd, sizeof(struct i596_rfd)); + rfd = lp->rfd_head; + CHECK_INV(rfd, sizeof(struct i596_rfd)); + } + + DEB(DEB_RXFRAME,printk ("frames %d\n", frames)); + + return 0; +} + + +static inline void i596_cleanup_cmd(struct net_device *dev, struct i596_private *lp) +{ + struct i596_cmd *ptr; + + while (lp->cmd_head != I596_NULL) { + ptr = lp->cmd_head; + lp->cmd_head = ptr->v_next; + lp->cmd_backlog--; + + switch ((ptr->command) & 0x7) { + case CmdTx: + { + struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr; + struct sk_buff *skb = tx_cmd->skb; + pci_unmap_single(NULL, tx_cmd->dma_addr, skb->len, PCI_DMA_TODEVICE); + + dev_kfree_skb(skb); + + lp->stats.tx_errors++; + lp->stats.tx_aborted_errors++; + + ptr->v_next = ptr->b_next = I596_NULL; + tx_cmd->cmd.command = 0; /* Mark as free */ + break; + } + default: + ptr->v_next = ptr->b_next = I596_NULL; + } + CHECK_WBACK_INV(ptr, sizeof(struct i596_cmd)); + } + + wait_cmd(dev,lp,100,"i596_cleanup_cmd timed out"); + lp->scb.cmd = I596_NULL; + CHECK_WBACK(&(lp->scb), sizeof(struct i596_scb)); +} + + +static inline void i596_reset(struct net_device *dev, struct i596_private *lp, int ioaddr) +{ + unsigned long flags; + + DEB(DEB_RESET,printk("i596_reset\n")); + + spin_lock_irqsave (&lp->lock, flags); + + wait_cmd(dev,lp,100,"i596_reset timed out"); + + netif_stop_queue(dev); + + /* FIXME: this command might cause an lpmc */ + lp->scb.command = CUC_ABORT | RX_ABORT; + CHECK_WBACK(&(lp->scb), sizeof(struct i596_scb)); + CA(dev); + + /* wait for shutdown */ + wait_cmd(dev,lp,1000,"i596_reset 2 timed out"); + spin_unlock_irqrestore (&lp->lock, flags); + + i596_cleanup_cmd(dev,lp); + i596_rx(dev); + + netif_start_queue(dev); + init_i596_mem(dev); +} + + +static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + int ioaddr = dev->base_addr; + unsigned long flags; + + DEB(DEB_ADDCMD,printk("i596_add_cmd cmd_head %p\n", lp->cmd_head)); + + cmd->status = 0; + cmd->command |= (CMD_EOL | CMD_INTR); + cmd->v_next = cmd->b_next = I596_NULL; + CHECK_WBACK(cmd, sizeof(struct i596_cmd)); + + spin_lock_irqsave (&lp->lock, flags); + + if (lp->cmd_head != I596_NULL) { + lp->cmd_tail->v_next = cmd; + lp->cmd_tail->b_next = WSWAPcmd(virt_to_dma(lp,&cmd->status)); + CHECK_WBACK(lp->cmd_tail, sizeof(struct i596_cmd)); + } else { + lp->cmd_head = cmd; + wait_cmd(dev,lp,100,"i596_add_cmd timed out"); + lp->scb.cmd = WSWAPcmd(virt_to_dma(lp,&cmd->status)); + lp->scb.command = CUC_START; + CHECK_WBACK(&(lp->scb), sizeof(struct i596_scb)); + CA(dev); + } + lp->cmd_tail = cmd; + lp->cmd_backlog++; + + spin_unlock_irqrestore (&lp->lock, flags); + + if (lp->cmd_backlog > max_cmd_backlog) { + unsigned long tickssofar = jiffies - lp->last_cmd; + + if (tickssofar < ticks_limit) + return; + + printk("%s: command unit timed out, status resetting.\n", dev->name); +#if 1 + i596_reset(dev, lp, ioaddr); +#endif + } +} + +#if 0 +/* this function makes a perfectly adequate probe... but we have a + device list */ +static int i596_test(struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + volatile int *tint; + u32 data; + + tint = (volatile int *)(&(lp->scp)); + data = virt_to_dma(lp,tint); + + tint[1] = -1; + CHECK_WBACK(tint,PAGE_SIZE); + + MPU_PORT(dev, 1, data); + + for(data = 1000000; data; data--) { + CHECK_INV(tint,PAGE_SIZE); + if(tint[1] != -1) + break; + + } + + printk("i596_test result %d\n", tint[1]); + +} +#endif + + +static int i596_open(struct net_device *dev) +{ + int res = 0; + + DEB(DEB_OPEN,printk("%s: i596_open() irq %d.\n", dev->name, dev->irq)); + + if (request_irq(dev->irq, &i596_interrupt, 0, "i82596", dev)) { + printk("%s: IRQ %d not free\n", dev->name, dev->irq); + return -EAGAIN; + } + + request_region(dev->base_addr, 12, dev->name); + + init_rx_bufs(dev); + + netif_start_queue(dev); + + MOD_INC_USE_COUNT; + + /* Initialize the 82596 memory */ + if (init_i596_mem(dev)) { + res = -EAGAIN; + free_irq(dev->irq, dev); + } + + return res; +} + +static void i596_tx_timeout (struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + int ioaddr = dev->base_addr; + + /* Transmitter timeout, serious problems. */ + DEB(DEB_ERRORS,printk("%s: transmit timed out, status resetting.\n", + dev->name)); + + lp->stats.tx_errors++; + + /* Try to restart the adaptor */ + if (lp->last_restart == lp->stats.tx_packets) { + DEB(DEB_ERRORS,printk ("Resetting board.\n")); + /* Shutdown and restart */ + i596_reset (dev, lp, ioaddr); + } else { + /* Issue a channel attention signal */ + DEB(DEB_ERRORS,printk ("Kicking board.\n")); + lp->scb.command = CUC_START | RX_START; + CHECK_WBACK_INV(&(lp->scb), sizeof(struct i596_scb)); + CA (dev); + lp->last_restart = lp->stats.tx_packets; + } + + dev->trans_start = jiffies; + netif_wake_queue (dev); +} + + +static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + struct tx_cmd *tx_cmd; + struct i596_tbd *tbd; + short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + dev->trans_start = jiffies; + + DEB(DEB_STARTTX,printk("%s: i596_start_xmit(%x,%x) called\n", dev->name, + skb->len, (unsigned int)skb->data)); + + netif_stop_queue(dev); + + tx_cmd = lp->tx_cmds + lp->next_tx_cmd; + tbd = lp->tbds + lp->next_tx_cmd; + + if (tx_cmd->cmd.command) { + DEB(DEB_ERRORS,printk ("%s: xmit ring full, dropping packet.\n", + dev->name)); + lp->stats.tx_dropped++; + + dev_kfree_skb(skb); + } else { + if (++lp->next_tx_cmd == TX_RING_SIZE) + lp->next_tx_cmd = 0; + tx_cmd->tbd = WSWAPtbd(virt_to_dma(lp,tbd)); + tbd->next = I596_NULL; + + tx_cmd->cmd.command = CMD_FLEX | CmdTx; + tx_cmd->skb = skb; + + tx_cmd->pad = 0; + tx_cmd->size = 0; + tbd->pad = 0; + tbd->size = EOF | length; + + tx_cmd->dma_addr = pci_map_single(NULL, skb->data, skb->len, + PCI_DMA_TODEVICE); + tbd->data = WSWAPchar(tx_cmd->dma_addr); + + DEB(DEB_TXADDR,print_eth(skb->data, "tx-queued")); + CHECK_WBACK_INV(tx_cmd, sizeof(struct tx_cmd)); + CHECK_WBACK_INV(tbd, sizeof(struct i596_tbd)); + i596_add_cmd(dev, &tx_cmd->cmd); + + lp->stats.tx_packets++; + lp->stats.tx_bytes += length; + } + + netif_start_queue(dev); + + return 0; +} + +static void print_eth(unsigned char *add, char *str) +{ + int i; + + printk("i596 0x%p, ", add); + for (i = 0; i < 6; i++) + printk(" %02X", add[i + 6]); + printk(" -->"); + for (i = 0; i < 6; i++) + printk(" %02X", add[i]); + printk(" %02X%02X, %s\n", add[12], add[13], str); +} + + +#define LAN_PROM_ADDR 0xF0810000 + +static int __init i82596_probe(struct net_device *dev, int options) +{ + int i; + struct i596_private *lp; + char eth_addr[6]; + dma_addr_t dma_addr; + + /* This lot is ensure things have been cache line aligned. */ + if (sizeof(struct i596_rfd) != 32) { + printk("82596: sizeof(struct i596_rfd) = %d\n", + sizeof(struct i596_rfd)); + return -ENODEV; + } + if (sizeof(struct i596_rbd) != 32) { + printk("82596: sizeof(struct i596_rbd) = %d\n", + sizeof(struct i596_rbd)); + return -ENODEV; + } + if (sizeof(struct tx_cmd) != 32) { + printk("82596: sizeof(struct tx_cmd) = %d\n", + sizeof(struct tx_cmd)); + return -ENODEV; + } + if (sizeof(struct i596_tbd) != 32) { + printk("82596: sizeof(struct i596_tbd) = %d\n", + sizeof(struct i596_tbd)); + return -ENODEV; + } + if (sizeof(struct i596_private) > 4096) { + printk("82596: sizeof(struct i596_private) = %d\n", + sizeof(struct i596_private)); + return -ENODEV; + } + + /* FIXME: + Currently this works only, if set-up from lasi.c. + This should be changed to use probing too ! + */ + + if (!dev->base_addr || !dev->irq) + return -ENODEV; + + if (!pdc_lan_station_id( (char*)ð_addr, (void*)dev->base_addr)) { + for(i=0;i<6;i++) + eth_addr[i] = gsc_readb(LAN_PROM_ADDR+i); + printk("82596.c: MAC of HP700 LAN blindely read from the prom!\n"); + } + + dev->mem_start = (int)pci_alloc_consistent( NULL, + sizeof(struct i596_private), &dma_addr); + if (!dev->mem_start) { + printk("%s: Couldn't get consistent shared memory\n", dev->name); + dma_consistent = 0; + dev->mem_start = (int)__get_free_pages(GFP_ATOMIC, 0); + if (!dev->mem_start) { + printk("%s: Couldn't get shared memory\n", dev->name); +#ifdef ENABLE_APRICOT + release_region(dev->base_addr, I596_TOTAL_SIZE); +#endif + return -ENOMEM; + } + dma_addr = virt_to_bus(dev->mem_start); + } + + ether_setup(dev); + DEB(DEB_PROBE,printk("%s: 82596 at %#3lx,", dev->name, dev->base_addr)); + + for (i = 0; i < 6; i++) + DEB(DEB_PROBE,printk(" %2.2X", dev->dev_addr[i] = eth_addr[i])); + + DEB(DEB_PROBE,printk(" IRQ %d.\n", dev->irq)); + + DEB(DEB_PROBE,printk(version)); + + /* The 82596-specific entries in the device structure. */ + dev->open = i596_open; + dev->stop = i596_close; + dev->hard_start_xmit = i596_start_xmit; + dev->get_stats = i596_get_stats; + dev->set_multicast_list = set_multicast_list; + dev->tx_timeout = i596_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->priv = (void *)(dev->mem_start); + + lp = (struct i596_private *) dev->priv; + DEB(DEB_INIT,printk ("%s: lp at 0x%08lx (%d bytes), lp->scb at 0x%08lx\n", + dev->name, (unsigned long)lp, + sizeof(struct i596_private), (unsigned long)&lp->scb)); + memset((void *) lp, 0, sizeof(struct i596_private)); + +#if 0 + kernel_set_cachemode((void *)(dev->mem_start), 4096, IOMAP_NOCACHE_SER); +#endif + lp->options = options; + lp->scb.command = 0; + lp->scb.cmd = I596_NULL; + lp->scb.rfd = I596_NULL; + lp->lock = SPIN_LOCK_UNLOCKED; + lp->dma_addr = dma_addr; + + CHECK_WBACK_INV(dev->mem_start, sizeof(struct i596_private)); + + return 0; +} + + +int __init lasi_i82596_probe(struct net_device *dev) +{ + return i82596_probe(dev, 0); +} + + +int __init asp_i82596_probe(struct net_device *dev) +{ + return i82596_probe(dev, OPT_SWAP_PORT); +} + + +static void i596_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct i596_private *lp; + unsigned short status, ack_cmd = 0; + + if (dev == NULL) { + printk("i596_interrupt(): irq %d for unknown device.\n", irq); + return; + } + + lp = (struct i596_private *) dev->priv; + + spin_lock (&lp->lock); + + wait_cmd(dev,lp,100,"i596 interrupt, timeout"); + status = lp->scb.status; + + DEB(DEB_INTS,printk("%s: i596 interrupt, IRQ %d, status %4.4x.\n", + dev->name, irq, status)); + + ack_cmd = status & 0xf000; + + if (!ack_cmd) { + DEB(DEB_ERRORS, printk("%s: interrupt with no events\n", dev->name)); + spin_unlock (&lp->lock); + return; + } + + if ((status & 0x8000) || (status & 0x2000)) { + struct i596_cmd *ptr; + + if ((status & 0x8000)) + DEB(DEB_INTS,printk("%s: i596 interrupt completed command.\n", dev->name)); + if ((status & 0x2000)) + DEB(DEB_INTS,printk("%s: i596 interrupt command unit inactive %x.\n", dev->name, status & 0x0700)); + + while (lp->cmd_head != I596_NULL) { + CHECK_INV(lp->cmd_head, sizeof(struct i596_cmd)); + if (!(lp->cmd_head->status & STAT_C)) + break; + + ptr = lp->cmd_head; + + DEB(DEB_STATUS,printk("cmd_head->status = %04x, ->command = %04x\n", + lp->cmd_head->status, lp->cmd_head->command)); + lp->cmd_head = ptr->v_next; + lp->cmd_backlog--; + + switch ((ptr->command) & 0x7) { + case CmdTx: + { + struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr; + struct sk_buff *skb = tx_cmd->skb; + + if ((ptr->status) & STAT_OK) { + DEB(DEB_TXADDR,print_eth(skb->data, "tx-done")); + } else { + lp->stats.tx_errors++; + if ((ptr->status) & 0x0020) + lp->stats.collisions++; + if (!((ptr->status) & 0x0040)) + lp->stats.tx_heartbeat_errors++; + if ((ptr->status) & 0x0400) + lp->stats.tx_carrier_errors++; + if ((ptr->status) & 0x0800) + lp->stats.collisions++; + if ((ptr->status) & 0x1000) + lp->stats.tx_aborted_errors++; + } + pci_unmap_single(NULL, tx_cmd->dma_addr, skb->len, PCI_DMA_TODEVICE); + dev_kfree_skb_irq(skb); + + tx_cmd->cmd.command = 0; /* Mark free */ + break; + } + case CmdTDR: + { + unsigned short status = ((struct tdr_cmd *)ptr)->status; + + if (status & 0x8000) { + DEB(DEB_ANY,printk("%s: link ok.\n", dev->name)); + } else { + if (status & 0x4000) + printk("%s: Transceiver problem.\n", dev->name); + if (status & 0x2000) + printk("%s: Termination problem.\n", dev->name); + if (status & 0x1000) + printk("%s: Short circuit.\n", dev->name); + + DEB(DEB_TDR,printk("%s: Time %d.\n", dev->name, status & 0x07ff)); + } + break; + } + case CmdConfigure: + /* Zap command so set_multicast_list() knows it is free */ + ptr->command = 0; + break; + } + ptr->v_next = ptr->b_next = I596_NULL; + CHECK_WBACK(ptr, sizeof(struct i596_cmd)); + lp->last_cmd = jiffies; + } + + /* This mess is arranging that only the last of any outstanding + * commands has the interrupt bit set. Should probably really + * only add to the cmd queue when the CU is stopped. + */ + ptr = lp->cmd_head; + while ((ptr != I596_NULL) && (ptr != lp->cmd_tail)) { + struct i596_cmd *prev = ptr; + + ptr->command &= 0x1fff; + ptr = ptr->v_next; + CHECK_WBACK_INV(prev, sizeof(struct i596_cmd)); + } + + if ((lp->cmd_head != I596_NULL)) + ack_cmd |= CUC_START; + lp->scb.cmd = WSWAPcmd(virt_to_dma(lp,&lp->cmd_head->status)); + CHECK_WBACK_INV(&lp->scb, sizeof(struct i596_scb)); + } + if ((status & 0x1000) || (status & 0x4000)) { + if ((status & 0x4000)) + DEB(DEB_INTS,printk("%s: i596 interrupt received a frame.\n", dev->name)); + i596_rx(dev); + /* Only RX_START if stopped - RGH 07-07-96 */ + if (status & 0x1000) { + if (netif_running(dev)) { + DEB(DEB_ERRORS,printk("%s: i596 interrupt receive unit inactive, status 0x%x\n", dev->name, status)); + ack_cmd |= RX_START; + lp->stats.rx_errors++; + lp->stats.rx_fifo_errors++; + rebuild_rx_bufs(dev); + } + } + } + wait_cmd(dev,lp,100,"i596 interrupt, timeout"); + lp->scb.command = ack_cmd; + CHECK_WBACK(&lp->scb, sizeof(struct i596_scb)); + + /* DANGER: I suspect that some kind of interrupt + acknowledgement aside from acking the 82596 might be needed + here... but it's running acceptably without */ + + CA(dev); + + wait_cmd(dev,lp,100,"i596 interrupt, exit timeout"); + DEB(DEB_INTS,printk("%s: exiting interrupt.\n", dev->name)); + + spin_unlock (&lp->lock); + return; +} + +static int i596_close(struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + unsigned long flags; + + netif_stop_queue(dev); + + DEB(DEB_INIT,printk("%s: Shutting down ethercard, status was %4.4x.\n", + dev->name, lp->scb.status)); + + save_flags(flags); + cli(); + + wait_cmd(dev,lp,100,"close1 timed out"); + lp->scb.command = CUC_ABORT | RX_ABORT; + CHECK_WBACK(&lp->scb, sizeof(struct i596_scb)); + + CA(dev); + + wait_cmd(dev,lp,100,"close2 timed out"); + restore_flags(flags); + DEB(DEB_STRUCT,i596_display_data(dev)); + i596_cleanup_cmd(dev,lp); + + disable_irq(dev->irq); + + free_irq(dev->irq, dev); + remove_rx_bufs(dev); + + release_region(dev->base_addr, 12); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct net_device_stats * + i596_get_stats(struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + + return &lp->stats; +} + +/* + * Set or clear the multicast filter for this adaptor. + */ + +static void set_multicast_list(struct net_device *dev) +{ + struct i596_private *lp = (struct i596_private *) dev->priv; + int config = 0, cnt; + + DEB(DEB_MULTI,printk("%s: set multicast list, %d entries, promisc %s, allmulti %s\n", dev->name, dev->mc_count, dev->flags & IFF_PROMISC ? "ON" : "OFF", dev->flags & IFF_ALLMULTI ? "ON" : "OFF")); + + if ((dev->flags & IFF_PROMISC) && !(lp->cf_cmd.i596_config[8] & 0x01)) { + lp->cf_cmd.i596_config[8] |= 0x01; + config = 1; + } + if (!(dev->flags & IFF_PROMISC) && (lp->cf_cmd.i596_config[8] & 0x01)) { + lp->cf_cmd.i596_config[8] &= ~0x01; + config = 1; + } + if ((dev->flags & IFF_ALLMULTI) && (lp->cf_cmd.i596_config[11] & 0x20)) { + lp->cf_cmd.i596_config[11] &= ~0x20; + config = 1; + } + if (!(dev->flags & IFF_ALLMULTI) && !(lp->cf_cmd.i596_config[11] & 0x20)) { + lp->cf_cmd.i596_config[11] |= 0x20; + config = 1; + } + if (config) { + if (lp->cf_cmd.cmd.command) + printk("%s: config change request already queued\n", + dev->name); + else { + lp->cf_cmd.cmd.command = CmdConfigure; + CHECK_WBACK_INV(&lp->cf_cmd, sizeof(struct cf_cmd)); + i596_add_cmd(dev, &lp->cf_cmd.cmd); + } + } + + cnt = dev->mc_count; + if (cnt > MAX_MC_CNT) + { + cnt = MAX_MC_CNT; + printk("%s: Only %d multicast addresses supported", + dev->name, cnt); + } + + if (dev->mc_count > 0) { + struct dev_mc_list *dmi; + unsigned char *cp; + struct mc_cmd *cmd; + + cmd = &lp->mc_cmd; + cmd->cmd.command = CmdMulticastList; + cmd->mc_cnt = dev->mc_count * 6; + cp = cmd->mc_addrs; + for (dmi = dev->mc_list; cnt && dmi != NULL; dmi = dmi->next, cnt--, cp += 6) { + memcpy(cp, dmi->dmi_addr, 6); + if (i596_debug > 1) + DEB(DEB_MULTI,printk("%s: Adding address %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, cp[0],cp[1],cp[2],cp[3],cp[4],cp[5])); + } + CHECK_WBACK_INV(&lp->mc_cmd, sizeof(struct mc_cmd)); + i596_add_cmd(dev, &cmd->cmd); + } +} + +#ifdef HAVE_DEVLIST +static unsigned int i596_portlist[] __initdata = +{0x300, 0}; +struct netdev_entry i596_drv = +{"lasi_i82596", lasi_i82596_probe, I596_TOTAL_SIZE, i596_portlist}; +#endif + +#ifdef MODULE +static char devicename[9] = +{0,}; +static struct net_device dev_82596 = +{ + devicename, /* device name inserted by drivers/net/net_init.c */ + 0, 0, 0, 0, + 0, 0, /* base, irq */ + 0, 0, 0, NULL, lasi_i82596_probe}; + + +MODULE_PARM(debug, "i"); +static int debug = -1; + +int init_module(void) +{ + if (debug >= 0) + i596_debug = debug; + if (register_netdev(&dev_82596) != 0) + return -EIO; + return 0; +} + +void cleanup_module(void) +{ + unregister_netdev(&dev_82596); + lp = (struct i596_private *) dev_82596.priv; + + if (dma_consistent) + pci_free_consistent( NULL, sizeof( struct i596_private), + dev_82596.mem_start, lp->dma_addr); + else + free_page ((u32)(dev_82596.mem_start)); + + dev_82596.priv = NULL; +} + +#endif /* MODULE */ + diff --git a/drivers/net/ne.c b/drivers/net/ne.c index 133e8d1a199c..5292c4a35aed 100644 --- a/drivers/net/ne.c +++ b/drivers/net/ne.c @@ -157,7 +157,9 @@ static void ne_block_output(struct net_device *dev, const int count, int __init ne_probe(struct net_device *dev) { - unsigned int base_addr = dev ? dev->base_addr : 0; + unsigned int base_addr = dev->base_addr; + + SET_MODULE_OWNER(dev); /* First check any supplied i/o locations. User knows best. */ if (base_addr > 0x1ff) /* Check a single specified location. */ @@ -469,7 +471,6 @@ err_out: static int ne_open(struct net_device *dev) { ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -478,7 +479,6 @@ static int ne_close(struct net_device *dev) if (ei_debug > 1) printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name); ei_close(dev); - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/ne2.c b/drivers/net/ne2.c index 3ff0ef14158c..9bb252dc5c5b 100644 --- a/drivers/net/ne2.c +++ b/drivers/net/ne2.c @@ -152,6 +152,8 @@ int __init ne2_probe(struct net_device *dev) int i; int adapter_found = 0; + SET_MODULE_OWNER(dev); + /* Do not check any supplied i/o locations. POS registers usually don't fail :) */ @@ -371,7 +373,6 @@ out: static int ne_open(struct net_device *dev) { ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -380,7 +381,6 @@ static int ne_close(struct net_device *dev) if (ei_debug > 1) printk("%s: Shutting down ethercard.\n", dev->name); ei_close(dev); - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c index 3ba20f197fbb..b404574fea8a 100644 --- a/drivers/net/ne2k-pci.c +++ b/drivers/net/ne2k-pci.c @@ -244,6 +244,7 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev, printk (KERN_ERR "ne2k-pci: cannot allocate ethernet device\n"); goto err_out_free_res; } + SET_MODULE_OWNER(dev); /* Reset card. Who knows what dain-bramaged state it was left in. */ { @@ -358,11 +359,10 @@ err_out_free_res: static int ne2k_pci_open(struct net_device *dev) { - MOD_INC_USE_COUNT; - if (request_irq(dev->irq, ei_interrupt, SA_SHIRQ, dev->name, dev)) { - MOD_DEC_USE_COUNT; - return -EAGAIN; - } + int ret = request_irq(dev->irq, ei_interrupt, SA_SHIRQ, dev->name, dev); + if (ret) + return ret; + /* Set full duplex for the chips that we know about. */ if (ei_status.ne2k_flags & FORCE_FDX) { long ioaddr = dev->base_addr; @@ -380,7 +380,6 @@ static int ne2k_pci_close(struct net_device *dev) { ei_close(dev); free_irq(dev->irq, dev); - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/ne3210.c b/drivers/net/ne3210.c index e57551271df5..eae50f902f32 100644 --- a/drivers/net/ne3210.c +++ b/drivers/net/ne3210.c @@ -99,6 +99,8 @@ int __init ne3210_probe(struct net_device *dev) { unsigned short ioaddr = dev->base_addr; + SET_MODULE_OWNER(dev); + if (ioaddr > 0x1ff) /* Check a single specified location. */ return ne3210_probe1(dev, ioaddr); else if (ioaddr > 0) /* Don't probe at all. */ @@ -345,7 +347,6 @@ static void ne3210_block_output(struct net_device *dev, int count, static int ne3210_open(struct net_device *dev) { ei_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -356,7 +357,6 @@ static int ne3210_close(struct net_device *dev) printk("%s: Shutting down ethercard.\n", dev->name); ei_close(dev); - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/plip.c b/drivers/net/plip.c index ee7ef15784fd..43c0e7bcdb4e 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -286,6 +286,7 @@ plip_init_dev(struct net_device *dev, struct parport *pb) struct net_local *nl; struct pardevice *pardev; + SET_MODULE_OWNER(dev); dev->irq = pb->irq; dev->base_addr = pb->base; @@ -1164,7 +1165,6 @@ plip_open(struct net_device *dev) netif_start_queue (dev); - MOD_INC_USE_COUNT; return 0; } @@ -1212,7 +1212,6 @@ plip_close(struct net_device *dev) /* Reset. */ outb(0x00, PAR_CONTROL(dev)); #endif - MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/net/tokenring/abyss.c b/drivers/net/tokenring/abyss.c index 3db04bdee4f1..a020b21ddc71 100644 --- a/drivers/net/tokenring/abyss.c +++ b/drivers/net/tokenring/abyss.c @@ -15,6 +15,7 @@ * Modification History: * 30-Dec-99 AF Split off from the tms380tr driver. * 22-Jan-00 AF Updated to use indirect read/writes + * 23-Nov-00 JG New PCI API, cleanups * * * TODO: @@ -23,7 +24,6 @@ * config registers) * */ -static const char *version = "abyss.c: v1.01 22/01/2000 by Adam Fritzler\n"; #include #include @@ -41,9 +41,18 @@ static const char *version = "abyss.c: v1.01 22/01/2000 by Adam Fritzler\n"; #include "tms380tr.h" #include "abyss.h" /* Madge-specific constants */ +static char version[] __initdata = +"abyss.c: v1.02 23/11/2000 by Adam Fritzler\n"; + #define ABYSS_IO_EXTENT 64 -int abyss_probe(void); +static struct pci_device_id abyss_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_MK2, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_TOKEN_RING << 8, 0x00ffffff, }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, abyss_pci_tbl); + static int abyss_open(struct net_device *dev); static int abyss_close(struct net_device *dev); static void abyss_enable(struct net_device *dev); @@ -51,17 +60,16 @@ static int abyss_chipset_init(struct net_device *dev); static void abyss_read_eeprom(struct net_device *dev); static unsigned short abyss_setnselout_pins(struct net_device *dev); -void at24_writedatabyte(unsigned long regaddr, unsigned char byte); -int at24_sendfullcmd(unsigned long regaddr, unsigned char cmd, unsigned char addr); -int at24_sendcmd(unsigned long regaddr, unsigned char cmd); -unsigned char at24_readdatabit(unsigned long regaddr); -unsigned char at24_readdatabyte(unsigned long regaddr); -int at24_waitforack(unsigned long regaddr); -int at24_waitfornack(unsigned long regaddr); -void at24_setlines(unsigned long regaddr, unsigned char clock, unsigned char data); -void at24_start(unsigned long regaddr); -void at24_stop(unsigned long regaddr); -unsigned char at24_readb(unsigned long regaddr, unsigned char addr); +static void at24_writedatabyte(unsigned long regaddr, unsigned char byte); +static int at24_sendfullcmd(unsigned long regaddr, unsigned char cmd, unsigned char addr); +static int at24_sendcmd(unsigned long regaddr, unsigned char cmd); +static unsigned char at24_readdatabit(unsigned long regaddr); +static unsigned char at24_readdatabyte(unsigned long regaddr); +static int at24_waitforack(unsigned long regaddr); +static int at24_waitfornack(unsigned long regaddr); +static void at24_setlines(unsigned long regaddr, unsigned char clock, unsigned char data); +static void at24_start(unsigned long regaddr); +static unsigned char at24_readb(unsigned long regaddr, unsigned char addr); static unsigned short abyss_sifreadb(struct net_device *dev, unsigned short reg) { @@ -83,129 +91,100 @@ static void abyss_sifwritew(struct net_device *dev, unsigned short val, unsigned outw(val, dev->base_addr + reg); } -struct tms_abyss_card { - struct net_device *dev; - struct pci_dev *pci_dev; - struct tms_abyss_card *next; -}; -static struct tms_abyss_card *abyss_card_list = NULL; - -int __init abyss_probe(void) +static int __init abyss_attach(struct pci_dev *pdev, const struct pci_device_id *ent) { - static int versionprinted = 0; - struct pci_dev *pdev = NULL ; + static int versionprinted; struct net_device *dev; struct net_local *tp; - int i; - - if (!pci_present()) - return (-1); /* No PCI present. */ + int i, ret, pci_irq_line; + unsigned long pci_ioaddr; - while ( (pdev=pci_find_class(PCI_CLASS_NETWORK_TOKEN_RING<<8, pdev))) { - unsigned int pci_irq_line; - unsigned long pci_ioaddr; - struct tms_abyss_card *card; - - /* We only support Madge Smart 16/4 PCI Mk2 (Abyss) cards */ - if ( (pdev->vendor != PCI_VENDOR_ID_MADGE) || - (pdev->device != PCI_DEVICE_ID_MADGE_MK2) ) - continue; - - if (versionprinted++ == 0) - printk("%s", version); + if (versionprinted++ == 0) + printk("%s", version); - if (pci_enable_device(pdev)) - continue; + if (pci_enable_device(pdev)) + return -EIO; - /* Remove I/O space marker in bit 0. */ - pci_irq_line = pdev->irq; - pci_ioaddr = pci_resource_start (pdev, 0); - - if(!request_region(pci_ioaddr, ABYSS_IO_EXTENT, "abyss")) - continue; + /* Remove I/O space marker in bit 0. */ + pci_irq_line = pdev->irq; + pci_ioaddr = pci_resource_start (pdev, 0); - /* At this point we have found a valid card. */ + /* At this point we have found a valid card. */ - dev = init_trdev(NULL, 0); - if (!dev) { - release_region(pci_ioaddr, ABYSS_IO_EXTENT); - continue; - } + dev = init_trdev(NULL, 0); + if (!dev) + return -ENOMEM; + SET_MODULE_OWNER(dev); + + if (!request_region(pci_ioaddr, ABYSS_IO_EXTENT, dev->name)) { + ret = -EBUSY; + goto err_out_trdev; + } - if(request_irq(pdev->irq, tms380tr_interrupt, SA_SHIRQ, - "abyss", dev)) { - release_region(pci_ioaddr, ABYSS_IO_EXTENT) ; - /* XXX free trdev */ - continue; /*return (-ENODEV);*/ /* continue; ?? */ - } + ret = request_irq(pdev->irq, tms380tr_interrupt, SA_SHIRQ, + dev->name, dev); + if (ret) + goto err_out_region; - /* - if (load_tms380_module("abyss.c")) { - return 0; - } - */ - - dev->base_addr = pci_ioaddr; - dev->irq = pci_irq_line; - dev->dma = 0; + dev->base_addr = pci_ioaddr; + dev->irq = pci_irq_line; - printk("%s: Madge Smart 16/4 PCI Mk2 (Abyss)\n", dev->name); - printk("%s: IO: %#4lx IRQ: %d\n", - dev->name, pci_ioaddr, dev->irq); - /* - * The TMS SIF registers lay 0x10 above the card base address. - */ - dev->base_addr += 0x10; + printk("%s: Madge Smart 16/4 PCI Mk2 (Abyss)\n", dev->name); + printk("%s: IO: %#4lx IRQ: %d\n", + dev->name, pci_ioaddr, dev->irq); + /* + * The TMS SIF registers lay 0x10 above the card base address. + */ + dev->base_addr += 0x10; - if (tmsdev_init(dev)) { - printk("%s: unable to get memory for dev->priv.\n", - dev->name); - return 0; - } + ret = tmsdev_init(dev); + if (ret) { + printk("%s: unable to get memory for dev->priv.\n", + dev->name); + goto err_out_irq; + } - abyss_read_eeprom(dev); + abyss_read_eeprom(dev); - printk("%s: Ring Station Address: ", dev->name); - printk("%2.2x", dev->dev_addr[0]); - for (i = 1; i < 6; i++) - printk(":%2.2x", dev->dev_addr[i]); - printk("\n"); - - tp = (struct net_local *)dev->priv; - tp->dmalimit = 0; /* XXX: should be the max PCI32 DMA max */ - tp->setnselout = abyss_setnselout_pins; - tp->sifreadb = abyss_sifreadb; - tp->sifreadw = abyss_sifreadw; - tp->sifwriteb = abyss_sifwriteb; - tp->sifwritew = abyss_sifwritew; - - memcpy(tp->ProductID, "Madge PCI 16/4 Mk2", PROD_ID_SIZE + 1); + printk("%s: Ring Station Address: ", dev->name); + printk("%2.2x", dev->dev_addr[0]); + for (i = 1; i < 6; i++) + printk(":%2.2x", dev->dev_addr[i]); + printk("\n"); + + tp = dev->priv; + tp->dmalimit = 0; /* XXX: should be the max PCI32 DMA max */ + tp->setnselout = abyss_setnselout_pins; + tp->sifreadb = abyss_sifreadb; + tp->sifreadw = abyss_sifreadw; + tp->sifwriteb = abyss_sifwriteb; + tp->sifwritew = abyss_sifwritew; + + memcpy(tp->ProductID, "Madge PCI 16/4 Mk2", PROD_ID_SIZE + 1); - dev->open = abyss_open; - dev->stop = abyss_close; - - if (register_trdev(dev) == 0) { - /* Enlist in the card list */ - card = kmalloc(sizeof(struct tms_abyss_card), - GFP_KERNEL); - card->next = abyss_card_list; - abyss_card_list = card; - card->dev = dev; - card->pci_dev = pdev; - } else { - printk("abyss: register_trdev() returned non-zero.\n"); - kfree(dev->priv); - kfree(dev); - return -1; - } - } - - if (abyss_card_list) - return 0; - return (-1); + dev->open = abyss_open; + dev->stop = abyss_close; + + ret = register_trdev(dev); + if (ret) + goto err_out_tmsdev; + + pci_set_drvdata(pdev, dev); + return 0; + +err_out_tmsdev: + kfree(dev->priv); +err_out_irq: + free_irq(pdev->irq, dev); +err_out_region: + release_region(pci_ioaddr, ABYSS_IO_EXTENT); +err_out_trdev: + unregister_netdev(dev); + kfree(dev); + return ret; } -unsigned short abyss_setnselout_pins(struct net_device *dev) +static unsigned short abyss_setnselout_pins(struct net_device *dev) { unsigned short val = 0; struct net_local *tp = (struct net_local *)dev->priv; @@ -228,7 +207,7 @@ unsigned short abyss_setnselout_pins(struct net_device *dev) * These access an Atmel AT24 SEEPROM using their glue chip registers. * */ -void at24_writedatabyte(unsigned long regaddr, unsigned char byte) +static void at24_writedatabyte(unsigned long regaddr, unsigned char byte) { int i; @@ -239,7 +218,7 @@ void at24_writedatabyte(unsigned long regaddr, unsigned char byte) } } -int at24_sendfullcmd(unsigned long regaddr, unsigned char cmd, unsigned char addr) +static int at24_sendfullcmd(unsigned long regaddr, unsigned char cmd, unsigned char addr) { if (at24_sendcmd(regaddr, cmd)) { at24_writedatabyte(regaddr, addr); @@ -248,7 +227,7 @@ int at24_sendfullcmd(unsigned long regaddr, unsigned char cmd, unsigned char add return 0; } -int at24_sendcmd(unsigned long regaddr, unsigned char cmd) +static int at24_sendcmd(unsigned long regaddr, unsigned char cmd) { int i; @@ -261,7 +240,7 @@ int at24_sendcmd(unsigned long regaddr, unsigned char cmd) return 0; } -unsigned char at24_readdatabit(unsigned long regaddr) +static unsigned char at24_readdatabit(unsigned long regaddr) { unsigned char val; @@ -273,7 +252,7 @@ unsigned char at24_readdatabit(unsigned long regaddr) return val; } -unsigned char at24_readdatabyte(unsigned long regaddr) +static unsigned char at24_readdatabyte(unsigned long regaddr) { unsigned char data = 0; int i; @@ -286,7 +265,7 @@ unsigned char at24_readdatabyte(unsigned long regaddr) return data; } -int at24_waitforack(unsigned long regaddr) +static int at24_waitforack(unsigned long regaddr) { int i; @@ -297,7 +276,7 @@ int at24_waitforack(unsigned long regaddr) return 0; } -int at24_waitfornack(unsigned long regaddr) +static int at24_waitfornack(unsigned long regaddr) { int i; for (i = 0; i < 10; i++) { @@ -307,10 +286,9 @@ int at24_waitfornack(unsigned long regaddr) return 0; } -void at24_setlines(unsigned long regaddr, unsigned char clock, unsigned char data) +static void at24_setlines(unsigned long regaddr, unsigned char clock, unsigned char data) { - unsigned char val; - val = AT24_ENABLE; + unsigned char val = AT24_ENABLE; if (clock) val |= AT24_CLOCK; if (data) @@ -320,25 +298,15 @@ void at24_setlines(unsigned long regaddr, unsigned char clock, unsigned char dat tms380tr_wait(20); /* Very necessary. */ } -void at24_start(unsigned long regaddr) +static void at24_start(unsigned long regaddr) { at24_setlines(regaddr, 0, 1); at24_setlines(regaddr, 1, 1); at24_setlines(regaddr, 1, 0); at24_setlines(regaddr, 0, 1); - return; } -void at24_stop(unsigned long regaddr) -{ - at24_setlines(regaddr, 0, 0); - at24_setlines(regaddr, 1, 0); - at24_setlines(regaddr, 1, 1); - at24_setlines(regaddr, 0, 1); - return; -} - -unsigned char at24_readb(unsigned long regaddr, unsigned char addr) +static unsigned char at24_readb(unsigned long regaddr, unsigned char addr) { unsigned char data = 0xff; @@ -367,7 +335,6 @@ static void abyss_enable(struct net_device *dev) reset_reg |= PCIBM2_RESET_REG_CHIP_NRES; outb(reset_reg, ioaddr + PCIBM2_RESET_REG); tms380tr_wait(100); - return; } /* @@ -411,14 +378,12 @@ static int abyss_chipset_init(struct net_device *dev) return 0; } -void abyss_chipset_close(struct net_device *dev) +static inline void abyss_chipset_close(struct net_device *dev) { unsigned long ioaddr; ioaddr = dev->base_addr; outb(0, ioaddr + PCIBM2_RESET_REG); - - return; } /* @@ -451,15 +416,12 @@ static void abyss_read_eeprom(struct net_device *dev) for (i = 0; i < 6; i++) dev->dev_addr[i] = at24_readb(ioaddr + PCIBM2_SEEPROM_REG, PCIBM2_SEEPROM_BIA+i); - - return; } static int abyss_open(struct net_device *dev) { abyss_chipset_init(dev); tms380tr_open(dev); - MOD_INC_USE_COUNT; return 0; } @@ -467,41 +429,49 @@ static int abyss_close(struct net_device *dev) { tms380tr_close(dev); abyss_chipset_close(dev); - MOD_DEC_USE_COUNT; return 0; } -#ifdef MODULE +static void __exit abyss_detach (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (!dev) + BUG(); + unregister_netdev(dev); + release_region(dev->base_addr-0x10, ABYSS_IO_EXTENT); + free_irq(dev->irq, dev); + kfree(dev->priv); + kfree(dev); + pci_set_drvdata(pdev, NULL); +} + +static struct pci_driver abyss_driver = { + name: "abyss", + id_table: abyss_pci_tbl, + probe: abyss_attach, + remove: abyss_detach, +}; -int init_module(void) +static int __init abyss_init (void) { - /* Probe for cards. */ - if (abyss_probe()) { - printk(KERN_NOTICE "abyss.c: No cards found.\n"); + int rc = pci_register_driver (&abyss_driver); + if (rc < 0) + return rc; + if (rc == 0) { + pci_unregister_driver (&abyss_driver); + return -ENODEV; } - /* lock_tms380_module(); */ - return (0); + return 0; } -void cleanup_module(void) +static void __exit abyss_rmmod (void) { - struct net_device *dev; - struct tms_abyss_card *this_card; - - while (abyss_card_list) { - dev = abyss_card_list->dev; - unregister_netdev(dev); - release_region(dev->base_addr-0x10, ABYSS_IO_EXTENT); - free_irq(dev->irq, dev); - kfree(dev->priv); - kfree(dev); - this_card = abyss_card_list; - abyss_card_list = this_card->next; - kfree(this_card); - } - /* unlock_tms380_module(); */ + pci_unregister_driver (&abyss_driver); } -#endif /* MODULE */ + +module_init(abyss_init); +module_exit(abyss_rmmod); /* diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c index b8784bb29feb..c42f3f6eb588 100644 --- a/drivers/net/tokenring/olympic.c +++ b/drivers/net/tokenring/olympic.c @@ -101,6 +101,13 @@ static char *version = "Olympic.c v0.5.0 3/10/00 - Peter De Schrijver & Mike Phillips" ; +static struct pci_device_id olympic_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR_WAKE, PCI_ANY_ID, PCI_ANY_ID, }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, olympic_pci_tbl); + + static char *open_maj_error[] = {"No error", "Lobe Media Test", "Physical Insertion", "Address Verification", "Neighbor Notification (Ring Poll)", "Request Parameters","FDX Registration Request", diff --git a/drivers/net/tokenring/tmspci.c b/drivers/net/tokenring/tmspci.c index 029e0cdfa813..216154db560f 100644 --- a/drivers/net/tokenring/tmspci.c +++ b/drivers/net/tokenring/tmspci.c @@ -19,12 +19,12 @@ * Modification History: * 30-Dec-99 AF Split off from the tms380tr driver. * 22-Jan-00 AF Updated to use indirect read/writes + * 23-Nov-00 JG New PCI API, cleanups * * TODO: * 1. See if we can use MMIO instead of port accesses * */ -static const char *version = "tmspci.c: v1.01 22/01/2000 by Adam Fritzler\n"; #include #include @@ -41,33 +41,32 @@ static const char *version = "tmspci.c: v1.01 22/01/2000 by Adam Fritzler\n"; #include #include "tms380tr.h" +static char version[] __initdata = +"tmspci.c: v1.02 23/11/2000 by Adam Fritzler\n"; + #define TMS_PCI_IO_EXTENT 32 -struct cardinfo_table { - int vendor_id; /* PCI info */ - int device_id; - int registeroffset; /* SIF offset from dev->base_addr */ +struct card_info { unsigned char nselout[2]; /* NSELOUT vals for 4mb([0]) and 16mb([1]) */ char *name; }; -struct cardinfo_table probelist[] = { - { 0, 0, - 0x0000, {0x00, 0x00}, "Unknown TMS380 Token Ring Adapter"}, - { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_TOKENRING, - 0x0000, {0x03, 0x01}, "Compaq 4/16 TR PCI"}, - { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_TR, - 0x0000, {0x03, 0x01}, "SK NET TR 4/16 PCI"}, - { PCI_VENDOR_ID_TCONRAD, PCI_DEVICE_ID_TCONRAD_TOKENRING, - 0x0000, {0x03, 0x01}, "Thomas-Conrad TC4048 PCI 4/16"}, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C339, - 0x0000, {0x03, 0x01}, "3Com Token Link Velocity"}, - { 0, 0, 0, {0x00, 0x00}, NULL} +static struct card_info card_info_table[] = { + { {0x03, 0x01}, "Compaq 4/16 TR PCI"}, + { {0x03, 0x01}, "SK NET TR 4/16 PCI"}, + { {0x03, 0x01}, "Thomas-Conrad TC4048 PCI 4/16"}, + { {0x03, 0x01}, "3Com Token Link Velocity"}, +}; + +static struct pci_device_id tmspci_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_TOKENRING, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_TR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, + { PCI_VENDOR_ID_TCONRAD, PCI_DEVICE_ID_TCONRAD_TOKENRING, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, + { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C339, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, + { } /* Terminating entry */ }; +MODULE_DEVICE_TABLE(pci, tmspci_pci_tbl); -int tms_pci_probe(void); -static int tms_pci_open(struct net_device *dev); -static int tms_pci_close(struct net_device *dev); static void tms_pci_read_eeprom(struct net_device *dev); static unsigned short tms_pci_setnselout_pins(struct net_device *dev); @@ -91,145 +90,97 @@ static void tms_pci_sifwritew(struct net_device *dev, unsigned short val, unsign outw(val, dev->base_addr + reg); } -struct tms_pci_card { - struct net_device *dev; - struct pci_dev *pci_dev; - struct cardinfo_table *cardinfo; - struct tms_pci_card *next; -}; -static struct tms_pci_card *tms_pci_card_list = NULL; - - -struct cardinfo_table * __init tms_pci_getcardinfo(unsigned short vendor, - unsigned short device) -{ - int cur; - for (cur = 1; probelist[cur].name != NULL; cur++) { - if ((probelist[cur].vendor_id == vendor) && - (probelist[cur].device_id == device)) - return &probelist[cur]; - } - - return NULL; -} - -int __init tms_pci_probe(void) +static int __init tms_pci_attach(struct pci_dev *pdev, const struct pci_device_id *ent) { - static int versionprinted = 0; - struct pci_dev *pdev = NULL ; + static int versionprinted; struct net_device *dev; struct net_local *tp; - int i; - - if (!pci_present()) - return (-1); /* No PCI present. */ - - while ( (pdev=pci_find_class(PCI_CLASS_NETWORK_TOKEN_RING<<8, pdev))) { - unsigned int pci_irq_line; - unsigned long pci_ioaddr; - struct tms_pci_card *card; - struct cardinfo_table *cardinfo; + int i, ret; + unsigned int pci_irq_line; + unsigned long pci_ioaddr; + struct card_info *cardinfo = &card_info_table[ent->driver_data]; - if ((cardinfo = - tms_pci_getcardinfo(pdev->vendor, pdev->device)) == NULL) - continue; + if (versionprinted++ == 0) + printk("%s", version); - if (versionprinted++ == 0) - printk("%s", version); + if (pci_enable_device(pdev)) + return -EIO; - if (pci_enable_device(pdev)) - continue; + /* Remove I/O space marker in bit 0. */ + pci_irq_line = pdev->irq; + pci_ioaddr = pci_resource_start (pdev, 0); - /* Remove I/O space marker in bit 0. */ - pci_irq_line = pdev->irq; - pci_ioaddr = pci_resource_start (pdev, 0); + /* At this point we have found a valid card. */ + dev = init_trdev(NULL, 0); + if (!dev) + return -ENOMEM; + SET_MODULE_OWNER(dev); - if(check_region(pci_ioaddr, TMS_PCI_IO_EXTENT)) - continue; - - /* At this point we have found a valid card. */ - - dev = init_trdev(NULL, 0); - if (!dev) { - continue; /*return (-ENOMEM);*/ /* continue; ?? */ - } - - request_region(pci_ioaddr, TMS_PCI_IO_EXTENT, cardinfo->name); /* XXX check return */ - if(request_irq(pdev->irq, tms380tr_interrupt, SA_SHIRQ, - cardinfo->name, dev)) { - release_region(pci_ioaddr, TMS_PCI_IO_EXTENT); - /* XXX free trdev */ - continue; /*return (-ENODEV);*/ /* continue; ?? */ - } - - /* - if (load_tms380_module("tmspci.c")) { - return 0; - } - */ - - pci_ioaddr &= ~3 ; - dev->base_addr = pci_ioaddr; - dev->irq = pci_irq_line; - dev->dma = 0; - - printk("%s: %s\n", dev->name, cardinfo->name); - printk("%s: IO: %#4lx IRQ: %d\n", - dev->name, dev->base_addr, dev->irq); - /* - * Some cards have their TMS SIF registers offset from - * their given base address. Account for that here. - */ - dev->base_addr += cardinfo->registeroffset; + if (!request_region(pci_ioaddr, TMS_PCI_IO_EXTENT, dev->name)) { + ret = -EBUSY; + goto err_out_trdev; + } + + ret = request_irq(pdev->irq, tms380tr_interrupt, SA_SHIRQ, + dev->name, dev); + if (ret) + goto err_out_region; + + dev->base_addr = pci_ioaddr; + dev->irq = pci_irq_line; + dev->dma = 0; + + printk("%s: %s\n", dev->name, cardinfo->name); + printk("%s: IO: %#4lx IRQ: %d\n", + dev->name, dev->base_addr, dev->irq); - tms_pci_read_eeprom(dev); + tms_pci_read_eeprom(dev); - printk("%s: Ring Station Address: ", dev->name); - printk("%2.2x", dev->dev_addr[0]); - for (i = 1; i < 6; i++) - printk(":%2.2x", dev->dev_addr[i]); - printk("\n"); + printk("%s: Ring Station Address: ", dev->name); + printk("%2.2x", dev->dev_addr[0]); + for (i = 1; i < 6; i++) + printk(":%2.2x", dev->dev_addr[i]); + printk("\n"); - if (tmsdev_init(dev)) { - printk("%s: unable to get memory for dev->priv.\n", dev->name); - return 0; - } - - tp = (struct net_local *)dev->priv; - tp->dmalimit = 0; /* XXX: should be the max PCI32 DMA max */ - tp->setnselout = tms_pci_setnselout_pins; + ret = tmsdev_init(dev); + if (ret) { + printk("%s: unable to get memory for dev->priv.\n", dev->name); + goto err_out_irq; + } + + tp = dev->priv; + tp->dmalimit = 0; /* XXX: should be the max PCI32 DMA max */ + tp->setnselout = tms_pci_setnselout_pins; - tp->sifreadb = tms_pci_sifreadb; - tp->sifreadw = tms_pci_sifreadw; - tp->sifwriteb = tms_pci_sifwriteb; - tp->sifwritew = tms_pci_sifwritew; + tp->sifreadb = tms_pci_sifreadb; + tp->sifreadw = tms_pci_sifreadw; + tp->sifwriteb = tms_pci_sifwriteb; + tp->sifwritew = tms_pci_sifwritew; - memcpy(tp->ProductID, cardinfo->name, PROD_ID_SIZE + 1); - - tp->tmspriv = cardinfo; - - dev->open = tms_pci_open; - dev->stop = tms_pci_close; - - if (register_trdev(dev) == 0) { - /* Enlist in the card list */ - card = kmalloc(sizeof(struct tms_pci_card), GFP_KERNEL); - card->next = tms_pci_card_list; - tms_pci_card_list = card; - card->dev = dev; - card->pci_dev = pdev; - card->cardinfo = cardinfo; - } else { - printk("%s: register_trdev() returned non-zero.\n", dev->name); - kfree(dev->priv); - kfree(dev); - return -1; - } - } + memcpy(tp->ProductID, cardinfo->name, PROD_ID_SIZE + 1); + + tp->tmspriv = cardinfo; + + dev->open = tms380tr_open; + dev->stop = tms380tr_close; + + ret = register_trdev(dev); + if (!ret) + goto err_out_tmsdev; - if (tms_pci_card_list) - return 0; - return (-1); + pci_set_drvdata(pdev, dev); + return 0; + +err_out_tmsdev: + kfree(dev->priv); +err_out_irq: + free_irq(pdev->irq, dev); +err_out_region: + release_region(pci_ioaddr, TMS_PCI_IO_EXTENT); +err_out_trdev: + unregister_netdev(dev); + kfree(dev); + return ret; } /* @@ -255,11 +206,11 @@ static void tms_pci_read_eeprom(struct net_device *dev) dev->dev_addr[i] = tms_pci_sifreadw(dev, SIFINC) >> 8; } -unsigned short tms_pci_setnselout_pins(struct net_device *dev) +static unsigned short tms_pci_setnselout_pins(struct net_device *dev) { unsigned short val = 0; - struct net_local *tp = (struct net_local *)dev->priv; - struct cardinfo_table *cardinfo = (struct cardinfo_table *)tp->tmspriv; + struct net_local *tp = dev->priv; + struct card_info *cardinfo = tp->tmspriv; if(tp->DataRate == SPEED_4) val |= cardinfo->nselout[0]; /* Set 4Mbps */ @@ -268,65 +219,46 @@ unsigned short tms_pci_setnselout_pins(struct net_device *dev) return val; } -static int tms_pci_open(struct net_device *dev) -{ - tms380tr_open(dev); - MOD_INC_USE_COUNT; - return 0; -} - -static int tms_pci_close(struct net_device *dev) +static void __exit tms_pci_detach (struct pci_dev *pdev) { - tms380tr_close(dev); - MOD_DEC_USE_COUNT; - return 0; + struct net_device *dev = pci_get_drvdata(pdev); + + if (!dev) + BUG(); + unregister_netdev(dev); + release_region(dev->base_addr, TMS_PCI_IO_EXTENT); + free_irq(dev->irq, dev); + kfree(dev->priv); + kfree(dev); + pci_set_drvdata(pdev, NULL); } -#ifdef MODULE +static struct pci_driver tms_pci_driver = { + name: "tmspci", + id_table: tmspci_pci_tbl, + probe: tms_pci_attach, + remove: tms_pci_detach, +}; -int init_module(void) +static int __init tms_pci_init (void) { - /* Probe for cards. */ - if (tms_pci_probe()) { - printk(KERN_NOTICE "tmspci.c: No cards found.\n"); + int rc = pci_register_driver (&tms_pci_driver); + if (rc < 0) + return rc; + if (rc == 0) { + pci_unregister_driver (&tms_pci_driver); + return -ENODEV; } - /* lock_tms380_module(); */ - return (0); + return 0; } -void cleanup_module(void) +static void __exit tms_pci_rmmod (void) { - struct net_device *dev; - struct tms_pci_card *this_card; - - while (tms_pci_card_list) { - dev = tms_pci_card_list->dev; - - /* - * If we used a register offset, revert here. - */ - if (dev->priv) - { - struct net_local *tp; - struct cardinfo_table *cardinfo; - - tp = (struct net_local *)dev->priv; - cardinfo = (struct cardinfo_table *)tp->tmspriv; - - dev->base_addr -= cardinfo->registeroffset; - } - unregister_netdev(dev); - release_region(dev->base_addr, TMS_PCI_IO_EXTENT); - free_irq(dev->irq, dev); - kfree(dev->priv); - kfree(dev); - this_card = tms_pci_card_list; - tms_pci_card_list = this_card->next; - kfree(this_card); - } - /* unlock_tms380_module(); */ + pci_unregister_driver (&tms_pci_driver); } -#endif /* MODULE */ + +module_init(tms_pci_init); +module_exit(tms_pci_rmmod); /* diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index de4af6890a68..e91f3b0cc610 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -73,9 +73,9 @@ static int rx_copybreak = 100; #if defined(__alpha__) static int csr0 = 0x01A00000 | 0xE000; -#elif defined(__i386__) || defined(__powerpc__) || defined(__hppa__) +#elif defined(__i386__) || defined(__powerpc__) static int csr0 = 0x01A00000 | 0x8000; -#elif defined(__sparc__) +#elif defined(__sparc__) || defined(__hppa__) /* The UltraSparc PCI controllers will disconnect at every 64-byte * crossing anyways so it makes no sense to tell Tulip to burst * any more than that. diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 99ed14b8fbd5..72a8ec211b2e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -573,10 +573,10 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) res->start |= ((unsigned long) l) << 32; res->end = res->start + sz; pci_write_config_dword(dev, reg+4, ~0); - pci_read_config_dword(dev, reg+4, &tmp); + pci_read_config_dword(dev, reg+4, &sz); pci_write_config_dword(dev, reg+4, l); - if (l) - res->end = res->start + (((unsigned long) ~l) << 32); + if (sz) + res->end = res->start + (((unsigned long) ~sz) << 32); #else if (l) { printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", dev->slot_name); diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 0aa3b1670d43..155f32b22e04 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -73,6 +73,7 @@ * Richard Hirst August 2000 */ +#include #include #include diff --git a/drivers/sound/ymfpci.c b/drivers/sound/ymfpci.c index 0bd691e8238f..d66d24e240e0 100644 --- a/drivers/sound/ymfpci.c +++ b/drivers/sound/ymfpci.c @@ -2264,10 +2264,13 @@ ymf_install(struct pci_dev *pcidev, int instance, int devx) codec->inst = instance; codec->irq = pcidev->irq; codec->device_id = pcidev->device; + + pci_enable_device(pcidev); + pci_set_master(pcidev); + pci_read_config_byte(pcidev, PCI_REVISION_ID, (u8 *)&codec->rev); codec->reg_area_phys = pci_resource_start(pcidev, 0); codec->reg_area_virt = (unsigned long)ioremap(codec->reg_area_phys, 0x8000); - pci_set_master(pcidev); /* XXX KERN_INFO */ printk("ymfpci%d: %s at 0x%lx IRQ %d\n", instance, diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c index 91f75f234eb2..88112996668c 100644 --- a/drivers/usb/acm.c +++ b/drivers/usb/acm.c @@ -1,5 +1,5 @@ /* - * acm.c Version 0.16 + * acm.c Version 0.18 * * Copyright (c) 1999 Armin Fuerst * Copyright (c) 1999 Pavel Machek @@ -19,6 +19,8 @@ * v0.14 - sized down struct acm * v0.15 - fixed flow control again - characters could be lost * v0.16 - added code for modems with swapped data and control interfaces + * v0.17 - added new style probing + * v0.18 - fixed new style probing for devices with more configurations */ /* @@ -144,7 +146,7 @@ struct acm { static struct usb_driver acm_driver; static struct tty_driver acm_tty_driver; -static struct acm *acm_table[ACM_TTY_MINORS] = { NULL, /* .... */ }; +static struct acm *acm_table[ACM_TTY_MINORS]; #define ACM_READY(acm) (acm && acm->dev && acm->used) @@ -185,7 +187,7 @@ static void acm_ctrl_irq(struct urb *urb) switch (dr->request) { case ACM_IRQ_NETWORK: - + dbg("%s network", data[0] ? "connected to" : "disconnected from"); return; @@ -267,7 +269,7 @@ static void acm_softint(void *private) struct acm *acm = private; struct tty_struct *tty = acm->tty; - if (!ACM_READY(acm)) return; + if (!ACM_READY(acm)) return; if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); @@ -420,15 +422,15 @@ static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int case TIOCMBIS: newctrl |= mask; break; case TIOCMBIC: newctrl &= ~mask; break; } - - if (acm->ctrlout == newctrl) return 0; + + if (acm->ctrlout == newctrl) return 0; return acm_set_control(acm, acm->ctrlout = newctrl); } return -ENOIOCTLCMD; } -static __u32 acm_tty_speed[] = { +static __u32 acm_tty_speed[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, @@ -457,7 +459,7 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_ newline.databits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; acm->clocal = termios->c_cflag & CLOCAL; - + if (!newline.speed) { newline.speed = acm->line.speed; newctrl &= ~ACM_CTRL_DTR; @@ -484,106 +486,114 @@ static void *acm_probe(struct usb_device *dev, unsigned int ifnum, struct usb_config_descriptor *cfacm; struct usb_interface_descriptor *ifcom, *ifdata; struct usb_endpoint_descriptor *epctrl, *epread, *epwrite; - int readsize, ctrlsize, minor; + int readsize, ctrlsize, minor, i; unsigned char *buf; - /* Since 0 is treated as a wildcard by the USB pattern matching, - we explicitly check bDeviceSubClass and bDeviceProtocol - here. */ - if (dev->descriptor.bDeviceSubClass != 0 - || dev->descriptor.bDeviceProtocol != 0) return NULL; +/* + * Since 0 is treated as a wildcard by the USB pattern matching, + * we explicitly check bDeviceSubClass and bDeviceProtocol here. + */ - cfacm = dev->actconfig; + if (dev->descriptor.bDeviceSubClass != 0 || + dev->descriptor.bDeviceProtocol != 0) + return NULL; - dbg("probing config %d", cfacm->bConfigurationValue); + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { - if (cfacm->bNumInterfaces != 2 || - usb_interface_claimed(cfacm->interface + 0) || - usb_interface_claimed(cfacm->interface + 1)) - return NULL; + cfacm = dev->config + i; - ifcom = cfacm->interface[0].altsetting + 0; - ifdata = cfacm->interface[1].altsetting + 0; + dbg("probing config %d", cfacm->bConfigurationValue); - if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) { - ifcom = cfacm->interface[1].altsetting + 0; - ifdata = cfacm->interface[0].altsetting + 0; - if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints < 2) - return NULL; - } + if (cfacm->bNumInterfaces != 2 || + usb_interface_claimed(cfacm->interface + 0) || + usb_interface_claimed(cfacm->interface + 1)) + continue; - if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 || - ifcom->bInterfaceProtocol != 1 || ifcom->bNumEndpoints < 1) - return NULL; + ifcom = cfacm->interface[0].altsetting + 0; + ifdata = cfacm->interface[1].altsetting + 0; - epctrl = ifcom->endpoint + 0; - epread = ifdata->endpoint + 0; - epwrite = ifdata->endpoint + 1; + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints < 2) { + ifcom = cfacm->interface[1].altsetting + 0; + ifdata = cfacm->interface[0].altsetting + 0; + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints < 2) + continue; + } - if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3 || - (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 || - ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80) - return NULL; + if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 || + ifcom->bInterfaceProtocol != 1 || ifcom->bNumEndpoints < 1) + continue; - if ((epread->bEndpointAddress & 0x80) != 0x80) { - epread = ifdata->endpoint + 1; - epwrite = ifdata->endpoint + 0; - } + epctrl = ifcom->endpoint + 0; + epread = ifdata->endpoint + 0; + epwrite = ifdata->endpoint + 1; - usb_set_configuration(dev, cfacm->bConfigurationValue); + if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3 || + (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 || + ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80) + continue; - for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); - if (acm_table[minor]) { - err("no more free acm devices"); - return NULL; - } + if ((epread->bEndpointAddress & 0x80) != 0x80) { + epread = ifdata->endpoint + 1; + epwrite = ifdata->endpoint + 0; + } - if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { - err("out of memory"); - return NULL; - } - memset(acm, 0, sizeof(struct acm)); + usb_set_configuration(dev, cfacm->bConfigurationValue); - ctrlsize = epctrl->wMaxPacketSize; - readsize = epread->wMaxPacketSize; - acm->writesize = epwrite->wMaxPacketSize; - acm->iface = cfacm->interface; - acm->minor = minor; - acm->dev = dev; + for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); + if (acm_table[minor]) { + err("no more free acm devices"); + return NULL; + } - acm->tqueue.routine = acm_softint; - acm->tqueue.data = acm; + if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { + err("out of memory"); + return NULL; + } + memset(acm, 0, sizeof(struct acm)); - if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { - err("out of memory"); - kfree(acm); - return NULL; - } + ctrlsize = epctrl->wMaxPacketSize; + readsize = epread->wMaxPacketSize; + acm->writesize = epwrite->wMaxPacketSize; + acm->iface = cfacm->interface; + acm->minor = minor; + acm->dev = dev; - FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), - buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); + acm->tqueue.routine = acm_softint; + acm->tqueue.data = acm; - FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), - buf += ctrlsize, readsize, acm_read_bulk, acm); - acm->readurb.transfer_flags |= USB_NO_FSBR; + if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { + err("out of memory"); + kfree(acm); + return NULL; + } + + FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), + buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); - FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), - buf += readsize, acm->writesize, acm_write_bulk, acm); - acm->writeurb.transfer_flags |= USB_NO_FSBR; + FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), + buf += ctrlsize, readsize, acm_read_bulk, acm); + acm->readurb.transfer_flags |= USB_NO_FSBR; - printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor); + FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), + buf += readsize, acm->writesize, acm_write_bulk, acm); + acm->writeurb.transfer_flags |= USB_NO_FSBR; - acm_set_control(acm, acm->ctrlout); + printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor); - acm->line.speed = cpu_to_le32(9600); - acm->line.databits = 8; - acm_set_line(acm, &acm->line); + acm_set_control(acm, acm->ctrlout); - usb_driver_claim_interface(&acm_driver, acm->iface + 0, acm); - usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm); + acm->line.speed = cpu_to_le32(9600); + acm->line.databits = 8; + acm_set_line(acm, &acm->line); + + usb_driver_claim_interface(&acm_driver, acm->iface + 0, acm); + usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm); + + tty_register_devfs(&acm_tty_driver, 0, minor); + return acm_table[minor] = acm; + } - tty_register_devfs(&acm_tty_driver, 0, minor); - return acm_table[minor] = acm; + return NULL; } static void acm_disconnect(struct usb_device *dev, void *ptr) @@ -621,10 +631,9 @@ static void acm_disconnect(struct usb_device *dev, void *ptr) * USB driver structure. */ -static struct usb_device_id acm_ids [] = { - { bDeviceClass: 2}, - { bInterfaceClass: 2, bInterfaceSubClass: 2, bInterfaceProtocol: 1}, - { } /* Terminating entry */ +static struct usb_device_id acm_ids[] = { + { bDeviceClass: 2, bDeviceSubClass: 0, bDeviceProtocol: 0}, + { } }; MODULE_DEVICE_TABLE (usb, acm_ids); diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index a22694d4e42b..63b5458e5472 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -13,6 +13,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (12/03/2000) gb + * Added port->tty->ldisc.set_termios(port->tty, NULL) to empeg_open() + * This notifies the tty driver that the termios have changed. + * * (11/13/2000) gb * Moved tty->low_latency = 1 from empeg_read_bulk_callback() to empeg_open() * (It only needs to be set once - Doh!) @@ -162,6 +166,15 @@ static int empeg_open (struct usb_serial_port *port, struct file *filp) port->tty->termios->c_cflag |= CS8; + /* gb - 2000/12/03 + * + * Contributed by Borislav Deianov + * + * Notify the tty driver that the termios have changed!! + * + */ + port->tty->ldisc.set_termios(port->tty, NULL); + /* gb - 2000/11/05 * * force low_latency on diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index fcdf71e841ea..c52ad0b37200 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -12,9 +12,20 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * See http://reality.sgi.com/bryder_wellington/ftdi_sio for upto date testing info + * and extra documentation + * + * (12/3/2000) Bill Ryder + * Added support for 8U232AM device. + * Moved PID and VIDs into header file only. + * Turned on low-latency for the tty (device will do high baudrates) + * Added shutdown routine to close files when device removed. + * More debug and error message cleanups. + * + * * (11/13/2000) Bill Ryder * Added spinlock protected open code and close code. - * Multiple opens work (sort of - see webpage). + * Multiple opens work (sort of - see webpage mentioned above). * Cleaned up comments. Removed multiple PID/VID definitions. * Factorised cts/dtr code * Made use of __FUNCTION__ in dbg's @@ -34,11 +45,11 @@ * driver is a loadable module now. * * (04/04/2000) Bill Ryder - * Fixed bugs in TCGET/TCSET ioctls (by removing them - they are - * handled elsewhere in the serial driver chain). + * Fixed bugs in TCGET/TCSET ioctls (by removing them - they are + * handled elsewhere in the tty io driver chain). * * (03/30/2000) Bill Ryder - * Implemented lots of ioctls + * Implemented lots of ioctls * Fixed a race condition in write * Changed some dbg's to errs * @@ -50,6 +61,7 @@ /* Bill Ryder - bryder@sgi.com - wrote the FTDI_SIO implementation */ /* Thanx to FTDI for so kindly providing details of the protocol required */ /* to talk to the device */ +/* Thanx to gkh and the rest of the usb dev group for all code I have assimilated :-) */ #include @@ -78,20 +90,42 @@ #include "ftdi_sio.h" -#define FTDI_VENDOR_ID FTDI_VID -#define FTDI_SIO_SERIAL_CONVERTER_ID FTDI_SIO_PID -#define FTDI_8U232AM_PID 0x6001 static __devinitdata struct usb_device_id id_table_sio [] = { { idVendor: FTDI_VID, idProduct: FTDI_SIO_PID }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table_sio); +/* THe 8U232AM has the same API as the sio - but it can support MUCH + higher baudrates (921600 at 48MHz/230400 at 12MHz + so .. it's baudrate setting codes are different */ + + +static __devinitdata struct usb_device_id id_table_8U232AM [] = { + { idVendor: FTDI_VID, idProduct: FTDI_8U232AM_PID }, + { } /* Terminating entry */ +}; + + +static __devinitdata struct usb_device_id id_table_combined [] = { + { idVendor: FTDI_VID, idProduct: FTDI_SIO_PID }, + { idVendor: FTDI_VID, idProduct: FTDI_8U232AM_PID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, id_table_combined); +struct ftdi_private { + ftdi_type_t ftdi_type; + char last_status_byte; /* device sends this every 40ms when open */ + + +}; /* function prototypes for a FTDI serial converter */ static int ftdi_sio_startup (struct usb_serial *serial); +static int ftdi_8U232AM_startup (struct usb_serial *serial); +static void ftdi_sio_shutdown (struct usb_serial *serial); static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp); static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp); static int ftdi_sio_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); @@ -100,13 +134,15 @@ static void ftdi_sio_read_bulk_callback (struct urb *urb); static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * old); static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); -/* All of the device info needed for the FTDI SIO serial converter */ +/* Should rename most ftdi_sio's to ftdi_ now since there are two devices + which share common code */ + struct usb_serial_device_type ftdi_sio_device = { name: "FTDI SIO", id_table: id_table_sio, - needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */ - needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ - needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ + needs_interrupt_in: MUST_HAVE_NOT, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, num_interrupt_in: 0, num_bulk_in: 1, num_bulk_out: 1, @@ -119,17 +155,35 @@ struct usb_serial_device_type ftdi_sio_device = { ioctl: ftdi_sio_ioctl, set_termios: ftdi_sio_set_termios, startup: ftdi_sio_startup, + shutdown: ftdi_sio_shutdown, }; +struct usb_serial_device_type ftdi_8U232AM_device = { + name: "FTDI 8U232AM", + id_table: id_table_8U232AM, + needs_interrupt_in: DONT_CARE, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, + num_interrupt_in: 0, + num_bulk_in: 1, + num_bulk_out: 1, + num_ports: 1, + open: ftdi_sio_open, + close: ftdi_sio_close, + write: ftdi_sio_write, + read_bulk_callback: ftdi_sio_read_bulk_callback, + write_bulk_callback: ftdi_sio_write_bulk_callback, + ioctl: ftdi_sio_ioctl, + set_termios: ftdi_sio_set_termios, + startup: ftdi_8U232AM_startup, + shutdown: ftdi_sio_shutdown, +}; + + /* * *************************************************************************** * FTDI SIO Serial Converter specific driver functions * *************************************************************************** - * - * See the webpage http://reality.sgi.com/bryder_wellington/ftdi_sio for upto date - * testing information - * - * */ #define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ @@ -165,15 +219,59 @@ static int set_dtr(struct usb_device *dev, } -/* do some startup allocations not currently performed by usb_serial_probe() */ + static int ftdi_sio_startup (struct usb_serial *serial) { + struct ftdi_private *priv; + init_waitqueue_head(&serial->port[0].write_wait); + + priv = serial->port->private = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL); + if (!priv){ + err(__FUNCTION__"- kmalloc(%d) failed.", sizeof(struct ftdi_private)); + return -ENOMEM; + } + priv->ftdi_type = sio; return (0); } + +static int ftdi_8U232AM_startup (struct usb_serial *serial) +{ + struct ftdi_private *priv; + + init_waitqueue_head(&serial->port[0].write_wait); + + priv = serial->port->private = kmalloc(sizeof(struct ftdi_private), GFP_KERNEL); + if (!priv){ + err(__FUNCTION__"- kmalloc(%d) failed.", sizeof(struct ftdi_private)); + return -ENOMEM; + } + + priv->ftdi_type = F8U232AM; + + return (0); +} + +static void ftdi_sio_shutdown (struct usb_serial *serial) +{ + + dbg (__FUNCTION__); + + /* Close ports if they are open */ + while (serial->port[0].open_count > 0) { + ftdi_sio_close (&serial->port[0], NULL); + } + if (serial->port->private){ + kfree(serial->port->private); + serial->port->private = NULL; + } +} + + + static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp) { /* ftdi_sio_open */ struct termios tmp_termios; @@ -182,7 +280,7 @@ static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp) int result; char buf[1]; /* Needed for the usb_control_msg I think */ - dbg(__FUNCTION__ " port %d", port->number); + dbg(__FUNCTION__); spin_lock_irqsave (&port->port_lock, flags); @@ -191,29 +289,33 @@ static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp) if (!port->active){ port->active = 1; + spin_unlock_irqrestore (&port->port_lock, flags); + /* do not allow a task to be queued to deliver received data */ + port->tty->low_latency = 1; + + /* No error checking for this (will get errors later anyway) */ /* See ftdi_sio.h for description of what is reset */ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, FTDI_SIO_RESET_SIO, 0, buf, 0, WDR_TIMEOUT); - /* Setup termios */ + /* Setup termios defaults. According to tty_io.c the + settings are driver specific */ port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; /* ftdi_sio_set_termios will send usb control messages */ - /* ftdi_sio_set_termios will set up port according to above list */ - ftdi_sio_set_termios(port, &tmp_termios); - /* Turn on RTS and DTR since we are not flow controlling*/ + /* Turn on RTS and DTR since we are not flow controlling by default */ if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0) { - err("Error from DTR HIGH urb"); + err(__FUNCTION__ " Error from DTR HIGH urb"); } if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0){ - err("Error from RTS HIGH urb"); + err(__FUNCTION__ " Error from RTS HIGH urb"); } /* Start reading from the device */ @@ -224,7 +326,7 @@ static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp) result = usb_submit_urb(port->read_urb); if (result) err(__FUNCTION__ " - failed submitting read urb, error %d", result); - } else { /* the port was already active - so no initialisation was done */ + } else { /* the port was already active - so no initialisation is done */ spin_unlock_irqrestore (&port->port_lock, flags); } @@ -239,7 +341,7 @@ static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp) char buf[1]; unsigned long flags; - dbg( __FUNCTION__ " port %d", port->number); + dbg( __FUNCTION__); spin_lock_irqsave (&port->port_lock, flags); --port->open_count; @@ -271,14 +373,18 @@ static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp) usb_unlink_urb (port->read_urb); port->active = 0; port->open_count = 0; - } else { + } else { spin_unlock_irqrestore (&port->port_lock, flags); + + /* Send a HUP if necessary */ + if (!(port->tty->termios->c_cflag & CLOCAL)){ + tty_hangup(port->tty); + } + } MOD_DEC_USE_COUNT; - - } /* ftdi_sio_close */ @@ -292,7 +398,8 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) { /* ftdi_sio_write */ struct usb_serial *serial = port->serial; - const int data_offset = 1; + struct ftdi_private *priv = (struct ftdi_private *)port->private; + int data_offset ; int rc; int result; DECLARE_WAITQUEUE(wait, current); @@ -303,24 +410,25 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user, err("write request of 0 bytes"); return 0; } + + if (priv->ftdi_type == sio){ + data_offset = 1; + } else { + data_offset = 0; + } + dbg("data_offset set to %d",data_offset); /* only do something if we have a bulk out endpoint */ if (serial->num_bulk_out) { unsigned char *first_byte = port->write_urb->transfer_buffer; /* Was seeing a race here, got a read callback, then write callback before - hitting interuptible_sleep_on - so wrapping in add_wait_queue stuff */ + hitting interuptible_sleep_on - so wrapping in a wait_queue */ add_wait_queue(&port->write_wait, &wait); set_current_state (TASK_INTERRUPTIBLE); while (port->write_urb->status == -EINPROGRESS) { dbg(__FUNCTION__ " write in progress - retrying"); - if (0 /* file->f_flags & O_NONBLOCK */) { - remove_wait_queue(&port->write_wait, &wait); - set_current_state(TASK_RUNNING); - rc = -EAGAIN; - goto err; - } if (signal_pending(current)) { current->state = TASK_RUNNING; remove_wait_queue(&port->write_wait, &wait); @@ -328,7 +436,6 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user, goto err; } schedule(); - set_current_state (TASK_INTERRUPTIBLE); } remove_wait_queue(&port->write_wait, &wait); set_current_state(TASK_RUNNING); @@ -349,11 +456,13 @@ static int ftdi_sio_write (struct usb_serial_port *port, int from_user, buf, count - data_offset ); } - /* Write the control byte at the front of the packet*/ first_byte = port->write_urb->transfer_buffer; - *first_byte = 1 | ((count-data_offset) << 2) ; + if (data_offset > 0){ + /* Write the control byte at the front of the packet*/ + *first_byte = 1 | ((count-data_offset) << 2) ; + } - dbg(__FUNCTION__ "Bytes: %d, Control Byte: 0o%03o",count, first_byte[0]); + dbg(__FUNCTION__ " Bytes: %d, First Byte: 0o%03o",count, first_byte[0]); usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte); /* send the data out the bulk port */ @@ -412,6 +521,7 @@ static void ftdi_sio_write_bulk_callback (struct urb *urb) static void ftdi_sio_read_bulk_callback (struct urb *urb) { /* ftdi_sio_serial_buld_callback */ struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct ftdi_private *priv = (struct ftdi_private *)port->private; struct usb_serial *serial; struct tty_struct *tty = port->tty ; unsigned char *data = urb->transfer_buffer; @@ -443,8 +553,10 @@ static void ftdi_sio_read_bulk_callback (struct urb *urb) dbg("Just status"); } + priv->last_status_byte = data[0]; /* this has modem control lines */ + /* TO DO -- check for hung up line and handle appropriately: */ - /* send hangup (need to find out how to do this) */ + /* send hangup */ /* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */ /* if CD is dropped and the line is not CLOCAL then we should hangup */ @@ -469,6 +581,53 @@ static void ftdi_sio_read_bulk_callback (struct urb *urb) return; } /* ftdi_sio_serial_read_bulk_callback */ + +__u16 translate_baudrate_to_ftdi(unsigned int cflag, ftdi_type_t ftdi_type) +{ /* translate_baudrate_to_ftdi */ + + __u16 urb_value = ftdi_sio_b9600; + + if (ftdi_type == sio){ + switch(cflag & CBAUD){ + case B0: break; /* ignored by this */ + case B300: urb_value = ftdi_sio_b300; dbg("Set to 300"); break; + case B600: urb_value = ftdi_sio_b600; dbg("Set to 600") ; break; + case B1200: urb_value = ftdi_sio_b1200; dbg("Set to 1200") ; break; + case B2400: urb_value = ftdi_sio_b2400; dbg("Set to 2400") ; break; + case B4800: urb_value = ftdi_sio_b4800; dbg("Set to 4800") ; break; + case B9600: urb_value = ftdi_sio_b9600; dbg("Set to 9600") ; break; + case B19200: urb_value = ftdi_sio_b19200; dbg("Set to 19200") ; break; + case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break; + case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break; + case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break; + default: dbg(__FUNCTION__ " FTDI_SIO does not support the baudrate (%d) requested", + (cflag & CBAUD)); + break; + } + } else { /* it is 8U232AM */ + switch(cflag & CBAUD){ + case B0: break; /* ignored by this */ + case B300: urb_value = ftdi_8U232AM_48MHz_b300; dbg("Set to 300"); break; + case B600: urb_value = ftdi_8U232AM_48MHz_b600; dbg("Set to 600") ; break; + case B1200: urb_value = ftdi_8U232AM_48MHz_b1200; dbg("Set to 1200") ; break; + case B2400: urb_value = ftdi_8U232AM_48MHz_b2400; dbg("Set to 2400") ; break; + case B4800: urb_value = ftdi_8U232AM_48MHz_b4800; dbg("Set to 4800") ; break; + case B9600: urb_value = ftdi_8U232AM_48MHz_b9600; dbg("Set to 9600") ; break; + case B19200: urb_value = ftdi_8U232AM_48MHz_b19200; dbg("Set to 19200") ; break; + case B38400: urb_value = ftdi_8U232AM_48MHz_b38400; dbg("Set to 38400") ; break; + case B57600: urb_value = ftdi_8U232AM_48MHz_b57600; dbg("Set to 57600") ; break; + case B115200: urb_value = ftdi_8U232AM_48MHz_b115200; dbg("Set to 115200") ; break; + case B230400: urb_value = ftdi_8U232AM_48MHz_b230400; dbg("Set to 230400") ; break; + case B460800: urb_value = ftdi_8U232AM_48MHz_b460800; dbg("Set to 460800") ; break; + case B921600: urb_value = ftdi_8U232AM_48MHz_b921600; dbg("Set to 921600") ; break; + default: dbg(__FUNCTION__ " The baudrate (%d) requested is not implemented", + (cflag & CBAUD)); + break; + } + } + return(urb_value); +} + /* As I understand this - old_termios contains the original termios settings */ /* and tty->termios contains the new setting to be used */ /* */ @@ -478,10 +637,12 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * { /* ftdi_sio_set_termios */ struct usb_serial *serial = port->serial; unsigned int cflag = port->tty->termios->c_cflag; + struct ftdi_private *priv = (struct ftdi_private *)port->private; __u16 urb_value; /* will hold the new flags */ char buf[1]; /* Perhaps I should dynamically alloc this? */ - dbg(__FUNCTION__ " port %d", port->number); + + dbg(__FUNCTION__); /* FIXME -For this cut I don't care if the line is really changing or @@ -515,26 +676,12 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * FTDI_SIO_SET_DATA_REQUEST_TYPE, urb_value , 0, buf, 0, 100) < 0) { - err("FAILED to set databits/stopbits/parity"); + err(__FUNCTION__ " FAILED to set databits/stopbits/parity"); } /* Now do the baudrate */ - - switch(cflag & CBAUD){ - case B0: break; /* Handled below */ - case B300: urb_value = ftdi_sio_b300; dbg("Set to 300"); break; - case B600: urb_value = ftdi_sio_b600; dbg("Set to 600") ; break; - case B1200: urb_value = ftdi_sio_b1200; dbg("Set to 1200") ; break; - case B2400: urb_value = ftdi_sio_b2400; dbg("Set to 2400") ; break; - case B4800: urb_value = ftdi_sio_b4800; dbg("Set to 4800") ; break; - case B9600: urb_value = ftdi_sio_b9600; dbg("Set to 9600") ; break; - case B19200: urb_value = ftdi_sio_b19200; dbg("Set to 19200") ; break; - case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break; - case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break; - case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break; - default: dbg(__FUNCTION__ "FTDI_SIO does not support the baudrate requested"); - /* FIXME - how to return an error for this? */ break; - } + urb_value = translate_baudrate_to_ftdi((cflag & CBAUD), priv->ftdi_type); + if ((cflag & CBAUD) == B0 ) { /* Disable flow control */ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), @@ -542,30 +689,31 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, 0, 0, buf, 0, WDR_TIMEOUT) < 0) { - err("error from disable flowcontrol urb"); + err(__FUNCTION__ " error from disable flowcontrol urb"); } /* Drop RTS and DTR */ if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){ - err("Error from DTR LOW urb"); + err(__FUNCTION__ " Error from DTR LOW urb"); } if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){ - err("Error from RTS LOW urb"); + err(__FUNCTION__ " Error from RTS LOW urb"); } } else { + /* set the baudrate determined before */ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), FTDI_SIO_SET_BAUDRATE_REQUEST, FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, urb_value, 0, buf, 0, 100) < 0) { - err("urb failed to set baurdrate"); + err(__FUNCTION__ " urb failed to set baurdrate"); } } /* Set flow control */ /* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */ if (cflag & CRTSCTS) { - dbg(__FUNCTION__ "Setting to CRTSCTS flow control"); + dbg(__FUNCTION__ " Setting to CRTSCTS flow control"); if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), FTDI_SIO_SET_FLOW_CTRL_REQUEST, @@ -576,9 +724,8 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * } } else { - /* CHECK Assuming XON/XOFF handled by stack - not by device */ - /* Disable flow control */ - dbg(__FUNCTION__ "Turning off hardware flow control"); + /* CHECKME Assuming XON/XOFF handled by tty stack - not by device */ + dbg(__FUNCTION__ " Turning off hardware flow control"); if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), FTDI_SIO_SET_FLOW_CTRL_REQUEST, @@ -595,6 +742,7 @@ static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) { struct usb_serial *serial = port->serial; + struct ftdi_private *priv = (struct ftdi_private *)port->private; __u16 urb_value=0; /* Will hold the new flags */ char buf[1]; int ret, mask; @@ -605,16 +753,27 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns switch (cmd) { case TIOCMGET: - dbg(__FUNCTION__ "TIOCMGET"); - /* Request the status from the device */ - if ((ret = usb_control_msg(serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - FTDI_SIO_GET_MODEM_STATUS_REQUEST, - FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, - 0, 0, - buf, 1, HZ * 5)) < 0 ) { - dbg(__FUNCTION__ "Get not get modem status of device"); - return(ret); + dbg(__FUNCTION__ " TIOCMGET"); + /* The MODEM_STATUS_REQUEST works for the sio but not the 232 */ + if (priv->ftdi_type == sio){ + /* TO DECIDE - use the 40ms status packets or not? */ + /* PRO: No need to send urb */ + /* CON: Could be 40ms out of date */ + + /* Request the status from the device */ + if ((ret = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + FTDI_SIO_GET_MODEM_STATUS_REQUEST, + FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, + 0, 0, + buf, 1, WDR_TIMEOUT)) < 0 ) { + err(__FUNCTION__ " Could not get modem status of device - err: %d", + ret); + return(ret); + } + } else { + /* This gets updated every 40ms - so just copy it in */ + buf[0] = priv->last_status_byte; } return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) | @@ -625,32 +784,20 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns break; case TIOCMSET: /* Turns on and off the lines as specified by the mask */ - dbg(__FUNCTION__ "TIOCMSET"); + dbg(__FUNCTION__ " TIOCMSET"); if ((ret = get_user(mask, (unsigned long *) arg))) return ret; - urb_value = ((mask & TIOCM_DTR) ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW); - if ((ret = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - urb_value , 0, - buf, 0, WDR_TIMEOUT)) < 0){ - err("Urb to set DTR failed"); - return(ret); - } - urb_value = ((mask & TIOCM_RTS) ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW); - if ((ret = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_MODEM_CTRL_REQUEST, - FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - urb_value , 0, - buf, 0, WDR_TIMEOUT)) < 0){ - err("Urb to set RTS failed"); - return(ret); + urb_value = ((mask & TIOCM_DTR) ? HIGH : LOW); + if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),urb_value) < 0){ + err("Error from DTR set urb (TIOCMSET)"); } + urb_value = ((mask & TIOCM_RTS) ? HIGH : LOW); + if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),urb_value) < 0){ + err("Error from RTS set urb (TIOCMSET)"); + } break; case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */ - dbg(__FUNCTION__ "TIOCMBIS"); + dbg(__FUNCTION__ " TIOCMBIS"); if ((ret = get_user(mask, (unsigned long *) arg))) return ret; if (mask & TIOCM_DTR){ if ((ret = set_dtr(serial->dev, @@ -671,7 +818,7 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns break; case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */ - dbg(__FUNCTION__ "TIOCMBIC"); + dbg(__FUNCTION__ " TIOCMBIC"); if ((ret = get_user(mask, (unsigned long *) arg))) return ret; if (mask & TIOCM_DTR){ if ((ret = set_dtr(serial->dev, @@ -704,25 +851,28 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns /* This is not an error - turns out the higher layers will do * some ioctls itself (see comment above) */ - dbg(__FUNCTION__ "arg not supported - it was 0x%04x",cmd); + dbg(__FUNCTION__ " arg not supported - it was 0x%04x",cmd); return(-ENOIOCTLCMD); break; } - dbg(__FUNCTION__ " returning 0"); return 0; } /* ftdi_sio_ioctl */ static int __init ftdi_sio_init (void) { + dbg(__FUNCTION__); usb_serial_register (&ftdi_sio_device); + usb_serial_register (&ftdi_8U232AM_device); return 0; } static void __exit ftdi_sio_exit (void) { + dbg(__FUNCTION__); usb_serial_deregister (&ftdi_sio_device); + usb_serial_deregister (&ftdi_8U232AM_device); } @@ -730,4 +880,4 @@ module_init(ftdi_sio_init); module_exit(ftdi_sio_exit); MODULE_AUTHOR("Greg Kroah-Hartman , Bill Ryder "); -MODULE_DESCRIPTION("USB FTDI SIO driver"); +MODULE_DESCRIPTION("USB FTDI RS232 converters driver"); diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index fe5f545db327..626aeb677315 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -20,9 +20,9 @@ */ #define FTDI_VID 0x0403 /* Vendor Id */ -#define FTDI_SIO_PID 0x8372 /* Product Id */ +#define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */ +#define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */ -/* Vendor Request Interface */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ #define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ @@ -85,6 +85,12 @@ * Data: None */ +typedef enum { + sio = 1, + F8U232AM = 2, +} ftdi_type_t; + + typedef enum { ftdi_sio_b300 = 0, ftdi_sio_b600 = 1, @@ -98,6 +104,38 @@ typedef enum { ftdi_sio_b115200 = 9 } FTDI_SIO_baudrate_t ; + +typedef enum { + ftdi_8U232AM_12MHz_b300 = 0x09c4, + ftdi_8U232AM_12MHz_b600 = 0x04E2, + ftdi_8U232AM_12MHz_b1200 = 0x0271, + ftdi_8U232AM_12MHz_b2400 = 0x4138, + ftdi_8U232AM_12MHz_b4800 = 0x809c, + ftdi_8U232AM_12MHz_b9600 = 0xc04e, + ftdi_8U232AM_12MHz_b19200 = 0x0027, + ftdi_8U232AM_12MHz_b38400 = 0x4013, + ftdi_8U232AM_12MHz_b57600 = 0x000d, + ftdi_8U232AM_12MHz_b115200 = 0x4006, + ftdi_8U232AM_12MHz_b230400 = 0x8003, +} FTDI_8U232AM_12MHz_baudrate_t; +/* Apparently all devices are 48MHz */ +typedef enum { + ftdi_8U232AM_48MHz_b300 = 0x2710, + ftdi_8U232AM_48MHz_b600 = 0x1388, + ftdi_8U232AM_48MHz_b1200 = 0x09c4, + ftdi_8U232AM_48MHz_b2400 = 0x04e2, + ftdi_8U232AM_48MHz_b4800 = 0x0271, + ftdi_8U232AM_48MHz_b9600 = 0x4138, + ftdi_8U232AM_48MHz_b19200 = 0x809c, + ftdi_8U232AM_48MHz_b38400 = 0xc04e, + ftdi_8U232AM_48MHz_b57600 = 0x0034, + ftdi_8U232AM_48MHz_b115200 = 0x001a, + ftdi_8U232AM_48MHz_b230400 = 0x000d, + ftdi_8U232AM_48MHz_b460800 = 0x4006, + ftdi_8U232AM_48MHz_b921600 = 0x8003, + +} FTDI_8U232AM_48MHz_baudrate_t; + #define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA #define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 #define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8 ) diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index ec039aaa24db..12a69c1e7c00 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -25,6 +25,7 @@ * - working around the horridness of the rest */ +#include #include #include #include diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 7fc5e5cb32f2..e97f458cf739 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -88,7 +88,7 @@ int usb_register(struct usb_driver *new_driver) init_MUTEX(&new_driver->serialize); /* Add it to the list of known drivers */ - list_add(&new_driver->driver_list, &usb_driver_list); + list_add_tail(&new_driver->driver_list, &usb_driver_list); usb_scan_devices(); @@ -560,7 +560,7 @@ usb_match_id(struct usb_device *dev, struct usb_interface *interface, continue; if (id->bDeviceSubClass && - id->bDeviceSubClass!= dev->descriptor.bDeviceClass) + id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass) continue; if (id->bDeviceProtocol && diff --git a/drivers/video/dummycon.c b/drivers/video/dummycon.c index 3e08ebcb2df2..f7a09a5d093b 100644 --- a/drivers/video/dummycon.c +++ b/drivers/video/dummycon.c @@ -20,6 +20,9 @@ #if defined(__arm__) #define DUMMY_COLUMNS ORIG_VIDEO_COLS #define DUMMY_ROWS ORIG_VIDEO_LINES +#elif defined(__hppa__) +#define DUMMY_COLUMNS 80 /* fixme ! (mine uses 160x64 at 1280x1024) */ +#define DUMMY_ROWS 25 #else #define DUMMY_COLUMNS 80 #define DUMMY_ROWS 25 diff --git a/drivers/video/fbcon-sti.c b/drivers/video/fbcon-sti.c new file mode 100644 index 000000000000..174ad05c4723 --- /dev/null +++ b/drivers/video/fbcon-sti.c @@ -0,0 +1,330 @@ +/* + * linux/drivers/video/fbcon-sti.c -- Low level frame buffer + * operations for generic HP video boards using STI (standard + * text interface) firmware + * + * Based on linux/drivers/video/fbcon-artist.c + * Created 5 Apr 1997 by Geert Uytterhoeven + * Copyright (C) 2000 Philipp Rumpf + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. */ + +#include +#include +#include +#include +#include +#include +#include + +#include