From be5f7d9266a6093de043019622b95e1a35fe56a1 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:22:37 -0500 Subject: [PATCH] Linux 2.2.18pre16 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit o Finally get the m68k tree merged (Andrew McPherson and a cast of many) o Bring the sparc back in line, make it build (Anton Blanchard) o USB Bluetooth fixes/docs (Greg Kroah-Hartman) o Fix auth_null credentials bug (Hai-Pao Fan) o Update cpu flag names (Dave Jones) o Console 'quiet' boot option as in 2.4 (Rusty Russell) o Make the sx serial driver work again (Patrick van de Lageweg) o Fix negotation on the SYM53C1010 (Gerard Roudier) o Fix alpha loops per jiffy (Jay Estabrook) o Fix pegasus to work with 2.2 kernels (Greg Kroah-Hartman) o Update plusb driver for 2.2.x (Eric Ayers, Deti Fliegl) o Fix ohci to use __init (Greg Kroah-Hartman) o /sbin/hotplug support for USB as in 2.4 (Greg Kroah-Hartman) o Update ksymoops url (Keith Owens) o Update the changes doc about gcc (Petri Kaukasoina) o Fix AMD flag naming (Ulrich Windl) o Restore old block size on devices after a partition scan (needed for powermac for one) (Michael Schmitz) o Fix GPL naming in SubmittingDrivers (Mike Harris) o NFSv3 server patches merge (Dave Higgen) o CS46xx changes (Nils Faerber) o Fix sys_nanosleep for >4GHz CPU changes (Alan Cox) (Spotted by Ben Herrenschmidt) o Fix pas rev D mixer (??) o Fix multiple spelling errors (André Dahlqvist) o ISDN updates (Kai Germaschewski) o XSpeed DSL driver (Timothy Lee, Dan Hollis) o IDE multi-lun/single-lun handling (Jens Axboe) o Fix alpha generic trident sound support (Rich Payne) o Fix PPC for loops per jiffy (Cort Dougan) --- Documentation/Changes | 19 +- Documentation/Configure.help | 172 +- Documentation/IO-mapping.txt | 2 +- Documentation/SubmittingDrivers | 10 +- Documentation/arm/nwfpe/README | 2 +- Documentation/cdrom/cdrom-standard.tex | 2 +- Documentation/cdrom/ide-cd | 2 +- Documentation/computone.txt | 8 +- Documentation/cpqarray.txt | 2 +- Documentation/fb/vesafb.txt | 2 +- Documentation/filesystems/affs.txt | 2 +- Documentation/filesystems/coda.txt | 4 +- Documentation/filesystems/fat_cvf.txt | 2 +- Documentation/filesystems/ufs.txt | 2 +- Documentation/isdn/00-INDEX | 2 +- Documentation/isdn/INTERFACE.fax | 4 +- Documentation/isdn/README | 2 +- Documentation/isdn/README.HiSax | 6 +- Documentation/isdn/README.sc | 8 +- Documentation/joystick-api.txt | 6 +- Documentation/joystick-parport.txt | 2 +- Documentation/kbuild/commands.txt | 2 +- Documentation/kbuild/config-language.txt | 4 +- Documentation/moxa-smartio | 16 +- Documentation/networking/README.sb1000 | 2 +- Documentation/networking/README.xpeed | 52 + Documentation/networking/comx.txt | 2 +- Documentation/networking/dmfe.txt | 6 +- Documentation/networking/olympic.txt | 6 +- Documentation/networking/shaper.txt | 2 +- Documentation/networking/sis900.txt | 2 +- Documentation/networking/sk98lin.txt | 8 +- Documentation/networking/tlan.txt | 2 +- Documentation/networking/wan-router.txt | 4 +- Documentation/networking/wanpipe.txt | 8 +- Documentation/s390/DASD | 4 +- Documentation/s390/cds.txt | 2 +- Documentation/sound/ESS | 2 +- Documentation/sound/Introduction | 4 +- Documentation/sound/Maestro | 2 +- Documentation/sound/PSS | 2 +- Documentation/sound/README.OSS | 8 +- Documentation/sound/README.modules | 2 +- Documentation/stallion.txt | 4 +- Documentation/sysctl/vm.txt | 8 +- Documentation/usb/bluetooth.txt | 44 + Documentation/video4linux/CQcam.txt | 2 +- Documentation/video4linux/README.cpia | 2 +- Documentation/video4linux/radiotrack.txt | 2 +- Makefile | 2 +- arch/alpha/kernel/core_mcpcia.c | 2 +- arch/alpha/kernel/core_tsunami.c | 2 +- arch/alpha/kernel/smp.c | 6 +- arch/i386/kernel/setup.c | 17 +- arch/m68k/Makefile | 20 +- arch/m68k/bvme6000/config.c | 46 +- arch/m68k/config.in | 32 +- arch/m68k/fpsp040/bindec.S | 2 +- arch/m68k/fpsp040/decbin.S | 6 +- arch/m68k/fpsp040/do_func.S | 2 +- arch/m68k/fpsp040/get_op.S | 2 +- arch/m68k/fpsp040/skeleton.S | 2 +- arch/m68k/fpsp040/util.S | 8 +- arch/m68k/fpsp040/x_store.S | 2 +- arch/m68k/kernel/entry.S | 26 +- arch/m68k/kernel/head.S | 531 +-- arch/m68k/kernel/kgdb.c | 168 + arch/m68k/kernel/m68k_defs.c | 16 +- arch/m68k/kernel/m68k_defs.h | 15 +- arch/m68k/kernel/m68k_ksyms.c | 11 + arch/m68k/kernel/process.c | 67 +- arch/m68k/kernel/ptrace.c | 24 +- arch/m68k/kernel/setup.c | 16 + arch/m68k/kernel/signal.c | 98 +- arch/m68k/kernel/traps.c | 259 +- arch/m68k/lib/Makefile | 2 +- arch/m68k/lib/ashldi3.c | 62 + arch/m68k/lib/lshrdi3.c | 62 + arch/m68k/mac/Makefile | 6 +- arch/m68k/mac/adb-bus.c | 622 ++-- arch/m68k/mac/adb-misc.c | 145 + arch/m68k/mac/bootparse.c | 1 + arch/m68k/mac/config.c | 528 ++- arch/m68k/mac/debug.c | 12 +- arch/m68k/mac/iop.c | 720 ++++ arch/m68k/mac/mac_ksyms.c | 7 + arch/m68k/mac/mac_penguin.S | 75 + arch/m68k/mac/macboing.c | 32 +- arch/m68k/mac/macints.c | 1882 +++------- arch/m68k/mac/mackeyb.c | 292 +- arch/m68k/mac/oss.c | 314 ++ arch/m68k/mac/psc.c | 200 ++ arch/m68k/mac/via.c | 788 +++++ arch/m68k/mac/via6522.c | 419 --- arch/m68k/mac/via6522.h | 131 - arch/m68k/math-emu/Makefile | 19 + arch/m68k/math-emu/fp_arith.c | 700 ++++ arch/m68k/math-emu/fp_arith.h | 52 + arch/m68k/math-emu/fp_cond.S | 334 ++ arch/m68k/math-emu/fp_decode.h | 417 +++ arch/m68k/math-emu/fp_emu.h | 137 + arch/m68k/math-emu/fp_entry.S | 324 ++ arch/m68k/math-emu/fp_log.c | 142 + arch/m68k/math-emu/fp_move.S | 244 ++ arch/m68k/math-emu/fp_movem.S | 368 ++ arch/m68k/math-emu/fp_scan.S | 478 +++ arch/m68k/math-emu/fp_trig.c | 183 + arch/m68k/math-emu/fp_trig.h | 32 + arch/m68k/math-emu/fp_util.S | 1454 ++++++++ arch/m68k/math-emu/multi_arith.h | 822 +++++ arch/m68k/mm/fault.c | 150 +- arch/m68k/mm/init.c | 9 +- arch/m68k/mm/kmap.c | 21 +- arch/m68k/mvme147/config.c | 9 + arch/m68k/mvme16x/Makefile | 2 +- arch/m68k/mvme16x/config.c | 53 +- arch/m68k/mvme16x/mvme16x_ksyms.c | 6 + arch/ppc/kernel/chrp_setup.c | 4 +- arch/ppc/kernel/gemini_setup.c | 5 +- arch/ppc/kernel/mbx_setup.c | 3 +- arch/ppc/kernel/pmac_setup.c | 6 +- arch/ppc/kernel/prep_setup.c | 4 +- arch/ppc/kernel/setup.c | 8 +- arch/ppc/kernel/smp.c | 2 +- arch/sparc/defconfig | 9 + arch/sparc/kernel/entry.S | 10 +- arch/sparc/kernel/setup.c | 4 +- arch/sparc/kernel/smp.c | 7 +- arch/sparc/kernel/sun4d_smp.c | 6 +- arch/sparc/kernel/sun4m_smp.c | 4 +- arch/sparc64/defconfig | 2 + arch/sparc64/kernel/setup.c | 4 +- arch/sparc64/kernel/smp.c | 10 +- drivers/Makefile | 2 +- drivers/block/acsi.c | 33 +- drivers/block/acsi_slm.c | 6 +- drivers/block/buddha.c | 168 + drivers/block/falconide.c | 72 + drivers/block/gayle.c | 169 + drivers/block/ide.c | 8 +- drivers/block/macide.c | 122 + drivers/block/q40ide.c | 109 + drivers/block/swim_iop.c | 674 ++++ drivers/block/z2ram.c | 26 +- drivers/char/amiga_ser.c | 558 +++ drivers/char/amikeyb.c | 7 +- drivers/char/atari_MFPser.c | 946 +++++ drivers/char/atari_MFPser.h | 131 + drivers/char/atari_MIDI.c | 567 +++ drivers/char/atari_MIDI.h | 32 + drivers/char/atari_SCC.README | 194 ++ drivers/char/atari_SCC.c | 2624 ++++++++++++++ drivers/char/atari_SCC.h | 616 ++++ drivers/char/lp_mfc.c | 174 + drivers/char/m68kserial.c | 1861 ++++++++++ drivers/char/mac_SCC.c | 37 +- drivers/char/q40_keyb.c | 471 +++ drivers/char/ser_hpdca.c | 997 ++++++ drivers/char/ser_hpdca.h | 91 + drivers/char/ser_hypercom1.c | 658 ++++ drivers/char/ser_hypercom1.h | 172 + drivers/char/ser_mfc.c | 726 ++++ drivers/char/ser_whippet.c | 837 +++++ drivers/char/ser_whippet.h | 130 + drivers/char/serial167.c | 53 +- drivers/char/sx.c | 1 + drivers/isdn/act2000/module.c | 4 +- drivers/isdn/icn/icn.c | 8 +- drivers/isdn/isdn_audio.c | 37 +- drivers/isdn/isdn_common.c | 43 +- drivers/isdn/isdn_common.h | 2 +- drivers/isdn/isdn_net.c | 18 +- drivers/isdn/isdn_net.h | 2 +- drivers/isdn/isdn_tty.c | 22 +- drivers/isdn/isdn_tty.h | 2 +- drivers/net/Config.in | 6 + drivers/net/Makefile | 46 + drivers/net/ariadne.c | 4 +- drivers/net/ariadne2.c | 3 + drivers/net/atari_bionet.c | 2 +- drivers/net/atari_pamsnet.c | 6 +- drivers/net/daynaport.c | 451 ++- drivers/net/mac8390.c | 612 ++++ drivers/net/mac89x0.c | 679 ++++ drivers/net/macmace.c | 793 +++++ drivers/net/macsonic.c | 624 ++++ drivers/net/mvme147.c | 209 ++ drivers/net/xpds/Makefile | 23 + drivers/net/xpds/xpds-encap-fr.c | 751 ++++ drivers/net/xpds/xpds-encap-fr.h | 105 + drivers/net/xpds/xpds-fsm.c | 790 +++++ drivers/net/xpds/xpds-fsm.h | 12 + drivers/net/xpds/xpds-reg.h | 220 ++ drivers/net/xpds/xpds-sdsl.c | 965 ++++++ drivers/net/xpds/xpds-sdsl.h | 122 + drivers/net/xpds/xpds-softnet.h | 44 + drivers/net/xpds/xpds.c | 4027 ++++++++++++++++++++++ drivers/net/xpds/xpds.h | 121 + drivers/nubus/Makefile | 14 +- drivers/nubus/nubus.c | 1153 ++++--- drivers/nubus/nubus_syms.c | 27 + drivers/nubus/proc.c | 183 + drivers/scsi/a2091.c | 125 +- drivers/scsi/a3000.c | 20 +- drivers/scsi/amiga7xx.c | 38 +- drivers/scsi/atari_NCR5380.c | 12 +- drivers/scsi/atari_scsi.c | 146 +- drivers/scsi/gvp11.c | 99 +- drivers/scsi/ide-scsi.c | 26 +- drivers/scsi/mac_NCR5380.c | 3139 ----------------- drivers/scsi/mac_esp.c | 39 +- drivers/scsi/mac_esp.h | 4 +- drivers/scsi/mac_scsi.c | 1245 ++----- drivers/scsi/mac_scsi.h | 335 +- drivers/scsi/oktagon_esp.c | 598 ++++ drivers/scsi/oktagon_esp.h | 57 + drivers/scsi/oktagon_io.S | 195 ++ drivers/scsi/sun3x_esp.c | 290 ++ drivers/scsi/sun3x_esp.h | 41 + drivers/scsi/sym53c8xx_defs.h | 4 +- drivers/scsi/wd33c93.c | 9 + drivers/scsi/wd33c93.h | 2 +- drivers/sound/cs46xx.c | 78 +- drivers/sound/pas2_mixer.c | 2 +- drivers/sound/trident.c | 28 +- drivers/usb/Config.in | 1 + drivers/usb/audio.c | 8 +- drivers/usb/bluetooth.c | 79 +- drivers/usb/pegasus.c | 946 +++-- drivers/usb/plusb.c | 77 +- drivers/usb/usb-core.c | 33 +- drivers/usb/usb-ohci.c | 19 +- drivers/usb/usb.c | 354 +- drivers/video/Config.in | 2 + drivers/video/Makefile | 2 +- drivers/video/cyberfb.c | 1234 ++++--- drivers/video/cyberfb.h | 45 +- drivers/video/fbcon-mac.c | 3 +- drivers/video/macfb.c | 1372 ++++++-- drivers/video/q40fb.c | 32 +- drivers/video/retz3fb.c | 101 +- fs/Config.in | 14 +- fs/ext2/ialloc.c | 8 +- fs/ext2/inode.c | 25 +- fs/ext2/ioctl.c | 5 +- fs/lockd/Makefile | 8 +- fs/lockd/lockd_syms.c | 14 + fs/lockd/svc.c | 19 +- fs/lockd/svc4proc.c | 561 +++ fs/lockd/svclock.c | 46 +- fs/lockd/svcproc.c | 54 +- fs/lockd/svcshare.c | 11 +- fs/lockd/svcsubs.c | 8 +- fs/lockd/xdr.c | 14 +- fs/lockd/xdr4.c | 16 +- fs/nfs/inode.c | 6 +- fs/nfs/nfsroot.c | 9 +- fs/nfsd/Makefile | 3 + fs/nfsd/export.c | 39 +- fs/nfsd/lockd.c | 14 +- fs/nfsd/nfs3proc.c | 623 ++-- fs/nfsd/nfs3xdr.c | 339 +- fs/nfsd/nfscache.c | 18 +- fs/nfsd/nfsctl.c | 2 - fs/nfsd/nfsfh.c | 1654 +++------ fs/nfsd/nfsproc.c | 68 +- fs/nfsd/nfssvc.c | 98 +- fs/nfsd/nfsxdr.c | 47 +- fs/nfsd/stats.c | 11 +- fs/nfsd/vfs.c | 867 +++-- fs/super.c | 2 + include/asm-i386/adb_iop.h | 44 + include/asm-i386/m68kserial.h | 454 +++ include/asm-i386/mac_iop.h | 162 + include/asm-i386/mac_oss.h | 94 + include/asm-i386/mac_via.h | 300 ++ include/asm-i386/math-emu.h | 300 ++ include/asm-i386/swim_iop.h | 221 ++ include/asm-m68k/adb.h | 2 +- include/asm-m68k/bootinfo.h | 24 + include/asm-m68k/fpu.h | 2 + include/asm-m68k/ide.h | 12 +- include/asm-m68k/init.h | 2 +- include/asm-m68k/irq.h | 10 +- include/asm-m68k/keyboard.h | 21 + include/asm-m68k/linux_logo.h | 5 + include/asm-m68k/mac_psc.h | 242 +- include/asm-m68k/machdep.h | 2 + include/asm-m68k/machw.h | 61 +- include/asm-m68k/macintosh.h | 6 +- include/asm-m68k/macints.h | 124 +- include/asm-m68k/page.h | 10 +- include/asm-m68k/setup.h | 13 + include/asm-m68k/system.h | 15 +- include/asm-m68k/uaccess.h | 14 +- include/asm-ppc/delay.h | 8 +- include/asm-ppc/smp.h | 2 +- include/asm-sparc/delay.h | 4 +- include/asm-sparc64/delay.h | 8 +- include/linux/16c552.h | 181 + include/linux/dcache.h | 6 + include/linux/ext2_fs.h | 2 +- include/linux/ext2_fs_i.h | 2 +- include/linux/fs.h | 9 + include/linux/hdreg.h | 2 +- include/linux/kcomp.h | 18 + include/linux/kmod.h | 3 + include/linux/lockd/debug.h | 1 + include/linux/lockd/lockd.h | 2 + include/linux/lockd/xdr4.h | 43 + include/linux/m68kserial.h | 16 + include/linux/mc6821.h | 51 + include/linux/nfsd/cache.h | 4 +- include/linux/nfsd/const.h | 58 +- include/linux/nfsd/export.h | 17 +- include/linux/nfsd/nfsd.h | 38 +- include/linux/nfsd/nfsfh.h | 128 +- include/linux/nfsd/stats.h | 7 +- include/linux/nfsd/syscall.h | 11 +- include/linux/nfsd/xdr3.h | 116 +- include/linux/nubus.h | 366 +- include/linux/sunrpc/auth.h | 1 + include/linux/sysctl.h | 1 + include/linux/usb.h | 5 +- include/linux/xpds-ioctl.h | 55 + init/main.c | 4 + kernel/kmod.c | 23 + kernel/ksyms.c | 3 + kernel/sched.c | 10 +- kernel/sysctl.c | 7 + net/core/dev.c | 4 + net/sunrpc/auth.c | 6 +- net/sunrpc/auth_null.c | 1 + net/sunrpc/svc.c | 7 +- scripts/ksymoops/ksymoops.c | 2 +- 335 files changed, 45870 insertions(+), 12538 deletions(-) create mode 100644 Documentation/networking/README.xpeed create mode 100644 Documentation/usb/bluetooth.txt create mode 100644 arch/m68k/lib/ashldi3.c create mode 100644 arch/m68k/lib/lshrdi3.c create mode 100644 arch/m68k/mac/adb-misc.c create mode 100644 arch/m68k/mac/iop.c create mode 100644 arch/m68k/mac/mac_penguin.S create mode 100644 arch/m68k/mac/oss.c create mode 100644 arch/m68k/mac/psc.c create mode 100644 arch/m68k/mac/via.c delete mode 100644 arch/m68k/mac/via6522.c delete mode 100644 arch/m68k/mac/via6522.h create mode 100644 arch/m68k/math-emu/Makefile create mode 100644 arch/m68k/math-emu/fp_arith.c create mode 100644 arch/m68k/math-emu/fp_arith.h create mode 100644 arch/m68k/math-emu/fp_cond.S create mode 100644 arch/m68k/math-emu/fp_decode.h create mode 100644 arch/m68k/math-emu/fp_emu.h create mode 100644 arch/m68k/math-emu/fp_entry.S create mode 100644 arch/m68k/math-emu/fp_log.c create mode 100644 arch/m68k/math-emu/fp_move.S create mode 100644 arch/m68k/math-emu/fp_movem.S create mode 100644 arch/m68k/math-emu/fp_scan.S create mode 100644 arch/m68k/math-emu/fp_trig.c create mode 100644 arch/m68k/math-emu/fp_trig.h create mode 100644 arch/m68k/math-emu/fp_util.S create mode 100644 arch/m68k/math-emu/multi_arith.h create mode 100644 arch/m68k/mvme16x/mvme16x_ksyms.c create mode 100644 drivers/block/buddha.c create mode 100644 drivers/block/falconide.c create mode 100644 drivers/block/gayle.c create mode 100644 drivers/block/macide.c create mode 100644 drivers/block/q40ide.c create mode 100644 drivers/block/swim_iop.c create mode 100644 drivers/char/amiga_ser.c create mode 100644 drivers/char/atari_MFPser.c create mode 100644 drivers/char/atari_MFPser.h create mode 100644 drivers/char/atari_MIDI.c create mode 100644 drivers/char/atari_MIDI.h create mode 100644 drivers/char/atari_SCC.README create mode 100644 drivers/char/atari_SCC.c create mode 100644 drivers/char/atari_SCC.h create mode 100644 drivers/char/lp_mfc.c create mode 100644 drivers/char/m68kserial.c create mode 100644 drivers/char/q40_keyb.c create mode 100644 drivers/char/ser_hpdca.c create mode 100644 drivers/char/ser_hpdca.h create mode 100644 drivers/char/ser_hypercom1.c create mode 100644 drivers/char/ser_hypercom1.h create mode 100644 drivers/char/ser_mfc.c create mode 100644 drivers/char/ser_whippet.c create mode 100644 drivers/char/ser_whippet.h create mode 100644 drivers/net/mac8390.c create mode 100644 drivers/net/mac89x0.c create mode 100644 drivers/net/macmace.c create mode 100644 drivers/net/macsonic.c create mode 100644 drivers/net/mvme147.c create mode 100644 drivers/net/xpds/Makefile create mode 100644 drivers/net/xpds/xpds-encap-fr.c create mode 100644 drivers/net/xpds/xpds-encap-fr.h create mode 100644 drivers/net/xpds/xpds-fsm.c create mode 100644 drivers/net/xpds/xpds-fsm.h create mode 100644 drivers/net/xpds/xpds-reg.h create mode 100644 drivers/net/xpds/xpds-sdsl.c create mode 100644 drivers/net/xpds/xpds-sdsl.h create mode 100644 drivers/net/xpds/xpds-softnet.h create mode 100644 drivers/net/xpds/xpds.c create mode 100644 drivers/net/xpds/xpds.h create mode 100644 drivers/nubus/nubus_syms.c create mode 100644 drivers/nubus/proc.c delete mode 100644 drivers/scsi/mac_NCR5380.c create mode 100644 drivers/scsi/oktagon_esp.c create mode 100644 drivers/scsi/oktagon_esp.h create mode 100644 drivers/scsi/oktagon_io.S create mode 100644 drivers/scsi/sun3x_esp.c create mode 100644 drivers/scsi/sun3x_esp.h create mode 100644 fs/lockd/svc4proc.c create mode 100644 include/asm-i386/adb_iop.h create mode 100644 include/asm-i386/m68kserial.h create mode 100644 include/asm-i386/mac_iop.h create mode 100644 include/asm-i386/mac_oss.h create mode 100644 include/asm-i386/mac_via.h create mode 100644 include/asm-i386/math-emu.h create mode 100644 include/asm-i386/swim_iop.h create mode 100644 include/linux/16c552.h create mode 100644 include/linux/lockd/xdr4.h create mode 100644 include/linux/m68kserial.h create mode 100644 include/linux/mc6821.h create mode 100644 include/linux/xpds-ioctl.h diff --git a/Documentation/Changes b/Documentation/Changes index da4e701e704f..131b4302f3a6 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -224,13 +224,14 @@ the latest stable public release. If you already have GCC 2.7.2 on your system, you don't have to upgrade just so the kernel will work (though feel free to upgrade if you want the gcc bug fixes). - Note that the latest compilers (egcs, pgcc, gcc 2.8) may do Bad + Note that the latest compilers (pgcc, gcc 2.95) may do Bad Things while compiling your kernel, particularly if absurd -optimizations (like -O9) are used. Caveat emptor. Currently, the only -C compiler available in a binary distribution is egcs. Version 1.1.2 -seems okay; if you have to have a binary, you may be successful using -that. In general, however, gcc-2.7.2.3 is known to be stable, while -egcs and others have not been as thoroughly tested yet. +optimizations (like -O9) are used. Caveat emptor. In general, however, +gcc-2.7.2.3 and egcs 1.1.2 are known to be stable on x86, while gcc 2.95 and +others have not been as thoroughly tested yet. + +For non x86 platforms consult the platform specific information for +recommended compilers. Networking Changes ================== @@ -651,9 +652,9 @@ The user-land 2.2beta40 release: ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz ftp://linux.nrao.edu/mirrors/fb0429.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz -The kernel-level 1.4.7 release: -ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.7.tar.gz -ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.7.tar.gz + +The kernel-level nfs-utils-0.1.6 release: +ftp://nfs.sourceforge.net/pub/nfs/nfs-utils-0.1.6.tar.gz Net-tools ========= diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 0d1f12798a7a..88eb5d379d92 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -693,6 +693,50 @@ CONFIG_BLK_DEV_ALI14XX I/O speeds to be set as well. See the files Documentation/ide.txt and drivers/block/ali14xx.c for more info. +Generic PC I/O port IDE interface support +CONFIG_BLK_DEV_PCIDE + This is the IDE driver for most generic PC style IDE interfaces. + Say Y if you have a PC and want to use IDE devices (hard disks, CD-ROM + drives, etc.) that are connected to the builtin IDE interface. + +Amiga builtin Gayle IDE interface support +CONFIG_BLK_DEV_GAYLE + This is the IDE driver for the builtin IDE interface on some Amiga + models. It supports both the `A1200 style' (used in A600 and A1200) + and `A4000 style' (used in A4000 and A4000T) of the Gayle IDE + interface. + Say Y if you have such an Amiga model and want to use IDE devices + (hard disks, CD-ROM drives, etc.) that are connected to the builtin + IDE interface. + +Falcon IDE interface support +CONFIG_BLK_DEV_FALCON_IDE + This is the IDE driver for the builtin IDE interface on the Atari + Falcon. + Say Y if you have a Falcon and want to use IDE devices (hard disks, + CD-ROM drives, etc.) that are connected to the builtin IDE interface. + +Amiga Buddha/Catweasel IDE interface support (EXPERIMENTAL) +CONFIG_BLK_DEV_BUDDHA + This is the IDE driver for the IDE interfaces on the Buddha and + Catweasel expansion boards. It supports up to two interfaces on the + Buddha and three on the Catweasel. + Say Y if you have a Buddha or Catweasel expansion board and want to + use IDE devices (hard disks, CD-ROM drives, etc.) that are connected + to one of its IDE interfaces. + +Amiga IDE Doubler support (EXPERIMENTAL) +CONFIG_BLK_DEV_IDEDOUBLER + This driver provides support for the so called `IDE doublers' (made by + various manufacturers, e.g. Eyetech) that can be connected to the + builtin IDE interface of some Amiga models. Using such an IDE doubler, + you can connect up to four instead of two IDE devices on the Amiga's + builtin IDE interface. + Note that the normal Amiga Gayle IDE driver may not work correctly if + you have an IDE doubler and don't enable this driver! + Say Y if you have an IDE doubler. The driver is enabled at kernel + runtime using the "ide=doubler" kernel boot parameter. + XT hard disk support CONFIG_BLK_DEV_XD Very old 8 bit hard disk controllers used in the IBM XT computer @@ -1913,7 +1957,8 @@ CONFIG_FB_PLATINUM PowerMac "valkyrie" frame buffer device support CONFIG_FB_VALKYRIE This driver supports a frame buffer for the "valkyrie" graphics - adapter in some Power Macintoshes. + adapter found in some Power Macintoshes, as well as the 580 and 630 + series 68k Macintoshes. Chips 65550 display support CONFIG_FB_CT65550 @@ -1922,8 +1967,9 @@ CONFIG_FB_CT65550 Mac frame buffer device CONFIG_FB_MAC - This is the frame buffer device driver for the graphics hardware in - m68k Macintoshes. + This is the a generic frame buffer device driver for the graphics + hardware in m68k Macintoshes. It can only use the video mode set + by MacOS at boot time. You should probably say Y here. HP300 frame buffer device CONFIG_FB_HP300 @@ -5850,6 +5896,20 @@ CONFIG_COMX_PROTO_FR If you want to compile this as a module, say M and read Documentation/modules.txt. The module will be called comx-proto-fr.o. +Xpeed DSL NIC support +CONFIG_XPEED + This driver supports Xpeed Inc. PCI network cards. + The following cards are supported with this driver: + + Xpeed X200 IDSL NIC (http://www.xpeed.com/Products/x200/x200_c.html) + Xpeed X300 SDSL NIC (http://www.xpeed.com/Products/x300/x300_c.html) + + This driver handles frame relay encapsulation of WAN link and presents + the card to the kernel as an ethernet-like device called dsl0, dsl1, etc. + + Currently the driver only functions as a module. Detailed configuration + information can be found in Documentation/networking/README.xpeed + Generic HDLC driver CONFIG_HDLC Say Y to this option if your Linux box contains a WAN card supported @@ -7954,6 +8014,18 @@ CONFIG_USB_DEVICEFS Most users want to say Y here. +Support for hot-pluggable USB devices +CONFIG_HOTPLUG + Say Y here if you want to plug devices into your computer while + the system is running, and be able to use them quickly. In many + cases, the devices can likewise be unplugged at any time too. + + Enable this with KMOD, and your kernel will automatically + call out to a user mode "policy agent" to load drivers and other + modules needed to use USB devices you plug in. With a bit of work, + it can invoke other device setup tasks. Get such agent software + (at http://www.linux-usb.org/policy.html) and install it. + USB Bandwidth allocation CONFIG_USB_BANDWIDTH If you say Y here, the USB subsystem enforces USB bandwidth @@ -8328,31 +8400,33 @@ CONFIG_ROOT_NFS NFS server support CONFIG_NFSD - If you want your Linux box to act as a NFS *server*, so that other + If you want your Linux box to act as an NFS *server*, so that other computers on your local network which support NFS can access certain directories on your box transparently, you have two options: you can use the self-contained user space program nfsd, in which case you - should say N here, or you can say Y and use this new experimental - kernel based NFS server. The advantage of the kernel based solution - is that it is faster; it might not be completely stable yet, though. + should say N here, or you can say Y and use the kernel based NFS + server. The kernel based solution is faster and is now the recommended + solution: no further development is occurring on the userspace server and + support of it may be discontinued in future. In either case, you will need support software; the respective locations are given in the file Documentation/Changes in the NFS section. - Please read the NFS-HOWTO, available via FTP (user: anonymous) from - ftp://metalab.unc.edu/pub/Linux/docs/HOWTO. + Please read the NFS-HOWTO, available from + http://www.linuxdoc.org/HOWTO/NFS-HOWTO.html . + The NFS server is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module is called nfsd.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. If unsure, say N. -Emulate Sun NFS daemon -CONFIG_NFSD_SUN - If you would like for the server to allow clients to access - directories that are mount points on the local filesystem (this is - how nfsd behaves on Sun systems), say yes here. If unsure, say N. +Provide NFSv3 server support (EXPERIMENTAL) +CONFIG_NFSD_V3 + If you would like to include the NFSv3 server as well as the NFSv2 + server, say Y here. File locking, via the NLMv4 protocol, is now + supported. If unsure, say N. OS/2 HPFS filesystem support (read only) CONFIG_HPFS_FS @@ -11894,6 +11968,32 @@ CONFIG_M68060 If you anticipate running this kernel on a computer with a MC68060 processor, say Y. Otherwise, say N. +Math emulation support +CONFIG_FPU_EMU + At some point in the future, this will cause floating-point math + instructions to be emulated by the kernel on machines that lack a + floating-point math coprocessor. Thrill-seekers and chronically + sleep-deprived psychotic hacker types can say Y now, everyone else + should probably wait a while. + +Math emulation only kernel +CONFIG_FPU_EMU_ONLY + This option prevents any floating-point instructions from being + compiled into the kernel, thereby the kernel doesn't save any + floating point context anymore during task switches, so this + kernel will only be usable on machines without a floating-point + math coprocessor. This makes the kernel a bit faster as no tests + needs to be executed whether a floating-point instruction in the + kernel should be executed or not. + +Math emulation extra precision +CONFIG_FPU_EMU_EXTRAPREC + The fpu uses normally a few bit more during calculations for + correct rounding, the emulator can (often) do the same but this + extra calculation can cost quite some time, so you can disable + it here. The emulator will then only calculate with a 64 bit + mantissa and round slightly incorrect. + Advanced processor options CONFIG_ADVANCED_CPU This gives you access to some advanced options for the CPU. The @@ -12096,6 +12196,21 @@ CONFIG_TT_DMA_EMUL has only been tested on a Hades with a 68060 processor. Before you use this, make backups of your entire hard disk. +Macintosh NCR5380 (II-series) SCSI +CONFIG_MAC_SCSI + If you have a Macintosh with NCR5380-based SCSI, say Y. Otherwise, + say N. This SCSI adaptor is found on all 68030-based Macs, + including the II, IIx, IIcx, IIci, IIsi, IIvx, IIvi, Color Classic, + Classic II, LC II, LC III, and their associated Performa models, as + well as the 68030-based PowerBooks and Duos. + +Macintosh NCR53c9[46] (Quadra/Centris) SCSI +CONFIG_SCSI_MAC_ESP + If you have a Macintosh with NCR53c96 or NCR53c94-based SCSI, say + Y. Otherwise, say N. These SCSI adaptors are found on all + 68040-based Macs, including all Quadra and Centris models, the LC + 475/476 and 575, and the Performa 575, 580, and 630. + Ariadne support CONFIG_ARIADNE If you have a Village Tronic Ariadne Ethernet adapter, say Y. @@ -12163,6 +12278,30 @@ CONFIG_ATARI_PAMSNET ACSI port ("ACSI node"). The driver works (has to work...) with a polled I/O scheme, so it's rather slow :-( +Macintosh NS 8390 based ethernet cards +CONFIG_DAYNAPORT + Say Y to include support for Macintosh ethernet adaptors based on + the National Semiconductor 8390 and ST-NIC chips. This family of + cards includes the majority of NuBus ethernet cards, as well as many + LC-PDS slot cards, from Apple, Asante, Cabletron, Farallon, and + other manufacturers. + +Macintosh SONIC based ethernet +CONFIG_MACSONIC + Say Y to include support for Macintosh ethernet adaptors based on + the National Semiconductor SONIC chip (83932 and 83934). If you + have a Quadra with onboard SONIC ethernet, say Y here. Onboard + SONIC ethernet chips are found on all Quadra 605, 610, 650, 700, + 800, 900, and 950 models, all Centris 650, most Centris 610, and all + LC475/476 models. It is also found in the DuoDock Plus and DuoDock + II, as well as many NuBus, LC-PDS, and comm-slot cards. + +Macintosh (AV) onboard MACE ethernet +CONFIG_MACMACE + Say Y here if you have a Centris 660AV, a Quadra 660AV, or a Quadra + 840AV, and you would like to use the onboard MACE Ethernet adaptor. + + Multiface Card III parallel support CONFIG_MULTIFACE_III_LP If you have a Multiface III card for your Amiga, and want to use its @@ -12278,6 +12417,11 @@ CONFIG_MULTIFACE_III_TTY If you want to compile it as a module, say M here and read Documentation/modules.txt. +Macintosh SCC serial support +CONFIG_MAC_SCC + If you have a Macintosh and would like to use its serial + ("Printer" and "Modem") ports in Linux, say Y. Otherwise, say N. + Amiga or Atari DMA sound support CONFIG_DMASOUND If you want to use the internal audio of your Atari or Amiga in diff --git a/Documentation/IO-mapping.txt b/Documentation/IO-mapping.txt index b18355ba77b3..78253e5b1d4f 100644 --- a/Documentation/IO-mapping.txt +++ b/Documentation/IO-mapping.txt @@ -176,7 +176,7 @@ ioremap() function "vremap()". ioremap() is the proper name, but I didn't think straight when I wrote it originally. People who have to support both can do something like: - /* support old naming sillyness */ + /* support old naming silliness */ #if LINUX_VERSION_CODE < 0x020100 #define ioremap vremap #define iounmap vfree diff --git a/Documentation/SubmittingDrivers b/Documentation/SubmittingDrivers index 29c5973851e7..a780abce0a08 100644 --- a/Documentation/SubmittingDrivers +++ b/Documentation/SubmittingDrivers @@ -41,11 +41,11 @@ Linux 2.4test: What Criteria Determine Acceptance ---------------------------------- -Licensing: The code must be released to us under the GNU public license. - We don't insist on any kind of exclusively GPL licensing, - and if you wish the driver to be useful to other communities - such as BSD you may well wish to release under multiple - licenses. +Licensing: The code must be released to us under the GNU General Public + License. We don't insist on any kind of exclusively GPL + licensing, and if you wish the driver to be useful to other + communities such as BSD you may well wish to release under + multiple licenses. Interfaces: If your driver uses existing interfaces and behaves like other drivers in the same class it will be much more likely diff --git a/Documentation/arm/nwfpe/README b/Documentation/arm/nwfpe/README index f05756ef0d81..d8bdd14d341e 100644 --- a/Documentation/arm/nwfpe/README +++ b/Documentation/arm/nwfpe/README @@ -15,7 +15,7 @@ have attempted to use the C_SYMBOL_NAME macro wherever this may be important. Another choice I made was in the file structure. I have attempted to -contain all operating system specfic code in one module (fpmodule.*). +contain all operating system specific code in one module (fpmodule.*). All the other files contain emulator specific code. This should allow others to port the emulator to NetBSD for instance relatively easily. diff --git a/Documentation/cdrom/cdrom-standard.tex b/Documentation/cdrom/cdrom-standard.tex index 56dd21bc8242..cf6399e370e5 100644 --- a/Documentation/cdrom/cdrom-standard.tex +++ b/Documentation/cdrom/cdrom-standard.tex @@ -919,7 +919,7 @@ the current flags. than fixing this interface by changing the assumptions it was made under, thereby breaking all user applications that use this function, the \UCD\ implements this $ioctl$ as follows: If the CD in - question has audio tracks on it, and it has absolutly no CD-I, XA, + question has audio tracks on it, and it has absolutely no CD-I, XA, or data tracks on it, it will be reported as $CDS_AUDIO$. If it has both audio and data tracks, it will return $CDS_MIXED$. If there are no audio tracks on the disc, and if the CD in question has any diff --git a/Documentation/cdrom/ide-cd b/Documentation/cdrom/ide-cd index ac202df58103..fc94b8be123e 100644 --- a/Documentation/cdrom/ide-cd +++ b/Documentation/cdrom/ide-cd @@ -286,7 +286,7 @@ b. Timeout/IRQ errors. Unfortunately, these drives seem to become very confused when we perform the standard Linux ATA disk drive probe. If you own one of these drives, you can bypass the ATA probing which confuses these CDROM drives, by - adding `append="hdX=noprobe hdX=cdrom"' to your lilo.conf file and runing + adding `append="hdX=noprobe hdX=cdrom"' to your lilo.conf file and running lilo (again where X is the drive letter corresponding to where your drive is installed.) diff --git a/Documentation/computone.txt b/Documentation/computone.txt index 444d0c4f4276..de281aa84464 100644 --- a/Documentation/computone.txt +++ b/Documentation/computone.txt @@ -93,7 +93,7 @@ Previously, the driver sources were packaged with a set of patch files to update the character drivers' makefile and configuration file, and other kernel source files. A build script (ip2build) was included which applies the patches if needed, and build any utilities needed. -What you recieve may be a single patch file in conventional kernel +What you receive may be a single patch file in conventional kernel patch format build script. That form can also be applied by running patch -p1 < ThePatchFile. Otherwise run ip2build. @@ -191,7 +191,7 @@ Higher speeds can be obtained using the setserial utility which remaps Intelliport II installations using the PowerPort expansion module can use the custom speed setting to select the highest speeds: 153,600 bps, 230,400 bps, 307,200 bps, 460,800bps and 921,600 bps. The base for -custom baud rate configuration is fixed at 921,600 for cards/expantion +custom baud rate configuration is fixed at 921,600 for cards/expansion modules with ST654's and 115200 for those with Cirrus CD1400's. This corresponds to the maximum bit rates those chips are capable. For example if the baud base is 921600 and the baud divisor is 18 then @@ -220,13 +220,13 @@ DEVFS is the DEVice File System available as an add on package for the 2.2.x kernels and available as a configuration option in 2.3.46 and higher. Devfs allows for the automatic creation and management of device names under control of the device drivers themselves. The Devfs namespace is -hierarchial and reduces the clutter present in the normal flat /dev +hierarchical and reduces the clutter present in the normal flat /dev namespace. Devfs names and conventional device names may be intermixed. A userspace daemon, devfsd, exists to allow for automatic creation and management of symbolic links from the devfs name space to the conventional names. More details on devfs can be found on the DEVFS home site at or in the file kernel -documenation files, .../linux/Documenation/filesystems/devfs/REAME. +documentation files, .../linux/Documentation/filesystems/devfs/REAME. If you are using devfs, existing devices are automatically created within the devfs name space. Normal devices will be ttf/0 - ttf/255 and callout diff --git a/Documentation/cpqarray.txt b/Documentation/cpqarray.txt index db9846679d42..620e12b6ef7c 100644 --- a/Documentation/cpqarray.txt +++ b/Documentation/cpqarray.txt @@ -1,4 +1,4 @@ -This driver is for Compaq's SMART2 Intellegent Disk Array Controllers. +This driver is for Compaq's SMART2 Intelligent Disk Array Controllers. Supported Cards: ---------------- diff --git a/Documentation/fb/vesafb.txt b/Documentation/fb/vesafb.txt index 19d810a4498c..d7e7bd78ce1c 100644 --- a/Documentation/fb/vesafb.txt +++ b/Documentation/fb/vesafb.txt @@ -62,7 +62,7 @@ So the table for the Kernel mode numbers are: 16M | 0x312 0x315 0x318 0x31B To enable one of those modes you have to specify "vga=ask" in the -lilo.conf file and rerun LILO. Then you can type in the descired +lilo.conf file and rerun LILO. Then you can type in the desired mode at the "vga=ask" prompt. For example if you like to use 1024x768x256 colors you have to say "305" at this prompt. diff --git a/Documentation/filesystems/affs.txt b/Documentation/filesystems/affs.txt index 543b02a7c1fd..30c9738590f4 100644 --- a/Documentation/filesystems/affs.txt +++ b/Documentation/filesystems/affs.txt @@ -158,7 +158,7 @@ If you boot Windows 95 (don't know about 3.x, 98 and NT) while you have an Amiga harddisk connected to your PC, it will overwrite the bytes 0x00dc..0x00df of block 0 with garbage, thus invalidating the Rigid Disk Block. Sheer luck has it that this is an unused -area of the RDB, so only the checksum doesn's match anymore. +area of the RDB, so only the checksum doesn't match anymore. Linux will ignore this garbage and recognize the RDB anyway, but before you connect that drive to your Amiga again, you must restore or repair your RDB. So please do make a backup copy of it diff --git a/Documentation/filesystems/coda.txt b/Documentation/filesystems/coda.txt index 4ab793a71b18..61311356025d 100644 --- a/Documentation/filesystems/coda.txt +++ b/Documentation/filesystems/coda.txt @@ -1531,7 +1531,7 @@ kernel support. DDeessccrriippttiioonn Remove all entries in the cache lying in a directory - CodaFid, and all children of this directory. This call is issed when + CodaFid, and all children of this directory. This call is issued when Venus receives a callback on the directory. @@ -1630,7 +1630,7 @@ kernel support. The following requirements should be accommodated: - 1. The message queueus should have open and close routines. On Unix + 1. The message queues should have open and close routines. On Unix the opening of the character devices are such routines. +o Before opening, no messages can be placed. diff --git a/Documentation/filesystems/fat_cvf.txt b/Documentation/filesystems/fat_cvf.txt index c1cf27bd74c0..9082cbe4e9da 100644 --- a/Documentation/filesystems/fat_cvf.txt +++ b/Documentation/filesystems/fat_cvf.txt @@ -140,7 +140,7 @@ It contains... THE KIND OF CVF I SUPPORT". The function must maintain the module usage counters for safety, i.e. do MOD_INC_USE_COUNT at the beginning and MOD_DEC_USE_COUNT at the end. The function *must not* assume that - successful recongition would lead to a call of the mount_cvf function + successful recognition would lead to a call of the mount_cvf function later. - mount_cvf: A function that sets up some values or initializes something additional diff --git a/Documentation/filesystems/ufs.txt b/Documentation/filesystems/ufs.txt index dafae246da9f..0e2b243ff445 100644 --- a/Documentation/filesystems/ufs.txt +++ b/Documentation/filesystems/ufs.txt @@ -9,7 +9,7 @@ UFS OPTIONS ufstype=type_of_ufs UFS is a file system widely used in different operating systems. - The problem are differencies among implementations. Features of + The problem are differences among implementations. Features of some implementations are undocumented, so its hard to recognize type of ufs automatically. That's why user must specify type of ufs manually by mount option ufstype. Possible values are: diff --git a/Documentation/isdn/00-INDEX b/Documentation/isdn/00-INDEX index b7161af1a78f..ca891ec75aec 100644 --- a/Documentation/isdn/00-INDEX +++ b/Documentation/isdn/00-INDEX @@ -31,7 +31,7 @@ README.act2000 README.eicon - info on driver for Eicon active cards. README.concap - - info on "CONCAP" ecapsulation protocol interface used for X.25. + - info on "CONCAP" encapsulation protocol interface used for X.25. README.diversion - info on module for isdn diversion services. README.sc diff --git a/Documentation/isdn/INTERFACE.fax b/Documentation/isdn/INTERFACE.fax index 2c9659f0df01..479344ba0039 100644 --- a/Documentation/isdn/INTERFACE.fax +++ b/Documentation/isdn/INTERFACE.fax @@ -4,7 +4,7 @@ $Id: INTERFACE.fax,v 1.1 1999/08/11 20:30:28 armin Exp $ Description of the fax-subinterface between linklevel and hardwarelevel of isdn4linux. - The communication between linklevel (LL) and harwarelevel (HL) for fax + The communication between link level (LL) and hardware level (HL) for fax is based on the struct T30_s (defined in isdnif.h). This struct is allocated in the LL. In order to use fax, the LL provides the pointer to this struct with the @@ -60,7 +60,7 @@ Structure T30_s: depending on progress and type of connection. If the phase changes because of an AT command, the LL driver changes this value. Otherwise the HL-driver takes care of it, but - only neccessary on call establishment (from IDLE to PHASE_A). + only necessary on call establishment (from IDLE to PHASE_A). (one of the constants ISDN_FAX_PHASE_[IDLE,A,B,C,D,E]) - direction diff --git a/Documentation/isdn/README b/Documentation/isdn/README index afd9f45af04d..0d3b0b02331b 100644 --- a/Documentation/isdn/README +++ b/Documentation/isdn/README @@ -146,7 +146,7 @@ README for the ISDN-subsystem x = 38400: 198 Note on value in Reg 19: There is _NO_ common convention for 38400 baud. - The value 198 is choosen arbitrarily. Users + The value 198 is chosen arbitrarily. Users _MUST_ negotiate this value before establishing a connection. AT&Sx Set window-size (x = 1..8) (not yet implemented) diff --git a/Documentation/isdn/README.HiSax b/Documentation/isdn/README.HiSax index 0416782255fc..fb64b1ecd2fc 100644 --- a/Documentation/isdn/README.HiSax +++ b/Documentation/isdn/README.HiSax @@ -77,7 +77,7 @@ Note: PCF, PCF-Pro: up to now, only the ISDN part is supported If you know other passive cards with the Siemens chipset, please let me know. To use the PNP cards you need the isapnptools. -You can combine any card, if there is no conflict between the ressources +You can combine any card, if there is no conflict between the resources (io, mem, irq). @@ -532,7 +532,7 @@ to e.g. the Internet: /sbin/isdnctrl huptimeout isdn0 0 /sbin/isdnctrl l2_prot isdn0 hdlc # Attention you must not set an outgoing number !!! This won't work !!! - # The incomming number is LEASED0 for the first card, LEASED1 for the + # The incoming number is LEASED0 for the first card, LEASED1 for the # second and so on. /sbin/isdnctrl addphone isdn0 in LEASED0 # Here is no need to bind the channel. @@ -551,7 +551,7 @@ a) Use state of the art isdn4k-utils Here an example script: #!/bin/sh -# Start/Stop ISDN lesaed line connection +# Start/Stop ISDN leased line connection I4L_AS_MODULE=yes I4L_REMOTE_IS_CISCO=no diff --git a/Documentation/isdn/README.sc b/Documentation/isdn/README.sc index b70db7a630b1..1153cd926059 100644 --- a/Documentation/isdn/README.sc +++ b/Documentation/isdn/README.sc @@ -93,7 +93,7 @@ include: this driver release? Before you can compile, install and use the SpellCaster ISA ISDN driver, you -must ensure that the following software is installed, configuraed and running: +must ensure that the following software is installed, configured and running: - Linux kernel 2.0.20 or later with the required init and ps versions. Please see your distribution vendor for the correct @@ -189,7 +189,7 @@ A) 10 steps to the establishment of a basic HDLC connection basic HDLC connection between its two channels. Two network interfaces are created and two routes added between the channels. - i) using the isdnctrl utitity, add an interface with "addif" and + i) using the isdnctrl utility, add an interface with "addif" and name it "isdn0" ii) add the outgoing and inbound telephone numbers iii) set the Layer 2 protocol to hdlc @@ -213,7 +213,7 @@ B) Establishment of a PPP connection This file is a script used to configure a BRI ISDN TA to establish a PPP connection between the two channels. The file is almost identical to the HDLC connection example except that the packet - ecapsulation type has to be set. + encapsulation type has to be set. use the same procedure as in the HDLC connection from steps i) to iii) then, after the Layer 2 protocol is set, set the encapsulation @@ -238,7 +238,7 @@ C) Establishment of a MLPPP connection This file is a script used to configure a BRI ISDN TA to accept a Multi Link PPP connection. - i) using the isdnctrl utitity, add an interface with "addif" and + i) using the isdnctrl utility, add an interface with "addif" and name it "ippp0" ii) add the inbound telephone number iii) set the Layer 2 protocol to hdlc and the Layer 3 protocol to diff --git a/Documentation/joystick-api.txt b/Documentation/joystick-api.txt index 622c67d170ca..e2c7b78fe005 100644 --- a/Documentation/joystick-api.txt +++ b/Documentation/joystick-api.txt @@ -29,13 +29,13 @@ By default, the device is opened in blocking mode. where js_event is defined as struct js_event { - __u32 time; /* event timestamp in miliseconds */ + __u32 time; /* event timestamp in milliseconds */ __s16 value; /* value */ __u8 type; /* event type */ __u8 number; /* axis/button number */ }; -If the read is successfull, it will return sizeof(struct js_event), unless +If the read is successful, it will return sizeof(struct js_event), unless you wanted to read more than one event per read as described in section 3.1. @@ -225,7 +225,7 @@ JS_VERSION symbol ~~~~~~~~~~~~~~ JSIOCGNAME(len) allows you to get the name string of the joystick - the same -as is being printed at boot time. The 'len' argument is the lenght of the +as is being printed at boot time. The 'len' argument is the length of the buffer provided by the application asking for the name. It is used to avoid possible overrun should the name be too long. diff --git a/Documentation/joystick-parport.txt b/Documentation/joystick-parport.txt index 2217a0489bf2..209bf03cf233 100644 --- a/Documentation/joystick-parport.txt +++ b/Documentation/joystick-parport.txt @@ -22,7 +22,7 @@ connecting such devices. 2. Devices supported ~~~~~~~~~~~~~~~~~~~~ - Many console and 8-bit coputer gamepads and joysticks are supported. The + Many console and 8-bit computer gamepads and joysticks are supported. The following subsections discuss usage of each. 2.1 NES and SNES diff --git a/Documentation/kbuild/commands.txt b/Documentation/kbuild/commands.txt index a732118484a5..d61e1a16e75c 100644 --- a/Documentation/kbuild/commands.txt +++ b/Documentation/kbuild/commands.txt @@ -84,7 +84,7 @@ Here are the targets available at the top level: occasionally. If you are adding configuration options, it's nice if you do it before you publish your patch! - You can run 'make checkhelp' withoug configuring the kernel. + You can run 'make checkhelp' without configuring the kernel. Also, 'make checkhelp' does not modify any files. make dep, make depend diff --git a/Documentation/kbuild/config-language.txt b/Documentation/kbuild/config-language.txt index 8c55b964ded8..a156b8347f35 100644 --- a/Documentation/kbuild/config-language.txt +++ b/Documentation/kbuild/config-language.txt @@ -80,7 +80,7 @@ Here are the basic grammar elements. A /word/ is a single unquoted word, a single-quoted string, or a double-quoted string. If the word is unquoted or double quoted, - then $-substition will be performed on the word. + then $-substitution will be performed on the word. A /symbol/ is a single unquoted word. A symbol must have a name of the form CONFIG_*. scripts/mkdep.c relies on this convention in order @@ -231,7 +231,7 @@ Note that the bool verb does not have a default value. People keep trying to write Config Language scripts with a default value for bool, but *all* of the existing language interpreters discard additional values. Feel free to submit a multi-interpreter patch to linux-kbuild if you -want to implement this as an enhancment. +want to implement this as an enhancement. Configure: implemented Menuconfig: implemented diff --git a/Documentation/moxa-smartio b/Documentation/moxa-smartio index 4357e6772d11..75efa68409d7 100644 --- a/Documentation/moxa-smartio +++ b/Documentation/moxa-smartio @@ -23,7 +23,7 @@ Content -C168P/H/HS, C168H/PCI 8 port multiport board. This driver has been modified a little and cleaned up from the Moxa - contributed driver code and merged into Linux 2.2.14pre. In paticular + contributed driver code and merged into Linux 2.2.14pre. In particular official major/minor numbers have been assigned which are different to those the original Moxa supplied driver used. @@ -83,7 +83,7 @@ Content PCI board --------- - You may need to adjust IRQ useage in BIOS to avoid from IRQ conflict + You may need to adjust IRQ usage in BIOS to avoid from IRQ conflict with other ISA devices. Please refer to hardware installation procedure in User's Manual in advance. @@ -150,7 +150,7 @@ Content # insmod mxser - to activate the moduler driver. You may run "lsmod" to check + to activate the modular driver. You may run "lsmod" to check if "mxser.o" is activated. 2. Create special files by executing "msmknod". @@ -158,7 +158,7 @@ Content # ./msmknod Default major numbers for dial-in device and callout device are - 174, 175. Msmknod will delete any special files occuping the same + 174, 175. Msmknod will delete any special files occupying the same device naming. 3. Up to now, you may manually execute "insmod mxser" to activate @@ -209,7 +209,7 @@ Content below. a. # cd /moxa/mxser/driver # vi mxser.c - b. Find the array mxserBoardCAP[] as belows. + b. Find the array mxserBoardCAP[] as below. static int mxserBoardCAP[] = {0x00, 0x00, 0x00, 0x00}; @@ -260,7 +260,7 @@ Content f. cp /usr/src/linux/arch/i386/boot/bzImage /boot/vmlinuz g. Please make sure the boot kernel (vmlinuz) is in the correct position. If you use 'lilo' utility, you should - check /etc/lilo.conf 'image' item specifiedd the path + check /etc/lilo.conf 'image' item specified the path which is the 'vmlinuz' path, or you will load wrong (or old) boot kernel image (vmlinuz). h. chmod 400 /vmlinuz @@ -372,7 +372,7 @@ Content ----------------------------------------------------------------------------- 6. Troubleshooting - The boot time error mesages and solutions are stated as clearly as + The boot time error messages and solutions are stated as clearly as possible. If all the possible solutions fail, please contact our technical support team to get more help. @@ -388,7 +388,7 @@ Content which device causes the situation,please check /proc/interrupts to find free IRQ and simply change another free IRQ for Moxa board. - Error msg: Board #: C1xx Series(CAP=xxx) interupt number invalid. + Error msg: Board #: C1xx Series(CAP=xxx) interrupt number invalid. Solution: Each port within the same multiport board shares the same IRQ. Please set one IRQ (IRQ doesn't equal to zero) for one Moxa board. diff --git a/Documentation/networking/README.sb1000 b/Documentation/networking/README.sb1000 index 37c39a0c86f8..f82d42584e98 100644 --- a/Documentation/networking/README.sb1000 +++ b/Documentation/networking/README.sb1000 @@ -30,7 +30,7 @@ cable modem easy. http://home.adelphia.net/~siglercm/sb1000.html http://linuxpower.cx/~cable/ - along with these utilties. + along with these utilities. 3.) The standard isapnp tools. These are necessary to configure your SB1000 card at boot time (or afterwards by hand) since it's a PnP card. diff --git a/Documentation/networking/README.xpeed b/Documentation/networking/README.xpeed new file mode 100644 index 000000000000..1aeea4b37c4e --- /dev/null +++ b/Documentation/networking/README.xpeed @@ -0,0 +1,52 @@ +Driver for Xpeed Inc. PCI network cards +--------------------------------------- +The following cards are supported with this driver: + +Xpeed X200 IDSL NIC (http://www.xpeed.com/Products/x200/x200_c.html) +Xpeed X300 SDSL NIC (http://www.xpeed.com/Products/x300/x300_c.html) + +This driver handles frame relay encapsulation of WAN link and presents +the card to the kernel as an ethernet-like device called dsl0, dsl1, etc. + +The driver supports up to 6 cards and supports operation in either +RFC1490 frame relay or bridged ethernet modes. Note that when operating +in RFC1490 frame relay mode, "ifconfig -arp" (no ARP) should be used. + +=============================== NOTE ==================================== +You will NEED to use the xpds-config utility to configure the link speed, +change LT/NT mode, change swap/invert parameters, and reflash the adapter +firmware. This utility and other support files can be downloaded at +ftp://ftp.xpeed.com/pub/linux/ +=============================== NOTE ==================================== + +The driver accepts the following parameters when compiled as a module: + +xpds_default_dlci + Defines the DLCI to be used for the local interface. Default is 16. + +xpds_default_dlci_cr + A special mask value to be used when speaking with certain buggy + vendor equipment. Can be 0 or 2. Default is 0. + +xpds_default_dlci_lmi + Defines how the card will perform LMI protocol negotiation. + Valid values are: + 0 No LMI will be used. + 1 LMI will operate in LT (Line Termination) mode. + 2 LMI will operate in NT (Network Termination) mode. + 3 LMI will operation in bidirectional NT mode. + -1 Attempt to autoconfigure LMI mode. (Default) + +xpds_default_bridged + Defines whether the card will operate in RFC1490 frame relay or + bridged ethernet mode. Default is 0, RFC1490 frame relay mode. + +Typical usage for a connection in RFC1490 frame relay mode: + +# modprobe xpds-fr +# ifconfig dsl0 10.0.0.2 netmask 255.255.255.0 broadcast 10.0.0.255 -arp + +Typical usage for a connection in bridged ethernet mode: + +# modprobe xpds-fr xpds_default_bridged=1 +# ifconfig dsl0 10.0.0.2 netmask 255.255.255.0 broadcast 10.0.0.255 diff --git a/Documentation/networking/comx.txt b/Documentation/networking/comx.txt index a58e78d905f9..38ad51622c04 100644 --- a/Documentation/networking/comx.txt +++ b/Documentation/networking/comx.txt @@ -63,7 +63,7 @@ values upon creation, so you don't necessarily have to change all of them. When you're ready with filling in the files in the comx[n] directory, you can configure the corresponding network interface with the standard network -configuration utilites. If you're unble to bring the interfaces up, look up +configuration utilities. If you're unable to bring the interfaces up, look up the various kernel log files on your system, and consult the messages for a probable reason. diff --git a/Documentation/networking/dmfe.txt b/Documentation/networking/dmfe.txt index 158e94ba6fef..7324e1c6b34a 100644 --- a/Documentation/networking/dmfe.txt +++ b/Documentation/networking/dmfe.txt @@ -29,7 +29,7 @@ /net/inet -Wall -Wstrict-prototypes -O6 -c dmfe.c" - B. The following steps teach you how to active DM9102 board: + B. The following steps teach you how to activate a DM9102 board: 1. Used the upper compiler command to compile dmfe.c @@ -44,13 +44,13 @@ "ifconfig eth0 172.22.3.18" ^^^^^^^^^^^ Your IP address - 4. Active the IP routing table. For some distributions, it is not + 4. Activate the IP routing table. For some distributions, it is not necessary. You can type "route" to check. "route add default eth0" - 5. Well done. Your DM9102 adapter actived now. + 5. Well done. Your DM9102 adapter is now activated. C. Object files description: diff --git a/Documentation/networking/olympic.txt b/Documentation/networking/olympic.txt index 04198aec5748..63806258a9ed 100644 --- a/Documentation/networking/olympic.txt +++ b/Documentation/networking/olympic.txt @@ -38,14 +38,14 @@ the driver now re-sizes buffers based on MTU settings as well. message_level: Controls level of messages created by the driver. Defaults to 0: which only displays start-up and critical messages. Presently any non-zero value will display all soft messages as well. NB This does not turn -debuging messages on, that must be done by modified the source code. +debugging messages on, that must be done by modified the source code. Multi-card: The driver will detect multiple cards and will work with shared interrupts, each card is assigned the next token ring device, i.e. tr0 , tr1, tr2. The driver should also happily reside in the system with other drivers. It has -been tested with ibmtr.c running, and I personnally have had one Olicom PCI +been tested with ibmtr.c running, and I personally have had one Olicom PCI card and two IBM olympic cards (all on the same interrupt), all running together. @@ -68,7 +68,7 @@ mode. All unexpected MAC frames (beaconing etc.) will be received by the driver and the source and destination addresses printed. Also an entry will be added in /proc/net called olympic_tr. This displays low level information about the configuration of the ring and -the adapter. This feature has been designed for network adiministrators +the adapter. This feature has been designed for network administrators to assist in the diagnosis of network / ring problems. 6/8/99 Peter De Schrijver and Mike Phillips diff --git a/Documentation/networking/shaper.txt b/Documentation/networking/shaper.txt index 1be0db8c0bc4..6c4ebb66a906 100644 --- a/Documentation/networking/shaper.txt +++ b/Documentation/networking/shaper.txt @@ -42,7 +42,7 @@ multiple route tables to get the flexibility. There is no "borrowing" or "sharing" scheme. This is a simple traffic limiter. We implement Van Jacobson and Sally Floyd's CBQ -architecture into Linux 2.2. THis is the preferred solution. Shaper is +architecture into Linux 2.2. This is the preferred solution. Shaper is for simple or back compatible setups. Alan diff --git a/Documentation/networking/sis900.txt b/Documentation/networking/sis900.txt index b353eaa3be79..6c26c9927728 100644 --- a/Documentation/networking/sis900.txt +++ b/Documentation/networking/sis900.txt @@ -161,7 +161,7 @@ Silicon Integrated System Corp. is cooperating closely with core Linux Kernel developers. The revisions of SiS 900 driver are distributed by - the usuall channels for kernel tar files and patches. Those kernel tar + the usual channels for kernel tar files and patches. Those kernel tar files for official kernel and patches for kernel pre-release can be download at official kernel ftp site and its mirrors. The 1.06 diff --git a/Documentation/networking/sk98lin.txt b/Documentation/networking/sk98lin.txt index 510d2c4ef82f..2af3964ef378 100644 --- a/Documentation/networking/sk98lin.txt +++ b/Documentation/networking/sk98lin.txt @@ -138,7 +138,7 @@ Insert a line of the form: options sk98lin ... For "...", use the same syntax as described below for the command -line paramaters of insmod. +line parameters of insmod. You either have to reboot your computer or unload and reload the driver to activate the new parameters. The syntax of the driver parameters is: @@ -187,7 +187,7 @@ which you set the parameter (A or B). this port is not "Sense". If autonegotiation is "On", all three values are possible. If it is "Off", only "Full" and "Half" are allowed. - It is usefull if your link partner does not support all + It is useful if your link partner does not support all possible combinations. - Flow Control @@ -269,7 +269,7 @@ which you set the parameter (A or B). Large frames (also called jumbo frames) are now supported by the driver. This can result in a greatly improved throughput if -transfering large amounts of data. +transferring large amounts of data. To enable large frames, set the MTU (maximum transfer unit) of the interface to the value you wish (up to 9000). The command for this is: @@ -285,7 +285,7 @@ it will simply drop them. You can switch back to the standard ethernet frame size with: ifconfig eth0 mtu 1500 -To make this setting persitent, add a script with the 'ifconfig' +To make this setting persistent, add a script with the 'ifconfig' line to the system startup sequence (named something like "S99sk98lin" in /etc/rc.d/rc2.d). *** diff --git a/Documentation/networking/tlan.txt b/Documentation/networking/tlan.txt index fe0bfab862d1..9f8f156353b3 100644 --- a/Documentation/networking/tlan.txt +++ b/Documentation/networking/tlan.txt @@ -44,7 +44,7 @@ II. Driver Options 0x01 Turn on general debugging messages. 0x02 Turn on receive debugging messages. 0x04 Turn on transmit debugging messages. - 0x08 Turn on list debugging messsages. + 0x08 Turn on list debugging messages. 2. You can append aui=1 to the end of the insmod line to cause the adapter to use the AUI interface instead of the 10 Base T diff --git a/Documentation/networking/wan-router.txt b/Documentation/networking/wan-router.txt index f82ceb548f63..5cb1b3e428be 100644 --- a/Documentation/networking/wan-router.txt +++ b/Documentation/networking/wan-router.txt @@ -142,11 +142,11 @@ REVISION HISTORY 2.0.8 Nov 02, 1999 - Fixed up the X25API code. - Clear call bug fixed.i - - Eanbled driver for multi-card + - Enabled driver for multi-card operation. 2.0.7 Aug 26, 1999 - Merged X25API code into WANPIPE. - - Fixed a memeory leak for X25API + - Fixed a memory leak for X25API - Updated the X25API code for 2.2.X kernels. - Improved NEM handling. diff --git a/Documentation/networking/wanpipe.txt b/Documentation/networking/wanpipe.txt index 7cb28178e908..1fd1642d8def 100644 --- a/Documentation/networking/wanpipe.txt +++ b/Documentation/networking/wanpipe.txt @@ -228,7 +228,7 @@ REVISION HISTORY creating applications using BiSync streaming. -2.0.5 Aug 04, 1999 CHDLC initializatin bug fix. +2.0.5 Aug 04, 1999 CHDLC initialization bug fix. PPP interrupt driven driver: Fix to the PPP line hangup problem. New PPP firmware @@ -241,13 +241,13 @@ REVISION HISTORY Streaming HDLC API has been taken out. Available as a patch. -2.0.6 Aug 17, 1999 Increased debugging in statup scripts - Fixed insallation bugs from 2.0.5 +2.0.6 Aug 17, 1999 Increased debugging in startup scripts + Fixed installation bugs from 2.0.5 Kernel patch works for both 2.2.10 and 2.2.11 kernels. There is no functional difference between the two packages 2.0.7 Aug 26, 1999 o Merged X25API code into WANPIPE. - o Fixed a memeory leak for X25API + o Fixed a memory leak for X25API o Updated the X25API code for 2.2.X kernels. o Improved NEM handling. diff --git a/Documentation/s390/DASD b/Documentation/s390/DASD index dc5f4de3b345..31bdcdfb429c 100644 --- a/Documentation/s390/DASD +++ b/Documentation/s390/DASD @@ -30,7 +30,7 @@ an Enterprise Storage Server (Seascape) should work fine as well. We currently implement one partition per volume, which is the whole volume, skipping the first blocks up to the volume label. These are reserved for IPL records and IBM's volume label to assure -accessability of the DASD from other OSs. In a later stage we will +accessibility of the DASD from other OSs. In a later stage we will provide support of partitions, maybe VTOC oriented or using a kind of partition table in the label record. @@ -38,7 +38,7 @@ USAGE -Low-level format (?CKD only) For using an ECKD-DASD as a Linux harddisk you have to low-level -format the tracks by issueing the BLKDASDFORMAT-ioctl on that +format the tracks by issuing the BLKDASDFORMAT-ioctl on that device. This will erase any data on that volume including IBM volume labels, VTOCs etc. The ioctl may take a 'struct format_data *' or 'NULL' as an argument. diff --git a/Documentation/s390/cds.txt b/Documentation/s390/cds.txt index 1acc9f37c103..f79d2e1fb022 100644 --- a/Documentation/s390/cds.txt +++ b/Documentation/s390/cds.txt @@ -778,7 +778,7 @@ flag : 0 (zero) or DOIO_WAIT_FOR_INTERRUPT The halt_IO() function returns : 0 - successful completion or request successfuly initiated --EBUSY - the device is currently performing a sysnchonous I/O +-EBUSY - the device is currently performing a synchronous I/O operation : do_IO() with flag DOIO_WAIT_FOR_INTERRUPT or an error was encountered and the device is currently be sensed diff --git a/Documentation/sound/ESS b/Documentation/sound/ESS index ac551fa4dd98..cd3928eef436 100644 --- a/Documentation/sound/ESS +++ b/Documentation/sound/ESS @@ -12,7 +12,7 @@ Every chip that's detected as a later-than-es1688 chip has a 6 bits logarithmic master volume control. Every chip that's detected as a ES1887 now has Full Duplex support. Made a -little testprogram that showes that is works, haven't seen a real program that +little test program that shows that is works, haven't seen a real program that needs this however. For ESS chips an additional parameter "esstype" can be specified. This controls diff --git a/Documentation/sound/Introduction b/Documentation/sound/Introduction index 9924c309499c..d5e90c9a0a89 100644 --- a/Documentation/sound/Introduction +++ b/Documentation/sound/Introduction @@ -322,7 +322,7 @@ in the Sound-HOWTO). 7) Turn on debug in drivers/sound/sound_config.h (DEB, DDB, MDB). -8) If the system reports insuffcient DMA memory then you may want to +8) If the system reports insufficient DMA memory then you may want to load sound with the "dmabufs=1" option. Or in /etc/conf.modules add preinstall sound dmabufs=1 @@ -356,7 +356,7 @@ Module Loading: When a sound card is first referenced and sound is modular the sound system will ask for the sound devices to be loaded. Initially it requests that -the driver for the sound system is loaded. It then wwill ask for +the driver for the sound system is loaded. It then will ask for sound-slot-0, where 0 is the first sound card. (sound-slot-1 the second and so on). Thus you can do diff --git a/Documentation/sound/Maestro b/Documentation/sound/Maestro index 940156fc68b1..1720b1443018 100644 --- a/Documentation/sound/Maestro +++ b/Documentation/sound/Maestro @@ -70,7 +70,7 @@ maestro chip. As this is a PCI device, the module does not need to be informed of any IO or IRQ resources it should use, it devines these from the -system. Somtimes, on sucky PCs, the BIOS fails to allocated resources +system. Sometimes, on sucky PCs, the BIOS fails to allocated resources for the maestro. This will result in a message like: maestro: PCI subsystem reports IRQ 0, this might not be correct. from the kernel. Should this happen the sound chip most likely will diff --git a/Documentation/sound/PSS b/Documentation/sound/PSS index ee81f73503cb..187b9525e1f6 100644 --- a/Documentation/sound/PSS +++ b/Documentation/sound/PSS @@ -3,7 +3,7 @@ downloadable programs and also has an AD1848 "Microsoft Sound System" device. The PSS driver enables MSS and MPU401 modes of the card. SB is not enabled since it doesn't work concurrently with MSS. -If you build this driver as a module then the driver takes the folowing +If you build this driver as a module then the driver takes the following parameters pss_io. The I/O base the PSS card is configured at (normally 0x220 diff --git a/Documentation/sound/README.OSS b/Documentation/sound/README.OSS index 379dd3bd57b2..28ddff9389b1 100644 --- a/Documentation/sound/README.OSS +++ b/Documentation/sound/README.OSS @@ -81,7 +81,7 @@ contributors. (I could have forgotten some names.) Gregor Hoffleit Mozart support (initial version) Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support James Hightower Spotting a tiny but important bug in CS423x support. - Denis Sablic OPTi 82C924 spesific enhancements (non PnP mode) + Denis Sablic OPTi 82C924 specific enhancements (non PnP mode) Tim MacKenzie Full duplex support for OPTi 82C930. Please look at lowlevel/README for more contributors. @@ -542,7 +542,7 @@ Yamaha OPL3-SA1 There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They are PnP chips and will not work with the OPL3-SA1 driver. You should - use the standard MSS, MPU401 and OPL3 options with thses chips and to + use the standard MSS, MPU401 and OPL3 options with these chips and to activate the card using isapnptools. 4Front Technologies SoftOSS @@ -1309,7 +1309,7 @@ with ES688). NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support of OSS doesn't work with it. -There are some ES1688/688 based sound cards and (particularily) motherboards +There are some ES1688/688 based sound cards and (particularly) motherboards which use software configurable I/O port relocation feature of the chip. This ESS proprietary feature is supported only by OSS/Linux. @@ -1319,7 +1319,7 @@ At least a card called (Pearl?) Hypersound 16 supports IRQ 15 but it doesn't work. ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688 -brobably works with OSS/Free after initialization using isapnptools. +probably works with OSS/Free after initialization using isapnptools. Reveal cards ------------ diff --git a/Documentation/sound/README.modules b/Documentation/sound/README.modules index 91d0445860c9..171788d482ac 100644 --- a/Documentation/sound/README.modules +++ b/Documentation/sound/README.modules @@ -71,7 +71,7 @@ Persistent DMA Buffers: The sound modules normally allocate DMA buffers during open() and deallocate them during close(). Linux can often have problems allocating DMA buffers for ISA cards on machines with more than 16MB RAM. This is -because ISA DMA buffers must exist below the 16MB boundry and it is quite +because ISA DMA buffers must exist below the 16MB boundary and it is quite possible that we can't find a large enough free block in this region after the machine has been running for any amount of time. The way to avoid this problem is to allocate the DMA buffers during module load and deallocate diff --git a/Documentation/stallion.txt b/Documentation/stallion.txt index 65f4bcb8a52f..084d485b1581 100644 --- a/Documentation/stallion.txt +++ b/Documentation/stallion.txt @@ -62,7 +62,7 @@ configuration structure. Note that kernel PCI support is required to use PCI boards. There are two methods of configuring ISA, EISA and MCA boards into the drivers. -If using the driver as a loadable module then the simplist method is to pass +If using the driver as a loadable module then the simplest method is to pass the driver configuration as module arguments. The other method is to modify the driver source to add configuration lines for each board in use. @@ -108,7 +108,7 @@ when loading the driver. The general form of the configuration argument is where: - board? -- specifies the arbitary board number of this board, + board? -- specifies the arbitrary board number of this board, can be in the range 0 to 3. name -- textual name of this board. The board name is the comman diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 884a79fc48b6..30e32d315770 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -118,11 +118,11 @@ freepages.min When the number of free pages in the system reaches this number, only the kernel can allocate more memory. freepages.low If the number of free pages gets below this - point, the kernel starts swapping agressively. + point, the kernel starts swapping aggressively. freepages.high The kernel tries to keep up to this amount of memory free; if memory comes below this point, the kernel gently starts swapping in the hopes - that it never has to do real agressive swapping. + that it never has to do real aggressive swapping. ============================================================== @@ -198,7 +198,7 @@ In 2.2, the page cache is used for 3 main purposes: - swap cache When your system is both deep in swap and high on cache, -it probably means that a lot of the swaped data is being +it probably means that a lot of the swapped data is being cached, making for more efficient swapping than possible with the 2.0 kernel. @@ -213,7 +213,7 @@ each processor will be between the low and the high value. On a low-memory, single CPU system you can safely set these values to 0 so you don't waste the memory. On SMP systems it is used so that the system can do fast pagetable allocations -without having to aquire the kernel memory lock. +without having to acquire the kernel memory lock. For large systems, the settings are probably OK. For normal systems they won't hurt a bit. For small systems (<16MB ram) diff --git a/Documentation/usb/bluetooth.txt b/Documentation/usb/bluetooth.txt new file mode 100644 index 000000000000..774f5d3835cc --- /dev/null +++ b/Documentation/usb/bluetooth.txt @@ -0,0 +1,44 @@ +INTRODUCTION + + The USB Bluetooth driver supports any USB Bluetooth device. + It currently works well with the Linux USB Bluetooth stack from Axis + (available at http://developer.axis.com/software/bluetooth/ ) and + has been rumored to work with other Linux USB Bluetooth stacks. + + +CONFIGURATION + + Currently the driver can handle up to 256 different USB Bluetooth + devices at once. + + If you are not using devfs: + The major number that the driver uses is 216 so to use the driver, + create the following nodes: + mknod /dev/ttyUB0 c 216 0 + mknod /dev/ttyUB1 c 216 1 + mknod /dev/ttyUB2 c 216 2 + mknod /dev/ttyUB3 c 216 3 + . + . + . + mknod /dev/ttyUB254 c 216 254 + mknod /dev/ttyUB255 c 216 255 + + If you are using devfs: + The devices supported by this driver will show up as + /dev/usb/ttub/{0,1,...} + + When the device is connected and recognized by the driver, the driver + will print to the system log, which node the device has been bound to. + + +CONTACT: + + If anyone has any problems using this driver, please contact me, or + join the Linux-USB mailing list (information on joining the mailing + list, as well as a link to its searchable archive is at + http://www.linux-usb.org/ ) + + +Greg Kroah-Hartman +greg@kroah.com diff --git a/Documentation/video4linux/CQcam.txt b/Documentation/video4linux/CQcam.txt index 6d54c07c0443..8bbe61103905 100644 --- a/Documentation/video4linux/CQcam.txt +++ b/Documentation/video4linux/CQcam.txt @@ -127,7 +127,7 @@ everything is working. The c-qcam is IEEE1284 compatible, so if you are using the proc file system (CONFIG_PROC_FS), the parallel printer support -(CONFIG_PRINTER), the IEEE 1284 sytem,(CONFIG_PRINTER_READBACK), you +(CONFIG_PRINTER), the IEEE 1284 system,(CONFIG_PRINTER_READBACK), you should be able to read some identification from your quickcam with modprobe -v parport diff --git a/Documentation/video4linux/README.cpia b/Documentation/video4linux/README.cpia index fec205f1aa6a..b9bcefcc9c04 100644 --- a/Documentation/video4linux/README.cpia +++ b/Documentation/video4linux/README.cpia @@ -173,7 +173,7 @@ IMPLEMENTATION NOTES: The camera can act in two modes, streaming or grabbing. Right now a polling grab-scheme is used. Maybe interrupt driven streaming will be -used for a ansychronous mmap interface in the next major release of the +used for a asynchronous mmap interface in the next major release of the driver. This might give a better frame rate. --------------------------------------------------------------------------- diff --git a/Documentation/video4linux/radiotrack.txt b/Documentation/video4linux/radiotrack.txt index fe942e8a9ff5..723f7e4e7e7e 100644 --- a/Documentation/video4linux/radiotrack.txt +++ b/Documentation/video4linux/radiotrack.txt @@ -35,7 +35,7 @@ PHYSICAL DESCRIPTION -------------------- The RadioTrack card is an ISA 8-bit FM radio card. The radio frequency (RF) input is simply an antenna lead, and the output is a power audio signal -available through a miniature phono plug. Its RF frequencies of operation are +available through a miniature phone plug. Its RF frequencies of operation are more or less limited from 87.0 to 109.0 MHz (the commercial FM broadcast band). Although the registers can be programmed to request frequencies beyond these limits, experiments did not give promising results. The variable diff --git a/Makefile b/Makefile index c4c3406a63f8..ddfb26a0906c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 SUBLEVEL = 18 -EXTRAVERSION = pre15 +EXTRAVERSION = pre16 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) diff --git a/arch/alpha/kernel/core_mcpcia.c b/arch/alpha/kernel/core_mcpcia.c index d75711c0179e..6c7bc1097e1e 100644 --- a/arch/alpha/kernel/core_mcpcia.c +++ b/arch/alpha/kernel/core_mcpcia.c @@ -190,7 +190,7 @@ mk_conf_addr(struct linux_hose_info *hose, { unsigned long addr; - if (!pci_probe_enabled) + if (!pci_probe_enabled || !hose->pci_config_space) return -1; DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x," diff --git a/arch/alpha/kernel/core_tsunami.c b/arch/alpha/kernel/core_tsunami.c index 4ca2dca6d9f5..81d08856488f 100644 --- a/arch/alpha/kernel/core_tsunami.c +++ b/arch/alpha/kernel/core_tsunami.c @@ -91,7 +91,7 @@ mk_conf_addr(u8 bus, u8 device_fn, u8 where, struct linux_hose_info *hose, { unsigned long addr; - if (!pci_probe_enabled) + if (!pci_probe_enabled || !hose->pci_config_space) return -1; DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, " diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 65664c76bda7..646c3c19a200 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -543,12 +543,12 @@ smp_boot_cpus(void) bogosum = 0; for (i = 0; i < NR_CPUS; i++) { if (cpu_present_mask & (1L << i)) - bogosum += cpu_data[i].loops_per_sec; + bogosum += cpu_data[i].loops_per_jiffy; } printk(KERN_INFO "SMP: Total of %d processors activated " "(%lu.%02lu BogoMIPS).\n", - cpu_count, (bogosum + 2500) / 500000, - ((bogosum + 2500) / 5000) % 100); + cpu_count, (bogosum + 2500) / (500000 / HZ), + ((bogosum + 2500) / (5000 / HZ)) % 100); smp_num_cpus = cpu_count; } diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index ec85ca63897e..87b1117c7052 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -1142,10 +1142,10 @@ int get_cpuinfo(char * buffer) char *p = buffer; int sep_bug; static char *x86_cap_flags[] = { - "fpu", "vme", "de", "pse", "tsc", "msr", "6", "mce", - "cx8", "9", "10", "sep", "mtrr", "pge", "14", "cmov", - "16", "17", "psn", "19", "20", "21", "22", "mmx", - "24", "kni", "26", "27", "28", "29", "30", "31" + "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", + "cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov", + "16", "pse36", "psn", "19", "20", "21", "22", "mmx", + "24", "xmm", "26", "27", "28", "29", "30", "31" }; struct cpuinfo_x86 *c = cpu_data; int i, n; @@ -1192,7 +1192,8 @@ int get_cpuinfo(char * buffer) x86_cap_flags[10] = "sep"; if (c->x86 < 6) x86_cap_flags[16] = "fcmov"; - x86_cap_flags[16] = "pat"; + else + x86_cap_flags[16] = "pat"; x86_cap_flags[22] = "mmxext"; x86_cap_flags[24] = "fxsr"; x86_cap_flags[30] = "3dnowext"; @@ -1200,17 +1201,11 @@ int get_cpuinfo(char * buffer) break; case X86_VENDOR_INTEL: - x86_cap_flags[6] = "pae"; - x86_cap_flags[9] = "apic"; - x86_cap_flags[14] = "mca"; x86_cap_flags[16] = "pat"; - x86_cap_flags[17] = "pse36"; - x86_cap_flags[18] = "psn"; x86_cap_flags[19] = "cflush"; x86_cap_flags[21] = "dtrace"; x86_cap_flags[22] = "acpi"; x86_cap_flags[24] = "fxsr"; - x86_cap_flags[25] = "xmm"; x86_cap_flags[26] = "xmm2"; x86_cap_flags[27] = "ssnp"; x86_cap_flags[29] = "acc"; diff --git a/arch/m68k/Makefile b/arch/m68k/Makefile index 3a5957b095e2..d5437103ff3e 100644 --- a/arch/m68k/Makefile +++ b/arch/m68k/Makefile @@ -116,6 +116,11 @@ CORE_FILES := $(CORE_FILES) arch/m68k/ifpsp060/ifpsp.o SUBDIRS := $(SUBDIRS) arch/m68k/ifpsp060 endif +ifdef CONFIG_FPU_EMU +CORE_FILES := $(CORE_FILES) arch/m68k/math-emu/mathemu.o +SUBDIRS := $(SUBDIRS) arch/m68k/math-emu +endif + lilo: vmlinux if [ -f $(INSTALL_PATH)/vmlinux ]; then mv -f $(INSTALL_PATH)/vmlinux $(INSTALL_PATH)/vmlinux.old; fi if [ -f $(INSTALL_PATH)/System.map ]; then mv -f $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi @@ -127,7 +132,7 @@ zImage compressed: vmlinux.gz vmlinux.gz: vmlinux -ifdef CONFIG_KGDB +ifndef CONFIG_KGDB cp vmlinux vmlinux.tmp $(STRIP) vmlinux.tmp gzip -9c vmlinux.tmp >vmlinux.gz @@ -136,6 +141,19 @@ else gzip -9c vmlinux >vmlinux.gz endif +bzImage: vmlinux.bz2 + +vmlinux.bz2: vmlinux + +ifndef CONFIG_KGDB + cp vmlinux vmlinux.tmp + $(STRIP) vmlinux.tmp + bzip2 -1c vmlinux.tmp >vmlinux.bz2 + rm vmlinux.tmp +else + bzip2 -1c vmlinux >vmlinux.bz2 +endif + archclean: rm -f vmlinux.gz rm -f arch/m68k/kernel/m68k_defs.h arch/m68k/kernel/m68k_defs.d diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c index fc1b5627f859..589e84d3b50a 100644 --- a/arch/m68k/bvme6000/config.c +++ b/arch/m68k/bvme6000/config.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,15 @@ static unsigned char bin2bcd (unsigned char b); static void (*tick_handler)(int, void *, struct pt_regs *); + +int bvme6000_parse_bootinfo(const struct bi_record *bi) +{ + if (bi->tag == BI_VME_TYPE) + return 0; + else + return 1; +} + int bvme6000_kbdrate (struct kbd_repeat *k) { return 0; @@ -164,7 +174,7 @@ void bvme6000_abort_int (int irq, void *dev_id, struct pt_regs *fp) unsigned long *old = (unsigned long *)0xf8000000; /* Wait for button release */ - while (*config_reg_ptr & BVME_ABORT_STATUS) + while (*(volatile unsigned char *)BVME_LOCAL_IRQ_STAT & BVME_ABORT_STATUS) ; *(new+4) = *(old+4); /* Illegal instruction */ @@ -405,15 +415,6 @@ int bvme6000_keyb_init (void) /*------------------- Serial console stuff ------------------------*/ -static void bvme_scc_write(struct console *co, const char *str, unsigned cnt); - - -void bvme6000_init_console_port (struct console *co, int cflag) -{ - co->write = bvme_scc_write; -} - - static void scc_delay (void) { int n; @@ -423,6 +424,7 @@ static void scc_delay (void) trash = n; } + static void scc_write (char ch) { volatile char *p = (volatile char *)BVME_SCC_A_ADDR; @@ -431,10 +433,7 @@ static void scc_write (char ch) scc_delay(); } while (!(*p & 4)); - scc_delay(); - *p = 8; - scc_delay(); - *p = ch; + *(p + 4) = ch; } @@ -454,3 +453,22 @@ static void bvme_scc_write (struct console *co, const char *str, unsigned count) restore_flags(flags); } + +static int bvme_scc_wait_key (struct console *co) +{ + volatile unsigned char *p = (volatile char *)BVME_SCC_A_ADDR; + + /* wait for rx buf filled */ + while ((*p & 0x01) == 0) + ; + + return *(p + 4); +} + + +void bvme6000_init_console_port (struct console *co, int cflag) +{ + co->write = bvme_scc_write; + co->wait_key = bvme_scc_wait_key; +} + diff --git a/arch/m68k/config.in b/arch/m68k/config.in index 4207efcdb33b..f2028fdf4003 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -58,6 +58,13 @@ bool '68020 support' CONFIG_M68020 bool '68030 support' CONFIG_M68030 bool '68040 support' CONFIG_M68040 bool '68060 support' CONFIG_M68060 +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'Math emulation support' CONFIG_FPU_EMU + if [ "$CONFIG_FPU_EMU" = "y" ]; then + bool 'Math emulation extra precision' CONFIG_FPU_EMU_EXTRAPREC + bool 'Math emulation only kernel' CONFIG_FPU_EMU_ONLY + fi +fi bool 'Advanced configuration options' CONFIG_ADVANCED if [ "$CONFIG_ADVANCED" = "y" ]; then bool 'Use read-modify-write instructions' CONFIG_RMW_INSNS @@ -115,7 +122,7 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then fi fi fi - if [ "$CONFIG_ATARI" == "y" ]; then + if [ "$CONFIG_ATARI" = "y" ]; then dep_tristate ' Atari builtin port' CONFIG_PARPORT_ATARI $CONFIG_PARPORT fi fi @@ -190,8 +197,8 @@ if [ "$CONFIG_ATARI" = "y" ]; then fi fi if [ "$CONFIG_MAC" = "y" ]; then - bool 'MAC NCR5380 SCSI' CONFIG_MAC_SCSI - dep_tristate 'MAC NCR53c9[46] SCSI' CONFIG_SCSI_MAC_ESP $CONFIG_SCSI + tristate 'Macintosh NCR5380 SCSI' CONFIG_MAC_SCSI + dep_tristate 'Macintosh NCR53c9[46] SCSI' CONFIG_SCSI_MAC_ESP $CONFIG_SCSI fi #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI @@ -251,9 +258,15 @@ if [ "$CONFIG_APOLLO" = "y" ] ; then tristate 'Apollo 3c505 support' CONFIG_APOLLO_ELPLUS fi if [ "$CONFIG_MAC" = "y" ]; then - bool 'Mac NS 8390 based ethernet cards' CONFIG_DAYNAPORT -# bool 'Macintosh (AV) onboard MACE ethernet' CONFIG_MACMACE - bool 'Macintosh (Quadra) onboard SONIC ethernet' CONFIG_MACSONIC + tristate 'Macintosh NS 8390 based ethernet cards (new driver)' CONFIG_MAC8390 + if [ "$CONFIG_MAC8390" = "n" ]; then + bool 'Macintosh NS 8390 based ethernet cards (old driver)' CONFIG_DAYNAPORT + fi + tristate 'Macintosh SONIC based ethernet (onboard, NuBus, LC, CS)' CONFIG_MACSONIC + tristate 'Macintosh SMC 9194 based ethernet cards' CONFIG_SMC9194 + tristate 'Macintosh CS89x0 based ethernet cards' CONFIG_MAC89x0 + bool 'Macintosh (AV) onboard MACE ethernet' CONFIG_MACMACE + fi if [ "$CONFIG_VME" = "y" -a "$CONFIG_MVME147" = "y" ]; then tristate 'MVME147 (Lance) Ethernet support' CONFIG_MVME147_NET @@ -336,7 +349,7 @@ if [ "$CONFIG_ATARI" = "y" ]; then tristate 'Atari mouse support' CONFIG_ATARIMOUSE fi if [ "$CONFIG_MAC" = "y" ]; then - bool 'Mac ADB mouse support' CONFIG_ADBMOUSE + bool 'Macintosh ADB mouse support' CONFIG_ADBMOUSE fi if [ "$CONFIG_ATARI" = "y" ]; then tristate 'Atari MFP serial support' CONFIG_ATARI_MFPSER @@ -361,10 +374,13 @@ if [ "$CONFIG_PARPORT" = "n" ]; then dep_tristate 'GVP IO-Extender parallel printer support' CONFIG_GVPIOEXT_LP $CONFIG_GVPIOEXT dep_tristate 'GVP IO-Extender PLIP support' CONFIG_GVPIOEXT_PLIP $CONFIG_GVPIOEXT tristate 'Multiface Card III serial support' CONFIG_MULTIFACE_III_TTY + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool 'HyperCom1 serial support (EXPERIMENTAL)' CONFIG_HYPERCOM1 + fi fi fi if [ "$CONFIG_MAC" = "y" ]; then - bool 'Mac SCC serial support' CONFIG_MAC_SCC + bool 'Macintosh SCC serial support' CONFIG_MAC_SCC fi if [ "$CONFIG_HP300" = "y" -a "$CONFIG_DIO" = "y" ]; then tristate 'HP DCA serial support' CONFIG_HPDCA diff --git a/arch/m68k/fpsp040/bindec.S b/arch/m68k/fpsp040/bindec.S index ef3a627bf895..bb33c48b4078 100644 --- a/arch/m68k/fpsp040/bindec.S +++ b/arch/m68k/fpsp040/bindec.S @@ -484,7 +484,7 @@ A9_str: fmovex (%a0),%fp0 |load X from memory fabsx %fp0 |use abs(X) tstw %d5 |LAMBDA is in lower word of d5 - bnes sc_mul |if neg (LAMBDA = 1), scale by mul + jne sc_mul |if neg (LAMBDA = 1), scale by mul fdivx %fp1,%fp0 |calculate X / SCALE -> Y to fp0 bras A10_st |branch to A10 diff --git a/arch/m68k/fpsp040/decbin.S b/arch/m68k/fpsp040/decbin.S index af1279a4ab6e..76c80e25ed5c 100644 --- a/arch/m68k/fpsp040/decbin.S +++ b/arch/m68k/fpsp040/decbin.S @@ -230,7 +230,7 @@ nextlw: | m_sign: btst #31,(%a0) |test sign of the mantissa - beqs ap_st_z |if clear, go to append/strip zeros + jeq ap_st_z |if clear, go to append/strip zeros fnegx %fp0 |if set, negate fp0 | @@ -288,7 +288,7 @@ ap_st_z: cmpl #27,%d1 |test is with 27 ble pwrten |if abs(expA) <28, skip ap/st zeros btst #30,(%a0) |check sign of exp - bnes ap_st_n |if neg, go to neg side + jne ap_st_n |if neg, go to neg side clrl %d1 |zero count reg movel (%a0),%d4 |load lword 1 to d4 bfextu %d4{#28:#4},%d0 |get M16 in d0 @@ -336,7 +336,7 @@ ap_p_en: tstl %d0 |check if d0 is zero bnes ap_p_el |if not, get next bit fmulx %fp1,%fp0 |mul mantissa by 10**(no_bits_shifted) - bras pwrten |go calc pwrten + jra pwrten |go calc pwrten | | This section handles a negative adjusted exponent. | diff --git a/arch/m68k/fpsp040/do_func.S b/arch/m68k/fpsp040/do_func.S index 2df0c3700130..1f2257ca70ac 100644 --- a/arch/m68k/fpsp040/do_func.S +++ b/arch/m68k/fpsp040/do_func.S @@ -77,7 +77,7 @@ not_fmovecr: movew CMDREG1B(%a6),%d0 andl #0x7F,%d0 cmpil #0x38,%d0 |if the extension is >= $38, - bges serror |it is illegal + jge serror |it is illegal bfextu STAG(%a6){#0:#3},%d1 lsll #3,%d0 |make room for STAG addl %d1,%d0 |combine for final index into table diff --git a/arch/m68k/fpsp040/get_op.S b/arch/m68k/fpsp040/get_op.S index 2bd236d455f6..e46e3ea5957e 100644 --- a/arch/m68k/fpsp040/get_op.S +++ b/arch/m68k/fpsp040/get_op.S @@ -171,7 +171,7 @@ PTENRP: get_op: clrb DY_MO_FLG(%a6) tstb UFLG_TMP(%a6) |test flag for unsupp/unimp state - beqs uni_getop + jeq uni_getop uns_getop: btstb #direction_bit,CMDREG1B(%a6) diff --git a/arch/m68k/fpsp040/skeleton.S b/arch/m68k/fpsp040/skeleton.S index 06d22b7b7851..1ddbca6ced4a 100644 --- a/arch/m68k/fpsp040/skeleton.S +++ b/arch/m68k/fpsp040/skeleton.S @@ -107,7 +107,7 @@ inex: bnes not_fmt40 fmovel %fpsr,-(%sp) btstb #E1,E_BYTE(%a6) |test for E1 set - beqs not_b1232 + beq not_b1232 btstb #snan_bit,2(%sp) |test for snan beq inex_ckofl addl #4,%sp diff --git a/arch/m68k/fpsp040/util.S b/arch/m68k/fpsp040/util.S index e59b352ab7dc..74fe26c75aa7 100644 --- a/arch/m68k/fpsp040/util.S +++ b/arch/m68k/fpsp040/util.S @@ -138,20 +138,20 @@ ovf_e1_exc: | ovf_fsgl: clrl %d0 - bras ovf_res + jra ovf_res ovff_sgl: movel #0x00000001,%d0 |set single - bras ovf_res + jra ovf_res ovff_dbl: movel #0x00000002,%d0 |set double - bras ovf_res + jra ovf_res | | The precision is in the fpcr. | ovf_fpcr: bfextu FPCR_MODE(%a6){#0:#2},%d0 |set round precision - bras ovf_res + jra ovf_res | | diff --git a/arch/m68k/fpsp040/x_store.S b/arch/m68k/fpsp040/x_store.S index a3468f3386e9..b06715fecb03 100644 --- a/arch/m68k/fpsp040/x_store.S +++ b/arch/m68k/fpsp040/x_store.S @@ -92,7 +92,7 @@ opc011: cmpil #0,%d0 |if dest format is extended beq dest_ext |then branch cmpil #1,%d0 |if dest format is single - beqs dest_sgl |then branch + jeq dest_sgl |then branch | | fall through to dest_dbl | diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index 61482c3a8654..d9c829eebcce 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -90,8 +90,18 @@ do_trace: jbsr SYMBOL_NAME(syscall_trace) SYMBOL_NAME_LABEL(ret_from_signal) - RESTORE_SWITCH_STACK + RESTORE_SWITCH_STACK + addql #4,%sp +/* on 68040 complete pending writebacks if any */ +#ifdef CONFIG_M68040 + bfextu %sp@(PT_VECTOR){#0,#4},%d0 + subql #7,%d0 | bus error frame ? + jbne 1f + movel %sp,%sp@- + jbsr SYMBOL_NAME(berr_040cleanup) addql #4,%sp +1: +#endif jra SYMBOL_NAME(ret_from_exception) ENTRY(system_call) @@ -283,6 +293,11 @@ SYMBOL_NAME_LABEL(resume) movel %sp,%a0@(TASK_TSS+TSS_KSP) /* save floating point context */ +#ifndef CONFIG_FPU_EMU_ONLY +#ifdef CONFIG_FPU_EMU + tstl SYMBOL_NAME(m68k_fputype) + jeq 3f +#endif fsave %a0@(TASK_TSS+TSS_FPSTATE) #if defined(CONFIG_M68060) @@ -304,6 +319,7 @@ SYMBOL_NAME_LABEL(resume) 2: fmovemx %fp0-%fp7,%a0@(TASK_TSS+TSS_FPREG) fmoveml %fpcr/%fpsr/%fpiar,%a0@(TASK_TSS+TSS_FPCNTL) 3: +#endif /* CONFIG_FPU_EMU_ONLY */ /* Return previous task in %d1 */ movel %curptr,%d1 @@ -374,7 +390,11 @@ SYMBOL_NAME_LABEL(resume) 2: 4: /* restore floating point context */ - +#ifndef CONFIG_FPU_EMU_ONLY +#ifdef CONFIG_FPU_EMU + tstl SYMBOL_NAME(m68k_fputype) + jeq 4f +#endif #if defined(CONFIG_M68060) #if !defined(CPU_M68060_ONLY) btst #3,SYMBOL_NAME(m68k_cputype)+3 @@ -394,6 +414,8 @@ SYMBOL_NAME_LABEL(resume) 2: fmovemx %a1@(TASK_TSS+TSS_FPREG),%fp0-%fp7 fmoveml %a1@(TASK_TSS+TSS_FPCNTL),%fpcr/%fpsr/%fpiar 3: frestore %a1@(TASK_TSS+TSS_FPSTATE) +4: +#endif /* CONFIG_FPU_EMU_ONLY */ /* restore the kernel stack pointer */ movel %a1@(TASK_TSS+TSS_KSP),%sp diff --git a/arch/m68k/kernel/head.S b/arch/m68k/kernel/head.S index 361b10cb663a..c4c59af96b36 100644 --- a/arch/m68k/kernel/head.S +++ b/arch/m68k/kernel/head.S @@ -130,7 +130,7 @@ * * mmu_engage * ---------- - * Thanks to a small helping routine enabling the mmu got quiet simple + * Thanks to a small helping routine enabling the mmu got quite simple * and there is only one way left. mmu_engage makes a complete a new mapping * that only includes the absolute necessary to be able to jump to the final * postion and to restore the original mapping. @@ -271,6 +271,7 @@ */ #define CONSOLE +#define CONSOLE_PENGUIN /* * Macintosh serial debug support; outputs boot info to the printer @@ -305,7 +306,7 @@ .globl SYMBOL_NAME(m68k_pgtable_cachemode) .globl SYMBOL_NAME(m68k_supervisor_cachemode) #ifdef CONFIG_MVME16x -.globl SYMBOL_NAME(mvme_bdid_ptr) +.globl SYMBOL_NAME(mvme_bdid) #endif #ifdef CONFIG_Q40 .globl SYMBOL_NAME(q40_mem_cptr) @@ -386,6 +387,17 @@ PAGE_INDEX_SHIFT = 12 #endif #endif +/* A macro to make relocating pointers read from memory structures easier. + * Remember, we're not necessarily running where the linker thinks we are, + * yet, so pointers given by the linker must be manually relocated to be + * valid. [CSA 13-May-1999] + */ +.macro load_and_relocate reference,dest +0: lea %pc@(0b),\dest + subl #0b,\dest + addl \reference,\dest +.endm + /* Several macros to make the writing of subroutines easier: * - func_start marks the beginning of the routine which setups the frame * register and saves the registers, it also defines another macro @@ -472,6 +484,12 @@ func_define set_leds func_define serial_putc,1 func_define console_putc,1 +func_define console_init +func_define console_put_stats +func_define console_put_penguin +func_define console_plot_pixel,3 +func_define console_scroll + .macro putc ch #if defined(CONSOLE) || defined(SERIAL_DEBUG) pea \ch @@ -503,10 +521,18 @@ func_define putn,1 .macro puts string #if defined(CONSOLE) || defined(SERIAL_DEBUG) +/* The __INITDATA stuff is a no-op when ftrace or kgdb are turned on */ +#if defined(CONFIG_FTRACE) || defined(CONFIG_KGDB) + bra 1f +#endif __INITDATA .Lstr\@: .string "\string" __FINIT +#if defined(CONFIG_FTRACE) || defined(CONFIG_KGDB) + .align 2 +1: +#endif pea %pc@(.Lstr\@) func_call puts addql #4,%sp @@ -525,6 +551,9 @@ func_define putn,1 #define is_not_mvme147(lab) cmpl &MACH_MVME147,%pc@(m68k_machtype); jne lab #define is_not_mvme16x(lab) cmpl &MACH_MVME16x,%pc@(m68k_machtype); jne lab #define is_not_bvme6000(lab) cmpl &MACH_BVME6000,%pc@(m68k_machtype); jne lab +#define is_mvme147(lab) cmpl &MACH_MVME147,%pc@(m68k_machtype); jeq lab +#define is_mvme16x(lab) cmpl &MACH_MVME16x,%pc@(m68k_machtype); jeq lab +#define is_bvme6000(lab) cmpl &MACH_BVME6000,%pc@(m68k_machtype); jeq lab #define is_not_hp300(lab) cmpl &MACH_HP300,%pc@(m68k_machtype); jne lab #define is_not_q40(lab) cmpl &MACH_Q40,%pc@(m68k_machtype); jne lab #define is_not_sun3x(lab) cmpl &MACH_SUN3X,%pc@(m68k_machtype); jne lab @@ -801,6 +830,57 @@ L(save_cachetype): L(notypetest): #endif +#ifdef CONFIG_VME + is_mvme147(L(getvmetype)) + is_bvme6000(L(getvmetype)) + is_not_mvme16x(L(gvtdone)) + + /* See if the loader has specified the BI_VME_TYPE tag. Recent + * versions of VMELILO and TFTPLILO do this. We have to do this + * early so we know how to handle console output. If the tag + * doesn't exist then we use the Bug for output on MVME16x. + */ +L(getvmetype): + get_bi_record BI_VME_TYPE + tstl %d0 + jbmi 1f + movel %a0@,%d3 + lea %pc@(SYMBOL_NAME(vme_brdtype)),%a0 + movel %d3,%a0@ +1: +#ifdef CONFIG_MVME16x + is_not_mvme16x(L(gvtdone)) + + /* Need to get the BRD_ID info to diferentiate between 162, 167, + * etc. This is available as a BI_VME_BRDINFO tag with later + * versions of VMELILO and TFTPLILO, otherwise we call the Bug. + */ + get_bi_record BI_VME_BRDINFO + tstl %d0 + jpl 1f + + /* Get pointer to board ID data from Bug */ + movel %d2,%sp@- + trap #15 + .word 0x70 /* trap 0x70 - .BRD_ID */ + movel %sp@+,%a0 +1: + lea %pc@(SYMBOL_NAME(mvme_bdid)),%a1 + /* Structure is 32 bytes long */ + movel %a0@+,%a1@+ + movel %a0@+,%a1@+ + movel %a0@+,%a1@+ + movel %a0@+,%a1@+ + movel %a0@+,%a1@+ + movel %a0@+,%a1@+ + movel %a0@+,%a1@+ + movel %a0@+,%a1@+ +#endif + +L(gvtdone): + +#endif + /* * Initialize serial port */ @@ -812,11 +892,11 @@ L(notypetest): #ifdef CONFIG_MAC is_not_mac(L(nocon)) #ifdef CONSOLE - jbsr L(console_init) + console_init #ifdef CONSOLE_PENGUIN - jbsr L(console_put_penguin) + console_put_penguin #endif /* CONSOLE_PENGUIN */ - jbsr L(console_put_stats) + console_put_stats #endif /* CONSOLE */ L(nocon): #endif /* CONFIG_MAC */ @@ -998,14 +1078,6 @@ L(not147): is_not_mvme16x(L(not16x)) - /* Get pointer to board ID data */ - movel %d2,%sp@- - trap #15 - .word 0x70 /* trap 0x70 - .BRD_ID */ - movel %sp@+,%d2 - lea %pc@(SYMBOL_NAME(mvme_bdid_ptr)),%a0 - movel %d2,%a0@ - /* * On MVME16x we have already created kernel page tables for * 4MB of RAM at address 0, so now need to do a transparent @@ -1082,10 +1154,6 @@ L(mmu_init_mac): putc 'F' - lea %pc@(L(mac_videobase)),%a0 - lea %pc@(L(console_video_virtual)),%a1 - movel %a0@,%a1@ - is_not_040_or_060(1f) moveq #_PAGE_NOCACHE_S,%d3 @@ -1099,15 +1167,16 @@ L(mmu_init_mac): */ movel #VIDEOMEMMASK,%d0 - andl L(mac_videobase),%d0 + andl %pc@(L(mac_videobase)),%d0 mmu_map #VIDEOMEMBASE,%d0,#VIDEOMEMSIZE,%d3 - mmu_map_eq #0x40800000,#0x02000000,%d3 /* rom ? */ - mmu_map_eq #0x50000000,#0x02000000,%d3 - mmu_map_eq #0x60000000,#0x00400000,%d3 - mmu_map_eq #0x9c000000,#0x00400000,%d3 + /* IO devices (incl. serial port) from 5000 0000 to 5300 0000 */ + mmu_map_eq #0x50000000,#0x03000000,%d3 + /* Nubus slot space (video at 0xF0000000, rom at 0xF0F80000) */ mmu_map_tt #1,#0xf8000000,#0x08000000,%d3 - + /* ROM from 4000 0000 to 4200 0000 */ + mmu_map_eq #0x40000000,#0x02000000,%d3 /* only for mac_reset() */ + jbra L(mmu_init_done) L(mmu_init_not_mac): @@ -1261,6 +1330,9 @@ L(mmu_fixup_done): andl L(mac_videobase),%d0 addl #VIDEOMEMBASE,%d0 movel %d0,L(mac_videobase) +#ifdef MAC_SERIAL_DEBUG + orl #0x50000000,L(mac_sccbase) +#endif 1: #endif @@ -1661,7 +1733,7 @@ mmu_030_print_flags: putZc('C','I') /* write through or copy-back */ rts -mmu_030_print: +mmu_030_print: /* print a 3-level (max) translation tree using 3 nested loops*/ puts "\nMMU030\n" puts "\nrp:" putn %a5 @@ -1670,62 +1742,66 @@ mmu_030_print: andil #0xfffffff0,%d0 movel %d0,%a0 movel #0x00000000,%a4 /* logical address */ - movel #0,%d0 + movel #0,%d0 /* iteration counter for outer loop */ 30: movel %a4,%d5 - addil #PAGESIZE<<13,%d5 + addil #PAGESIZE<<13,%d5 /* next logical address */ movel %a0@+,%d6 - btst #1,%d6 /* is it a ptr? */ - jbne 31f /* yes */ + btst #1,%d6 /* is it a table ptr? */ + jbne 31f /* if yes, jump */ btst #0,%d6 /* is it early terminating? */ - jbeq 1f /* no */ - jbsr mmu_030_print_helper + jbeq 1f /* if no, jump */ + /* DT (descriptor tag) = $01 (PAGE DESCRIPTOR) (early termination) */ + jbsr mmu_030_print_helper /* print " %a4-> %d6"+flags(%d6) */ jbra 38f -1: - jbsr mmu_print_tuple_invalidate +1: /* DT (descriptor tag) = $00 (INVALID) */ + jbsr mmu_print_tuple_invalidate /* prints " %a4##" */ jbra 38f -31: - movel #0,%d1 +31: /* DT (descriptor tag) = $1x (table pointer with 4 or 8 byte entries)*/ + movel #0,%d1 /* iteration counter for middle loop */ andil #0xfffffff0,%d6 movel %d6,%a1 32: movel %a4,%d5 addil #PAGESIZE<<6,%d5 movel %a1@+,%d6 - btst #1,%d6 - jbne 33f - btst #0,%d6 - jbeq 1f /* no */ - jbsr mmu_030_print_helper + btst #1,%d6 /* is it a table ptr? */ + jbne 33f /* if so, jump */ + btst #0,%d6 /* is it a page descriptor? */ + jbeq 1f /* if no, jump */ + /* DT (descriptor tag) = $01 (PAGE DESCRIPTOR) */ + jbsr mmu_030_print_helper /* prints " %a4-> %d6"+flags(%d6) */ jbra 37f -1: - jbsr mmu_print_tuple_invalidate +1: /* DT (descriptor tag) = $00 (INVALID) */ + jbsr mmu_print_tuple_invalidate /* prints " %a4##" */ jbra 37f -33: - movel #0,%d2 +33: /* DT (descriptor tag) = $1x (table pointer with 4 or 8 byte entries)*/ + movel #0,%d2 /* iteration counter for inner loop */ andil #0xfffffff0,%d6 movel %d6,%a2 34: movel %a4,%d5 addil #PAGESIZE,%d5 movel %a2@+,%d6 + /* note that we assume there are no more table indirections left */ btst #0,%d6 jbne 35f - jbsr mmu_print_tuple_invalidate + /* DT (descriptor tag) = $00 (INVALID) */ + jbsr mmu_print_tuple_invalidate /* prints " %a4##" */ jbra 36f -35: - jbsr mmu_030_print_helper -36: +35: /* DT (descriptor tag) = $01 (PAGE DESCRIPTOR) */ + jbsr mmu_030_print_helper /* prints " %a4-> %d6"+flags(%d6) */ +36: /* continue inner loop */ movel %d5,%a4 addq #1,%d2 cmpib #64,%d2 jbne 34b -37: - movel %d5,%a4 +37: /* continue middle loop */ + movel %d5,%a4 /* move to the next logical address */ addq #1,%d1 cmpib #128,%d1 jbne 32b -38: +38: /* continue outer loop */ movel %d5,%a4 /* move to the next logical address */ addq #1,%d0 cmpib #128,%d0 @@ -1746,6 +1822,13 @@ mmu_030_print_helper: moveml %sp@+,%d0-%d1 rts +/* mmu_print_tuple_invalidate(%a4=this_logical) { + * if (mmu_print_data.mmu_next_valid != INVALID) { + * mmu_print_data.mmu_next_valid = INVALID; + * printf(" %08x##\n", this_logical); + * } + * } [rough C translation by CSA, not the original author] + */ mmu_print_tuple_invalidate: moveml %a0/%d7,%sp@- @@ -1764,6 +1847,19 @@ mmu_print_tuple_invalidate_exit: rts +/* mmu_print_tuple(%d0=logical,%d1=physical, + * %d5=next_logical,%a4=this_logical, + * %a6=print_flags) { + * if (mmu_print_data.mmu_next_valid != VALID || + * mmu_print_data.mmu_next_physical != physical) { + * printf(" %08x-> %08x", logical, physical); + * print_flags(physical); + * mmu_print_data.mmu_next_valid = VALID; + * mmu_print_data.mmu_next_physical=physical; + * } + * mmu_print_data.mmu_next_physical += (next_logical-this_logical); + * } [rough C translation by CSA, not the original author] + */ mmu_print_tuple: moveml %d0-%d7/%a0,%sp@- @@ -2168,7 +2264,7 @@ func_start mmu_fixup_page_mmu_cache,%d0/%a0 func_return mmu_fixup_page_mmu_cache /* - * mmu_temp_map + * mmu_temp_map (ARG1=physical_addr, ARG2=logical_addr) * * create a temporary mapping to enable the mmu, * this we don't need any transparation translation tricks. @@ -2834,10 +2930,6 @@ func_start serial_putc,%d0/%d1/%a0/%a1 #ifdef CONFIG_MAC is_not_mac(5f) -#ifdef CONSOLE - console_putc %d0 -#endif /* CONSOLE */ - #ifdef MAC_SERIAL_DEBUG #ifdef MAC_USE_SCC_A @@ -2903,13 +2995,54 @@ func_start serial_putc,%d0/%d1/%a0/%a1 #ifdef CONFIG_MVME16x is_not_mvme16x(2f) /* - * The VME 16x class has PROM support for serial output - * of some kind; the TRAP table is still valid. + * If the loader gave us a board type then we can use that to + * select an appropriate output routine; otherwise we just use + * the Bug code. If we haev to use the Bug that means the Bug + * workspace has to be valid, which means the Bug has to use + * the SRAM, which is non-standard. */ moveml %d0-%d7/%a2-%a6,%sp@- + movel SYMBOL_NAME(vme_brdtype),%d1 + jeq 1f | No tag - use the Bug + cmpi #VME_TYPE_MVME162,%d1 + jeq 6f + cmpi #VME_TYPE_MVME172,%d1 + jne 5f + /* 162/172; it's an SCC */ +6: btst #2,M162_SCC_CTRL_A + nop + nop + nop + jeq 6b + moveb #8,M162_SCC_CTRL_A + nop + nop + nop + moveb %d0,M162_SCC_CTRL_A + jra 3f +5: + /* 166/167/177; its a CD2401 */ + moveb #0,M167_CYCAR + moveb M167_CYIER,%d2 + moveb #0x02,M167_CYIER +7: + btst #5,M167_PCSCCTICR + jeq 7b + moveb M167_PCTPIACKR,%d1 + moveb M167_CYLICR,%d1 + jeq 8f + moveb #0x08,M167_CYTEOIR + jra 7b +8: + moveb %d0,M167_CYTDR + moveb #0,M167_CYTEOIR + moveb %d2,M167_CYIER + jra 3f +1: moveb %d0,%sp@- trap #15 .word 0x0020 /* TRAP 0x020 */ +3: moveml %sp@+,%d0-%d7/%a2-%a6 jbra L(serial_putc_done) 2: @@ -3004,20 +3137,18 @@ func_return putn * simple strings! */ ENTRY(mac_serial_print) +#ifdef MAC_SERIAL_DEBUG moveml %d0/%a0,%sp@- -#if 1 move %sr,%sp@- ori #0x0700,%sr -#endif - movel %sp@(10),%a0 /* fetch parameter */ + movel %sp@(14),%a0 /* fetch parameter */ jra 2f 1: serial_putc %d0 2: moveb %a0@+,%d0 jne 1b -#if 1 move %sp@+,%sr -#endif moveml %sp@+,%d0/%a0 +#endif /* MAC_SERIAL_DEBUG */ rts #endif /* CONFIG_MAC */ @@ -3041,7 +3172,7 @@ func_return set_leds #define Lconsole_struct_left_edge 16 #define Lconsole_struct_penguin_putc 20 -L(console_init): +func_start console_init,%a0-%a4/%d0-%d7 /* * Some of the register usage that follows * a0 = pointer to boot_info @@ -3055,49 +3186,57 @@ L(console_init): * d5 = number of bytes per scan line * d6 = number of bytes on the entire screen */ - moveml %a0-%a4/%d0-%d7,%sp@- lea %pc@(L(console_globals)),%a2 - lea %pc@(L(mac_videobase)),%a0 - movel %a0@,%a1 - lea %pc@(L(mac_rowbytes)),%a0 - movel %a0@,%d5 - lea %pc@(L(mac_dimensions)),%a0 - movel %a0@,%d3 /* -> low byte */ + movel %pc@(L(mac_videobase)),%a1 + movel %pc@(L(mac_rowbytes)),%d5 + movel %pc@(L(mac_dimensions)),%d3 /* -> low word */ movel %d3,%d4 - swap %d4 /* -> high byte */ - andl #0xffff,%d3 /* d3 = screen width in pixels */ - andl #0xffff,%d4 /* d4 = screen height in pixels */ + swap %d4 /* -> high word */ + andl #0xffff,%d3 /* d3 = screen width in pixels */ + andl #0xffff,%d4 /* d4 = screen height in pixels */ movel %d5,%d6 - subl #20,%d6 - mulul %d4,%d6 /* scan line bytes x num scan lines */ - divul #8,%d6 /* we'll clear 8 bytes at a time */ +| subl #20,%d6 + mulul %d4,%d6 /* scan line bytes x num scan lines */ + lsrl #3,%d6 /* we'll clear 8 bytes at a time */ + moveq #-1,%d0 /* Mac_black */ subq #1,%d6 -console_clear_loop: - movel #0xffffffff,%a1@+ /* Mac_black */ - movel #0xffffffff,%a1@+ /* Mac_black */ - dbra %d6,console_clear_loop +L(console_clear_loop): + movel %d0,%a1@+ + movel %d0,%a1@+ + dbra %d6,L(console_clear_loop) /* Calculate font size */ - + /* Use absolute (not pc-relative) pointer here; which means we'll + * have to do relocation every time before we use it. + * (remember, before mmu_engage we're probably not running where + * the linker expected that we'd end up.) We could store + * %pc@(SYMBOL_NAME(font_xxx)) in memory instead, but if + * everyone did this we'd end up with quite a motley collection of + * fixup code after mmu_engage, which offends our spirit of + * disentangledness (code for similar things together, code for + * unrelated things apart). + */ + #if defined(FONT_8x8) - lea %pc@(SYMBOL_NAME(font_vga_8x8)), %a0 + lea SYMBOL_NAME(font_vga_8x8), %a0 #elif defined(FONT_8x16) - lea %pc@(SYMBOL_NAME(font_vga_8x16)),%a0 + lea SYMBOL_NAME(font_vga_8x16),%a0 #elif defined(FONT_6x11) - lea %pc@(SYMBOL_NAME(font_vga_6x11)),%a0 + lea SYMBOL_NAME(font_vga_6x11),%a0 #else /* (FONT_8x8) default */ - lea %pc@(SYMBOL_NAME(font_vga_8x8)), %a0 + lea SYMBOL_NAME(font_vga_8x8), %a0 #endif /* * At this point we make a shift in register usage - * a1 = address of Lconsole_font pointer + * a1 = address of console_font pointer */ lea %pc@(L(console_font)),%a1 - movel %a0,%a1@ /* store pointer to struct fbcon_font_desc in Lconsole_font */ + movel %a0,%a1@ /* store pointer to struct fbcon_font_desc in console_font */ + load_and_relocate %a1@,%a0 /* now relocate the pointer. */ /* * Calculate global maxs @@ -3106,11 +3245,11 @@ console_clear_loop: * 6 x 11 also supported */ /* ASSERT: a0 = contents of Lconsole_font */ - movel %d3,%d0 /* screen width in pixels */ - divul %a0@(FBCON_FONT_DESC_WIDTH),%d0 /* d0 = max num chars per row */ + movel %d3,%d0 /* screen width in pixels */ + divul %a0@(FBCON_FONT_DESC_WIDTH),%d0 /* d0 = max num chars per row */ - movel %d4,%d1 /* screen height in pixels */ - divul %a0@(FBCON_FONT_DESC_HEIGHT),%d1 /* d1 = max num rows */ + movel %d4,%d1 /* screen height in pixels */ + divul %a0@(FBCON_FONT_DESC_HEIGHT),%d1 /* d1 = max num rows */ movel %d0,%a2@(Lconsole_struct_num_columns) movel %d1,%a2@(Lconsole_struct_num_rows) @@ -3125,16 +3264,14 @@ console_clear_loop: /* * Initialization is complete */ - moveml %sp@+,%a0-%a4/%d0-%d7 - rts +func_return console_init -L(console_put_stats): +func_start console_put_stats,%a0/%d7 /* * Some of the register usage that follows * a0 = pointer to boot_info * d7 = value of boot_info fields */ - moveml %a0/%d7,%sp@- puts "\nMacLinux\n\n" @@ -3154,21 +3291,28 @@ L(console_put_stats): putn %pc@(L(cputype)) putc '\n' + putn %pc@(L(mac_videodepth)) + putn %pc@(L(mac_dimensions)) + putn %pc@(L(mac_rowbytes)) + putn %pc@(L(mac_videodepth)) +#ifdef MAC_SERIAL_DEBUG + putn %pc@(L(mac_sccbase)) +#endif + putc '\n' + # if defined(MMU_PRINT) jbsr mmu_print_machine_cpu_types # endif /* MMU_PRINT */ #endif /* SERIAL_DEBUG */ - moveml %sp@+,%a0/%d7 - rts +func_return console_put_stats #ifdef CONSOLE_PENGUIN -L(console_put_penguin): +func_start console_put_penguin,%a0-%a1/%d0-%d7 /* * Get 'that_penguin' onto the screen in the upper right corner * penguin is 64 x 74 pixels, align against right edge of screen */ - moveml %a0-%a1/%d0-%d7,%sp@- lea %pc@(L(mac_dimensions)),%a0 movel %a0@,%d0 @@ -3177,40 +3321,42 @@ L(console_put_penguin): clrl %d1 /* start at the top */ movel #73,%d7 lea %pc@(SYMBOL_NAME(that_penguin)),%a1 -console_penguin_row: +L(console_penguin_row): movel #31,%d6 -console_penguin_pixel_pair: +L(console_penguin_pixel_pair): moveb %a1@,%d2 lsrb #4,%d2 - jbsr console_plot_pixel + console_plot_pixel %d0,%d1,%d2 addq #1,%d0 moveb %a1@+,%d2 - jbsr console_plot_pixel + console_plot_pixel %d0,%d1,%d2 addq #1,%d0 - dbra %d6,console_penguin_pixel_pair + dbra %d6,L(console_penguin_pixel_pair) subil #64,%d0 addq #1,%d1 - dbra %d7,console_penguin_row + dbra %d7,L(console_penguin_row) - moveml %sp@+,%a0-%a1/%d0-%d7 - rts -#endif +func_return console_put_penguin -console_scroll: - moveml %a0-%a4/%d0-%d7,%sp@- +/* include penguin bitmap */ +SYMBOL_NAME_LABEL(that_penguin) +#include "../mac/mac_penguin.S" +#endif /* * Calculate source and destination addresses * output a1 = dest * a2 = source */ + +func_start console_scroll,%a0-%a4/%d0-%d7 lea %pc@(L(mac_videobase)),%a0 movel %a0@,%a1 movel %a1,%a2 lea %pc@(L(mac_rowbytes)),%a0 movel %a0@,%d5 - movel %pc@(L(console_font)),%a0 + load_and_relocate %pc@(L(console_font)),%a0 mulul %a0@(FBCON_FONT_DESC_HEIGHT),%d5 /* account for # scan lines per character */ addal %d5,%a2 @@ -3229,13 +3375,13 @@ console_scroll: */ lea %pc@(L(mac_rowbytes)),%a0 movel %a0@,%d6 - movel %pc@(L(console_font)),%a0 + load_and_relocate %pc@(L(console_font)),%a0 subl %a0@(FBCON_FONT_DESC_HEIGHT),%d4 /* we're not scrolling the top row! */ mulul %d4,%d6 /* scan line bytes x num scan lines */ divul #32,%d6 /* we'll move 8 longs at a time */ subq #1,%d6 -console_scroll_loop: +L(console_scroll_loop): movel %a2@+,%a1@+ movel %a2@+,%a1@+ movel %a2@+,%a1@+ @@ -3244,17 +3390,17 @@ console_scroll_loop: movel %a2@+,%a1@+ movel %a2@+,%a1@+ movel %a2@+,%a1@+ - dbra %d6,console_scroll_loop + dbra %d6,L(console_scroll_loop) lea %pc@(L(mac_rowbytes)),%a0 movel %a0@,%d6 - movel %pc@(L(console_font)),%a0 + load_and_relocate %pc@(L(console_font)),%a0 mulul %a0@(FBCON_FONT_DESC_HEIGHT),%d6 /* scan line bytes x font height */ divul #32,%d6 /* we'll move 8 words at a time */ subq #1,%d6 moveq #-1,%d0 -console_scroll_clear_loop: +L(console_scroll_clear_loop): movel %d0,%a1@+ movel %d0,%a1@+ movel %d0,%a1@+ @@ -3263,15 +3409,13 @@ console_scroll_clear_loop: movel %d0,%a1@+ movel %d0,%a1@+ movel %d0,%a1@+ - dbra %d6,console_scroll_clear_loop - - moveml %sp@+,%a0-%a4/%d0-%d7 - rts + dbra %d6,L(console_scroll_clear_loop) +func_return console_scroll func_start console_putc,%a0/%a1/%d0-%d7 - is_not_mac(console_exit) + is_not_mac(L(console_exit)) /* Output character in d7 on console. */ @@ -3285,7 +3429,7 @@ func_start console_putc,%a0/%a1/%d0-%d7 lea %pc@(L(console_globals)),%a0 cmpib #10,%d7 - jne console_not_lf + jne L(console_not_lf) movel %a0@(Lconsole_struct_cur_row),%d0 addil #1,%d0 movel %d0,%a0@(Lconsole_struct_cur_row) @@ -3294,22 +3438,22 @@ func_start console_putc,%a0/%a1/%d0-%d7 jcs 1f subil #1,%d0 movel %d0,%a0@(Lconsole_struct_cur_row) - jbsr console_scroll + console_scroll 1: - jra console_exit + jra L(console_exit) -console_not_lf: +L(console_not_lf): cmpib #13,%d7 - jne console_not_cr + jne L(console_not_cr) clrl %a0@(Lconsole_struct_cur_column) - jra console_exit + jra L(console_exit) -console_not_cr: +L(console_not_cr): cmpib #1,%d7 - jne console_not_home + jne L(console_not_home) clrl %a0@(Lconsole_struct_cur_row) clrl %a0@(Lconsole_struct_cur_column) - jra console_exit + jra L(console_exit) /* * At this point we know that the %d7 character is going to be @@ -3320,13 +3464,13 @@ console_not_cr: * d1 = cursor row to draw the character * d7 = character number */ -console_not_home: +L(console_not_home): movel %a0@(Lconsole_struct_cur_column),%d0 - addil #1,%a0@(Lconsole_struct_cur_column) + addql #1,%a0@(Lconsole_struct_cur_column) movel %a0@(Lconsole_struct_num_columns),%d1 cmpl %d1,%d0 jcs 1f - putc '\n' /* recursion is OK! */ + console_putc #'\n' /* recursion is OK! */ 1: movel %a0@(Lconsole_struct_cur_row),%d1 @@ -3334,8 +3478,8 @@ console_not_home: * At this point we make a shift in register usage * a0 = address of pointer to font data (fbcon_font_desc) */ - movel %pc@(L(console_font)),%a0 - movel %a0@(FBCON_FONT_DESC_DATA),%a1 /* Load fbcon_font_desc.data into a1 */ + load_and_relocate %pc@(L(console_font)),%a0 + load_and_relocate %a0@(FBCON_FONT_DESC_DATA),%a1 andl #0x000000ff,%d7 /* ASSERT: a0 = contents of Lconsole_font */ mulul %a0@(FBCON_FONT_DESC_HEIGHT),%d7 /* d7 = index into font data */ @@ -3350,35 +3494,34 @@ console_not_home: * d6 = count down for the font's pixel width (8) * d7 = count down for the font's pixel count in height */ + /* ASSERT: a0 = contents of Lconsole_font */ mulul %a0@(FBCON_FONT_DESC_WIDTH),%d0 mulul %a0@(FBCON_FONT_DESC_HEIGHT),%d1 movel %a0@(FBCON_FONT_DESC_HEIGHT),%d7 /* Load fbcon_font_desc.height into d7 */ subq #1,%d7 -console_read_char_scanline: +L(console_read_char_scanline): moveb %a1@+,%d3 /* ASSERT: a0 = contents of Lconsole_font */ movel %a0@(FBCON_FONT_DESC_WIDTH),%d6 /* Load fbcon_font_desc.width into d6 */ subql #1,%d6 -console_do_font_scanline: +L(console_do_font_scanline): lslb #1,%d3 scsb %d2 /* convert 1 bit into a byte */ - jbsr console_plot_pixel + console_plot_pixel %d0,%d1,%d2 addq #1,%d0 - dbra %d6,console_do_font_scanline + dbra %d6,L(console_do_font_scanline) /* ASSERT: a0 = contents of Lconsole_font */ subl %a0@(FBCON_FONT_DESC_WIDTH),%d0 addq #1,%d1 - dbra %d7,console_read_char_scanline - -console_exit: + dbra %d7,L(console_read_char_scanline) +L(console_exit): func_return console_putc -console_plot_pixel: /* * Input: * d0 = x coordinate @@ -3386,14 +3529,14 @@ console_plot_pixel: * d2 = (bit 0) 1/0 for white/black (!) * All registers are preserved */ - moveml %a0-%a1/%d0-%d4,%sp@- +func_start console_plot_pixel,%a0-%a1/%d0-%d4 - lea %pc@(L(mac_videobase)),%a0 - movel %a0@,%a1 - lea %pc@(L(mac_videodepth)),%a0 - movel %a0@,%d3 - lea %pc@(L(mac_rowbytes)),%a0 - mulul %a0@,%d1 + movel %pc@(L(mac_videobase)),%a1 + movel %pc@(L(mac_videodepth)),%d3 + movel ARG1,%d0 + movel ARG2,%d1 + mulul %pc@(L(mac_rowbytes)),%d1 + movel ARG3,%d2 /* * Register usage: @@ -3402,13 +3545,10 @@ console_plot_pixel: * d2 = black or white (0/1) * d3 = video depth * d4 = temp of x (d0) for many bit depths - * d5 = unused - * d6 = unused - * d7 = unused */ -test_1bit: +L(test_1bit): cmpb #1,%d3 - jbne test_2bit + jbne L(test_2bit) movel %d0,%d4 /* we need the low order 3 bits! */ divul #8,%d0 addal %d0,%a1 @@ -3416,16 +3556,16 @@ test_1bit: andb #7,%d4 eorb #7,%d4 /* reverse the x-coordinate w/ screen-bit # */ andb #1,%d2 - jbne white_1 + jbne L(white_1) bsetb %d4,%a1@ - jbra console_plot_pixel_exit -white_1: + jbra L(console_plot_pixel_exit) +L(white_1): bclrb %d4,%a1@ - jbra console_plot_pixel_exit + jbra L(console_plot_pixel_exit) -test_2bit: +L(test_2bit): cmpb #2,%d3 - jbne test_4bit + jbne L(test_4bit) movel %d0,%d4 /* we need the low order 2 bits! */ divul #4,%d0 addal %d0,%a1 @@ -3434,20 +3574,20 @@ test_2bit: eorb #3,%d4 /* reverse the x-coordinate w/ screen-bit # */ lsll #1,%d4 /* ! */ andb #1,%d2 - jbne white_2 + jbne L(white_2) bsetb %d4,%a1@ addq #1,%d4 bsetb %d4,%a1@ - jbra console_plot_pixel_exit -white_2: + jbra L(console_plot_pixel_exit) +L(white_2): bclrb %d4,%a1@ addq #1,%d4 bclrb %d4,%a1@ - jbra console_plot_pixel_exit + jbra L(console_plot_pixel_exit) -test_4bit: +L(test_4bit): cmpb #4,%d3 - jbne test_8bit + jbne L(test_8bit) movel %d0,%d4 /* we need the low order bit! */ divul #2,%d0 addal %d0,%a1 @@ -3456,7 +3596,7 @@ test_4bit: eorb #1,%d4 lsll #2,%d4 /* ! */ andb #1,%d2 - jbne white_4 + jbne L(white_4) bsetb %d4,%a1@ addq #1,%d4 bsetb %d4,%a1@ @@ -3464,8 +3604,8 @@ test_4bit: bsetb %d4,%a1@ addq #1,%d4 bsetb %d4,%a1@ - jbra console_plot_pixel_exit -white_4: + jbra L(console_plot_pixel_exit) +L(white_4): bclrb %d4,%a1@ addq #1,%d4 bclrb %d4,%a1@ @@ -3473,38 +3613,37 @@ white_4: bclrb %d4,%a1@ addq #1,%d4 bclrb %d4,%a1@ - jbra console_plot_pixel_exit + jbra L(console_plot_pixel_exit) -test_8bit: +L(test_8bit): cmpb #8,%d3 - jbne test_16bit + jbne L(test_16bit) addal %d0,%a1 addal %d1,%a1 andb #1,%d2 - jbne white_8 + jbne L(white_8) moveb #0xff,%a1@ - jbra console_plot_pixel_exit -white_8: + jbra L(console_plot_pixel_exit) +L(white_8): clrb %a1@ - jbra console_plot_pixel_exit + jbra L(console_plot_pixel_exit) -test_16bit: +L(test_16bit): cmpb #16,%d3 - jbne console_plot_pixel_exit + jbne L(console_plot_pixel_exit) addal %d0,%a1 addal %d0,%a1 addal %d1,%a1 andb #1,%d2 - jbne white_16 + jbne L(white_16) clrw %a1@ - jbra console_plot_pixel_exit -white_16: + jbra L(console_plot_pixel_exit) +L(white_16): movew #0x0fff,%a1@ - jbra console_plot_pixel_exit + jbra L(console_plot_pixel_exit) -console_plot_pixel_exit: - moveml %sp@+,%a0-%a1/%d0-%d4 - rts +L(console_plot_pixel_exit): +func_return console_plot_pixel #endif /* CONSOLE */ #if 0 @@ -3549,11 +3688,6 @@ L(iobase): .long 0 #endif -#ifdef CONFIG_MAC -L(console_video_virtual): - .long 0 -#endif /* CONFIG_MAC */ - #if defined(CONSOLE) L(console_globals): .long 0 /* cursor column */ @@ -3597,6 +3731,17 @@ M147_SCC_CTRL_A = 0xfffe3002 M147_SCC_DATA_A = 0xfffe3003 #endif +#if defined (CONFIG_MVME16x) +M162_SCC_CTRL_A = 0xfff45005 +M167_CYCAR = 0xfff450ee +M167_CYIER = 0xfff45011 +M167_CYLICR = 0xfff45026 +M167_CYTEOIR = 0xfff45085 +M167_CYTDR = 0xfff450f8 +M167_PCSCCTICR = 0xfff4201e +M167_PCTPIACKR = 0xfff42025 +#endif + #if defined (CONFIG_BVME6000) BVME_SCC_CTRL_A = 0xffb0000b BVME_SCC_DATA_A = 0xffb0000f @@ -3630,8 +3775,8 @@ SYMBOL_NAME_LABEL(m68k_pgtable_cachemode) SYMBOL_NAME_LABEL(m68k_supervisor_cachemode) .long 0 #if defined(CONFIG_MVME16x) -SYMBOL_NAME_LABEL(mvme_bdid_ptr) - .long 0 +SYMBOL_NAME_LABEL(mvme_bdid) + .long 0,0,0,0,0,0,0,0 #endif #if defined(CONFIG_Q40) SYMBOL_NAME_LABEL(q40_mem_cptr) diff --git a/arch/m68k/kernel/kgdb.c b/arch/m68k/kernel/kgdb.c index cdbd250cd4ae..7ab4e098e2c9 100644 --- a/arch/m68k/kernel/kgdb.c +++ b/arch/m68k/kernel/kgdb.c @@ -189,6 +189,11 @@ #include #include #endif +#ifdef CONFIG_MAC +#include +#include +#include +#endif #undef DEBUG @@ -239,6 +244,15 @@ static unsigned char atari_scc_intr( void ); extern int amiga_ser_out( unsigned char c ); extern unsigned char amiga_ser_in( void ); #endif +#ifdef CONFIG_MAC +static unsigned char mac_scca_in( void ); +static unsigned char mac_scca_out( unsigned char c ); +static unsigned char mac_scca_intr( void ); +static unsigned char mac_sccb_in( void ); +static unsigned char mac_sccb_out( unsigned char c); +static unsigned char mac_sccb_intr( void ); +extern void mac_init_scc_port( int cflag, int port ); +#endif /************************* End of Prototypes **************************/ @@ -668,6 +682,31 @@ void kgdb_init(void) } #endif +#ifdef CONFIG_MAC + if (MACH_IS_MAC) { + if (!strcmp( m68k_debug_device, "ser" ) || + !strcmp( m68k_debug_device, "ser1" )) { + mac_init_scc_port( B9600|CS8, 0 ); + serial_in = mac_scca_in; + serial_out = mac_scca_out; + serial_intr = mac_scca_intr; + } else if (!strcmp( m68k_debug_device, "ser2" )) { + mac_init_scc_port( B9600|CS8, 1 ); + serial_in = mac_sccb_in; + serial_out = mac_sccb_out; + serial_intr = mac_sccb_intr; + } + } + if (!serial_in || !serial_out) { + if (*m68k_debug_device) + printk( "kgdb_init failed: no valid serial device!\n" ); + else + printk( "kgdb not enabled\n" ); + return; + } + request_irq(4, kgdb_intr, IRQ_TYPE_FAST, "kgdb", NULL); +#endif + #ifdef CONFIG_ATARI if (!serial_in || !serial_out) { if (*m68k_debug_device) @@ -781,8 +820,15 @@ __asm__ /* copy format/vector word */ " movew %a0@("FRAMEOFF_VECTOR"),%a1@("GDBOFF_VECTOR")\n" /* save FPU regs */ +#ifndef CONFIG_FPU_EMU_ONLY +#ifdef CONFIG_FPU_EMU + " tstl "SYMBOL_NAME_STR(m68k_fputype)"\n" + " jeq 1f\n" +#endif " fmovemx %fp0-%fp7,%a1@("GDBOFF_FP0")\n" " fmoveml %fpcr/%fpsr/%fpiar,%a1@("GDBOFF_FPCTL")\n" + "1:\n" +#endif /* CONFIG_FPU_EMU_ONLY */ /* set stack to CPU frame */ " addl #"FRAMEOFF_SR",%a0\n" @@ -801,8 +847,15 @@ __asm__ /* after return, first restore FPU registers */ " movel #"SYMBOL_NAME_STR(kgdb_registers)",%a0\n" /* source */ +#ifndef CONFIG_FPU_EMU_ONLY +#ifdef CONFIG_FPU_EMU + " tstl "SYMBOL_NAME_STR(m68k_fputype)"\n" + " jeq 1f\n" +#endif " fmovemx %a0@("GDBOFF_FP0"),%fp0-%fp7\n" " fmoveml %a0@("GDBOFF_FPCTL"),%fpcr/%fpsr/%fpiar\n" + "1:\n" +#endif /* CONFIG_FPU_EMU_ONLY */ /* set new stack pointer */ " movel %a0@("GDBOFF_A7"),%sp\n" " clrw %sp@-\n" /* fake format $0 frame */ @@ -849,8 +902,15 @@ __asm__ /* fake format 0 and vector 1 (translated to SIGINT) */ " movew #4,%a1@("GDBOFF_VECTOR")\n" /* save FPU regs */ +#ifndef CONFIG_FPU_EMU_ONLY +#ifdef CONFIG_FPU_EMU + " tstl "SYMBOL_NAME_STR(m68k_fputype)"\n" + " jeq 1f\n" +#endif " fmovemx %fp0-%fp7,%a1@("GDBOFF_FP0")\n" " fmoveml %fpcr/%fpsr/%fpiar,%a1@("GDBOFF_FPCTL")\n" + "1:\n" +#endif /* CONFIG_FPU_EMU_ONLY */ /* pop off the CPU stack frame */ " addql #8,%sp\n" " movel %sp,%a1@("GDBOFF_A7")\n" /* save a7 now */ @@ -1192,3 +1252,111 @@ static unsigned char atari_scc_intr( void ) } #endif + +/* -------------------- Macintosh serial I/O -------------------- */ + +#ifdef CONFIG_MAC + +struct SCC + { + u_char cha_b_ctrl; + u_char char_dummy1; + u_char cha_a_ctrl; + u_char char_dummy2; + u_char cha_b_data; + u_char char_dummy3; + u_char cha_a_data; + }; + +#define scc (*((volatile struct SCC*)mac_bi_data.sccbase)) + +#define uSEC 1 +#define LONG_DELAY() \ + do { \ + int i; \ + for( i = 60*uSEC; i > 0; --i ) \ + barrier(); \ + } while(0) + +static unsigned char mac_sccb_out (unsigned char c) +{ + int i; + do { + LONG_DELAY(); + } while (!(scc.cha_b_ctrl & 0x04)); /* wait for tx buf empty */ + for( i = uSEC; i > 0; --i ) + barrier(); + scc.cha_b_data = c; +} + +static unsigned char mac_scca_out (unsigned char c) +{ + int i; + do { + LONG_DELAY(); + } while (!(scc.cha_a_ctrl & 0x04)); /* wait for tx buf empty */ + for( i = uSEC; i > 0; --i ) + barrier(); + scc.cha_a_data = c; +} + +static unsigned char mac_sccb_in( void ) +{ + do { + LONG_DELAY(); + } while( !(scc.cha_b_ctrl & 0x01) ); /* wait for rx buf filled */ + LONG_DELAY(); + return( scc.cha_b_data ); +} + +static unsigned char mac_scca_in( void ) + +{ + do { + LONG_DELAY(); + } while( !(scc.cha_a_ctrl & 0x01) ); /* wait for rx buf filled */ + LONG_DELAY(); + return( scc.cha_a_data ); +} + +static unsigned char mac_sccb_intr( void ) + +{ unsigned char c, stat; + + LONG_DELAY(); + scc.cha_b_ctrl = 1; /* RR1 */ + LONG_DELAY(); + stat = scc.cha_b_ctrl; + LONG_DELAY(); + c = scc.cha_b_data; + LONG_DELAY(); + if (stat & 0x30) { + scc.cha_b_ctrl = 0x30; /* error reset for overrun and parity */ + LONG_DELAY(); + } + scc.cha_b_ctrl = 0x38; /* reset highest IUS */ + LONG_DELAY(); + return( c ); +} + +static unsigned char mac_scca_intr( void ) + +{ unsigned char c, stat; + + LONG_DELAY(); + scc.cha_a_ctrl = 1; /* RR1 */ + LONG_DELAY(); + stat = scc.cha_a_ctrl; + LONG_DELAY(); + c = scc.cha_a_data; + LONG_DELAY(); + if (stat & 0x30) { + scc.cha_a_ctrl = 0x30; /* error reset for overrun and parity */ + LONG_DELAY(); + } + scc.cha_a_ctrl = 0x38; /* reset highest IUS */ + LONG_DELAY(); + return( c ); +} + +#endif diff --git a/arch/m68k/kernel/m68k_defs.c b/arch/m68k/kernel/m68k_defs.c index e2e6715e7ad7..590a1ef2ba0a 100644 --- a/arch/m68k/kernel/m68k_defs.c +++ b/arch/m68k/kernel/m68k_defs.c @@ -43,8 +43,16 @@ int main(void) /* offsets into the pt_regs */ DEFINE(PT_D0, offsetof(struct pt_regs, d0)); DEFINE(PT_ORIG_D0, offsetof(struct pt_regs, orig_d0)); + DEFINE(PT_D1, offsetof(struct pt_regs, d1)); + DEFINE(PT_D2, offsetof(struct pt_regs, d2)); + DEFINE(PT_D3, offsetof(struct pt_regs, d3)); + DEFINE(PT_D4, offsetof(struct pt_regs, d4)); + DEFINE(PT_D5, offsetof(struct pt_regs, d5)); + DEFINE(PT_A0, offsetof(struct pt_regs, a0)); + DEFINE(PT_A1, offsetof(struct pt_regs, a1)); + DEFINE(PT_A2, offsetof(struct pt_regs, a2)); + DEFINE(PT_PC, offsetof(struct pt_regs, pc)); DEFINE(PT_SR, offsetof(struct pt_regs, sr)); - /* bitfields are a bit difficult */ DEFINE(PT_VECTOR, offsetof(struct pt_regs, pc) + 4); @@ -69,6 +77,12 @@ int main(void) DEFINE(FBCON_FONT_DESC_DATA, offsetof(struct fbcon_font_desc, data)); DEFINE(FBCON_FONT_DESC_PREF, offsetof(struct fbcon_font_desc, pref)); + /* signal defines */ + DEFINE(SIGSEGV, SIGSEGV); + DEFINE(SEGV_MAPERR, SEGV_MAPERR); + DEFINE(SIGTRAP, SIGTRAP); + DEFINE(TRAP_TRACE, TRAP_TRACE); + /* offsets into the custom struct */ DEFINE(CUSTOMBASE, &custom); DEFINE(C_INTENAR, offsetof(struct CUSTOM, intenar)); diff --git a/arch/m68k/kernel/m68k_defs.h b/arch/m68k/kernel/m68k_defs.h index 4926a6dadac1..b92955a91cc3 100644 --- a/arch/m68k/kernel/m68k_defs.h +++ b/arch/m68k/kernel/m68k_defs.h @@ -8,7 +8,7 @@ #define TASK_SIGPENDING 8 #define TASK_NEEDRESCHED 20 #define TASK_TSS 470 -#define TASK_MM 622 +#define TASK_MM 826 #define TSS_KSP 0 #define TSS_USP 4 #define TSS_SR 8 @@ -20,6 +20,15 @@ #define TSS_FPSTATE 132 #define PT_D0 32 #define PT_ORIG_D0 36 +#define PT_D1 0 +#define PT_D2 4 +#define PT_D3 8 +#define PT_D4 12 +#define PT_D5 16 +#define PT_A0 20 +#define PT_A1 24 +#define PT_A2 28 +#define PT_PC 46 #define PT_SR 44 #define PT_VECTOR 50 #define IRQ_HANDLER 0 @@ -35,6 +44,10 @@ #define FBCON_FONT_DESC_HEIGHT 12 #define FBCON_FONT_DESC_DATA 16 #define FBCON_FONT_DESC_PREF 20 +#define SIGSEGV 11 +#define SEGV_MAPERR 1 +#define SIGTRAP 5 +#define TRAP_TRACE 2 #define CUSTOMBASE -2132807680 #define C_INTENAR 28 #define C_INTREQR 30 diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c index 5aae5953cc7d..96e2da27830c 100644 --- a/arch/m68k/kernel/m68k_ksyms.c +++ b/arch/m68k/kernel/m68k_ksyms.c @@ -19,9 +19,13 @@ #include #include #include +#include asmlinkage long long __ashrdi3 (long long, int); +asmlinkage long long __ashldi3 (long long, int); +asmlinkage long long __lshrdi3 (long long, int); extern char m68k_debug_device[]; +extern void ret_from_exception(void); extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(elf_fpregset_t *); @@ -44,6 +48,7 @@ EXPORT_SYMBOL(m68k_memory); EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(m68k_debug_device); +EXPORT_SYMBOL(hwreg_present); EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(strnlen); @@ -57,6 +62,10 @@ EXPORT_SYMBOL(kernel_set_cachemode); EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL(register_serial); EXPORT_SYMBOL(unregister_serial); +EXPORT_SYMBOL(ret_from_exception); +#ifdef CONFIG_VME +EXPORT_SYMBOL(vme_brdtype); +#endif /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); @@ -66,6 +75,8 @@ EXPORT_SYMBOL(csum_partial_copy); their interface isn't gonna change any time soon now, so it's OK to leave it out of version control. */ EXPORT_SYMBOL_NOVERS(__ashrdi3); +EXPORT_SYMBOL_NOVERS(__ashldi3); +EXPORT_SYMBOL_NOVERS(__lshrdi3); EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memset); EXPORT_SYMBOL_NOVERS(memcmp); diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index ee45219cbf71..de1c17041dc8 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -61,6 +61,7 @@ asmlinkage int sys_idle(void) /* endless idle loop with no priority at all */ current->priority = 0; current->counter = -100; + init_idle(); for (;;) { if (!current->need_resched) #if defined(CONFIG_ATARI) && !defined(CONFIG_AMIGA) && !defined(CONFIG_MAC) @@ -78,14 +79,21 @@ void machine_restart(char * __unused) { if (mach_reset) mach_reset(); + for (;;); } void machine_halt(void) { + if (mach_halt) + mach_halt(); + for (;;); } void machine_power_off(void) { + if (mach_power_off) + mach_power_off(); + for (;;); } void show_regs(struct pt_regs * regs) @@ -146,9 +154,10 @@ void flush_thread(void) unsigned long zero = 0; set_fs(USER_DS); current->tss.fs = __USER_DS; - asm volatile (".chip 68k/68881\n\t" - "frestore %0@\n\t" - ".chip 68k" : : "a" (&zero)); + if (!FPU_IS_EMU) + asm volatile (".chip 68k/68881\n\t" + "frestore %0\n\t" + ".chip 68k" : : "m" (zero)); } /* @@ -209,16 +218,18 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, */ p->tss.fs = get_fs().seg; - /* Copy the current fpu state */ - asm volatile ("fsave %0" : : "m" (p->tss.fpstate[0]) : "memory"); - - if (!CPU_IS_060 ? p->tss.fpstate[0] : p->tss.fpstate[2]) - asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t" - "fmoveml %/fpiar/%/fpcr/%/fpsr,%1" - : : "m" (p->tss.fp[0]), "m" (p->tss.fpcntl[0]) - : "memory"); - /* Restore the state in case the fpu was busy */ - asm volatile ("frestore %0" : : "m" (p->tss.fpstate[0])); + if (!FPU_IS_EMU) { + /* Copy the current fpu state */ + asm volatile ("fsave %0" : : "m" (p->tss.fpstate[0]) : "memory"); + + if (!CPU_IS_060 ? p->tss.fpstate[0] : p->tss.fpstate[2]) + asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t" + "fmoveml %/fpiar/%/fpcr/%/fpsr,%1" + : : "m" (p->tss.fp[0]), "m" (p->tss.fpcntl[0]) + : "memory"); + /* Restore the state in case the fpu was busy */ + asm volatile ("frestore %0" : : "m" (p->tss.fpstate[0])); + } return 0; } @@ -227,20 +238,34 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu) { - char fpustate[216]; + char fpustate[216]; + + if (FPU_IS_EMU) { + int i; + + memcpy(fpu->fpcntl, current->tss.fpcntl, 12); + memcpy(fpu->fpregs, current->tss.fp, 96); + /* Convert internal fpu reg representation + * into long double format + */ + for (i = 0; i < 24; i += 3) + fpu->fpregs[i] = ((fpu->fpregs[i] & 0xffff0000) << 15) | + ((fpu->fpregs[i] & 0x0000ffff) << 16); + return 1; + } - /* First dump the fpu context to avoid protocol violation. */ - asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory"); - if (!CPU_IS_060 ? !fpustate[0] : !fpustate[2]) - return 0; + /* First dump the fpu context to avoid protocol violation. */ + asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory"); + if (!CPU_IS_060 ? !fpustate[0] : !fpustate[2]) + return 0; - asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0" + asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0" :: "m" (fpu->fpcntl[0]) : "memory"); - asm volatile ("fmovemx %/fp0-%/fp7,%0" + asm volatile ("fmovemx %/fp0-%/fp7,%0" :: "m" (fpu->fpregs[0]) : "memory"); - return 1; + return 1; } /* diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index e9254cfa7e31..76e166493050 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -403,10 +404,17 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) tmp = get_reg(child, addr); if (addr == PT_SR) tmp >>= 16; - } - else if (addr >= 21 && addr < 49) + } else if (addr >= 21 && addr < 49) { tmp = child->tss.fp[addr - 21]; - else +#ifdef CONFIG_FPU_EMU + /* Convert internal fpu reg representation + * into long double format + */ + if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) + tmp = ((tmp & 0xffff0000) << 15) | + ((tmp & 0x0000ffff) << 16); +#endif + } else goto out; ret = put_user(tmp,(unsigned long *) data); goto out; @@ -442,6 +450,16 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) } if (addr >= 21 && addr < 48) { +#ifdef CONFIG_FPU_EMU + /* Convert long double format + * into internal fpu reg representation + */ + if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) { + data = (unsigned long)data << 15; + data = (data & 0xffff0000) | + ((data & 0x0000ffff) >> 1); + } +#endif child->tss.fp[addr - 21] = data; ret = 0; } diff --git a/arch/m68k/kernel/setup.c b/arch/m68k/kernel/setup.c index 39dcd9c15ae6..85db89d1e6ad 100644 --- a/arch/m68k/kernel/setup.c +++ b/arch/m68k/kernel/setup.c @@ -41,6 +41,9 @@ unsigned long m68k_machtype; unsigned long m68k_cputype; unsigned long m68k_fputype; unsigned long m68k_mmutype; +#ifdef CONFIG_VME +unsigned long vme_brdtype; +#endif int m68k_is040or060 = 0; @@ -76,6 +79,8 @@ void (*mach_gettod) (int*, int*, int*, int*, int*, int*); int (*mach_hwclk) (int, struct hwclk_time*) = NULL; int (*mach_set_clock_mmss) (unsigned long) = NULL; void (*mach_reset)( void ); +void (*mach_halt)( void ) = NULL; +void (*mach_power_off)( void ) = NULL; long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */ #if defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY) || defined(CONFIG_BLK_DEV_FD) void (*mach_floppy_setup) (char *, int *) __initdata = NULL; @@ -114,6 +119,9 @@ extern int amiga_parse_bootinfo(const struct bi_record *); extern int atari_parse_bootinfo(const struct bi_record *); extern int mac_parse_bootinfo(const struct bi_record *); extern int q40_parse_bootinfo(const struct bi_record *); +extern int bvme6000_parse_bootinfo(const struct bi_record *); +extern int mvme16x_parse_bootinfo(const struct bi_record *); +extern int mvme147_parse_bootinfo(const struct bi_record *); extern void config_amiga(void); extern void config_atari(void); @@ -171,6 +179,12 @@ __initfunc(static void m68k_parse_bootinfo(const struct bi_record *record)) unknown = mac_parse_bootinfo(record); else if (MACH_IS_Q40) unknown = q40_parse_bootinfo(record); + else if (MACH_IS_BVME6000) + unknown = bvme6000_parse_bootinfo(record); + else if (MACH_IS_MVME16x) + unknown = mvme16x_parse_bootinfo(record); + else if (MACH_IS_MVME147) + unknown = mvme147_parse_bootinfo(record); else unknown = 1; } @@ -208,10 +222,12 @@ __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, base_trap_init(); /* clear the fpu if we have one */ +#ifndef CONFIG_FPU_EMU_ONLY if (m68k_fputype & (FPU_68881|FPU_68882|FPU_68040|FPU_68060)) { volatile int zero = 0; asm __volatile__ ("frestore %0" : : "m" (zero)); } +#endif init_task.mm->start_code = PAGE_OFFSET; init_task.mm->end_code = (unsigned long) &_etext; diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index a2b9ce1d4102..e635e3447e6e 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -14,6 +14,10 @@ * 68060 fixes by Jesper Skov * * 1997-12-01 Modified for POSIX.1b signals by Andreas Schwab + * + * mathemu support by Roman Zippel + * (Note: fpstate in the signal context is completly ignored for the emulator + * and the internal floating point format is put on stack) */ /* @@ -156,6 +160,9 @@ sys_sigaltstack(const stack_t *uss, stack_t *uoss) /* * Do a signal return; undo the signal stack. + * + * Keep the return code on the stack quadword aligned! + * That makes the cache flush below easier. */ struct sigframe @@ -175,9 +182,9 @@ struct rt_sigframe int sig; struct siginfo *pinfo; void *puc; + char retcode[8]; struct siginfo info; struct ucontext uc; - char retcode[8]; }; @@ -187,6 +194,13 @@ static inline int restore_fpu_state(struct sigcontext *sc) { int err = 1; + if (FPU_IS_EMU) { + /* restore registers */ + memcpy(current->tss.fpcntl, sc->sc_fpcntl, 12); + memcpy(current->tss.fp, sc->sc_fpregs, 24); + return 0; + } + if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) { /* Verify the frame format. */ if (!CPU_IS_060 && (sc->sc_fpstate[0] != fpu_version)) @@ -239,6 +253,18 @@ static inline int rt_restore_fpu_state(struct ucontext *uc) fpregset_t fpregs; int err = 1; + if (FPU_IS_EMU) { + /* restore fpu control register */ + if (__copy_from_user(current->tss.fpcntl, + &uc->uc_mcontext.fpregs.f_pcr, 12)) + goto out; + /* restore all other fpu register */ + if (__copy_from_user(current->tss.fp, + uc->uc_mcontext.fpregs.f_fpregs, 96)) + goto out; + return 0; + } + if (__get_user(*(long *)fpstate, (long *)&uc->uc_fpstate)) goto out; if (CPU_IS_060 ? fpstate[2] : fpstate[0]) { @@ -478,7 +504,7 @@ asmlinkage int do_sigreturn(unsigned long __unused) struct switch_stack *sw = (struct switch_stack *) &__unused; struct pt_regs *regs = (struct pt_regs *) (sw + 1); unsigned long usp = rdusp(); - struct sigframe *frame = (struct sigframe *)(usp - 24); + struct sigframe *frame = (struct sigframe *)(usp - 4); sigset_t set; int d0; @@ -536,6 +562,13 @@ badframe: static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs) { + if (FPU_IS_EMU) { + /* save registers */ + memcpy(sc->sc_fpcntl, current->tss.fpcntl, 12); + memcpy(sc->sc_fpregs, current->tss.fp, 24); + return; + } + __asm__ volatile (".chip 68k/68881\n\t" "fsave %0\n\t" ".chip 68k" @@ -567,6 +600,16 @@ static inline int rt_save_fpu_state(struct ucontext *uc, struct pt_regs *regs) int context_size = CPU_IS_060 ? 8 : 0; int err = 0; + if (FPU_IS_EMU) { + /* save fpu control register */ + err |= copy_to_user(&uc->uc_mcontext.fpregs.f_pcr, + current->tss.fpcntl, 12); + /* save all other fpu register */ + err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs, + current->tss.fp, 96); + return err; + } + __asm__ volatile (".chip 68k/68881\n\t" "fsave %0\n\t" ".chip 68k" @@ -677,25 +720,6 @@ static inline void push_cache (unsigned long vaddr) "cpushl %%bc,(%0)\n\t" ".chip 68k" : : "a" (temp)); - if (((vaddr + 8) ^ vaddr) & ~15) { - if (((vaddr + 8) ^ vaddr) & PAGE_MASK) - __asm__ __volatile__ (".chip 68040\n\t" - "nop\n\t" - "ptestr (%1)\n\t" - "movec %%mmusr,%0\n\t" - ".chip 68k" - : "=r" (temp) - : "a" (vaddr + 8)); - - temp &= PAGE_MASK; - temp |= (vaddr + 8) & ~PAGE_MASK; - - __asm__ __volatile__ (".chip 68040\n\t" - "nop\n\t" - "cpushl %%bc,(%0)\n\t" - ".chip 68k" - : : "a" (temp)); - } } else if (CPU_IS_060) { unsigned long temp; @@ -708,18 +732,6 @@ static inline void push_cache (unsigned long vaddr) "cpushl %%bc,(%0)\n\t" ".chip 68k" : : "a" (temp)); - if (((vaddr + 8) ^ vaddr) & ~15) { - if (((vaddr + 8) ^ vaddr) & PAGE_MASK) - __asm__ __volatile__ (".chip 68060\n\t" - "plpar (%0)\n\t" - ".chip 68k" - : "=a" (temp) - : "0" (vaddr + 8)); - __asm__ __volatile__ (".chip 68060\n\t" - "cpushl %%bc,(%0)\n\t" - ".chip 68k" - : : "a" (temp)); - } } else { /* @@ -797,11 +809,9 @@ static void setup_frame (int sig, struct k_sigaction *ka, /* Set up to return from userspace. */ err |= __put_user(frame->retcode, &frame->pretcode); - /* addaw #20,sp */ - err |= __put_user(0xdefc0014, (long *)(frame->retcode + 0)); /* moveq #,d0; trap #0 */ err |= __put_user(0x70004e40 + (__NR_sigreturn << 16), - (long *)(frame->retcode + 4)); + (long *)(frame->retcode)); if (err) goto give_sigsegv; @@ -881,10 +891,10 @@ static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up to return from userspace. */ err |= __put_user(frame->retcode, &frame->pretcode); - /* movel #,d0; trap #0 */ - err |= __put_user(0x203c, (short *)(frame->retcode + 0)); - err |= __put_user(__NR_rt_sigreturn, (long *)(frame->retcode + 2)); - err |= __put_user(0x4e40, (short *)(frame->retcode + 6)); + /* moveq #,d0; notb d0; trap #0 */ + err |= __put_user(0x70004600 + ((__NR_rt_sigreturn ^ 0xff) << 16), + (long *)(frame->retcode + 0)); + err |= __put_user(0x4e40, (short *)(frame->retcode + 4)); if (err) goto give_sigsegv; @@ -964,11 +974,10 @@ handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) sigaddset(¤t->blocked,sig); - recalc_sigpending(current); - } + recalc_sigpending(current); } /* @@ -1092,6 +1101,7 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) default: sigaddset(¤t->signal, signr); + recalc_sigpending(current); current->flags |= PF_SIGNALED; do_exit(exit_code); /* NOTREACHED */ diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index 1f07014b2260..d25f28a5f11c 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -41,12 +41,17 @@ #include #endif +/*#define DEBUG*/ + /* assembler routines */ asmlinkage void system_call(void); asmlinkage void buserr(void); asmlinkage void trap(void); asmlinkage void inthandler(void); asmlinkage void nmihandler(void); +#ifdef CONFIG_FPU_EMU +asmlinkage void fpu_emu(void); +#endif e_vector vectors[256] = { 0, 0, buserr, trap, trap, trap, trap, trap, @@ -70,7 +75,7 @@ __initfunc(void base_trap_init(void)) /* setup the exception vector table */ __asm__ volatile ("movec %0,%%vbr" : : "r" ((void*)vectors)); - if (CPU_IS_040) { + if (CPU_IS_040 && !FPU_IS_EMU) { /* set up FPSP entry points */ asmlinkage void dz_vec(void) asm ("dz"); asmlinkage void inex_vec(void) asm ("inex"); @@ -93,6 +98,12 @@ __initfunc(void base_trap_init(void)) vectors[VEC_FPUNSUP] = unsupp_vec; } if (CPU_IS_060) { + /* set up ISP entry points */ + asmlinkage void unimp_vec(void) asm ("_060_isp_unimp"); + + vectors[VEC_UNIMPII] = unimp_vec; + } + if (CPU_IS_060 && !FPU_IS_EMU) { /* set up IFPSP entry points */ asmlinkage void snan_vec(void) asm ("_060_fpsp_snan"); asmlinkage void operr_vec(void) asm ("_060_fpsp_operr"); @@ -104,8 +115,6 @@ __initfunc(void base_trap_init(void)) asmlinkage void unsupp_vec(void) asm ("_060_fpsp_unsupp"); asmlinkage void effadd_vec(void) asm ("_060_fpsp_effadd"); - asmlinkage void unimp_vec(void) asm ("_060_isp_unimp"); - vectors[VEC_FPNAN] = snan_vec; vectors[VEC_FPOE] = operr_vec; vectors[VEC_FPOVER] = ovfl_vec; @@ -115,10 +124,6 @@ __initfunc(void base_trap_init(void)) vectors[VEC_LINE11] = fline_vec; vectors[VEC_FPUNSUP] = unsupp_vec; vectors[VEC_UNIMPEA] = effadd_vec; - - /* set up ISP entry points */ - - vectors[VEC_UNIMPII] = unimp_vec; } } @@ -133,6 +138,11 @@ __initfunc(void trap_init (void)) for (i = 64; i < 256; i++) vectors[i] = inthandler; +#ifdef CONFIG_FPU_EMU + if (FPU_IS_EMU) + vectors[VEC_LINE11] = fpu_emu; +#endif + /* if running on an amiga, make the NMI interrupt do nothing */ if (MACH_IS_AMIGA) { vectors[VEC_INT7] = nmihandler; @@ -182,8 +192,9 @@ static char *space_names[] = { void die_if_kernel(char *,struct pt_regs *,int); -asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, - unsigned long error_code); +int do_page_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code); +int send_fault_sig(struct pt_regs *regs); asmlinkage void trap_c(struct frame *fp); @@ -232,74 +243,141 @@ static inline void access_error060 (struct frame *fp) #endif /* CONFIG_M68060 */ #if defined (CONFIG_M68040) -static inline unsigned long probe040 (int iswrite, int fc, unsigned long addr) + +static inline unsigned long probe040(int iswrite, unsigned long addr) { unsigned long mmusr; - mm_segment_t fs = get_fs(); - set_fs (MAKE_MM_SEG(fc)); + asm volatile (".chip 68040"); if (iswrite) - /* write */ - asm volatile (".chip 68040\n\t" - "ptestw (%1)\n\t" - "movec %%mmusr,%0\n\t" - ".chip 68k" - : "=r" (mmusr) - : "a" (addr)); + asm volatile ("ptestw (%0)" : : "a" (addr)); else - asm volatile (".chip 68040\n\t" - "ptestr (%1)\n\t" - "movec %%mmusr,%0\n\t" - ".chip 68k" - : "=r" (mmusr) - : "a" (addr)); + asm volatile ("ptestr (%0)" : : "a" (addr)); - set_fs (fs); + asm volatile ("movec %%mmusr,%0" : "=r" (mmusr)); + + asm volatile (".chip 68k"); return mmusr; } -static inline void do_040writeback (unsigned short ssw, - unsigned short wbs, - unsigned long wba, - unsigned long wbd, - struct frame *fp) +static inline int do_040writeback1(unsigned short wbs, unsigned long wba, + unsigned long wbd) { - mm_segment_t fs = get_fs (); - unsigned long mmusr; - unsigned long errorcode; - - /* - * No special handling for the second writeback anymore. - * It misinterpreted the misaligned status sometimes. - * This way an extra page-fault may be caused (Martin Apel). - */ + mm_segment_t old_fs; + int res = 0; - mmusr = probe040 (1, wbs & WBTM_040, wba); - errorcode = (mmusr & MMU_R_040) ? 3 : 2; - if (do_page_fault (&fp->ptregs, wba, errorcode)) - /* just return if we can't perform the writeback */ - return; + old_fs = get_fs(); + set_fs(MAKE_MM_SEG(wbs)); - set_fs (MAKE_MM_SEG(wbs & WBTM_040)); switch (wbs & WBSIZ_040) { - case BA_SIZE_BYTE: - put_user (wbd & 0xff, (char *)wba); + case BA_SIZE_BYTE: + res = put_user(wbd & 0xff, (char *)wba); break; - case BA_SIZE_WORD: - put_user (wbd & 0xffff, (short *)wba); + case BA_SIZE_WORD: + res = put_user(wbd & 0xffff, (short *)wba); break; - case BA_SIZE_LONG: - put_user (wbd, (int *)wba); + case BA_SIZE_LONG: + res = put_user(wbd, (int *)wba); break; } - set_fs (fs); + set_fs(old_fs); + +#ifdef DEBUG + printk("do_040writeback1, res=%d\n",res); +#endif + + return res; +} + +#define FIX_XFRAME(fp,_faddr_,_set_ma_,_wbs_) \ + do{ (fp)->un.fmt7.faddr = (_faddr_); \ + (fp)->un.fmt7.ssw &= ~( 0x7f | MA_040 | RW_040); \ + (fp)->un.fmt7.ssw |= (~0x7f & (_wbs_)); \ + (fp)->un.fmt7.ssw |= ((_set_ma_) ? MA_040 : 0); \ + } while(0) + +/* after an exception in a writeback the stack frame coresponding + * to that exception is discarded, set a few bits in the old frame + * to simulate what it should look like */ + +inline void fix_xframe040(struct frame *fp, unsigned long faddr, unsigned short wbs) +{ + unsigned short ssw=(fp)->un.fmt7.ssw; + + ssw &= ~( 0x7f | MA_040 | RW_040); + ssw |= (~0x7f & wbs); + if (faddr != (unsigned long)(current->buserr_info.si_addr) ) + ssw |= MA_040; + (fp)->un.fmt7.faddr = faddr; + (fp)->un.fmt7.ssw = ssw; } -static inline void access_error040 (struct frame *fp) +inline void do_040writebacks(struct frame *fp) +{ + unsigned long wbaddr; + int xp=0; +#if 0 + if (fp->un.fmt7.wb1s & WBV_040) + printk("access_error040: cannot handle 1st writeback. oops.\n"); +#endif + + if ((fp->un.fmt7.wb2s & WBV_040) && + !(fp->un.fmt7.wb2s & WBTT_040)) { + wbaddr = fp->un.fmt7.wb2a; + if (xp=do_040writeback1(fp->un.fmt7.wb2s, wbaddr, + fp->un.fmt7.wb2d)) + { + fix_xframe040(fp,wbaddr,fp->un.fmt7.wb2s); + } + else + fp->un.fmt7.wb2s &= ~WBV_040; + } + + if (fp->un.fmt7.wb3s & WBV_040) { + wbaddr= fp->un.fmt7.wb3a; + if (do_040writeback1(fp->un.fmt7.wb3s, wbaddr, + fp->un.fmt7.wb3d)) + { + if (!xp) + { + fix_xframe040(fp,wbaddr,fp->un.fmt7.wb3s); + fp->un.fmt7.wb2s = fp->un.fmt7.wb3s; + fp->un.fmt7.wb3s &= (~WBV_040); + fp->un.fmt7.wb2a = wbaddr; + fp->un.fmt7.wb2d = fp->un.fmt7.wb3d; + xp=1; + } + } + else + fp->un.fmt7.wb3s &= ~WBV_040; + } + + if (!xp) return; + + send_fault_sig(&fp->ptregs); +} + +/* + * called from sigreturn(), must ensure userspace code didn't + * manipulate exception frame to circumvent protection, then complete + * pending writebacks + * Theory is that setting TT1=0 and TM2=0 will avoid all trouble + */ +asmlinkage void berr_040cleanup(struct frame *fp) +{ + fp->un.fmt7.ssw &= ~(0x10 | 0x4); + fp->un.fmt7.wb2s &= ~(0x10 | 0x4); + fp->un.fmt7.wb3s &= ~(0x10 | 0x4); + + do_040writebacks(fp); +} + +static inline void access_error040(struct frame *fp) { unsigned short ssw = fp->un.fmt7.ssw; + mm_segment_t old_fs = get_fs(); unsigned long mmusr; #ifdef DEBUG @@ -323,43 +401,43 @@ static inline void access_error040 (struct frame *fp) if (ssw & MA_040) addr = PAGE_ALIGN (addr); + set_fs(MAKE_MM_SEG(ssw)); /* MMU error, get the MMUSR info for this access */ - mmusr = probe040 (!(ssw & RW_040), ssw & TM_040, addr); + mmusr = probe040(!(ssw & RW_040), addr); + set_fs(old_fs); #ifdef DEBUG printk("mmusr = %lx\n", mmusr); #endif + errorcode = ((mmusr & MMU_R_040) ? 1 : 0) | - ((ssw & RW_040) ? 0 : 2); - do_page_fault (&fp->ptregs, addr, errorcode); - } else { - printk ("68040 access error, ssw=%x\n", ssw); - trap_c (fp); - } + ((ssw & RW_040) ? 0 : 2) | + ((ssw & LK_040) ? 2 : 0); -#if 0 - if (fp->un.fmt7.wb1s & WBV_040) - printk("access_error040: cannot handle 1st writeback. oops.\n"); + if (do_page_fault(&fp->ptregs, addr, errorcode)) { +#ifdef DEBUG + printk("do_page_fault() !=0 \n"); #endif + if (user_mode(&fp->ptregs)){ + /* delay writebacks after signal delivery */ +#ifdef DEBUG + printk(".. was usermode - return\n"); +#endif + return; + } + /* disable writeback into user space from kernel */ +#ifdef DEBUG + printk(".. disabling wb2\n"); +#endif + if (fp->un.fmt7.wb2a == fp->un.fmt7.faddr) + fp->un.fmt7.wb2s &= ~WBV_040; + } -/* - * We may have to do a couple of writebacks here. - * - * MR: we can speed up the thing a little bit and let do_040writeback() - * not produce another page fault as wb2 corresponds to the address that - * caused the fault. on write faults no second fault is generated, but - * on read faults for security reasons (although per definitionem impossible) - */ - - if (fp->un.fmt7.wb2s & WBV_040 && (fp->un.fmt7.wb2s & - WBTT_040) != BA_TT_MOVE16) - do_040writeback (ssw, - fp->un.fmt7.wb2s, fp->un.fmt7.wb2a, - fp->un.fmt7.wb2d, fp); + } else { + printk("68040 access error, ssw=%x\n", ssw); + trap_c(fp); + } - if (fp->un.fmt7.wb3s & WBV_040) - do_040writeback (ssw, fp->un.fmt7.wb3s, - fp->un.fmt7.wb3a, fp->un.fmt7.wb3d, - fp); + do_040writebacks(fp); } #endif /* CONFIG_M68040 */ @@ -650,7 +728,7 @@ asmlinkage void buserr_c(struct frame *fp) if (user_mode(&fp->ptregs)) current->tss.esp0 = (unsigned long) fp; -#if DEBUG +#if 0/*DEBUG*/ printk ("*** Bus Error *** Format is %x\n", fp->ptregs.format); #endif @@ -673,7 +751,7 @@ asmlinkage void buserr_c(struct frame *fp) #endif default: die_if_kernel("bad frame format",&fp->ptregs,0); -#if DEBUG +#if 0/*DEBUG*/ printk("Unknown SIGSEGV - 4\n"); #endif force_sig(SIGSEGV, current); @@ -990,3 +1068,16 @@ asmlinkage void fpsp040_die(void) { do_exit(SIGSEGV); } + +#ifdef CONFIG_FPU_EMU +asmlinkage void fpemu_signal(int signal, int code, void *addr) +{ + siginfo_t info; + + info.si_signo = signal; + info.si_errno = 0; + info.si_code = code; + info.si_addr = addr; + force_sig_info(signal, &info, current); +} +#endif diff --git a/arch/m68k/lib/Makefile b/arch/m68k/lib/Makefile index f152e28e4714..9c6d59604c55 100644 --- a/arch/m68k/lib/Makefile +++ b/arch/m68k/lib/Makefile @@ -6,6 +6,6 @@ $(CC) -D__ASSEMBLY__ -traditional -c $< -o $@ L_TARGET = lib.a -L_OBJS = ashrdi3.o checksum.o memcpy.o memcmp.o memset.o semaphore.o +L_OBJS = ashrdi3.o lshrdi3.o ashldi3.o checksum.o memcpy.o memcmp.o memset.o semaphore.o include $(TOPDIR)/Rules.make diff --git a/arch/m68k/lib/ashldi3.c b/arch/m68k/lib/ashldi3.c new file mode 100644 index 000000000000..331367df1ca5 --- /dev/null +++ b/arch/m68k/lib/ashldi3.c @@ -0,0 +1,62 @@ +/* ashldi3.c extracted from gcc-2.8.1/libgcc2.c which is: */ +/* Copyright (C) 1989, 92-97, 1998 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define BITS_PER_UNIT 8 + +typedef int SItype __attribute__ ((mode (SI))); +typedef unsigned int USItype __attribute__ ((mode (SI))); +typedef int DItype __attribute__ ((mode (DI))); +typedef int word_type __attribute__ ((mode (__word__))); + +struct DIstruct {SItype high, low;}; + +typedef union +{ + struct DIstruct s; + DItype ll; +} DIunion; + +DItype +__ashldi3 (DItype u, word_type b) +{ + DIunion w; + word_type bm; + DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof (SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) + { + w.s.low = 0; + w.s.high = (USItype)uu.s.low << -bm; + } + else + { + USItype carries = (USItype)uu.s.low >> bm; + w.s.low = (USItype)uu.s.low << b; + w.s.high = ((USItype)uu.s.high << b) | carries; + } + + return w.ll; +} diff --git a/arch/m68k/lib/lshrdi3.c b/arch/m68k/lib/lshrdi3.c new file mode 100644 index 000000000000..93b1cb6fdee8 --- /dev/null +++ b/arch/m68k/lib/lshrdi3.c @@ -0,0 +1,62 @@ +/* lshrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC 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. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define BITS_PER_UNIT 8 + +typedef int SItype __attribute__ ((mode (SI))); +typedef unsigned int USItype __attribute__ ((mode (SI))); +typedef int DItype __attribute__ ((mode (DI))); +typedef int word_type __attribute__ ((mode (__word__))); + +struct DIstruct {SItype high, low;}; + +typedef union +{ + struct DIstruct s; + DItype ll; +} DIunion; + +DItype +__lshrdi3 (DItype u, word_type b) +{ + DIunion w; + word_type bm; + DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof (SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) + { + w.s.high = 0; + w.s.low = (USItype)uu.s.high >> -bm; + } + else + { + USItype carries = (USItype)uu.s.high << bm; + w.s.high = (USItype)uu.s.high >> b; + w.s.low = ((USItype)uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/m68k/mac/Makefile b/arch/m68k/mac/Makefile index cfd63295a6c7..8e1bcb558c9b 100644 --- a/arch/m68k/mac/Makefile +++ b/arch/m68k/mac/Makefile @@ -7,11 +7,9 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -EXTRA_CFLAGS := -Wa,-m68020 - O_TARGET := mac.o -O_OBJS := config.o bootparse.o macints.o via6522.o \ - mackeyb.o adb-bus.o macboing.o debug.o +O_OBJS := config.o bootparse.o macints.o iop.o via.o oss.o psc.o \ + mackeyb.o adb-bus.o macboing.o debug.o adb-misc.o OX_OBJS := mac_ksyms.o include $(TOPDIR)/Rules.make diff --git a/arch/m68k/mac/adb-bus.c b/arch/m68k/mac/adb-bus.c index b466ce5333f8..91d1ebc53903 100644 --- a/arch/m68k/mac/adb-bus.c +++ b/arch/m68k/mac/adb-bus.c @@ -11,6 +11,28 @@ * - Guido Koerber's session with a logic analyzer * * MSch (1/98) Integrated start of IIsi driver by Robert Thompson + * + * 1999-05-30 (JMT) - Added support for reading/writing the PRAM clock. + * We really need to clean up the reply_expected usage + * in here so that the special cases in adb_interrupt + * for faking replies can be removed. + * + * 1999-06-10 (JMT) - Wedged in some IOP support. This is really getting + * messy... + * + * 1999-06-11 (JMT) - IOP support sort of working. Autopolling of the + * keyboard works but if we transmit and get a timeout + * we'll get bombarded with timeout message from the + * IOP and I don't know how to make them stop. Thus + * I have disabled transmission until I can figure out + * how to handle the timeouts properly. + * 1999-06-12 (JMT) - Rewrote IOP support to match changes in IOP + * architecture. Also discovered that you need to send + * a sensible reply to an autopoll packet or else the + * keyboard will stop sending packets. I chose to send + * back what we received, as that was what we were + * essentially doing before the IOP rewrite and it + * seemed to work. */ #include @@ -22,7 +44,6 @@ #include #include #include -#include "via6522.h" #include #include @@ -32,7 +53,9 @@ #include #include #include - +#include +#include +#include #define MACII /* For now - will be a switch */ @@ -51,11 +74,7 @@ /* Bits in ACR */ #define SR_CTRL 0x1c /* Shift register control bits */ -#ifdef USE_ORIG -#define SR_EXT 0x1c /* Shift on external clock */ -#else #define SR_EXT 0x0c /* Shift on external clock */ -#endif #define SR_OUT 0x10 /* Shift out if 1 */ /* Bits in IFR and IER */ @@ -68,6 +87,17 @@ /* JRT */ #define ADB_DELAY 150 +#ifdef DEBUG_ADB_INTS +static int nclock, ndata; +#endif + +static int need_poll = 0; +static int command_byte = 0; +static int last_reply = 0; +static int last_active = 0; + +static struct adb_request *retry_req; + static struct adb_handler { void (*handler)(unsigned char *, int, struct pt_regs *); } adb_handler[16]; @@ -100,14 +130,23 @@ static int driver_running = 0; int in_keybinit = 1; static void adb_start(void); -extern void adb_interrupt(int irq, void *arg, struct pt_regs *regs); -extern void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs); -extern void adb_clock_interrupt(int irq, void *arg, struct pt_regs *regs); -extern void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs); +static void adb_interrupt(int irq, void *arg, struct pt_regs *regs); +static void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs); +static void adb_pm_interrupt(int irq, void *arg, struct pt_regs *regs); +#ifdef DEBUG_ADB_INTS +static void adb_clock_interrupt(int irq, void *arg, struct pt_regs *regs); +static void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs); +#endif static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs); +static void adb_iop_receive(struct iop_msg *, struct pt_regs *); +static void adb_hw_setup_macII(void); static void adb_hw_setup_IIsi(void); static void adb_hw_setup_cuda(void); +static void adb_hw_setup_iop(void); +static void adb_hw_setup_pm(void); + +void adb_retransmit(int); /* * debug level 10 required for ADB logging (should be && debug_adb, ideally) @@ -149,7 +188,6 @@ extern int console_loglevel; void adb_bus_init(void) { unsigned long flags; - unsigned char c, i; save_flags(flags); cli(); @@ -162,122 +200,47 @@ void adb_bus_init(void) { case MAC_ADB_II: - printk("adb: MacII style keyboard/mouse driver.\n"); - /* Set the lines up. We want TREQ as input TACK|TIP as output */ - via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ)); - /* - * Docs suggest TREQ should be output - that seems nuts - * BSD agrees here :-) - * Setup vPCR ?? - */ - -#ifdef USE_ORIG - /* Lower the bus signals (MacII is active low it seems ???) */ - via_write(via1, vBufB, via_read(via1, vBufB)&~TACK); -#else - /* Corresponding state: idle (clear state bits) */ - via_write(via1, vBufB, (via_read(via1, vBufB)&~ST_MASK)|ST_IDLE); - last_status = (via_read(via1, vBufB)&~ST_MASK); -#endif - /* Shift register on input */ - c=via_read(via1, vACR); - c&=~SR_CTRL; /* Clear shift register bits */ - c|=SR_EXT; /* Shift on external clock; out or in? */ - via_write(via1, vACR, c); - /* Wipe any pending data and int */ - via_read(via1, vSR); - - /* This is interrupts on enable SR for keyboard */ - via_write(via1, vIER, IER_SET|SR_INT); - /* This clears the interrupt bit */ - via_write(via1, vIFR, SR_INT); - - /* - * Ok we probably ;) have a ready to use adb bus. Its also - * hopefully idle (Im assuming the mac didnt leave a half - * complete transaction on booting us). - */ - + printk(KERN_INFO "adb: MacII style keyboard/mouse driver.\n"); + adb_hw_setup_macII(); request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK, "adb interrupt", adb_interrupt); adb_state = idle; break; - /* - * Unsupported; but later code doesn't notice !! - */ - case MAC_ADB_CUDA: - printk("adb: CUDA interface.\n"); -#if 0 - /* don't know what to set up here ... */ - adb_state = idle; - /* Set the lines up. We want TREQ as input TACK|TIP as output */ - via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ)); -#endif - adb_hw_setup_cuda(); - adb_state = idle; - request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK, - "adb CUDA interrupt", adb_cuda_interrupt); - break; case MAC_ADB_IISI: printk("adb: Using IIsi hardware.\n"); - printk("\tDEBUG_JRT\n"); - /* Set the lines up. We want TREQ as input TACK|TIP as output */ - via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ)); - - /* - * MSch: I'm pretty sure the setup is mildly wrong - * for the IIsi. - */ - /* Initial state: idle (clear state bits) */ - via_write(via1, vBufB, (via_read(via1, vBufB) & ~(TIP|TACK)) ); - last_status = (via_read(via1, vBufB)&~ST_MASK); - /* Shift register on input */ - c=via_read(via1, vACR); - c&=~SR_CTRL; /* Clear shift register bits */ - c|=SR_EXT; /* Shift on external clock; out or in? */ - via_write(via1, vACR, c); - /* Wipe any pending data and int */ - via_read(via1, vSR); - - /* This is interrupts on enable SR for keyboard */ - via_write(via1, vIER, IER_SET|SR_INT); - /* This clears the interrupt bit */ - via_write(via1, vIFR, SR_INT); - - /* get those pesky clock ticks we missed while booting */ - for ( i = 0; i < 60; i++) { - udelay(ADB_DELAY); - adb_hw_setup_IIsi(); - udelay(ADB_DELAY); - if (via_read(via1, vBufB) & TREQ) - break; - } - if (i == 60) - printk("adb_IIsi: maybe bus jammed ??\n"); - - /* - * Ok we probably ;) have a ready to use adb bus. Its also - */ + adb_hw_setup_IIsi(); request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK, "adb interrupt", adb_cuda_interrupt); adb_state = idle; break; + case MAC_ADB_CUDA: + printk(KERN_INFO "adb: CUDA interface.\n"); + + adb_hw_setup_cuda(); + request_irq(IRQ_MAC_ADB, adb_cuda_interrupt, IRQ_FLG_LOCK, + "adb CUDA interrupt", adb_cuda_interrupt); + adb_state = idle; + break; + case MAC_ADB_IOP: + printk("adb: using IOP for ADB...good luck.\n"); + iop_listen(ADB_IOP, ADB_CHAN, adb_iop_receive, "ADB"); + adb_hw_setup_iop(); + adb_state = idle; + break; + case MAC_ADB_PB1: + case MAC_ADB_PB2: + printk("adb: using PowerManager for ADB... you are doomed.\n"); + adb_hw_setup_pm(); + request_irq(IRQ_MAC_ADB, adb_pm_interrupt, IRQ_FLG_LOCK, + "adb PowerManager interrupt", adb_pm_interrupt); + adb_state = idle; + break; default: printk("adb: Unknown hardware interface.\n"); - nosupp: - printk("adb: Interface unsupported.\n"); restore_flags(flags); return; } - /* - * XXX: interrupt only registered if supported HW !! - * -> unsupported HW will just time out on keyb_init! - */ -#if 0 - request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK, - "adb interrupt", adb_interrupt); -#endif #ifdef DEBUG_ADB_INTS request_irq(IRQ_MAC_ADB_CL, adb_clock_interrupt, IRQ_FLG_LOCK, "adb clock interrupt", adb_clock_interrupt); @@ -289,6 +252,100 @@ void adb_bus_init(void) restore_flags(flags); } +/* Set up IOP ADB */ + +void adb_hw_setup_iop(void) +{ +#if 0 + volatile struct adb_request req; +#endif + + printk("adb-iop: hardware setup in progress.\n"); + +#if 0 + adb_request((struct adb_request *) &req, NULL, + 2, ADB_PACKET, AIF_RESET); + while (!req.complete); +#endif + + printk("adb-iop: hardware setup done!\n"); +} + +/* Strip the adb message out of an IOP message */ + +static void adb_from_iopmsg(struct iop_msg *src, struct adb_iopmsg *dst, + int is_reply) +{ + if (is_reply) { + memcpy(dst, &src->reply[0], sizeof(struct adb_iopmsg)); + } else { + memcpy(dst, &src->message[0], sizeof(struct adb_iopmsg)); + } +} + +/* + * Receive an ADB message from the IOP. + * + * This will be called in two cases: + * + * 1. A message has been successfully sent to the IOP. + * 4. An unsolicited message was received from the IOP. + */ + +void adb_iop_receive(struct iop_msg *msg, struct pt_regs *regs) +{ + struct adb_iopmsg amsg; + struct adb_request *req; + unsigned char packet[16]; + int i; + + req = current_req; + + if (msg->status == IOP_MSGSTATUS_COMPLETE) { + if (req->reply_expected) { + adb_from_iopmsg(msg, &amsg, 1); + req->reply_len = amsg.count + 1; + req->reply[0] = ADB_PACKET; + for (i = 0 ; i < sizeof(amsg.data) ; i++) { + req->reply[i+1] = amsg.data[i]; + } + } + req->complete = 1; + current_req = req->next; + if (req->done) (*req->done)(req); + adb_state = idle; + } else if (msg->status == IOP_MSGSTATUS_UNSOL) { + adb_from_iopmsg(msg, &amsg, 0); + if (amsg.flags & ADB_IOP_TIMEOUT) { /* timeout */ + printk("adb_iop_receive: timeout, retransmitting to %d\n", last_active); + if (last_active == -1) + last_active = (amsg.cmd&0xf0)>>4; + if (current_req) { + adb_start(); + } else { + adb_retransmit(last_active); + } + } else { + /* fake a CUDA-format packet */ + packet[0] = ADB_PACKET; /* packet type */ + packet[1] = 0; /* ???? */ + packet[2] = amsg.cmd; /* command byte */ + i = 0; + while (i < amsg.count) { + packet[i+3] = amsg.data[i]; + i++; + } + adb_input(packet, amsg.count + 3, regs); + memcpy(&msg->reply[0], &msg->message[0], IOP_MSG_LEN); + } + iop_complete_message(msg); + adb_state = idle; + } else { + printk("adb_iop_receive: unknown IOP message state %d.\n", + msg->status); + } +} + void adb_hw_setup_cuda(void) { int x; @@ -398,20 +455,45 @@ void adb_hw_setup_cuda(void) printk("\nCUDA: HW Setup done!\n"); } -void adb_hw_setup_IIsi(void) +static void adb_hw_setup_macII(void) +{ + /* Set the lines up. We want TREQ as input TACK|TIP as output */ + via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ)); + /* + * Docs suggest TREQ should be output - that seems nuts + * BSD agrees here :-) + * Setup vPCR ?? + */ + + /* Set up state: idle */ + via_write(via1, vBufB, via_read(via1, vBufB) | ST_IDLE); + last_status = (via_read(via1, vBufB)&~ST_MASK); + + /* Shift register on input */ + via_write(via1, vACR, (via_read(via1, vACR) & ~SR_CTRL) | SR_EXT); + /* Wipe any pending data and int */ + via_read(via1, vSR); + + /* This is interrupts on enable SR for keyboard */ + via_write(via1, vIER, IER_SET|SR_INT); + /* This clears the interrupt bit */ + via_write(via1, vIFR, SR_INT); +} + +static void adb_IIsi_cleanup(void) { int dummy; long poll_timeout; - printk("adb_IIsi: cleanup!\n"); + printk(KERN_DEBUG "adb_IIsi: cleanup!\n"); /* ??? */ udelay(ADB_DELAY); - /* disable SR int. */ - via_write(via1, vIER, IER_CLR|SR_INT); /* set SR to shift in */ via_write(via1, vACR, via_read(via1, vACR ) & ~SR_OUT); + /* disable SR int. */ + via_write(via1, vIER, IER_CLR|SR_INT); /* this is required, especially on faster machines */ udelay(ADB_DELAY); @@ -454,6 +536,44 @@ void adb_hw_setup_IIsi(void) via_write(via1, vIER, IER_SET|SR_INT); } +static void adb_hw_setup_IIsi(void) +{ + int i; + + /* Set the lines up. We want TREQ as input TACK|TIP as output */ + via_write(via1, vDirB, (via_read(via1,vDirB)|TACK|TIP) & ~TREQ); + + /* Shift register on input */ + via_write(via1, vACR, (via_read(via1, vACR) & ~SR_CTRL) | SR_EXT); + /* Wipe any pending data and int */ + via_read(via1, vSR); + + /* This is interrupts on enable SR for keyboard */ + via_write(via1, vIER, IER_SET|SR_INT); + + /* Set initial state: idle */ + via_write(via1, vBufB, via_read(via1, vBufB) & ~ST_IDLE); + last_status = (via_read(via1, vBufB)&~ST_MASK); + + /* This clears the interrupt bit */ + via_write(via1, vIFR, SR_INT); + + /* get those pesky clock ticks we missed while booting */ + for ( i = 0; i < 60; i++) { + udelay(ADB_DELAY); + adb_IIsi_cleanup(); + udelay(ADB_DELAY); + if (via_read(via1, vBufB) & TREQ) + break; + } + if (i == 60) + printk("adb_IIsi: maybe bus jammed ??\n"); +} + +static void adb_hw_setup_pm(void) +{ +} + #define WAIT_FOR(cond, what) \ do { \ for (x = 1000; !(cond); --x) { \ @@ -477,7 +597,7 @@ void adb_hw_setup_IIsi(void) * here depending on hardware type ... */ int adb_request(struct adb_request *req, void (*done)(struct adb_request *), - int nbytes, ...) + int nbytes, ...) { va_list list; int i, start; @@ -488,6 +608,9 @@ int adb_request(struct adb_request *req, void (*done)(struct adb_request *), * skip first byte if not CUDA */ if (macintosh_config->adb_type == MAC_ADB_II) { +/* + (macintosh_config->adb_type == MAC_ADB_IOP)) { +*/ start = va_arg(list, int); nbytes--; } @@ -599,7 +722,7 @@ void adb_queue_poll(void) r.reply_expected=0; r.done=NULL; r.sent=0; - r.got_reply=0; + r.complete=0; r.reply_len=0; save_flags(flags); cli(); @@ -636,7 +759,7 @@ void adb_retransmit(int device) rt.reply_expected = 0; rt.done = NULL; rt.sent = 0; - rt.got_reply = 0; + rt.complete = 0; rt.reply_len = 0; rt.next = NULL; @@ -674,7 +797,7 @@ int adb_send_request(struct adb_request *req) req->next = 0; req->sent = 0; - req->got_reply = 0; + req->complete = 0; req->reply_len = 0; save_flags(flags); cli(); @@ -696,14 +819,92 @@ int adb_send_request(struct adb_request *req) return 0; } -static int nclock, ndata; +/* + * Start sending an ADB packet, IOP style + * + * There isn't much to do other than hand the packet over to the IOP + * after encapsulating it in an adb_iopmsg. + */ -static int need_poll = 0; -static int command_byte = 0; -static int last_reply = 0; -static int last_active = 0; +static void adb_start_iop(void) +{ + unsigned long flags; + struct adb_request *req; + struct adb_iopmsg amsg; + int reply_expected,i,nb; -static struct adb_request *retry_req; + /* get the packet to send */ + req = current_req; + + /* TODO: re-enable transmits when timeout handling is fixed */ + + req->sent = 1; + req->complete = 1; + if (req->done) (*req->done)(req); + return; + + /* assert adb_state == idle */ + if (adb_state != idle) { + printk("adb-iop: adb_start_iop called while driver busy (%p %x)!\n", + req, adb_state); + return; + } + + if (req->data[0] != ADB_PACKET) { + printk("abb-iop: attempt to send non-ADB packet through IOP\n"); + return; + } + + if (req == 0) return; + save_flags(flags); + cli(); + + /* Always expecting a reply could be fatal since it could clog */ + /* the receive channel, not to mention it won't reset the adb */ + /* state back to idle since adb_iop_receive won't be called. */ + /* It's best to play it safe; if we aren't sure then don't */ + /* set reply_expected; it won't work right but at least it */ + /* won't clog the ADB subsystem. */ + + if ((req->data[1] & 0x0C) == AIF_TALK) { + reply_expected = 1; + } else { + reply_expected = 0; + } + + /* The IOP appears to take MacII-style packets, not CUDA packets */ + + nb = req->nbytes - 2; + if (nb > sizeof(amsg.data)) nb = sizeof(amsg.data); + + printk("adb-iop: sending packet, %d bytes:", nb); + + amsg.flags = ADB_IOP_EXPLICIT; + amsg.cmd = req->data[1]; + amsg.count = nb; + + printk(" %02X", (uint) amsg.cmd); + + i = 0; + while(i < nb) { + amsg.data[i] = req->data[i+2]; + printk(" %02X", (uint) amsg.data[i]); + i++; + } + printk("\n"); + + /* Now send it. The IOP manager will call */ + /* adb_iop_receive when the reply comes */ + /* or when the send is completed if no */ + /* reply is expected. */ + + iop_send_message(ADB_IOP, ADB_CHAN, req, + sizeof(amsg), (__u8 *) &amsg, adb_iop_receive); + + adb_state = sending; + + restore_flags(flags); +} /* * Start sending ADB packet @@ -713,10 +914,15 @@ static void adb_start(void) unsigned long flags; struct adb_request *req; + if (macintosh_config->adb_type == MAC_ADB_IOP) { + adb_start_iop(); + return; + } + /* * We get here on three 'sane' conditions: * 1) called from send_adb_request, if adb_state == idle - * 2) called from within adb_interrupt, if adb_state == idle + * 2) called from within adb_interrupt, if ade_state == idle * (after receiving, or after sending a LISTEN) * 3) called from within adb_interrupt, if adb_state == sending * and no reply is expected (immediate next command). @@ -741,8 +947,10 @@ static void adb_start(void) printk("adb_start: request %p ", req); #endif +#ifdef DEBUG_ADB_INTS nclock = 0; ndata = 0; +#endif /* * IRQ signaled ?? (means ADB controller wants to send, or might @@ -926,6 +1134,7 @@ void adb_poll(void) restore_flags(flags); } +#ifdef DEBUG_ADB_INTS /* * Debugging gimmicks */ @@ -938,6 +1147,7 @@ void adb_data_interrupt(int irq, void *arg, struct pt_regs *regs) { ndata++; } +#endif /* * The notorious ADB interrupt handler - does all of the protocol handling, @@ -1345,7 +1555,7 @@ void adb_interrupt(int irq, void *arg, struct pt_regs *regs) /* XXX: fake ADB header? */ req->reply[0] = req->reply[1] = req->reply[2] = 0xFF; req->reply_len = 3; - req->got_reply = 1; + req->complete = 1; current_req = req->next; if (req->done) (*req->done)(req); @@ -1413,7 +1623,7 @@ void adb_interrupt(int irq, void *arg, struct pt_regs *regs) /* invert state bits, toggle ODD/EVEN */ x = via_read(via1, vBufB); via_write(via1, vBufB, - (x&~ST_MASK)|~(x&ST_MASK)); + (x&~ST_MASK)|(~(x&ST_MASK) & ST_MASK)); } } break; @@ -1632,7 +1842,7 @@ void adb_interrupt(int irq, void *arg, struct pt_regs *regs) /* invert state bits, toggle ODD/EVEN */ x = via_read(via1, vBufB); via_write(via1, vBufB, - (x&~ST_MASK)|~(x&ST_MASK)); + (x&~ST_MASK)|(~(x&ST_MASK) & ST_MASK)); #endif /* adjust packet length */ reply_len--; @@ -1655,7 +1865,7 @@ void adb_interrupt(int irq, void *arg, struct pt_regs *regs) /* invert state bits, toggle ODD/EVEN */ x = via_read(via1, vBufB); via_write(via1, vBufB, - (x&~ST_MASK)|~(x&ST_MASK)); + (x&~ST_MASK)|(~(x&ST_MASK) & ST_MASK)); } } } @@ -1672,7 +1882,7 @@ void adb_interrupt(int irq, void *arg, struct pt_regs *regs) { req = current_req; req->reply_len = reply_ptr - req->reply; - req->got_reply = 1; + req->complete = 1; current_req = req->next; if (req->done) (*req->done)(req); @@ -1824,14 +2034,6 @@ void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs) udelay(150); via_write(via1, vBufB, via_read(via1, vBufB) & ~TACK); } - else if(macintosh_config->adb_type==MAC_ADB_II) - { - if (status != TREQ) - printk("adb_macII: state=idle status=%x want=%x\n", - status, TREQ); - x = via_read(via1, vSR); - via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK)); - } adb_state = reading; reply_ptr = cuda_rbuf; reply_len = 0; @@ -1949,9 +2151,7 @@ void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs) /* delay */ udelay(ADB_DELAY); /* set the shift register to shift out and send a byte */ -#if 0 via_write(via1, vACR, via_read(via1, vACR) | SR_OUT); -#endif via_write(via1, vSR, current_req->data[1]); /* signal 'byte ready' */ via_write(via1, vBufB, via_read(via1, vBufB) | TACK); @@ -1959,17 +2159,6 @@ void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs) adb_state = sending; } } - else if(macintosh_config->adb_type==MAC_ADB_II) - { - if(status!=TIP+SR_OUT) - printk("adb_macII: state=send_first_byte status=%x want=%x\n", - status, TIP+SR_OUT); - via_write(via1, vSR, current_req->data[1]); - via_write(via1, vBufB, - via_read(via1, vBufB)^TACK); - data_index=2; - adb_state = sending; - } break; case sending: @@ -1997,21 +2186,13 @@ void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs) /* delay */ udelay(ADB_DELAY); /* set the shift register to shift in */ - via_write(via1, vACR, via_read(via1, vACR)|SR_OUT); + via_write(via1, vACR, via_read(via1, vACR) & ~SR_OUT); /* clear SR int. */ x = via_read(via1, vSR); /* set ADB state 'idle' (end of frame) */ via_write(via1, vBufB, via_read(via1,vBufB) & ~(TACK|TIP)); } - else if(macintosh_config->adb_type==MAC_ADB_II) - { - via_write(via1, vACR, - via_read(via1, vACR) & ~SR_OUT); - x=via_read(via1, vSR); - via_write(via1, vBufB, - via_read(via1, vBufB)|TACK|TIP); - } req->sent = 1; if (req->reply_expected) { @@ -2019,10 +2200,11 @@ void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs) * maybe fake a reply here on Listen ?? * Otherwise, a Listen hangs on success * CUDA+IIsi: only ADB Talk considered - * RTC/PRAM read (0x1 0x3) to follow. */ if ( (req->data[0] == 0x0) && ((req->data[1]&0xc) == 0xc) ) adb_state = awaiting_reply; + else if ((req->data[0] == 0x1) && (req->data[1] == 0x3) ) + adb_state = awaiting_reply; else { /* * Reply expected, but none @@ -2036,7 +2218,7 @@ void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs) /* XXX: fake ADB header? */ req->reply[0] = req->reply[1] = req->reply[2] = 0xFF; req->reply_len = 3; - req->got_reply = 1; + req->complete = 1; current_req = req->next; if (req->done) (*req->done)(req); @@ -2088,12 +2270,6 @@ void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs) /* signal 'byte ready' */ via_write(via1, vBufB, via_read(via1, vBufB) | TACK); } - else if(macintosh_config->adb_type==MAC_ADB_II) - { - via_write(via1, vSR, req->data[data_index++]); - via_write(via1, vBufB, - via_read(via1, vBufB)^TACK); - } } break; @@ -2169,24 +2345,6 @@ void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs) Handshake anyway?? */ } } - if(macintosh_config->adb_type==MAC_ADB_II) - { - if( status == TIP) - { - via_write(via1, vBufB, - via_read(via1, vBufB)|TACK|TIP); - adb_state = read_done; - } - else - { -#if (ADBDEBUG & ADBDEBUG_STATUS) - if(status!=TIP+TREQ) - printk("macII_adb: state=reading status=%x\n", status); -#endif - via_write(via1, vBufB, - via_read(via1, vBufB)^TACK); - } - } /* fall through for IIsi on end of frame */ if (macintosh_config->adb_type != MAC_ADB_IISI || adb_state != read_done) @@ -2203,7 +2361,7 @@ void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs) { req = current_req; req->reply_len = reply_ptr - req->reply; - req->got_reply = 1; + req->complete = 1; current_req = req->next; if (req->done) (*req->done)(req); @@ -2247,31 +2405,38 @@ void adb_cuda_interrupt(int irq, void *arg, struct pt_regs *regs) } +static void adb_pm_interrupt(int irq, void *arg, struct pt_regs *regs) +{ + printk("adb_pm_interrupt called!\n"); +} + /* * The 'reply delivery' routine; determines which device sent the * request and calls the appropriate handler. * Reply data are expected in CUDA format (again, argh...) so we need * to fake this in the interrupt handler for MacII. + * + * Note! The PowerPC code now expects data in ADB format, and + * has their CUDA handler throw non-ADB packets on the floor. We + * are going to sync with them. It is only a matter of time. + * * Only one handler per device ID is currently possible. * XXX: is the ID field here representing the default or real ID? */ static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs) { - int i, id; + int id; switch (buf[0]) { case ADB_PACKET: /* what's in buf[1] ?? */ + /* according to via-cuda.c, buf[1] should tell + us whether to autopoll or not */ id = buf[2] >> 4; -#if 0 - xmon_printf("adb packet: "); - for (i = 0; i < nb; ++i) - xmon_printf(" %x", buf[i]); - xmon_printf(", id = %d\n", id); -#endif #if (ADBDEBUG & ADBDEBUG_INPUT) if (console_loglevel == 10) { + int i; printk("adb_input: adb packet "); for (i = 0; i < nb; ++i) printk(" %x", buf[i]); @@ -2287,6 +2452,7 @@ static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs) default: #if (ADBDEBUG & ADBDEBUG_INPUT) if (console_loglevel == 10) { + int i; printk("adb_input: data from via (%d bytes):", nb); for (i = 0; i < nb; ++i) printk(" %.2x", buf[i]); @@ -2338,7 +2504,7 @@ static int adb_wait_reply(struct adbdev_state *state, struct file *file) add_wait_queue(&adb_wait, &wait); current->state = TASK_INTERRUPTIBLE; - while (!state->req.got_reply) { + while (!state->req.complete) { if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; break; @@ -2358,9 +2524,9 @@ static int adb_wait_reply(struct adbdev_state *state, struct file *file) static void adb_write_done(struct adb_request *req) { - if (!req->got_reply) { + if (!req->complete) { req->reply_len = 0; - req->got_reply = 1; + req->complete = 1; } wake_up_interruptible(&adb_wait); } @@ -2371,7 +2537,7 @@ struct file_operations *adb_cooked[16]; static int adb_open(struct inode *inode, struct file *file) { - int adb_type, adb_subtype; + int adb_subtype; struct adbdev_state *state; if (MINOR(inode->i_rdev) > ADB_MAX_MINOR) @@ -2401,28 +2567,29 @@ static int adb_open(struct inode *inode, struct file *file) return 0; } -static void adb_release(struct inode *inode, struct file *file) +static int adb_release(struct inode *inode, struct file *file) { struct adbdev_state *state = file->private_data; if (state) { + int ret; + file->private_data = NULL; - if (state->req.reply_expected && !state->req.got_reply) - if (adb_wait_reply(state, file)) - return; + if (state->req.reply_expected && !state->req.complete) + if ((ret = adb_wait_reply(state, file))) + return ret; kfree(state); } - return; + return 0; } -static int adb_lseek(struct inode *inode, struct file *file, - off_t offset, int origin) +static loff_t adb_lseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } -static int adb_read(struct inode *inode, struct file *file, - char *buf, int count) +static ssize_t adb_read(struct file *file, char *buf, + size_t count, loff_t *offset) { int ret; struct adbdev_state *state = file->private_data; @@ -2449,8 +2616,8 @@ static int adb_read(struct inode *inode, struct file *file, return ret; } -static int adb_write(struct inode *inode, struct file *file, - const char *buf, int count) +static ssize_t adb_write(struct file *file, const char *buf, + size_t count, loff_t *offset) { int ret, i; struct adbdev_state *state = file->private_data; @@ -2461,7 +2628,7 @@ static int adb_write(struct inode *inode, struct file *file, if (ret) return ret; - if (state->req.reply_expected && !state->req.got_reply) { + if (state->req.reply_expected && !state->req.complete) { /* A previous request is still being processed. Wait for it to finish. */ ret = adb_wait_reply(state, file); @@ -2471,7 +2638,7 @@ static int adb_write(struct inode *inode, struct file *file, state->req.nbytes = count; state->req.done = adb_write_done; - state->req.got_reply = 0; + state->req.complete = 0; copy_from_user(state->req.data, buf, count); #if 0 switch (adb_hardware) { @@ -2508,7 +2675,12 @@ static struct file_operations adb_fops = { NULL, /* no mmap */ adb_open, NULL, /* flush */ - adb_release + adb_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ }; int adbdev_register(int subtype, struct file_operations *fops) @@ -2537,7 +2709,6 @@ void adbdev_init() printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR); } - #if 0 /* old ADB device */ /* @@ -2566,7 +2737,7 @@ static int adb_wait_reply(struct adbdev_state *state, struct file *file) add_wait_queue(&adb_wait, &wait); current->state = TASK_INTERRUPTIBLE; - while (!state->req.got_reply) { + while (!state->req.complete) { if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; break; @@ -2586,9 +2757,9 @@ static int adb_wait_reply(struct adbdev_state *state, struct file *file) static void adb_write_done(struct adb_request *req) { - if (!req->got_reply) { + if (!req->complete) { req->reply_len = 0; - req->got_reply = 1; + req->complete = 1; } wake_up_interruptible(&adb_wait); } @@ -2611,7 +2782,7 @@ static void adb_release(struct inode *inode, struct file *file) if (state) { file->private_data = NULL; - if (state->req.reply_expected && !state->req.got_reply) + if (state->req.reply_expected && !state->req.complete) if (adb_wait_reply(state, file)) return; kfree(state); @@ -2619,14 +2790,13 @@ static void adb_release(struct inode *inode, struct file *file) return; } -static int adb_lseek(struct inode *inode, struct file *file, - off_t offset, int origin) +static int adb_lseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } -static int adb_read(struct inode *inode, struct file *file, - char *buf, int count) +static int adb_read(struct file *file, char *buf, size_t count, + loff_t *offset) { int ret; struct adbdev_state *state = file->private_data; @@ -2653,8 +2823,8 @@ static int adb_read(struct inode *inode, struct file *file, return ret; } -static int adb_write(struct inode *inode, struct file *file, - const char *buf, int count) +static int adb_write(struct file *file, const char *buf, + size_t count, loff_t *offset) { int ret; struct adbdev_state *state = file->private_data; @@ -2665,7 +2835,7 @@ static int adb_write(struct inode *inode, struct file *file, if (ret) return ret; - if (state->req.reply_expected && !state->req.got_reply) { + if (state->req.reply_expected && !state->req.complete) { /* A previous request is still being processed. Wait for it to finish. */ ret = adb_wait_reply(state, file); @@ -2677,7 +2847,7 @@ static int adb_write(struct inode *inode, struct file *file, state->req.done = adb_write_done; memcpy_fromfs(state->req.data, buf, count); state->req.reply_expected = 1; - state->req.got_reply = 0; + state->req.complete = 0; adb_send_request(&state->req); return count; diff --git a/arch/m68k/mac/adb-misc.c b/arch/m68k/mac/adb-misc.c new file mode 100644 index 000000000000..f5dc8e2afc9b --- /dev/null +++ b/arch/m68k/mac/adb-misc.c @@ -0,0 +1,145 @@ +/* + * ADB Miscellaneous stuff + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef CONFIG_ADB_NEW +#include +#else +#include +#endif +#include +#include +#include +#include + +extern void cuda_poll(void); +extern int cuda_request(struct adb_request *req, + void (*done)(struct adb_request *), + int nbytes, ...); + +/* + * Return the current time as the number of seconds since January 1, 1904. + * + * This method works for both CUDA and IIsi-style ADB. It'll probably + * work for PowerBooks too once we have PowerBook ADB working. + */ + +__u32 adb_read_time(void) +{ + volatile struct adb_request req; + __u32 time; + +#ifdef CONFIG_ADB_NEW +#ifdef CONFIG_ADB_CUDA + cuda_request((struct adb_request *) &req, NULL, + 2, CUDA_PACKET, CUDA_GET_TIME); + while (!req.complete) cuda_poll(); +#endif +#else + adb_request((struct adb_request *) &req, NULL, + 2, CUDA_PACKET, CUDA_GET_TIME); + while (!req.complete); +#endif + + time = (req.reply[3] << 24) | (req.reply[4] << 16) + | (req.reply[5] << 8) | req.reply[6]; + return time; +} + +/* + * Set the current system time + * + * This method works for both CUDA and IIsi-style ADB. It'll probably + * work for PowerBooks too once we have PowerBook ADB working. + */ + +void adb_write_time(__u32 data) +{ + volatile struct adb_request req; + +#ifdef CONFIG_ADB_NEW +#ifdef CONFIG_ADB_CUDA + cuda_request((struct adb_request *) &req, NULL, + 6, CUDA_PACKET, CUDA_SET_TIME, + (data >> 24) & 0xFF, (data >> 16) & 0xFF, + (data >> 8) & 0xFF, data & 0xFF); + while (!req.complete) cuda_poll(); +#endif +#else + adb_request((struct adb_request *) &req, NULL, + 6, CUDA_PACKET, CUDA_SET_TIME, + (data >> 24) & 0xFF, (data >> 16) & 0xFF, + (data >> 8) & 0xFF, data & 0xFF); + while (!req.complete); +#endif +} + +/* + * Initially discovered this technique in the Mach kernel of MkLinux in + * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c. Found equivalent LinuxPPC + * code in arch/ppc/kernel/setup.c, which also has a PMU technique for + * PowerBooks! + * + * --David Kilzer + */ + +void adb_poweroff(void) +{ + struct adb_request req; + + /* + * Print our "safe" message before we send the request + * just in case the request never returns. + */ + + printk ("It is now safe to switch off your machine.\n"); + +#ifdef CONFIG_ADB_NEW +#ifdef CONFIG_ADB_CUDA + cuda_request (&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); + for (;;) cuda_poll(); +#endif +#else + adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); + for (;;) ; +#endif +} + +/* + * Initially discovered this technique in the Mach kernel of MkLinux in + * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c. Found equivalent LinuxPPC + * code in arch/ppc/kernel/setup.c, which also has a PMU technique! + * --David Kilzer + * + * I suspect the MAC_ADB_CUDA code might work with other ADB types of machines + * but have no way to test this myself. --DDK + */ + +void adb_hwreset(void) +{ + struct adb_request req; + + printk ("Resetting system...\n"); + +#ifdef CONFIG_ADB_NEW +#ifdef CONFIG_ADB_CUDA + cuda_request (&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM); + for (;;) cuda_poll(); +#endif +#else + adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM); + for (;;) ; +#endif +} diff --git a/arch/m68k/mac/bootparse.c b/arch/m68k/mac/bootparse.c index ae5046c3e32e..f7600999bd5d 100644 --- a/arch/m68k/mac/bootparse.c +++ b/arch/m68k/mac/bootparse.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c index 8e902d0bd1ac..f7419ebed5a7 100644 --- a/arch/m68k/mac/config.c +++ b/arch/m68k/mac/config.c @@ -37,7 +37,20 @@ #include #include -#include "via6522.h" +#include +#include +#include +#include + +/* Offset between Unix time (1970-based) and Mac time (1904-based) */ + +#define MAC_TIME_OFFSET 2082844800 + +/* + * hardware reset vector + */ + +static void (*rom_reset)(void); /* Mac bootinfo struct */ @@ -59,30 +72,20 @@ void *mac_env; /* Loaded by the boot asm */ unsigned long mac_orig_videoaddr; /* Mac specific keyboard functions */ -extern int mac_keyb_init(void); -extern int mac_kbdrate(struct kbd_repeat *k); -extern void mac_kbd_leds(unsigned int leds); - -/* Mac specific irq functions */ -extern void mac_init_IRQ (void); -extern void (*mac_handlers[]) (int, void *, struct pt_regs *); -extern int mac_request_irq (unsigned int irq, - void (*handler)(int, void *, struct pt_regs *), - unsigned long flags, const char *devname, - void *dev_id); -extern void mac_free_irq (unsigned int irq, void *dev_id); -extern void mac_enable_irq (unsigned int); -extern void mac_disable_irq (unsigned int); -static void mac_get_model(char *model); -/*static int mac_get_hardware_list(char *buffer);*/ -extern int mac_get_irq_list (char *); +extern int mackbd_init_hw(void); +extern void mackbd_leds(unsigned int leds); /* Mac specific timer functions */ extern unsigned long mac_gettimeoffset (void); static void mac_gettod (int *, int *, int *, int *, int *, int *); static int mac_hwclk (int, struct hwclk_time *); static int mac_set_clock_mmss (unsigned long); +extern void iop_init(void); +extern void via_init(void); extern void via_init_clock(void (*func)(int, void *, struct pt_regs *)); +extern void via_flush_cache(void); +extern void oss_init(void); +extern void psc_init(void); extern void (*kd_mksound)(unsigned int, unsigned int); extern void mac_mksound(unsigned int, unsigned int); @@ -95,6 +98,18 @@ extern void nubus_sweep_video(void); extern void mac_debug_init(void); extern void mac_debugging_long(int, long); +/* poweroff functions */ +extern void via_poweroff(void); +extern void oss_poweroff(void); +extern void adb_poweroff(void); +extern void adb_hwreset(void); + +/* pram functions */ +extern __u32 via_read_time(void); +extern void via_write_time(__u32); +extern __u32 adb_read_time(void); +extern void adb_write_time(__u32); + #ifdef CONFIG_MAGIC_SYSRQ static char mac_sysrq_xlate[128] = "\000sdfghzxcv\000bqwer" /* 0x00 - 0x0f */ @@ -124,92 +139,197 @@ static void mac_sched_init(void (*vector)(int, void *, struct pt_regs *)) extern int console_loglevel; +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +static unsigned long mktime(unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + return ((( + (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + /* - * This function translates the boot timeval into a proper date, to initialize - * the system time. + * This function translates seconds since 1970 into a proper date. + * + * Algorithm cribbed from glibc2.1, __offtime(). */ +#define SECS_PER_MINUTE (60) +#define SECS_PER_HOUR (SECS_PER_MINUTE * 60) +#define SECS_PER_DAY (SECS_PER_HOUR * 24) -static void mac_gettod (int *yearp, int *monp, int *dayp, - int *hourp, int *minp, int *secp) +static void unmktime(unsigned long time, long offset, + int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp) { - unsigned long time; - int leap, oldleap, isleap; - int mon_days[14] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, -1 }; - - time = mac_bi_data.boottime - 60*mac_bi_data.gmtbias; /* seconds */ - - *minp = time / 60; - *secp = time - (*minp * 60); - time = *minp; /* minutes now */ - - *hourp = time / 60; - *minp = time - (*hourp * 60); - time = *hourp; /* hours now */ - - *dayp = time / 24; - *hourp = time - (*dayp * 24); - time = *dayp; /* days now ... */ - - /* for leap day calculation */ - *yearp = (time / 365) + 1970; /* approx. year */ - - /* leap year calculation - there's an easier way, I bet. And it's broken :-( */ - /* calculate leap days up to previous year */ - oldleap = (*yearp-1)/4 - (*yearp-1)/100 + (*yearp-1)/400; - /* calculate leap days incl. this year */ - leap = *yearp/4 - *yearp/100 + *yearp/400; - /* this year a leap year ?? */ - isleap = (leap != oldleap); - - /* adjust days: days, excluding past leap days since epoch */ - time -= oldleap - (1970/4 - 1970/100 + 1970/400); - - /* precise year, and day in year */ - *yearp = (time / 365); /* #years since epoch */ - *dayp = time - (*yearp * 365) + 1; /* #days this year (0: Jan 1) */ - *yearp += 70; /* add epoch :-) */ - time = *dayp; - - if (isleap) /* add leap day ?? */ - mon_days[2] += 1; - - /* count the months */ - for (*monp = 1; time > mon_days[*monp]; (*monp)++) - time -= mon_days[*monp]; + /* How many days come before each month (0-12). */ + static const unsigned short int __mon_yday[2][13] = + { + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } + }; + long int days, rem, y, wday, yday; + const unsigned short int *ip; + + days = time / SECS_PER_DAY; + rem = time % SECS_PER_DAY; + rem += offset; + while (rem < 0) { + rem += SECS_PER_DAY; + --days; + } + while (rem >= SECS_PER_DAY) { + rem -= SECS_PER_DAY; + ++days; + } + *hourp = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + *minp = rem / SECS_PER_MINUTE; + *secp = rem % SECS_PER_MINUTE; + /* January 1, 1970 was a Thursday. */ + wday = (4 + days) % 7; /* Day in the week. Not currently used */ + if (wday < 0) wday += 7; + y = 1970; + +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) +#define __isleap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + + while (days < 0 || days >= (__isleap (y) ? 366 : 365)) + { + /* Guess a corrected year, assuming 365 days per year. */ + long int yg = y + days / 365 - (days % 365 < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + y = yg; + } + *yearp = y - 1900; + yday = days; /* day in the year. Not currently used. */ + ip = __mon_yday[__isleap(y)]; + for (y = 11; days < (long int) ip[y]; --y) + continue; + days -= ip[y]; + *monp = y; + *dayp = days + 1; /* day in the month */ + return; +} - *dayp = time; +/* + * Return the boot time for use in initializing the kernel clock. + * + * I'd like to read the hardware clock here but many machines read + * the PRAM through ADB, and interrupts aren't initialized when this + * is called so ADB obviously won't work. + */ - return; +static void mac_gettod(int *yearp, int *monp, int *dayp, + int *hourp, int *minp, int *secp) +{ + /* Yes the GMT bias is backwards. It looks like Penguin is + screwing up the boottime it gives us... This works for me + in Canada/Eastern but it might be wrong everywhere else. */ + unmktime(mac_bi_data.boottime, -mac_bi_data.gmtbias * 60, + yearp, monp, dayp, hourp, minp, secp); + /* For some reason this is off by one */ + *monp = *monp + 1; } /* - * TBI: read and write hwclock + * Read/write the hardware clock. */ -static int mac_hwclk( int op, struct hwclk_time *t ) +static int mac_hwclk(int op, struct hwclk_time *t) { - return 0; + unsigned long now; + + if (!op) { /* read */ + if (macintosh_config->adb_type == MAC_ADB_II) { + now = via_read_time(); + } else if ((macintosh_config->adb_type == MAC_ADB_IISI) || + (macintosh_config->adb_type == MAC_ADB_CUDA)) { + now = adb_read_time(); + } else if (macintosh_config->adb_type == MAC_ADB_IOP) { + now = via_read_time(); + } else { + now = MAC_TIME_OFFSET; + } + + now -= MAC_TIME_OFFSET; + + t->wday = 0; + unmktime(now, 0, + &t->year, &t->mon, &t->day, + &t->hour, &t->min, &t->sec); + } else { /* write */ + now = mktime(t->year + 1900, t->mon + 1, t->day, + t->hour, t->min, t->sec) + MAC_TIME_OFFSET; + + if (macintosh_config->adb_type == MAC_ADB_II) { + via_write_time(now); + } else if ((macintosh_config->adb_type == MAC_ADB_IISI) || + (macintosh_config->adb_type == MAC_ADB_CUDA)) { + adb_write_time(now); + } else if (macintosh_config->adb_type == MAC_ADB_IOP) { + via_write_time(now); + } + } + return 0; } /* - * TBI: set minutes/seconds in hwclock + * Set minutes/seconds in the hardware clock */ static int mac_set_clock_mmss (unsigned long nowtime) { - short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; + struct hwclk_time now; - return 0; + mac_hwclk(0, &now); + now.sec = nowtime % 60; + now.min = (nowtime / 60) % 60; + mac_hwclk(1, &now); + + return 0; } +#if 0 void mac_waitbut (void) { ; } +#endif extern struct consw fb_con; extern struct fb_info *mac_fb_init(long *); -extern void mac_video_setup(char *, int *); + +extern void mac_default_handler(int, void *, struct pt_regs *); void (*mac_handlers[8])(int, void *, struct pt_regs *)= { @@ -237,7 +357,7 @@ __initfunc(int mac_parse_bootinfo(const struct bi_record *record)) mac_bi_data.id = *data; break; case BI_MAC_VADDR: - mac_bi_data.videoaddr = VIDEOMEMBASE + (*data & ~VIDEOMEMMASK); + mac_bi_data.videoaddr = *data; break; case BI_MAC_VDEPTH: mac_bi_data.videodepth = *data; @@ -249,7 +369,8 @@ __initfunc(int mac_parse_bootinfo(const struct bi_record *record)) mac_bi_data.dimensions = *data; break; case BI_MAC_VLOGICAL: - mac_bi_data.videological = *data; + mac_bi_data.videological = VIDEOMEMBASE + (*data & ~VIDEOMEMMASK); + mac_orig_videoaddr = *data; break; case BI_MAC_SCCBASE: mac_bi_data.sccbase = *data; @@ -266,6 +387,9 @@ __initfunc(int mac_parse_bootinfo(const struct bi_record *record)) case BI_MAC_CPUID: mac_bi_data.cpuid = *data; break; + case BI_MAC_ROMBASE: + mac_bi_data.rombase = *data; + break; default: unknown = 1; } @@ -280,12 +404,11 @@ __initfunc(int mac_parse_bootinfo(const struct bi_record *record)) static void mac_cache_card_flush(int writeback) { - unsigned long flags; - save_flags(flags); + unsigned long cpu_flags; + save_flags(cpu_flags); cli(); - via_write(via2, vBufB, via_read(via2,vBufB)&~VIA2B_vMode32); - via_write(via2, vBufB, via_read(via2,vBufB)|VIA2B_vMode32); - restore_flags(flags); + via_flush_cache(); + restore_flags(cpu_flags); } __initfunc(void config_mac(void)) @@ -298,9 +421,8 @@ __initfunc(void config_mac(void)) mac_debug_init(); mach_sched_init = mac_sched_init; - mach_keyb_init = mac_keyb_init; - mach_kbdrate = mac_kbdrate; - mach_kbd_leds = mac_kbd_leds; + mach_keyb_init = mackbd_init_hw; + mach_kbd_leds = mackbd_leds; mach_init_IRQ = mac_init_IRQ; mach_request_irq = mac_request_irq; mach_free_irq = mac_free_irq; @@ -319,11 +441,12 @@ __initfunc(void config_mac(void)) mach_mksound = mac_mksound; #endif mach_reset = mac_reset; + mach_halt = mac_poweroff; + mach_power_off = mac_poweroff; conswitchp = &dummy_con; mach_max_dma_address = 0xffffffff; #if 0 mach_debug_init = mac_debug_init; - mach_video_setup = mac_video_setup; #endif kd_mksound = mac_mksound; #ifdef CONFIG_MAGIC_SYSRQ @@ -346,18 +469,15 @@ __initfunc(void config_mac(void)) mac_identify(); mac_report_hardware(); - if( - /* Cache cards */ - macintosh_config->ident == MAC_MODEL_IICI|| - macintosh_config->ident == MAC_MODEL_IISI|| - macintosh_config->ident == MAC_MODEL_IICX|| - /* On board L2 cache */ - macintosh_config->ident == MAC_MODEL_IIFX) - { + /* AFAIK only the IIci takes a cache card. The IIfx has onboard + cache ... someone needs to figure out how to tell if it's on or + not. */ + if (macintosh_config->ident == MAC_MODEL_IICI + || macintosh_config->ident == MAC_MODEL_IIFX) { mach_l2_flush = mac_cache_card_flush; } - /* goes on forever if timers broken */ #ifdef MAC_DEBUG_SOUND + /* goes on forever if timers broken */ mac_mksound(1000,10); #endif @@ -365,7 +485,9 @@ __initfunc(void config_mac(void)) * Check for machine specific fixups. */ +#ifdef OLD_NUBUS_CODE nubus_sweep_video(); +#endif } @@ -403,10 +525,11 @@ static struct mac_model mac_data_table[]= * The IIfx apparently has different ADB hardware, and stuff * so zany nobody knows how to drive it. * Even so, with Marten's help we'll try to deal with it :-) + * CSA: see http://developer.apple.com/technotes/hw/hw_09.html */ { MAC_MODEL_IICI, "IIci", MAC_ADB_II, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_IIFX, "IIfx", MAC_ADB_II, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_IIFX, "IIfx", MAC_ADB_IOP, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_IISI, "IIsi", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_IIVI, "IIvi", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_IIVX, "IIvx", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, @@ -435,70 +558,72 @@ static struct mac_model mac_data_table[]= * confuse us. The 840AV has a SCSI location of its own (same as * the 660AV). */ - + { MAC_MODEL_Q605, "Quadra 605", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_Q605_ACC, "Quadra 605 (33MHz)", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, { MAC_MODEL_Q610, "Quadra 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q630, "Quadra 630", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_QUADRA, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q650, "Quadra 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, /* The Q700 does have a NS Sonic */ - { MAC_MODEL_Q700, "Quadra 700", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_QUADRA2, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_Q700, "Quadra 700", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_QUADRA2, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q800, "Quadra 800", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, { MAC_MODEL_Q840, "Quadra 840AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_MACE, MAC_NUBUS}, - /* These might have IOP problems */ - { MAC_MODEL_Q900, "Quadra 900", MAC_ADB_IISI, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_SONIC, MAC_NUBUS}, - { MAC_MODEL_Q950, "Quadra 950", MAC_ADB_IISI, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_Q900, "Quadra 900", MAC_ADB_IOP, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_Q950, "Quadra 950", MAC_ADB_IOP, MAC_VIA_QUADRA, MAC_SCSI_QUADRA2, MAC_IDE_NONE, MAC_SCC_IOP, MAC_ETHER_SONIC, MAC_NUBUS}, /* * Performa - more LC type machines */ - { MAC_MODEL_P460, "Performa 460", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P475, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P475F, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P520, "Performa 520", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P550, "Performa 550", MAC_ADB_CUDA, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P575, "Performa 575", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P588, "Performa 588", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_TV, "TV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_P600, "Performa 600", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, -#if 0 /* other sources seem to suggest the P630/Q630/LC630 is more like LCIII */ - { MAC_MODEL_P630, "Performa 630", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, -#endif + { MAC_MODEL_P460, "Performa 460", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P475, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P475F, "Performa 475", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P520, "Performa 520", MAC_ADB_CUDA, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P550, "Performa 550", MAC_ADB_CUDA, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P575, "Performa 575", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + /* These have the comm slot, and therefore the possibility of SONIC ethernet */ + { MAC_MODEL_P588, "Performa 588", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_QUADRA, MAC_SCC_II, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_TV, "TV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_P600, "Performa 600", MAC_ADB_IISI, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_II, MAC_ETHER_NONE, MAC_NUBUS}, + /* * Centris - just guessing again; maybe like Quadra */ - { MAC_MODEL_C610, "Centris 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, - { MAC_MODEL_C650, "Centris 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, - { MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + /* The C610 may or may not have SONIC. We probe to make sure */ + { MAC_MODEL_C610, "Centris 610", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_C650, "Centris 650", MAC_ADB_II, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, + { MAC_MODEL_C660, "Centris 660AV", MAC_ADB_CUDA, MAC_VIA_QUADRA, MAC_SCSI_QUADRA3, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_MACE, MAC_NUBUS}, /* * Power books - seem similar to early Quadras ? (most have 030 though) */ - { MAC_MODEL_PB140, "PowerBook 140", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB145, "PowerBook 145", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB140, "PowerBook 140", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB145, "PowerBook 145", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, /* The PB150 has IDE, and IIci style VIA */ - { MAC_MODEL_PB150, "PowerBook 150", MAC_ADB_PB1, MAC_VIA_IIci, MAC_SCSI_QUADRA, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB160, "PowerBook 160", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB165, "PowerBook 165", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB165C, "PowerBook 165c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB170, "PowerBook 170", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB180, "PowerBook 180", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB180C, "PowerBook 180c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB190, "PowerBook 190", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB520, "PowerBook 520", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB150, "PowerBook 150", MAC_ADB_PB1, MAC_VIA_IIci, MAC_SCSI_NONE, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB160, "PowerBook 160", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB165, "PowerBook 165", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB165C, "PowerBook 165c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB170, "PowerBook 170", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB180, "PowerBook 180", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB180C, "PowerBook 180c", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB190, "PowerBook 190cs", MAC_ADB_PB1, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_PB, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + /* These have onboard SONIC */ + { MAC_MODEL_PB520, "PowerBook 520", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_NONE, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_SONIC, MAC_NUBUS}, /* * Power book Duos - similar to Power books, I hope */ - { MAC_MODEL_PB210, "PowerBook Duo 210", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB230, "PowerBook Duo 230", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB250, "PowerBook Duo 250", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB270C, "PowerBook Duo 270c", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB280, "PowerBook Duo 280", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, - { MAC_MODEL_PB280C, "PowerBook Duo 280c", MAC_ADB_PB2, MAC_VIA_QUADRA, MAC_SCSI_QUADRA, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + /* All of these might have onboard SONIC in the Dock but I'm not quite sure */ + { MAC_MODEL_PB210, "PowerBook Duo 210", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB230, "PowerBook Duo 230", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB250, "PowerBook Duo 250", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB270C, "PowerBook Duo 270c", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB280, "PowerBook Duo 280", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, + { MAC_MODEL_PB280C, "PowerBook Duo 280c", MAC_ADB_PB2, MAC_VIA_IIci, MAC_SCSI_OLD, MAC_IDE_NONE, MAC_SCC_QUADRA, MAC_ETHER_NONE, MAC_NUBUS}, /* * Other stuff ?? @@ -533,7 +658,8 @@ void mac_identify(void) model); model = MAC_MODEL_Q800; printk("Defaulting to: Quadra800, model id %d\n", model); - printk("Please report this case to linux-mac68k@wave.lm.com\n"); + printk("Please report this case to " + "linux-mac68k@mac.linux-m68k.org\n"); m=&mac_data_table[0]; while(m->ident != -1) { @@ -592,7 +718,10 @@ void mac_identify(void) break; } - via_configure_base(); + iop_init(); + via_init(); + oss_init(); + psc_init(); } void mac_report_hardware(void) @@ -604,8 +733,133 @@ static void mac_get_model(char *str) { strcpy(str,"Macintosh "); strcat(str, macintosh_config->name); - if(mach_l2_flush && !(via_read(via2, vBufB)&VIA2B_vCDis)) - strcat(str, "(+L2 cache)"); +} + +/* + * The power switch - yes it's software! + */ + +void mac_poweroff(void) +{ + /* + * MAC_ADB_IISI may need to be moved up here if it doesn't actually + * work using the ADB packet method. --David Kilzer + */ + + if (oss_present) { + oss_poweroff(); + } else if (macintosh_config->adb_type == MAC_ADB_II) { + via_poweroff(); + } else { + adb_poweroff(); + } +} + +/* + * Not all Macs support software power down; for the rest, just + * try the ROM reset vector ... + */ +void mac_reset(void) +{ + /* + * MAC_ADB_IISI may need to be moved up here if it doesn't actually + * work using the ADB packet method. --David Kilzer + */ + + if (macintosh_config->adb_type == MAC_ADB_II) { + unsigned long cpu_flags; + + /* need ROMBASE in booter */ + /* indeed, plus need to MAP THE ROM !! */ + + if (mac_bi_data.rombase == 0) + mac_bi_data.rombase = 0x40800000; + + /* works on some */ + rom_reset = (void *) (mac_bi_data.rombase + 0xa); + + if (macintosh_config->ident == MAC_MODEL_SE30) { + /* + * MSch: Machines known to crash on ROM reset ... + */ + printk("System halted.\n"); + while(1); + } else { + save_flags(cpu_flags); + cli(); + + rom_reset(); + + restore_flags(cpu_flags); + } + + /* We never make it this far... it usually panics above. */ + printk ("Restart failed. Please restart manually.\n"); + + /* XXX - delay do we need to spin here ? */ + while(1); /* Just in case .. */ + } else if (macintosh_config->adb_type == MAC_ADB_IISI + || macintosh_config->adb_type == MAC_ADB_CUDA) { + adb_hwreset(); + } else if (CPU_IS_030) { + + /* 030-specific reset routine. The idea is general, but the + * specific registers to reset are '030-specific. Until I + * have a non-030 machine, I can't test anything else. + * -- C. Scott Ananian + */ + + unsigned long rombase = 0x40000000; + + /* make a 1-to-1 mapping, using the transparent tran. reg. */ + unsigned long virt = (unsigned long) mac_reset; + unsigned long phys = virt_to_phys(mac_reset); + unsigned long offset = phys-virt; + cli(); /* lets not screw this up, ok? */ + __asm__ __volatile__(".chip 68030\n\t" + "pmove %0,%/tt0\n\t" + ".chip 68k" + : : "m" ((phys&0xFF000000)|0x8777)); + /* Now jump to physical address so we can disable MMU */ + __asm__ __volatile__( + ".chip 68030\n\t" + "lea %/pc@(1f),%/a0\n\t" + "addl %0,%/a0\n\t"/* fixup target address and stack ptr */ + "addl %0,%/sp\n\t" + "pflusha\n\t" + "jmp %/a0@\n\t" /* jump into physical memory */ + "0:.long 0\n\t" /* a constant zero. */ + /* OK. Now reset everything and jump to reset vector. */ + "1:\n\t" + "lea %/pc@(0b),%/a0\n\t" + "pmove %/a0@, %/tc\n\t" /* disable mmu */ + "pmove %/a0@, %/tt0\n\t" /* disable tt0 */ + "pmove %/a0@, %/tt1\n\t" /* disable tt1 */ + "movel #0, %/a0\n\t" + "movec %/a0, %/vbr\n\t" /* clear vector base register */ + "movec %/a0, %/cacr\n\t" /* disable caches */ + "movel #0x0808,%/a0\n\t" + "movec %/a0, %/cacr\n\t" /* flush i&d caches */ + "movew #0x2700,%/sr\n\t" /* set up status register */ + "movel %1@(0x0),%/a0\n\t"/* load interrupt stack pointer */ + "movec %/a0, %/isp\n\t" + "movel %1@(0x4),%/a0\n\t" /* load reset vector */ + "reset\n\t" /* reset external devices */ + "jmp %/a0@\n\t" /* jump to the reset vector */ + ".chip 68k" + : : "r" (offset), "a" (rombase) : "a0"); + + /* should never get here */ + sti(); /* sure, why not */ + printk ("030 Restart failed. Please restart manually.\n"); + while(1); + } else { + /* We never make it here... The above shoule handle all cases. */ + printk ("Restart failed. Please restart manually.\n"); + + /* XXX - delay do we need to spin here ? */ + while(1); /* Just in case .. */ + } } /* diff --git a/arch/m68k/mac/debug.c b/arch/m68k/mac/debug.c index 5aa7ce6cf4b0..e6c8082f6d61 100644 --- a/arch/m68k/mac/debug.c +++ b/arch/m68k/mac/debug.c @@ -243,7 +243,7 @@ void mac_scca_console_write (struct console *co, const char *str, } } -#ifdef CONFIG_SERIAL_CONSOLE +#if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_SERIAL) int mac_sccb_console_wait_key(struct console *co) { int i; @@ -385,6 +385,16 @@ void mac_init_scc_port( int cflag, int port ) } #endif /* DEBUG_SERIAL */ +void mac_init_scca_port( int cflag ) +{ + mac_init_scc_port(cflag, 0); +} + +void mac_init_sccb_port( int cflag ) +{ + mac_init_scc_port(cflag, 1); +} + __initfunc(void mac_debug_init(void)) { #ifdef CONFIG_KGDB diff --git a/arch/m68k/mac/iop.c b/arch/m68k/mac/iop.c new file mode 100644 index 000000000000..f5bbf5613dab --- /dev/null +++ b/arch/m68k/mac/iop.c @@ -0,0 +1,720 @@ +/* + * I/O Processor (IOP) management + * Written and (C) 1999 by Joshua M. Thompson (funaho@jurai.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice and this list of conditions. + * 2. Redistributions in binary form must reproduce the above copyright + * notice and this list of conditions in the documentation and/or other + * materials provided with the distribution. + */ + +/* + * The IOP chips are used in the IIfx and some Quadras (900, 950) to manage + * serial and ADB. They are actually a 6502 processor and some glue logic. + * + * 990429 (jmt) - Initial implementation, just enough to knock the SCC IOP + * into compatible mode so nobody has to fiddle with the + * Serial Switch control panel anymore. + * 990603 (jmt) - Added code to grab the correct ISM IOP interrupt for OSS + * and non-OSS machines (at least I hope it's correct on a + * non-OSS machine -- someone with a Q900 or Q950 needs to + * check this.) + * 990605 (jmt) - Rearranged things a bit wrt IOP detection; iop_present is + * gone, IOP base addresses are now in an array and the + * globally-visible functions take an IOP number instead of an + * an actual base address. + * 990610 (jmt) - Finished the message passing framework and it seems to work. + * Sending _definately_ works; my adb-bus.c mods can send + * messages and receive the MSG_COMPLETED status back from the + * IOP. The trick now is figuring out the message formats. + * 990611 (jmt) - More cleanups. Fixed problem where unclaimed messages on a + * receive channel were never properly acknowledged. Bracketed + * the remaining debug printk's with #ifdef's and disabled + * debugging. I can now type on the console. + * 990612 (jmt) - Copyright notice added. Reworked the way replies are handled. + * It turns out that replies are placed back in the send buffer + * for that channel; messages on the receive channels are always + * unsolicited messages from the IOP (and our replies to them + * should go back in the receive channel.) Also added tracking + * of device names to the listener functions ala the interrupt + * handlers. + * 990729 (jmt) - Added passing of pt_regs structure to IOP handlers. This is + * used by the new unified ADB driver. + * + * TODO: + * + * o Something should be periodically checking iop_alive() to make sure the + * IOP hasn't died. + * o Some of the IOP manager routines need better error checking and + * return codes. Nothing major, just prettying up. + */ + +/* + * ----------------------- + * IOP Message Passing 101 + * ----------------------- + * + * The host talks to the IOPs using a rather simple message-passing scheme via + * a shared memory area in the IOP RAM. Each IOP has seven "channels"; each + * channel is conneced to a specific software driver on the IOP. For example + * on the SCC IOP there is one channel for each serial port. Each channel has + * an incoming and and outgoing message queue with a depth of one. + * + * A message is 32 bytes plus a state byte for the channel (MSG_IDLE, MSG_NEW, + * MSG_RCVD, MSG_COMPLETE). To send a message you copy the message into the + * buffer, set the state to MSG_NEW and signal the IOP by setting the IRQ flag + * in the IOP control to 1. The IOP will move the state to MSG_RCVD when it + * receives the message and then to MSG_COMPLETE when the message processing + * has completed. It is the host's responsibility at that point to read the + * reply back out of the send channel buffer and reset the channel state back + * to MSG_IDLE. + * + * To receive message from the IOP the same procedure is used except the roles + * are reversed. That is, the IOP puts message in the channel with a state of + * MSG_NEW, and the host receives the message and move its state to MSG_RCVD + * and then to MSG_COMPLETE when processing is completed and the reply (if any) + * has been placed back in the receive channel. The IOP will then reset the + * channel state to MSG_IDLE. + * + * Two sets of host interrupts are provided, INT0 and INT1. Both appear on one + * interrupt level; they are distinguished by a pair of bits in the IOP status + * register. The IOP will raise INT0 when one or more messages in the send + * channels have gone to the MSG_COMPLETE state and it will raise INT1 when one + * or more messages on the receive channels have gone to the MSG_NEW state. + * + * Since each channel handles only one message we have to implement a small + * interrupt-driven queue on our end. Messages to e sent are placed on the + * queue for sending and contain a pointer to an optional callback function. + * The handler for a message is called when the message state goes to + * MSG_COMPLETE. + * + * For receiving message we maintain a list of handler functions to call when + * a message is received on that IOP/channel combination. The handlers are + * called much like an interrupt handler and are passed a copy of the message + * from the IOP. The message state will be in MSG_RCVD while the handler runs; + * it is the handler's responsibility to call iop_complete_message() when + * finished; this function moves the message state to MSG_COMPLETE and signals + * the IOP. This two-step process is provided to allow the handler to defer + * message processing to a bottom-half handler if the processing will take + * a signifigant amount of time (handlers are called at interrupt time so they + * should execute quickly.) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/*#define DEBUG_IOP*/ + +/* Set to nonezero if the IOPs are present. Set by iop_init() */ + +int iop_scc_present,iop_ism_present; + +#ifdef CONFIG_PROC_FS + +/* + * sneaky reuse of the PROC_MAC_VIA inode. It's not needed by via.c + * anymore so we'll use it to debut the IOPs. + */ + +int iop_get_proc_info(char *, char **, off_t, int, int); + +static struct proc_dir_entry proc_mac_iop = { + PROC_MAC_VIA, 7, "mac_iop", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_array_inode_operations, + &iop_get_proc_info +}; + +#endif /* CONFIG_PROC_FS */ + +/* structure for tracking channel listeners */ + +struct listener { + const char *devname; + void (*handler)(struct iop_msg *, struct pt_regs *); +}; + +/* + * IOP structures for the two IOPs + * + * The SCC IOP controls both serial ports (A and B) as its two functions. + * The ISM IOP controls the SWIM (floppy drive) and ADB. + */ + +static volatile struct mac_iop *iop_base[NUM_IOPS]; + +/* + * IOP message queues + */ + +static struct iop_msg iop_msg_pool[NUM_IOP_MSGS]; +static struct iop_msg *iop_send_queue[NUM_IOPS][NUM_IOP_CHAN]; +static struct listener iop_listeners[NUM_IOPS][NUM_IOP_CHAN]; + +void iop_ism_irq(int, void *, struct pt_regs *); + +extern void oss_irq_enable(int); + +/* + * Private access functions + */ + +static __inline__ void iop_loadaddr(volatile struct mac_iop *iop, __u16 addr) +{ + iop->ram_addr_lo = addr; + iop->ram_addr_hi = addr >> 8; +} + +static __inline__ __u8 iop_readb(volatile struct mac_iop *iop, __u16 addr) +{ + iop->ram_addr_lo = addr; + iop->ram_addr_hi = addr >> 8; + return iop->ram_data; +} + +static __inline__ void iop_writeb(volatile struct mac_iop *iop, __u16 addr, __u8 data) +{ + iop->ram_addr_lo = addr; + iop->ram_addr_hi = addr >> 8; + iop->ram_data = data; +} + +static __inline__ void iop_stop(volatile struct mac_iop *iop) +{ + iop->status_ctrl &= ~IOP_RUN; +} + +static __inline__ void iop_start(volatile struct mac_iop *iop) +{ + iop->status_ctrl = IOP_RUN | IOP_AUTOINC; +} + +static __inline__ void iop_bypass(volatile struct mac_iop *iop) +{ + iop->status_ctrl |= IOP_BYPASS; +} + +static __inline__ void iop_interrupt(volatile struct mac_iop *iop) +{ + iop->status_ctrl |= IOP_IRQ; +} + +static int iop_alive(volatile struct mac_iop *iop) +{ + int retval; + + retval = (iop_readb(iop, IOP_ADDR_ALIVE) == 0xFF); + iop_writeb(iop, IOP_ADDR_ALIVE, 0); + return retval; +} + +static struct iop_msg *iop_alloc_msg(void) +{ + int i; + ulong cpu_flags; + + save_flags(cpu_flags); + cli(); + + for (i = 0 ; i < NUM_IOP_MSGS ; i++) { + if (iop_msg_pool[i].status == IOP_MSGSTATUS_UNUSED) { + iop_msg_pool[i].status = IOP_MSGSTATUS_WAITING; + restore_flags(cpu_flags); + return &iop_msg_pool[i]; + } + } + + restore_flags(cpu_flags); + return NULL; +} + +static void iop_free_msg(struct iop_msg *msg) +{ + msg->status = IOP_MSGSTATUS_UNUSED; +} + +/* + * Initialize the IOPs, if present. + */ + +__initfunc(void iop_init(void)) +{ + int i; + long tmp = 0; + volatile long *iop_bogon = &tmp; + + if (macintosh_config->scc_type == MAC_SCC_IOP) { + if (macintosh_config->ident == MAC_MODEL_IIFX) { + iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_IIFX; + } else { + iop_base[IOP_NUM_SCC] = (struct mac_iop *) SCC_IOP_BASE_QUADRA; + } + + iop_scc_present = 1; + + printk("IOP: detected SCC IOP at %p\n", iop_base[IOP_NUM_SCC]); + iop_base[IOP_NUM_SCC]->status_ctrl = 0; + iop_bypass(iop_base[IOP_NUM_SCC]); + } else { + iop_base[IOP_NUM_SCC] = NULL; + iop_scc_present = 0; + } + if (macintosh_config->adb_type == MAC_ADB_IOP) { + if (macintosh_config->ident == MAC_MODEL_IIFX) { + iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_IIFX; + } else { + iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_QUADRA; + } + iop_ism_present = 1; + + printk("IOP: detected ISM IOP at %p\n", iop_base[IOP_NUM_ISM]); + iop_base[IOP_NUM_SCC]->status_ctrl = 0; + for (i = 0 ; i < 16; i++) { + *iop_bogon = *iop_bogon; + } + iop_start(iop_base[IOP_NUM_ISM]); + iop_alive(iop_base[IOP_NUM_ISM]); /* clears the alive flag */ + } else { + iop_base[IOP_NUM_ISM] = NULL; + iop_ism_present = 0; + } + + /* Make the whole pool available and empty the queues */ + + for (i = 0 ; i < NUM_IOP_MSGS ; i++) { + iop_msg_pool[i].status = IOP_MSGSTATUS_UNUSED; + } + + for (i = 0 ; i < NUM_IOP_CHAN ; i++) { + iop_send_queue[IOP_NUM_SCC][i] = 0; + iop_send_queue[IOP_NUM_ISM][i] = 0; + iop_listeners[IOP_NUM_SCC][i].devname = NULL; + iop_listeners[IOP_NUM_SCC][i].handler = NULL; + iop_listeners[IOP_NUM_ISM][i].devname = NULL; + iop_listeners[IOP_NUM_ISM][i].handler = NULL; + } + +#ifdef CONFIG_PROC_FS + proc_register(&proc_root, &proc_mac_iop); +#endif +} + +/* + * Register the interrupt handler for the IOPs. + * TODO: might be wrong for non-OSS machines. Anyone? + */ + +__initfunc(void iop_register_interrupts(void)) +{ + if (iop_ism_present) { + if (oss_present) { + sys_request_irq(OSS_IRQLEV_IOPISM, iop_ism_irq, + IRQ_FLG_LOCK, "ISM IOP", + (void *) IOP_NUM_ISM); + oss_irq_enable(IRQ_MAC_ADB); + } else { + request_irq(IRQ_VIA2_0, iop_ism_irq, + IRQ_FLG_LOCK|IRQ_FLG_FAST, "ISM IOP", + (void *) IOP_NUM_ISM); + } + if (!iop_alive(iop_base[IOP_NUM_ISM])) { + printk("IOP: oh my god, they killed the ISM IOP!\n"); + } else { + printk("IOP: the ISM IOP seems to be alive.\n"); + } + } +} + +/* + * Register or unregister a listener for a specific IOP and channel + * + * If the handler pointer is NULL the current listener (if any) is + * unregistered. Otherwise the new listener is registered provided + * there is no existing listener registered. + */ + +int iop_listen(uint iop_num, uint chan, + void (*handler)(struct iop_msg *, struct pt_regs *), + const char *devname) +{ + if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL; + if (chan >= NUM_IOP_CHAN) return -EINVAL; + if (iop_listeners[iop_num][chan].handler && handler) return -EINVAL; + iop_listeners[iop_num][chan].devname = devname; + iop_listeners[iop_num][chan].handler = handler; + return 0; +} + +/* + * Complete reception of a message, which just means copying the reply + * into the buffer, setting the channel state to MSG_COMPLETE and + * notifying the IOP. + */ + +void iop_complete_message(struct iop_msg *msg) +{ + int iop_num = msg->iop_num; + int chan = msg->channel; + int i,offset; + +#ifdef DEBUG_IOP + printk("iop_complete(%p): iop %d chan %d\n", msg, msg->iop_num, msg->channel); +#endif + + offset = IOP_ADDR_RECV_MSG + (msg->channel * IOP_MSG_LEN); + + for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { + iop_writeb(iop_base[iop_num], offset, msg->reply[i]); + } + + iop_writeb(iop_base[iop_num], + IOP_ADDR_RECV_STATE + chan, IOP_MSG_COMPLETE); + iop_interrupt(iop_base[msg->iop_num]); + + iop_free_msg(msg); +} + +/* + * Actually put a message into a send channel buffer + */ + +static void iop_do_send(struct iop_msg *msg) +{ + volatile struct mac_iop *iop = iop_base[msg->iop_num]; + int i,offset; + + offset = IOP_ADDR_SEND_MSG + (msg->channel * IOP_MSG_LEN); + + for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { + iop_writeb(iop, offset, msg->message[i]); + } + + iop_writeb(iop, IOP_ADDR_SEND_STATE + msg->channel, IOP_MSG_NEW); + + iop_interrupt(iop); +} + +/* + * Handle sending a message on a channel that + * has gone into the IOP_MSG_COMPLETE state. + */ + +static void iop_handle_send(uint iop_num, uint chan, struct pt_regs *regs) +{ + volatile struct mac_iop *iop = iop_base[iop_num]; + struct iop_msg *msg,*msg2; + int i,offset; + +#ifdef DEBUG_IOP + printk("iop_handle_send: iop %d channel %d\n", iop_num, chan); +#endif + + iop_writeb(iop, IOP_ADDR_SEND_STATE + chan, IOP_MSG_IDLE); + + if (!(msg = iop_send_queue[iop_num][chan])) return; + + msg->status = IOP_MSGSTATUS_COMPLETE; + offset = IOP_ADDR_SEND_MSG + (chan * IOP_MSG_LEN); + for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { + msg->reply[i] = iop_readb(iop, offset); + } + if (msg->handler) (*msg->handler)(msg, regs); + msg2 = msg; + msg = msg->next; + iop_free_msg(msg2); + + iop_send_queue[iop_num][chan] = msg; + if (msg) iop_do_send(msg); +} + +/* + * Handle reception of a message on a channel that has + * gone into the IOP_MSG_NEW state. + */ + +static void iop_handle_recv(uint iop_num, uint chan, struct pt_regs *regs) +{ + volatile struct mac_iop *iop = iop_base[iop_num]; + int i,offset; + struct iop_msg *msg; + +#ifdef DEBUG_IOP + printk("iop_handle_recv: iop %d channel %d\n", iop_num, chan); +#endif + + msg = iop_alloc_msg(); + msg->iop_num = iop_num; + msg->channel = chan; + msg->status = IOP_MSGSTATUS_UNSOL; + msg->handler = iop_listeners[iop_num][chan].handler; + + offset = IOP_ADDR_RECV_MSG + (chan * IOP_MSG_LEN); + + for (i = 0 ; i < IOP_MSG_LEN ; i++, offset++) { + msg->message[i] = iop_readb(iop, offset); + } + + iop_writeb(iop, IOP_ADDR_RECV_STATE + chan, IOP_MSG_RCVD); + + /* If there is a listener, call it now. Otherwise complete */ + /* the message ourselves to avoid possible stalls. */ + + if (msg->handler) { + (*msg->handler)(msg, regs); + } else { +#ifdef DEBUG_IOP + printk("iop_handle_recv: unclaimed message on iop %d channel %d\n", iop_num, chan); + printk("iop_handle_recv:"); + for (i = 0 ; i < IOP_MSG_LEN ; i++) { + printk(" %02X", (uint) msg->message[i]); + } + printk("\n"); +#endif + iop_complete_message(msg); + } +} + +/* + * Send a message + * + * The message is placed at the end of the send queue. Afterwards if the + * channel is idle we force an immediate send of the next message in the + * queue. + */ + +int iop_send_message(uint iop_num, uint chan, void *privdata, + uint msg_len, __u8 *msg_data, + void (*handler)(struct iop_msg *, struct pt_regs *)) +{ + struct iop_msg *msg, *q; + + if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return -EINVAL; + if (chan >= NUM_IOP_CHAN) return -EINVAL; + if (msg_len > IOP_MSG_LEN) return -EINVAL; + + msg = iop_alloc_msg(); + if (!msg) return -ENOMEM; + + msg->next = NULL; + msg->status = IOP_MSGSTATUS_WAITING; + msg->iop_num = iop_num; + msg->channel = chan; + msg->caller_priv = privdata; + memcpy(msg->message, msg_data, msg_len); + msg->handler = handler; + + if (!(q = iop_send_queue[iop_num][chan])) { + iop_send_queue[iop_num][chan] = msg; + } else { + while (q->next) q = q->next; + q->next = msg; + } + + if (iop_readb(iop_base[iop_num], + IOP_ADDR_SEND_STATE + chan) == IOP_MSG_IDLE) { + iop_do_send(msg); + } + + return 0; +} + +/* + * Upload code to the shared RAM of an IOP. + */ + +void iop_upload_code(uint iop_num, __u8 *code_start, + uint code_len, __u16 shared_ram_start) +{ + if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return; + + iop_loadaddr(iop_base[iop_num], shared_ram_start); + + while (code_len--) { + iop_base[iop_num]->ram_data = *code_start++; + } +} + +/* + * Download code from the shared RAM of an IOP. + */ + +void iop_download_code(uint iop_num, __u8 *code_start, + uint code_len, __u16 shared_ram_start) +{ + if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return; + + iop_loadaddr(iop_base[iop_num], shared_ram_start); + + while (code_len--) { + *code_start++ = iop_base[iop_num]->ram_data; + } +} + +/* + * Compare the code in the shared RAM of an IOP with a copy in system memory + * and return 0 on match or the first nonmatching system memory address on + * failure. + */ + +__u8 *iop_compare_code(uint iop_num, __u8 *code_start, + uint code_len, __u16 shared_ram_start) +{ + if ((iop_num >= NUM_IOPS) || !iop_base[iop_num]) return code_start; + + iop_loadaddr(iop_base[iop_num], shared_ram_start); + + while (code_len--) { + if (*code_start != iop_base[iop_num]->ram_data) { + return code_start; + } + code_start++; + } + return (__u8 *) 0; +} + +/* + * Handle an ISM IOP interrupt + */ + +void iop_ism_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + uint iop_num = (uint) dev_id; + volatile struct mac_iop *iop = iop_base[iop_num]; + int i,state; + +#ifdef DEBUG_IOP + printk("iop_ism_irq: status = %02X\n", (uint) iop->status_ctrl); +#endif + + /* INT0 indicates a state change on an outgoing message channel */ + + if (iop->status_ctrl & IOP_INT0) { + iop->status_ctrl = IOP_INT0 | IOP_RUN | IOP_AUTOINC; +#ifdef DEBUG_IOP + printk("iop_ism_irq: new status = %02X, send states", + (uint) iop->status_ctrl); +#endif + for (i = 0 ; i < NUM_IOP_CHAN ; i++) { + state = iop_readb(iop, IOP_ADDR_SEND_STATE + i); +#ifdef DEBUG_IOP + printk(" %02X", state); +#endif + if (state == IOP_MSG_COMPLETE) { + iop_handle_send(iop_num, i, regs); + } + } +#ifdef DEBUG_IOP + printk("\n"); +#endif + } + + if (iop->status_ctrl & IOP_INT1) { /* INT1 for incoming msgs */ + iop->status_ctrl = IOP_INT1 | IOP_RUN | IOP_AUTOINC; +#ifdef DEBUG_IOP + printk("iop_ism_irq: new status = %02X, recv states", + (uint) iop->status_ctrl); +#endif + for (i = 0 ; i < NUM_IOP_CHAN ; i++) { + state = iop_readb(iop, IOP_ADDR_RECV_STATE + i); +#ifdef DEBUG_IOP + printk(" %02X", state); +#endif + if (state == IOP_MSG_NEW) { + iop_handle_recv(iop_num, i, regs); + } + } +#ifdef DEBUG_IOP + printk("\n"); +#endif + } + +} + +#ifdef CONFIG_PROC_FS + +char *iop_chan_state(int state) +{ + switch(state) { + case IOP_MSG_IDLE : return "idle "; + case IOP_MSG_NEW : return "new "; + case IOP_MSG_RCVD : return "received "; + case IOP_MSG_COMPLETE : return "completed "; + default : return "unknown "; + } +} + +int iop_dump_one_iop(char *buf, int iop_num, char *iop_name) +{ + int i,len = 0; + volatile struct mac_iop *iop = iop_base[iop_num]; + + len += sprintf(buf+len, "%s IOP channel states:\n\n", iop_name); + len += sprintf(buf+len, "## send_state recv_state device\n"); + len += sprintf(buf+len, "------------------------------------------------\n"); + for (i = 0 ; i < NUM_IOP_CHAN ; i++) { + len += sprintf(buf+len, "%2d %10s %10s %s\n", i, + iop_chan_state(iop_readb(iop, IOP_ADDR_SEND_STATE+i)), + iop_chan_state(iop_readb(iop, IOP_ADDR_RECV_STATE+i)), + iop_listeners[iop_num][i].handler? + iop_listeners[iop_num][i].devname : ""); + + } + len += sprintf(buf+len, "\n"); + return len; +} + +int iop_get_proc_info(char *buf, char **start, off_t pos, int count, int wr) +{ + int len, cnt; + + cnt = 0; + len = sprintf(buf, "IOPs detected:\n\n"); + + if (iop_scc_present) { + len += sprintf(buf+len, "SCC IOP (%p): status %02X\n", + iop_base[IOP_NUM_SCC], + (uint) iop_base[IOP_NUM_SCC]->status_ctrl); + } + if (iop_ism_present) { + len += sprintf(buf+len, "ISM IOP (%p): status %02X\n\n", + iop_base[IOP_NUM_ISM], + (uint) iop_base[IOP_NUM_ISM]->status_ctrl); + } + + if (iop_scc_present) { + len += iop_dump_one_iop(buf+len, IOP_NUM_SCC, "SCC"); + + } + + if (iop_ism_present) { + len += iop_dump_one_iop(buf+len, IOP_NUM_ISM, "ISM"); + + } + + if (len >= pos) { + if (!*start) { + *start = buf + pos; + cnt = len - pos; + } else { + cnt += len; + } + } + return (count > cnt) ? cnt : count; +} +#endif /* CONFIG_PROC_FS */ diff --git a/arch/m68k/mac/mac_ksyms.c b/arch/m68k/mac/mac_ksyms.c index a558d16844fa..3c3eac75b263 100644 --- a/arch/m68k/mac/mac_ksyms.c +++ b/arch/m68k/mac/mac_ksyms.c @@ -1,7 +1,14 @@ #include #include #include + /* Hook for mouse driver */ extern void (*adb_mouse_interrupt_hook) (char *); +/* Says whether we're using A/UX interrupts or not */ +extern int via_alt_mapping; + +#ifndef CONFIG_ADB_NEW EXPORT_SYMBOL(adb_mouse_interrupt_hook); +#endif +EXPORT_SYMBOL(via_alt_mapping); diff --git a/arch/m68k/mac/mac_penguin.S b/arch/m68k/mac/mac_penguin.S new file mode 100644 index 000000000000..b3ce30b6071d --- /dev/null +++ b/arch/m68k/mac/mac_penguin.S @@ -0,0 +1,75 @@ +.byte \ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0xFF,0x0F,0xF0,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0x00,0xFF,0xFF,0x0F,0xFF,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0x0F,0xFF,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x0F,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x0F,0xF0,0x00,0x00,0xFF,0xF0,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x0F,0xF0,0xFF,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0xF0,0x00,0x00,\ +0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0xF0,0x00,0x00,\ +0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,\ +0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,\ +0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,\ +0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,\ +0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,\ +0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,\ +0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\ +0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,\ +0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,\ +0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,\ +0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,\ +0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,\ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00 diff --git a/arch/m68k/mac/macboing.c b/arch/m68k/mac/macboing.c index 1731c929c2ad..557bcba2752f 100644 --- a/arch/m68k/mac/macboing.c +++ b/arch/m68k/mac/macboing.c @@ -91,14 +91,8 @@ static void mac_init_asc( void ) mac_special_bell = mac_quadra_start_bell; mac_asc_samplespersec = 22150; break; - case MAC_MODEL_Q650: - case MAC_MODEL_Q700: - case MAC_MODEL_Q800: - case MAC_MODEL_Q900: - case MAC_MODEL_Q950: - /* - * Currently not implemented! - */ + case MAC_MODEL_C660: + case MAC_MODEL_Q840: /* * The Quadra 660AV and 840AV use the "Singer" custom ASIC for sound I/O. * It appears to be similar to the "AWACS" custom ASIC in the Power Mac @@ -126,6 +120,22 @@ static void mac_init_asc( void ) */ mac_special_bell = mac_av_start_bell; break; + case MAC_MODEL_Q650: + case MAC_MODEL_Q700: + case MAC_MODEL_Q800: + case MAC_MODEL_Q900: + case MAC_MODEL_Q950: + /* + * Currently not implemented! + */ + mac_special_bell = NULL; + break; + default: + /* + * Every switch needs a default + */ + mac_special_bell = NULL; + break; } /* @@ -155,6 +165,12 @@ void mac_mksound( unsigned int freq, unsigned int length ) __u32 flags; int i; + if ( mac_special_bell == NULL ) + { + /* Do nothing */ + return; + } + if ( !mac_asc_inited ) mac_init_asc(); diff --git a/arch/m68k/mac/macints.c b/arch/m68k/mac/macints.c index 87e6692d41ba..edc7a62dc45e 100644 --- a/arch/m68k/mac/macints.c +++ b/arch/m68k/mac/macints.c @@ -7,8 +7,8 @@ * interrupts with exception vectors 0x19-0x1f). The following interrupt levels * are used: * 1 - VIA1 - * - slot 0: one second interrupt - * - slot 1: VBlank + * - slot 0: one second interrupt (CA2) + * - slot 1: VBlank (CA1) * - slot 2: ADB data ready (SR full) * - slot 3: ADB data (CB2) * - slot 4: ADB clock (CB1) @@ -16,57 +16,103 @@ * - slot 6: timer 1 * - slot 7: status of IRQ; signals 'any enabled int.' * - * 2 - VIA2, RBV or OSS - * - slot 0: SCSI DRQ - * - slot 1: NUBUS IRQ - * - slot 3: SCSI IRQ + * 2 - VIA2 or RBV + * - slot 0: SCSI DRQ (CA2) + * - slot 1: NUBUS IRQ (CA1) need to read port A to find which + * - slot 2: /EXP IRQ (only on IIci) + * - slot 3: SCSI IRQ (CB2) + * - slot 4: ASC IRQ (CB1) + * - slot 5: timer 2 (not on IIci) + * - slot 6: timer 1 (not on IIci) + * - slot 7: status of IRQ; signals 'any enabled int.' + * + * 2 - OSS (IIfx only?) + * - slot 0: SCSI interrupt + * - slot 1: Sound interrupt + * + * Levels 3-6 vary by machine type. For VIA or RBV Macintohes: + * + * 3 - unused (?) + * + * 4 - SCC (slot number determined by reading RR3 on the SSC itself) + * - slot 0: SCC channel A + * - slot 1: SCC channel B + * + * 5 - unused (?) + * [serial errors or special conditions seem to raise level 6 + * interrupts on some models (LC4xx?)] + * + * 6 - off switch (?) * - * 4 - SCC - * - subdivided into Channel B and Channel A interrupts + * For OSS Macintoshes (IIfx only at this point): * - * 6 - Off switch (??) + * 3 - Nubus interrupt + * - slot 0: Slot $9 + * - slot 1: Slot $A + * - slot 2: Slot $B + * - slot 3: Slot $C + * - slot 4: Slot $D + * - slot 5: Slot $E * - * 7 - Debug output + * 4 - SCC IOP + * - slot 0: SCC channel A + * - slot 1: SCC channel B * - * AV Macs only, handled by PSC: + * 5 - ISM IOP (ADB?) * - * 3 - MACE ethernet IRQ (DMA complete on level 4) + * 6 - unused * - * 5 - DSP ?? + * For PSC Macintoshes (660AV, 840AV): * - * Using the autovector irq numbers for Linux/m68k hardware interrupts without - * the IRQ_MACHSPEC bit set would interfere with the general m68k interrupt - * handling in kernel versions 2.0.x, so the following strategy is used: + * 3 - PSC level 3 + * - slot 0: MACE * - * - mac_init_IRQ installs the low-level entry points for the via1 and via2 - * exception vectors and the corresponding handlers (C functions); these - * entry points just add the machspec bit and call the handlers proper. - * (in principle, the C functions can be installed as the exception vectors - * directly, as they are hardcoded anyway; that's the current method). + * 4 - PSC level 4 + * - slot 1: SCC channel A interrupt + * - slot 2: SCC channel B interrupt + * - slot 3: MACE DMA * - * - via[12]_irq determine what interrupt sources have triggered the interrupt, - * and call the corresponding device interrupt handlers. - * (currently, via1_irq and via2_irq just call via_irq, passing the via base - * address. RBV interrupts are handled by (you guessed it) rbv_irq). - * Some interrupt functions want to have the interrupt number passed, so - * via_irq and rbv_irq need to generate the 'fake' numbers from scratch. + * 5 - PSC level 5 * - * - for the request/free/enable/disable business, interrupt sources are - * numbered internally (suggestion: keep irq 0-7 unused :-). One bit in the - * irq number specifies the via# to use, i.e. via1 interrupts are 8-16, - * via2 interrupts 17-32, rbv interrupts ... - * The device interrupt table and the irq_enable bitmap is maintained by - * the machspec interrupt code; all device drivers should only use these - * functions ! + * 6 - PSC level 6 * - * - For future porting to version 2.1 (and removing of the machspec bit) it - * should be sufficient to use the same numbers (everything > 7 is assumed - * to be machspec, according to Jes!). + * Finally we have good 'ole level 7, the non-maskable interrupt: + * + * 7 - NMI (programmer's switch on the back of some Macs) + * Also RAM parity error on models which support it (IIc, IIfx?) + * + * The current interrupt logic looks something like this: + * + * - We install dispatchers for the autovector interrupts (1-7). These + * dispatchers are responsible for querying the hardware (the + * VIA/RBV/OSS/PSC chips) to determine the actual interrupt source. Using + * this information a machspec interrupt number is generated by placing the + * index of the interrupt hardware into the low three bits and the original + * autovector interrupt number in the upper 5 bits. The handlers for the + * resulting machspec interrupt are then called. + * + * - Nubus is a special case because its interrupts are hidden behind two + * layers of hardware. Nubus interrupts come in as index 1 on VIA #2, + * which translates to IRQ number 17. In this spot we install _another_ + * dispatcher. This dispatcher finds the interrupting slot number (9-F) and + * then forms a new machspec interrupt number as above with the slot number + * minus 9 in the low three bits and the pseudo-level 7 in the upper five + * bits. The handlers for this new machspec interrupt number are then + * called. This puts Nubus interrupts into the range 56-62. + * + * - We support "fast" and "slow" handlers, just like the Amiga port. The + * fast handlers are called first and with all interrupts disabled. They + * are expected to execute quickly (hence the name). The slow handlers are + * called last with interrupts enabled and the interrupt level restored. + * They must therefore be reentrant. + * + * - Drivers should never try to request autovector interrupt numbers. It + * won't work. * * TODO: - * - integrate Nubus interrupts in request/free_irq * - * - + * o Perhaps build some intelligence into mac_SCC_handler(); we could check + * the SCC ourselves and only call the handler for the appopriate channel. */ #include @@ -82,124 +128,61 @@ #include #include #include -#include "via6522.h" +#include +#include #include /* - * Interrupt handler and parameter types - */ -struct irqhandler { - void (*handler)(int, void *, struct pt_regs *); - void *dev_id; -}; - -struct irqparam { - unsigned long flags; - const char *devname; -}; - -struct irqflags { - unsigned int disabled; - unsigned int pending; -}; - -/* - * Array with irq's and their parameter data. - */ -static struct irqhandler via1_handler[8]; -static struct irqhandler via2_handler[8]; -static struct irqhandler rbv_handler[8]; -static struct irqhandler psc3_handler[8]; -static struct irqhandler scc_handler[8]; -static struct irqhandler psc5_handler[8]; -static struct irqhandler psc6_handler[8]; -static struct irqhandler nubus_handler[8]; - -static struct irqhandler *handler_table[8]; - -/* - * This array hold the rest of parameters of int handlers: type - * (slow,fast,prio) and the name of the handler. These values are only - * accessed from C + * The mac_irq_list array is an array of linked lists of irq_node_t nodes. + * Each node contains one handler to be called whenever the interrupt + * occurs, with fast handlers listed before slow handlers. */ -static struct irqparam via1_param[8]; -static struct irqparam via2_param[8]; -static struct irqparam rbv_param[8]; -static struct irqparam psc3_param[8]; -static struct irqparam scc_param[8]; -static struct irqparam psc5_param[8]; -static struct irqparam psc6_param[8]; -static struct irqparam nubus_param[8]; -static struct irqparam *param_table[8]; +irq_node_t *mac_irq_list[NUM_MAC_SOURCES]; /* - * This array holds the 'disabled' and 'pending' software flags maintained - * by mac_{enable,disable}_irq and the generic via_irq function. + * VIA/RBV hooks */ -static struct irqflags irq_flags[8]; +extern void via_init(void); +extern void via_register_interrupts(void); +extern void via_irq_enable(int); +extern void via_irq_disable(int); +extern void via_irq_clear(int); +extern int via_irq_pending(int); /* - * This array holds the pointers to the various VIA or other interrupt - * controllers, indexed by interrupt level + * OSS hooks */ -static volatile unsigned char *via_table[8]; - -/* - * Arrays with irq statistics - */ -static unsigned long via1_irqs[8]; -static unsigned long via2_irqs[8]; -static unsigned long rbv_irqs[8]; -static unsigned long psc3_irqs[8]; -static unsigned long scc_irqs[8]; -static unsigned long psc5_irqs[8]; -static unsigned long psc6_irqs[8]; -static unsigned long nubus_irqs[8]; - -static unsigned long *mac_irqs[8]; - -/* - * Some special nutcases ... - */ +extern int oss_present; -static unsigned long mac_ide_irqs = 0; -static unsigned long nubus_stuck_events = 0; +extern void oss_init(void); +extern void oss_register_interrupts(void); +extern void oss_irq_enable(int); +extern void oss_irq_disable(int); +extern void oss_irq_clear(int); +extern int oss_irq_pending(int); /* - * VIA/RBV/OSS/PSC register base pointers + * PSC hooks */ -volatile unsigned char *via2_regp=(volatile unsigned char *)VIA2_BAS; -volatile unsigned char *rbv_regp=(volatile unsigned char *)VIA2_BAS_IIci; -volatile unsigned char *oss_regp=(volatile unsigned char *)OSS_BAS; -volatile unsigned char *psc_regp=(volatile unsigned char *)PSC_BAS; - -/* - * Flags to control via2 / rbv behaviour - */ - -static int via2_is_rbv = 0; -static int via2_is_oss = 0; -static int rbv_clear = 0; - -/* fake VIA2 to OSS bit mapping */ -static int oss_map[8] = {2, 7, 0, 1, 3, 4, 5}; - -void oss_irq(int irq, void *dev_id, struct pt_regs *regs); -static void oss_do_nubus(int irq, void *dev_id, struct pt_regs *regs); +extern int psc_present; -/* PSC ints */ -void psc_irq(int irq, void *dev_id, struct pt_regs *regs); +extern void psc_init(void); +extern void psc_register_interrupts(void); +extern void psc_irq_enable(int); +extern void psc_irq_disable(int); +extern void psc_irq_clear(int); +extern int psc_irq_pending(int); /* - * PSC hooks + * IOP hooks */ -extern void psc_init(void); +extern void iop_register_interrupts(void); /* * console_loglevel determines NMI handler function @@ -207,640 +190,401 @@ extern void psc_init(void); extern int console_loglevel; -/* - * ADB test hooks - */ -extern int in_keybinit; -void adb_queue_poll(void); - -/* Defined in entry.S; only increments 'num_spurious' */ -asmlinkage void bad_interrupt(void); - -void nubus_wtf(int slot, void *via, struct pt_regs *regs); +extern void mac_bang(int, void *, struct pt_regs *); -void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *regs); -void mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs); - -static void via_do_nubus(int slot, void *via, struct pt_regs *regs); +void mac_nmi_handler(int, void *, struct pt_regs *); +void mac_debug_handler(int, void *, struct pt_regs *); +void mac_SCC_handler(int, void *, struct pt_regs *); /* #define DEBUG_MACINTS */ -#define DEBUG_SPURIOUS -#define DEBUG_NUBUS_SPURIOUS -#define DEBUG_NUBUS_INT - -/* #define DEBUG_VIA */ -#define DEBUG_VIA_NUBUS - void mac_init_IRQ(void) { int i; #ifdef DEBUG_MACINTS - printk("Mac interrupt stuff initializing ...\n"); + printk("mac_init_IRQ(): Setting things up...\n"); #endif + /* Initialize the IRQ handler lists. Initially each list is empty, */ - via2_regp = (unsigned char *)VIA2_BAS; - rbv_regp = (unsigned char *)VIA2_BAS_IIci; - - /* initialize the hardwired (primary, autovector) IRQs */ - - /* level 1 IRQ: VIA1, always present */ - sys_request_irq(1, via1_irq, IRQ_FLG_LOCK, "via1", via1_irq); - - /* via2 or rbv?? */ - if (macintosh_config->via_type == MAC_VIA_IIci) { - /* - * A word of caution: the definitions here only affect interrupt - * handling, see via6522.c for yet another file to change - * base addresses and RBV flags - */ - - /* yes, this is messy - the IIfx deserves a class of his own */ - if (macintosh_config->ident == MAC_MODEL_IIFX) { - /* no real VIA2, the OSS seems _very_ different */ - via2_is_oss = 1; - /* IIfx has OSS, at a different base address than RBV */ - rbv_regp = (unsigned char *) OSS_BAS; - sys_request_irq(2, oss_irq, IRQ_FLG_LOCK, "oss", oss_irq); - } else { - /* VIA2 is part of the RBV: different base, other offsets */ - via2_is_rbv = 1; - - /* LC III weirdness: IFR seems to behave like VIA2 */ - /* FIXME: maybe also for LC II ?? */ - if (macintosh_config->ident == MAC_MODEL_LCIII) { - rbv_clear = 0x0; - } else { - rbv_clear = 0x80; - } - /* level 2 IRQ: RBV/OSS; we only care about RBV for now */ - sys_request_irq(2, rbv_irq, IRQ_FLG_LOCK, "rbv", rbv_irq); - } - } else - /* level 2 IRQ: VIA2 */ - sys_request_irq(2, via2_irq, IRQ_FLG_LOCK, "via2", via2_irq); + for (i = 0; i < NUM_MAC_SOURCES; i++) { + mac_irq_list[i] = NULL; + } /* - * level 4 IRQ: SCC - use 'master' interrupt routine that calls the - * registered channel-specific interrupts in turn. - * Currently, one interrupt per channel is used, solely - * to pass the correct async_info as parameter! + * Now register the handlers for the the master IRQ handlers + * at levels 1-7. Most of the work is done elsewhere. */ - sys_request_irq(4, mac_debug_handler, IRQ_FLG_STD, "INT4", mac_debug_handler); - - /* level 6 */ - sys_request_irq(6, mac_bang, IRQ_FLG_LOCK, "offswitch", mac_bang); - - /* level 7 (or NMI) : debug stuff */ - sys_request_irq(7, mac_nmi_handler, IRQ_FLG_STD, "NMI", mac_nmi_handler); - - /* initialize the handler tables for VIAs */ - for (i = 0; i < 8; i++) { - via1_handler[i].handler = mac_default_handler; - via1_handler[i].dev_id = NULL; - via1_param[i].flags = IRQ_FLG_STD; - via1_param[i].devname = NULL; - - via2_handler[i].handler = mac_default_handler; - via2_handler[i].dev_id = NULL; - via2_param[i].flags = IRQ_FLG_STD; - via2_param[i].devname = NULL; - - rbv_handler[i].handler = mac_default_handler; - rbv_handler[i].dev_id = NULL; - rbv_param[i].flags = IRQ_FLG_STD; - rbv_param[i].devname = NULL; - - scc_handler[i].handler = mac_default_handler; - scc_handler[i].dev_id = NULL; - scc_param[i].flags = IRQ_FLG_STD; - scc_param[i].devname = NULL; - - /* NUBUS interrupts routed through VIA2 slot 2 - special */ - nubus_handler[i].handler = nubus_wtf; - nubus_handler[i].dev_id = NULL; - nubus_param[i].flags = IRQ_FLG_STD; - nubus_param[i].devname = NULL; - - } - - /* initialize the handler tables (level 1 -> via_handler[0] !!!) */ - via_table[0] = via1_regp; - handler_table[0] = &via1_handler[0]; - param_table[0] = &via1_param[0]; - mac_irqs[0] = &via1_irqs[0]; - - if (via2_is_rbv || via2_is_oss) { - via_table[1] = rbv_regp; - handler_table[1] = &rbv_handler[0]; - param_table[1] = &rbv_param[0]; - mac_irqs[1] = &rbv_irqs[0]; + if (oss_present) { + oss_register_interrupts(); } else { - via_table[1] = via2_regp; - handler_table[1] = &via2_handler[0]; - param_table[1] = &via2_param[0]; - mac_irqs[1] = &via2_irqs[0]; - } - via_table[2] = NULL; - via_table[3] = NULL; - - handler_table[2] = &rbv_handler[0]; - handler_table[3] = &scc_handler[0]; - handler_table[4] = NULL; - handler_table[5] = NULL; - handler_table[6] = NULL; - handler_table[7] = &nubus_handler[0]; - - param_table[2] = &rbv_param[0]; - param_table[3] = &scc_param[0]; - param_table[7] = &nubus_param[0]; - - mac_irqs[2] = &rbv_irqs[0]; - mac_irqs[3] = &scc_irqs[0]; - mac_irqs[7] = &nubus_irqs[0]; - - /* - * Nubus Macs: turn off the Nubus dispatch interrupt for now - */ - - mac_turnoff_irq(IRQ_MAC_NUBUS); - - /* - * AV Macs: shutup the PSC ints - */ - if (macintosh_config->ident == MAC_MODEL_C660 - || macintosh_config->ident == MAC_MODEL_Q840) - { - psc_init(); - - handler_table[2] = &psc3_handler[0]; - /* handler_table[3] = &psc4_handler[0]; */ - handler_table[4] = &psc5_handler[0]; - handler_table[5] = &psc6_handler[0]; - - param_table[2] = &psc3_param[0]; - /* param_table[3] = &psc4_param[0]; */ - param_table[4] = &psc5_param[0]; - param_table[5] = &psc6_param[0]; - - mac_irqs[2] = &psc3_irqs[0]; - /* mac_irqs[3] = &psc4_irqs[0]; */ - mac_irqs[4] = &psc5_irqs[0]; - mac_irqs[5] = &psc6_irqs[0]; - - sys_request_irq(3, psc_irq, IRQ_FLG_STD, "PSC3", psc_irq); - sys_request_irq(4, psc_irq, IRQ_FLG_STD, "PSC4", psc_irq); - sys_request_irq(5, psc_irq, IRQ_FLG_STD, "PSC5", psc_irq); - sys_request_irq(6, psc_irq, IRQ_FLG_STD, "PSC6", psc_irq); + via_register_interrupts(); } - + if (psc_present) psc_register_interrupts(); + iop_register_interrupts(); + sys_request_irq(7, mac_nmi_handler, IRQ_FLG_LOCK, "NMI", mac_nmi_handler); #ifdef DEBUG_MACINTS - printk("Mac interrupt init done!\n"); + printk("mac_init_IRQ(): Done!\n"); #endif } /* - * We have no machine specific interrupts on a macintoy - * Yet, we need to register/unregister interrupts ... :-) - * Currently unimplemented: Test for valid irq number, chained irqs, - * Nubus interrupts (use nubus_request_irq!). + * Routines to work with irq_node_t's on linked lists lifted from + * the Amiga code written by Roman Zippel. */ - -int mac_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *), - unsigned long flags, const char *devname, void *dev_id) + +static inline void mac_insert_irq(irq_node_t **list, irq_node_t *node) { - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - struct irqhandler *via_handler; - struct irqparam *via_param; - volatile unsigned char *via; + unsigned long cpu_flags; + irq_node_t *cur; -#ifdef DEBUG_MACINTS - printk ("%s: IRQ %d on VIA%d[%d] requested from %s\n", - __FUNCTION__, irq, srcidx+1, irqidx, devname); -#endif + if (!node->dev_id) + printk("%s: Warning: dev_id of %s is zero\n", + __FUNCTION__, node->devname); - if (flags < IRQ_TYPE_SLOW || flags > IRQ_TYPE_PRIO) { - printk ("%s: Bad irq type %ld requested from %s\n", - __FUNCTION__, flags, devname); - return -EINVAL; - } + save_flags(cpu_flags); + cli(); - /* figure out what VIA is handling this irq */ - if (irq < IRQ_IDX(IRQ_VIA1_1) || irq >= IRQ_IDX(IRQ_NUBUS_1)) { - /* non-via irqs unimplemented */ - printk ("%s: Bad irq source %d on VIA %d requested from %s\n", - __FUNCTION__, irq, srcidx, devname); - return -EINVAL; - } - - /* figure out if SCC pseudo-irq (redundant ??) */ - if (irq >= IRQ_IDX(IRQ_SCC) && irq < IRQ_IDX(IRQ_PSC5_0)) { - /* set specific SCC handler */ - scc_handler[irqidx].handler = handler; - scc_handler[irqidx].dev_id = dev_id; - scc_param[irqidx].flags = flags; - scc_param[irqidx].devname = devname; - /* and done! */ - return 0; - } + cur = *list; - /* - * code below: only for VIA irqs currently - * add similar hack for Nubus pseudo-irq here - hide nubus_request_irq - */ - via = (volatile unsigned char *) via_table[srcidx]; - if (!via) - return -EINVAL; - - via_handler = handler_table[srcidx]; - via_param = param_table[srcidx]; - - /* check for conflicts or possible replacement */ - - /* set the handler - no chained irqs yet !! */ - via_handler[irqidx].handler = handler; - via_handler[irqidx].dev_id = dev_id; - via_param[irqidx].flags = flags; - via_param[irqidx].devname = devname; - - /* and turn it on ... careful, that's VIA only ... */ - if (srcidx == SRC_VIA2 && via2_is_rbv) - via_write(via, rIER, via_read(via, rIER)|0x80|(1<<(irqidx))); - else if (srcidx == SRC_VIA2 && via2_is_oss) - via_write(oss_regp, oss_map[irqidx]+8, 2); - else - via_write(via, vIER, via_read(via, vIER)|0x80|(1<<(irqidx))); - - - if (irq == IRQ_IDX(IRQ_MAC_SCSI)) { - /* - * Set vPCR for SCSI interrupts. (what about RBV here?) - * 980429 MS: RBV is ok, OSS seems to be different - */ - if (!via2_is_oss) - if (macintosh_config->scsi_type == MAC_SCSI_OLD) { - /* CB2 (IRQ) indep. interrupt input, positive edge */ - /* CA2 (DRQ) indep. interrupt input, positive edge */ - via_write(via, vPCR, 0x66); - } else { - /* CB2 (IRQ) indep. interrupt input, negative edge */ - /* CA2 (DRQ) indep. interrupt input, negative edge */ - via_write(via, vPCR, 0x22); - } -#if 0 - else - /* CB2 (IRQ) indep. interrupt input, negative edge */ - /* CA2 (DRQ) indep. interrupt input, negative edge */ - via_write(via, vPCR, 0x22); -#endif + if (node->flags & IRQ_FLG_FAST) { + node->flags &= ~IRQ_FLG_SLOW; + while (cur && cur->flags & IRQ_FLG_FAST) { + list = &cur->next; + cur = cur->next; + } + } else if (node->flags & IRQ_FLG_SLOW) { + while (cur) { + list = &cur->next; + cur = cur->next; + } + } else { + while (cur && !(cur->flags & IRQ_FLG_SLOW)) { + list = &cur->next; + cur = cur->next; + } } - return 0; -} - -void mac_free_irq (unsigned int irq, void *dev_id) -{ - unsigned long flags; - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - struct irqhandler *via_handler; - struct irqparam *via_param; - volatile unsigned char *via; + node->next = cur; + *list = node; -#ifdef DEBUG_MACINTS - printk ("%s: IRQ %d on VIA%d[%d] freed\n", - __FUNCTION__, irq, srcidx+1, irqidx); -#endif + restore_flags(cpu_flags); +} - /* figure out what VIA is handling this irq */ - if (irq < IRQ_IDX(IRQ_VIA1_1) || irq >= IRQ_IDX(IRQ_NUBUS_1)) { - /* non-via irqs unimplemented */ - return; - } +static inline void mac_delete_irq(irq_node_t **list, void *dev_id) +{ + unsigned long cpu_flags; + irq_node_t *node; - save_flags(flags); + save_flags(cpu_flags); cli(); - /* figure out if SCC pseudo-irq */ - if (irq >= IRQ_IDX(IRQ_SCC) && irq < IRQ_IDX(IRQ_PSC5_0)) { - /* clear specific SCC handler */ - scc_handler[irqidx].handler = mac_default_handler; - scc_handler[irqidx].dev_id = NULL; - scc_param[irqidx].flags = IRQ_FLG_STD; - scc_param[irqidx].devname = NULL; - /* and done! */ - restore_flags(flags); - return; - } - - via = (volatile unsigned char *) via_table[srcidx]; - via_handler = handler_table[srcidx]; - via_param = param_table[srcidx]; - - if ( !via || (via_handler[irqidx].dev_id != dev_id) ) { - restore_flags(flags); - goto not_found; + for (node = *list; node; list = &node->next, node = *list) { + if (node->dev_id == dev_id) { + *list = node->next; + /* Mark it as free. */ + node->handler = NULL; + restore_flags(cpu_flags); + return; + } } + restore_flags(cpu_flags); + printk ("%s: tried to remove invalid irq\n", __FUNCTION__); +} - /* clear the handler - no chained irqs yet !! */ - via_handler[irqidx].handler = mac_default_handler; - via_handler[irqidx].dev_id = NULL; - via_param[irqidx].flags = IRQ_FLG_STD; - via_param[irqidx].devname = NULL; +/* + * Call all the handlers for a given interrupt. Fast handlers are called + * first followed by slow handlers. + * + * This code taken from the original Amiga code written by Roman Zippel. + */ - /* and turn it off */ - if (srcidx == SRC_VIA2 && via2_is_rbv) - via_write(via, rIER, (via_read(via, rIER)&(1< 7)) { + printk("mac_do_irq_list: spurious interrupt %d!\n", irq); + } +#endif + /* serve first fast and normal handlers */ + for (node = mac_irq_list[irq]; + node && (!(node->flags & IRQ_FLG_SLOW)); + node = node->next) + node->handler(irq, node->dev_id, fp); + if (!node) return; + save_flags(cpu_flags); + restore_flags((cpu_flags & ~0x0700) | (fp->sr & 0x0700)); + /* if slow handlers exists, serve them now */ + slow_nodes = node; + for (; node; node = node->next) { + node->handler(irq, node->dev_id, fp); + } } /* - * {en,dis}able_irq have the usual semantics of temporary blocking the - * interrupt, but not loosing requests that happen between disabling and - * enabling. On Atari, this is done with the MFP mask registers. + * mac_enable_irq - enable an interrupt source + * mac_disable_irq - disable an interrupt source + * mac_clear_irq - clears a pending interrupt + * mac_pending_irq - Returns the pending status of an IRQ (nonzero = pending) * - * On the Mac, this isn't possible: there is no VIA mask register. - * Needs to be implemented in software, setting 'mask' bits in a separate - * struct for each interrupt controller. These mask bits need to be checked - * by the VIA interrupt routine which should ignore requests for masked IRQs - * (after possibly ack'ing them). - * - * On second thought: some of the IRQ sources _can_ be turned off via bits - * in the VIA output registers. Need to check this ... - * - * TBI: According to the VIA docs, clearing a bit in the IER has the effect of - * blocking generation of the interrupt, but the internal interrupt condition - * is preserved. So the IER might be used as mask register here, and turnon_irq - * would need to clear the interrupt bit in the IFR to prevent getting an - * interrupt at all. - * - * Implementation note: the irq no's here are the _machspec_ irqs, hence the - * hack with srcidx to figure out which VIA/RBV handles the interrupt. - * That's fundamentally different when it comes to the interrupt handlers - * proper: these get the interrupt level no. as argument, all information about - * which source triggered the int. is buried in the VIA IFR ... The int. level - * points us to the proper handler, so we could do a sanity check there ... + * These routines are just dispatchers to the VIA/OSS/PSC routines. */ void mac_enable_irq (unsigned int irq) { - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - - irq_flags[srcidx].disabled &= ~(1<>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); + switch(IRQ_SRC(irq)) { + case 1: via_irq_disable(irq); + break; + case 2: + case 7: + if (oss_present) { + oss_irq_disable(irq); + } else { + via_irq_disable(irq); + } + break; + case 3: + case 4: + case 5: + case 6: if (psc_present) { + psc_irq_disable(irq); + } else if (oss_present) { + oss_irq_disable(irq); + } + break; + } +} + +void mac_clear_irq( unsigned int irq ) +{ + switch(IRQ_SRC(irq)) { + case 1: via_irq_clear(irq); + break; + case 2: + case 7: if (oss_present) { + oss_irq_clear(irq); + } else { + via_irq_clear(irq); + } + break; + case 3: + case 4: + case 5: + case 6: if (psc_present) { + psc_irq_clear(irq); + } else if (oss_present) { + oss_irq_clear(irq); + } + break; + } +} - irq_flags[srcidx].disabled |= (1<>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - volatile unsigned char *via; + irq_node_t *node; - via = (volatile unsigned char *) via_table[srcidx]; - if (!via) - return; +#ifdef DEBUG_MACINTS + printk ("%s: irq %d requested for %s\n", __FUNCTION__, irq, devname); +#endif - if (srcidx == SRC_VIA2 && via2_is_rbv) /* RBV as VIA2 */ - via_write(via, rIER, via_read(via, rIER)|0x80|(1<<(irqidx))); - else if (srcidx == SRC_VIA2 && via2_is_oss) /* OSS */ - via_write(oss_regp, oss_map[irqidx]+8, 2); - else if (srcidx > SRC_VIA2) /* hope AVs have VIA2 */ - via_write(via, (0x104 + 0x10*srcidx), - via_read(via, (0x104 + 0x10*srcidx))|0x80|(1<<(irqidx))); - else /* VIA1+2 */ - via_write(via, vIER, via_read(via, vIER)|0x80|(1<<(irqidx))); + if (irq < VIA1_SOURCE_BASE) { + return sys_request_irq(irq, handler, flags, devname, dev_id); + } -} + if (irq >= NUM_MAC_SOURCES) { + printk ("%s: unknown irq %d requested by %s\n", + __FUNCTION__, irq, devname); + } -void mac_turnoff_irq( unsigned int irq ) -{ - int srcidx = ((irq & IRQ_SRC_MASK)>>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); - volatile unsigned char *via; + /* Get a node and stick it onto the right list */ - via = (volatile unsigned char *) via_table[srcidx]; - if (!via) - return; + if (!(node = new_irq_node())) return -ENOMEM; - if (srcidx == SRC_VIA2 && via2_is_rbv) /* RBV as VIA2 */ - via_write(via, rIER, (via_read(via, rIER)&(1< SRC_VIA2) - via_write(via, (0x104 + 0x10*srcidx), - via_read(via, (0x104 + 0x10*srcidx))|(1<<(irqidx))); - else /* VIA1+2 */ - via_write(via, vIER, (via_read(via, vIER)&(1<handler = handler; + node->flags = flags; + node->dev_id = dev_id; + node->devname = devname; + node->next = NULL; + mac_insert_irq(&mac_irq_list[irq], node); -/* - * These functions currently only handle the software-maintained irq pending - * list for disabled irqs - manipulating the actual irq flags in the via would - * require clearing single bits in the via, such as (probably) - * via_write(via, vIFR, (via_read(via, vIFR)&(1<>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); + mac_enable_irq(irq); - irq_flags[srcidx].pending &= ~(1<>3) - 1; - int irqidx = (irq & IRQ_IDX_MASK); + if (irq < VIA1_SOURCE_BASE) { + return sys_free_irq(irq, dev_id); + } - pending = irq_flags[srcidx].pending & (1<= NUM_MAC_SOURCES) { + printk ("%s: unknown irq %d freed\n", + __FUNCTION__, irq); + return; + } - via = (volatile unsigned char *) via_table[srcidx]; - if (!via) - return (pending); + mac_delete_irq(&mac_irq_list[irq], dev_id); - if (srcidx == SRC_VIA2 && via2_is_rbv) - pending |= via_read(via, rIFR)&(1< SRC_VIA2) - pending |= via_read(via, (0x100 + 0x10*srcidx))&(1<>3) - 1; - irqidx = (i & IRQ_IDX_MASK); - - /* - * Not present: skip - */ - - if (mac_irqs[srcidx] == NULL) - continue; - - /* - * never used by VIAs, unused by others so far, counts - * the magic 'nothing pending' cases ... - */ - if (irqidx == 7 && mac_irqs[srcidx][irqidx]) { - len += sprintf(buf+len, "Level %01d: %10lu (spurious) \n", - srcidx, - mac_irqs[srcidx][irqidx]); - continue; - } + irq_node_t *node; + char *base; - /* - * Nothing registered for this IPL: skip - */ - - if (handler_table[srcidx] == NULL) - continue; - - /* - * No handler installed: skip - */ - - if (handler_table[srcidx][irqidx].handler == mac_default_handler || - handler_table[srcidx][irqidx].handler == nubus_wtf) - continue; - - - if (i < VIA2_SOURCE_BASE) - len += sprintf(buf+len, "via1 %01d: %10lu ", - irqidx, - mac_irqs[srcidx][irqidx]); - else if (i < RBV_SOURCE_BASE) - len += sprintf(buf+len, "via2 %01d: %10lu ", - irqidx, - mac_irqs[srcidx][irqidx]); - else if (i < MAC_SCC_SOURCE_BASE) - len += sprintf(buf+len, "rbv %01d: %10lu ", - irqidx, - mac_irqs[srcidx][irqidx]); - else if (i < NUBUS_SOURCE_BASE) - len += sprintf(buf+len, "scc %01d: %10lu ", - irqidx, - mac_irqs[srcidx][irqidx]); - else /* Nubus */ - len += sprintf(buf+len, "nubus %01d: %10lu ", - irqidx, - mac_irqs[srcidx][irqidx]); - - len += sprintf(buf+len, "%s\n", - param_table[srcidx][irqidx].devname); + /* Don't do Nubus interrupts in this loop; we do them separately */ + /* below so that we can print slot numbers instead of IRQ numbers */ - } - if (num_spurious) - len += sprintf(buf+len, "spurio.: %10u\n", num_spurious); + for (i = VIA1_SOURCE_BASE ; i < NUM_MAC_SOURCES ; ++i) { - /* - * XXX Fixme: Nubus sources are never logged above ... - */ + /* Nonexistant interrupt or nothing registered; skip it. */ - len += sprintf(buf+len, "Nubus interrupts:\n"); + if ((node = mac_irq_list[i]) == NULL) continue; + if (node->flags & IRQ_FLG_STD) continue; - for (i = 0; i < 7; i++) { - if (nubus_handler[i].handler == nubus_wtf) - continue; - len += sprintf(buf+len, "nubus %01X: %10lu ", - i+9, - nubus_irqs[i]); - len += sprintf(buf+len, "%s\n", - nubus_param[i].devname); + base = ""; + switch(IRQ_SRC(i)) { + case 1: base = "via1"; + break; + case 2: if (oss_present) { + base = "oss"; + } else { + base = "via2"; + } + break; + case 3: + case 4: + case 5: + case 6: if (psc_present) { + base = "psc"; + } else if (oss_present) { + base = "oss"; + } else { + if (IRQ_SRC(i) == 4) base = "scc"; + } + break; + case 7: base = "nbus"; + break; + } + len += sprintf(buf+len, "%4s %2d: %10u ", + base, i, kstat.irqs[0][i]); + + do { + if (node->flags & IRQ_FLG_FAST) { + len += sprintf(buf+len, "F "); + } else if (node->flags & IRQ_FLG_SLOW) { + len += sprintf(buf+len, "S "); + } else { + len += sprintf(buf+len, " "); + } + len += sprintf(buf+len, "%s\n", node->devname); + if ((node = node->next)) { + len += sprintf(buf+len, " "); + } + } while(node); } - len += sprintf(buf+len, "nubus spurious ints: %10lu\n", - nubus_irqs[7]); - len += sprintf(buf+len, "nubus stuck events : %10lu\n", - nubus_stuck_events); -#ifdef CONFIG_BLK_DEV_IDE - len += sprintf(buf+len, "nubus/IDE interrupt: %10lu\n", - mac_ide_irqs); -#endif - return len; } -void via_scsi_clear(void) -{ - volatile unsigned char deep_magic; - if (via2_is_rbv) { - via_write(rbv_regp, rIFR, (1<<3)|(1<<0)|0x80); - deep_magic = via_read(rbv_regp, rBufB); - } else if (via2_is_oss) { - /* nothing */ - /* via_write(oss_regp, 9, 0) */; - } else - deep_magic = via_read(via2_regp, vBufB); - mac_enable_irq( IRQ_IDX(IRQ_MAC_SCSI) ); -} - - void mac_default_handler(int irq, void *dev_id, struct pt_regs *regs) { #ifdef DEBUG_SPURIOUS - if (console_loglevel > 6) + if (console_loglevel > 6) { printk("Unexpected IRQ %d on device %p\n", irq, dev_id); + } #endif } @@ -854,9 +598,6 @@ void mac_debug_handler(int irq, void *dev_id, struct pt_regs *regs) } } -void scsi_mac_debug(void); -void scsi_mac_polled(void); - static int in_nmi = 0; static volatile int nmi_hold = 0; @@ -869,10 +610,6 @@ void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) */ in_nmi++; -#if 0 - scsi_mac_debug(); - printk("PC: %08lx\nSR: %04x SP: %p\n", fp->pc, fp->sr, fp); -#endif for (i=0; i<100; i++) udelay(1000); @@ -889,10 +626,6 @@ void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) while (nmi_hold == 1) udelay(1000); -#if 0 - scsi_mac_polled(); -#endif - if ( console_loglevel >= 8 ) { #if 0 show_state(); @@ -915,417 +648,6 @@ void mac_nmi_handler(int irq, void *dev_id, struct pt_regs *fp) in_nmi--; } -/* - * Unexpected via interrupt - */ - -void via_wtf(int slot, void *via, struct pt_regs *regs) -{ -#ifdef DEBUG_SPURIOUS - if (console_loglevel > 6) - printk("Unexpected nubus event %d on via %p\n",slot,via); -#endif -} - -/* - * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's - * via6522.c :-), disable/pending masks added. - * The int *viaidx etc. is just to keep the prototype happy ... - */ - -static void via_irq(unsigned char *via, int *viaidx, struct pt_regs *regs) -{ - unsigned char events=(via_read(via, vIFR)&via_read(via,vIER))&0x7F; - int i; - int ct = 0; - struct irqhandler *via_handler = handler_table[*viaidx]; - struct irqparam *via_param = param_table[*viaidx]; - unsigned long *via_irqs = mac_irqs[*viaidx]; - - /* to be changed, possibly: for each non'masked', enabled IRQ, read - * flag bit, ack and call handler ... - * Currently: all pending irqs ack'ed en bloc. - * If ack for masked IRQ required: keep 'pending' info separate. - */ - - /* shouldn't we disable interrupts here ?? */ - - - /* - * Shouldnt happen - */ - - if(events==0) - { -#ifdef DEBUG_VIA - /* should go away; mostly missing timer ticks and ADB events */ - printk("via%d_irq: nothing pending, flags %x mask %x!\n", - *viaidx + 1, via_read(via, vIFR), via_read(via,vIER)); -#endif - via_irqs[7]++; - return; - } - -#ifdef DEBUG_VIA - /* - * limited verbosity for VIA interrupts - */ -#if 0 - if ( (*viaidx == 0 && events != 1<<6) /* timer int */ - || (*viaidx == 1 && events != 1<<3) ) /* SCSI IRQ */ -#else - if ( *viaidx == 0 && (events & 1<<2) ) -#endif - printk("via_irq: irq %d events %x !\n", (*viaidx)+1, events); -#endif - - do { - /* - * Clear the pending flag - */ - - via_write(via, vIFR, events); - - /* - * Now see what bits are raised - */ - - for(i=0;i<7;i++) - { - /* determine machspec. irq no. */ - int irq = ((*viaidx)+1)* 8 + i; - /* call corresponding handlers */ - if (events&(1< mark pending */ - irq_flags[*viaidx].pending |= (1< call handler */ - (via_handler[i].handler)(irq, via, regs); - } - } - /* and call handlers for pending irqs - first ?? */ - if ( (irq_flags[*viaidx].pending & (1<8) - { -#ifdef DEBUG_VIA - printk("via%d: stuck events %x\n", (*viaidx)+1, events); -#endif - break; - } - } - while(events); -#if 0 - scsi_mac_polled(); -#endif -} - -/* - * Caution: the following stuff is called from process_int as _autovector_ - * system interrupts. So irq is always in the range 0-7 :-( and the selection - * of the appropriate VIA is up to the irq handler here based on the autovec - * irq number. There's no information whatsoever about which source on the VIA - * triggered the int - and that's what the machspec irq no's are about. - * Broken design :-(((( - */ - -/* - * System interrupts - */ - -void via1_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - int srcidx = IRQ_IDX(irq) - 1; - via_irq((unsigned char *)via1_regp, &srcidx, regs); -} - - -/* - * Nubus / SCSI interrupts, VIA style (could be wrapped into via1_irq or - * via_irq directly by selecting the regp based on the irq!) - */ - -void via2_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - int srcidx = IRQ_IDX(irq) - 1; - via_irq((unsigned char *)via2_regp, &srcidx, regs); -} - -/* - * Nubus / SCSI interrupts; RBV style - * The RBV is different. RBV appears to stand for randomly broken - * VIA (or even real broken VIA). - */ - -void rbv_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - int srcidx = IRQ_IDX(irq) - 1; /* MUST be 1 !! */ - volatile unsigned char *via = rbv_regp; - unsigned char events=(via_read(via, rIFR)&via_read(via,rIER))&0x7F; - int i; - int ct = 0; - struct irqhandler *via_handler = handler_table[srcidx]; - struct irqparam *via_param = param_table[srcidx]; - - /* shouldn't we disable interrupts here ?? */ - - - /* - * Shouldnt happen - */ - - if(events==0) - { -#ifdef DEBUG_VIA - printk("rbv_irq: nothing pending, flags %x mask %x!\n", - via_read(via, rIFR), via_read(via,rIER)); -#endif - rbv_irqs[7]++; - return; - } - -#ifdef DEBUG_VIA - /* - * limited verbosity for RBV interrupts (add more if needed) - */ - if ( srcidx == 1 && events != 1<<3 ) /* SCSI IRQ */ - printk("rbv_irq: irq %d (%d) events %x !\n", irq, srcidx+1, events); -#endif - - /* to be changed, possibly: for each non'masked', enabled IRQ, read - * flag bit, ack and call handler ... - * Currently: all pending irqs ack'ed en bloc. - * If ack for masked IRQ required: keep 'pending' info separate. - */ - - do { - /* - * Clear the pending flag - */ - - via_write(via, rIFR, events | rbv_clear); - - /* - * Now see what bits are raised - */ - - for(i=0;i<7;i++) - { - /* determine machspec. irq no. */ - int irq = (srcidx+1)* 8 + i; - /* call corresponding handlers */ - if (events&(1< mark pending */ - irq_flags[srcidx].pending |= (1< call handler */ - (via_handler[i].handler)(irq, via, regs); - } - } - /* and call handlers for pending irqs - first ?? */ - if ( (irq_flags[srcidx].pending & (1<8) - { - printk("rbv: stuck events %x\n",events); - for(i=0;i<7;i++) - { - if(events&(1< mark pending */ - irq_flags[srcidx].pending |= (1< call handler */ - (via_handler[i].handler)(irq, via, regs); - } - } - /* and call handlers for pending irqs - first ?? */ - if ( (irq_flags[srcidx].pending & (1<8) - { - printk("oss: stuck events %x\n",events); - for(i=0;i<7;i++) - { - if(events&(1< 6) - printk("Unexpected interrupt on nubus slot %d\n",slot); -#endif -} - /* * SCC master interrupt handler; sole purpose: pass the registered * async struct to the SCC handler proper. @@ -1333,382 +655,6 @@ void nubus_wtf(int slot, void *via, struct pt_regs *regs) void mac_SCC_handler(int irq, void *dev_id, struct pt_regs *regs) { - int i; - /* 1+2: compatibility with PSC ! */ - for (i = 1; i < 3; i++) /* currently only these two used */ - if (scc_handler[i].handler != mac_default_handler) { - (scc_handler[i].handler)(i, scc_handler[i].dev_id, regs); - scc_irqs[i]++; - } -} - -/* - * PSC interrupt handler - */ - -void psc_irq(int irq, void *dev_id, struct pt_regs *regs) -{ - int srcidx = IRQ_IDX(irq) - 1; - volatile unsigned char *via = psc_regp; - unsigned int pIFR = 0x100 + 0x10*srcidx; - unsigned int pIER = 0x104 + 0x10*srcidx; - unsigned char events=(via_read(via, pIFR)&via_read(via,pIER))&0xF; - int i; - int ct = 0; - struct irqhandler *via_handler = handler_table[srcidx]; - struct irqparam *via_param = param_table[srcidx]; - - /* shouldn't we disable interrupts here ?? */ - - - /* - * Shouldnt happen - */ - - if(events==0) - { -#ifdef DEBUG_VIA - printk("psc_irq: nothing pending, flags %x mask %x!\n", - via_read(via, pIFR), via_read(via,pIER)); -#endif - mac_irqs[srcidx][7]++; - return; - } - -#ifdef DEBUG_VIA - /* - * limited verbosity for PSC interrupts (add more if needed) - */ - if ( srcidx == 1 && events != 1<<3 && events != 1<<1 ) /* SCSI IRQ */ - printk("psc_irq: irq %d srcidx+1 %d events %x !\n", irq, srcidx+1, events); -#endif - - /* to be changed, possibly: for each non'masked', enabled IRQ, read - * flag bit, ack and call handler ... - * Currently: all pending irqs ack'ed en bloc. - * If ack for masked IRQ required: keep 'pending' info separate. - */ - - do { - /* - * Clear the pending flag - */ - - /* via_write(via, pIFR, events); */ - - /* - * Now see what bits are raised - */ - - for(i=0;i<7;i++) - { - /* determine machspec. irq no. */ - int irq = (srcidx+1)* 8 + i; - /* call corresponding handlers */ - if (events&(1< mark pending */ - irq_flags[srcidx].pending |= (1< call handler */ - (via_handler[i].handler)(irq, via, regs); - } - } - /* and call handlers for pending irqs - first ?? */ - if ( (irq_flags[srcidx].pending & (1<8) - { - printk("psc: stuck events %x\n",events); - for(i=0;i<7;i++) - { - if(events&(1< 5) - printk("nubus_irq: nothing pending, map %x mask %x active %x\n", - allints, nubus_active, map); -#endif - nubus_irqs[7]++; - } - /* clear it */ - if (allints) - if (via2_is_rbv) - via_write(rbv_regp, rIFR, 0x02); - else - via_write(via2_regp, vIFR, 0x02); - break; - } - -#ifdef DEBUG_VIA_NUBUS - if (console_loglevel > 6) - printk("nubus_irq: map %x mask %x active %x\n", - allints, nubus_active, map); -#endif - -#ifdef CONFIG_BLK_DEV_MAC_IDE - if (mac_ide_intr_hook && ide_pending) { - mac_ide_intr_hook(IRQ_MAC_NUBUS, via, regs); - mac_ide_irqs++; - } -#endif - - if(ct++>2) - { - if (console_loglevel > 5) - printk("nubus stuck events - %x/%x/%x ide %x\n", - allints, nubus_active, map, ide_pending); - nubus_stuck_events++; - - return; - } - - for(i=0;i<7;i++) - { - if(map&(1<2) - { -#if 0 - printk("nubus stuck events - %d/%d\n", map, nubus_active); -#endif - return; - } - - for(i=0;i<7;i++) - { - if(map&(1< hide this as function in arch/m68k/mac ? - * Current emulation buttons: right alt/option and control - * (wanted: command and alt/option, or KP= and KP( ...) - * Debug version; might be rewritten to be faster on normal keys. */ - if (adb_emulate_buttons - && (adb_mouse_interrupt_hook || console_loglevel >= 8)) { - unsigned char button, button2, button3, fake_event; - static unsigned char button2state=0, button3state=0; /* up */ + if (adb_emulate_buttons + && (keycode == adb_button2_keycode + || keycode == adb_button3_keycode) + && (adb_mouse_interrupt_hook || console_loglevel == 10)) { + int button; /* faked ADB packet */ static unsigned char data[4] = { 0, 0x80, 0x80, 0x80 }; - button = 0; - fake_event = 0; - if (keycode == adb_button2_keycode) { /* which 'button' ? */ - /* R-option */ - button2 = (!up_flag); /* new state */ - if (button2 != button2state) /* change ? */ - button = 2; - button2state = button2; /* save state */ - fake_event = 2; - } else if (keycode == adb_button3_keycode) { - /* R-control */ - button3 = (!up_flag); /* new state */ - if (button3 != button3state) /* change ? */ - button = 3; - button3state = button3; /* save state */ - fake_event = 3; - } -#ifdef DEBUG_ADBMOUSE - if (fake_event && console_loglevel >= 8) - printk("fake event: button2 %d button3 %d button %d\n", - button2state, button3state, button); -#endif - if (button) { /* there's been a button state change */ - /* fake a mouse packet : send all bytes, change one! */ - data[button] = (up_flag ? 0x80 : 0); + button = keycode == adb_button2_keycode? 2: 3; + if (data[button] != up_flag) { + /* send a fake mouse packet */ + data[button] = up_flag; + if (console_loglevel >= 8) + printk("fake mouse event: %x %x %x\n", + data[1], data[2], data[3]); if (adb_mouse_interrupt_hook) adb_mouse_interrupt_hook(data, 4); -#ifdef DEBUG_ADBMOUSE - else - printk("mouse_fake: data %2x %2x %2x buttons %2x \n", - data[1], data[2], data[3], - ~( (data[1] & 0x80 ? 0 : 4) - | (data[2] & 0x80 ? 0 : 1) - | (data[3] & 0x80 ? 0 : 2) )&7 ); -#endif } - /* - * for mouse 3-button emulation: don't process 'fake' keys! - * Keys might autorepeat, and console state gets generally messed - * up enough so that selection stops working. - */ - if (fake_event) - return; + return; } #endif /* CONFIG_ADBMOUSE */ /* - * Convert R-shift/control/option to L version. + * This should not be done in raw mode, but basically X is + * all screwed up, so we try to make it less so by adjusting + * things. Note that this is also redundant with + * mackbd_translate() above. Either we are wrong somewhere + * or X is wrong, and I'm betting on the latter. */ switch (keycode) { case 0x7b: keycode = 0x38; break; /* R-shift */ @@ -493,16 +466,18 @@ kbd_repeat(unsigned long xxx) static void mouse_input(unsigned char *data, int nb, struct pt_regs *regs) { - struct kbd_struct *kbd; - int i; +#ifdef DEBUG_ADB + if (console_loglevel == 10 && + (nb < 5 || nb > 6 || (data[2] & 3) != MOUSE_DATAREG)) { + int i; - if (nb < 5 || nb > 6 || (data[2] & 3) != MOUSE_DATAREG) { - printk("data from mouse:"); + printk(KERN_DEBUG "data from mouse:"); for (i = 0; i < nb; ++i) printk(" %x", data[i]); printk("\n"); return; } +#endif if (adb_mouse_interrupt_hook) { adb_mouse_interrupt_hook(data+2, nb-2); @@ -512,60 +487,6 @@ mouse_input(unsigned char *data, int nb, struct pt_regs *regs) */ return; } -#ifdef DEBUG_ADBMOUSE - else - if (console_loglevel >= 8) - printk("mouse_input: data %x %x %x buttons %x dx %d dy %d \n", - data[3], data[4], data[5], - ~((data[3] & 0x80 ? 0 : 4) - | (data[4] & 0x80 ? 0 : 1) - | (data[5] & 0x80 ? 0 : 2))&7, - ((data[4]&0x7f) < 64 ? (data[4]&0x7f) : (data[4]&0x7f)-128 ), - ((data[3]&0x7f) < 64 ? -(data[3]&0x7f) : 128-(data[3]&0x7f) ) ); -#endif - - - kbd = kbd_table + fg_console; - -#if 0 /* The entirely insane way of MkLinux handling mouse input */ - /* Requires put_queue which is static in keyboard.c :-( */ - /* Only send mouse codes when keyboard is in raw mode. */ - if (kbd->kbdmode == VC_RAW) { - static unsigned char uch_ButtonStateSecond = 0; - unsigned char uchButtonSecond; - - /* Send first button, second button and movement. */ - put_queue( 0x7e ); - put_queue( data[3] ); - put_queue( data[4] ); - - /* [ACA: Are there any two-button ADB mice that use handler 1 or 2?] */ - - /* Store the button state. */ - uchButtonSecond = (data[4] & 0x80); - - /* Send second button. */ - if (uchButtonSecond != uch_ButtonStateSecond) { - put_queue( 0x3f | uchButtonSecond ); - uch_ButtonStateSecond = uchButtonSecond; - } - - /* Macintosh 3-button mouse (handler 4). */ - if ((nb == 6) && (data[1] & 0x40)) { - static unsigned char uch_ButtonStateThird = 0; - unsigned char uchButtonThird; - - /* Store the button state for speed. */ - uchButtonThird = (data[5] & 0x80); - - /* Send third button. */ - if (uchButtonThird != uch_ButtonStateThird) { - put_queue( 0x40 | uchButtonThird ); - uch_ButtonStateThird = uchButtonThird; - } - } - } -#endif /* insane MkLinux mouse hack */ } /* Map led flags as defined in kbd_kern.h to bits for Apple keyboard. */ @@ -582,13 +503,11 @@ static unsigned char mac_ledmap[8] = { static int leds_pending; -void mac_kbd_leds(unsigned int leds) +void mackbd_leds(unsigned int leds) { - if (led_request.got_reply) { -#ifdef DEBUG_ADB + if (led_request.complete) { if (console_loglevel == 10) - printk("mac_kbd_leds: got reply, sending request!\n"); -#endif + printk(KERN_DEBUG "mackbd_leds: got reply, sending request!\n"); adb_request(&led_request, mac_leds_done, 4, ADB_PACKET, ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, ~mac_ledmap[leds]); @@ -603,20 +522,15 @@ static void mac_leds_done(struct adb_request *req) if (leds_pending) { leds = leds_pending & 0xff; leds_pending = 0; - mac_kbd_leds(leds); + mackbd_leds(leds); } mark_bh(KEYBOARD_BH); } -int mac_kbdrate(struct kbd_repeat *k) -{ - return 0; -} - -__initfunc(int mac_keyb_init(void)) +__initfunc(int mackbd_init_hw(void)) { - static struct adb_request autopoll_req, confcod_req, mouse_req, readkey_req; - volatile int ct; + static struct adb_request autopoll_req, confcod_req; + int ct; /* setup key map */ memcpy(key_maps[0], mac_plain_map, sizeof(plain_map)); @@ -629,12 +543,7 @@ __initfunc(int mac_keyb_init(void)) /* initialize mouse interrupt hook */ adb_mouse_interrupt_hook = NULL; - - /* - * Might put that someplace else, possibly .... - */ - adb_bus_init(); - + /* the input functions ... */ adb_register(ADB_KEYBOARD, keyboard_input); adb_register(ADB_MOUSE, mouse_input); @@ -653,17 +562,13 @@ __initfunc(int mac_keyb_init(void)) */ if (macintosh_config->adb_type == MAC_ADB_CUDA) { - printk("CUDA autopoll on ...\n"); + printk(KERN_DEBUG "CUDA autopoll on ...\n"); adb_request(&autopoll_req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1); - ct=0; - while (!autopoll_req.got_reply && ++ct<1000) - { - udelay(10); - } - if(ct==1000) { - printk("Keyboard timed out.\n"); - autopoll_req.got_reply = 1; - } + ct = 0; + while (!autopoll_req.complete && ct++ < 1000) + adb_poll(); + if (ct == 1000) printk(KERN_ERR "ADB timeout\n"); + autopoll_req.complete = 1; } /* @@ -671,15 +576,15 @@ __initfunc(int mac_keyb_init(void)) * care of that for other Macs. */ - printk("Configuring keyboard:\n"); - - udelay(3000); + printk(KERN_DEBUG "Configuring keyboard:\n"); + /* Aarrrgggh! Die in hell! */ + udelay(8000); /* * turn on all leds - the keyboard driver will turn them back off - * via mac_kbd_leds if everything works ok! + * via mackbd_leds if everything works ok! */ - printk("leds on ...\n"); + printk(KERN_DEBUG "leds on ...\n"); adb_request(&led_request, NULL, 4, ADB_PACKET, ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, ~7); @@ -687,19 +592,13 @@ __initfunc(int mac_keyb_init(void)) * The polling stuff should go away as soon as the ADB driver is stable */ ct = 0; - while (!led_request.got_reply && ++ct<1000) - { - udelay(10); - } - if(ct==1000) { - printk("keyboard timed out.\n"); - led_request.got_reply = 1; - } - -#if 1 - printk("configuring coding mode ...\n"); + while (!led_request.complete && ct++ < 1000) + adb_poll(); + if (ct == 1000) printk(KERN_ERR "ADB timeout\n"); + led_request.complete = 1; - udelay(3000); + printk(KERN_DEBUG "configuring coding mode ...\n"); + udelay(8000); /* * get the keyboard to send separate codes for @@ -707,56 +606,15 @@ __initfunc(int mac_keyb_init(void)) */ adb_request(&confcod_req, NULL, 4, ADB_PACKET, ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3); - - ct=0; - while (!confcod_req.got_reply && ++ct<1000) - { - udelay(10); - } - if(ct==1000) { - printk("keyboard timed out.\n"); - confcod_req.got_reply = 1; - } -#endif - -#if 0 /* seems to hurt, at least Geert's Mac */ - printk("Configuring mouse (3-button mode) ...\n"); - - udelay(3000); - - /* - * XXX: taken from the PPC driver again ... - * Try to switch the mouse (id 3) to handler 4, for three-button - * mode. (0x20 is Service Request Enable, 0x03 is Device ID). - */ - adb_request(&mouse_req, NULL, 4, ADB_PACKET, - ADB_WRITEREG(ADB_MOUSE, 3), 0x23, 4 ); - - ct=0; - while (!mouse_req.got_reply && ++ct<1000) - { - udelay(10); - } - if(ct==1000) - printk("Mouse timed out.\n"); -#endif - -#if 0 - printk("Start polling keyboard ...\n"); - - /* - * get the keyboard to send data back, via the adb_input hook - * XXX: was never used properly, and the driver takes care - * of polling and timeout retransmits now. - * Might be of use if we want to start talking to a specific - * device here... - */ - adb_request(&readkey_req, NULL, 2, ADB_PACKET, - ADB_READREG(ADB_KEYBOARD, KEYB_KEYREG)); -#endif + ct = 0; + while (!confcod_req.complete && ct++ < 1000) + adb_poll(); + if (ct == 1000) printk(KERN_ERR "ADB timeout\n"); + confcod_req.complete = 1; in_keybinit = 0; - printk("keyboard init done\n"); + printk(KERN_DEBUG "keyboard init done\n"); + udelay(8000); return 0; } diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c new file mode 100644 index 000000000000..6d807ab76a90 --- /dev/null +++ b/arch/m68k/mac/oss.c @@ -0,0 +1,314 @@ +/* + * OSS handling + * Written by Joshua M. Thompson (funaho@jurai.org) + * + * + * This chip is used in the IIfx in place of VIA #2. It acts like a fancy + * VIA chip with prorammable interrupt levels. + * + * 990502 (jmt) - Major rewrite for new interrupt architecture as well as some + * recent insights into OSS operational details. + * 990610 (jmt) - Now taking fulll advantage of the OSS. Interrupts are mapped + * to mostly match the A/UX interrupt scheme supported on the + * VIA side. Also added support for enabling the ISM irq again + * since we now have a functional IOP manager. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int oss_present; +volatile struct mac_oss *oss; + +void oss_irq(int, void *, struct pt_regs *); +void oss_nubus_irq(int, void *, struct pt_regs *); + +extern void via1_irq(int, void *, struct pt_regs *); +extern void mac_SCC_handler(int, void *, struct pt_regs *); +extern int console_loglevel; + +/* + * Initialize the OSS + * + * The OSS "detection" code is actually in via_init() which is always called + * before us. Thus we can count on oss_present being valid on entry. + */ + +__initfunc(void oss_init(void)) +{ + int i; + + if (!oss_present) return; + + oss = (struct mac_oss *) OSS_BASE; + + /* Disable all interrupts. Unlike a VIA it looks like we */ + /* do this by setting the source's interrupt level to zero. */ + + for (i = 0; i <= OSS_NUM_SOURCES; i++) { + oss->irq_level[i] = OSS_IRQLEV_DISABLED; + } + /* If we disable VIA1 here, we never really handle it... */ + oss->irq_level[OSS_VIA1] = OSS_IRQLEV_VIA1; +} + +/* + * Register the OSS and NuBus interrupt dispatchers. + */ + +__initfunc(void oss_register_interrupts(void)) +{ + sys_request_irq(OSS_IRQLEV_SCSI, oss_irq, IRQ_FLG_LOCK, + "OSS SCSI Dispatch", (void *) oss); + sys_request_irq(OSS_IRQLEV_IOPSCC, mac_SCC_handler, IRQ_FLG_LOCK, + "SCC Dispatch", mac_SCC_handler); + sys_request_irq(OSS_IRQLEV_NUBUS, oss_nubus_irq, IRQ_FLG_LOCK, + "Nubus Dispatch", (void *) oss); + sys_request_irq(OSS_IRQLEV_SOUND, oss_irq, IRQ_FLG_LOCK, + "OSS Sound Dispatch", (void *) oss); + sys_request_irq(OSS_IRQLEV_VIA1, via1_irq, IRQ_FLG_LOCK, + "VIA1 Dispatch", (void *) via1); +} + +/* + * Initialize OSS for Nubus access + */ + +__initfunc(void oss_nubus_init(void)) +{ +} + +/* + * Turn off the power via the ROM control register + * + * FIXME: not sure how this is supposed to work exactly... + */ + +void oss_poweroff(void) +{ + oss->rom_ctrl = OSS_POWEROFF; + + /* We should never make it this far... */ + + printk ("It is now safe to switch off your machine.\n"); + while(1); +} + +/* + * Handle miscellaneous OSS interrupts. Right now that's just sound + * and SCSI; everything else is routed to its own autovector IRQ. + */ + +void oss_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int events; + + events = oss->irq_pending & (OSS_IP_SOUND|OSS_IP_SCSI); + if (!events) return; + +#ifdef DEBUG_IRQS + if ((console_loglevel == 10) && !(events & OSS_IP_SCSI)) { + printk("oss_irq: irq %d events = 0x%04X\n", irq, + (int) oss->irq_pending); + } +#endif + /* FIXME: how do you clear a pending IRQ? */ + + if (events & OSS_IP_SOUND) { + oss->irq_pending &= ~OSS_IP_SOUND; + /* FIXME: call sound handler */ + } else if (events & OSS_IP_SCSI) { + oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; + oss->irq_pending &= ~OSS_IP_SCSI; + mac_do_irq_list(IRQ_MAC_SCSI, regs); + oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; + } else { + /* FIXME: error check here? */ + } +} + +/* + * Nubus IRQ handler, OSS style + * + * Unlike the VIA/RBV this is on its own autovector interupt level. + */ + +void oss_nubus_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int events, irq_bit, i; + + events = oss->irq_pending & OSS_IP_NUBUS; + if (!events) return; + +#ifdef DEBUG_NUBUS_INT + if (console_loglevel > 7) { + printk("oss_nubus_irq: events = 0x%04X\n", events); + } +#endif + /* There are only six slots on the OSS, not seven */ + + for (i = 0, irq_bit = 1 ; i < 6 ; i++, irq_bit <<= 1) { + if (events & irq_bit) { + oss->irq_level[i] = OSS_IRQLEV_DISABLED; + oss->irq_pending &= ~irq_bit; + mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs); + oss->irq_level[i] = OSS_IRQLEV_NUBUS; + } + } +} + +/* + * Enable an OSS interrupt + * + * It looks messy but it's rather straightforward. The switch() statement + * just maps the machspec interrupt numbers to the right OSS interrupt + * source (if the OSS handles that interrupt) and then sets the interrupt + * level for that source to nonzero, thus enabling the interrupt. + */ + +void oss_irq_enable(int irq) { +#ifdef DEBUG_IRQUSE + printk("oss_irq_enable(%d)\n", irq); +#endif + switch(irq) { + case IRQ_SCC: + case IRQ_SCCA: + case IRQ_SCCB: + oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_IOPSCC; + break; + case IRQ_MAC_ADB: + oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_IOPISM; + break; + case IRQ_MAC_SCSI: + oss->irq_level[OSS_SCSI] = OSS_IRQLEV_SCSI; + break; + case IRQ_NUBUS_9: + case IRQ_NUBUS_A: + case IRQ_NUBUS_B: + case IRQ_NUBUS_C: + case IRQ_NUBUS_D: + case IRQ_NUBUS_E: + irq -= NUBUS_SOURCE_BASE; + oss->irq_level[irq] = OSS_IRQLEV_NUBUS; + break; +#ifdef DEBUG_IRQUSE + default: + printk("%s unknown irq %d\n",__FUNCTION__, irq); + break; +#endif + } +} + +/* + * Disable an OSS interrupt + * + * Same as above except we set the source's interrupt level to zero, + * to disable the interrupt. + */ + +void oss_irq_disable(int irq) { +#ifdef DEBUG_IRQUSE + printk("oss_irq_disable(%d)\n", irq); +#endif + switch(irq) { + case IRQ_SCC: + case IRQ_SCCA: + case IRQ_SCCB: + oss->irq_level[OSS_IOPSCC] = OSS_IRQLEV_DISABLED; + break; + case IRQ_MAC_ADB: + oss->irq_level[OSS_IOPISM] = OSS_IRQLEV_DISABLED; + break; + case IRQ_MAC_SCSI: + oss->irq_level[OSS_SCSI] = OSS_IRQLEV_DISABLED; + break; + case IRQ_NUBUS_9: + case IRQ_NUBUS_A: + case IRQ_NUBUS_B: + case IRQ_NUBUS_C: + case IRQ_NUBUS_D: + case IRQ_NUBUS_E: + irq -= NUBUS_SOURCE_BASE; + oss->irq_level[irq] = OSS_IRQLEV_DISABLED; + break; +#ifdef DEBUG_IRQUSE + default: + printk("%s unknown irq %d\n", __FUNCTION__, irq); + break; +#endif + } +} + +/* + * Clear an OSS interrupt + * + * Not sure if this works or not but it's the only method I could + * think of based on the contents of the mac_oss structure. + */ + +void oss_irq_clear(int irq) { + /* FIXME: how to do this on OSS? */ + switch(irq) { + case IRQ_SCC: + case IRQ_SCCA: + case IRQ_SCCB: + oss->irq_pending &= ~OSS_IP_IOPSCC; + break; + case IRQ_MAC_ADB: + oss->irq_pending &= ~OSS_IP_IOPISM; + break; + case IRQ_MAC_SCSI: + oss->irq_pending &= ~OSS_IP_SCSI; + break; + case IRQ_NUBUS_9: + case IRQ_NUBUS_A: + case IRQ_NUBUS_B: + case IRQ_NUBUS_C: + case IRQ_NUBUS_D: + case IRQ_NUBUS_E: + irq -= NUBUS_SOURCE_BASE; + oss->irq_pending &= ~(1 << irq); + break; + } +} + +/* + * Check to see if a specific OSS interrupt is pending + */ + +int oss_irq_pending(int irq) +{ + switch(irq) { + case IRQ_SCC: + case IRQ_SCCA: + case IRQ_SCCB: + return oss->irq_pending & OSS_IP_IOPSCC; + break; + case IRQ_MAC_ADB: + return oss->irq_pending & OSS_IP_IOPISM; + break; + case IRQ_MAC_SCSI: + return oss->irq_pending & OSS_IP_SCSI; + break; + case IRQ_NUBUS_9: + case IRQ_NUBUS_A: + case IRQ_NUBUS_B: + case IRQ_NUBUS_C: + case IRQ_NUBUS_D: + case IRQ_NUBUS_E: + irq -= NUBUS_SOURCE_BASE; + return oss->irq_pending & (1 << irq); + break; + } + return 0; +} diff --git a/arch/m68k/mac/psc.c b/arch/m68k/mac/psc.c new file mode 100644 index 000000000000..ee40cf56fcf5 --- /dev/null +++ b/arch/m68k/mac/psc.c @@ -0,0 +1,200 @@ +/* + * Apple Peripheral System Controller (PSC) + * + * The PSC is used on the AV Macs to control IO functions not handled + * by the VIAs (Ethernet, DSP, SCC). + * + * TO DO: + * + * Try to figure out what's going on in pIFR5 and pIFR6. There seem to be + * persisant interrupt conditions in those registers and I have no idea what + * they are. Granted it doesn't affect since we're not enabling any interrupts + * on those levels at the moment, but it would be nice to know. I have a feeling + * they aren't actually interrupt lines but data lines (to the DSP?) + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DEBUG_PSC + +int psc_present; +volatile __u8 *psc; + +void psc_irq(int, void *, struct pt_regs *); + +extern int console_loglevel; + +/* + * Debugging dump, used in various places to see what's going on. + */ + +void psc_debug_dump(void) +{ + int i; + + if (!psc_present) return; + for (i = 0x30 ; i < 0x70 ; i += 0x10) { + printk("PSC #%d: IFR = 0x%02X IER = 0x%02X\n", + i >> 4, + (int) psc_read_byte(pIFRbase + i), + (int) psc_read_byte(pIERbase + i)); + } +} + +/* + * Try to kill all DMA channels on the PSC. Not sure how this his + * supposed to work; this is code lifted from macmace.c and then + * expanded to cover what I think are the other 7 channels. + */ + +void psc_dma_die_die_die(void) +{ + int i; + + printk("Killing all PSC DMA channels..."); + for (i = 0 ; i < 9 ; i++) { + psc_write_word(PSC_CTL_BASE + (i << 4), 0x8800); + psc_write_word(PSC_CTL_BASE + (i << 4), 0x1000); + psc_write_word(PSC_CMD_BASE + (i << 5), 0x1100); + psc_write_word(PSC_CMD_BASE + (i << 5) + 0x10, 0x1100); + } + printk("done!\n"); +} + +/* + * Initialize the PSC. For now this just involves shutting down all + * interrupt sources using the IERs. + */ + +__initfunc(void psc_init(void)) +{ + int i; + + if (macintosh_config->ident != MAC_MODEL_C660 + && macintosh_config->ident != MAC_MODEL_Q840) + { + psc = NULL; + psc_present = 0; + return; + } + + /* + * The PSC is always at the same spot, but using psc + * keeps things consisant with the psc_xxxx functions. + */ + + psc = (void *) PSC_BASE; + psc_present = 1; + + printk("PSC detected at %p\n", psc); + + psc_dma_die_die_die(); + +#ifdef DEBUG_PSC + psc_debug_dump(); +#endif + /* + * Mask and clear all possible interrupts + */ + + for (i = 0x30 ; i < 0x70 ; i += 0x10) { + psc_write_byte(pIERbase + i, 0x0F); + psc_write_byte(pIFRbase + i, 0x0F); + } +} + +/* + * Register the PSC interrupt dispatchers for autovector interrupts 3-6. + */ + +__initfunc(void psc_register_interrupts(void)) +{ + sys_request_irq(3, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch", + (void *) 0x30); + sys_request_irq(4, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch", + (void *) 0x40); + sys_request_irq(5, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch", + (void *) 0x50); + sys_request_irq(6, psc_irq, IRQ_FLG_LOCK, "PSC Dispatch", + (void *) 0x60); +} + +/* + * PSC interrupt handler. It's a lot like the VIA interrupt handler. + */ + +void psc_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int pIFR = pIFRbase + ((int) dev_id); + int pIER = pIERbase + ((int) dev_id); + int base_irq = irq << 3; + int irq_bit,i; + unsigned char events; + +#ifdef DEBUG_IRQS + printk("psc_irq: irq %d pIFR = 0x%02X pIER = 0x%02X\n", + irq, (int) psc_read_byte(pIFR), (int) psc_read_byte(pIER)); +#endif + + events = psc_read_byte(pIFR) & psc_read_byte(pIER) & 0xF; + if (!events) return; + + for (i = 0, irq_bit = 1 ; i < 4 ; i++, irq_bit <<= 1) { + if (events & irq_bit) { + psc_write_byte(pIER, irq_bit); + psc_write_byte(pIFR, irq_bit); + mac_do_irq_list(base_irq + i, regs); + psc_write_byte(pIER, irq_bit | 0x80); + } + } +} + +void psc_irq_enable(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int pIER = pIERbase + (irq_src << 4); + +#ifdef DEBUG_IRQUSE + printk("psc_irq_enable(%d)\n", irq); +#endif + psc_write_byte(pIER, (1 << irq_idx) | 0x80); +} + +void psc_irq_disable(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int pIER = pIERbase + (irq_src << 4); + +#ifdef DEBUG_IRQUSE + printk("psc_irq_disable(%d)\n", irq); +#endif + psc_write_byte(pIER, 1 << irq_idx); +} + +void psc_irq_clear(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int pIFR = pIERbase + (irq_src << 4); + + psc_write_byte(pIFR, 1 << irq_idx); +} + +int psc_irq_pending(int irq) +{ + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int pIFR = pIERbase + (irq_src << 4); + + return psc_read_byte(pIFR) & (1 << irq_idx); +} diff --git a/arch/m68k/mac/via.c b/arch/m68k/mac/via.c new file mode 100644 index 000000000000..013c5041ff56 --- /dev/null +++ b/arch/m68k/mac/via.c @@ -0,0 +1,788 @@ +/* + * 6522 Versatile Interface Adapter (VIA) + * + * There are two of these on the Mac II. Some IRQ's are vectored + * via them as are assorted bits and bobs - eg RTC, ADB. + * + * CSA: Motorola seems to have removed documentation on the 6522 from + * their web site; try + * http://nerini.drf.com/vectrex/other/text/chips/6522/ + * http://www.zymurgy.net/classic/vic20/vicdet1.htm + * and + * http://193.23.168.87/mikro_laborversuche/via_iobaustein/via6522_1.html + * for info. A full-text web search on 6522 AND VIA will probably also + * net some usefulness. 20apr1999 + * + * PRAM/RTC access algorithms are from the NetBSD RTC toolkit version 1.08b + * by Erik Vogan and adapted to Linux by Joshua M. Thompson (funaho@jurai.org) + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +volatile __u8 *via1, *via2; +#if 0 +/* See note in mac_via.h about how this is possibly not useful */ +volatile long *via_memory_bogon=(long *)&via_memory_bogon; +#endif +int rbv_present,via_alt_mapping; +__u8 rbv_clear; + +/* + * Globals for accessing the VIA chip registers without having to + * check if we're hitting a real VIA or an RBV. Normally you could + * just hit the combined register (ie, vIER|rIER) but that seems to + * break on AV Macs...probably because they actually decode more than + * eight address bits. Why can't Apple engineers at least be + * _consistantly_ lazy? - 1999-05-21 (jmt) + */ + +static int gIER,gIFR,gBufA,gBufB; + +/* + * Timer defs. + */ + +#define TICK_SIZE 10000 +#define MAC_CLOCK_TICK (783300/HZ) /* ticks per HZ */ +#define MAC_CLOCK_LOW (MAC_CLOCK_TICK&0xFF) +#define MAC_CLOCK_HIGH (MAC_CLOCK_TICK>>8) + +static int nubus_active; + +void via_debug_dump(void); +void via1_irq(int, void *, struct pt_regs *); +void via2_irq(int, void *, struct pt_regs *); +void via_nubus_irq(int, void *, struct pt_regs *); +void via_irq_enable(int irq); +void via_irq_disable(int irq); +void via_irq_clear(int irq); + +extern void mac_bang(int, void *, struct pt_regs *); +extern void mac_SCC_handler(int, void *, struct pt_regs *); +extern int console_loglevel; +extern int oss_present; + +/* + * Initialize the VIAs + * + * First we figure out where they actually _are_ as well as what type of + * VIA we have for VIA2 (it could be a real VIA or an RBV or even an OSS.) + * Then we pretty much clear them out and disable all IRQ sources. + * + * Note: the OSS is actually "detected" here and not in oss_init(). It just + * seems more logical to do it here since via_init() needs to know + * these things anyways. + */ + +__initfunc(void via_init(void)) +{ + switch(macintosh_config->via_type) { + + /* IIci, IIsi, IIvx, IIvi (P6xx), LC series */ + + case MAC_VIA_IIci: + via1 = (void *) VIA1_BASE; + if (macintosh_config->ident == MAC_MODEL_IIFX) { + via2 = NULL; + rbv_present = 0; + oss_present = 1; + } else { + via2 = (void *) RBV_BASE; + rbv_present = 1; + oss_present = 0; + } + if (macintosh_config->ident == MAC_MODEL_LCIII) { + rbv_clear = 0x00; + } else { + /* on most RBVs (& unlike the VIAs), you */ + /* need to set bit 7 when you write to IFR */ + /* in order for your clear to occur. */ + rbv_clear = 0x80; + } + gIER = rIER; + gIFR = rIFR; + gBufA = rSIFR; + gBufB = rBufB; + break; + + /* Quadra and early MacIIs agree on the VIA locations */ + + case MAC_VIA_QUADRA: + case MAC_VIA_II: + via1 = (void *) VIA1_BASE; + via2 = (void *) VIA2_BASE; + rbv_present = 0; + oss_present = 0; + rbv_clear = 0x00; + gIER = vIER; + gIFR = vIFR; + gBufA = vBufA; + gBufB = vBufB; + break; + default: + panic("UNKNOWN VIA TYPE"); + } + + printk("VIA1 at %p is a 6522 or clone\n", via1); + + printk("VIA2 at %p is ", via2); + if (rbv_present) { + printk("an RBV\n"); + } else if (oss_present) { + printk("an OSS\n"); + } else { + printk("a 6522 or clone\n"); + } + +#ifdef DEBUG_VIA + via_debug_dump(); +#endif + + /* + * Shut down all IRQ sources, reset the timers, and + * kill the timer latch on VIA1. + */ + + via_write(via1, vIER, 0x7F); + via_write(via1, vIFR, 0x7F); + via_write(via1, vT1LL, 0); + via_write(via1, vT1LH, 0); + via_write(via1, vT1CL, 0); + via_write(via1, vT1CH, 0); + via_write(via1, vT2CL, 0); + via_write(via1, vT2CH, 0); + via_write(via1, vACR, via_read(via1, vACR) & 0x3F); + + /* + * SE/30: disable video IRQ + * XXX: testing for SE/30 VBL + */ + + if (macintosh_config->ident == MAC_MODEL_SE30) { + via_write(via1, vBufB, via_read(via1, vBufB) | 0x40); + via_write(via1, vDirB, via_read(via1, vDirB) | 0x40); + } + + /* + * Set the RTC bits to a known state: all lines to outputs and + * RTC disabled (yes that's 0 to enable and 1 to disable). + */ + + via_write(via1, vDirB, via_read(via1, vDirB) | (VIA1B_vRTCEnb | + VIA1B_vRTCClk | + VIA1B_vRTCData)); + via_write(via1, vBufB, via_read(via1, vBufB) | (VIA1B_vRTCEnb | + VIA1B_vRTCClk)); + + /* Everything below this point is VIA2/RBV only... */ + + if (oss_present) return; + +#if 1 + /* Some machines support an alternate IRQ mapping that spreads */ + /* Ethernet and Sound out to their own autolevel IRQs and moves */ + /* VIA1 to level 6. A/UX uses this mapping and we do too. Note */ + /* that the IIfx emulates this alternate mapping using the OSS. */ + + switch(macintosh_config->ident) { + case MAC_MODEL_C610: + case MAC_MODEL_Q610: + case MAC_MODEL_C650: + case MAC_MODEL_Q650: + case MAC_MODEL_Q700: + case MAC_MODEL_Q800: + case MAC_MODEL_Q900: + case MAC_MODEL_Q950: + via_alt_mapping = 1; + via_write(via1, vDirB, via_read(via1, vDirB) | 0x40); + via_write(via1, vBufB, via_read(via1, vBufB) & ~0x40); + break; + default: + via_alt_mapping = 0; + break; + } +#else + via_alt_mapping = 0; +#endif + + /* + * Now initialize VIA2. For RBV we just kill all interrupts; + * for a regular VIA we also reset the timers and stuff. + */ + + via_write(via2, gIER, 0x7F); + via_write(via2, gIFR, 0x7F | rbv_clear); + if (!rbv_present) { + via_write(via2, vT1LL, 0); + via_write(via2, vT1LH, 0); + via_write(via2, vT1CL, 0); + via_write(via2, vT1CH, 0); + via_write(via2, vT2CL, 0); + via_write(via2, vT2CH, 0); + via_write(via2, vACR, via_read(via2, vACR) & 0x3F); + } +} + +/* + * Start the 100 Hz clock + */ + +__initfunc(void via_init_clock(void (*func)(int, void *, struct pt_regs *))) +{ + via_write(via1, vACR, via_read(via1, vACR) | 0x40); + via_write(via1, vT1LL, MAC_CLOCK_LOW); + via_write(via1, vT1LH, MAC_CLOCK_HIGH); + via_write(via1, vT1CL, MAC_CLOCK_LOW); + via_write(via1, vT1CH, MAC_CLOCK_HIGH); + + request_irq(IRQ_MAC_TIMER_1, func, IRQ_FLG_LOCK, "timer", func); +} + +/* + * Register the interrupt dispatchers for VIA or RBV machines only. + */ + +__initfunc(void via_register_interrupts(void)) +{ + if (via_alt_mapping) { + sys_request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "Software IRQ", (void *) via1); + sys_request_irq(IRQ_AUTO_6, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "VIA1 Dispatch", (void *) via1); + } else { + sys_request_irq(IRQ_AUTO_1, via1_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "VIA1 Dispatch", (void *) via1); +#if 0 /* interferes with serial on some machines */ + if (!psc_present) { + sys_request_irq(IRQ_AUTO_6, mac_bang, IRQ_FLG_LOCK, + "Off Switch", mac_bang); + } +#endif + } + sys_request_irq(IRQ_AUTO_2, via2_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "VIA2 Dispatch", (void *) via2); + if (!psc_present) { + sys_request_irq(IRQ_AUTO_4, mac_SCC_handler, IRQ_FLG_LOCK, + "SCC Dispatch", mac_SCC_handler); + } + request_irq(IRQ_MAC_NUBUS, via_nubus_irq, IRQ_FLG_LOCK|IRQ_FLG_FAST, + "Nubus Dispatch", (void *) via2); +} + +/* + * Debugging dump, used in various places to see what's going on. + */ + +void via_debug_dump(void) +{ + printk("VIA1: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n", + via_read(via1, vDirA), + via_read(via1, vDirB), + via_read(via1, vACR)); + printk(" PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n", + via_read(via1, vPCR), + via_read(via1, vIFR), + via_read(via1, vIER)); + if (oss_present) { + printk("VIA2: \n"); + } else if (rbv_present) { + printk("VIA2: IFR = 0x%02X IER = 0x%02X\n", + via_read(via2, rIFR), + via_read(via2, rIER)); + printk(" SIFR = 0x%02X SIER = 0x%02X\n", + via_read(via2, rSIFR), + via_read(via2, rSIER)); + } else { + printk("VIA2: DDRA = 0x%02X DDRB = 0x%02X ACR = 0x%02X\n", + via_read(via2, vDirA), + via_read(via2, vDirB), + via_read(via2, vACR)); + printk(" PCR = 0x%02X IFR = 0x%02X IER = 0x%02X\n", + via_read(via2, vPCR), + via_read(via2, vIFR), + via_read(via2, vIER)); + } +} + +/* + * This is always executed with interrupts disabled. + * + * TBI: get time offset between scheduling timer ticks + */ + +unsigned long mac_gettimeoffset (void) +{ + unsigned long ticks, offset = 0; + + /* read VIA1 timer 2 current value */ + ticks = via_read(via1, vT1CL) + (via_read(via1, vT1CH)<<8); + /* The probability of underflow is less than 2% */ + if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50) + /* Check for pending timer interrupt in VIA1 IFR */ + if (via_read(via1, vIFR) & 0x40) + offset = TICK_SIZE; + + ticks = MAC_CLOCK_TICK - ticks; + ticks = ticks * 10000L / MAC_CLOCK_TICK; + + return ticks + offset; +} + +/* + * Flush the L2 cache on Macs that have it by flipping + * the system into 24-bit mode for an instant. + */ + +void via_flush_cache(void) +{ + via_write(via2, gBufB, via_read(via2, vBufB) & ~VIA2B_vMode32); + via_write(via2, gBufB, via_read(via2, vBufB) | VIA2B_vMode32); +} + +/* + * Return the status of the L2 cache on a IIci + */ + +int via_get_cache_disable(void) +{ + /* Safeguard against being called accidentally */ + if (!via2) { + printk(KERN_ERR "via_get_cache_disable called on a non-VIA machine!\n"); + return 1; + } + + return via_read(via2, gBufB) & VIA2B_vCDis; +} + +/* + * VIA-based power switch, for machines that support it. + */ + +void via_poweroff(void) +{ + if (rbv_present) { + via_write(via2, rBufB, via_read(via2, rBufB) & ~0x04); + } else { + /* Direction of vDirB is output */ + via_write(via2, vDirB, via_read(via2, vDirB) | 0x04); + /* Send a value of 0 on that line */ + via_write(via2, vBufB, via_read(via2, vBufB) & ~0x04); + /* Otherwise it prints "It is now.." then shuts off */ + mdelay(1000); + } + + /* We should never make it this far... */ + printk ("It is now safe to switch off your machine.\n"); + while(1); +} + +/* + * Initialize VIA2 for Nubus access + */ + +__initfunc(void via_nubus_init(void)) +{ + nubus_active = 0; + + /* unlock nubus transactions */ + + if (!rbv_present) { + /* set the line to be an output on non-RBV machines */ + via_write(via2, vDirB, via_read(via2, vDirB) | 0x02); + } + via_write(via2, gBufB, via_read(via2, gBufB) | 0x02); + + /* disable nubus slot interrupts. */ + if (rbv_present) { + via_write(via2, rSIER, 0x7F); /* like VIA; bit 7=clr,set */ + if ((via_read(via2, rSIER) & 0x7F) != 0) { + printk("SIER not behaving properly: " + "email \n"); + } + } else { + via_write(via2, vBufA, 0xFF); /* active low irqs, force high */ + via_write(via2, vDirA, 0xFF); /* ddr to output. */ + } +} + +/* + * The generic VIA interrupt routines (shamelessly stolen from Alan Cox's + * via6522.c :-), disable/pending masks added. + * + * The new interrupt architecture in macints.c takes care of a lot of the + * gruntwork for us, including tallying the interrupts and calling the + * handlers on the linked list. All we need to do here is basically generate + * the machspec interrupt number after clearing the interrupt. + */ + +void via1_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int irq_bit, i; + unsigned char events, mask; + + mask = via_read(via1, vIER) & 0x7f; + events = via_read(via1, vIFR) & mask; + if (events == 0) return; + + for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) + if (events & irq_bit) { + via_write(via1, vIER, irq_bit); + via_write(via1, vIFR, irq_bit); + mac_do_irq_list(VIA1_SOURCE_BASE + i, regs); + via_write(via1, vIER, irq_bit | 0x80); + } + + if (!oss_present) { + /* This (still) seems to be necessary to get IDE + working. However, if you enable VBL interrupts, + you're screwed... */ + /* FIXME: should we check the SLOTIRQ bit before + pulling this stunt? */ + via_irq_disable(IRQ_MAC_NUBUS); + via_irq_clear(IRQ_MAC_NUBUS); + mac_do_irq_list(IRQ_MAC_NUBUS, regs); + via_irq_enable(IRQ_MAC_NUBUS); + } +} + +void via2_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int irq_bit, i; + unsigned char events, mask; + + mask = via_read(via2, gIER) & 0x7f; + events = via_read(via2, gIFR) & mask; + if (events == 0) return; + + for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) + if (events & irq_bit) { + via_write(via2, gIER, irq_bit); + via_write(via2, gIFR, irq_bit | rbv_clear); + mac_do_irq_list(VIA2_SOURCE_BASE + i, regs); + via_write(via2, gIER, irq_bit | 0x80); + } +} + +/* + * Dispatch Nubus interrupts. We are called as a secondary dispatch by the + * VIA2 dispatcher as a fast interrupt handler. + */ + +void via_nubus_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int irq_bit, i; + unsigned char events; + + events = ~via_read(via2, gBufA) & nubus_active; + if (events == 0) return; + + for (i = 0, irq_bit = 1 ; i < 7 ; i++, irq_bit <<= 1) { + if (events & irq_bit) { + via_irq_disable(NUBUS_SOURCE_BASE + i); + /* FIXME: this does nothing. Should we clear + the SLOTIRQ bit here? */ + via_irq_clear(NUBUS_SOURCE_BASE + i); + mac_do_irq_list(NUBUS_SOURCE_BASE + i, regs); + via_irq_enable(NUBUS_SOURCE_BASE + i); + } + } +} + +void via_irq_enable(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int irq_bit = 1 << irq_idx; + +#ifdef DEBUG_IRQUSE + printk("via_irq_enable(%d)\n", irq); +#endif + + if (irq_src == 1) { + via_write(via1, vIER, irq_bit | 0x80); + } else if (irq_src == 2) { + /* + * Set vPCR for SCSI interrupts (but not on RBV) + */ + if ((irq_idx == 0) && !rbv_present) { + if (macintosh_config->scsi_type == MAC_SCSI_OLD) { + /* CB2 (IRQ) indep. input, positive edge */ + /* CA2 (DRQ) indep. input, positive edge */ + via_write(via2, vPCR, 0x66); + } else { + /* CB2 (IRQ) indep. input, negative edge */ + /* CA2 (DRQ) indep. input, negative edge */ + via_write(via2, vPCR, 0x22); + } + } + via_write(via2, gIER, irq_bit | 0x80); + } else if (irq_src == 7) { + if (rbv_present) { + /* enable the slot interrupt. SIER works like IER. */ + via_write(via2, rSIER, IER_SET_BIT(irq_idx)); + } else { + /* Make sure the bit is an input, to enable the irq */ + via_write(via2, vDirA, + via_read(via2, vDirA) & ~irq_bit); + } + nubus_active |= irq_bit; + } +} + +void via_irq_disable(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int irq_bit = 1 << irq_idx; + +#ifdef DEBUG_IRQUSE + printk("via_irq_disable(%d)\n", irq); +#endif + + if (irq_src == 1) { + via_write(via1, vIER, irq_bit); + } else if (irq_src == 2) { + via_write(via2, gIER, irq_bit); + } else if (irq_src == 7) { + if (rbv_present) { + /* disable the slot interrupt. SIER works like IER. */ + via_write(via2, rSIER, IER_CLR_BIT(irq_idx)); + } else { + /* disable the nubus irq by changing dir to output */ + via_write(via2, vBufA, ~nubus_active); + via_write(via2, vDirA, via_read(via2, vDirA) | irq_bit); + } + nubus_active &= ~irq_bit; + } +} + +void via_irq_clear(int irq) { + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int irq_bit = 1 << irq_idx; + + if (irq_src == 1) { + via_write(via1, vIFR, irq_bit); + } else if (irq_src == 2) { + via_write(via2, gIFR, irq_bit | rbv_clear); + } else if (irq_src == 7) { + /* FIXME: hmm.. */ + } +} + +/* + * Returns nonzero if an interrupt is pending on the given + * VIA/IRQ combination. + */ + +int via_irq_pending(int irq) +{ + int irq_src = IRQ_SRC(irq); + int irq_idx = IRQ_IDX(irq); + int irq_bit = 1 << irq_idx; + + if (irq_src == 1) { + return via_read(via1, vIFR) & irq_bit; + } else if (irq_src == 2) { + return via_read(via2, gIFR) & irq_bit; + } else if (irq_src == 7) { + return (~via_read(via2, gBufA)) & irq_bit; + } + return 0; +} + +void via_scsi_clear(void) +{ + volatile unsigned char deep_magic; + +#ifdef DEBUG_IRQUSE + printk("via_scsi_clear()\n"); +#endif + + /* We handle this in oss.c , but this gets called in mac_scsinew.c */ + if(oss_present) return; + + if (rbv_present) { + via_write(via2, rIFR, (1<<3) | (1<<0) | rbv_clear); + deep_magic = via_read(via2, rBufB); + } else { + deep_magic = via_read(via2, vBufB); + } + mac_enable_irq((IRQ_IDX(IRQ_MAC_SCSI))); +} + +/* + * PRAM/RTC access routines + * + * Must be called with interrupts disabled and + * the RTC should be enabled. + */ + +static __u8 via_pram_readbyte(void) +{ + int i,reg; + __u8 data; + + reg = via_read(via1, vBufB) & ~VIA1B_vRTCClk; + + /* Set the RTC data line to be an input. */ + + via_write(via1, vDirB, via_read(via1, vDirB) & ~VIA1B_vRTCData); + + /* The bits of the byte come out in MSB order */ + + data = 0; + for (i = 0 ; i < 8 ; i++) { + via_write(via1, vBufB, reg); + via_write(via1, vBufB, reg | VIA1B_vRTCClk); + data = (data << 1) | (via_read(via1, vBufB) & VIA1B_vRTCData); + } + + /* Return RTC data line to output state */ + + via_write(via1, vDirB, via_read(via1, vDirB) | VIA1B_vRTCData); + + return data; +} + +static void via_pram_writebyte(__u8 data) +{ + int i,reg,bit; + + reg = via_read(via1, vBufB) & ~(VIA1B_vRTCClk | VIA1B_vRTCData); + + /* The bits of the byte go in in MSB order */ + + for (i = 0 ; i < 8 ; i++) { + bit = data & 0x80? 1 : 0; + data <<= 1; + via_write(via1, vBufB, reg | bit); + via_write(via1, vBufB, reg | bit | VIA1B_vRTCClk); + } +} + +/* + * Execute a PRAM/RTC command. For read commands + * data should point to a one-byte buffer for the + * resulting data. For write commands it should point + * to the data byte to for the command. + * + * This function disables all interrupts while running. + */ + +void via_pram_command(int command, __u8 *data) +{ + unsigned long cpu_flags; + int is_read,tmp; + + save_flags(cpu_flags); + cli(); + + /* Enable the RTC and make sure the strobe line is high */ + + tmp = (via_read(via1, vBufB) | VIA1B_vRTCClk) & ~VIA1B_vRTCEnb; + via_write(via1, vBufB, tmp); + + if (command & 0xFF00) { /* extended (two-byte) command */ + via_pram_writebyte((command & 0xFF00) >> 8); + via_pram_writebyte(command & 0xFF); + is_read = command & 0x8000; + } else { /* one-byte command */ + via_pram_writebyte(command); + is_read = command & 0x80; + } + if (is_read) { + *data = via_pram_readbyte(); + } else { + via_pram_writebyte(*data); + } + + /* All done, disable the RTC */ + + via_write(via1, vBufB, via_read(via1, vBufB) | VIA1B_vRTCEnb); + + restore_flags(cpu_flags); +} + +/* + * Return the current time in seconds since January 1, 1904. + * + * This only works on machines with the VIA-based PRAM/RTC, which + * is basically any machine with Mac II-style ADB. + */ + +__u32 via_read_time(void) +{ + union { + __u8 cdata[4]; + __u32 idata; + } result, last_result; + int ct; + + /* + * The NetBSD guys say to loop until you get the same reading + * twice in a row. + */ + + ct = 0; + do { + if (++ct > 10) { + printk("via_read_time: couldn't get valid time, " + "last read = 0x%08X and 0x%08X\n", last_result.idata, + result.idata); + break; + } + + last_result.idata = result.idata; + result.idata = 0; + + via_pram_command(0x81, &result.cdata[3]); + via_pram_command(0x85, &result.cdata[2]); + via_pram_command(0x89, &result.cdata[1]); + via_pram_command(0x8D, &result.cdata[0]); + } while (result.idata != last_result.idata); + + return result.idata; +} + +/* + * Set the current time to a number of seconds since January 1, 1904. + * + * This only works on machines with the VIA-based PRAM/RTC, which + * is basically any machine with Mac II-style ADB. + */ + +void via_write_time(__u32 time) +{ + union { + __u8 cdata[4]; + __u32 idata; + } data; + __u8 temp; + + /* Clear the write protect bit */ + + temp = 0x55; + via_pram_command(0x35, &temp); + + data.idata = time; + via_pram_command(0x01, &data.cdata[3]); + via_pram_command(0x05, &data.cdata[2]); + via_pram_command(0x09, &data.cdata[1]); + via_pram_command(0x0D, &data.cdata[0]); + + /* Set the write protect bit */ + + temp = 0xD5; + via_pram_command(0x35, &temp); +} diff --git a/arch/m68k/mac/via6522.c b/arch/m68k/mac/via6522.c deleted file mode 100644 index 05e6f44e4da3..000000000000 --- a/arch/m68k/mac/via6522.c +++ /dev/null @@ -1,419 +0,0 @@ -/* - * 6522 Versatile Interface Adapter (VIA) - * - * There are two of these on the Mac II. Some IRQ's are vectored - * via them as are assorted bits and bobs - eg rtc, adb. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include "via6522.h" -#include - -volatile unsigned char *via1=(unsigned char *)VIABASE; -volatile unsigned char *via2=(unsigned char *)VIABASE2; -volatile unsigned char *psc=(unsigned char *)PSCBASE; - -volatile long *via_memory_bogon=(long *)&via_memory_bogon; - -unsigned char via1_clock, via1_datab; - -static int rbv=0; -static int oss=0; - -extern void adb_interrupt(int slot, void *via, struct pt_regs *regs); - -/* - * hardware reset vector - */ -static void (*rom_reset)(void); - -/* - * Timer defs. - */ -#define MAC_CLOCK_TICK (783300/HZ) /* ticks per HZ */ -#define MAC_CLOCK_LOW (MAC_CLOCK_TICK&0xFF) -#define MAC_CLOCK_HIGH (MAC_CLOCK_TICK>>8) - - -void via_configure_base(void) -{ - - switch(macintosh_config->via_type) - { - /* - * CI, SI, VX, LC - */ - case MAC_VIA_IIci: - via1=(void *)0x50F00000; - via2=(void *)0x50F26000; - rbv=1; - if (macintosh_config->ident == MAC_MODEL_IIFX) { - via2=(void *)0x50F1A000; - oss=1; - } - break; - /* - * Quadra and early MacIIs agree on the VIA locations - */ - case MAC_VIA_QUADRA: - case MAC_VIA_II: - via1=(void *)0x50F00000; - via2=(void *)0x50F02000; - break; - default: - } -} - - -void via_init_clock(void (*func)(int, void *, struct pt_regs *)) -{ - unsigned char c; - - via1_clock=via_read(via1, vACR); - via1_datab=via_read(via1, vBufB); - - /* - * Tell what MacOS left us with - */ - - printk("via_init: boot via1 acr=%X pcr=%X buf_a=%X dir_a=%X buf_b=%X dir_b=%X \n", - (int)via1_clock, (int)via_read(via1, vPCR), - (int)via_read(via1, vBufA), (int)via_read(via1, vDirA), - (int)via_read(via1, vBufB), (int)via_read(via1, vDirB)); - - if (rbv == 0) - printk("via_init: boot via2 acr=%X pcr=%X buf_a=%X dir_a=%X buf_b=%X dir_b=%X \n", - (int)via_read(via2, vACR), (int)via_read(via2, vPCR), - (int)via_read(via2, vBufA), (int)via_read(via2, vDirA), - (int)via_read(via2, vBufB), (int)via_read(via2, vDirB)); - - /* - * Shut it down - */ - - via_write(via1,vIER, 0x7F); - - /* - * Kill the timers - */ - - via_write(via1,vT1LL,0); - via_write(via1,vT1LH,0); - via_write(via1,vT1CL,0); - via_write(via1,vT1CH,0); - via_write(via1,vT2CL,0); - via_write(via1,vT2CH,0); - - /* - * Now do via2 - */ - - if(rbv==0) - { - via_write(via2,vT1LL,0); - via_write(via2,vT1LH,0); - via_write(via2,vT1CL,0); - via_write(via2,vT1CH,0); - via_write(via2,vT2CL,0); - via_write(via2,vT2CH,0); - via_write(via2,vIER, 0x7F); - } - else if (oss==0) - { - /* - * Init the RBV chip a bit - */ - - via_write(via2, rIER,0x7F); - } - - /* - * Disable the timer latches - */ - - c=via_read(via1,vACR); - via_write(via1,vACR,c&0x3F); - - if(rbv==0) - { - c=via_read(via2,vACR); - via_write(via2,vACR,c&0x3F); - } - - /* - * Now start the clock - we want 100Hz - */ - - via_write(via1,vACR,via_read(via1,vACR)|0x40); - - via_write(via1,vT1LL, MAC_CLOCK_LOW); - via_write(via1,vT1LH, MAC_CLOCK_HIGH); - via_write(via1,vT1CL, MAC_CLOCK_LOW); - via_write(via1,vT1CH, MAC_CLOCK_HIGH); - - /* - * And enable its interrupt - */ - - request_irq(IRQ_MAC_TIMER_1, func, IRQ_FLG_LOCK, "timer", func); - - /* - * SE/30: disable video int. - * XXX: testing for SE/30 VBL - */ - - if (macintosh_config->ident == MAC_MODEL_SE30) { - c = via_read(via1, vBufB); - via_write(via1, vBufB, c|(0x40)); - c = via_read(via1, vDirB); - via_write(via1, vDirB, c|(0x40)); - } - -#if 0 /* gone to mac_init_IRQ */ - /* - * Set vPCR for SCSI interrupts. - * - * That is: CA1 negative edge int., CA2 indep., positive edge int.; - * CB1 negative edge int., CB2 indep., positive edge int.. - */ - via_write(via2,vPCR, 0x66); -#endif - -} - -/* - * TBI: get time offset between scheduling timer ticks - */ -#define TICK_SIZE 10000 - -/* This is always executed with interrupts disabled. */ - -unsigned long mac_gettimeoffset (void) -{ - unsigned long ticks, offset = 0; - - /* read VIA1 timer 2 current value */ - ticks = via_read(via1, vT1CL) + (via_read(via1, vT1CH)<<8); - /* The probability of underflow is less than 2% */ - if (ticks > MAC_CLOCK_TICK - MAC_CLOCK_TICK / 50) - /* Check for pending timer interrupt in VIA1 IFR */ - if (via_read(via1, vIFR) & 0x40) - offset = TICK_SIZE; - - ticks = MAC_CLOCK_TICK - ticks; - ticks = ticks * 10000L / MAC_CLOCK_TICK; - - return ticks + offset; -} - -/* - * PSC (AV Macs; level 3-6): initialize interrupt enable registers - */ - -void psc_init(void) -{ - via_write(psc, pIER3, 0x01); - via_write(psc, pIER4, 0x09); - via_write(psc, pIER4, 0x86); - via_write(psc, pIER5, 0x03); - via_write(psc, pIER6, 0x07); -} - -/* - * The power switch - yes it's software! - */ - -void mac_poweroff(void) -{ - - /* - * MAC_ADB_IISI may need to be moved up here if it doesn't actually - * work using the ADB packet method. --David Kilzer - */ - - if (macintosh_config->adb_type == MAC_ADB_II) - { - if(rbv) { - via_write(via2, rBufB, via_read(via2, rBufB)&~0x04); - } else { - /* Direction of vDirB is output */ - via_write(via2,vDirB,via_read(via2,vDirB)|0x04); - /* Send a value of 0 on that line */ - via_write(via2,vBufB,via_read(via2,vBufB)&~0x04); - /* Otherwise it prints "It is now.." then shuts off */ - mdelay(1000); - } - - /* We should never make it this far... */ - printk ("It is now safe to switch off your machine.\n"); - - /* XXX - delay do we need to spin here ? */ - while(1); /* Just in case .. */ - } - - /* - * Initially discovered this technique in the Mach kernel of MkLinux in - * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c. Found equivalent LinuxPPC - * code in arch/ppc/kernel/setup.c, which also has a PMU technique for PowerBooks! - * --David Kilzer - */ - - else if (macintosh_config->adb_type == MAC_ADB_IISI - || macintosh_config->adb_type == MAC_ADB_CUDA) - { - struct adb_request req; - - /* - * Print our "safe" message before we send the request - * just in case the request never returns. - */ - - printk ("It is now safe to switch off your machine.\n"); - - adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); - - printk ("ADB powerdown request sent.\n"); - for (;;) - { - adb_poll(); - } - } -} - -/* - * Not all Macs support software power down; for the rest, just - * try the ROM reset vector ... - */ -void mac_reset(void) -{ - /* - * MAC_ADB_IISI may need to be moved up here if it doesn't actually - * work using the ADB packet method. --David Kilzer - */ - - if (macintosh_config->adb_type == MAC_ADB_II) - { - unsigned long flags; - unsigned long *reset_hook; - - /* need ROMBASE in booter */ - /* indeed, plus need to MAP THE ROM !! */ - - if (mac_bi_data.rombase == 0) - mac_bi_data.rombase = 0x40800000; - - /* works on some */ - rom_reset = (void *) (mac_bi_data.rombase + 0xa); - -#if 0 - /* testing, doesn't work on SE/30 either */ - reset_hook = (unsigned long *) (mac_bi_data.rombase + 0x4); - printk("ROM reset hook: %p\n", *reset_hook); - rom_reset = *reset_hook; -#endif - if (macintosh_config->ident == MAC_MODEL_SE30) { - /* - * MSch: Machines known to crash on ROM reset ... - */ - printk("System halted.\n"); - while(1); - } else { - save_flags(flags); - cli(); - - rom_reset(); - - restore_flags(flags); - } - - /* We never make it this far... it usually panics above. */ - printk ("Restart failed. Please restart manually.\n"); - - /* XXX - delay do we need to spin here ? */ - while(1); /* Just in case .. */ - } - - /* - * Initially discovered this technique in the Mach kernel of MkLinux in - * osfmk/src/mach_kernel/ppc/POWERMAC/cuda_power.c. Found equivalent LinuxPPC - * code in arch/ppc/kernel/setup.c, which also has a PMU technique! - * --David Kilzer - * - * I suspect the MAC_ADB_CUDA code might work with other ADB types of machines - * but have no way to test this myself. --DDK - */ - - else if (macintosh_config->adb_type == MAC_ADB_IISI - || macintosh_config->adb_type == MAC_ADB_CUDA) - { - struct adb_request req; - - adb_request (&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM); - - printk ("Restart failed. Please restart manually.\n"); - for (;;) - { - adb_poll(); - } - } -} - -/* - * Set up the keyboard - */ - -void via_setup_keyboard(void) -{ -#if 0 /* moved to adb */ - via1_func_tab.vector[2]=adb_interrupt; -#else - request_irq(IRQ_MAC_ADB, adb_interrupt, IRQ_FLG_LOCK, "adb interrupt", - adb_interrupt); -#endif -} - -/* - * Floppy hook - */ - -void via1_set_head(int head) -{ - if(head==0) - via_write(via1, vBufA, via_read(via1, vBufA)&~0x20); - else - via_write(via1, vBufA, via_read(via1, vBufA)|0x20); -} - -void nubus_init_via(void) -{ - if (rbv) { - if (oss==0) { - via_write(via2, rBufB, via_read(via2, rBufB)|0x02); - via_write(via2, rIER, 0x82); /* Interrupts on */ - } - } else { - /* Assert the nubus active */ - via_write(via2, vDirB, via_read(via2, vDirB)|0x02); - via_write(via2, vBufB, via_read(via2, vBufB)|0x02); - /* Make the nubus interrupt source register all output (disable) */ - /* via_write(via2, vDirA, 0xFF); */ - via_write(via2, vIER, 0x82); /* Interrupts on */ - } - - printk("nubus_init_via: via1 acr=%X datab=%X pcr=%X\n", - (int)via_read(via1, vACR), (int)via_read(via1, vBufB), - (int)via_read(via1, vPCR)); - - if (rbv==0) - printk("nubus_init_via: via2 acr=%X datab=%X pcr=%X\n", - (int)via_read(via2, vACR), (int)via_read(via2, vBufB), - (int)via_read(via2, vPCR)); -} diff --git a/arch/m68k/mac/via6522.h b/arch/m68k/mac/via6522.h deleted file mode 100644 index 91ba1d58e051..000000000000 --- a/arch/m68k/mac/via6522.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 6522 Versatile Interface Adapter (VIA) - * - * There are two of these on the Mac II. Some IRQ's are vectored - * via them as are assorted bits and bobs - eg rtc, adb. The picture - * is a bit incomplete as the Mac documentation doesnt cover this well - */ - -#ifndef _ASM_VIA6522_H_ -#define _ASM_VIA6522_H_ - -#define VIABASE 0x50F00000 -#define VIABASE2 0x50F02000 - -/* - * Not all of these are true post MacII I think - */ - -#define VIA1A_vSccWrReq 0x80 /* SCC write */ -#define VIA1A_vRev8 0x40 /* Revision 8 board ??? */ -#define VIA1A_vHeadSel 0x20 /* Head select for IWM */ -#define VIA1A_vOverlay 0x10 -#define VIA1A_vSync 0x08 -#define VIA1A_vVolume 0x07 /* Audio volume mask */ - -#define VIA1B_vSound 0x80 /* Audio on/off */ -#define VIA1B_vMystery 0x40 -#define VIA1B_vADBS2 0x20 /* ADB state 2 */ -#define VIA1B_vADBS1 0x10 /* ADB state 1 */ -#define VIA1B_vADBInt 0x08 /* ADB interrupt */ -#define VIA1B_vRTCEnb 0x04 /* Real time clock */ -#define VIA1B_vRTCClk 0x02 -#define VIA1B_vRTCData 0x01 - -/* - * VIA2 A register is the interrupt lines raised off the nubus - * slots. - */ - -#define VIA2A_vIRQE 0x20 -#define VIA2A_vIRQD 0x10 -#define VIA2A_vIRQC 0x08 -#define VIA2A_vIRQB 0x04 -#define VIA2A_vIRQA 0x02 -#define VIA2A_vIRQ9 0x01 - -/* - * Register B has the fun stuff in it - */ - -#define VIA2B_vMode32 0x08 /* 24/32bit switch - doubles as cache flush */ -#define VIA2B_vPower 0x04 /* Off switch */ -#define VIA2B_vBusLk 0x02 /* Nubus in use ?? */ -#define VIA2B_vCDis 0x01 /* Cache disable */ - -/* - * The 6522 via is a 2MHz part, and needs a delay. MacOS seems to - * execute MOV (Ax),(Ax) for this... Oh and we can't use udelay - * here... see we need the via to calibrate the udelay loop ... - */ - -extern volatile long *via_memory_bogon; - -extern __inline__ void via_write(volatile unsigned char *via,int reg, int v) -{ - *via_memory_bogon; - *via_memory_bogon; - *via_memory_bogon; - via[reg]=v; -} - -extern __inline__ int via_read(volatile unsigned char *via,int reg) -{ - *via_memory_bogon; - *via_memory_bogon; - *via_memory_bogon; - return (int)via[reg]; -} - -extern volatile unsigned char *via1,*via2; - -/* - * 6522 registers - see databook - */ - -#define vBufB 0x0000 -#define vBufA 0x0200 -#define vDirB 0x0400 -#define vDirA 0x0600 -#define vT1CL 0x0800 -#define vT1CH 0x0a00 -#define vT1LL 0x0c00 -#define vT1LH 0x0e00 -#define vT2CL 0x1000 -#define vT2CH 0x1200 -#define vSR 0x1400 -#define vACR 0x1600 -#define vPCR 0x1800 -#define vIFR 0x1a00 -#define vIER 0x1c00 -#define vANH 0x1e00 /* register A (no shake) */ - -#define rBufB 0x00 -#define rBufA 0x02 -/*#define rIFR 0x03*/ -#define rIFR 0x1A03 -#define rVideo 0x10 -#define rSlot 0x12 -/*#define rIER 0x13*/ -#define rIER 0x1C13 -/* -#define R_rIFR 0x03 -#define R_rIER 0x13 -#define W_rIFR 0x1A03 -#define W_rIER 0x1C13 -*/ -/* - * VIA interrupt - */ - -struct via_irq_tab -{ - void (*vector[8])(int, void *, struct pt_regs *); -}; - -extern void via1_irq(int, void *, struct pt_regs *); -extern void via2_irq(int, void *, struct pt_regs *); - -extern void via_setup_keyboard(void); - -#endif /* _ASM_VIA6522_H_ */ diff --git a/arch/m68k/math-emu/Makefile b/arch/m68k/math-emu/Makefile new file mode 100644 index 000000000000..fe379cb2c9dd --- /dev/null +++ b/arch/m68k/math-emu/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.o: + $(CC) $(EXTRA_CFLAGS) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +#EXTRA_CFLAGS=-DFPU_EMU_DEBUG + +O_TARGET := mathemu.o +O_OBJS := fp_entry.o fp_scan.o fp_util.o fp_move.o fp_movem.o \ + fp_cond.o fp_arith.o fp_log.o fp_trig.o + +include $(TOPDIR)/Rules.make diff --git a/arch/m68k/math-emu/fp_arith.c b/arch/m68k/math-emu/fp_arith.c new file mode 100644 index 000000000000..c494b1da9c2e --- /dev/null +++ b/arch/m68k/math-emu/fp_arith.c @@ -0,0 +1,700 @@ +/* + + fp_arith.c: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998-1999 David Huggins-Daines. + + Somewhat based on the AlphaLinux floating point emulator, by David + Mosberger-Tang. + + You may copy, modify, and redistribute this file under the terms of + the GNU General Public License, version 2, or any later version, at + your convenience. + */ + +#include "fp_emu.h" +#include "multi_arith.h" +#include "fp_arith.h" + +const struct fp_ext fp_QNaN = +{ + 0, 0, 0x7fff, { ~0 } +}; + +const struct fp_ext fp_Inf = +{ + 0, 0, 0x7fff, { 0 } +}; + +/* let's start with the easy ones */ + +struct fp_ext * +fp_fabs(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fabs\n"); + + fp_monadic_check(dest, src); + + dest->sign = 0; + + return dest; +} + +struct fp_ext * +fp_fneg(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fneg\n"); + + fp_monadic_check(dest, src); + + dest->sign = !dest->sign; + + return dest; +} + +/* Now, the slightly harder ones */ + +/* fp_fadd: Implements the kernel of the FADD, FSADD, FDADD, FSUB, + FDSUB, and FCMP instructions. */ + +struct fp_ext * +fp_fadd(struct fp_ext *dest, struct fp_ext *src) +{ + int diff; + + dprint(PINSTR, "fadd\n"); + + fp_dyadic_check(dest, src); + + if (IS_INF(dest)) { + /* infinity - infinity == NaN */ + if (IS_INF(src) && (src->sign != dest->sign)) + fp_set_nan(dest); + return dest; + } + if (IS_INF(src)) { + fp_copy_ext(dest, src); + return dest; + } + + if (IS_ZERO(dest)) { + if (IS_ZERO(src)) { + if (src->sign != dest->sign) { + if (FPDATA->rnd == FPCR_ROUND_RM) + dest->sign = 1; + else + dest->sign = 0; + } + } else + fp_copy_ext(dest, src); + return dest; + } + + dest->lowmant = src->lowmant = 0; + + if ((diff = dest->exp - src->exp) > 0) + fp_denormalize(src, diff); + else if ((diff = -diff) > 0) + fp_denormalize(dest, diff); + + if (dest->sign == src->sign) { + if (fp_addmant(dest, src)) + if (!fp_addcarry(dest)) + return dest; + } else { + if (dest->mant.m64 < src->mant.m64) { + fp_submant(dest, src, dest); + dest->sign = !dest->sign; + } else + fp_submant(dest, dest, src); + } + + return dest; +} + +/* fp_fsub: Implementes the kernel of the FSUB, FSSUB, and FDSUB + instructions. + + Remember that the arguments are in assembler-syntax order! */ + +struct fp_ext * +fp_fsub(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fsub "); + + src->sign = !src->sign; + return fp_fadd(dest, src); +} + + +struct fp_ext * +fp_fcmp(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fcmp "); + + FPDATA->temp[1] = *dest; + src->sign = !src->sign; + return fp_fadd(&FPDATA->temp[1], src); +} + +struct fp_ext * +fp_ftst(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "ftst\n"); + + (void)dest; + + return src; +} + +struct fp_ext * +fp_fmul(struct fp_ext *dest, struct fp_ext *src) +{ + union fp_mant128 temp; + int exp; + + dprint(PINSTR, "fmul\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + if (IS_ZERO(src)) + fp_set_nan(dest); + return dest; + } + if (IS_INF(src)) { + if (IS_ZERO(dest)) + fp_set_nan(dest); + else + fp_copy_ext(dest, src); + return dest; + } + + /* Of course, as we all know, zero * anything = zero. You may + not have known that it might be a positive or negative + zero... */ + if (IS_ZERO(dest) || IS_ZERO(src)) { + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + exp = dest->exp + src->exp - 0x3ffe; + + /* shift up the mantissa for denormalized numbers, + so that the highest bit is set, this makes the + shift of the result below easier */ + if ((long)dest->mant.m32[0] >= 0) + exp -= fp_overnormalize(dest); + if ((long)src->mant.m32[0] >= 0) + exp -= fp_overnormalize(src); + + /* now, do a 64-bit multiply with expansion */ + fp_multiplymant(&temp, dest, src); + + /* normalize it back to 64 bits and stuff it back into the + destination struct */ + if ((long)temp.m32[0] > 0) { + exp--; + fp_putmant128(dest, &temp, 1); + } else + fp_putmant128(dest, &temp, 0); + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +/* fp_fdiv: Implements the "kernel" of the FDIV, FSDIV, FDDIV and + FSGLDIV instructions. + + Note that the order of the operands is counter-intuitive: instead + of src / dest, the result is actually dest / src. */ + +struct fp_ext * +fp_fdiv(struct fp_ext *dest, struct fp_ext *src) +{ + union fp_mant128 temp; + int exp; + + dprint(PINSTR, "fdiv\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + /* infinity / infinity = NaN (quiet, as always) */ + if (IS_INF(src)) + fp_set_nan(dest); + /* infinity / anything else = infinity (with approprate sign) */ + return dest; + } + if (IS_INF(src)) { + /* anything / infinity = zero (with appropriate sign) */ + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + /* zeroes */ + if (IS_ZERO(dest)) { + /* zero / zero = NaN */ + if (IS_ZERO(src)) + fp_set_nan(dest); + /* zero / anything else = zero */ + return dest; + } + if (IS_ZERO(src)) { + /* anything / zero = infinity (with appropriate sign) */ + fp_set_sr(FPSR_EXC_DZ); + dest->exp = 0x7fff; + dest->mant.m64 = 0; + + return dest; + } + + exp = dest->exp - src->exp + 0x3fff; + + /* shift up the mantissa for denormalized numbers, + so that the highest bit is set, this makes lots + of things below easier */ + if ((long)dest->mant.m32[0] >= 0) + exp -= fp_overnormalize(dest); + if ((long)src->mant.m32[0] >= 0) + exp -= fp_overnormalize(src); + + /* now, do the 64-bit divide */ + fp_dividemant(&temp, dest, src); + + /* normalize it back to 64 bits and stuff it back into the + destination struct */ + if (!temp.m32[0]) { + exp--; + fp_putmant128(dest, &temp, 32); + } else + fp_putmant128(dest, &temp, 31); + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +struct fp_ext * +fp_fsglmul(struct fp_ext *dest, struct fp_ext *src) +{ + int exp; + + dprint(PINSTR, "fsglmul\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + if (IS_ZERO(src)) + fp_set_nan(dest); + return dest; + } + if (IS_INF(src)) { + if (IS_ZERO(dest)) + fp_set_nan(dest); + else + fp_copy_ext(dest, src); + return dest; + } + + /* Of course, as we all know, zero * anything = zero. You may + not have known that it might be a positive or negative + zero... */ + if (IS_ZERO(dest) || IS_ZERO(src)) { + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + exp = dest->exp + src->exp - 0x3ffe; + + /* do a 32-bit multiply */ + fp_mul64(dest->mant.m32[0], dest->mant.m32[1], + dest->mant.m32[0] & 0xffffff00, + src->mant.m32[0] & 0xffffff00); + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +struct fp_ext * +fp_fsgldiv(struct fp_ext *dest, struct fp_ext *src) +{ + int exp; + unsigned long quot, rem; + + dprint(PINSTR, "fsgldiv\n"); + + fp_dyadic_check(dest, src); + + /* calculate the correct sign now, as it's necessary for infinities */ + dest->sign = src->sign ^ dest->sign; + + /* Handle infinities */ + if (IS_INF(dest)) { + /* infinity / infinity = NaN (quiet, as always) */ + if (IS_INF(src)) + fp_set_nan(dest); + /* infinity / anything else = infinity (with approprate sign) */ + return dest; + } + if (IS_INF(src)) { + /* anything / infinity = zero (with appropriate sign) */ + dest->exp = 0; + dest->mant.m64 = 0; + dest->lowmant = 0; + + return dest; + } + + /* zeroes */ + if (IS_ZERO(dest)) { + /* zero / zero = NaN */ + if (IS_ZERO(src)) + fp_set_nan(dest); + /* zero / anything else = zero */ + return dest; + } + if (IS_ZERO(src)) { + /* anything / zero = infinity (with appropriate sign) */ + fp_set_sr(FPSR_EXC_DZ); + dest->exp = 0x7fff; + dest->mant.m64 = 0; + + return dest; + } + + exp = dest->exp - src->exp + 0x3fff; + + dest->mant.m32[0] &= 0xffffff00; + src->mant.m32[0] &= 0xffffff00; + + /* do the 32-bit divide */ + if (dest->mant.m32[0] >= src->mant.m32[0]) { + fp_sub64(dest->mant, src->mant); + fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); + dest->mant.m32[0] = 0x80000000 | (quot >> 1); + dest->mant.m32[1] = (quot & 1) | rem; /* only for rounding */ + } else { + fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); + dest->mant.m32[0] = quot; + dest->mant.m32[1] = rem; /* only for rounding */ + exp--; + } + + if (exp >= 0x7fff) { + fp_set_ovrflw(dest); + return dest; + } + dest->exp = exp; + if (exp < 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -exp); + } + + return dest; +} + +/* fp_roundint: Internal rounding function for use by several of these + emulated instructions. + + This one rounds off the fractional part using the rounding mode + specified. */ + +static void fp_roundint(struct fp_ext *dest, int mode) +{ + union fp_mant64 oldmant; + unsigned long mask; + + if (!fp_normalize_ext(dest)) + return; + + /* infinities and zeroes */ + if (IS_INF(dest) || IS_ZERO(dest)) + return; + + /* first truncate the lower bits */ + oldmant = dest->mant; + switch (dest->exp) { + case 0 ... 0x3ffe: + dest->mant.m64 = 0; + break; + case 0x3fff ... 0x401e: + dest->mant.m32[0] &= 0xffffffffU << (0x401e - dest->exp); + dest->mant.m32[1] = 0; + if (oldmant.m64 == dest->mant.m64) + return; + break; + case 0x401f ... 0x403e: + dest->mant.m32[1] &= 0xffffffffU << (0x403e - dest->exp); + if (oldmant.m32[1] == dest->mant.m32[1]) + return; + break; + default: + return; + } + fp_set_sr(FPSR_EXC_INEX2); + + /* We might want to normalize upwards here... however, since + we know that this is only called on the output of fp_fdiv, + or with the input to fp_fint or fp_fintrz, and the inputs + to all these functions are either normal or denormalized + (no subnormals allowed!), there's really no need. + + In the case of fp_fdiv, observe that 0x80000000 / 0xffff = + 0xffff8000, and the same holds for 128-bit / 64-bit. (i.e. the + smallest possible normal dividend and the largest possible normal + divisor will still produce a normal quotient, therefore, (normal + << 64) / normal is normal in all cases) */ + + switch (mode) { + case FPCR_ROUND_RN: + switch (dest->exp) { + case 0 ... 0x3ffd: + return; + case 0x3ffe: + /* As noted above, the input is always normal, so the + guard bit (bit 63) is always set. therefore, the + only case in which we will NOT round to 1.0 is when + the input is exactly 0.5. */ + if (oldmant.m64 == (1ULL << 63)) + return; + break; + case 0x3fff ... 0x401d: + mask = 1 << (0x401d - dest->exp); + if (!(oldmant.m32[0] & mask)) + return; + if (oldmant.m32[0] & (mask << 1)) + break; + if (!(oldmant.m32[0] << (dest->exp - 0x3ffd)) && + !oldmant.m32[1]) + return; + break; + case 0x401e: + if (!(oldmant.m32[1] >= 0)) + return; + if (oldmant.m32[0] & 1) + break; + if (!(oldmant.m32[1] << 1)) + return; + break; + case 0x401f ... 0x403d: + mask = 1 << (0x403d - dest->exp); + if (!(oldmant.m32[1] & mask)) + return; + if (oldmant.m32[1] & (mask << 1)) + break; + if (!(oldmant.m32[1] << (dest->exp - 0x401d))) + return; + break; + default: + return; + } + break; + case FPCR_ROUND_RZ: + return; + default: + if (dest->sign ^ (mode - FPCR_ROUND_RM)) + break; + return; + } + + switch (dest->exp) { + case 0 ... 0x3ffe: + dest->exp = 0x3fff; + dest->mant.m64 = 1ULL << 63; + break; + case 0x3fff ... 0x401e: + mask = 1 << (0x401e - dest->exp); + if (dest->mant.m32[0] += mask) + break; + dest->mant.m32[0] = 0x80000000; + dest->exp++; + break; + case 0x401f ... 0x403e: + mask = 1 << (0x403e - dest->exp); + if (dest->mant.m32[1] += mask) + break; + if (dest->mant.m32[0] += 1) + break; + dest->mant.m32[0] = 0x80000000; + dest->exp++; + break; + } +} + +/* modrem_kernel: Implementation of the FREM and FMOD instructions + (which are exactly the same, except for the rounding used on the + intermediate value) */ + +static struct fp_ext * +modrem_kernel(struct fp_ext *dest, struct fp_ext *src, int mode) +{ + struct fp_ext tmp; + + fp_dyadic_check(dest, src); + + /* Infinities and zeros */ + if (IS_INF(dest) || IS_ZERO(src)) { + fp_set_nan(dest); + return dest; + } + if (IS_ZERO(dest) || IS_INF(src)) + return dest; + + /* FIXME: there is almost certainly a smarter way to do this */ + fp_copy_ext(&tmp, dest); + fp_fdiv(&tmp, src); /* NOTE: src might be modified */ + fp_roundint(&tmp, mode); + fp_fmul(&tmp, src); + fp_fsub(dest, &tmp); + + /* set the quotient byte */ + fp_set_quotient((dest->mant.m64 & 0x7f) | (dest->sign << 7)); + return dest; +} + +/* fp_fmod: Implements the kernel of the FMOD instruction. + + Again, the argument order is backwards. The result, as defined in + the Motorola manuals, is: + + fmod(src,dest) = (dest - (src * floor(dest / src))) */ + +struct fp_ext * +fp_fmod(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fmod\n"); + return modrem_kernel(dest, src, FPCR_ROUND_RZ); +} + +/* fp_frem: Implements the kernel of the FREM instruction. + + frem(src,dest) = (dest - (src * round(dest / src))) + */ + +struct fp_ext * +fp_frem(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "frem\n"); + return modrem_kernel(dest, src, FPCR_ROUND_RN); +} + +struct fp_ext * +fp_fint(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fint\n"); + + fp_copy_ext(dest, src); + + fp_roundint(dest, FPDATA->rnd); + + return dest; +} + +struct fp_ext * +fp_fintrz(struct fp_ext *dest, struct fp_ext *src) +{ + dprint(PINSTR, "fintrz\n"); + + fp_copy_ext(dest, src); + + fp_roundint(dest, FPCR_ROUND_RZ); + + return dest; +} + +struct fp_ext * +fp_fscale(struct fp_ext *dest, struct fp_ext *src) +{ + int scale, oldround; + + dprint(PINSTR, "fscale\n"); + + fp_dyadic_check(dest, src); + + /* Infinities */ + if (IS_INF(src)) { + fp_set_nan(dest); + return dest; + } + if (IS_INF(dest)) + return dest; + + /* zeroes */ + if (IS_ZERO(src) || IS_ZERO(dest)) + return dest; + + /* Source exponent out of range */ + if (src->exp >= 0x400c) { + fp_set_ovrflw(dest); + return dest; + } + + /* src must be rounded with round to zero. */ + oldround = FPDATA->rnd; + FPDATA->rnd = FPCR_ROUND_RZ; + scale = fp_conv_ext2long(src); + FPDATA->rnd = oldround; + + /* new exponent */ + scale += dest->exp; + + if (scale >= 0x7fff) { + fp_set_ovrflw(dest); + } else if (scale <= 0) { + fp_set_sr(FPSR_EXC_UNFL); + fp_denormalize(dest, -scale); + } else + dest->exp = scale; + + return dest; +} + diff --git a/arch/m68k/math-emu/fp_arith.h b/arch/m68k/math-emu/fp_arith.h new file mode 100644 index 000000000000..2cc3f846c393 --- /dev/null +++ b/arch/m68k/math-emu/fp_arith.h @@ -0,0 +1,52 @@ +/* + + fp_arith.h: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998 David Huggins-Daines. + + Somewhat based on the AlphaLinux floating point emulator, by David + Mosberger-Tang. + + You may copy, modify, and redistribute this file under the terms of + the GNU General Public License, version 2, or any later version, at + your convenience. + + */ + +#ifndef FP_ARITH_H +#define FP_ARITH_H + +/* easy ones */ +struct fp_ext * +fp_fabs(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fneg(struct fp_ext *dest, struct fp_ext *src); + +/* straightforward arithmetic */ +struct fp_ext * +fp_fadd(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fsub(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fcmp(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_ftst(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fmul(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fdiv(struct fp_ext *dest, struct fp_ext *src); + +/* ones that do rounding and integer conversions */ +struct fp_ext * +fp_fmod(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_frem(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fint(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fintrz(struct fp_ext *dest, struct fp_ext *src); +struct fp_ext * +fp_fscale(struct fp_ext *dest, struct fp_ext *src); + +#endif /* FP_ARITH__H */ diff --git a/arch/m68k/math-emu/fp_cond.S b/arch/m68k/math-emu/fp_cond.S new file mode 100644 index 000000000000..d9981d6a833b --- /dev/null +++ b/arch/m68k/math-emu/fp_cond.S @@ -0,0 +1,334 @@ +/* + * fp_cond.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + + .globl fp_fscc, fp_fbccw, fp_fbccl + +#ifdef FPU_EMU_DEBUG +fp_fnop: + printf PDECODE,"fnop\n" + jra fp_end +#else +#define fp_fnop fp_end +#endif + +fp_fbccw: + tst.w %d2 + jeq fp_fnop + printf PDECODE,"fbccw " + fp_get_pc %a0 + lea (-2,%a0,%d2.w),%a0 + jra 1f + +fp_fbccl: + printf PDECODE,"fbccl " + fp_get_pc %a0 + move.l %d2,%d0 + swap %d0 + fp_get_instr_word %d0,fp_err_ua1 + lea (-2,%a0,%d0.l),%a0 +1: printf PDECODE,"%x",1,%a0 + move.l %d2,%d0 + swap %d0 + jsr fp_compute_cond + tst.l %d0 + jeq 1f + fp_put_pc %a0,1 +1: printf PDECODE,"\n" + jra fp_end + +fp_fdbcc: + printf PDECODE,"fdbcc " + fp_get_pc %a1 | calculate new pc + fp_get_instr_word %d0,fp_err_ua1 + add.w %d0,%a1 + fp_decode_addr_reg + printf PDECODE,"d%d,%x\n",2,%d0,%a1 + swap %d1 | test condition in %d1 + tst.w %d1 + jne 2f + move.l %d0,%d1 + jsr fp_get_data_reg + subq.w #1,%d0 + jcs 1f + fp_put_pc %a1,1 +1: jsr fp_put_data_reg +2: jra fp_end + +| set flags for decode macros for fs +do_fscc=1 +do_no_pc_mode=1 + +fp_fscc: + printf PDECODE,"fscc " + move.l %d2,%d0 + jsr fp_compute_cond + move.w %d0,%d1 + swap %d1 + + | decode addressing mode + fp_decode_addr_mode + + .long fp_data, fp_fdbcc + .long fp_indirect, fp_postinc + .long fp_predecr, fp_disp16 + .long fp_extmode0, fp_extmode1 + + | addressing mode: data register direct +fp_data: + fp_mode_data_direct + move.w %d0,%d1 | save register nr + jsr fp_get_data_reg + swap %d1 + move.b %d1,%d0 + swap %d1 + jsr fp_put_data_reg + printf PDECODE,"\n" + jra fp_end + +fp_indirect: + fp_mode_addr_indirect + jra fp_do_scc + +fp_postinc: + fp_mode_addr_indirect_postinc + jra fp_do_scc + +fp_predecr: + fp_mode_addr_indirect_predec + jra fp_do_scc + +fp_disp16: + fp_mode_addr_indirect_disp16 + jra fp_do_scc + +fp_extmode0: + fp_mode_addr_indirect_extmode0 + jra fp_do_scc + +fp_extmode1: + bfextu %d2{#13,#3},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_absolute_short, fp_absolute_long + .long fp_ill, fp_ill | NOTE: jump here to ftrap.x + .long fp_ill, fp_ill + .long fp_ill, fp_ill + +fp_absolute_short: + fp_mode_abs_short + jra fp_do_scc + +fp_absolute_long: + fp_mode_abs_long +| jra fp_do_scc + +fp_do_scc: + swap %d1 + putuser.b %d1,(%a0),fp_err_ua1,%a0 + printf PDECODE,"\n" + jra fp_end + + +#define tst_NAN btst #24,%d1 +#define tst_Z btst #26,%d1 +#define tst_N btst #27,%d1 + +fp_compute_cond: + move.l (FPD_FPSR,FPDATA),%d1 + btst #4,%d0 + jeq 1f + tst_NAN + jeq 1f + bset #15,%d1 + bset #7,%d1 + move.l %d1,(FPD_FPSR,FPDATA) +1: and.w #0xf,%d0 + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_f , fp_eq , fp_ogt, fp_oge + .long fp_olt, fp_ole, fp_ogl, fp_or + .long fp_un , fp_ueq, fp_ugt, fp_uge + .long fp_ult, fp_ule, fp_ne , fp_t + +fp_f: + moveq #0,%d0 + rts + +fp_eq: + moveq #0,%d0 + tst_Z + jeq 1f + moveq #-1,%d0 +1: rts + +fp_ogt: + moveq #0,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jne 1f + moveq #-1,%d0 +1: rts + +fp_oge: + moveq #-1,%d0 + tst_Z + jne 2f + tst_NAN + jne 1f + tst_N + jeq 2f +1: moveq #0,%d0 +2: rts + +fp_olt: + moveq #0,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jeq 1f + moveq #-1,%d0 +1: rts + +fp_ole: + moveq #-1,%d0 + tst_Z + jne 2f + tst_NAN + jne 1f + tst_N + jne 2f +1: moveq #0,%d0 +2: rts + +fp_ogl: + moveq #0,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + moveq #-1,%d0 +1: rts + +fp_or: + moveq #0,%d0 + tst_NAN + jne 1f + moveq #-1,%d0 +1: rts + +fp_un: + moveq #0,%d0 + tst_NAN + jeq 1f + moveq #-1,%d0 + rts + +fp_ueq: + moveq #-1,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + moveq #0,%d0 +1: rts + +fp_ugt: + moveq #-1,%d0 + tst_NAN + jne 2f + tst_N + jne 1f + tst_Z + jeq 2f +1: moveq #0,%d0 +2: rts + +fp_uge: + moveq #-1,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jeq 1f + moveq #0,%d0 +1: rts + +fp_ult: + moveq #-1,%d0 + tst_NAN + jne 2f + tst_Z + jne 1f + tst_N + jne 2f +1: moveq #0,%d0 +2: rts + +fp_ule: + moveq #-1,%d0 + tst_NAN + jne 1f + tst_Z + jne 1f + tst_N + jne 1f + moveq #0,%d0 +1: rts + +fp_ne: + moveq #0,%d0 + tst_Z + jne 1f + moveq #-1,%d0 +1: rts + +fp_t: + moveq #-1,%d0 + rts diff --git a/arch/m68k/math-emu/fp_decode.h b/arch/m68k/math-emu/fp_decode.h new file mode 100644 index 000000000000..233269415ba4 --- /dev/null +++ b/arch/m68k/math-emu/fp_decode.h @@ -0,0 +1,417 @@ +/* + * fp_decode.h + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FP_DECODE_H +#define _FP_DECODE_H + +/* These macros do the dirty work of the instr decoding, several variables + * can be defined in the source file to modify the work of these macros, + * currently the following variables are used: + * ... + * The register usage: + * d0 - will contain source operand for data direct mode, + * otherwise scratch register + * d1 - upper 16bit are reserved for caller + * lower 16bit may contain further arguments, + * is destroyed during decoding + * d2 - contains first two instruction words, + * first word will be used for extension word + * a0 - will point to source/dest operand for any indirect mode + * otherwise scratch register + * a1 - scratch register + * a2 - base addr to the task structure + * + * the current implementation doesn't check for every disallowed + * addressing mode (e.g. pc relative modes as destination), as long + * as it only means a new addressing mode, which should not appear + * in a program and that doesn't crash the emulation, I think it's + * not a problem to allow these modes. + */ + +do_fmovem=0 +do_fmovem_cr=0 +do_no_pc_mode=0 +do_fscc=0 + +| first decoding of the instr type +| this seperates the conditional instr +.macro fp_decode_cond_instr_type + bfextu %d2{#8,#2},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "f","fscc/fdbcc" +| .long "fbccw","fbccl" +.endm + +| second decoding of the instr type +| this seperates most move instr +.macro fp_decode_move_instr_type + bfextu %d2{#16,#3},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "f fpx,fpx","invalid instr" +| .long "f ,fpx","fmove fpx," +| .long "fmovem ,fpcr","fmovem ,fpx" +| .long "fmovem fpcr,","fmovem fpx," +.endm + +| extract the source specifier, specifies +| either source fp register or data format +.macro fp_decode_sourcespec + bfextu %d2{#19,#3},%d0 +.endm + +| decode destination format for fmove reg,ea +.macro fp_decode_dest_format + bfextu %d2{#19,#3},%d0 +.endm + +| decode source register for fmove reg,ea +.macro fp_decode_src_reg + bfextu %d2{#22,#3},%d0 +.endm + +| extract the addressing mode +| it depends on the instr which of the modes is valid +.macro fp_decode_addr_mode + bfextu %d2{#10,#3},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "data register direct","addr register direct" +| .long "addr register indirect" +| .long "addr register indirect postincrement" +| .long "addr register indirect predecrement" +| .long "addr register + index16" +| .long "extension mode1","extension mode2" +.endm + +| extract the register for the addressing mode +.macro fp_decode_addr_reg + bfextu %d2{#13,#3},%d0 +.endm + +| decode the 8bit diplacement from the brief extension word +.macro fp_decode_disp8 + move.b %d2,%d0 + ext.w %d0 +.endm + +| decode the index of the brief/full extension word +.macro fp_decode_index + bfextu %d2{#17,#3},%d0 | get the register nr + btst #15,%d2 | test for data/addr register + jne 1\@f + printf PDECODE,"d%d",1,%d0 + jsr fp_get_data_reg + jra 2\@f +1\@: printf PDECODE,"a%d",1,%d0 + jsr fp_get_addr_reg + move.l %a0,%d0 +2\@: +debug lea "'l'.w,%a0" + btst #11,%d2 | 16/32 bit size? + jne 3\@f +debug lea "'w'.w,%a0" + ext.l %d0 +3\@: printf PDECODE,":%c",1,%a0 + move.w %d2,%d1 | scale factor + rol.w #7,%d1 + and.w #3,%d1 +debug move.l "%d1,-(%sp)" +debug ext.l "%d1" + printf PDECODE,":%d",1,%d1 +debug move.l "(%sp)+,%d1" + lsl.l %d1,%d0 +.endm + +| decode the base displacement size +.macro fp_decode_basedisp + bfextu %d2{#26,#2},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "reserved","null displacement" +| .long "word displacement","long displacement" +.endm + +.macro fp_decode_outerdisp + bfextu %d2{#30,#2},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: +| .long "no memory indirect action/reserved","null outer displacement" +| .long "word outer displacement","long outer displacement" +.endm + +| get the extension word and test for brief or full extension type +.macro fp_get_test_extword label + fp_get_instr_word %d2,fp_err_ua1 + btst #8,%d2 + jne \label +.endm + + +| test if %pc is the base register for the indirect addr mode +.macro fp_test_basereg_d16 label + btst #20,%d2 + jeq \label +.endm + +| test if %pc is the base register for one of the extended modes +.macro fp_test_basereg_ext label + btst #19,%d2 + jeq \label +.endm + +.macro fp_test_suppr_index label + btst #6,%d2 + jne \label +.endm + + +| addressing mode: data register direct +.macro fp_mode_data_direct + fp_decode_addr_reg + printf PDECODE,"d%d",1,%d0 +.endm + +| addressing mode: address register indirect +.macro fp_mode_addr_indirect + fp_decode_addr_reg + printf PDECODE,"(a%d)",1,%d0 + jsr fp_get_addr_reg +.endm + +| adjust stack for byte moves from/to stack +.macro fp_test_sp_byte_move + .if !do_fmovem + .if do_fscc + move.w #6,%d1 + .endif + cmp.w #7,%d0 + jne 1\@f + .if !do_fscc + cmp.w #6,%d1 + jne 1\@f + .endif + move.w #4,%d1 +1\@: + .endif +.endm + +| addressing mode: address register indirect with postincrement +.macro fp_mode_addr_indirect_postinc + fp_decode_addr_reg + printf PDECODE,"(a%d)+",1,%d0 + fp_test_sp_byte_move + jsr fp_get_addr_reg + move.l %a0,%a1 | save addr + .if do_fmovem + lea (%a0,%d1.w*4),%a0 + .if !do_fmovem_cr + lea (%a0,%d1.w*8),%a0 + .endif + .else + add.w (fp_datasize,%d1.w*2),%a0 + .endif + jsr fp_put_addr_reg + move.l %a1,%a0 +.endm + +| addressing mode: address register indirect with predecrement +.macro fp_mode_addr_indirect_predec + fp_decode_addr_reg + printf PDECODE,"-(a%d)",1,%d0 + fp_test_sp_byte_move + jsr fp_get_addr_reg + .if do_fmovem + .if !do_fmovem_cr + lea (-12,%a0),%a1 | setup to addr of 1st reg to move + neg.w %d1 + lea (%a0,%d1.w*4),%a0 + add.w %d1,%d1 + lea (%a0,%d1.w*4),%a0 + jsr fp_put_addr_reg + move.l %a1,%a0 + .else + neg.w %d1 + lea (%a0,%d1.w*4),%a0 + jsr fp_put_addr_reg + .endif + .else + sub.w (fp_datasize,%d1.w*2),%a0 + jsr fp_put_addr_reg + .endif +.endm + +| addressing mode: address register/programm counter indirect +| with 16bit displacement +.macro fp_mode_addr_indirect_disp16 + .if !do_no_pc_mode + fp_test_basereg_d16 1f + printf PDECODE,"pc" + fp_get_pc %a0 + jra 2f + .endif +1: fp_decode_addr_reg + printf PDECODE,"a%d",1,%d0 + jsr fp_get_addr_reg +2: fp_get_instr_word %a1,fp_err_ua1 + printf PDECODE,"@(%x)",1,%a1 + add.l %a1,%a0 +.endm + +| perform preindex (if I/IS == 0xx and xx != 00) +.macro fp_do_preindex + moveq #3,%d0 + and.w %d2,%d0 + jeq 1f + btst #2,%d2 + jne 1f + printf PDECODE,")@(" + getuser.l (%a1),%a1,fp_err_ua1,%a1 +debug jra "2f" +1: printf PDECODE,"," +2: +.endm + +| perform postindex (if I/IS == 1xx) +.macro fp_do_postindex + btst #2,%d2 + jeq 1f + printf PDECODE,")@(" + getuser.l (%a1),%a1,fp_err_ua1,%a1 +debug jra "2f" +1: printf PDECODE,"," +2: +.endm + +| all other indirect addressing modes will finally end up here +.macro fp_mode_addr_indirect_extmode0 + .if !do_no_pc_mode + fp_test_basereg_ext 1f + printf PDECODE,"pc" + fp_get_pc %a0 + jra 2f + .endif +1: fp_decode_addr_reg + printf PDECODE,"a%d",1,%d0 + jsr fp_get_addr_reg +2: move.l %a0,%a1 + swap %d2 + fp_get_test_extword 3f + | addressing mode: address register/programm counter indirect + | with index and 8bit displacement + fp_decode_disp8 +debug ext.l "%d0" + printf PDECODE,"@(%x,",1,%d0 + add.w %d0,%a1 + fp_decode_index + add.l %d0,%a1 + printf PDECODE,")" + jra 9f +3: | addressing mode: address register/programm counter memory indirect + | with base and/or outer displacement + btst #7,%d2 | base register suppressed? + jeq 1f + printf PDECODE,"!" + sub.l %a1,%a1 +1: printf PDECODE,"@(" + fp_decode_basedisp + + .long fp_ill,1f + .long 2f,3f + +#ifdef FPU_EMU_DEBUG +1: printf PDECODE,"0" | null base displacement + jra 1f +#endif +2: fp_get_instr_word %a0,fp_err_ua1 | 16bit base displacement + printf PDECODE,"%x:w",1,%a0 + jra 4f +3: fp_get_instr_long %a0,fp_err_ua1 | 32bit base displacement + printf PDECODE,"%x:l",1,%a0 +4: add.l %a0,%a1 +1: + fp_do_postindex + fp_test_suppr_index 1f + fp_decode_index + add.l %d0,%a1 +1: fp_do_preindex + + fp_decode_outerdisp + + .long 5f,1f + .long 2f,3f + +#ifdef FPU_EMU_DEBUG +1: printf PDECODE,"0" | null outer displacement + jra 1f +#endif +2: fp_get_instr_word %a0,fp_err_ua1 | 16bit outer displacement + printf PDECODE,"%x:w",1,%a0 + jra 4f +3: fp_get_instr_long %a0,fp_err_ua1 | 32bit outer displacement + printf PDECODE,"%x:l",1,%a0 +4: add.l %a0,%a1 +1: +5: printf PDECODE,")" +9: move.l %a1,%a0 + swap %d2 +.endm + +| get the absolute short address from user space +.macro fp_mode_abs_short + fp_get_instr_word %a0,fp_err_ua1 + printf PDECODE,"%x.w",1,%a0 +.endm + +| get the absolute long address from user space +.macro fp_mode_abs_long + fp_get_instr_long %a0,fp_err_ua1 + printf PDECODE,"%x.l",1,%a0 +.endm + +#endif /* _FP_DECODE_H */ diff --git a/arch/m68k/math-emu/fp_emu.h b/arch/m68k/math-emu/fp_emu.h new file mode 100644 index 000000000000..9344b939b09c --- /dev/null +++ b/arch/m68k/math-emu/fp_emu.h @@ -0,0 +1,137 @@ +/* + * fp_emu.h + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FP_EMU_H +#define _FP_EMU_H + +#ifndef __ASSEMBLY__ + +#include + +#define IS_INF(a) ((a)->exp == 0x7fff) +#define IS_ZERO(a) ((a)->mant.m64 == 0) + + +#define fp_set_sr(bit) ({ \ + FPDATA->fpsr |= 1 << (bit); \ +}) + +#define fp_set_quotient(quotient) ({ \ + FPDATA->fpsr &= 0xff00ffff; \ + FPDATA->fpsr |= ((quotient) & 0xff) << 16; \ +}) + +/* linkage for several useful functions */ + +/* Normalize the extended struct, return 0 for a NaN */ +#define fp_normalize_ext(fpreg) ({ \ + register struct fp_ext *reg asm ("a0") = fpreg; \ + register int res asm ("d0"); \ + \ + asm volatile ("jsr fp_conv_ext2ext" \ + : "=d" (res) : "a" (reg) \ + : "a1", "d1", "d2", "memory"); \ + res; \ +}) + +#define fp_copy_ext(dest, src) ({ \ + *dest = *src; \ +}) + +#define fp_monadic_check(dest, src) ({ \ + fp_copy_ext(dest, src); \ + if (!fp_normalize_ext(dest)) \ + return dest; \ +}) + +#define fp_dyadic_check(dest, src) ({ \ + if (!fp_normalize_ext(dest)) \ + return dest; \ + if (!fp_normalize_ext(src)) { \ + fp_copy_ext(dest, src); \ + return dest; \ + } \ +}) + +extern const struct fp_ext fp_QNaN; +extern const struct fp_ext fp_Inf; + +#define fp_set_nan(dest) ({ \ + fp_set_sr(FPSR_EXC_OPERR); \ + *dest = fp_QNaN; \ +}) + +/* TODO check rounding mode? */ +#define fp_set_ovrflw(dest) ({ \ + fp_set_sr(FPSR_EXC_OVFL); \ + dest->exp = 0x7fff; \ + dest->mant.m64 = 0; \ +}) + +#define fp_conv_ext2long(src) ({ \ + register struct fp_ext *__src asm ("a0") = src; \ + register int __res asm ("d0"); \ + \ + asm volatile ("jsr fp_conv_ext2long" \ + : "=d" (__res) : "a" (__src) \ + : "a1", "d1", "d2", "memory"); \ + __res; \ +}) + +#else /* __ASSEMBLY__ */ + +#include "../kernel/m68k_defs.h" +#include + +/* + * set, reset or clear a bit in the fp status register + */ +.macro fp_set_sr bit + bset #(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA) +.endm + +.macro fp_clr_sr bit + bclr #(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA) +.endm + +.macro fp_tst_sr bit + btst #(\bit&7),(FPD_FPSR+3-(\bit/8),FPDATA) +.endm + +#endif /* __ASSEMBLY__ */ + +#endif /* _FP_EMU_H */ diff --git a/arch/m68k/math-emu/fp_entry.S b/arch/m68k/math-emu/fp_entry.S new file mode 100644 index 000000000000..f2e699c8c289 --- /dev/null +++ b/arch/m68k/math-emu/fp_entry.S @@ -0,0 +1,324 @@ +/* + * fp_emu.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "fp_emu.h" + + .globl SYMBOL_NAME(fpu_emu) + .globl fp_debugprint + .globl fp_err_ua1,fp_err_ua2 + + .text +SYMBOL_NAME_LABEL(fpu_emu) + SAVE_ALL_INT + GET_CURRENT(%d0) + +#if defined(CPU_M68020_OR_M68030) && defined(CPU_M68040_OR_M68060) + tst.l SYMBOL_NAME(m68k_is040or060) + jeq 1f +#endif +#if defined(CPU_M68040_OR_M68060) + move.l (FPS_PC2,%sp),(FPS_PC,%sp) +#endif +1: + | emulate the instruction + jsr fp_scan + +#if defined(CONFIG_M68060) +#if !defined(CPU_M68060_ONLY) + btst #3,SYMBOL_NAME(m68k_cputype)+3 + jeq 1f +#endif + btst #7,(FPS_SR,%sp) + jne fp_sendtrace060 +#endif +1: + | emulation successful? + tst.l %d0 + jeq SYMBOL_NAME(ret_from_exception) + + | send some signal to program here + + jra SYMBOL_NAME(ret_from_exception) + + | we jump here after an access error while trying to access + | user space, we correct stackpointer and send a SIGSEGV to + | the user process +fp_err_ua2: + addq.l #4,%sp +fp_err_ua1: + addq.l #4,%sp + move.l %a0,-(%sp) + pea SEGV_MAPERR + pea SIGSEGV + jsr SYMBOL_NAME(fpemu_signal) + add.w #12,%sp + jra SYMBOL_NAME(ret_from_exception) + +#if defined(CONFIG_M68060) + | send a trace signal if we are debugged + | it does not really belong here, but... +fp_sendtrace060: + move.l (FPS_PC,%sp),-(%sp) + pea TRAP_TRACE + pea SIGTRAP + jsr SYMBOL_NAME(fpemu_signal) + add.w #12,%sp + jra SYMBOL_NAME(ret_from_exception) +#endif + + .globl fp_get_data_reg, fp_put_data_reg + .globl fp_get_addr_reg, fp_put_addr_reg + + | Entry points to get/put a register. Some of them can be get/put + | directly, others are on the stack, as we read/write the stack + | directly here, these function may only be called from within + | instruction decoding, otherwise the stack pointer is incorrect + | and the stack gets corrupted. +fp_get_data_reg: + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_get_d0, fp_get_d1 + .long fp_get_d2, fp_get_d3 + .long fp_get_d4, fp_get_d5 + .long fp_get_d6, fp_get_d7 + +fp_get_d0: + move.l (PT_D0+8,%sp),%d0 + printf PREGISTER,"{d0->%08x}",1,%d0 + rts + +fp_get_d1: + move.l (PT_D1+8,%sp),%d0 + printf PREGISTER,"{d1->%08x}",1,%d0 + rts + +fp_get_d2: + move.l (PT_D2+8,%sp),%d0 + printf PREGISTER,"{d2->%08x}",1,%d0 + rts + +fp_get_d3: + move.l %d3,%d0 + printf PREGISTER,"{d3->%08x}",1,%d0 + rts + +fp_get_d4: + move.l %d4,%d0 + printf PREGISTER,"{d4->%08x}",1,%d0 + rts + +fp_get_d5: + move.l %d5,%d0 + printf PREGISTER,"{d5->%08x}",1,%d0 + rts + +fp_get_d6: + move.l %d6,%d0 + printf PREGISTER,"{d6->%08x}",1,%d0 + rts + +fp_get_d7: + move.l %d7,%d0 + printf PREGISTER,"{d7->%08x}",1,%d0 + rts + +fp_put_data_reg: + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: + .long fp_put_d0, fp_put_d1 + .long fp_put_d2, fp_put_d3 + .long fp_put_d4, fp_put_d5 + .long fp_put_d6, fp_put_d7 + +fp_put_d0: + printf PREGISTER,"{d0<-%08x}",1,%d0 + move.l %d0,(PT_D0+8,%sp) + rts + +fp_put_d1: + printf PREGISTER,"{d1<-%08x}",1,%d0 + move.l %d0,(PT_D1+8,%sp) + rts + +fp_put_d2: + printf PREGISTER,"{d2<-%08x}",1,%d0 + move.l %d0,(PT_D2+8,%sp) + rts + +fp_put_d3: + printf PREGISTER,"{d3<-%08x}",1,%d0 +| move.l %d0,%d3 + move.l %d0,(PT_D3+8,%sp) + rts + +fp_put_d4: + printf PREGISTER,"{d4<-%08x}",1,%d0 +| move.l %d0,%d4 + move.l %d0,(PT_D4+8,%sp) + rts + +fp_put_d5: + printf PREGISTER,"{d5<-%08x}",1,%d0 +| move.l %d0,%d5 + move.l %d0,(PT_D5+8,%sp) + rts + +fp_put_d6: + printf PREGISTER,"{d6<-%08x}",1,%d0 + move.l %d0,%d6 + rts + +fp_put_d7: + printf PREGISTER,"{d7<-%08x}",1,%d0 + move.l %d0,%d7 + rts + +fp_get_addr_reg: + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_get_a0, fp_get_a1 + .long fp_get_a2, fp_get_a3 + .long fp_get_a4, fp_get_a5 + .long fp_get_a6, fp_get_a7 + +fp_get_a0: + move.l (PT_A0+8,%sp),%a0 + printf PREGISTER,"{a0->%08x}",1,%a0 + rts + +fp_get_a1: + move.l (PT_A1+8,%sp),%a0 + printf PREGISTER,"{a1->%08x}",1,%a0 + rts + +fp_get_a2: + move.l (PT_A2+8,%sp),%a0 + printf PREGISTER,"{a2->%08x}",1,%a0 + rts + +fp_get_a3: + move.l %a3,%a0 + printf PREGISTER,"{a3->%08x}",1,%a0 + rts + +fp_get_a4: + move.l %a4,%a0 + printf PREGISTER,"{a4->%08x}",1,%a0 + rts + +fp_get_a5: + move.l %a5,%a0 + printf PREGISTER,"{a5->%08x}",1,%a0 + rts + +fp_get_a6: + move.l %a6,%a0 + printf PREGISTER,"{a6->%08x}",1,%a0 + rts + +fp_get_a7: + move.l %usp,%a0 + printf PREGISTER,"{a7->%08x}",1,%a0 + rts + +fp_put_addr_reg: + jmp ([0f:w,%pc,%d0.w*4]) + + .align 4 +0: + .long fp_put_a0, fp_put_a1 + .long fp_put_a2, fp_put_a3 + .long fp_put_a4, fp_put_a5 + .long fp_put_a6, fp_put_a7 + +fp_put_a0: + printf PREGISTER,"{a0<-%08x}",1,%a0 + move.l %a0,(PT_A0+8,%sp) + rts + +fp_put_a1: + printf PREGISTER,"{a1<-%08x}",1,%a0 + move.l %a0,(PT_A1+8,%sp) + rts + +fp_put_a2: + printf PREGISTER,"{a2<-%08x}",1,%a0 + move.l %a0,(PT_A2+8,%sp) + rts + +fp_put_a3: + printf PREGISTER,"{a3<-%08x}",1,%a0 + move.l %a0,%a3 + rts + +fp_put_a4: + printf PREGISTER,"{a4<-%08x}",1,%a0 + move.l %a0,%a4 + rts + +fp_put_a5: + printf PREGISTER,"{a5<-%08x}",1,%a0 + move.l %a0,%a5 + rts + +fp_put_a6: + printf PREGISTER,"{a6<-%08x}",1,%a0 + move.l %a0,%a6 + rts + +fp_put_a7: + printf PREGISTER,"{a7<-%08x}",1,%a0 + move.l %a0,%usp + rts + + .data + .align 4 + +fp_debugprint: +| .long PMDECODE + .long PMINSTR+PMDECODE+PMCONV+PMNORM +| .long PMCONV+PMNORM+PMINSTR +| .long 0 diff --git a/arch/m68k/math-emu/fp_log.c b/arch/m68k/math-emu/fp_log.c new file mode 100644 index 000000000000..8e1c96420ec0 --- /dev/null +++ b/arch/m68k/math-emu/fp_log.c @@ -0,0 +1,142 @@ +/* + + fp_trig.c: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998-1999 David Huggins-Daines / Roman Zippel. + + I hereby give permission, free of charge, to copy, modify, and + redistribute this software, in source or binary form, provided that + the above copyright notice and the following disclaimer are included + in all such copies. + + THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL + OR IMPLIED. + +*/ + +#include "fp_emu.h" + +struct fp_ext * +fp_fsqrt(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsqrt\n"); + + fp_monadic_check(dest, src); + + if (IS_ZERO(dest)) + return dest; + + if (dest->sign) { + fp_set_nan(dest); + return dest; + } + if (IS_INF(dest)) + return dest; + + return dest; +} + +struct fp_ext * +fp_fetoxm1(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fetoxm1\n"); + + fp_monadic_check(dest, src); + + if (IS_ZERO(dest)) + return dest; + + return dest; +} + +struct fp_ext * +fp_fetox(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fetox\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftwotox(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftwotox\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftentox(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftentox\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flogn(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flogn\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flognp1(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flognp1\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flog10(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flog10\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_flog2(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("flog2\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fgetexp(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fgetexp\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fgetman(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fgetman\n"); + + fp_monadic_check(dest, src); + + return dest; +} + diff --git a/arch/m68k/math-emu/fp_move.S b/arch/m68k/math-emu/fp_move.S new file mode 100644 index 000000000000..45fb02bfe934 --- /dev/null +++ b/arch/m68k/math-emu/fp_move.S @@ -0,0 +1,244 @@ +/* + * fp_move.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + +do_no_pc_mode=1 + + .globl fp_fmove_fp2mem + +fp_fmove_fp2mem: + clr.b (2+FPD_FPSR,FPDATA) + fp_decode_dest_format + move.w %d0,%d1 | store data size twice in %d1 + swap %d1 | one can be trashed below + move.w %d0,%d1 +#ifdef FPU_EMU_DEBUG + lea 0f,%a0 + clr.l %d0 + move.b (%a0,%d1.w),%d0 + printf PDECODE,"fmove.%c ",1,%d0 + fp_decode_src_reg + printf PDECODE,"fp%d,",1,%d0 + + .data +0: .byte 'l','s','x','p','w','d','b','p' + .previous +#endif + + | encode addressing mode for dest + fp_decode_addr_mode + + .long fp_data, fp_ill + .long fp_indirect, fp_postinc + .long fp_predecr, fp_disp16 + .long fp_extmode0, fp_extmode1 + + | addressing mode: data register direct +fp_data: + fp_mode_data_direct + move.w %d0,%d1 + fp_decode_src_reg + fp_get_fp_reg + lea (FPD_TEMPFP1,FPDATA),%a1 + move.l (%a0)+,(%a1)+ + move.l (%a0)+,(%a1)+ + move.l (%a0),(%a1) + lea (-8,%a1),%a0 + swap %d1 + move.l %d1,%d2 + printf PDECODE,"\n" + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: + .long fp_data_long, fp_data_single + .long fp_ill, fp_ill + .long fp_data_word, fp_ill + .long fp_data_byte, fp_ill + +fp_data_byte: + jsr fp_normalize_ext + jsr fp_conv_ext2byte + move.l %d0,%d1 + swap %d2 + move.w %d2,%d0 + jsr fp_get_data_reg + move.b %d1,%d0 + move.w %d2,%d1 + jsr fp_put_data_reg + jra fp_final + +fp_data_word: + jsr fp_normalize_ext + jsr fp_conv_ext2short + move.l %d0,%d1 + swap %d2 + move.w %d2,%d0 + jsr fp_get_data_reg + move.w %d1,%d0 + move.l %d2,%d1 + jsr fp_put_data_reg + jra fp_final + +fp_data_long: + jsr fp_normalize_ext + jsr fp_conv_ext2long + swap %d2 + move.w %d2,%d1 + jsr fp_put_data_reg + jra fp_final + +fp_data_single: + jsr fp_normalize_ext + jsr fp_conv_ext2single + swap %d2 + move.w %d2,%d1 + jsr fp_put_data_reg + jra fp_final + + | addressing mode: address register indirect +fp_indirect: + fp_mode_addr_indirect + jra fp_putdest + + | addressing mode: address register indirect with postincrement +fp_postinc: + fp_mode_addr_indirect_postinc + jra fp_putdest + + | addressing mode: address register indirect with predecrement +fp_predecr: + fp_mode_addr_indirect_predec + jra fp_putdest + + | addressing mode: address register indirect with 16bit displacement +fp_disp16: + fp_mode_addr_indirect_disp16 + jra fp_putdest + +fp_extmode0: + fp_mode_addr_indirect_extmode0 + jra fp_putdest + +fp_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_abs_short, fp_abs_long + .long fp_ill, fp_ill + .long fp_ill, fp_ill + .long fp_ill, fp_ill + +fp_abs_short: + fp_mode_abs_short + jra fp_putdest + +fp_abs_long: + fp_mode_abs_long + jra fp_putdest + +fp_putdest: + move.l %a0,%a1 + fp_decode_src_reg + move.l %d1,%d2 | save size + fp_get_fp_reg + printf PDECODE,"\n" + addq.l #8,%a0 + move.l (%a0),-(%sp) + move.l -(%a0),-(%sp) + move.l -(%a0),-(%sp) + move.l %sp,%a0 + jsr fp_normalize_ext + + swap %d2 + jmp ([0f:w,%pc,%d2.w*4]) + + .align 4 +0: + .long fp_format_long, fp_format_single + .long fp_format_extended, fp_format_packed + .long fp_format_word, fp_format_double + .long fp_format_byte, fp_format_packed + +fp_format_long: + jsr fp_conv_ext2long + putuser.l %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_single: + jsr fp_conv_ext2single + putuser.l %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_extended: + move.l (%a0)+,%d0 + lsl.w #1,%d0 + lsl.l #7,%d0 + lsl.l #8,%d0 + putuser.l %d0,(%a1)+,fp_err_ua1,%a1 + move.l (%a0)+,%d0 + putuser.l %d0,(%a1)+,fp_err_ua1,%a1 + move.l (%a0),%d0 + putuser.l %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_packed: + /* not supported yet */ + lea (12,%sp),%sp + jra fp_ill + +fp_format_word: + jsr fp_conv_ext2short + putuser.w %d0,(%a1),fp_err_ua1,%a1 + jra fp_finish_move + +fp_format_double: + jsr fp_conv_ext2double + jra fp_finish_move + +fp_format_byte: + jsr fp_conv_ext2byte + putuser.b %d0,(%a1),fp_err_ua1,%a1 +| jra fp_finish_move + +fp_finish_move: + lea (12,%sp),%sp + jra fp_final diff --git a/arch/m68k/math-emu/fp_movem.S b/arch/m68k/math-emu/fp_movem.S new file mode 100644 index 000000000000..01058b33bd91 --- /dev/null +++ b/arch/m68k/math-emu/fp_movem.S @@ -0,0 +1,368 @@ +/* + * fp_movem.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + +| set flags for decode macros for fmovem +do_fmovem=1 + + .globl fp_fmovem_fp, fp_fmovem_cr + +| %d1 contains the mask and count of the register list +| for other register usage see fp_decode.h + +fp_fmovem_fp: + printf PDECODE,"fmovem.x " + | get register list and count them + btst #11,%d2 + jne 1f + bfextu %d2{#24,#8},%d0 | static register list + jra 2f +1: bfextu %d2{#25,#3},%d0 | dynamic register list + jsr fp_get_data_reg +2: move.l %d0,%d1 + swap %d1 + jra 2f +1: addq.w #1,%d1 | count the # of registers in +2: lsr.b #1,%d0 | register list and keep it in %d1 + jcs 1b + jne 2b + printf PDECODE,"#%08x",1,%d1 +#ifdef FPU_EMU_DEBUG + btst #12,%d2 + jne 1f + printf PDECODE,"-" | decremental move + jra 2f +1: printf PDECODE,"+" | incremental move +2: btst #13,%d2 + jeq 1f + printf PDECODE,"->" | fpu -> cpu + jra 2f +1: printf PDECODE,"<-" | fpu <- cpu +2: +#endif + + | decode address mode + fp_decode_addr_mode + + .long fp_ill, fp_ill + .long fpr_indirect, fpr_postinc + .long fpr_predecr, fpr_disp16 + .long fpr_extmode0, fpr_extmode1 + + | addressing mode: address register indirect +fpr_indirect: + fp_mode_addr_indirect + jra fpr_do_movem + + | addressing mode: address register indirect with postincrement +fpr_postinc: + fp_mode_addr_indirect_postinc + jra fpr_do_movem + +fpr_predecr: + fp_mode_addr_indirect_predec + jra fpr_do_movem + + | addressing mode: address register/programm counter indirect + | with 16bit displacement +fpr_disp16: + fp_mode_addr_indirect_disp16 + jra fpr_do_movem + +fpr_extmode0: + fp_mode_addr_indirect_extmode0 + jra fpr_do_movem + +fpr_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fpr_absolute_short, fpr_absolute_long + .long fpr_disp16, fpr_extmode0 + .long fp_ill, fp_ill + .long fp_ill, fp_ill + +fpr_absolute_short: + fp_mode_abs_short + jra fpr_do_movem + +fpr_absolute_long: + fp_mode_abs_long +| jra fpr_do_movem + +fpr_do_movem: + swap %d1 | get fpu register list + lea (FPD_FPREG,FPDATA),%a1 + moveq #12,%d0 + btst #12,%d2 + jne 1f + lea (-12,%a1,%d0*8),%a1 + neg.l %d0 +1: btst #13,%d2 + jne 4f + | move register from memory into fpu + jra 3f +1: printf PMOVEM,"(%p>%p)",2,%a0,%a1 + getuser.l (%a0)+,%d2,fp_err_ua1,%a0 + lsr.l #8,%d2 + lsr.l #7,%d2 + lsr.w #1,%d2 + move.l %d2,(%a1)+ + getuser.l (%a0)+,%d2,fp_err_ua1,%a0 + move.l %d2,(%a1)+ + getuser.l (%a0),%d2,fp_err_ua1,%a0 + move.l %d2,(%a1) + subq.l #8,%a0 + subq.l #8,%a1 + add.l %d0,%a0 +2: add.l %d0,%a1 +3: lsl.b #1,%d1 + jcs 1b + jne 2b + jra 5f + | move register from fpu into memory +1: printf PMOVEM,"(%p>%p)",2,%a1,%a0 + move.l (%a1)+,%d2 + lsl.w #1,%d2 + lsl.l #7,%d2 + lsl.l #8,%d2 + putuser.l %d2,(%a0)+,fp_err_ua1,%a0 + move.l (%a1)+,%d2 + putuser.l %d2,(%a0)+,fp_err_ua1,%a0 + move.l (%a1),%d2 + putuser.l %d2,(%a0),fp_err_ua1,%a0 + subq.l #8,%a1 + subq.l #8,%a0 + add.l %d0,%a0 +2: add.l %d0,%a1 +4: lsl.b #1,%d1 + jcs 1b + jne 2b +5: + printf PDECODE,"\n" +#if 0 + lea (FPD_FPREG,FPDATA),%a0 + printf PMOVEM,"fp:" + printx PMOVEM,%a0@(0) + printx PMOVEM,%a0@(12) + printf PMOVEM,"\n " + printx PMOVEM,%a0@(24) + printx PMOVEM,%a0@(36) + printf PMOVEM,"\n " + printx PMOVEM,%a0@(48) + printx PMOVEM,%a0@(60) + printf PMOVEM,"\n " + printx PMOVEM,%a0@(72) + printx PMOVEM,%a0@(84) + printf PMOVEM,"\n" +#endif + jra fp_end + +| set flags for decode macros for fmovem control register +do_fmovem=1 +do_fmovem_cr=1 + +fp_fmovem_cr: + printf PDECODE,"fmovem.cr " + | get register list and count them + bfextu %d2{#19,#3},%d0 + move.l %d0,%d1 + swap %d1 + jra 2f +1: addq.w #1,%d1 +2: lsr.l #1,%d0 + jcs 1b + jne 2b + printf PDECODE,"#%08x",1,%d1 +#ifdef FPU_EMU_DEBUG + btst #13,%d2 + jeq 1f + printf PDECODE,"->" | fpu -> cpu + jra 2f +1: printf PDECODE,"<-" | fpu <- cpu +2: +#endif + + | decode address mode + fp_decode_addr_mode + + .long fpc_data, fpc_addr + .long fpc_indirect, fpc_postinc + .long fpc_predecr, fpc_disp16 + .long fpc_extmode0, fpc_extmode1 + +fpc_data: + fp_mode_data_direct + move.w %d0,%d1 + bfffo %d2{#19,#3},%d0 + sub.w #19,%d0 + lea (FPD_FPCR,FPDATA,%d0.w*4),%a1 + btst #13,%d2 + jne 1f + move.w %d1,%d0 + jsr fp_get_data_reg + move.l %d0,(%a1) + jra fpc_movem_fin +1: move.l (%a1),%d0 + jsr fp_put_data_reg + jra fpc_movem_fin + +fpc_addr: + fp_decode_addr_reg + printf PDECODE,"a%d",1,%d0 + btst #13,%d2 + jne 1f + jsr fp_get_addr_reg + move.l %a0,(FPD_FPIAR,FPDATA) + jra fpc_movem_fin +1: move.l (FPD_FPIAR,FPDATA),%a0 + jsr fp_put_addr_reg + jra fpc_movem_fin + +fpc_indirect: + fp_mode_addr_indirect + jra fpc_do_movem + +fpc_postinc: + fp_mode_addr_indirect_postinc + jra fpc_do_movem + +fpc_predecr: + fp_mode_addr_indirect_predec + jra fpc_do_movem + +fpc_disp16: + fp_mode_addr_indirect_disp16 + jra fpc_do_movem + +fpc_extmode0: + fp_mode_addr_indirect_extmode0 + jra fpc_do_movem + +fpc_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fpc_absolute_short, fpc_absolute_long + .long fpc_disp16, fpc_extmode0 + .long fpc_immediate, fp_ill + .long fp_ill, fp_ill + +fpc_absolute_short: + fp_mode_abs_short + jra fpc_do_movem + +fpc_absolute_long: + fp_mode_abs_long + jra fpc_do_movem + +fpc_immediate: + fp_get_pc %a0 + lea (%a0,%d1.w*4),%a1 + fp_put_pc %a1 + printf PDECODE,"#imm" +| jra fpc_do_movem +#if 0 + swap %d1 + lsl.l #5,%d1 + lea (FPD_FPCR,FPDATA),%a0 + jra 3f +1: move.l %d0,(%a0) +2: addq.l #4,%a0 +3: lsl.b #1,%d1 + jcs 1b + jne 2b + jra fpc_movem_fin +#endif + +fpc_do_movem: + swap %d1 | get fpu register list + lsl.l #5,%d1 + lea (FPD_FPCR,FPDATA),%a1 +1: btst #13,%d2 + jne 4f + + | move register from memory into fpu + jra 3f +1: printf PMOVEM,"(%p>%p)",2,%a0,%a1 + getuser.l (%a0)+,%d0,fp_err_ua1,%a0 + move.l %d0,(%a1) +2: addq.l #4,%a1 +3: lsl.b #1,%d1 + jcs 1b + jne 2b + jra fpc_movem_fin + + | move register from fpu into memory +1: printf PMOVEM,"(%p>%p)",2,%a1,%a0 + move.l (%a1),%d0 + putuser.l %d0,(%a0)+,fp_err_ua1,%a0 +2: addq.l #4,%a1 +4: lsl.b #1,%d1 + jcs 1b + jne 2b + +fpc_movem_fin: + and.l #0x0000fff0,(FPD_FPCR,FPDATA) + and.l #0x0ffffff8,(FPD_FPSR,FPDATA) + move.l (FPD_FPCR,FPDATA),%d0 + lsr.l #4,%d0 + moveq #3,%d1 + and.l %d0,%d1 + move.w %d1,(FPD_RND,FPDATA) + lsr.l #2,%d0 + moveq #3,%d1 + and.l %d0,%d1 + move.w %d1,(FPD_PREC,FPDATA) + printf PDECODE,"\n" +#if 0 + printf PMOVEM,"fpcr : %08x\n",1,FPDATA@(FPD_FPCR) + printf PMOVEM,"fpsr : %08x\n",1,FPDATA@(FPD_FPSR) + printf PMOVEM,"fpiar: %08x\n",1,FPDATA@(FPD_FPIAR) + clr.l %d0 + move.w (FPD_PREC,FPDATA),%d0 + printf PMOVEM,"prec : %04x\n",1,%d0 + move.w (FPD_RND,FPDATA),%d0 + printf PMOVEM,"rnd : %04x\n",1,%d0 +#endif + jra fp_end diff --git a/arch/m68k/math-emu/fp_scan.S b/arch/m68k/math-emu/fp_scan.S new file mode 100644 index 000000000000..4f404914c3b5 --- /dev/null +++ b/arch/m68k/math-emu/fp_scan.S @@ -0,0 +1,478 @@ +/* + * fp_scan.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" +#include "fp_decode.h" + + .globl fp_scan, fp_datasize + + .data + +| %d2 - first two instr words +| %d1 - operand size + +/* operand formats are: + + Long = 0, i.e. fmove.l + Single, i.e. fmove.s + Extended, i.e. fmove.x + Packed-BCD, i.e. fmove.p + Word, i.e. fmove.w + Double, i.e. fmove.d +*/ + + .text + +| On entry: +| FPDATA - base of emulated FPU registers + +fp_scan: +| normal fpu instruction? (this excludes fsave/frestore) + fp_get_pc %a0 + printf PDECODE,"%08x: ",1,%a0 + getuser.b (%a0),%d0,fp_err_ua1,%a0 +#if 1 + cmp.b #0xf2,%d0 | cpid = 1 +#else + cmp.b #0xfc,%d0 | cpid = 6 +#endif + jne fp_nonstd +| first two instruction words are kept in %d2 + getuser.l (%a0)+,%d2,fp_err_ua1,%a0 + fp_put_pc %a0 +fp_decode_cond: | seperate conditional instr + fp_decode_cond_instr_type + + .long fp_decode_move, fp_fscc + .long fp_fbccw, fp_fbccl + +fp_decode_move: | seperate move instr + fp_decode_move_instr_type + + .long fp_fgen_fp, fp_ill + .long fp_fgen_ea, fp_fmove_fp2mem + .long fp_fmovem_cr, fp_fmovem_cr + .long fp_fmovem_fp, fp_fmovem_fp + +| now all arithmetic instr and a few move instr are left +fp_fgen_fp: | source is a fpu register + clr.b (FPD_FPSR+2,FPDATA) | clear the exception byte + fp_decode_sourcespec + printf PDECODE,"f.x fp%d",1,%d0 + fp_get_fp_reg + lea (FPD_TEMPFP1,FPDATA),%a1 | copy src into a temp location + move.l (%a0)+,(%a1)+ + move.l (%a0)+,(%a1)+ + move.l (%a0),(%a1) + lea (-8,%a1),%a0 + jra fp_getdest + +fp_fgen_ea: | source is + clr.b (FPD_FPSR+2,FPDATA) | clear the exception byte + | sort out fmovecr, keep data size in %d1 + fp_decode_sourcespec + cmp.w #7,%d0 + jeq fp_fmovecr + move.w %d0,%d1 | store data size twice in %d1 + swap %d1 | one can be trashed below + move.w %d0,%d1 +#ifdef FPU_EMU_DEBUG + lea 0f,%a0 + clr.l %d0 + move.b (%a0,%d1.w),%d0 + printf PDECODE,"f.%c ",1,%d0 + + .data +0: .byte 'l','s','x','p','w','d','b',0 + .previous +#endif + +/* + fp_getsource, fp_getdest + + basically, we end up with a pointer to the source operand in + %a1, and a pointer to the destination operand in %a0. both + are, of course, 96-bit extended floating point numbers. +*/ + +fp_getsource: + | decode addressing mode for source + fp_decode_addr_mode + + .long fp_data, fp_ill + .long fp_indirect, fp_postinc + .long fp_predecr, fp_disp16 + .long fp_extmode0, fp_extmode1 + + | addressing mode: data register direct +fp_data: + fp_mode_data_direct + jsr fp_get_data_reg + lea (FPD_TEMPFP1,FPDATA),%a0 + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: + .long fp_data_long, fp_data_single + .long fp_ill, fp_ill + .long fp_data_word, fp_ill + .long fp_data_byte, fp_ill + + | data types that fit in an integer data register +fp_data_byte: + extb.l %d0 + jra fp_data_long + +fp_data_word: + ext.l %d0 + +fp_data_long: + jsr fp_conv_long2ext + jra fp_getdest + +fp_data_single: + jsr fp_conv_single2ext + jra fp_getdest + + | addressing mode: address register indirect +fp_indirect: + fp_mode_addr_indirect + jra fp_fetchsource + + | addressing mode: address register indirect with postincrement +fp_postinc: + fp_mode_addr_indirect_postinc + jra fp_fetchsource + + | addressing mode: address register indirect with predecrement +fp_predecr: + fp_mode_addr_indirect_predec + jra fp_fetchsource + + | addressing mode: address register/programm counter indirect + | with 16bit displacement +fp_disp16: + fp_mode_addr_indirect_disp16 + jra fp_fetchsource + + | all other indirect addressing modes will finally end up here +fp_extmode0: + fp_mode_addr_indirect_extmode0 + jra fp_fetchsource + +| all pc relative addressing modes and immediate/absolute modes end up here +| the first ones are sent to fp_extmode0 or fp_disp16 +| and only the latter are handled here +fp_extmode1: + fp_decode_addr_reg + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_abs_short, fp_abs_long + .long fp_disp16, fp_extmode0 + .long fp_immediate, fp_ill + .long fp_ill, fp_ill + + | addressing mode: absolute short +fp_abs_short: + fp_mode_abs_short + jra fp_fetchsource + + | addressing mode: absolute long +fp_abs_long: + fp_mode_abs_long + jra fp_fetchsource + + | addressing mode: immediate data +fp_immediate: + printf PDECODE,"#" + fp_get_pc %a0 + move.w (fp_datasize,%d1.w*2),%d0 + addq.w #1,%d0 + and.w #-2,%d0 +#ifdef FPU_EMU_DEBUG + movem.l %d0/%d1,-(%sp) + movel %a0,%a1 + clr.l %d1 + jra 2f +1: getuser.b (%a1)+,%d1,fp_err_ua1,%a1 + printf PDECODE,"%02x",1,%d1 +2: dbra %d0,1b + movem.l (%sp)+,%d0/%d1 +#endif + lea (%a0,%d0.w),%a1 + fp_put_pc %a1 +| jra fp_fetchsource + +fp_fetchsource: + move.l %a0,%a1 + swap %d1 + lea (FPD_TEMPFP1,FPDATA),%a0 + jmp ([0f:w,%pc,%d1.w*4]) + + .align 4 +0: .long fp_long, fp_single + .long fp_ext, fp_pack + .long fp_word, fp_double + .long fp_byte, fp_ill + +fp_long: + getuser.l (%a1),%d0,fp_err_ua1,%a1 + jsr fp_conv_long2ext + jra fp_getdest + +fp_single: + getuser.l (%a1),%d0,fp_err_ua1,%a1 + jsr fp_conv_single2ext + jra fp_getdest + +fp_ext: + getuser.l (%a1)+,%d0,fp_err_ua1,%a1 + lsr.l #8,%d0 + lsr.l #7,%d0 + lsr.w #1,%d0 + move.l %d0,(%a0)+ + getuser.l (%a1)+,%d0,fp_err_ua1,%a1 + move.l %d0,(%a0)+ + getuser.l (%a1),%d0,fp_err_ua1,%a1 + move.l %d0,(%a0) + subq.l #8,%a0 + jra fp_getdest + +fp_pack: + /* not supported yet */ + jra fp_ill + +fp_word: + getuser.w (%a1),%d0,fp_err_ua1,%a1 + ext.l %d0 + jsr fp_conv_long2ext + jra fp_getdest + +fp_double: + jsr fp_conv_double2ext + jra fp_getdest + +fp_byte: + getuser.b (%a1),%d0,fp_err_ua1,%a1 + extb.l %d0 + jsr fp_conv_long2ext +| jra fp_getdest + +fp_getdest: + move.l %a0,%a1 + bfextu %d2{#22,#3},%d0 + printf PDECODE,",fp%d\n",1,%d0 + fp_get_fp_reg + movem.l %a0/%a1,-(%sp) + pea fp_finalrounding + bfextu %d2{#25,#7},%d0 + jmp ([0f:w,%pc,%d0*4]) + + .align 4 +0: + .long fp_fmove_mem2fp, fp_fint, fp_fsinh, fp_fintrz + .long fp_fsqrt, fp_ill, fp_flognp1, fp_ill + .long fp_fetoxm1, fp_ftanh, fp_fatan, fp_ill + .long fp_fasin, fp_fatanh, fp_fsin, fp_ftan + .long fp_fetox, fp_ftwotox, fp_ftentox, fp_ill + .long fp_flogn, fp_flog10, fp_flog2, fp_ill + .long fp_fabs, fp_fcosh, fp_fneg, fp_ill + .long fp_facos, fp_fcos, fp_fgetexp, fp_fgetman + .long fp_fdiv, fp_fmod, fp_fadd, fp_fmul + .long fpa_fsgldiv, fp_frem, fp_fscale, fpa_fsglmul + .long fp_fsub, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_fsincos0, fp_fsincos1, fp_fsincos2, fp_fsincos3 + .long fp_fsincos4, fp_fsincos5, fp_fsincos6, fp_fsincos7 + .long fp_fcmp, fp_ill, fp_ftst, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_fsmove, fp_fssqrt, fp_ill, fp_ill + .long fp_fdmove, fp_fdsqrt, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_fsabs, fp_ill, fp_fsneg, fp_ill + .long fp_fdabs, fp_ill, fp_fdneg, fp_ill + .long fp_fsdiv, fp_ill, fp_fsadd, fp_fsmul + .long fp_fddiv, fp_ill, fp_fdadd, fp_fdmul + .long fp_fssub, fp_ill, fp_ill, fp_ill + .long fp_fdsub, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + .long fp_ill, fp_ill, fp_ill, fp_ill + + | Instructions follow + + | Move an (emulated) ROM constant +fp_fmovecr: + bfextu %d2{#27,#5},%d0 + printf PINSTR,"fp_fmovecr #%d",1,%d0 + move.l %d0,%d1 + add.l %d0,%d0 + add.l %d1,%d0 + lea (fp_constants,%d0*4),%a0 + move.l #0x801cc0ff,%d0 + addq.l #1,%d1 + lsl.l %d1,%d0 + jcc 1f + fp_set_sr FPSR_EXC_INEX2 | INEX2 exception +1: moveq #-128,%d0 | continue with fmove + and.l %d0,%d2 + jra fp_getdest + + .data + .align 4 +fp_constants: + .long 0x00004000,0xc90fdaa2,0x2168c235 | pi + .extend 0,0,0,0,0,0,0,0,0,0 + .long 0x00003ffd,0x9a209a84,0xfbcff798 | log10(2) + .long 0x00004000,0xadf85458,0xa2bb4a9a | e + .long 0x00003fff,0xb8aa3b29,0x5c17f0bc | log2(e) + .long 0x00003ffd,0xde5bd8a9,0x37287195 | log10(e) + .long 0x00000000,0x00000000,0x00000000 | 0.0 + .long 0x00003ffe,0xb17217f7,0xd1cf79ac | 1n(2) + .long 0x00004000,0x935d8ddd,0xaaa8ac17 | 1n(10) + | read this as "1.0 * 2^0" - note the high bit in the mantissa + .long 0x00003fff,0x80000000,0x00000000 | 10^0 + .long 0x00004002,0xa0000000,0x00000000 | 10^1 + .long 0x00004005,0xc8000000,0x00000000 | 10^2 + .long 0x0000400c,0x9c400000,0x00000000 | 10^4 + .long 0x00004019,0xbebc2000,0x00000000 | 10^8 + .long 0x00004034,0x8e1bc9bf,0x04000000 | 10^16 + .long 0x00004069,0x9dc5ada8,0x2b70b59e | 10^32 + .long 0x000040d3,0xc2781f49,0xffcfa6d5 | 10^64 + .long 0x000041a8,0x93ba47c9,0x80e98ce0 | 10^128 + .long 0x00004351,0xaa7eebfb,0x9df9de8e | 10^256 + .long 0x000046a3,0xe319a0ae,0xa60e91c7 | 10^512 + .long 0x00004d48,0xc9767586,0x81750c17 | 10^1024 + .long 0x00005a92,0x9e8b3b5d,0xc53d5de5 | 10^2048 + .long 0x00007525,0xc4605202,0x8a20979b | 10^4096 + .previous + +fp_fmove_mem2fp: + printf PINSTR,"fmove %p,%p\n",2,%a0,%a1 + move.l (%a1)+,(%a0)+ + move.l (%a1)+,(%a0)+ + move.l (%a1),(%a0) + subq.l #8,%a0 + rts + +fpa_fsglmul: + move.l #fp_finalrounding_single_fast,(%sp) + jra fp_fsglmul + +fpa_fsgldiv: + move.l #fp_finalrounding_single_fast,(%sp) + jra fp_fsgldiv + +.macro fp_dosingleprec instr + printf PINSTR,"single " + move.l #fp_finalrounding_single,(%sp) + jra \instr +.endm + +.macro fp_dodoubleprec instr + printf PINSTR,"double " + move.l #fp_finalrounding_double,(%sp) + jra \instr +.endm + +fp_fsmove: + fp_dosingleprec fp_fmove_mem2fp + +fp_fssqrt: + fp_dosingleprec fp_fsqrt + +fp_fdmove: + fp_dodoubleprec fp_fmove_mem2fp + +fp_fdsqrt: + fp_dodoubleprec fp_fsqrt + +fp_fsabs: + fp_dosingleprec fp_fabs + +fp_fsneg: + fp_dosingleprec fp_fneg + +fp_fdabs: + fp_dodoubleprec fp_fabs + +fp_fdneg: + fp_dodoubleprec fp_fneg + +fp_fsdiv: + fp_dosingleprec fp_fdiv + +fp_fsadd: + fp_dosingleprec fp_fadd + +fp_fsmul: + fp_dosingleprec fp_fmul + +fp_fddiv: + fp_dodoubleprec fp_fdiv + +fp_fdadd: + fp_dodoubleprec fp_fadd + +fp_fdmul: + fp_dodoubleprec fp_fmul + +fp_fssub: + fp_dosingleprec fp_fsub + +fp_fdsub: + fp_dodoubleprec fp_fsub + +fp_nonstd: + fp_get_pc %a0 + getuser.l (%a0),%d0,fp_err_ua1,%a0 + printf ,"nonstd ((%08x)=%08x)\n",2,%a0,%d0 + moveq #-1,%d0 + rts + + .data + .align 4 + + | data sizes corresponding to the operand formats +fp_datasize: + .word 4, 4, 12, 12, 2, 8, 1, 0 diff --git a/arch/m68k/math-emu/fp_trig.c b/arch/m68k/math-emu/fp_trig.c new file mode 100644 index 000000000000..6361d0784df2 --- /dev/null +++ b/arch/m68k/math-emu/fp_trig.c @@ -0,0 +1,183 @@ +/* + + fp_trig.c: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998-1999 David Huggins-Daines / Roman Zippel. + + I hereby give permission, free of charge, to copy, modify, and + redistribute this software, in source or binary form, provided that + the above copyright notice and the following disclaimer are included + in all such copies. + + THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL + OR IMPLIED. + +*/ + +#include "fp_emu.h" +#include "fp_trig.h" + +struct fp_ext * +fp_fsin(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsin\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fcos(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fcos\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftan(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftan\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fasin(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fasin\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_facos(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("facos\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fatan(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fatan\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fsinh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsinh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fcosh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fcosh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_ftanh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("ftanh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fatanh(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fatanh\n"); + + fp_monadic_check(dest, src); + + return dest; +} + +struct fp_ext * +fp_fsincos0(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos0\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos1(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos1\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos2(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos2\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos3(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos3\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos4(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos4\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos5(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos5\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos6(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos6\n"); + + return dest; +} + +struct fp_ext * +fp_fsincos7(struct fp_ext *dest, struct fp_ext *src) +{ + uprint("fsincos7\n"); + + return dest; +} diff --git a/arch/m68k/math-emu/fp_trig.h b/arch/m68k/math-emu/fp_trig.h new file mode 100644 index 000000000000..af8b247e9c98 --- /dev/null +++ b/arch/m68k/math-emu/fp_trig.h @@ -0,0 +1,32 @@ +/* + + fp_trig.h: floating-point math routines for the Linux-m68k + floating point emulator. + + Copyright (c) 1998 David Huggins-Daines. + + I hereby give permission, free of charge, to copy, modify, and + redistribute this software, in source or binary form, provided that + the above copyright notice and the following disclaimer are included + in all such copies. + + THIS SOFTWARE IS PROVIDED "AS IS", WITH ABSOLUTELY NO WARRANTY, REAL + OR IMPLIED. + +*/ + +#ifndef FP_TRIG_H +#define FP_TRIG_H + +#include "fp_emu.h" + +/* floating point trigonometric instructions: + + the arguments to these are in the "internal" extended format, that + is, an "exploded" version of the 96-bit extended fp format used by + the 68881. + + they return a status code, which should end up in %d0, if all goes + well. */ + +#endif /* FP_TRIG__H */ diff --git a/arch/m68k/math-emu/fp_util.S b/arch/m68k/math-emu/fp_util.S new file mode 100644 index 000000000000..a29c6114c60d --- /dev/null +++ b/arch/m68k/math-emu/fp_util.S @@ -0,0 +1,1454 @@ +/* + * fp_util.S + * + * Copyright Roman Zippel, 1997. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fp_emu.h" + +/* + * Here are lots of conversion and normalization functions mainly + * used by fp_scan.S + * Note that these functions are optimized for "normal" numbers, + * these are handled first and exit as fast as possible, this is + * especially important for fp_normalize_ext/fp_conv_ext2ext, as + * it's called very often. + * The register usage is optimized for fp_scan.S and which register + * is currently at that time unused, be careful if you want change + * something here. %d0 and %d1 is always usable, sometimes %d2 (or + * only the lower half) most function have to return the %a0 + * unmodified, so that the caller can immediatly reuse it. + */ + + .globl fp_ill, fp_end + + | exits from fp_scan: + | illegal instruction +fp_ill: + printf ,"fp_illegal\n" + rts + | completed instruction +fp_end: + tst.l (TASK_MM-8,%a2) + jmi 1f + tst.l (TASK_MM-4,%a2) + jmi 1f + tst.l (TASK_MM,%a2) + jpl 2f +1: printf ,"oops:%p,%p,%p\n",3,%a2@(TASK_MM-8),%a2@(TASK_MM-4),%a2@(TASK_MM) +2: clr.l %d0 + rts + + .globl fp_conv_long2ext, fp_conv_single2ext + .globl fp_conv_double2ext, fp_conv_ext2ext + .globl fp_normalize_ext, fp_normalize_double + .globl fp_normalize_single, fp_normalize_single_fast + .globl fp_conv_ext2double, fp_conv_ext2single + .globl fp_conv_ext2long, fp_conv_ext2short + .globl fp_conv_ext2byte + .globl fp_finalrounding_single, fp_finalrounding_single_fast + .globl fp_finalrounding_double + .globl fp_finalrounding, fp_finaltest, fp_final + +/* + * First several conversion functions from a source operand + * into the extended format. Note, that only fp_conv_ext2ext + * normalizes the number and is always called after the other + * conversion functions, which only move the information into + * fp_ext structure. + */ + + | fp_conv_long2ext: + | + | args: %d0 = source (32-bit long) + | %a0 = destination (ptr to struct fp_ext) + +fp_conv_long2ext: + printf PCONV,"l2e: %p -> %p(",2,%d0,%a0 + clr.l %d1 | sign defaults to zero + tst.l %d0 + jeq fp_l2e_zero | is source zero? + jpl 1f | positive? + moveq #1,%d1 + neg.l %d0 +1: swap %d1 + move.w #0x3fff+31,%d1 + move.l %d1,(%a0)+ | set sign / exp + move.l %d0,(%a0)+ | set mantissa + clr.l (%a0) + subq.l #8,%a0 | restore %a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | source is zero +fp_l2e_zero: + clr.l (%a0)+ + clr.l (%a0)+ + clr.l (%a0) + subq.l #8,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + + | fp_conv_single2ext + | args: %d0 = source (single-precision fp value) + | %a0 = dest (struct fp_ext *) + +fp_conv_single2ext: + printf PCONV,"s2e: %p -> %p(",2,%d0,%a0 + move.l %d0,%d1 + lsl.l #8,%d0 | shift mantissa + lsr.l #8,%d1 | exponent / sign + lsr.l #7,%d1 + lsr.w #8,%d1 + jeq fp_s2e_small | zero / denormal? + cmp.w #0xff,%d1 | NaN / Inf? + jeq fp_s2e_large + bset #31,%d0 | set explizit bit + add.w #0x3fff-0x7f,%d1 | re-bias the exponent. +9: move.l %d1,(%a0)+ | fp_ext.sign, fp_ext.exp + move.l %d0,(%a0)+ | high lword of fp_ext.mant + clr.l (%a0) | low lword = 0 + subq.l #8,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | zeros and denormalized +fp_s2e_small: + | exponent is zero, so explizit bit is already zero too + tst.l %d0 + jeq 9b + move.w #0x4000-0x7f,%d1 + jra 9b + | infinities and NAN +fp_s2e_large: + bclr #31,%d0 | clear explizit bit + move.w #0x7fff,%d1 + jra 9b + +fp_conv_double2ext: +#ifdef FPU_EMU_DEBUG + getuser.l %a1@(0),%d0,fp_err_ua2,%a1 + getuser.l %a1@(4),%d1,fp_err_ua2,%a1 + printf PCONV,"d2e: %p%p -> %p(",3,%d0,%d1,%a0 +#endif + getuser.l (%a1)+,%d0,fp_err_ua2,%a1 + move.l %d0,%d1 + lsl.l #8,%d0 | shift high mantissa + lsl.l #3,%d0 + lsr.l #8,%d1 | exponent / sign + lsr.l #7,%d1 + lsr.w #5,%d1 + jeq fp_d2e_small | zero / denormal? + cmp.w #0x7ff,%d1 | NaN / Inf? + jeq fp_d2e_large + bset #31,%d0 | set explizit bit + add.w #0x3fff-0x3ff,%d1 | re-bias the exponent. +9: move.l %d1,(%a0)+ | fp_ext.sign, fp_ext.exp + move.l %d0,(%a0)+ + getuser.l (%a1)+,%d0,fp_err_ua2,%a1 + move.l %d0,%d1 + lsl.l #8,%d0 + lsl.l #3,%d0 + move.l %d0,(%a0) + moveq #21,%d0 + lsr.l %d0,%d1 + or.l %d1,-(%a0) + subq.l #4,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | zeros and denormalized +fp_d2e_small: + | exponent is zero, so explizit bit is already zero too + tst.l %d0 + jeq 9b + move.w #0x4000-0x3ff,%d1 + jra 9b + | infinities and NAN +fp_d2e_large: + bclr #31,%d0 | clear explizit bit + move.w #0x7fff,%d1 + jra 9b + + | fp_conv_ext2ext: + | originally used to get longdouble from userspace, now it's + | called before arithmetic operations to make sure the number + | is normalized [maybe rename it?]. + | args: %a0 = dest (struct fp_ext *) + | returns 0 in %d0 for a NaN, otherwise 1 + +fp_conv_ext2ext: + printf PCONV,"e2e: %p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,"), " + move.l (%a0)+,%d0 + cmp.w #0x7fff,%d0 | Inf / NaN? + jeq fp_e2e_large + move.l (%a0),%d0 + jpl fp_e2e_small | zero / denorm? + | The high bit is set, so normalization is irrelevant. +fp_e2e_checkround: + subq.l #4,%a0 +#ifdef CONFIG_FPU_EMU_EXTRAPREC + move.b (%a0),%d0 + jne fp_e2e_round +#endif + printf PCONV,"%p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + moveq #1,%d0 + rts +#ifdef CONFIG_FPU_EMU_EXTRAPREC +fp_e2e_round: + fp_set_sr FPSR_EXC_INEX2 + clr.b (%a0) + move.w (FPD_RND,FPDATA),%d2 + jne fp_e2e_roundother | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #0,(11,%a0) | test lsb bit + jne fp_e2e_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_e2e_doroundup: + addq.l #1,(8,%a0) + jcc 9f + addq.l #1,(4,%a0) + jcc 9f + move.w #0x8000,(4,%a0) + addq.w #1,(2,%a0) +9: printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_e2e_roundother: + subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 1f | %d2 > 2, round to +infinity + tst.b (1,%a0) | to -inf + jne fp_e2e_doroundup | negative, round to infinity + jra 9b | positive, round to zero +1: tst.b (1,%a0) | to +inf + jeq fp_e2e_doroundup | positive, round to infinity + jra 9b | negative, round to zero +#endif + | zeros and subnormals: + | try to normalize these anyway. +fp_e2e_small: + jne fp_e2e_small1 | high lword zero? + move.l (4,%a0),%d0 + jne fp_e2e_small2 +#ifdef CONFIG_FPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + jne fp_e2e_small3 +#endif + | Genuine zero. + clr.w -(%a0) + subq.l #2,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + moveq #1,%d0 + rts + | definitely subnormal, need to shift all 64 bits +fp_e2e_small1: + bfffo %d0{#0,#32},%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + move.w %d1,%d2 + jeq fp_e2e_checkround + | fancy 64-bit double-shift begins here + lsl.l %d2,%d0 + move.l %d0,(%a0)+ + move.l (%a0),%d0 + move.l %d0,%d1 + lsl.l %d2,%d0 + move.l %d0,(%a0) + neg.w %d2 + and.w #0x1f,%d2 + lsr.l %d2,%d1 + or.l %d1,-(%a0) +#ifdef CONFIG_FPU_EMU_EXTRAPREC +fp_e2e_extra1: + clr.l %d0 + move.b (-4,%a0),%d0 + neg.w %d2 + add.w #24,%d2 + jcc 1f + clr.b (-4,%a0) + lsl.l %d2,%d0 + or.l %d0,(4,%a0) + jra fp_e2e_checkround +1: addq.w #8,%d2 + lsl.l %d2,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + or.l %d0,(4,%a0) +#endif + jra fp_e2e_checkround + | pathologically small subnormal +fp_e2e_small2: + bfffo %d0{#0,#32},%d1 + add.w #32,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Beyond pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_e2e_checkround + clr.l (4,%a0) + sub.w #32,%d2 + jcs 1f + lsl.l %d1,%d0 | lower lword needs only to be shifted + move.l %d0,(%a0) | into the higher lword +#ifdef CONFIG_FPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + clr.b (-4,%a0) + neg.w %d1 + add.w #32,%d1 + bfins %d0,(%a0){%d1,#8} +#endif + jra fp_e2e_checkround +1: neg.w %d1 | lower lword is splitted between + bfins %d0,(%a0){%d1,#32} | higher and lower lword +#ifndef CONFIG_FPU_EMU_EXTRAPREC + jra fp_e2e_checkround +#else + move.w %d1,%d2 + jra fp_e2e_extra1 + | These are extremely small numbers, that will mostly end up as zero + | anyway, so this is only important for correct rounding. +fp_e2e_small3: + bfffo %d0{#24,#8},%d1 + add.w #40,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_e2e_checkround + cmp.w #8,%d1 + jcs 2f +1: clr.b (-4,%a0) + sub.w #64,%d1 + jcs 1f + add.w #24,%d1 + lsl.l %d1,%d0 + move.l %d0,(%a0) + jra fp_e2e_checkround +1: neg.w %d1 + bfins %d0,(%a0){%d1,#8} + jra fp_e2e_checkround +2: lsl.l %d1,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + move.b %d0,(7,%a0) + jra fp_e2e_checkround +#endif +1: move.l %d0,%d1 | lower lword is splitted between + lsl.l %d2,%d0 | higher and lower lword + move.l %d0,(%a0) + move.l %d1,%d0 + neg.w %d2 + add.w #32,%d2 + lsr.l %d2,%d0 + move.l %d0,-(%a0) + jra fp_e2e_checkround + | Infinities and NaNs +fp_e2e_large: + move.l (%a0)+,%d0 + jne 3f +1: tst.l (%a0) + jne 4f + moveq #1,%d0 +2: subq.l #8,%a0 + printf PCONV,"%p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,")\n" + rts + | we have maybe a NaN, shift off the highest bit +3: lsl.l #1,%d0 + jeq 1b + | we have a NaN, clear the return value +4: clrl %d0 + jra 2b + + +/* + * Normalization functions. Call these on the output of general + * FP operators, and before any conversion into the destination + * formats. fp_normalize_ext has always to be called first, the + * following conversion functions expect an already normalized + * number. + */ + + | fp_normalize_ext: + | normalize an extended in extended (unpacked) format, basically + | it does the same as fp_conv_ext2ext, additionally it also does + | the necessary postprocessing checks. + | args: %a0 (struct fp_ext *) + | NOTE: it does _not_ modify %a0/%a1 and the upper word of %d2 + +fp_normalize_ext: + printf PNORM,"ne: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,"), " + move.l (%a0)+,%d0 + cmp.w #0x7fff,%d0 | Inf / NaN? + jeq fp_ne_large + move.l (%a0),%d0 + jpl fp_ne_small | zero / denorm? + | The high bit is set, so normalization is irrelevant. +fp_ne_checkround: + subq.l #4,%a0 +#ifdef CONFIG_FPU_EMU_EXTRAPREC + move.b (%a0),%d0 + jne fp_ne_round +#endif + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +#ifdef CONFIG_FPU_EMU_EXTRAPREC +fp_ne_round: + fp_set_sr FPSR_EXC_INEX2 + clr.b (%a0) + move.w (FPD_RND,FPDATA),%d2 + jne fp_ne_roundother | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #0,(11,%a0) | test lsb bit + jne fp_ne_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_ne_doroundup: + addq.l #1,(8,%a0) + jcc 9f + addq.l #1,(4,%a0) + jcc 9f + addq.w #1,(2,%a0) + move.w #0x8000,(4,%a0) +9: printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_ne_roundother: + subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 1f | %d2 > 2, round to +infinity + tst.b (1,%a0) | to -inf + jne fp_ne_doroundup | negative, round to infinity + jra 9b | positive, round to zero +1: tst.b (1,%a0) | to +inf + jeq fp_ne_doroundup | positive, round to infinity + jra 9b | negative, round to zero +#endif + | Zeros and subnormal numbers + | These are probably merely subnormal, rather than "denormalized" + | numbers, so we will try to make them normal again. +fp_ne_small: + jne fp_ne_small1 | high lword zero? + move.l (4,%a0),%d0 + jne fp_ne_small2 +#ifdef CONFIG_FPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + jne fp_ne_small3 +#endif + | Genuine zero. + clr.w -(%a0) + subq.l #2,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | Subnormal. +fp_ne_small1: + bfffo %d0{#0,#32},%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 + fp_set_sr FPSR_EXC_UNFL +1: move.w %d2,(%a0)+ + move.w %d1,%d2 + jeq fp_ne_checkround + | This is exactly the same 64-bit double shift as seen above. + lsl.l %d2,%d0 + move.l %d0,(%a0)+ + move.l (%a0),%d0 + move.l %d0,%d1 + lsl.l %d2,%d0 + move.l %d0,(%a0) + neg.w %d2 + and.w #0x1f,%d2 + lsr.l %d2,%d1 + or.l %d1,-(%a0) +#ifdef CONFIG_FPU_EMU_EXTRAPREC +fp_ne_extra1: + clr.l %d0 + move.b (-4,%a0),%d0 + neg.w %d2 + add.w #24,%d2 + jcc 1f + clr.b (-4,%a0) + lsl.l %d2,%d0 + or.l %d0,(4,%a0) + jra fp_ne_checkround +1: addq.w #8,%d2 + lsl.l %d2,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + or.l %d0,(4,%a0) +#endif + jra fp_ne_checkround + | May or may not be subnormal, if so, only 32 bits to shift. +fp_ne_small2: + bfffo %d0{#0,#32},%d1 + add.w #32,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Beyond pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 + fp_set_sr FPSR_EXC_UNFL +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_ne_checkround + clr.l (4,%a0) + sub.w #32,%d1 + jcs 1f + lsl.l %d1,%d0 | lower lword needs only to be shifted + move.l %d0,(%a0) | into the higher lword +#ifdef CONFIG_FPU_EMU_EXTRAPREC + clr.l %d0 + move.b (-4,%a0),%d0 + clr.b (-4,%a0) + neg.w %d1 + add.w #32,%d1 + bfins %d0,(%a0){%d1,#8} +#endif + jra fp_ne_checkround +1: neg.w %d1 | lower lword is splitted between + bfins %d0,(%a0){%d1,#32} | higher and lower lword +#ifndef CONFIG_FPU_EMU_EXTRAPREC + jra fp_ne_checkround +#else + move.w %d1,%d2 + jra fp_ne_extra1 + | These are extremely small numbers, that will mostly end up as zero + | anyway, so this is only important for correct rounding. +fp_ne_small3: + bfffo %d0{#24,#8},%d1 + add.w #40,%d1 + move.w -(%a0),%d2 + sub.w %d1,%d2 + jcc 1f + | Pathologically small, denormalize. + add.w %d2,%d1 + clr.w %d2 +1: move.w %d2,(%a0)+ + ext.l %d1 + jeq fp_ne_checkround + cmp.w #8,%d1 + jcs 2f +1: clr.b (-4,%a0) + sub.w #64,%d1 + jcs 1f + add.w #24,%d1 + lsl.l %d1,%d0 + move.l %d0,(%a0) + jra fp_ne_checkround +1: neg.w %d1 + bfins %d0,(%a0){%d1,#8} + jra fp_ne_checkround +2: lsl.l %d1,%d0 + move.b %d0,(-4,%a0) + lsr.l #8,%d0 + move.b %d0,(7,%a0) + jra fp_ne_checkround +#endif + | Infinities and NaNs, again, same as above. +fp_ne_large: + move.l (%a0)+,%d0 + jne 3f +1: tst.l (%a0) + jne 4f +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | we have maybe a NaN, shift off the highest bit +3: move.l %d0,%d1 + lsl.l #1,%d1 + jne 4f + clr.l (-4,%a0) + jra 1b + | we have a NaN, test if it is signaling +4: bset #30,%d0 + jne 2b + fp_set_sr FPSR_EXC_SNAN + move.l %d0,(-4,%a0) + jra 2b + + | these next two do rounding as per the IEEE standard. + | values for the rounding modes appear to be: + | 0: Round to nearest + | 1: Round to zero + | 2: Round to -Infinity + | 3: Round to +Infinity + | both functions expect that fp_normalize was already + | called (and extended argument is already normalized + | as far as possible), these are used if there is different + | rounding precision is selected and before converting + | into single/double + + | fp_normalize_double: + | normalize an extended with double (52-bit) precision + | args: %a0 (struct fp_ext *) + +fp_normalize_double: + printf PNORM,"nd: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,"), " + move.l (%a0)+,%d2 + tst.w %d2 + jeq fp_nd_zero | zero / denormalized + cmp.w #0x7fff,%d2 + jeq fp_nd_huge | NaN / infinitive. + sub.w #0x4000-0x3ff,%d2 | will the exponent fit? + jcs fp_nd_small | too small. + cmp.w #0x7fe,%d2 + jcc fp_nd_large | too big. + addq.l #4,%a0 + move.l (%a0),%d0 | low lword of mantissa + | now, round off the low 11 bits. +fp_nd_round: + moveq #21,%d1 + lsl.l %d1,%d0 | keep 11 low bits. + jne fp_nd_checkround | Are they non-zero? + | nothing to do here +9: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | Be careful with the X bit! It contains the lsb + | from the shift above, it is needed for round to nearest. +fp_nd_checkround: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + and.w #0xf800,(2,%a0) | clear bits 0-10 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.l %d0 | test guard bit + jpl 9b | zero is closer + | here we test the X bit by adding it to %d2 + clr.w %d2 | first set z bit, addx only clears it + addx.w %d2,%d2 | test lsb bit + | IEEE754-specified "round to even" behaviour. If the guard + | bit is set, then the number is odd, so rounding works like + | in grade-school arithmetic (i.e. 1.5 rounds to 2.0) + | Otherwise, an equal distance rounds towards zero, so as not + | to produce an odd number. This is strange, but it is what + | the standard says. + jne fp_nd_doroundup | round to infinity + lsl.l #1,%d0 | check low bits + jeq 9b | round to zero +fp_nd_doroundup: + | round (the mantissa, that is) towards infinity + add.l #0x800,(%a0) + jcc 9b | no overflow, good. + addq.l #1,-(%a0) | extend to high lword + jcc 1f | no overflow, good. + | Yow! we have managed to overflow the mantissa. Since this + | only happens when %d1 was 0xfffff800, it is now zero, so + | reset the high bit, and increment the exponent. + move.w #0x8000,(%a0) + addq.w #1,-(%a0) + cmp.w #0x43ff,(%a0)+ | exponent now overflown? + jeq fp_nd_large | yes, so make it infinity. +1: subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + | Round to +Inf or -Inf. High word of %d2 contains the + | sign of the number, by the way. + swap %d2 | to -inf + tst.b %d2 + jne fp_nd_doroundup | negative, round to infinity + jra 9b | positive, round to zero +3: swap %d2 | to +inf + tst.b %d2 + jeq fp_nd_doroundup | positive, round to infinity + jra 9b | negative, round to zero + | Exponent underflow. Try to make a denormal, and set it to + | the smallest possible fraction if this fails. +fp_nd_small: + fp_set_sr FPSR_EXC_UNFL | set UNFL bit + move.w #0x3c01,(-2,%a0) | 2**-1022 + neg.w %d2 | degree of underflow + cmp.w #32,%d2 | single or double shift? + jcc 1f + | Again, another 64-bit double shift. + move.l (%a0),%d0 + move.l %d0,%d1 + lsr.l %d2,%d0 + move.l %d0,(%a0)+ + move.l (%a0),%d0 + lsr.l %d2,%d0 + neg.w %d2 + add.w #32,%d2 + lsl.l %d2,%d1 + or.l %d1,%d0 + move.l (%a0),%d1 + move.l %d0,(%a0) + | Check to see if we shifted off any significant bits + lsl.l %d2,%d1 + jeq fp_nd_round | Nope, round. + bset #0,%d0 | Yes, so set the "sticky bit". + jra fp_nd_round | Now, round. + | Another 64-bit single shift and store +1: sub.w #32,%d2 + cmp.w #32,%d2 | Do we really need to shift? + jcc 2f | No, the number is too small. + move.l (%a0),%d0 + clr.l (%a0)+ + move.l %d0,%d1 + lsr.l %d2,%d0 + neg.w %d2 + add.w #32,%d2 + | Again, check to see if we shifted off any significant bits. + tst.l (%a0) + jeq 1f + bset #0,%d0 | Sticky bit. +1: move.l %d0,(%a0) + lsl.l %d2,%d1 + jeq fp_nd_round + bset #0,%d0 + jra fp_nd_round + | Sorry, the number is just too small. +2: clr.l (%a0)+ + clr.l (%a0) + moveq #1,%d0 | Smallest possible fraction, + jra fp_nd_round | round as desired. + | zero and denormalized +fp_nd_zero: + tst.l (%a0)+ + jne 1f + tst.l (%a0) + jne 1f + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts | zero. nothing to do. + | These are not merely subnormal numbers, but true denormals, + | i.e. pathologically small (exponent is 2**-16383) numbers. + | It is clearly impossible for even a normal extended number + | with that exponent to fit into double precision, so just + | write these ones off as "too darn small". +1: fp_set_sr FPSR_EXC_UNFL | Set UNFL bit + clr.l (%a0) + clr.l -(%a0) + move.w #0x3c01,-(%a0) | i.e. 2**-1022 + addq.l #6,%a0 + moveq #1,%d0 + jra fp_nd_round | round. + | Exponent overflow. Just call it infinity. +fp_nd_large: + move.w #0x7ff,%d0 + and.w (6,%a0),%d0 + jeq 1f + fp_set_sr FPSR_EXC_INEX2 +1: fp_set_sr FPSR_EXC_OVFL + move.w (FPD_RND,FPDATA),%d2 + jne 3f | %d2 = 0 round to nearest +1: move.w #0x7fff,(-2,%a0) + clr.l (%a0)+ + clr.l (%a0) +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +3: subq.w #2,%d2 + jcs 5f | %d2 < 2, round to zero + jhi 4f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne 1b + jra 5f +4: tst.b (-3,%a0) | to +inf + jeq 1b +5: move.w #0x43fe,(-2,%a0) + moveq #-1,%d0 + move.l %d0,(%a0)+ + move.w #0xf800,%d0 + move.l %d0,(%a0) + jra 2b + | Infinities or NaNs +fp_nd_huge: + subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + + | fp_normalize_single: + | normalize an extended with single (23-bit) precision + | args: %a0 (struct fp_ext *) + +fp_normalize_single: + printf PNORM,"ns: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,") " + addq.l #2,%a0 + move.w (%a0)+,%d2 + jeq fp_ns_zero | zero / denormalized + cmp.w #0x7fff,%d2 + jeq fp_ns_huge | NaN / infinitive. + sub.w #0x4000-0x7f,%d2 | will the exponent fit? + jcs fp_ns_small | too small. + cmp.w #0xfe,%d2 + jcc fp_ns_large | too big. + move.l (%a0)+,%d0 | get high lword of mantissa +fp_ns_round: + tst.l (%a0) | check the low lword + jeq 1f + | Set a sticky bit if it is non-zero. This should only + | affect the rounding in what would otherwise be equal- + | distance situations, which is what we want it to do. + bset #0,%d0 +1: clr.l (%a0) | zap it from memory. + | now, round off the low 8 bits of the hi lword. + tst.b %d0 | 8 low bits. + jne fp_ns_checkround | Are they non-zero? + | nothing to do here + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_ns_checkround: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + clr.b -(%a0) | clear low byte of high lword + subq.l #3,%a0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #8,%d0 | test lsb bit + | round to even behaviour, see above. + jne fp_ns_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_ns_doroundup: + | round (the mantissa, that is) towards infinity + add.l #0x100,(%a0) + jcc 9f | no overflow, good. + | Overflow. This means that the %d1 was 0xffffff00, so it + | is now zero. We will set the mantissa to reflect this, and + | increment the exponent (checking for overflow there too) + move.w #0x8000,(%a0) + addq.w #1,-(%a0) + cmp.w #0x407f,(%a0)+ | exponent now overflown? + jeq fp_ns_large | yes, so make it infinity. +9: subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | check nondefault rounding modes +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne fp_ns_doroundup | negative, round to infinity + jra 9b | positive, round to zero +3: tst.b (-3,%a0) | to +inf + jeq fp_ns_doroundup | positive, round to infinity + jra 9b | negative, round to zero + | Exponent underflow. Try to make a denormal, and set it to + | the smallest possible fraction if this fails. +fp_ns_small: + fp_set_sr FPSR_EXC_UNFL | set UNFL bit + move.w #0x3f81,(-2,%a0) | 2**-126 + neg.w %d2 | degree of underflow + cmp.w #32,%d2 | single or double shift? + jcc 2f + | a 32-bit shift. + move.l (%a0),%d0 + move.l %d0,%d1 + lsr.l %d2,%d0 + move.l %d0,(%a0)+ + | Check to see if we shifted off any significant bits. + neg.w %d2 + add.w #32,%d2 + lsl.l %d2,%d1 + jeq 1f + bset #0,%d0 | Sticky bit. + | Check the lower lword +1: tst.l (%a0) + jeq fp_ns_round + clr (%a0) + bset #0,%d0 | Sticky bit. + jra fp_ns_round + | Sorry, the number is just too small. +2: clr.l (%a0)+ + clr.l (%a0) + moveq #1,%d0 | Smallest possible fraction, + jra fp_ns_round | round as desired. + | Exponent overflow. Just call it infinity. +fp_ns_large: + tst.b (3,%a0) + jeq 1f + fp_set_sr FPSR_EXC_INEX2 +1: fp_set_sr FPSR_EXC_OVFL + move.w (FPD_RND,FPDATA),%d2 + jne 3f | %d2 = 0 round to nearest +1: move.w #0x7fff,(-2,%a0) + clr.l (%a0)+ + clr.l (%a0) +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +3: subq.w #2,%d2 + jcs 5f | %d2 < 2, round to zero + jhi 4f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne 1b + jra 5f +4: tst.b (-3,%a0) | to +inf + jeq 1b +5: move.w #0x407e,(-2,%a0) + move.l #0xffffff00,(%a0)+ + clr.l (%a0) + jra 2b + | zero and denormalized +fp_ns_zero: + tst.l (%a0)+ + jne 1f + tst.l (%a0) + jne 1f + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts | zero. nothing to do. + | These are not merely subnormal numbers, but true denormals, + | i.e. pathologically small (exponent is 2**-16383) numbers. + | It is clearly impossible for even a normal extended number + | with that exponent to fit into single precision, so just + | write these ones off as "too darn small". +1: fp_set_sr FPSR_EXC_UNFL | Set UNFL bit + clr.l (%a0) + clr.l -(%a0) + move.w #0x3f81,-(%a0) | i.e. 2**-126 + addq.l #6,%a0 + moveq #1,%d0 + jra fp_ns_round | round. + | Infinities or NaNs +fp_ns_huge: + subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + + | fp_normalize_single_fast: + | normalize an extended with single (23-bit) precision + | this is only used by fsgldiv/fsgdlmul, where the + | operand is not completly normalized. + | args: %a0 (struct fp_ext *) + +fp_normalize_single_fast: + printf PNORM,"nsf: %p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,") " + addq.l #2,%a0 + move.w (%a0)+,%d2 + cmp.w #0x7fff,%d2 + jeq fp_nsf_huge | NaN / infinitive. + move.l (%a0)+,%d0 | get high lword of mantissa +fp_nsf_round: + tst.l (%a0) | check the low lword + jeq 1f + | Set a sticky bit if it is non-zero. This should only + | affect the rounding in what would otherwise be equal- + | distance situations, which is what we want it to do. + bset #0,%d0 +1: clr.l (%a0) | zap it from memory. + | now, round off the low 8 bits of the hi lword. + tst.b %d0 | 8 low bits. + jne fp_nsf_checkround | Are they non-zero? + | nothing to do here + subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +fp_nsf_checkround: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + clr.b -(%a0) | clear low byte of high lword + subq.l #3,%a0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.b %d0 | test guard bit + jpl 9f | zero is closer + btst #8,%d0 | test lsb bit + | round to even behaviour, see above. + jne fp_nsf_doroundup | round to infinity + lsl.b #1,%d0 | check low bits + jeq 9f | round to zero +fp_nsf_doroundup: + | round (the mantissa, that is) towards infinity + add.l #0x100,(%a0) + jcc 9f | no overflow, good. + | Overflow. This means that the %d1 was 0xffffff00, so it + | is now zero. We will set the mantissa to reflect this, and + | increment the exponent (checking for overflow there too) + move.w #0x8000,(%a0) + addq.w #1,-(%a0) + cmp.w #0x407f,(%a0)+ | exponent now overflown? + jeq fp_nsf_large | yes, so make it infinity. +9: subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + | check nondefault rounding modes +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne fp_nsf_doroundup | negative, round to infinity + jra 9b | positive, round to zero +3: tst.b (-3,%a0) | to +inf + jeq fp_nsf_doroundup | positive, round to infinity + jra 9b | negative, round to zero + | Exponent overflow. Just call it infinity. +fp_nsf_large: + tst.b (3,%a0) + jeq 1f + fp_set_sr FPSR_EXC_INEX2 +1: fp_set_sr FPSR_EXC_OVFL + move.w (FPD_RND,FPDATA),%d2 + jne 3f | %d2 = 0 round to nearest +1: move.w #0x7fff,(-2,%a0) + clr.l (%a0)+ + clr.l (%a0) +2: subq.l #8,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts +3: subq.w #2,%d2 + jcs 5f | %d2 < 2, round to zero + jhi 4f | %d2 > 2, round to +infinity + tst.b (-3,%a0) | to -inf + jne 1b + jra 5f +4: tst.b (-3,%a0) | to +inf + jeq 1b +5: move.w #0x407e,(-2,%a0) + move.l #0xffffff00,(%a0)+ + clr.l (%a0) + jra 2b + | Infinities or NaNs +fp_nsf_huge: + subq.l #4,%a0 + printf PNORM,"%p(",1,%a0 + printx PNORM,%a0@ + printf PNORM,")\n" + rts + + | conv_ext2int (macro): + | Generates a subroutine that converts an extended value to an + | integer of a given size, again, with the appropriate type of + | rounding. + + | Macro arguments: + | s: size, as given in an assembly instruction. + | b: number of bits in that size. + + | Subroutine arguments: + | %a0: source (struct fp_ext *) + + | Returns the integer in %d0 (like it should) + +.macro conv_ext2int s,b + .set inf,(1<<(\b-1))-1 | i.e. MAXINT + printf PCONV,"e2i%d: %p(",2,#\b,%a0 + printx PCONV,%a0@ + printf PCONV,") " + addq.l #2,%a0 + move.w (%a0)+,%d2 | exponent + jeq fp_e2i_zero\b | zero / denorm (== 0, here) + cmp.w #0x7fff,%d2 + jeq fp_e2i_huge\b | Inf / NaN + sub.w #0x3ffe,%d2 + jcs fp_e2i_small\b + cmp.w #\b,%d2 + jhi fp_e2i_large\b + move.l (%a0),%d0 + move.l %d0,%d1 + lsl.l %d2,%d1 + jne fp_e2i_round\b + tst.l (4,%a0) + jne fp_e2i_round\b + neg.w %d2 + add.w #32,%d2 + lsr.l %d2,%d0 +9: tst.w (-4,%a0) + jne 1f + tst.\s %d0 + jmi fp_e2i_large\b + printf PCONV,"-> %p\n",1,%d0 + rts +1: neg.\s %d0 + jeq 1f + jpl fp_e2i_large\b +1: printf PCONV,"-> %p\n",1,%d0 + rts +fp_e2i_round\b: + fp_set_sr FPSR_EXC_INEX2 | INEX2 bit + neg.w %d2 + add.w #32,%d2 + .if \b>16 + jeq 5f + .endif + lsr.l %d2,%d0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2f | %d2 == 0, round to nearest + tst.l %d1 | test guard bit + jpl 9b | zero is closer + btst %d2,%d0 | test lsb bit (%d2 still 0) + jne fp_e2i_doroundup\b + lsl.l #1,%d1 | check low bits + jne fp_e2i_doroundup\b + tst.l (4,%a0) + jeq 9b +fp_e2i_doroundup\b: + addq.l #1,%d0 + jra 9b + | check nondefault rounding modes +2: subq.w #2,%d2 + jcs 9b | %d2 < 2, round to zero + jhi 3f | %d2 > 2, round to +infinity + tst.w (-4,%a0) | to -inf + jne fp_e2i_doroundup\b | negative, round to infinity + jra 9b | positive, round to zero +3: tst.w (-4,%a0) | to +inf + jeq fp_e2i_doroundup\b | positive, round to infinity + jra 9b | negative, round to zero + | we are only want -2**127 get correctly rounded here, + | since the guard bit is in the lower lword. + | everything else ends up anyway as overflow. + .if \b>16 +5: move.w (FPD_RND,FPDATA),%d2 | rounding mode + jne 2b | %d2 == 0, round to nearest + move.l (4,%a0),%d1 | test guard bit + jpl 9b | zero is closer + lsl.l #1,%d1 | check low bits + jne fp_e2i_doroundup\b + jra 9b + .endif +fp_e2i_zero\b: + clr.l %d0 + tst.l (%a0)+ + jne 1f + tst.l (%a0) + jeq 3f +1: subq.l #4,%a0 + fp_clr_sr FPSR_EXC_UNFL | fp_normalize_ext has set this bit +fp_e2i_small\b: + fp_set_sr FPSR_EXC_INEX2 + clr.l %d0 + move.w (FPD_RND,FPDATA),%d2 | rounding mode + subq.w #2,%d2 + jcs 3f | %d2 < 2, round to nearest/zero + jhi 2f | %d2 > 2, round to +infinity + tst.w (-4,%a0) | to -inf + jeq 3f + subq.\s #1,%d0 + jra 3f +2: tst.w (-4,%a0) | to +inf + jne 3f + addq.\s #1,%d0 +3: printf PCONV,"-> %p\n",1,%d0 + rts +fp_e2i_large\b: + fp_set_sr FPSR_EXC_OPERR + move.\s #inf,%d0 + tst.w (-4,%a0) + jeq 1f + addq.\s #1,%d0 +1: printf PCONV,"-> %p\n",1,%d0 + rts +fp_e2i_huge\b: + move.\s (%a0),%d0 + tst.l (%a0) + jne 1f + tst.l (%a0) + jeq fp_e2i_large\b + | fp_normalize_ext has set this bit already + | and made the number nonsignaling +1: fp_tst_sr FPSR_EXC_SNAN + jne 1f + fp_set_sr FPSR_EXC_OPERR +1: printf PCONV,"-> %p\n",1,%d0 + rts +.endm + +fp_conv_ext2long: + conv_ext2int l,32 + +fp_conv_ext2short: + conv_ext2int w,16 + +fp_conv_ext2byte: + conv_ext2int b,8 + +fp_conv_ext2double: + jsr fp_normalize_double + printf PCONV,"e2d: %p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,"), " + move.l (%a0)+,%d2 + cmp.w #0x7fff,%d2 + jne 1f + move.w #0x7ff,%d2 + move.l (%a0)+,%d0 + jra 2f +1: sub.w #0x3fff-0x3ff,%d2 + move.l (%a0)+,%d0 + jmi 2f + clr.w %d2 +2: lsl.w #5,%d2 + lsl.l #7,%d2 + lsl.l #8,%d2 + move.l %d0,%d1 + lsl.l #1,%d0 + lsr.l #4,%d0 + lsr.l #8,%d0 + or.l %d2,%d0 + putuser.l %d0,(%a1)+,fp_err_ua2,%a1 + moveq #21,%d0 + lsl.l %d0,%d1 + move.l (%a0),%d0 + lsr.l #4,%d0 + lsr.l #7,%d0 + or.l %d1,%d0 + putuser.l %d0,(%a1),fp_err_ua2,%a1 +#ifdef FPU_EMU_DEBUG + getuser.l %a1@(-4),%d0,fp_err_ua2,%a1 + getuser.l %a1@(0),%d1,fp_err_ua2,%a1 + printf PCONV,"%p(%08x%08x)\n",3,%a1,%d0,%d1 +#endif + rts + +fp_conv_ext2single: + jsr fp_normalize_single + printf PCONV,"e2s: %p(",1,%a0 + printx PCONV,%a0@ + printf PCONV,"), " + move.l (%a0)+,%d1 + cmp.w #0x7fff,%d1 + jne 1f + move.w #0xff,%d1 + move.l (%a0)+,%d0 + jra 2f +1: sub.w #0x3fff-0x7f,%d1 + move.l (%a0)+,%d0 + jmi 2f + clr.w %d1 +2: lsl.w #8,%d1 + lsl.l #7,%d1 + lsl.l #8,%d1 + bclr #31,%d0 + lsr.l #8,%d0 + or.l %d1,%d0 + printf PCONV,"%08x\n",1,%d0 + rts + + | special return addresses for instr that + | encode the rounding precision in the opcode + | (e.g. fsmove,fdmove) + +fp_finalrounding_single: + addq.l #8,%sp + jsr fp_normalize_ext + jsr fp_normalize_single + jra fp_finaltest + +fp_finalrounding_single_fast: + addq.l #8,%sp + jsr fp_normalize_ext + jsr fp_normalize_single_fast + jra fp_finaltest + +fp_finalrounding_double: + addq.l #8,%sp + jsr fp_normalize_ext + jsr fp_normalize_double + jra fp_finaltest + + | fp_finaltest: + | set the emulated status register based on the outcome of an + | emulated instruction. + +fp_finalrounding: + addq.l #8,%sp +| printf ,"f: %p\n",1,%a0 + jsr fp_normalize_ext + move.w (FPD_PREC,FPDATA),%d0 + subq.w #1,%d0 + jcs fp_finaltest + jne 1f + jsr fp_normalize_single + jra 2f +1: jsr fp_normalize_double +2:| printf ,"f: %p\n",1,%a0 +fp_finaltest: + | First, we do some of the obvious tests for the exception + | status byte and condition code bytes of fp_sr here, so that + | they do not have to be handled individually by every + | emulated instruction. + clr.l %d0 + addq.l #1,%a0 + tst.b (%a0)+ | sign + jeq 1f + bset #FPSR_CC_NEG-24,%d0 | N bit +1: cmp.w #0x7fff,(%a0)+ | exponent + jeq 2f + | test for zero + moveq #FPSR_CC_Z-24,%d1 + tst.l (%a0)+ + jne 9f + tst.l (%a0) + jne 9f + jra 8f + | infinitiv and NAN +2: moveq #FPSR_CC_NAN-24,%d1 + move.l (%a0)+,%d2 + lsl.l #1,%d2 | ignore high bit + jne 8f + tst.l (%a0) + jne 8f + moveq #FPSR_CC_INF-24,%d1 +8: bset %d1,%d0 +9: move.b %d0,(FPD_FPSR+0,FPDATA) | set condition test result + | move instructions enter here + | Here, we test things in the exception status byte, and set + | other things in the accrued exception byte accordingly. + | Emulated instructions can set various things in the former, + | as defined in fp_emu.h. +fp_final: + move.l (FPD_FPSR,FPDATA),%d0 +#if 0 + btst #FPSR_EXC_SNAN,%d0 | EXC_SNAN + jne 1f + btst #FPSR_EXC_OPERR,%d0 | EXC_OPERR + jeq 2f +1: bset #FPSR_AEXC_IOP,%d0 | set IOP bit +2: btst #FPSR_EXC_OVFL,%d0 | EXC_OVFL + jeq 1f + bset #FPSR_AEXC_OVFL,%d0 | set OVFL bit +1: btst #FPSR_EXC_UNFL,%d0 | EXC_UNFL + jeq 1f + btst #FPSR_EXC_INEX2,%d0 | EXC_INEX2 + jeq 1f + bset #FPSR_AEXC_UNFL,%d0 | set UNFL bit +1: btst #FPSR_EXC_DZ,%d0 | EXC_INEX1 + jeq 1f + bset #FPSR_AEXC_DZ,%d0 | set DZ bit +1: btst #FPSR_EXC_OVFL,%d0 | EXC_OVFL + jne 1f + btst #FPSR_EXC_INEX2,%d0 | EXC_INEX2 + jne 1f + btst #FPSR_EXC_INEX1,%d0 | EXC_INEX1 + jeq 2f +1: bset #FPSR_AEXC_INEX,%d0 | set INEX bit +2: move.l %d0,(FPD_FPSR,FPDATA) +#else + | same as above, greatly optimized, but untested (yet) + move.l %d0,%d2 + lsr.l #5,%d0 + move.l %d0,%d1 + lsr.l #4,%d1 + or.l %d0,%d1 + and.b #0x08,%d1 + move.l %d2,%d0 + lsr.l #6,%d0 + or.l %d1,%d0 + move.l %d2,%d1 + lsr.l #4,%d1 + or.b #0xdf,%d1 + and.b %d1,%d0 + move.l %d2,%d1 + lsr.l #7,%d1 + and.b #0x80,%d1 + or.b %d1,%d0 + and.b #0xf8,%d0 + or.b %d0,%d2 + move.l %d2,(FPD_FPSR,FPDATA) +#endif + move.b (FPD_FPSR+2,FPDATA),%d0 + and.b (FPD_FPCR+2,FPDATA),%d0 + jeq 1f + printf ,"send signal!!!\n" +1: jra fp_end diff --git a/arch/m68k/math-emu/multi_arith.h b/arch/m68k/math-emu/multi_arith.h new file mode 100644 index 000000000000..2f11436e361a --- /dev/null +++ b/arch/m68k/math-emu/multi_arith.h @@ -0,0 +1,822 @@ +/* multi_arith.h: multi-precision integer arithmetic functions, needed + to do extended-precision floating point. + + (c) 1998 David Huggins-Daines. + + Somewhat based on arch/alpha/math-emu/ieee-math.c, which is (c) + David Mosberger-Tang. + + You may copy, modify, and redistribute this file under the terms of + the GNU General Public License, version 2, or any later version, at + your convenience. */ + +/* Note: + + These are not general multi-precision math routines. Rather, they + implement the subset of integer arithmetic that we need in order to + multiply, divide, and normalize 128-bit unsigned mantissae. */ + +#ifndef MULTI_ARITH_H +#define MULTI_ARITH_H + +#if 0 /* old code... */ + +/* Unsigned only, because we don't need signs to multiply and divide. */ +typedef unsigned int int128[4]; + +/* Word order */ +enum { + MSW128, + NMSW128, + NLSW128, + LSW128 +}; + +/* big-endian */ +#define LO_WORD(ll) (((unsigned int *) &ll)[1]) +#define HI_WORD(ll) (((unsigned int *) &ll)[0]) + +/* Convenience functions to stuff various integer values into int128s */ + +extern inline void zero128(int128 a) +{ + a[LSW128] = a[NLSW128] = a[NMSW128] = a[MSW128] = 0; +} + +/* Human-readable word order in the arguments */ +extern inline void set128(unsigned int i3, + unsigned int i2, + unsigned int i1, + unsigned int i0, + int128 a) +{ + a[LSW128] = i0; + a[NLSW128] = i1; + a[NMSW128] = i2; + a[MSW128] = i3; +} + +/* Convenience functions (for testing as well) */ +extern inline void int64_to_128(unsigned long long src, + int128 dest) +{ + dest[LSW128] = (unsigned int) src; + dest[NLSW128] = src >> 32; + dest[NMSW128] = dest[MSW128] = 0; +} + +extern inline void int128_to_64(const int128 src, + unsigned long long *dest) +{ + *dest = src[LSW128] | (long long) src[NLSW128] << 32; +} + +extern inline void put_i128(const int128 a) +{ + printk("%08x %08x %08x %08x\n", a[MSW128], a[NMSW128], + a[NLSW128], a[LSW128]); +} + +/* Internal shifters: + + Note that these are only good for 0 < count < 32. + */ + +extern inline void _lsl128(unsigned int count, int128 a) +{ + a[MSW128] = (a[MSW128] << count) | (a[NMSW128] >> (32 - count)); + a[NMSW128] = (a[NMSW128] << count) | (a[NLSW128] >> (32 - count)); + a[NLSW128] = (a[NLSW128] << count) | (a[LSW128] >> (32 - count)); + a[LSW128] <<= count; +} + +extern inline void _lsr128(unsigned int count, int128 a) +{ + a[LSW128] = (a[LSW128] >> count) | (a[NLSW128] << (32 - count)); + a[NLSW128] = (a[NLSW128] >> count) | (a[NMSW128] << (32 - count)); + a[NMSW128] = (a[NMSW128] >> count) | (a[MSW128] << (32 - count)); + a[MSW128] >>= count; +} + +/* Should be faster, one would hope */ + +extern inline void lslone128(int128 a) +{ + asm volatile ("lsl.l #1,%0\n" + "roxl.l #1,%1\n" + "roxl.l #1,%2\n" + "roxl.l #1,%3\n" + : + "=d" (a[LSW128]), + "=d"(a[NLSW128]), + "=d"(a[NMSW128]), + "=d"(a[MSW128]) + : + "0"(a[LSW128]), + "1"(a[NLSW128]), + "2"(a[NMSW128]), + "3"(a[MSW128])); +} + +extern inline void lsrone128(int128 a) +{ + asm volatile ("lsr.l #1,%0\n" + "roxr.l #1,%1\n" + "roxr.l #1,%2\n" + "roxr.l #1,%3\n" + : + "=d" (a[MSW128]), + "=d"(a[NMSW128]), + "=d"(a[NLSW128]), + "=d"(a[LSW128]) + : + "0"(a[MSW128]), + "1"(a[NMSW128]), + "2"(a[NLSW128]), + "3"(a[LSW128])); +} + +/* Generalized 128-bit shifters: + + These bit-shift to a multiple of 32, then move whole longwords. */ + +extern inline void lsl128(unsigned int count, int128 a) +{ + int wordcount, i; + + if (count % 32) + _lsl128(count % 32, a); + + if (0 == (wordcount = count / 32)) + return; + + /* argh, gak, endian-sensitive */ + for (i = 0; i < 4 - wordcount; i++) { + a[i] = a[i + wordcount]; + } + for (i = 3; i >= 4 - wordcount; --i) { + a[i] = 0; + } +} + +extern inline void lsr128(unsigned int count, int128 a) +{ + int wordcount, i; + + if (count % 32) + _lsr128(count % 32, a); + + if (0 == (wordcount = count / 32)) + return; + + for (i = 3; i >= wordcount; --i) { + a[i] = a[i - wordcount]; + } + for (i = 0; i < wordcount; i++) { + a[i] = 0; + } +} + +extern inline int orl128(int a, int128 b) +{ + b[LSW128] |= a; +} + +extern inline int btsthi128(const int128 a) +{ + return a[MSW128] & 0x80000000; +} + +/* test bits (numbered from 0 = LSB) up to and including "top" */ +extern inline int bftestlo128(int top, const int128 a) +{ + int r = 0; + + if (top > 31) + r |= a[LSW128]; + if (top > 63) + r |= a[NLSW128]; + if (top > 95) + r |= a[NMSW128]; + + r |= a[3 - (top / 32)] & ((1 << (top % 32 + 1)) - 1); + + return (r != 0); +} + +/* Aargh. We need these because GCC is broken */ +/* FIXME: do them in assembly, for goodness' sake! */ +extern inline void mask64(int pos, unsigned long long *mask) +{ + *mask = 0; + + if (pos < 32) { + LO_WORD(*mask) = (1 << pos) - 1; + return; + } + LO_WORD(*mask) = -1; + HI_WORD(*mask) = (1 << (pos - 32)) - 1; +} + +extern inline void bset64(int pos, unsigned long long *dest) +{ + /* This conditional will be optimized away. Thanks, GCC! */ + if (pos < 32) + asm volatile ("bset %1,%0":"=m" + (LO_WORD(*dest)):"id"(pos)); + else + asm volatile ("bset %1,%0":"=m" + (HI_WORD(*dest)):"id"(pos - 32)); +} + +extern inline int btst64(int pos, unsigned long long dest) +{ + if (pos < 32) + return (0 != (LO_WORD(dest) & (1 << pos))); + else + return (0 != (HI_WORD(dest) & (1 << (pos - 32)))); +} + +extern inline void lsl64(int count, unsigned long long *dest) +{ + if (count < 32) { + HI_WORD(*dest) = (HI_WORD(*dest) << count) + | (LO_WORD(*dest) >> count); + LO_WORD(*dest) <<= count; + return; + } + count -= 32; + HI_WORD(*dest) = LO_WORD(*dest) << count; + LO_WORD(*dest) = 0; +} + +extern inline void lsr64(int count, unsigned long long *dest) +{ + if (count < 32) { + LO_WORD(*dest) = (LO_WORD(*dest) >> count) + | (HI_WORD(*dest) << (32 - count)); + HI_WORD(*dest) >>= count; + return; + } + count -= 32; + LO_WORD(*dest) = HI_WORD(*dest) >> count; + HI_WORD(*dest) = 0; +} +#endif + +extern inline void fp_denormalize(struct fp_ext *reg, unsigned int cnt) +{ + reg->exp += cnt; + + switch (cnt) { + case 0 ... 8: + reg->lowmant = reg->mant.m32[1] << (8 - cnt); + reg->mant.m32[1] = (reg->mant.m32[1] >> cnt) | + (reg->mant.m32[0] << (32 - cnt)); + reg->mant.m32[0] = reg->mant.m32[0] >> cnt; + break; + case 9 ... 32: + reg->lowmant = reg->mant.m32[1] >> (cnt - 8); + if (reg->mant.m32[1] << (40 - cnt)) + reg->lowmant |= 1; + reg->mant.m32[1] = (reg->mant.m32[1] >> cnt) | + (reg->mant.m32[0] << (32 - cnt)); + reg->mant.m32[0] = reg->mant.m32[0] >> cnt; + break; + case 33 ... 39: + asm volatile ("bfextu %1{%2,#8},%0" : "=d" (reg->lowmant) + : "m" (reg->mant.m32[0]), "d" (64 - cnt)); + if (reg->mant.m32[1] << (40 - cnt)) + reg->lowmant |= 1; + reg->mant.m32[1] = reg->mant.m32[0] >> (cnt - 32); + reg->mant.m32[0] = 0; + break; + case 40 ... 71: + reg->lowmant = reg->mant.m32[0] >> (cnt - 40); + if ((reg->mant.m32[0] << (72 - cnt)) || reg->mant.m32[1]) + reg->lowmant |= 1; + reg->mant.m32[1] = reg->mant.m32[0] >> (cnt - 32); + reg->mant.m32[0] = 0; + break; + default: + reg->lowmant = reg->mant.m32[0] || reg->mant.m32[1]; + reg->mant.m32[0] = 0; + reg->mant.m32[1] = 0; + break; + } +} + +extern inline int fp_overnormalize(struct fp_ext *reg) +{ + int shift; + + if (reg->mant.m32[0]) { + asm ("bfffo %1{#0,#32},%0" : "=d" (shift) : "dm" (reg->mant.m32[0])); + reg->mant.m32[0] = (reg->mant.m32[0] << shift) | (reg->mant.m32[1] >> (32 - shift)); + reg->mant.m32[1] = (reg->mant.m32[1] << shift); + } else { + asm ("bfffo %1{#0,#32},%0" : "=d" (shift) : "dm" (reg->mant.m32[1])); + reg->mant.m32[0] = (reg->mant.m32[1] << shift); + reg->mant.m32[1] = 0; + shift += 32; + } + + return shift; +} + +extern inline int fp_addmant(struct fp_ext *dest, struct fp_ext *src) +{ + int carry; + + /* we assume here, gcc only insert move and a clr instr */ + asm volatile ("add.b %1,%0" : "=d,g" (dest->lowmant) + : "g,d" (src->lowmant), "0,0" (dest->lowmant)); + asm volatile ("addx.l %1,%0" : "=d" (dest->mant.m32[1]) + : "d" (src->mant.m32[1]), "0" (dest->mant.m32[1])); + asm volatile ("addx.l %1,%0" : "=d" (dest->mant.m32[0]) + : "d" (src->mant.m32[0]), "0" (dest->mant.m32[0])); + asm volatile ("addx.l %0,%0" : "=d" (carry) : "0" (0)); + + return carry; +} + +extern inline int fp_addcarry(struct fp_ext *reg) +{ + if (++reg->exp == 0x7fff) { + if (reg->mant.m64) + fp_set_sr(FPSR_EXC_INEX2); + reg->mant.m64 = 0; + fp_set_sr(FPSR_EXC_OVFL); + return 0; + } + reg->lowmant = (reg->mant.m32[1] << 7) | (reg->lowmant ? 1 : 0); + reg->mant.m32[1] = (reg->mant.m32[1] >> 1) | + (reg->mant.m32[0] << 31); + reg->mant.m32[0] = (reg->mant.m32[0] >> 1) | 0x80000000; + + return 1; +} + +extern inline void fp_submant(struct fp_ext *dest, struct fp_ext *src1, struct fp_ext *src2) +{ + /* we assume here, gcc only insert move and a clr instr */ + asm volatile ("sub.b %1,%0" : "=d,g" (dest->lowmant) + : "g,d" (src2->lowmant), "0,0" (src1->lowmant)); + asm volatile ("subx.l %1,%0" : "=d" (dest->mant.m32[1]) + : "d" (src2->mant.m32[1]), "0" (src1->mant.m32[1])); + asm volatile ("subx.l %1,%0" : "=d" (dest->mant.m32[0]) + : "d" (src2->mant.m32[0]), "0" (src1->mant.m32[0])); +} + +#define fp_mul64(desth, destl, src1, src2) ({ \ + asm ("mulu.l %2,%1:%0" : "=d" (destl), "=d" (desth) \ + : "g" (src1), "0" (src2)); \ +}) +#define fp_div64(quot, rem, srch, srcl, div) \ + asm ("divu.l %2,%1:%0" : "=d" (quot), "=d" (rem) \ + : "dm" (div), "1" (srch), "0" (srcl)) +#define fp_add64(dest1, dest2, src1, src2) ({ \ + asm ("add.l %1,%0" : "=d,dm" (dest2) \ + : "dm,d" (src2), "0,0" (dest2)); \ + asm ("addx.l %1,%0" : "=d" (dest1) \ + : "d" (src1), "0" (dest1)); \ +}) +#define fp_addx96(dest, src) ({ \ + /* we assume here, gcc only insert move and a clr instr */ \ + asm volatile ("add.l %1,%0" : "=d,g" (dest->m32[2]) \ + : "g,d" (temp.m32[1]), "0,0" (dest->m32[2])); \ + asm volatile ("addx.l %1,%0" : "=d" (dest->m32[1]) \ + : "d" (temp.m32[0]), "0" (dest->m32[1])); \ + asm volatile ("addx.l %1,%0" : "=d" (dest->m32[0]) \ + : "d" (0), "0" (dest->m32[0])); \ +}) +#define fp_sub64(dest, src) ({ \ + asm ("sub.l %1,%0" : "=d,dm" (dest.m32[1]) \ + : "dm,d" (src.m32[1]), "0,0" (dest.m32[1])); \ + asm ("subx.l %1,%0" : "=d" (dest.m32[0]) \ + : "d" (src.m32[0]), "0" (dest.m32[0])); \ +}) +#define fp_sub96c(dest, srch, srcm, srcl) ({ \ + char carry; \ + asm ("sub.l %1,%0" : "=d,dm" (dest.m32[2]) \ + : "dm,d" (srcl), "0,0" (dest.m32[2])); \ + asm ("subx.l %1,%0" : "=d" (dest.m32[1]) \ + : "d" (srcm), "0" (dest.m32[1])); \ + asm ("subx.l %2,%1; scs %0" : "=d" (carry), "=d" (dest.m32[0]) \ + : "d" (srch), "1" (dest.m32[0])); \ + carry; \ +}) + +extern inline void fp_multiplymant(union fp_mant128 *dest, struct fp_ext *src1, struct fp_ext *src2) +{ + union fp_mant64 temp; + + fp_mul64(dest->m32[0], dest->m32[1], src1->mant.m32[0], src2->mant.m32[0]); + fp_mul64(dest->m32[2], dest->m32[3], src1->mant.m32[1], src2->mant.m32[1]); + + fp_mul64(temp.m32[0], temp.m32[1], src1->mant.m32[0], src2->mant.m32[1]); + fp_addx96(dest, temp); + + fp_mul64(temp.m32[0], temp.m32[1], src1->mant.m32[1], src2->mant.m32[0]); + fp_addx96(dest, temp); +} + +extern inline void fp_dividemant(union fp_mant128 *dest, struct fp_ext *src, struct fp_ext *div) +{ + union fp_mant128 tmp; + union fp_mant64 tmp64; + unsigned long *mantp = dest->m32; + unsigned long fix, rem, first, dummy; + int i; + + /* the algorithm below requires dest to be smaller than div, + but both have the high bit set */ + if (src->mant.m64 >= div->mant.m64) { + fp_sub64(src->mant, div->mant); + *mantp = 1; + } else + *mantp = 0; + mantp++; + + /* basic idea behind this algorithm: we can't divide two 64bit numbers + (AB/CD) directly, but we can calculate AB/C0, but this means this + quotient is off by C0/CD, so we have to multiply the first result + to fix the result, after that we have nearly the correct result + and only a few corrections are needed. */ + + /* C0/CD can be precalculated, but it's an 64bit division again, but + we can make it a bit easier, by dividing first through C so we get + 10/1D and now only a single shift and the value fits into 32bit. */ + fix = 0x80000000; + dummy = div->mant.m32[1] / div->mant.m32[0] + 1; + dummy = (dummy >> 1) | fix; + fp_div64(fix, dummy, fix, 0, dummy); + fix--; + + for (i = 0; i < 3; i++, mantp++) { + if (src->mant.m32[0] == div->mant.m32[0]) { + fp_div64(first, rem, 0, src->mant.m32[1], div->mant.m32[0]); + + fp_mul64(*mantp, dummy, first, fix); + *mantp += fix; + } else { + fp_div64(first, rem, src->mant.m32[0], src->mant.m32[1], div->mant.m32[0]); + + fp_mul64(*mantp, dummy, first, fix); + } + + fp_mul64(tmp.m32[0], tmp.m32[1], div->mant.m32[0], first - *mantp); + fp_add64(tmp.m32[0], tmp.m32[1], 0, rem); + tmp.m32[2] = 0; + + fp_mul64(tmp64.m32[0], tmp64.m32[1], *mantp, div->mant.m32[1]); + fp_sub96c(tmp, 0, tmp64.m32[0], tmp64.m32[1]); + + src->mant.m32[0] = tmp.m32[1]; + src->mant.m32[1] = tmp.m32[2]; + + while (!fp_sub96c(tmp, 0, div->mant.m32[0], div->mant.m32[1])) { + src->mant.m32[0] = tmp.m32[1]; + src->mant.m32[1] = tmp.m32[2]; + *mantp += 1; + } + } +} + +#if 0 +extern inline unsigned int fp_fls128(union fp_mant128 *src) +{ + unsigned long data; + unsigned int res, off; + + if ((data = src->m32[0])) + off = 0; + else if ((data = src->m32[1])) + off = 32; + else if ((data = src->m32[2])) + off = 64; + else if ((data = src->m32[3])) + off = 96; + else + return 128; + + asm ("bfffo %1{#0,#32},%0" : "=d" (res) : "dm" (data)); + return res + off; +} + +extern inline void fp_shiftmant128(union fp_mant128 *src, int shift) +{ + unsigned long sticky; + + switch (shift) { + case 0: + return; + case 1: + asm volatile ("lsl.l #1,%0" + : "=d" (src->m32[3]) : "0" (src->m32[3])); + asm volatile ("roxl.l #1,%0" + : "=d" (src->m32[2]) : "0" (src->m32[2])); + asm volatile ("roxl.l #1,%0" + : "=d" (src->m32[1]) : "0" (src->m32[1])); + asm volatile ("roxl.l #1,%0" + : "=d" (src->m32[0]) : "0" (src->m32[0])); + return; + case 2 ... 31: + src->m32[0] = (src->m32[0] << shift) | (src->m32[1] >> (32 - shift)); + src->m32[1] = (src->m32[1] << shift) | (src->m32[2] >> (32 - shift)); + src->m32[2] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift)); + src->m32[3] = (src->m32[3] << shift); + return; + case 32 ... 63: + shift -= 32; + src->m32[0] = (src->m32[1] << shift) | (src->m32[2] >> (32 - shift)); + src->m32[1] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift)); + src->m32[2] = (src->m32[3] << shift); + src->m32[3] = 0; + return; + case 64 ... 95: + shift -= 64; + src->m32[0] = (src->m32[2] << shift) | (src->m32[3] >> (32 - shift)); + src->m32[1] = (src->m32[3] << shift); + src->m32[2] = src->m32[3] = 0; + return; + case 96 ... 127: + shift -= 96; + src->m32[0] = (src->m32[3] << shift); + src->m32[1] = src->m32[2] = src->m32[3] = 0; + return; + case -31 ... -1: + shift = -shift; + sticky = 0; + if (src->m32[3] << (32 - shift)) + sticky = 1; + src->m32[3] = (src->m32[3] >> shift) | (src->m32[2] << (32 - shift)) | sticky; + src->m32[2] = (src->m32[2] >> shift) | (src->m32[1] << (32 - shift)); + src->m32[1] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)); + src->m32[0] = (src->m32[0] >> shift); + return; + case -63 ... -32: + shift = -shift - 32; + sticky = 0; + if ((src->m32[2] << (32 - shift)) || src->m32[3]) + sticky = 1; + src->m32[3] = (src->m32[2] >> shift) | (src->m32[1] << (32 - shift)) | sticky; + src->m32[2] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)); + src->m32[1] = (src->m32[0] >> shift); + src->m32[0] = 0; + return; + case -95 ... -64: + shift = -shift - 64; + sticky = 0; + if ((src->m32[1] << (32 - shift)) || src->m32[2] || src->m32[3]) + sticky = 1; + src->m32[3] = (src->m32[1] >> shift) | (src->m32[0] << (32 - shift)) | sticky; + src->m32[2] = (src->m32[0] >> shift); + src->m32[1] = src->m32[0] = 0; + return; + case -127 ... -96: + shift = -shift - 96; + sticky = 0; + if ((src->m32[0] << (32 - shift)) || src->m32[1] || src->m32[2] || src->m32[3]) + sticky = 1; + src->m32[3] = (src->m32[0] >> shift) | sticky; + src->m32[2] = src->m32[1] = src->m32[0] = 0; + return; + } + + if (shift < 0 && (src->m32[0] || src->m32[1] || src->m32[2] || src->m32[3])) + src->m32[3] = 1; + else + src->m32[3] = 0; + src->m32[2] = 0; + src->m32[1] = 0; + src->m32[0] = 0; +} +#endif + +extern inline void fp_putmant128(struct fp_ext *dest, union fp_mant128 *src, int shift) +{ + unsigned long tmp; + + switch (shift) { + case 0: + dest->mant.m64 = src->m64[0]; + dest->lowmant = src->m32[2] >> 24; + if (src->m32[3] || (src->m32[2] << 8)) + dest->lowmant |= 1; + break; + case 1: + asm volatile ("lsl.l #1,%0" + : "=d" (tmp) : "0" (src->m32[2])); + asm volatile ("roxl.l #1,%0" + : "=d" (dest->mant.m32[1]) : "0" (src->m32[1])); + asm volatile ("roxl.l #1,%0" + : "=d" (dest->mant.m32[0]) : "0" (src->m32[0])); + dest->lowmant = tmp >> 24; + if (src->m32[3] || (tmp << 8)) + dest->lowmant |= 1; + break; + case 31: + asm volatile ("lsr.l #1,%1; roxr.l #1,%0" + : "=d" (dest->mant.m32[0]) + : "d" (src->m32[0]), "0" (src->m32[1])); + asm volatile ("roxr.l #1,%0" + : "=d" (dest->mant.m32[1]) : "0" (src->m32[2])); + asm volatile ("roxr.l #1,%0" + : "=d" (tmp) : "0" (src->m32[3])); + dest->lowmant = tmp >> 24; + if (src->m32[3] << 7) + dest->lowmant |= 1; + break; + case 32: + dest->mant.m32[0] = src->m32[1]; + dest->mant.m32[1] = src->m32[2]; + dest->lowmant = src->m32[3] >> 24; + if (src->m32[3] << 8) + dest->lowmant |= 1; + break; + } +} + +#if 0 /* old code... */ +extern inline int fls(unsigned int a) +{ + int r; + + asm volatile ("bfffo %1{#0,#32},%0" + : "=d" (r) : "md" (a)); + return r; +} + +/* fls = "find last set" (cf. ffs(3)) */ +extern inline int fls128(const int128 a) +{ + if (a[MSW128]) + return fls(a[MSW128]); + if (a[NMSW128]) + return fls(a[NMSW128]) + 32; + /* XXX: it probably never gets beyond this point in actual + use, but that's indicative of a more general problem in the + algorithm (i.e. as per the actual 68881 implementation, we + really only need at most 67 bits of precision [plus + overflow]) so I'm not going to fix it. */ + if (a[NLSW128]) + return fls(a[NLSW128]) + 64; + if (a[LSW128]) + return fls(a[LSW128]) + 96; + else + return -1; +} + +extern inline int zerop128(const int128 a) +{ + return !(a[LSW128] | a[NLSW128] | a[NMSW128] | a[MSW128]); +} + +extern inline int nonzerop128(const int128 a) +{ + return (a[LSW128] | a[NLSW128] | a[NMSW128] | a[MSW128]); +} + +/* Addition and subtraction */ +/* Do these in "pure" assembly, because "extended" asm is unmanageable + here */ +extern inline void add128(const int128 a, int128 b) +{ + /* rotating carry flags */ + unsigned int carry[2]; + + carry[0] = a[LSW128] > (0xffffffff - b[LSW128]); + b[LSW128] += a[LSW128]; + + carry[1] = a[NLSW128] > (0xffffffff - b[NLSW128] - carry[0]); + b[NLSW128] = a[NLSW128] + b[NLSW128] + carry[0]; + + carry[0] = a[NMSW128] > (0xffffffff - b[NMSW128] - carry[1]); + b[NMSW128] = a[NMSW128] + b[NMSW128] + carry[1]; + + b[MSW128] = a[MSW128] + b[MSW128] + carry[0]; +} + +/* Note: assembler semantics: "b -= a" */ +extern inline void sub128(const int128 a, int128 b) +{ + /* rotating borrow flags */ + unsigned int borrow[2]; + + borrow[0] = b[LSW128] < a[LSW128]; + b[LSW128] -= a[LSW128]; + + borrow[1] = b[NLSW128] < a[NLSW128] + borrow[0]; + b[NLSW128] = b[NLSW128] - a[NLSW128] - borrow[0]; + + borrow[0] = b[NMSW128] < a[NMSW128] + borrow[1]; + b[NMSW128] = b[NMSW128] - a[NMSW128] - borrow[1]; + + b[MSW128] = b[MSW128] - a[MSW128] - borrow[0]; +} + +/* Poor man's 64-bit expanding multiply */ +extern inline void mul64(unsigned long long a, + unsigned long long b, + int128 c) +{ + unsigned long long acc; + int128 acc128; + + zero128(acc128); + zero128(c); + + /* first the low words */ + if (LO_WORD(a) && LO_WORD(b)) { + acc = (long long) LO_WORD(a) * LO_WORD(b); + c[NLSW128] = HI_WORD(acc); + c[LSW128] = LO_WORD(acc); + } + /* Next the high words */ + if (HI_WORD(a) && HI_WORD(b)) { + acc = (long long) HI_WORD(a) * HI_WORD(b); + c[MSW128] = HI_WORD(acc); + c[NMSW128] = LO_WORD(acc); + } + /* The middle words */ + if (LO_WORD(a) && HI_WORD(b)) { + acc = (long long) LO_WORD(a) * HI_WORD(b); + acc128[NMSW128] = HI_WORD(acc); + acc128[NLSW128] = LO_WORD(acc); + add128(acc128, c); + } + /* The first and last words */ + if (HI_WORD(a) && LO_WORD(b)) { + acc = (long long) HI_WORD(a) * LO_WORD(b); + acc128[NMSW128] = HI_WORD(acc); + acc128[NLSW128] = LO_WORD(acc); + add128(acc128, c); + } +} + +/* Note: unsigned */ +extern inline int cmp128(int128 a, int128 b) +{ + if (a[MSW128] < b[MSW128]) + return -1; + if (a[MSW128] > b[MSW128]) + return 1; + if (a[NMSW128] < b[NMSW128]) + return -1; + if (a[NMSW128] > b[NMSW128]) + return 1; + if (a[NLSW128] < b[NLSW128]) + return -1; + if (a[NLSW128] > b[NLSW128]) + return 1; + + return (signed) a[LSW128] - b[LSW128]; +} + +inline void div128(int128 a, int128 b, int128 c) +{ + int128 mask; + + /* Algorithm: + + Shift the divisor until it's at least as big as the + dividend, keeping track of the position to which we've + shifted it, i.e. the power of 2 which we've multiplied it + by. + + Then, for this power of 2 (the mask), and every one smaller + than it, subtract the mask from the dividend and add it to + the quotient until the dividend is smaller than the raised + divisor. At this point, divide the dividend and the mask + by 2 (i.e. shift one place to the right). Lather, rinse, + and repeat, until there are no more powers of 2 left. */ + + /* FIXME: needless to say, there's room for improvement here too. */ + + /* Shift up */ + /* XXX: since it just has to be "at least as big", we can + probably eliminate this horribly wasteful loop. I will + have to prove this first, though */ + set128(0, 0, 0, 1, mask); + while (cmp128(b, a) < 0 && !btsthi128(b)) { + lslone128(b); + lslone128(mask); + } + + /* Shift down */ + zero128(c); + do { + if (cmp128(a, b) >= 0) { + sub128(b, a); + add128(mask, c); + } + lsrone128(mask); + lsrone128(b); + } while (nonzerop128(mask)); + + /* The remainder is in a... */ +} +#endif + +#endif /* MULTI_ARITH_H */ diff --git a/arch/m68k/mm/fault.c b/arch/m68k/mm/fault.c index ef1b855bd72b..80cc5d3667ec 100644 --- a/arch/m68k/mm/fault.c +++ b/arch/m68k/mm/fault.c @@ -19,6 +19,48 @@ extern void die_if_kernel(char *, struct pt_regs *, long); extern const int frame_extra_sizes[]; /* in m68k/kernel/signal.c */ +int send_fault_sig(struct pt_regs *regs) +{ + if (user_mode(regs)) { + force_sig_info(current->buserr_info.si_signo, + ¤t->buserr_info, current); + } else { + unsigned long fixup; + + /* Are we prepared to handle this kernel fault? */ + if ((fixup = search_exception_table(regs->pc))) { + struct pt_regs *tregs; + /* Create a new four word stack frame, discarding the old + one. */ + regs->stkadj = frame_extra_sizes[regs->format]; + tregs = (struct pt_regs *)((ulong)regs + regs->stkadj); + tregs->vector = regs->vector; + tregs->format = 0; + tregs->pc = fixup; + tregs->sr = regs->sr; + return -1; + } + + if (current->buserr_info.si_signo == SIGBUS) + force_sig_info(current->buserr_info.si_signo, + ¤t->buserr_info, current); + + /* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + if ((unsigned long)current->buserr_info.si_addr < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel access"); + printk(" at virtual address %p\n", current->buserr_info.si_addr); + die_if_kernel("Oops", regs, 0 /*error_code*/); + do_exit(SIGKILL); + } + + return 1; +} + /* * This routine handles page faults. It determines the problem, and * then passes it off to one of the appropriate routines. @@ -30,12 +72,11 @@ extern const int frame_extra_sizes[]; /* in m68k/kernel/signal.c */ * If this routine detects a bad access, it returns 1, otherwise it * returns 0. */ -asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, - unsigned long error_code) +int do_page_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code) { struct mm_struct *mm = current->mm; struct vm_area_struct * vma; - unsigned long fixup; int write; #ifdef DEBUG @@ -56,23 +97,23 @@ asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, vma = find_vma(mm, address); if (!vma) - goto bad_area; + goto map_err; if (vma->vm_flags & VM_IO) - goto bad_area; + goto acc_err; if (vma->vm_start <= address) goto good_area; if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; + goto map_err; if (user_mode(regs)) { /* Accessing the stack below usp is always a bug. The "+ 256" is there due to some instructions doing pre-decrement on the stack and that doesn't show up until later. */ if (address + 256 < rdusp()) - goto bad_area; + goto map_err; } if (expand_stack(vma, address)) - goto bad_area; + goto map_err; /* * Ok, we have a good vm_area for this memory access, so @@ -85,14 +126,14 @@ good_area: /* fall through */ case 2: /* write, not present */ if (!(vma->vm_flags & VM_WRITE)) - goto bad_area; + goto acc_err; write++; break; case 1: /* read, present */ - goto bad_area; + goto acc_err; case 0: /* read, not present */ if (!(vma->vm_flags & (VM_READ | VM_EXEC))) - goto bad_area; + goto acc_err; } /* @@ -101,77 +142,42 @@ good_area: * the fault. */ if (!handle_mm_fault(current, vma, address, write)) - goto do_sigbus; + goto bus_err; /* There seems to be a missing invalidate somewhere in do_no_page. * Until I found it, this one cures the problem and makes * 1.2 run on the 68040 (Martin Apel). */ + if (CPU_IS_040_OR_060) flush_tlb_page(vma, address); - up(&mm->mmap_sem); - return 0; -/* - * Something tried to access memory that isn't in our memory map.. - * Fix it, but check if it's kernel or user first.. - */ -bad_area: up(&mm->mmap_sem); - - /* User mode accesses just cause a SIGSEGV */ - if (user_mode(regs)) { - siginfo_t info; - info.si_signo = SIGSEGV; - info.si_code = SEGV_MAPERR; - info.si_addr = (void *)address; - force_sig_info(SIGSEGV, &info, current); - return 1; - } + return 0; no_context: - /* Are we prepared to handle this kernel fault? */ - if ((fixup = search_exception_table(regs->pc)) != 0) { - struct pt_regs *tregs; - /* Create a new four word stack frame, discarding the old - one. */ - regs->stkadj = frame_extra_sizes[regs->format]; - tregs = (struct pt_regs *)((ulong)regs + regs->stkadj); - tregs->vector = regs->vector; - tregs->format = 0; - tregs->pc = fixup; - tregs->sr = regs->sr; - return -1; - } - -/* - * Oops. The kernel tried to access some bad page. We'll have to - * terminate things with extreme prejudice. - */ - if ((unsigned long) address < PAGE_SIZE) { - printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - } else - printk(KERN_ALERT "Unable to handle kernel access"); - printk(" at virtual address %08lx\n",address); - die_if_kernel("Oops", regs, error_code); - do_exit(SIGKILL); - -/* - * We ran out of memory, or some other thing happened to us that made - * us unable to handle the page fault gracefully. - */ -do_sigbus: + current->buserr_info.si_signo = SIGBUS; + current->buserr_info.si_addr = (void *)address; + return send_fault_sig(regs); + +bus_err: + current->buserr_info.si_signo = SIGBUS; + current->buserr_info.si_code = BUS_ADRERR; + current->buserr_info.si_addr = (void *)address; + goto send_sig; + +map_err: + current->buserr_info.si_signo = SIGSEGV; + current->buserr_info.si_code = SEGV_MAPERR; + current->buserr_info.si_addr = (void *)address; + goto send_sig; + +acc_err: + current->buserr_info.si_signo = SIGSEGV; + current->buserr_info.si_code = SEGV_ACCERR; + current->buserr_info.si_addr = (void *)address; + +send_sig: up(&mm->mmap_sem); - - /* - * Send a sigbus, regardless of whether we were in kernel - * or user mode. - */ - force_sig(SIGBUS, current); - - /* Kernel mode? Handle exceptions or die */ - if (!user_mode(regs)) - goto no_context; - - return 1; + return send_fault_sig(regs); } diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 9a41dc279564..b479970b832e 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -139,6 +139,7 @@ __initfunc(static pte_t * kernel_page_table(unsigned long *memavailp)) } static pmd_t *last_pgtable __initdata = NULL; +static pmd_t *zero_pgtable __initdata = NULL; __initfunc(static pmd_t * kernel_ptr_table(unsigned long *memavailp)) { @@ -236,7 +237,8 @@ map_chunk (unsigned long addr, long size, unsigned long *memavailp)) #ifdef DEBUG printk ("[zero map]"); #endif - pte_dir = (pte_t *)kernel_ptr_table(memavailp); + zero_pgtable = kernel_ptr_table(memavailp); + pte_dir = (pte_t *)zero_pgtable; pmd_dir->pmd[0] = virt_to_phys(pte_dir) | _PAGE_TABLE | _PAGE_ACCESSED; pte_val(*pte_dir++) = 0; @@ -380,7 +382,7 @@ __initfunc(unsigned long paging_init(unsigned long start_mem, "pmove %0,%%crp\n\t" ".chip 68k" : /* no outputs */ - : "m" (task[0]->tss.crp[0])); + : "m" (task[0]->tss.crp[1]|_PAGE_TABLE)); #ifdef DEBUG printk ("set crp\n"); #endif @@ -450,6 +452,9 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) if (pgd_present(kernel_pg_dir[i])) init_pointer_table(pgd_page(kernel_pg_dir[i])); } + /* insert also pointer table that we used to unmap the zero page */ + if (zero_pgtable) + init_pointer_table((unsigned long)zero_pgtable); printk("Memory: %luk/%luk available (%dk kernel code, %dk data, %dk init)\n", (unsigned long) nr_free_pages << (PAGE_SHIFT-10), diff --git a/arch/m68k/mm/kmap.c b/arch/m68k/mm/kmap.c index 60912a5b7752..999110b0727b 100644 --- a/arch/m68k/mm/kmap.c +++ b/arch/m68k/mm/kmap.c @@ -25,6 +25,7 @@ #undef DEBUG #define PTRTREESIZE (256*1024) +#define ROOTTREESIZE (32*1024*1024) /* * For 040/060 we can use the virtual memory area like other architectures, @@ -189,7 +190,20 @@ void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) printk ("\npa=%#lx va=%#lx ", physaddr, virtaddr); #endif pgd_dir = pgd_offset_k(virtaddr); - pmd_dir = pmd_alloc_kernel(pgd_dir, virtaddr); + if (CPU_IS_020_OR_030) { + if (!(virtaddr & (ROOTTREESIZE-1)) && + size >= ROOTTREESIZE) { + pgd_val(*pgd_dir) = physaddr; + size -= ROOTTREESIZE; + virtaddr += ROOTTREESIZE; + physaddr += ROOTTREESIZE; + continue; + } + } + if (!pgd_present(*pgd_dir)) + pmd_dir = pmd_alloc_kernel(pgd_dir, virtaddr); + else + pmd_dir = pmd_offset(pgd_dir, virtaddr); if (!pmd_dir) { printk("ioremap: no mem for pmd_dir\n"); return NULL; @@ -201,7 +215,10 @@ void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) virtaddr += PTRTREESIZE; size -= PTRTREESIZE; } else { - pte_dir = pte_alloc_kernel(pmd_dir, virtaddr); + if (!pmd_present(*pmd_dir)) + pte_dir = pte_alloc_kernel(pmd_dir, virtaddr); + else + pte_dir = pte_offset(pmd_dir, virtaddr); if (!pte_dir) { printk("ioremap: no mem for pte_dir\n"); return NULL; diff --git a/arch/m68k/mvme147/config.c b/arch/m68k/mvme147/config.c index 2fe61766a3ec..01315e588912 100644 --- a/arch/m68k/mvme147/config.c +++ b/arch/m68k/mvme147/config.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -63,6 +64,14 @@ static int bcd2int (unsigned char b); void (*tick_handler)(int, void *, struct pt_regs *); +int mvme147_parse_bootinfo(const struct bi_record *bi) +{ + if (bi->tag == BI_VME_TYPE || bi->tag == BI_VME_BRDINFO) + return 0; + else + return 1; +} + int mvme147_kbdrate (struct kbd_repeat *k) { return 0; diff --git a/arch/m68k/mvme16x/Makefile b/arch/m68k/mvme16x/Makefile index bb7485ab8985..1c6bfd3ca828 100644 --- a/arch/m68k/mvme16x/Makefile +++ b/arch/m68k/mvme16x/Makefile @@ -9,6 +9,6 @@ O_TARGET := mvme16x.o O_OBJS := config.o 16xints.o rtc.o -#OX_OBJS = ksyms.o +OX_OBJS := mvme16x_ksyms.o include $(TOPDIR)/Rules.make diff --git a/arch/m68k/mvme16x/config.c b/arch/m68k/mvme16x/config.c index db7eb3137e08..030c7bbf26c5 100644 --- a/arch/m68k/mvme16x/config.c +++ b/arch/m68k/mvme16x/config.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,7 @@ int atari_SCC_reset_done = 1; /* So SCC doesn't get reset */ u_long atari_mch_cookie = 0; +extern unsigned long mvme_bdid; static MK48T08ptr_t volatile rtc = (MK48T08ptr_t)MVME_RTC_BASE; @@ -72,6 +74,14 @@ static void (*tick_handler)(int, void *, struct pt_regs *); unsigned short mvme16x_config; +int mvme16x_parse_bootinfo(const struct bi_record *bi) +{ + if (bi->tag == BI_VME_TYPE || bi->tag == BI_VME_BRDINFO) + return 0; + else + return 1; +} + int mvme16x_kbdrate (struct kbd_repeat *k) { return 0; @@ -92,7 +102,7 @@ void mvme16x_reset() static void mvme16x_get_model(char *model) { - p_bdid p = (p_bdid)mvme_bdid_ptr; + p_bdid p = (p_bdid)&mvme_bdid; char suf[4]; suf[1] = p->brdsuffix[0]; @@ -106,7 +116,7 @@ static void mvme16x_get_model(char *model) static int mvme16x_get_hardware_list(char *buffer) { - p_bdid p = (p_bdid)mvme_bdid_ptr; + p_bdid p = (p_bdid)&mvme_bdid; int len = 0; if (p->brdno == 0x0162 || p->brdno == 0x0172) @@ -134,7 +144,7 @@ static int mvme16x_get_hardware_list(char *buffer) __initfunc(void config_mvme16x(void)) { - p_bdid p = (p_bdid)mvme_bdid_ptr; + p_bdid p = (p_bdid)&mvme_bdid; char id[40]; mach_sched_init = mvme16x_sched_init; @@ -200,7 +210,7 @@ __initfunc(void config_mvme16x(void)) static void mvme16x_abort_int (int irq, void *dev_id, struct pt_regs *fp) { - p_bdid p = (p_bdid)mvme_bdid_ptr; + p_bdid p = (p_bdid)&mvme_bdid; unsigned long *new = (unsigned long *)vectors; unsigned long *old = (unsigned long *)0xffe00000; volatile unsigned char uc, *ucp; @@ -233,7 +243,7 @@ static void mvme16x_timer_int (int irq, void *dev_id, struct pt_regs *fp) void mvme16x_sched_init (void (*timer_routine)(int, void *, struct pt_regs *)) { - p_bdid p = (p_bdid)mvme_bdid_ptr; + p_bdid p = (p_bdid)&mvme_bdid; int irq; tick_handler = timer_routine; @@ -299,19 +309,22 @@ int mvme16x_keyb_init (void) extern void mvme167_serial_console_setup(int cflag); extern void serial167_write(struct console *co, const char *str, unsigned cnt); +extern int serial167_wait_key(struct console *co); extern void vme_scc_write(struct console *co, const char *str, unsigned cnt); +extern int vme_scc_wait_key(struct console *co); void mvme16x_init_console_port (struct console *co, int cflag) { - p_bdid p = (p_bdid)mvme_bdid_ptr; + p_bdid p = (p_bdid)&mvme_bdid; switch (p->brdno) { #ifdef CONFIG_MVME162_SCC case 0x0162: case 0x0172: - co->write = vme_scc_write; + co->write = vme_scc_write; + co->wait_key = vme_scc_wait_key; return; #endif #ifdef CONFIG_SERIAL167 @@ -319,7 +332,8 @@ void mvme16x_init_console_port (struct console *co, int cflag) case 0x0167: case 0x0176: case 0x0177: - co->write = serial167_write; + co->write = serial167_write; + co->wait_key = serial167_wait_key; mvme167_serial_console_setup (cflag); return; #endif @@ -371,4 +385,27 @@ void vme_scc_write (struct console *co, const char *str, unsigned count) } restore_flags(flags); } + + +int vme_scc_wait_key (struct console *co) +{ + volatile unsigned char *p = (volatile char *)MVME_SCC_A_ADDR; + unsigned long flags; + int c; + + /* wait for rx buf filled */ + while ((*p & 0x01) == 0) + ; + + save_flags(flags); + cli(); + + *p = 8; + scc_delay(); + c = *p; + + restore_flags(flags); + return c; +} + #endif diff --git a/arch/m68k/mvme16x/mvme16x_ksyms.c b/arch/m68k/mvme16x/mvme16x_ksyms.c new file mode 100644 index 000000000000..7185bb0f0467 --- /dev/null +++ b/arch/m68k/mvme16x/mvme16x_ksyms.c @@ -0,0 +1,6 @@ +#include +#include +#include +#include + +EXPORT_SYMBOL(mvme16x_config); diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 8ad78e5ecb67..8d0630ffe62a 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -94,7 +95,6 @@ kdev_t boot_dev; extern PTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern int probingmem; -extern unsigned long loops_per_sec; unsigned long empty_zero_page[1024]; @@ -240,7 +240,7 @@ __initfunc(void struct device_node *device; /* init to some ~sane value until calibrate_delay() runs */ - loops_per_sec = 50000000; + loops_per_jiffy = 50000000/HZ; #ifdef CONFIG_BLK_DEV_INITRD /* this is fine for chrp */ diff --git a/arch/ppc/kernel/gemini_setup.c b/arch/ppc/kernel/gemini_setup.c index 968b6677f9d6..79b6764db8f4 100644 --- a/arch/ppc/kernel/gemini_setup.c +++ b/arch/ppc/kernel/gemini_setup.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -166,8 +167,6 @@ void __init gemini_openpic_init(void) ioremap( GEMINI_MPIC_ADDR, sizeof( struct OpenPIC )); } - -extern unsigned long loops_per_sec; extern int root_mountflags; extern char cmd_line[]; @@ -179,7 +178,7 @@ gemini_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p) extern char cmd_line[]; - loops_per_sec = 50000000; + loops_per_jiffy = 50000000/HZ; #ifdef CONFIG_BLK_DEV_INITRD /* bootable off CDROM */ diff --git a/arch/ppc/kernel/mbx_setup.c b/arch/ppc/kernel/mbx_setup.c index d1e96545e265..d4f3c85fce96 100644 --- a/arch/ppc/kernel/mbx_setup.c +++ b/arch/ppc/kernel/mbx_setup.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -56,8 +57,6 @@ extern char mackbd_unexpected_up(unsigned char keycode); extern void mackbd_leds(unsigned char leds); extern void mackbd_init_hw(void); -extern unsigned long loops_per_sec; - unsigned long empty_zero_page[1024]; #ifdef CONFIG_BLK_DEV_RAM diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index 9f8cf5b978dd..78def39c3639 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -260,13 +260,13 @@ pmac_setup_arch(unsigned long *memory_start_p, unsigned long *memory_end_p)) case 10: /* mach V (604ev5) */ case 12: /* G4 */ case 20: /* 620 */ - loops_per_sec = *fp; + loops_per_jiffy = (*fp)/HZ; break; default: /* 601, 603, etc. */ - loops_per_sec = *fp / 2; + loops_per_jiffy = (*fp / 2)/HZ; } } else - loops_per_sec = 50000000; + loops_per_jiffy = 50000000/HZ; } /* this area has the CPU identification register diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index a85b027a515e..7b8137106f7b 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -100,7 +101,6 @@ unsigned long empty_zero_page[1024]; extern PTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern int probingmem; -extern unsigned long loops_per_sec; #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ @@ -218,7 +218,7 @@ prep_setup_arch(unsigned long * memory_start_p, unsigned long * memory_end_p)) unsigned char ucEquipPres1; /* init to some ~sane value until calibrate_delay() runs */ - loops_per_sec = 50000000; + loops_per_jiffy = 50000000/HZ; /* Set up floppy in PS/2 mode */ outb(0x09, SIO_CONFIG_RA); diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index f66c5b6de6e9..4d3a9a9e36da 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -313,9 +313,9 @@ int get_cpuinfo(char *buffer) (GET_PVR & 0xff00) >> 8, GET_PVR & 0xff); len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", - (CD(loops_per_sec)+2500)/500000, - (CD(loops_per_sec)+2500)/5000 % 100); - bogosum += CD(loops_per_sec); + (CD(loops_per_jiffy*HZ)+2500)/500000, + (CD(loops_per_jiffy*HZ)+2500)/5000 % 100); + bogosum += CD(loops_per_jiffy); } #ifdef __SMP__ @@ -713,7 +713,7 @@ void ppc_generic_ide_fix_driveid(struct hd_driveid *id) id->word123 = __le16_to_cpu(id->word123); id->word124 = __le16_to_cpu(id->word124); id->word125 = __le16_to_cpu(id->word125); - id->word126 = __le16_to_cpu(id->word126); + id->last_lun = __le16_to_cpu(id->last_lun); id->word127 = __le16_to_cpu(id->word127); id->security = __le16_to_cpu(id->security); for (i=0; i<127; i++) diff --git a/arch/ppc/kernel/smp.c b/arch/ppc/kernel/smp.c index 237aa4e2ec20..dea942bc267e 100644 --- a/arch/ppc/kernel/smp.c +++ b/arch/ppc/kernel/smp.c @@ -461,7 +461,7 @@ void __init smp_store_cpu_info(int id) { struct cpuinfo_PPC *c = &cpu_data[id]; /* assume bogomips are same for everything */ - c->loops_per_sec = loops_per_sec; + c->loops_per_jiffy = loops_per_jiffy; c->pvr = _get_PVR(); } diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index 06d896dacd4c..10163c632aed 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -93,6 +93,7 @@ CONFIG_MD_STRIPED=m CONFIG_MD_MIRRORING=m CONFIG_MD_RAID5=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m @@ -133,6 +134,7 @@ CONFIG_ATALK=m # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set +# CONFIG_NET_DIVERT is not set # CONFIG_LLC is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set @@ -262,7 +264,9 @@ CONFIG_NFSD=m # CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y +CONFIG_NFS_V3=y CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set CONFIG_NCP_FS=m # CONFIG_NCPFS_PACKET_SIGNING is not set # CONFIG_NCPFS_IOCTL_LOCKING is not set @@ -287,6 +291,7 @@ CONFIG_NLS=y # # Native Language Support # +CONFIG_NLS_DEFAULT="cp437" # CONFIG_NLS_CODEPAGE_437 is not set # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set @@ -303,6 +308,10 @@ CONFIG_NLS=y # CONFIG_NLS_CODEPAGE_866 is not set # CONFIG_NLS_CODEPAGE_869 is not set # CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_950 is not set # CONFIG_NLS_ISO8859_1 is not set # CONFIG_NLS_ISO8859_2 is not set # CONFIG_NLS_ISO8859_3 is not set diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index de6cfa3ea354..eb2b331caf16 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1,4 +1,4 @@ -/* $Id: entry.S,v 1.159.2.7 2000/01/21 01:05:35 davem Exp $ +/* $Id: entry.S,v 1.159.2.8 2000/10/05 04:17:17 anton Exp $ * arch/sparc/kernel/entry.S: Sparc trap low-level entry points. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -1807,13 +1807,13 @@ C_LABEL(fpload): C_LABEL(udelay): save %sp, -REGWIN_SZ, %sp mov %i0, %o0 - sethi %hi(0x10c6), %o1 + sethi %hi(0x10c600), %o1 call .umul - or %o1, %lo(0x10c6), %o1 + or %o1, %lo(0x10c600), %o1 #ifndef __SMP__ - sethi %hi(C_LABEL(loops_per_sec)), %o3 + sethi %hi(C_LABEL(loops_per_jiffy)), %o3 call .umul - ld [%o3 + %lo(C_LABEL(loops_per_sec))], %o1 + ld [%o3 + %lo(C_LABEL(loops_per_jiffy))], %o1 #else GET_PROCESSOR_OFFSET(o4, o2) set C_LABEL(cpu_data), %o3 diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index 823100e7c5d3..c3d01afa9c64 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.105.2.1 1999/11/16 06:29:31 davem Exp $ +/* $Id: setup.c,v 1.105.2.2 2000/10/05 04:17:17 anton Exp $ * linux/arch/sparc/kernel/setup.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -531,7 +531,7 @@ int get_cpuinfo(char *buffer) &cputypval, linux_num_cpus, smp_num_cpus #ifndef __SMP__ - , loops_per_sec/500000, (loops_per_sec/5000) % 100 + , loops_per_jiffy/(500000/HZ), (loops_per_jiffy/(5000/HZ)) % 100 #endif ); #ifdef __SMP__ diff --git a/arch/sparc/kernel/smp.c b/arch/sparc/kernel/smp.c index fb55c1ffabe1..3e7e67f8b512 100644 --- a/arch/sparc/kernel/smp.c +++ b/arch/sparc/kernel/smp.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -85,7 +86,7 @@ __initfunc(void smp_setup(char *str, int *ints)) __initfunc(void smp_store_cpu_info(int id)) { - cpu_data[id].udelay_val = loops_per_sec; /* this is it on sparc. */ + cpu_data[id].udelay_val = loops_per_jiffy; /* this is it on sparc. */ } __initfunc(void smp_commence(void)) @@ -288,8 +289,8 @@ int smp_bogo_info(char *buf) if (cpu_present_map & (1 << i)) len += sprintf(buf + len, "Cpu%dBogo\t: %lu.%02lu\n", i, - cpu_data[i].udelay_val/500000, - (cpu_data[i].udelay_val/5000)%100); + cpu_data[i].udelay_val/(500000/HZ), + (cpu_data[i].udelay_val/(5000/HZ))%100); return len; } diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 1cff15a09544..37083ae8089e 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -278,11 +278,11 @@ __initfunc(void smp4d_boot_cpus(void)) smp_highest_cpu = i; } } - SMP_PRINTK(("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, (bogosum + 2500)/500000, ((bogosum + 2500)/5000)%100)); + SMP_PRINTK(("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, bogosum/(500000/HZ), (bogosum/(5000/HZ))%100)); printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, - (bogosum + 2500)/500000, - ((bogosum + 2500)/5000)%100); + bogosum/(500000/HZ), + (bogosum/(5000/HZ))%100); smp_activated = 1; smp_num_cpus = cpucount + 1; } diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index 09dd2d0bf6e7..bcd638cc3d32 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c @@ -243,8 +243,8 @@ __initfunc(void smp4m_boot_cpus(void)) } printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, - (bogosum + 2500)/500000, - ((bogosum + 2500)/5000)%100); + bogosum/(500000/HZ), + (bogosum/(5000/HZ))%100); smp_activated = 1; smp_num_cpus = cpucount + 1; } diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 086a4a901a29..2763aa73fbbe 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -100,6 +100,7 @@ CONFIG_PARPORT_LOWLEVEL_MODULE=y CONFIG_PRINTER=m CONFIG_PRINTER_READBACK=y CONFIG_ENVCTRL=m +CONFIG_DISPLAY7SEG=m # # Floppy, IDE, and other block devices @@ -111,6 +112,7 @@ CONFIG_MD_STRIPED=m CONFIG_MD_MIRRORING=m CONFIG_MD_RAID5=m CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 8c0f4bdd686a..0c230f2cc27e 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.43.2.4 2000/09/16 16:56:08 davem Exp $ +/* $Id: setup.c,v 1.43.2.5 2000/10/02 02:05:37 anton Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -642,7 +642,7 @@ int get_cpuinfo(char *buffer) prom_rev, prom_prev >> 16, (prom_prev >> 8) & 0xff, prom_prev & 0xff, linux_num_cpus, smp_num_cpus #ifndef __SMP__ - , loops_per_sec/500000, (loops_per_sec/5000) % 100 + , loops_per_jiffy/(500000/HZ), (loops_per_jiffy/(5000/HZ)) % 100 #endif ); #ifdef __SMP__ diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index cfb1749774ac..f4963632ed3d 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -78,8 +78,8 @@ int smp_bogo(char *buf) if(cpu_present_map & (1UL << i)) len += sprintf(buf + len, "Cpu%dBogo\t: %lu.%02lu\n", - i, cpu_data[i].udelay_val / 500000, - (cpu_data[i].udelay_val / 5000) % 100); + i, cpu_data[i].udelay_val / (500000/HZ), + (cpu_data[i].udelay_val / (5000/HZ)) % 100); return len; } @@ -91,7 +91,7 @@ __initfunc(void smp_store_cpu_info(int id)) cpu_data[id].bh_count = 0; /* multiplier and counter set by smp_setup_percpu_timer() */ - cpu_data[id].udelay_val = loops_per_sec; + cpu_data[id].udelay_val = loops_per_jiffy; cpu_data[id].pgcache_size = 0; cpu_data[id].pte_cache[0] = NULL; @@ -280,8 +280,8 @@ __initfunc(void smp_boot_cpus(void)) } printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, - (bogosum + 2500)/500000, - ((bogosum + 2500)/5000)%100); + (bogosum + 2500)/(500000/HZ), + ((bogosum + 2500)/(5000/HZ))%100); smp_activated = 1; smp_num_cpus = cpucount + 1; } diff --git a/drivers/Makefile b/drivers/Makefile index fbb1f3c2ac35..61aa7a7140ba 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -10,7 +10,7 @@ SUB_DIRS := block char net misc sound MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp \ - macintosh video dio zorro fc4 usb telephony i2o + macintosh video dio zorro fc4 nubus usb telephony i2o ifdef CONFIG_DIO SUB_DIRS += dio diff --git a/drivers/block/acsi.c b/drivers/block/acsi.c index 8cad4ba93592..72385db02b45 100644 --- a/drivers/block/acsi.c +++ b/drivers/block/acsi.c @@ -252,17 +252,8 @@ static int CurrentNSect; static char *CurrentBuffer; -#define SET_TIMER() \ - do { \ - del_timer( &acsi_timer ); \ - acsi_timer.expires = jiffies + ACSI_TIMEOUT; \ - add_timer( &acsi_timer ); \ - } while(0) - -#define CLEAR_TIMER() \ - do { \ - del_timer( &acsi_timer ); \ - } while(0) +#define SET_TIMER() mod_timer(&acsi_timer, jiffies + ACSI_TIMEOUT) +#define CLEAR_TIMER() del_timer(&acsi_timer) static unsigned long STramMask; #define STRAM_ADDR(a) (((a) & STramMask) == 0) @@ -424,8 +415,8 @@ int acsi_wait_for_IRQ( unsigned timeout ) { if (INT_LEVEL < 6) { - unsigned long maxjif; - for( maxjif = jiffies + timeout; time_before(jiffies, maxjif); ) + unsigned long maxjif = jiffies + timeout; + while (time_before(jiffies, maxjif)) if (!(mfp.par_dt_reg & 0x20)) return( 1 ); } else { @@ -441,8 +432,8 @@ int acsi_wait_for_noIRQ( unsigned timeout ) { if (INT_LEVEL < 6) { - unsigned long maxjif; - for( maxjif = jiffies + timeout; time_before(jiffies, maxjif); ) + unsigned long maxjif = jiffies + timeout; + while (time_before(jiffies, maxjif)) if (mfp.par_dt_reg & 0x20) return( 1 ); } else { @@ -501,7 +492,7 @@ static int acsicmd_dma( const char *cmd, char *buffer, int blocks, int rwflag, i #endif rwflag = rwflag ? 0x100 : 0; - paddr = VTOP( buffer ); + paddr = virt_to_phys( buffer ); acsi_delay_end(COMMAND_DELAY); DISABLE_IRQ(); @@ -609,7 +600,7 @@ static int acsi_reqsense( char *buffer, int targ, int lun) if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 ); if (!acsi_wait_for_IRQ( 10 )) return( 0 ); acsi_getstatus(); - dma_cache_maintenance( VTOP(buffer), 16, 0 ); + dma_cache_maintenance( virt_to_phys(buffer), 16, 0 ); return( 1 ); } @@ -808,7 +799,7 @@ static void read_intr( void ) return; } - dma_cache_maintenance( VTOP(CurrentBuffer), CurrentNSect*512, 0 ); + dma_cache_maintenance( virt_to_phys(CurrentBuffer), CurrentNSect*512, 0 ); if (CurrentBuffer == acsi_buffer) copy_from_acsibuffer(); @@ -1029,7 +1020,7 @@ static void redo_acsi_request( void ) * consecutive buffers and thus can be done with a single command. */ buffer = CURRENT->buffer; - pbuffer = VTOP(buffer); + pbuffer = virt_to_phys(buffer); nsect = CURRENT->current_nr_sectors; CurrentNReq = 1; @@ -1051,7 +1042,7 @@ static void redo_acsi_request( void ) unsigned long pendadr, pnewadr; pendadr = pbuffer + nsect*512; while( (bh = bh->b_reqnext) ) { - pnewadr = VTOP(bh->b_data); + pnewadr = virt_to_phys(bh->b_data); if (!STRAM_ADDR(pnewadr) || pendadr != pnewadr) break; nsect += bh->b_size >> 9; pendadr = pnewadr + bh->b_size; @@ -1814,7 +1805,7 @@ int acsi_init( void ) unregister_blkdev( MAJOR_NR, "ad" ); return -ENOMEM; } - phys_acsi_buffer = VTOP( acsi_buffer ); + phys_acsi_buffer = virt_to_phys( acsi_buffer ); STramMask = ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 : 0xff000000; blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; diff --git a/drivers/block/acsi_slm.c b/drivers/block/acsi_slm.c index 1f5d31b8897b..7910dff65124 100644 --- a/drivers/block/acsi_slm.c +++ b/drivers/block/acsi_slm.c @@ -413,8 +413,8 @@ static void start_print( int device ) CMDSET_TARG_LUN( slmprint_cmd, sip->target, sip->lun ); cmd = slmprint_cmd; - paddr = VTOP( SLMBuffer ); - dma_cache_maintenance( paddr, VTOP(BufferP)-paddr, 1 ); + paddr = virt_to_phys( SLMBuffer ); + dma_cache_maintenance( paddr, virt_to_phys(BufferP)-paddr, 1 ); DISABLE_IRQ(); /* Low on A1 */ @@ -466,7 +466,7 @@ static void slm_interrupt(int irc, void *data, struct pt_regs *fp) addr = get_dma_addr(); stat = acsi_getstatus(); SLMError = (stat < 0) ? SLMSTAT_ACSITO : - (addr < VTOP(BufferP)) ? SLMSTAT_NOTALL : + (addr < virt_to_phys(BufferP)) ? SLMSTAT_NOTALL : stat; dma_wd.dma_mode_status = 0x80; diff --git a/drivers/block/buddha.c b/drivers/block/buddha.c new file mode 100644 index 000000000000..02496d0cdd60 --- /dev/null +++ b/drivers/block/buddha.c @@ -0,0 +1,168 @@ +/* + * linux/drivers/block/buddha.c -- Amiga Buddha and Catweasel IDE Driver + * + * Copyright (C) 1997 by Geert Uytterhoeven + * + * This driver was written by based on the specifications in README.buddha. + * + * 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. + * + * TODO: + * - test it :-) + * - tune the timings using the speed-register + */ + +#include +#include +#include +#include +#include +#include + +#include "ide.h" + +#include +#include + + + /* + * The Buddha has 2 IDE interfaces, the Catweasel has 3 + */ + +#define BUDDHA_NUM_HWIFS 2 +#define CATWEASEL_NUM_HWIFS 3 + + + /* + * Bases of the IDE interfaces (relative to the board address) + */ + +#define BUDDHA_BASE1 0x800 +#define BUDDHA_BASE2 0xa00 +#define BUDDHA_BASE3 0xc00 + +static const u_int buddha_bases[CATWEASEL_NUM_HWIFS] = { + BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3 +}; + + + /* + * Offsets from one of the above bases + */ + +#define BUDDHA_DATA 0x00 +#define BUDDHA_ERROR 0x06 /* see err-bits */ +#define BUDDHA_NSECTOR 0x0a /* nr of sectors to read/write */ +#define BUDDHA_SECTOR 0x0e /* starting sector */ +#define BUDDHA_LCYL 0x12 /* starting cylinder */ +#define BUDDHA_HCYL 0x16 /* high byte of starting cyl */ +#define BUDDHA_SELECT 0x1a /* 101dhhhh , d=drive, hhhh=head */ +#define BUDDHA_STATUS 0x1e /* see status-bits */ +#define BUDDHA_CONTROL 0x11a + +static const u_int buddha_offsets[IDE_NR_PORTS] = { + BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL, + BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, BUDDHA_CONTROL +}; + + + /* + * Other registers + */ + +#define BUDDHA_IRQ1 0xf00 /* MSB = 1, Harddisk is source of */ +#define BUDDHA_IRQ2 0xf40 /* interrupt */ +#define BUDDHA_IRQ3 0xf80 + +static const int buddha_irqports[CATWEASEL_NUM_HWIFS] = { + BUDDHA_IRQ1, BUDDHA_IRQ2, BUDDHA_IRQ3 +}; + +#define BUDDHA_IRQ_MR 0xfc0 /* master interrupt enable */ + + + /* + * Board information + */ + +static u_long buddha_board = 0; +static int buddha_num_hwifs = -1; + + + /* + * Check and acknowledge the interrupt status + */ + +static int buddha_ack_intr(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & 0x80)) + return 0; + return 1; +} + + + /* + * Any Buddha or Catweasel boards present? + */ + +static int find_buddha(void) +{ + u_int key; + const struct ConfigDev *cd; + + buddha_num_hwifs = 0; + if ((key = zorro_find(ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA, 0, 0))) + buddha_num_hwifs = BUDDHA_NUM_HWIFS; + else if ((key = zorro_find(ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL, 0, + 0))) + buddha_num_hwifs = CATWEASEL_NUM_HWIFS; + if (key) { + cd = zorro_get_board(key); + buddha_board = (u_long)cd->cd_BoardAddr; + if (buddha_board) { + buddha_board = ZTWO_VADDR(buddha_board); + /* write to BUDDHA_IRQ_MR to enable the board IRQ */ + *(char *)(buddha_board+BUDDHA_IRQ_MR) = 0; + zorro_config_board(key, 0); + } + } + return buddha_num_hwifs; +} + + + /* + * Probe for a Buddha or Catweasel IDE interface + * We support only _one_ of them, no multiple boards! + */ + +int buddha_probe_hwif(int index, ide_hwif_t *hwif) +{ + static int buddha_index[CATWEASEL_NUM_HWIFS] = { 0, }; + int i; + + if (buddha_num_hwifs < 0 && !find_buddha()) + return 0; + + for (i = 0; i < buddha_num_hwifs; i++) { + if (!buddha_index[i]) { + printk("ide%d: %s IDE interface\n", index, + buddha_num_hwifs == BUDDHA_NUM_HWIFS ? "Buddha" : + "Catweasel"); + buddha_index[i] = index+1; + } + if (buddha_index[i] == index+1) { + ide_setup_ports(hwif, (ide_ioreg_t)(buddha_board+buddha_bases[i]), + buddha_offsets, + (ide_ioreg_t)(buddha_board+buddha_irqports[i]), + buddha_ack_intr); + hwif->irq = IRQ_AMIGA_PORTS; + return 1; + } + } + return 0; +} diff --git a/drivers/block/falconide.c b/drivers/block/falconide.c new file mode 100644 index 000000000000..02339d648852 --- /dev/null +++ b/drivers/block/falconide.c @@ -0,0 +1,72 @@ +/* + * linux/drivers/block/falconide.c -- Atari Falcon IDE Driver + * + * Created 12 Jul 1997 by Geert Uytterhoeven + * + * 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 "ide.h" + +#include +#include +#include + + + /* + * Base of the IDE interface + */ + +#define ATA_HD_BASE 0xfff00000 + + /* + * Offsets from the above base + */ + +#define ATA_HD_DATA 0x00 +#define ATA_HD_ERROR 0x05 /* see err-bits */ +#define ATA_HD_NSECTOR 0x09 /* nr of sectors to read/write */ +#define ATA_HD_SECTOR 0x0d /* starting sector */ +#define ATA_HD_LCYL 0x11 /* starting cylinder */ +#define ATA_HD_HCYL 0x15 /* high byte of starting cyl */ +#define ATA_HD_SELECT 0x19 /* 101dhhhh , d=drive, hhhh=head */ +#define ATA_HD_STATUS 0x1d /* see status-bits */ +#define ATA_HD_CONTROL 0x39 + +static const int falconide_offsets[IDE_NR_PORTS] = { + ATA_HD_DATA, ATA_HD_ERROR, ATA_HD_NSECTOR, ATA_HD_SECTOR, ATA_HD_LCYL, + ATA_HD_HCYL, ATA_HD_SELECT, ATA_HD_STATUS, ATA_HD_CONTROL +}; + + + /* + * Probe for a Falcon IDE interface + */ + +int falconide_probe_hwif(int index, ide_hwif_t *hwif) +{ + static int falcon_index = 0; + + if (!MACH_IS_ATARI || !ATARIHW_PRESENT(IDE)) + return 0; + + if (!falcon_index) { + printk("ide%d: Falcon IDE interface\n", index); + falcon_index = index+1; + } + if (falcon_index == index+1) { + ide_setup_ports(hwif, (ide_ioreg_t)ATA_HD_BASE, falconide_offsets, 0, + NULL); + hwif->irq = IRQ_MFP_IDE; + return 1; + } + return 0; +} diff --git a/drivers/block/gayle.c b/drivers/block/gayle.c new file mode 100644 index 000000000000..b9d6e33f15b4 --- /dev/null +++ b/drivers/block/gayle.c @@ -0,0 +1,169 @@ +/* + * linux/drivers/block/gayle.c -- Amiga Gayle IDE Driver + * + * Created 9 Jul 1997 by Geert Uytterhoeven + * + * 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 "ide.h" + +#include +#include +#include + + + /* + * Bases of the IDE interfaces + */ + +#define GAYLE_BASE_4000 0xdd2020 /* A4000/A4000T */ +#define GAYLE_BASE_1200 0xda0000 /* A1200/A600 */ + + /* + * Offsets from one of the above bases + */ + +#define GAYLE_DATA 0x00 +#define GAYLE_ERROR 0x06 /* see err-bits */ +#define GAYLE_NSECTOR 0x0a /* nr of sectors to read/write */ +#define GAYLE_SECTOR 0x0e /* starting sector */ +#define GAYLE_LCYL 0x12 /* starting cylinder */ +#define GAYLE_HCYL 0x16 /* high byte of starting cyl */ +#define GAYLE_SELECT 0x1a /* 101dhhhh , d=drive, hhhh=head */ +#define GAYLE_STATUS 0x1e /* see status-bits */ +#define GAYLE_CONTROL 0x101a + +static u_int gayle_offsets[IDE_NR_PORTS] = { + GAYLE_DATA, GAYLE_ERROR, GAYLE_NSECTOR, GAYLE_SECTOR, GAYLE_LCYL, + GAYLE_HCYL, GAYLE_SELECT, GAYLE_STATUS, GAYLE_CONTROL +}; + + + /* + * These are at different offsets from the base + */ + +#define GAYLE_IRQ_4000 0xdd3020 /* MSB = 1, Harddisk is source of */ +#define GAYLE_IRQ_1200 0xda9000 /* interrupt */ + + + /* + * Offset of the secondary port for IDE doublers + * Note that GAYLE_CONTROL is NOT available then! + */ + +#define GAYLE_NEXT_PORT 0x1000 + +#ifndef CONFIG_BLK_DEV_IDEDOUBLER +#define GAYLE_NUM_HWIFS 1 +#define GAYLE_NUM_PROBE_HWIFS GAYLE_NUM_HWIFS +#define GAYLE_HAS_CONTROL_REG 1 +#else /* CONFIG_BLK_DEV_IDEDOUBLER */ +#define GAYLE_NUM_HWIFS 2 +#define GAYLE_NUM_PROBE_HWIFS (ide_doubler ? GAYLE_NUM_HWIFS : \ + GAYLE_NUM_HWIFS-1) +#define GAYLE_HAS_CONTROL_REG (!ide_doubler) +int ide_doubler = 0; /* support IDE doublers? */ +#endif /* CONFIG_BLK_DEV_IDEDOUBLER */ + + + /* + * Check and acknowledge the interrupt status + */ + +static int gayle_ack_intr_a4000(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & 0x80)) + return 0; + return 1; +} + +static int gayle_ack_intr_a1200(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & 0x80)) + return 0; + (void)inb(hwif->io_ports[IDE_STATUS_OFFSET]); + outb(0x7c | (ch & 0x03), hwif->io_ports[IDE_IRQ_OFFSET]); + return 1; +} + + + /* + * Probe for a Gayle IDE interface (and optionally for an IDE doubler) + */ + +int gayle_probe_hwif(int index, ide_hwif_t *hwif) +{ + static int gayle_index[GAYLE_NUM_HWIFS] = { 0, }; + int a4000, i; + + if (!MACH_IS_AMIGA) + return 0; + + if (!(a4000 = AMIGAHW_PRESENT(A4000_IDE)) && !AMIGAHW_PRESENT(A1200_IDE)) + return 0; + + if (!GAYLE_HAS_CONTROL_REG) + gayle_offsets[IDE_CONTROL_OFFSET] = -1; + + for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) { + if (!gayle_index[i]) { + switch (i) { + case 0: + printk("ide%d: Gayle IDE interface (A%d style)\n", index, + a4000 ? 4000 : 1200); + break; +#ifdef CONFIG_BLK_DEV_IDEDOUBLER + case 1: + printk("ide%d: IDE doubler\n", index); + break; +#endif /* CONFIG_BLK_DEV_IDEDOUBLER */ + } + gayle_index[i] = index+1; + } + if (gayle_index[i] == index+1) { + ide_ioreg_t base, irqport; + ide_ack_intr_t *ack_intr; + if (a4000) { + base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_4000); + irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_4000); + ack_intr = gayle_ack_intr_a4000; + } else { + base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_1200); + irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_1200); + ack_intr = gayle_ack_intr_a1200; + } + base += i*GAYLE_NEXT_PORT; + ide_setup_ports(hwif, base, gayle_offsets, irqport, ack_intr); +#if 1 /* TESTING */ + if (i == 1) { + volatile u_short *addr = (u_short *)base; + u_short data; + printk("+++ Probing for IDE doubler... "); + *addr = 0xffff; + data = *addr; + printk("probe returned 0x%02x (PLEASE REPORT THIS!!)\n", data); + } +#endif /* TESTING */ + hwif->irq = IRQ_AMIGA_PORTS; + return 1; + } + } + return 0; +} diff --git a/drivers/block/ide.c b/drivers/block/ide.c index f47b7a59370c..d706f4506adb 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -1627,6 +1627,8 @@ int ide_revalidate_disk(kdev_t i_rdev) ide_drive_t *drive; ide_hwgroup_t *hwgroup; unsigned int p, major, minor; + int old_blksize; + extern int *blksize_size[]; long flags; if ((drive = get_info_ptr(i_rdev)) == NULL) @@ -1634,6 +1636,7 @@ int ide_revalidate_disk(kdev_t i_rdev) major = MAJOR(i_rdev); minor = drive->select.b.unit << PARTN_BITS; hwgroup = HWGROUP(drive); + old_blksize = blksize_size[major][minor]; spin_lock_irqsave(&io_request_lock, flags); if (drive->busy || (drive->usage > 1)) { spin_unlock_irqrestore(&io_request_lock, flags); @@ -1651,7 +1654,10 @@ int ide_revalidate_disk(kdev_t i_rdev) if (sb) invalidate_inodes(sb); invalidate_buffers (devp); - set_blocksize(devp, 1024); + if (!old_blksize) + set_blocksize(devp, 1024); + else + set_blocksize(devp, old_blksize); } drive->part[p].start_sect = 0; drive->part[p].nr_sects = 0; diff --git a/drivers/block/macide.c b/drivers/block/macide.c new file mode 100644 index 000000000000..50037b831106 --- /dev/null +++ b/drivers/block/macide.c @@ -0,0 +1,122 @@ +/* + * linux/drivers/block/macide.c -- Macintosh IDE Driver + * + * Copyright (C) 1998 by Michael Schmitz + * + * This driver was written based on information obtained from the MacOS IDE + * driver binary by Mikael Forselius + * + * 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 "ide.h" + +#include +#include +#include + + /* + * Base of the IDE interface (see ATAManager ROM code) + */ + +#define MAC_HD_BASE 0x50f1a000 + + /* + * Offsets from the above base (scaling 4) + */ + +#define MAC_HD_DATA 0x00 +#define MAC_HD_ERROR 0x04 /* see err-bits */ +#define MAC_HD_NSECTOR 0x08 /* nr of sectors to read/write */ +#define MAC_HD_SECTOR 0x0c /* starting sector */ +#define MAC_HD_LCYL 0x10 /* starting cylinder */ +#define MAC_HD_HCYL 0x14 /* high byte of starting cyl */ +#define MAC_HD_SELECT 0x18 /* 101dhhhh , d=drive, hhhh=head */ +#define MAC_HD_STATUS 0x1c /* see status-bits */ +#define MAC_HD_CONTROL 0x38 /* control/altstatus */ + +static const int macide_offsets[IDE_NR_PORTS] = { + MAC_HD_DATA, MAC_HD_ERROR, MAC_HD_NSECTOR, MAC_HD_SECTOR, MAC_HD_LCYL, + MAC_HD_HCYL, MAC_HD_SELECT, MAC_HD_STATUS, MAC_HD_CONTROL +}; + + /* + * Other registers + */ + + /* + * IDE interrupt status register for both (?) hwifs on Quadra + * Initial setting: 0xc + * Guessing again: + * Bit 0+1: some interrupt flags + * Bit 2+3: some interrupt enable + * Bit 4: ?? + * Bit 5: IDE interrupt flag (any hwif) + * Bit 6: maybe IDE interrupt enable (any hwif) ?? + * Bit 7: Any interrupt condition + * + * Only relevant item: bit 5, to be checked by mac_ack_intr + */ + +#define MAC_HD_ISR 0x101 + +static int mac_ack_intr(ide_hwif_t* hwif) +{ + unsigned char isr; + isr = readb(MAC_HD_BASE + MAC_HD_ISR); + if (isr & (1<<5)) { + writeb(isr & ~(1<<5), MAC_HD_BASE + MAC_HD_ISR); + return 1; + } + + return 0; +} + + /* + * Probe for a Macintosh IDE interface + */ + +__initfunc(int macide_probe_hwif(int index, ide_hwif_t *hwif)) +{ + static int mac_index = 0; + + if (!MACH_IS_MAC || macintosh_config->ide_type == 0) + return 0; + + if (!mac_index) { + if (macintosh_config->ide_type == MAC_IDE_QUADRA) + printk("ide%d: Macintosh Quadra IDE interface\n", + index); + else + printk("ide%d: Macintosh Powerbook IDE interface\n", + index); + mac_index = index+1; + } + if (mac_index == index+1) { + ide_setup_ports(hwif, + (ide_ioreg_t)MAC_HD_BASE, + macide_offsets, + 0, NULL); + + if (macintosh_config->ide_type == MAC_IDE_QUADRA) { + hwif->irq = IRQ_NUBUS_F; + } else { + /* PowerBooks - Slot C is the Comm Slot on the + Q630 */ + hwif->irq = IRQ_NUBUS_C; + } + hwif->ack_intr = mac_ack_intr; + + return 1; + } + return 0; +} diff --git a/drivers/block/q40ide.c b/drivers/block/q40ide.c new file mode 100644 index 000000000000..93d302a61967 --- /dev/null +++ b/drivers/block/q40ide.c @@ -0,0 +1,109 @@ +/* + * linux/drivers/block/q40ide.c -- Q40 I/O port IDE Driver + * + * original file created 12 Jul 1997 by Geert Uytterhoeven + * + * 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. + * + * RZ: + * almost identical with pcide.c, maybe we can merge it later. + * Differences: + * max 2 HWIFS for now + * translate portaddresses to q40 native addresses (not yet...) instead rely on in/out[bw] + * address translation + * + */ + +#include +#include +#include +#include +#include + +#include "ide.h" + + /* + * Bases of the IDE interfaces + */ + +#define PCIDE_NUM_HWIFS 2 + +#define PCIDE_BASE1 0x1f0 +#define PCIDE_BASE2 0x170 +#define PCIDE_BASE3 0x1e8 +#define PCIDE_BASE4 0x168 +#define PCIDE_BASE5 0x1e0 +#define PCIDE_BASE6 0x160 + +static const q40ide_ioreg_t pcide_bases[PCIDE_NUM_HWIFS] = { + PCIDE_BASE1, PCIDE_BASE2, /* PCIDE_BASE3, PCIDE_BASE4 , PCIDE_BASE5, + PCIDE_BASE6 */ +}; + + + /* + * Offsets from one of the above bases + */ + +#undef HD_DATA +#define HD_DATA 0x1f0 + +#define PCIDE_REG(x) ((q40ide_ioreg_t)(HD_##x-PCIDE_BASE1)) + +static const int pcide_offsets[IDE_NR_PORTS] = { + PCIDE_REG(DATA), PCIDE_REG(ERROR), PCIDE_REG(NSECTOR), PCIDE_REG(SECTOR), + PCIDE_REG(LCYL), PCIDE_REG(HCYL), PCIDE_REG(CURRENT), PCIDE_REG(STATUS), + PCIDE_REG(CMD) +}; + +int q40ide_default_irq(q40ide_ioreg_t base) +{ + switch (base) { + case 0x1f0: return 14; + case 0x170: return 15; + case 0x1e8: return 11; + default: + return 0; + } +} + +void q40_ide_init_hwif_ports (q40ide_ioreg_t *p, q40ide_ioreg_t base, int *irq) +{ + q40ide_ioreg_t port = base; + int i = 8; + + while (i--) + *p++ = port++; + *p++ = base + 0x206; + if (irq != NULL) + *irq = 0; +} + + + /* + * Probe for PC IDE interfaces + */ + +int q40ide_probe_hwif(int index, ide_hwif_t *hwif) +{ + static int pcide_index[PCIDE_NUM_HWIFS] = { 0, }; + int i; + + if (!MACH_IS_Q40) + return 0; + + for (i = 0; i < PCIDE_NUM_HWIFS; i++) { + if (!pcide_index[i]) { + /*printk("ide%d: Q40 IDE interface\n", index);*/ + pcide_index[i] = index+1; + } + if (pcide_index[i] == index+1) { + ide_setup_ports(hwif,(ide_ioreg_t) pcide_bases[i], pcide_offsets, 0, /*q40_ack_intr???*/ NULL); + hwif->irq = ide_default_irq((ide_ioreg_t)pcide_bases[i]); /*q40_ide_irq[i]; */ /* 14 */ + return 1; + } + } + return 0; +} diff --git a/drivers/block/swim_iop.c b/drivers/block/swim_iop.c new file mode 100644 index 000000000000..462ef7f26e7b --- /dev/null +++ b/drivers/block/swim_iop.c @@ -0,0 +1,674 @@ +/* + * Driver for the SWIM (Super Woz Integrated Machine) IOP + * floppy controller on the Macintosh IIfx and Quadra 900/950 + * + * Written by Joshua M. Thompson (funaho@jurai.org) + * based on the SWIM3 driver (c) 1996 by Paul Mackerras. + * + * 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. + * + * 1999-06-12 (jmt) - Initial implementation. + */ + +/* + * ------------------- + * Theory of Operation + * ------------------- + * + * Since the SWIM IOP is message-driven we implement a simple request queue + * system. One outstanding request may be queued at any given time (this is + * an IOP limitation); only when that request has completed can a new request + * be sent. + */ + +/* This has to be defined before some of the #includes below */ + +#define MAJOR_NR FLOPPY_MAJOR + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "Version 0.1 (1999-06-12)" + +#define MAX_FLOPPIES 4 + +#define IOCTL_MODE_BIT 8 +#define OPEN_WRITE_BIT 16 + +enum swim_state { + idle, + available, + revalidating, + transferring, + ejecting +}; + +struct floppy_state { + enum swim_state state; + int drive_num; /* device number */ + int secpercyl; /* disk geometry information */ + int secpertrack; + int total_secs; + int write_prot; /* 1 if write-protected, 0 if not, -1 dunno */ + int ref_count; + struct timer_list timeout; + int ejected; + struct wait_queue *wait; + int wanted; + int timeout_pending; +}; + +struct swim_iop_req { + int sent; + int complete; + __u8 command[32]; + struct floppy_state *fs; + void (*done)(struct swim_iop_req *); +}; + +static struct swim_iop_req *current_req; +static int floppy_count; + +static struct floppy_state floppy_states[MAX_FLOPPIES]; + +static int floppy_blocksizes[2] = {512,512}; +static int floppy_sizes[2] = {2880,2880}; + +static char *drive_names[7] = { + "not installed", /* DRV_NONE */ + "unknown (1)", /* DRV_UNKNOWN */ + "a 400K drive", /* DRV_400K */ + "an 800K drive" /* DRV_800K */ + "unknown (4)", /* ???? */ + "an FDHD", /* DRV_FDHD */ + "unknown (6)", /* ???? */ + "an Apple HD20" /* DRV_HD20 */ +}; + +int swimiop_init(void); +static void swimiop_init_request(struct swim_iop_req *); +static int swimiop_send_request(struct swim_iop_req *); +static void swimiop_receive(struct iop_msg *, struct pt_regs *); +static void swimiop_status_update(int, struct swim_drvstatus *); +static int swimiop_eject(struct floppy_state *fs); + +static ssize_t floppy_read(struct file *filp, char *buf, + size_t count, loff_t *ppos); +static ssize_t floppy_write(struct file *filp, const char *buf, + size_t count, loff_t *ppos); +static int floppy_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long param); +static int floppy_open(struct inode *inode, struct file *filp); +static int floppy_release(struct inode *inode, struct file *filp); +static int floppy_check_change(kdev_t dev); +static int floppy_revalidate(kdev_t dev); +static int grab_drive(struct floppy_state *fs, enum swim_state state, + int interruptible); +static void release_drive(struct floppy_state *fs); +static void set_timeout(struct floppy_state *fs, int nticks, + void (*proc)(unsigned long)); +static void fd_request_timeout(unsigned long); +static void do_fd_request(void); +static void start_request(struct floppy_state *fs); + +static struct file_operations floppy_fops = { + NULL, /* lseek */ + floppy_read, /* read */ + floppy_write, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + floppy_ioctl, /* ioctl */ + NULL, /* mmap */ + floppy_open, /* open */ + NULL, /* flush */ + floppy_release, /* release */ + block_fsync, /* fsync */ + NULL, /* fasync */ + floppy_check_change, /* check_media_change */ + floppy_revalidate, /* revalidate */ +}; + +/* + * SWIM IOP initialization + */ + +int swimiop_init(void) +{ + volatile struct swim_iop_req req; + struct swimcmd_status *cmd = (struct swimcmd_status *) &req.command[0]; + struct swim_drvstatus *ds = &cmd->status; + struct floppy_state *fs; + int i; + + current_req = NULL; + floppy_count = 0; + + if (!iop_ism_present) return -ENODEV; + + if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) { + printk(KERN_ERR "SWIM-IOP: Unable to get major %d for floppy\n", + MAJOR_NR); + return -EBUSY; + } + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + blksize_size[MAJOR_NR] = floppy_blocksizes; + blk_size[MAJOR_NR] = floppy_sizes; + + printk("SWIM-IOP: %s by Joshua M. Thompson (funaho@jurai.org)\n", + DRIVER_VERSION); + + if (iop_listen(SWIM_IOP, SWIM_CHAN, swimiop_receive, "SWIM") != 0) { + printk(KERN_ERR "SWIM-IOP: IOP channel already in use; can't initialize.\n"); + return -EBUSY; + } + + printk(KERN_ERR "SWIM_IOP: probing for installed drives.\n"); + + for (i = 0 ; i < MAX_FLOPPIES ; i++) { + memset(&floppy_states[i], 0, sizeof(struct floppy_state)); + fs = &floppy_states[floppy_count]; + + swimiop_init_request(&req); + cmd->code = CMD_STATUS; + cmd->drive_num = i + 1; + if (swimiop_send_request(&req) != 0) continue; + while (!req.complete); + if (cmd->error != 0) { + printk(KERN_ERR "SWIM-IOP: probe on drive %d returned error %d\n", i, (uint) cmd->error); + continue; + } + if (ds->installed != 0x01) continue; + printk("SWIM-IOP: drive %d is %s (%s, %s, %s, %s)\n", i, + drive_names[ds->info.type], + ds->info.external? "ext" : "int", + ds->info.scsi? "scsi" : "floppy", + ds->info.fixed? "fixed" : "removable", + ds->info.secondary? "secondary" : "primary"); + swimiop_status_update(floppy_count, ds); + fs->state = idle; + + init_timer(&fs->timeout); + floppy_count++; + } + printk("SWIM-IOP: detected %d installed drives.\n", floppy_count); + + do_floppy = NULL; + + return 0; +} + +static void swimiop_init_request(struct swim_iop_req *req) +{ + req->sent = 0; + req->complete = 0; + req->done = NULL; +} + +static int swimiop_send_request(struct swim_iop_req *req) +{ + unsigned long cpu_flags; + int err; + + /* It's doubtful an interrupt routine would try to send */ + /* a SWIM request, but I'd rather play it safe here. */ + + save_flags(cpu_flags); + cli(); + + if (current_req != NULL) { + restore_flags(cpu_flags); + return -ENOMEM; + } + + current_req = req; + + /* Interrupts should be back on for iop_send_message() */ + + restore_flags(cpu_flags); + + err = iop_send_message(SWIM_IOP, SWIM_CHAN, (void *) req, + sizeof(req->command), (__u8 *) &req->command[0], + swimiop_receive); + + /* No race condition here; we own current_req at this point */ + + if (err) { + current_req = NULL; + } else { + req->sent = 1; + } + return err; +} + +/* + * Receive a SWIM message from the IOP. + * + * This will be called in two cases: + * + * 1. A message has been successfully sent to the IOP. + * 2. An unsolicited message was received from the IOP. + */ + +void swimiop_receive(struct iop_msg *msg, struct pt_regs *regs) +{ + struct swim_iop_req *req; + struct swimmsg_status *sm; + struct swim_drvstatus *ds; + + req = current_req; + + switch(msg->status) { + case IOP_MSGSTATUS_COMPLETE: + memcpy(&req->command[0], &msg->reply[0], sizeof(req->command)); + req->complete = 1; + if (req->done) (*req->done)(req); + current_req = NULL; + break; + case IOP_MSGSTATUS_UNSOL: + sm = (struct swimmsg_status *) &msg->message[0]; + ds = &sm->status; + swimiop_status_update(sm->drive_num, ds); + iop_complete_message(msg); + break; + } +} + +static void swimiop_status_update(int drive_num, struct swim_drvstatus *ds) +{ + struct floppy_state *fs = &floppy_states[drive_num]; + + fs->write_prot = (ds->write_prot == 0x80); + if ((ds->disk_in_drive != 0x01) && (ds->disk_in_drive != 0x02)) { + fs->ejected = 1; + } else { + fs->ejected = 0; + } + switch(ds->info.type) { + case DRV_400K: + fs->secpercyl = 10; + fs->secpertrack = 10; + fs->total_secs = 800; + break; + case DRV_800K: + fs->secpercyl = 20; + fs->secpertrack = 10; + fs->total_secs = 1600; + break; + case DRV_FDHD: + fs->secpercyl = 36; + fs->secpertrack = 18; + fs->total_secs = 2880; + break; + default: + fs->secpercyl = 0; + fs->secpertrack = 0; + fs->total_secs = 0; + break; + } +} + +static int swimiop_eject(struct floppy_state *fs) +{ + int err, n; + struct swim_iop_req req; + struct swimcmd_eject *cmd = (struct swimcmd_eject *) &req.command[0]; + + err = grab_drive(fs, ejecting, 1); + if (err) return err; + + swimiop_init_request(&req); + cmd->code = CMD_EJECT; + cmd->drive_num = fs->drive_num; + err = swimiop_send_request(&req); + if (err) { + release_drive(fs); + return err; + } + for (n = 2*HZ; n > 0; --n) { + if (req.complete) break; + if (signal_pending(current)) { + err = -EINTR; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + release_drive(fs); + return cmd->error; +} + +static ssize_t floppy_read(struct file *filp, char *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct floppy_state *fs; + int devnum = MINOR(inode->i_rdev); + + if (devnum >= floppy_count) + return -ENODEV; + + fs = &floppy_states[devnum]; + if (fs->ejected) + return -ENXIO; + return block_read(filp, buf, count, ppos); +} + +static ssize_t floppy_write(struct file * filp, const char * buf, + size_t count, loff_t *ppos) +{ + struct inode * inode = filp->f_dentry->d_inode; + struct floppy_state *fs; + int devnum = MINOR(inode->i_rdev); + + if (devnum >= floppy_count) + return -ENODEV; + check_disk_change(inode->i_rdev); + fs = &floppy_states[devnum]; + if (fs->ejected) + return -ENXIO; + if (fs->write_prot) + return -EROFS; + return block_write(filp, buf, count, ppos); +} + +static struct floppy_struct floppy_type = + { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */ + +static int floppy_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long param) +{ + struct floppy_state *fs; + int err; + int devnum = MINOR(inode->i_rdev); + + if (devnum >= floppy_count) + return -ENODEV; + + if (((cmd & 0x40) && !(filp && (filp->f_mode & IOCTL_MODE_BIT))) || + ((cmd & 0x80) && !suser())) + return -EPERM; + + fs = &floppy_states[devnum]; + + switch (cmd) { + case FDEJECT: + if (fs->ref_count != 1) + return -EBUSY; + err = swimiop_eject(fs); + return err; + case FDGETPRM: + err = copy_to_user((void *) param, (void *) &floppy_type, + sizeof(struct floppy_struct)); + return err; + } + return -ENOIOCTLCMD; +} + +static int floppy_open(struct inode *inode, struct file *filp) +{ + struct floppy_state *fs; + int err; + int devnum = MINOR(inode->i_rdev); + + if (devnum >= floppy_count) + return -ENODEV; + if (filp == 0) + return -EIO; + + fs = &floppy_states[devnum]; + err = 0; + if (fs->ref_count == -1 || filp->f_flags & O_EXCL) return -EBUSY; + + if (err == 0 && (filp->f_flags & O_NDELAY) == 0 + && (filp->f_mode & 3)) { + check_disk_change(inode->i_rdev); + if (fs->ejected) + err = -ENXIO; + } + + if (err == 0 && (filp->f_mode & 2)) { + if (fs->write_prot) + err = -EROFS; + } + + if (err) return err; + + if (filp->f_flags & O_EXCL) + fs->ref_count = -1; + else + ++fs->ref_count; + + /* Allow ioctls if we have write-permissions even if read-only open */ + if ((filp->f_mode & 2) || (permission(inode, 2) == 0)) + filp->f_mode |= IOCTL_MODE_BIT; + if (filp->f_mode & 2) + filp->f_mode |= OPEN_WRITE_BIT; + + return 0; +} + +static int floppy_release(struct inode *inode, struct file *filp) +{ + struct floppy_state *fs; + int devnum = MINOR(inode->i_rdev); + + if (devnum >= floppy_count) + return -ENODEV; + + /* + * If filp is NULL, we're being called from blkdev_release + * or after a failed mount attempt. In the former case the + * device has already been sync'ed, and in the latter no + * sync is required. Otherwise, sync if filp is writable. + */ + if (filp && (filp->f_mode & (2 | OPEN_WRITE_BIT))) + block_fsync (filp, filp->f_dentry); + + fs = &floppy_states[devnum]; + if (fs->ref_count > 0) fs->ref_count--; + return 0; +} + +static int floppy_check_change(kdev_t dev) +{ + struct floppy_state *fs; + int devnum = MINOR(dev); + + if (MAJOR(dev) != MAJOR_NR || (devnum >= floppy_count)) + return 0; + + fs = &floppy_states[devnum]; + return fs->ejected; +} + +static int floppy_revalidate(kdev_t dev) +{ + struct floppy_state *fs; + int devnum = MINOR(dev); + + if (MAJOR(dev) != MAJOR_NR || (devnum >= floppy_count)) + return 0; + + fs = &floppy_states[devnum]; + + grab_drive(fs, revalidating, 0); + /* yadda, yadda */ + release_drive(fs); + + return 0; +} + +static void floppy_off(unsigned int nr) +{ +} + +static int grab_drive(struct floppy_state *fs, enum swim_state state, + int interruptible) +{ + unsigned long flags; + + save_flags(flags); + cli(); + if (fs->state != idle) { + ++fs->wanted; + while (fs->state != available) { + if (interruptible && signal_pending(current)) { + --fs->wanted; + restore_flags(flags); + return -EINTR; + } + interruptible_sleep_on(&fs->wait); + } + --fs->wanted; + } + fs->state = state; + restore_flags(flags); + return 0; +} + +static void release_drive(struct floppy_state *fs) +{ + unsigned long flags; + + save_flags(flags); + cli(); + fs->state = idle; + start_request(fs); + restore_flags(flags); +} + +static void set_timeout(struct floppy_state *fs, int nticks, + void (*proc)(unsigned long)) +{ + unsigned long flags; + + save_flags(flags); cli(); + if (fs->timeout_pending) + del_timer(&fs->timeout); + fs->timeout.expires = jiffies + nticks; + fs->timeout.function = proc; + fs->timeout.data = (unsigned long) fs; + add_timer(&fs->timeout); + fs->timeout_pending = 1; + restore_flags(flags); +} + +static void do_fd_request(void) +{ + int i; + + for (i = 0 ; i < floppy_count ; i++) { + start_request(&floppy_states[i]); + } +} + +static void fd_request_complete(struct swim_iop_req *req) +{ + struct floppy_state *fs = req->fs; + struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req->command[0]; + + del_timer(&fs->timeout); + fs->timeout_pending = 0; + fs->state = idle; + if (cmd->error) { + printk(KERN_ERR "SWIM-IOP: error %d on read/write request.\n", cmd->error); + end_request(0); + } else { + CURRENT->sector += cmd->num_blocks; + CURRENT->current_nr_sectors -= cmd->num_blocks; + if (CURRENT->current_nr_sectors <= 0) { + end_request(1); + return; + } + } + start_request(fs); +} + +static void fd_request_timeout(unsigned long data) +{ + struct floppy_state *fs = (struct floppy_state *) data; + + fs->timeout_pending = 0; + end_request(0); + fs->state = idle; +} + +static void start_request(struct floppy_state *fs) +{ + volatile struct swim_iop_req req; + struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req.command[0]; + + if (fs->state == idle && fs->wanted) { + fs->state = available; + wake_up(&fs->wait); + return; + } + while (CURRENT && fs->state == idle) { + if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) + panic(DEVICE_NAME ": request list destroyed"); + if (CURRENT->bh && !buffer_locked(CURRENT->bh)) + panic(DEVICE_NAME ": block not locked"); +#if 0 + printk("do_fd_req: dev=%x cmd=%d sec=%ld nr_sec=%ld buf=%p\n", + kdev_t_to_nr(CURRENT->rq_dev), CURRENT->cmd, + CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer); + printk(" rq_status=%d errors=%d current_nr_sectors=%ld\n", + CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors); +#endif + + if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) { + end_request(0); + continue; + } + if (CURRENT->current_nr_sectors == 0) { + end_request(1); + continue; + } + if (fs->ejected) { + end_request(0); + continue; + } + + swimiop_init_request(&req); + req.fs = fs; + req.done = fd_request_complete; + + if (CURRENT->cmd == WRITE) { + if (fs->write_prot) { + end_request(0); + continue; + } + cmd->code = CMD_WRITE; + } else { + cmd->code = CMD_READ; + + } + cmd->drive_num = fs->drive_num; + cmd->buffer = CURRENT->buffer; + cmd->first_block = CURRENT->sector; + cmd->num_blocks = CURRENT->current_nr_sectors; + + if (swimiop_send_request(&req)) { + end_request(0); + continue; + } + + set_timeout(fs, HZ*CURRENT->current_nr_sectors, + fd_request_timeout); + + fs->state = transferring; + } +} diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c index f863a8326fcd..c44d0abd5792 100644 --- a/drivers/block/z2ram.c +++ b/drivers/block/z2ram.c @@ -32,23 +32,18 @@ #include #include #include - -#if defined(MODULE) #include -#endif #include #include #include -#ifdef CONFIG_APUS #include #include -#endif #include -extern int num_memory; -extern struct mem_info memory[NUM_MEMINFO]; +extern int m68k_realnum_memory; +extern struct mem_info m68k_memory[NUM_MEMINFO]; #define TRUE (1) #define FALSE (0) @@ -191,14 +186,14 @@ z2_open( struct inode *inode, struct file *filp ) int index = device - Z2MINOR_MEMLIST1 + 1; unsigned long size, paddr, vaddr; - if (index >= num_memory) { + if (index >= m68k_realnum_memory) { printk( KERN_ERR DEVICE_NAME ": no such entry in z2ram_map\n" ); return -ENOMEM; } - paddr = memory[index].addr; - size = memory[index].size & ~(Z2RAM_CHUNKSIZE-1); + paddr = m68k_memory[index].addr; + size = m68k_memory[index].size & ~(Z2RAM_CHUNKSIZE-1); #ifdef __powerpc__ /* FIXME: ioremap doesn't build correct memory tables. */ @@ -212,8 +207,7 @@ z2_open( struct inode *inode, struct file *filp ) _PAGE_WRITETHRU); #else - vaddr = kernel_map (paddr, size, KERNELMAP_FULL_CACHING, - NULL); + vaddr = (unsigned long)ioremap(paddr, size); #endif z2ram_map = kmalloc((size/Z2RAM_CHUNKSIZE)*sizeof(z2ram_map[0]), @@ -316,9 +310,7 @@ z2_open( struct inode *inode, struct file *filp ) blk_size[ MAJOR_NR ] = z2_sizes; } -#if defined(MODULE) MOD_INC_USE_COUNT; -#endif return 0; } @@ -331,9 +323,11 @@ z2_release( struct inode *inode, struct file *filp ) sync_dev( inode->i_rdev ); -#if defined(MODULE) + /* + * FIXME: unmap memory + */ + MOD_DEC_USE_COUNT; -#endif return 0; } diff --git a/drivers/char/amiga_ser.c b/drivers/char/amiga_ser.c new file mode 100644 index 000000000000..bd85e1db86d5 --- /dev/null +++ b/drivers/char/amiga_ser.c @@ -0,0 +1,558 @@ +/* + * drivers/char/amiga_ser.c: Amiga built-in serial port driver. + * + * Copyright 1994 Roman Hodek, 1994 Hamish Macdonald + * Based on the Atari MFP driver by Roman Hodek + * + * Modifications by Matthias Welwarsky + * - fixed reentrancy problem in ser_tx_int() + * + * 20/02/99 - Jesper Skov: Added mb() calls and KGDB support. + * 27/04/96 - Jes Soerensen: Upgraded for Linux-1.3.x. + * 02/09/96 - Jes Soerensen: Moved the {request,free}_irq call for + * AMIGA_VERTB interrupts into the init/deinit funtions as + * there is no reason to service the ser_vbl_int when the + * serial port is not in use. + * 30/04/96 - Geert Uytterhoeven: Added incoming BREAK detection + * + * 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. + * + */ + + +/* + * This file implements the driver for the Amiga built-in serial port. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* some serial hardware definitions */ +#define SDR_OVRUN (1<<15) +#define SDR_RBF (1<<14) +#define SDR_TBE (1<<13) +#define SDR_TSRE (1<<12) + +#define AC_SETCLR (1<<15) +#define AC_UARTBRK (1<<11) + +#define SER_DTR (1<<7) +#define SER_RTS (1<<6) +#define SER_DCD (1<<5) +#define SER_CTS (1<<4) +#define SER_DSR (1<<3) + +/***************************** Prototypes *****************************/ + +static void ser_rx_int( int irq, void *data, struct pt_regs *fp); +static void ser_tx_int( int irq, void *data, struct pt_regs *fp); +static void ser_vbl_int( int irq, void *data, struct pt_regs *fp); +static void ser_init( struct m68k_async_struct *info ); +static void ser_deinit( struct m68k_async_struct *info, int leave_dtr ); +static void ser_enab_tx_int( struct m68k_async_struct *info, int enab_flag ); +static int ser_check_custom_divisor (struct m68k_async_struct *info, + int baud_base, int divisor); +static void ser_change_speed( struct m68k_async_struct *info ); +static void ser_throttle( struct m68k_async_struct *info, int status ); +static void ser_set_break( struct m68k_async_struct *info, int break_flag ); +static void ser_get_serial_info( struct m68k_async_struct *info, + struct serial_struct *retinfo ); +static unsigned int ser_get_modem_info( struct m68k_async_struct *info ); +static int ser_set_modem_info( struct m68k_async_struct *info, int new_dtr, + int new_rts ); +static void ser_stop_receive( struct m68k_async_struct *info ); +static int ser_trans_empty( struct m68k_async_struct *info ); + +/************************* End of Prototypes **************************/ + + + +/* SERIALSWITCH structure for the Amiga serial port + */ + +static SERIALSWITCH amiga_ser_switch = { + ser_init, ser_deinit, ser_enab_tx_int, + ser_check_custom_divisor, ser_change_speed, + ser_throttle, ser_set_break, + ser_get_serial_info, ser_get_modem_info, + ser_set_modem_info, NULL, ser_stop_receive, ser_trans_empty, NULL +}; + +/* Standard speeds table */ +static int baud_table[19] = { + /* B0 */ 0, + /* B50 */ 50, + /* B75 */ 75, + /* B110 */ 110, + /* B134 */ 134, + /* B150 */ 150, + /* B200 */ 200, + /* B300 */ 300, + /* B600 */ 600, + /* B1200 */ 1200, + /* B1800 */ 1800, + /* B2400 */ 2400, + /* B4800 */ 4800, + /* B9600 */ 9600, + /* B19200 */ 19200, + /* B38400 */ 38400, + /* B57600 */ 57600, + /* B115200*/ 115200, + /* B230400*/ 230400 +}; + +static __inline__ void ser_DTRoff(void) +{ + ciab.pra |= SER_DTR; /* active low */ +} + +static __inline__ void ser_DTRon(void) +{ + ciab.pra &= ~SER_DTR; /* active low */ +} + +static __inline__ void ser_RTSoff(void) +{ + ciab.pra |= SER_RTS; /* active low */ +} + +static __inline__ void ser_RTSon(void) +{ + ciab.pra &= ~SER_RTS; /* active low */ +} + +static int line; /* the serial line assigned by register_serial() */ +/* use this value in isr's to get rid of the data pointer in the future */ +/* This variable holds the current state of the DCD/CTS bits */ +static unsigned char current_ctl_bits; + +static __inline__ void check_modem_status(struct m68k_async_struct *info) +{ + unsigned char bits; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + bits = ciab.pra & (SER_DCD | SER_CTS); + + if (bits ^ current_ctl_bits) { + if ((bits ^ current_ctl_bits) & SER_DCD) { + rs_dcd_changed(info, !(bits & SER_DCD)); + } + + if ((bits ^ current_ctl_bits) & SER_CTS) + rs_check_cts(info, !(bits & SER_CTS)); + } + current_ctl_bits = bits; +} + +static struct m68k_async_struct *amiga_info; + +int amiga_serinit( void ) +{ + unsigned long flags; + struct serial_struct req; + struct m68k_async_struct *info; + + if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_SERIAL)) + return -ENODEV; + + req.line = -1; /* first free ttyS? device */ + req.type = SER_AMIGA; + req.port = (int) &custom.serdatr; /* dummy value */ + if ((line = m68k_register_serial( &req )) < 0) { + printk( "Cannot register built-in serial port: no free device\n" ); + return -EBUSY; + } + info = &rs_table[line]; + + save_flags (flags); + cli(); + + /* set ISRs, and then disable the rx interrupts */ + request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", info); + request_irq(IRQ_AMIGA_RBF, ser_rx_int, 0, "serial RX", info); + + amiga_info = info; + + /* turn off Rx and Tx interrupts */ + custom.intena = IF_RBF | IF_TBE; + + /* clear any pending interrupt */ + custom.intreq = IF_RBF | IF_TBE; + restore_flags (flags); + + /* + * set the appropriate directions for the modem control flags, + * and clear RTS and DTR + */ + ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ + ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ + + info->sw = &amiga_ser_switch; + +#ifdef CONFIG_KGDB + /* turn Rx interrupts on for GDB */ + custom.intena = IF_SETCLR | IF_RBF; + ser_RTSon(); +#endif + + return 0; +} + +static void ser_rx_int(int irq, void *data, struct pt_regs *fp) +{ +#ifdef CONFIG_KGDB + extern void breakpoint (void); + int ch; + + ch = custom.serdatr; + mb(); + + custom.intreq = IF_RBF; + + /* Break signal from GDB? */ + if (0x03 == (ch & 0xff)) { + /* FIXME: This way of doing a breakpoint sucks + big time. I will fix it later. */ + breakpoint (); + } +#else + struct m68k_async_struct *info = data; + int ch, err; + + ch = custom.serdatr; + mb(); + + custom.intreq = IF_RBF; + + if ((ch & 0x1ff) == 0) + err = TTY_BREAK; + else if (ch & SDR_OVRUN) + err = TTY_OVERRUN; + else + err = 0; + rs_receive_char(info, ch & 0xff, err); +#endif +} + +static void ser_tx_int( int irq, void *data, struct pt_regs *fp) +{ +#ifndef CONFIG_KGDB + struct m68k_async_struct *info = data; + int ch; + + if (custom.serdatr & SDR_TBE) { + if ((ch = rs_get_tx_char(info)) >= 0) + /* write next char */ + custom.serdat = ch | 0x100; + if (ch == -1 || rs_no_more_tx( info )) + /* disable tx interrupts */ + custom.intena = IF_TBE; + } +#endif +} + + +static void ser_vbl_int( int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + + check_modem_status(info); +} + + +static void ser_init( struct m68k_async_struct *info ) +{ +#ifndef CONFIG_KGDB + request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, + "serial status", amiga_info); + + /* enable both Rx and Tx interrupts */ + custom.intena = IF_SETCLR | IF_RBF | IF_TBE; + + /* turn on DTR and RTS */ + ser_DTRon(); + ser_RTSon(); + + /* remember current state of the DCD and CTS bits */ + current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS); + + MOD_INC_USE_COUNT; +#endif +} + + +static void ser_enab_tx_int (struct m68k_async_struct *info, int enab_flag) +{ +#ifndef CONFIG_KGDB + if (enab_flag) { + unsigned long flags; + save_flags(flags); + cli(); + custom.intena = IF_SETCLR | IF_TBE; + mb(); + /* set a pending Tx Interrupt, transmitter should restart now */ + custom.intreq = IF_SETCLR | IF_TBE; + mb(); + restore_flags(flags); + } else { + /* disable Tx interrupt and remove any pending interrupts */ + custom.intena = IF_TBE; + custom.intreq = IF_TBE; + } +#endif +} + + +static void ser_deinit( struct m68k_async_struct *info, int leave_dtr ) +{ +#ifndef CONFIG_KGDB + /* disable Rx and Tx interrupt */ + custom.intena = IF_RBF | IF_TBE; + mb(); + + /* wait for last byte to be completely shifted out */ + while( !(custom.serdatr & SDR_TSRE) ) + barrier(); + + /* drop RTS and DTR if required */ + ser_RTSoff(); + if (!leave_dtr) + ser_DTRoff(); + + free_irq(IRQ_AMIGA_VERTB, amiga_info); + MOD_DEC_USE_COUNT; +#endif +} + + +static int ser_check_custom_divisor (struct m68k_async_struct *info, + int baud_base, int divisor) +{ + /* allow any divisor */ + return 0; +} + + +static void ser_change_speed( struct m68k_async_struct *info ) +{ +#ifndef CONFIG_KGDB + unsigned cflag, baud, chsize, stopb, parity, aflags; + unsigned div = 0; + int realbaud; + + if (!info->tty || !info->tty->termios) return; + + cflag = info->tty->termios->c_cflag; + baud = cflag & CBAUD; + chsize = cflag & CSIZE; + stopb = cflag & CSTOPB; + parity = cflag & (PARENB | PARODD); + aflags = info->flags & ASYNC_SPD_MASK; + + if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; + else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + if (baud & CBAUDEX) + baud = baud - (B57600 - B38400 - 1); + + + if (baud == 15) { + switch (aflags) { + case ASYNC_SPD_HI: + baud += 1; + break; + case ASYNC_SPD_VHI: + baud += 2; + break; + case ASYNC_SPD_SHI: + baud += 3; + break; + case ASYNC_SPD_WARP: + baud += 4; + break; + case ASYNC_SPD_CUST: + div = info->custom_divisor; + break; + } + } + if (!div){ + /* Maximum speed is 230400 :-) */ + if (baud > 18) baud = 18; + realbaud = baud_table[baud]; + if (realbaud) + div = (amiga_colorclock+realbaud/2)/realbaud - 1; + } + + if (div) { + /* turn on DTR */ + ser_DTRon(); + } else { + /* speed == 0 -> drop DTR */ + ser_DTRoff(); + return; + } + + /* setup the serial port period register */ + custom.serper = div; +#endif +} + + +static void ser_throttle( struct m68k_async_struct *info, int status ) +{ +#ifndef CONFIG_KGDB + if (status) + ser_RTSoff(); + else + ser_RTSon(); +#endif +} + + +static void ser_set_break( struct m68k_async_struct *info, int break_flag ) +{ +#ifndef CONFIG_KGDB + if (break_flag) + custom.adkcon = AC_SETCLR | AC_UARTBRK; + else + custom.adkcon = AC_UARTBRK; +#endif +} + + +static void ser_get_serial_info( struct m68k_async_struct *info, + struct serial_struct *retinfo ) +{ + retinfo->baud_base = amiga_colorclock; + retinfo->custom_divisor = info->custom_divisor; +} + + +static unsigned int ser_get_modem_info( struct m68k_async_struct *info ) +{ + unsigned int minfo = ciab.pra; + + return( + ((minfo & SER_DTR) ? 0 : TIOCM_DTR) | + ((minfo & SER_RTS) ? 0 : TIOCM_RTS) | + ((minfo & SER_DCD) ? 0 : TIOCM_CAR) | + ((minfo & SER_CTS) ? 0 : TIOCM_CTS) | + ((minfo & SER_DSR) ? 0 : TIOCM_DSR) | + /* TICM_RNG */ 0 + ); +} + + +static int ser_set_modem_info( struct m68k_async_struct *info, + int new_dtr, int new_rts ) +{ +#ifndef CONFIG_KGDB + if (new_dtr == 0) + ser_DTRoff(); + else if (new_dtr == 1) + ser_DTRon(); + + if (new_rts == 0) + ser_RTSoff(); + else if (new_rts == 1) + ser_RTSon(); + + return 0; +#endif +} + +static void ser_stop_receive( struct m68k_async_struct *info ) +{ +#ifndef CONFIG_KGDB + /* disable receive interrupts */ + custom.intena = IF_RBF; + /* clear any pending receive interrupt */ + custom.intreq = IF_RBF; +#endif +} + +static int ser_trans_empty( struct m68k_async_struct *info ) +{ + return (custom.serdatr & SDR_TSRE); +} + +#ifdef CONFIG_KGDB +int amiga_ser_out( unsigned char c ) +{ + custom.serdat = c | 0x100; + mb(); + while (!(custom.serdatr & 0x2000)) + barrier(); + return 1; +} + +unsigned char amiga_ser_in( void ) +{ + unsigned char c; + + /* XXX: is that ok?? derived from amiga_ser.c... */ + while( !(custom.intreqr & IF_RBF) ) + barrier(); + c = custom.serdatr; + /* clear the interrupt, so that another character can be read */ + custom.intreq = IF_RBF; + return c; +} +#endif + +#ifdef MODULE +int init_module(void) +{ + return amiga_serinit(); +} + +void cleanup_module(void) +{ + m68k_unregister_serial(line); + custom.intena = IF_RBF | IF_TBE; /* forbid interrupts */ + custom.intreq = IF_RBF | IF_TBE; /* clear pending interrupts */ + mb(); + free_irq(IRQ_AMIGA_TBE, amiga_info); + free_irq(IRQ_AMIGA_RBF, amiga_info); +} +#endif + + +/* ------------------------------------ + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * End: + * ------------------------------------ + */ diff --git a/drivers/char/amikeyb.c b/drivers/char/amikeyb.c index 8c8c58388d32..d3e7938652de 100644 --- a/drivers/char/amikeyb.c +++ b/drivers/char/amikeyb.c @@ -257,7 +257,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL; add_timer(&amikeyb_rep_timer); } - handle_scancode(scancode, !break_flag); + handle_scancode(keycode, !break_flag); } else switch (keycode) { case 0x78: @@ -341,8 +341,3 @@ int amiga_kbdrate( struct kbd_repeat *k ) return( 0 ); } - -/* for "kbd-reset" cmdline param */ -__initfunc(void amiga_kbd_reset_setup(char *str, int *ints)) -{ -} diff --git a/drivers/char/atari_MFPser.c b/drivers/char/atari_MFPser.c new file mode 100644 index 000000000000..247592888bd3 --- /dev/null +++ b/drivers/char/atari_MFPser.c @@ -0,0 +1,946 @@ +/* + * drivers/char/atari_MFPser.c: Atari MFP serial ports implementation + * + * Copyright 1994 Roman Hodek + * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o + * + * Special thanks to Harun Scheutzow (developer of RSVE, RSFI, ST_ESCC and + * author of hsmoda-package) for the code to detect RSVE/RSFI. + * + * 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. + * + */ + + +/* This file implements the MFP serial ports. These come in two + * flavors: with or without control lines (RTS, CTS, DTR, ...). They + * are distinguished by having two different types, MFP_CTRL and + * MFP_BARE, resp. Most of the low-level functions are the same for + * both, but some differ. + * + * Note that some assumptions are made about where to access the + * control lines. If the port type is MFP_CTRL, the input lines (CTS + * and DCD) are assumed to be in the MFP GPIP register, bits 1 and 2. + * The output lines (DTR and RTS) have to be in the Soundchip Port A, + * bits 3 and 4. This is the standard ST/TT assigment. If Atari will + * build a serial port in future, that uses other registers, you have + * to rewrite this code. But if the port type is MFP_BARE, no such + * assumptions are necessary. All registers needed are fixed by the + * MFP hardware. The only parameter is the MFP base address. This is + * used to implement Serial1 for the TT and the (not connected) MFP + * port of the Falcon. + * + * Juergen: changes based on Harun Scheutzows code + * o added detection of RSVE, RSFI and possible PLL's + * o set info->hub6 to identify speeder-hardware + * o changed Tx output-level when transmitter is disabled + * o no need for CONFIG_ATARI_MFPSER_EXT + * + * + * o add delays for baudrate-setting to lock PLLs (RSFI, RSVE) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "atari_MFPser.h" + +#define RSFI_DEBUG /* undefine to get rid of "detect_MFP_speeder: clock[x] = y" */ +#define RSFI_PLL_LOCK_DELAY /* use delay to allow PLL settling */ + +/***************************** Prototypes *****************************/ + +static void MFPser_init_port( struct m68k_async_struct *info, int type, + int tt_flag ); +#ifdef MODULE +static void MFPser_deinit_port( struct m68k_async_struct *info, int tt_flag ); +#endif +static void MFPser_rx_int (int irq, void *data, struct pt_regs *fp); +static void MFPser_rxerr_int (int irq, void *data, struct pt_regs *fp); +static void MFPser_tx_int (int irq, void *data, struct pt_regs *fp); +static void MFPctrl_dcd_int (int irq, void *data, struct pt_regs *fp); +static void MFPctrl_cts_int (int irq, void *data, struct pt_regs *fp); +static void MFPctrl_ri_int (int irq, void *data, struct pt_regs *fp); +static void MFPser_init( struct m68k_async_struct *info ); +static void MFPser_deinit( struct m68k_async_struct *info, int leave_dtr ); +static void MFPser_enab_tx_int( struct m68k_async_struct *info, int enab_flag ); +static int MFPser_check_custom_divisor (struct m68k_async_struct *info, + int baud_base, int divisor); +static void MFPser_change_speed( struct m68k_async_struct *info ); +static void MFPctrl_throttle( struct m68k_async_struct *info, int status ); +static void MFPbare_throttle( struct m68k_async_struct *info, int status ); +static void MFPser_set_break( struct m68k_async_struct *info, int break_flag ); +static void MFPser_get_serial_info( struct m68k_async_struct *info, struct + serial_struct *retinfo ); +static unsigned int MFPctrl_get_modem_info( struct m68k_async_struct *info ); +static unsigned int MFPbare_get_modem_info( struct m68k_async_struct *info ); +static int MFPctrl_set_modem_info( struct m68k_async_struct *info, int new_dtr, + int new_rts ); +static int MFPbare_set_modem_info( struct m68k_async_struct *info, int new_dtr, + int new_rts ); +static void MFPser_stop_receive (struct m68k_async_struct *info); +static int MFPser_trans_empty (struct m68k_async_struct *info); + +/************************* End of Prototypes **************************/ + + +/* SERIALSWITCH structures for MFP ports + * Most functions are common to MFP ports with or without control lines + */ + +static SERIALSWITCH MFPctrl_switch = { + MFPser_init, MFPser_deinit, MFPser_enab_tx_int, + MFPser_check_custom_divisor, MFPser_change_speed, + MFPctrl_throttle, MFPser_set_break, + MFPser_get_serial_info, MFPctrl_get_modem_info, + MFPctrl_set_modem_info, NULL, MFPser_stop_receive, MFPser_trans_empty, + NULL +}; + +static SERIALSWITCH MFPbare_switch = { + MFPser_init, MFPser_deinit, MFPser_enab_tx_int, + MFPser_check_custom_divisor, MFPser_change_speed, + MFPbare_throttle, MFPser_set_break, + MFPser_get_serial_info, MFPbare_get_modem_info, + MFPbare_set_modem_info, NULL, MFPser_stop_receive, MFPser_trans_empty, + NULL +}; + + /* MFP Timer Modes divided by 2 (this already done in the BAUD_BASE + * The real 68901 prescaler factors are twice these values! + * prescaler_factor[] = {4, 10, 16, 50, 64, 100, 200} + */ +int MFP_timer_modes[] = { 2, 5, 8, 25, 32, 50, 100 }; + + /* RSVE or RSSPEED will only recognize the 3 frequencies for + * 110, 134, 150 Baud, if the prescaler is 4 and the counter value does + * the rest. The divisors 350 and 256 can be built in multiple ways. + * This driver tries to use the largest prescaler factor possible and + * uses small counter values. TOS uses a prescaler factor of 4 + * ==> MFP_timer_mode = 2 ==> Index 0. Then RSVE replaces the clock + * correctly. Since the absolute frequencies don't have to be so accurate + * but the the prescaler factor has to be 4 we have to make sure that the + * divisors for the 'real' 110, 134, 150 baud can be built with a + * prescaler factor > 4 and the divisors for the replaced 110, 134, 150 + * baud can only be built with a prescaler factor of 4 and a larger + * counter value. Accuracy is not so important here, since RSVE catches + * the values by ignoring the lower 3 bits. + */ + + /* Added support for RSFI + * RSFI is a hardware-FIFO with the following features: + * o 2048bit Rx-FIFO + * o baudrates: 38400, 57600, 76800, 115200, 153600, 230400 + * baudrate-selection and FIFO-enable is done by setting + * the effective baudrate to 50 .. 200. In contrast to the + * RSVE the clockselection is independend from the prescale- + * factor. Instead, the MFP must be in x1 clockmode. + * o the FIFO is only enabled when speeds are above 19k2 + * + * Relationship between MFP-baudrate and RSFI-baudrate + * MFP mode RSFI + * 50 x1 76.8K *) + * 75 x1 153.6K *) + * 110 x1 38.4K + * 134 x1 57.6K + * 150 x1 115.2K + * 200 x1 230.4K + * + * *) Non-standard baudrates, not supported. + * + * We keep the speeder-type in info->hub6. This flag is unused on + * non-Intel architectures. + * + */ + +int MFP_baud_table[22] = { /* Divisors for standard speeds, RSVE & RSFI */ + /* B0 */ 0, + /* B50 */ 768, + /* B75 */ 512, + /* B110 */ 350, /* really 109.71 bps */ /* MFP_mode 50 != 2 */ + /* B134 */ 288, /* really 133.33 bps */ /* MFP_mode 8 != 2 */ + /* B150 */ 256, /* MFP_mode 32 != 2 */ + /* B200 */ 192, + /* B300 */ 128, + /* B600 */ 64, + /* B1200 */ 32, + /* B1800 */ 21, + /* B2400 */ 16, + /* B4800 */ 8, + /* B9600 */ 4, + /* B19200 */ 2, + /* B38400 */ 348, /* 38.4K with RSVE/RSFI, prescaler 4 = MFP_mode 2 */ + /* B57600 */ 286, /* 57.6K with RSVE/RSFI, prescaler 4 = MFP_mode 2 */ + /* B115200 */ 258, /* 115.2K with RSVE/RSFI, prescaler 4 = MFP_mode 2 */ + /* --------------- the following values are ignored in RSVE-mode */ + /* B230400 */ 192, /* 230.4K with RSFI */ + /* B460800 */ 0, /* illegal */ +}; + + +#define DEFAULT_STMFP_LINE 0 /* ttyS0 */ +#define DEFAULT_TTMFP_LINE 2 /* ttyS2 */ + +static int stmfp_line = -1, ttmfp_line = -1; + +extern int atari_MFP_init_done; + + +int atari_MFPser_init( void ) + +{ struct serial_struct req; + int nr = 0; + extern char m68k_debug_device[]; + + if (!MACH_IS_ATARI) + return( -ENODEV ); + + if (ATARIHW_PRESENT(ST_MFP)) { + if (!strcmp( m68k_debug_device, "ser1" )) + printk(KERN_NOTICE "ST-MFP serial port used as debug device\n" ); + else { + req.line = DEFAULT_STMFP_LINE; + req.type = SER_MFP_CTRL; + req.port = (int)&mfp; + if ((stmfp_line = register_serial( &req )) >= 0) { + MFPser_init_port( &rs_table[stmfp_line], req.type, 0 ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for ST-MFP\n", req.line ); + } + } + + if (ATARIHW_PRESENT(TT_MFP)) { + req.line = DEFAULT_TTMFP_LINE; + req.type = SER_MFP_BARE; + req.port = (int)&tt_mfp; + if ((ttmfp_line = register_serial( &req )) >= 0) { + MFPser_init_port( &rs_table[ttmfp_line], req.type, 1 ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for TT-MFP\n", req.line ); + } + + return( nr > 0 ? 0 : -ENODEV ); +} + +static void __inline__ set_timer_D(volatile struct MFP *thismfp, + int baud, int prescale) { +/* set timer d to given value, prescale 4 + * allow PLL-settling (3 bit-times) + */ + + int count; + + thismfp->tim_ct_cd &= 0xf8; /* disable timer D */ + thismfp->tim_dt_d = baud; /* preset baudrate */ + thismfp->tim_ct_cd |= prescale; /* enable timer D, prescale N */ + + for( count = 6; count; --count ) { + thismfp->int_pn_b = ~0x10; + while( !(thismfp->int_pn_b & 0x10) ) + ; + } +} + +static int detect_MFP_speeder(volatile struct MFP *currMFP) { +/* try to autodetect RSVE, RSFI or similiar RS232 speeders + * + * (c) Harun Scheutzow + * developer of RSVE, RSFI, ST_ESCC and author of hsmoda-package + * + * integrated by Juergen Orschiedt + * + * noise-free detection of Tx/Rx baudrate + * - set MFP to loopback, syncmode, 8bpc, syncchar=0xff + * - enable transmitter and measure time between syncdetect + * depending on the relationship between measured time and + * timer-d setting we can tell which (if any) speeder we have. + * + * returncodes: + * 0 something wrong (1200 too slow) + * 1 no speeder detected + * 2 RSVE detected + * 3 RSFI detected + * 4 PLL or fixed Baudrate (Hardware-hack) + */ + + int count, speeder; + unsigned int flags; + int imra, imrb; + + /* prepare IRQ registers for measurement */ + + save_flags(flags); + cli(); + + imra = currMFP->int_mk_a; + imrb = currMFP->int_mk_b; + + currMFP->int_mk_a = imra & 0xe1; /* mask off all Rx/Tx ints */ + currMFP->int_mk_b = imrb & 0xef; /* disable timer d int in IMRB */ + currMFP->int_en_b |= 0x10; /* enable in IERB (to see pending ints) */ + restore_flags(flags); + + (void)currMFP->par_dt_reg; /* consume some cycles */ + (void)currMFP->par_dt_reg; + + /* initialize MFP */ + currMFP->rcv_stat = 0x00; /* disable Rx */ + currMFP->trn_stat = TSR_SOMODE_HIGH; /* disable Tx, output-level high */ + currMFP->sync_char= 0xff; /* syncchar = 0xff */ + currMFP->usart_ctr= (UCR_PARITY_OFF | UCR_SYNC_MODE | UCR_CHSIZE_8); + currMFP->trn_stat = (TSR_TX_ENAB | TSR_SOMODE_LOOP); + + /* look at 1200 baud setting (== effective 19200 in syncmode) */ + set_timer_D(currMFP, 0x10, 0x01); + save_flags(flags); + cli(); + + /* check for fixed speed / bad speed */ + currMFP->rcv_stat = RSR_RX_ENAB; + count = -1; + do { + continue_outer: + ++count; + currMFP->int_pn_b = ~0x10; + do { + if (currMFP->int_pn_b & 0x10) + goto continue_outer; + } while( !(currMFP->rcv_stat & RSR_SYNC_SEARCH) && count <= 22 ); + } while(0); + restore_flags(flags); + + /* for RSxx or standard MFP we have 8 bittimes (count=16) */ +#ifdef RSFI_DEBUG + printk(KERN_INFO " detect_MFP_speeder: count[1200]=%d\n", count); +#endif + + if (count < 10) + speeder = MFP_WITH_PLL; /* less than 5 bittimes: primitive speeder */ + else + if (count >22) + speeder = MFP_WITH_WEIRED_CLOCK; /* something wrong - too slow! */ + else { + /* 1200 baud is working, we neither have fixed clock nor simple PLL */ + set_timer_D(currMFP, 0xaf, 0x01); + save_flags(flags); + cli(); + + /* check for RSxx or Standard MFP with 110 baud + * timer D toggles each 290us + * bps sync char count + * RSVE: 614400 22.33 13uS + * RSFI: 38400 1.39 208us + * Standard: 1720 0.06 + * + */ + currMFP->int_pn_b = ~0x10; + /* syncronize to timer D */ + while( !(currMFP->int_pn_b & 0x10) ) + ; + currMFP->int_pn_b = ~0x10; + count = -1; + do { + continue_outer2: + currMFP->rcv_stat = 0; /* disable Rx */ + ++count; + (void)currMFP->par_dt_reg; /* delay */ + currMFP->rcv_stat = RSR_RX_ENAB; /* enable Rx */ + nop(); + do { + /* increment counter if sync char detected */ + if (currMFP->rcv_stat & RSR_SYNC_SEARCH) + goto continue_outer2; + } while( !(currMFP->int_pn_b & 0x10) ); + } while(0); + +#ifdef RSFI_DEBUG + printk(KERN_INFO " detect_MFP_speeder: count[110]=%d\n", count); +#endif + + if (count < 1) speeder = MFP_STANDARD; /* no speeder detected */ + else + if (count > 4) speeder = MFP_WITH_RSVE; + else + speeder = MFP_WITH_RSFI; + } + + restore_flags(flags); + currMFP->rcv_stat = 0x00; /* disable Rx */ + currMFP->trn_stat = TSR_SOMODE_HIGH; /* disable Tx, output-level high */ + currMFP->usart_ctr= (UCR_PARITY_OFF | UCR_ASYNC_2 | UCR_CHSIZE_8); + currMFP->int_mk_a = imra; + currMFP->int_mk_b = imrb; + currMFP->int_en_b &= 0xef; + currMFP->int_pn_a = 0xe1; /* mask off pending Rx/Tx */ + currMFP->int_pn_b = 0xef; /* mask off pending Timer D */ + return speeder; + +} + +static void MFPser_init_port( struct m68k_async_struct *info, int type, int tt_flag) +{ + INIT_currMFP(info); + int speeder; + static char *speeder_name[]= {"", "", "PLL or fixed clock", "RSVE", "RSFI" }; + + /* look for possible speeders */ + info->hub6 = speeder = detect_MFP_speeder((struct MFP *)currMFP); + if (speeder > MFP_STANDARD) + printk(KERN_INFO "ttyS%d: Detected %s extension\n", + info->line, speeder_name[speeder]); + + + /* set ISRs, but don't enable interrupts yet (done in init()); + * all ints are choosen of type FAST, and they're really quite fast. + * Furthermore, we have to account for the fact that these are three ints, + * and one can interrupt another. So better protect them against one + * another... + */ + request_irq(tt_flag ? IRQ_TT_MFP_SEREMPT : IRQ_MFP_SEREMPT, + MFPser_tx_int, IRQ_TYPE_FAST, + tt_flag ? "TT-MFP TX" : "ST-MFP TX", info); + request_irq(tt_flag ? IRQ_TT_MFP_RECFULL : IRQ_MFP_RECFULL, + MFPser_rx_int, IRQ_TYPE_FAST, + tt_flag ? "TT-MFP RX" : "ST-MFP RX", info); + request_irq(tt_flag ? IRQ_TT_MFP_RECERR : IRQ_MFP_RECERR, + MFPser_rxerr_int, IRQ_TYPE_FAST, + tt_flag ? "TT-MFP RX error" : "ST-MFP RX error", info); + /* Tx_err interrupt unused (it signals only that the Tx shift reg + * is empty) + */ + + if (type == SER_MFP_CTRL && !tt_flag) { + /* The DCD, CTS and RI ints are slow ints, because I + see no races with the other ints */ + request_irq(IRQ_MFP_DCD, MFPctrl_dcd_int, IRQ_TYPE_SLOW, + "ST-MFP DCD", info); + request_irq(IRQ_MFP_CTS, MFPctrl_cts_int, IRQ_TYPE_SLOW, + "ST-MFP CTS", info); + request_irq(IRQ_MFP_RI, MFPctrl_ri_int, IRQ_TYPE_SLOW, + "ST-MFP RI", info); + /* clear RTS and DTR */ + if (!atari_MFP_init_done) + /* clear RTS and DTR */ + GIACCESS( GI_RTS | GI_DTR ); + } + + info->sw = (type == SER_MFP_CTRL ? &MFPctrl_switch : &MFPbare_switch); + + info->custom_divisor = 4; /* 9600 Baud */ + info->baud_base = MFP_BAUD_BASE; + + if (tt_flag || !atari_MFP_init_done) { + currMFP->rcv_stat = 0; /* disable Rx */ + currMFP->trn_stat = TSR_SOMODE_HIGH; /* disable Tx */ + } +} + + +#ifdef MODULE +static void MFPser_deinit_port( struct m68k_async_struct *info, int tt_flag ) +{ + free_irq(tt_flag ? IRQ_TT_MFP_SEREMPT : IRQ_MFP_SEREMPT, info); + free_irq(tt_flag ? IRQ_TT_MFP_RECFULL : IRQ_MFP_RECFULL, info); + free_irq(tt_flag ? IRQ_TT_MFP_RECERR : IRQ_MFP_RECERR, info); + if (info->type == SER_MFP_CTRL && !tt_flag) { + free_irq(IRQ_MFP_DCD, info ); + free_irq(IRQ_MFP_CTS, info ); + free_irq(IRQ_MFP_RI, info); + } +} +#endif + + +static void MFPser_rx_int( int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + int ch, stat, err; + INIT_currMFP(info); + + stat = currMFP->rcv_stat; + ch = currMFP->usart_dta; + /* Frame Errors don't cause a RxErr IRQ! */ + err = (stat & RSR_FRAME_ERR) ? TTY_FRAME : 0; + + rs_receive_char (info, ch, err); +} + + +static void MFPser_rxerr_int( int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + int ch, stat, err; + INIT_currMFP(info); + + stat = currMFP->rcv_stat; + ch = currMFP->usart_dta; /* most probably junk data */ + + if (stat & RSR_PARITY_ERR) + err = TTY_PARITY; + else if (stat & RSR_OVERRUN_ERR) + err = TTY_OVERRUN; + else if (stat & RSR_BREAK_DETECT) + err = TTY_BREAK; + else if (stat & RSR_FRAME_ERR) /* should not be needed */ + err = TTY_FRAME; + else + err = 0; + + rs_receive_char (info, ch, err); +} + + +static void MFPser_tx_int( int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + int ch; + INIT_currMFP(info); + + if (currMFP->trn_stat & TSR_BUF_EMPTY) { + if ((ch = rs_get_tx_char( info )) >= 0) + currMFP->usart_dta = ch; + if (ch == -1 || rs_no_more_tx( info )) + /* disable tx interrupts */ + currMFP->int_en_a &= ~0x04; + } +} + + +static void MFPctrl_dcd_int( int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + INIT_currMFP(info); + + /* Toggle active edge to get next change of DCD! */ + currMFP->active_edge ^= GPIP_DCD; + + rs_dcd_changed( info, !(currMFP->par_dt_reg & GPIP_DCD) ); +} + + +static void MFPctrl_cts_int( int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + INIT_currMFP(info); + + /* Toggle active edge to get next change of CTS! */ + currMFP->active_edge ^= GPIP_CTS; + + rs_check_cts( info, !(currMFP->par_dt_reg & GPIP_CTS) ); +} + + +static void MFPctrl_ri_int(int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + /* update input line counter */ + info->icount.rng++; + wake_up_interruptible(&info->delta_msr_wait); +} + + +static void MFPser_init( struct m68k_async_struct *info ) +{ + INIT_currMFP(info); + + /* base value for UCR */ + if (info->type != SER_MFP_CTRL || !atari_MFP_init_done) + currMFP->usart_ctr = (UCR_PARITY_OFF | UCR_ASYNC_1 | + UCR_CHSIZE_8 | UCR_PREDIV); + + /* enable Rx and clear any error conditions */ + currMFP->rcv_stat = RSR_RX_ENAB; + + /* enable Tx */ + currMFP->trn_stat = (TSR_TX_ENAB | TSR_SOMODE_HIGH); + + /* enable Rx, RxErr and Tx interrupts */ + currMFP->int_en_a |= 0x1c; + currMFP->int_mk_a |= 0x1c; + + if (info->type == SER_MFP_CTRL) { + + int status; + + /* set RTS and DTR (low-active!) */ + GIACCESS( ~(GI_RTS | GI_DTR) ); + + /* Set active edge of CTS and DCD signals depending on their + * current state. + * If the line status changes between reading the status and + * enabling the interrupt, this won't work :-( How could it be + * done better?? + * ++andreas: do it better by looping until stable + */ + do { + status = currMFP->par_dt_reg & GPIP_CTS; + if (status) + currMFP->active_edge &= ~GPIP_CTS; + else + currMFP->active_edge |= GPIP_CTS; + } while ((currMFP->par_dt_reg & GPIP_CTS) != status); + + do { + status = currMFP->par_dt_reg & GPIP_DCD; + if (status) + currMFP->active_edge &= ~GPIP_DCD; + else + currMFP->active_edge |= GPIP_DCD; + } while ((currMFP->par_dt_reg & GPIP_DCD) != status); + + /* enable CTS and DCD interrupts */ + currMFP->int_en_b |= 0x06; + currMFP->int_mk_b |= 0x06; + } + MOD_INC_USE_COUNT; +} + + +static void MFPser_deinit( struct m68k_async_struct *info, int leave_dtr ) +{ + INIT_currMFP(info); + + /* disable Rx, RxErr and Tx interrupts */ + currMFP->int_en_a &= ~0x1c; + + if (info->type == SER_MFP_CTRL) { + /* disable CTS and DCD interrupts */ + currMFP->int_en_b &= ~0x06; + } + + /* disable Rx and Tx */ + currMFP->rcv_stat = 0; + currMFP->trn_stat = TSR_SOMODE_HIGH; + + /* wait for last byte to be completely shifted out */ + while( !(currMFP->trn_stat & TSR_LAST_BYTE_SENT) ) + ; + + if (info->type == SER_MFP_CTRL) { + /* drop RTS and DTR if required */ + MFPser_RTSoff(); + if (!leave_dtr) + MFPser_DTRoff(); + } + + /* read Rx status and data to clean up */ + (void)currMFP->rcv_stat; + (void)currMFP->usart_dta; + MOD_DEC_USE_COUNT; +} + + +static void MFPser_enab_tx_int( struct m68k_async_struct *info, int enab_flag ) +{ + INIT_currMFP(info); + + if (enab_flag) { + unsigned long flags; + currMFP->int_en_a |= 0x04; + save_flags(flags); + cli(); + MFPser_tx_int (0, info, 0); + restore_flags(flags); + } + else + currMFP->int_en_a &= ~0x04; +} + + +static int MFPser_check_custom_divisor (struct m68k_async_struct *info, + int baud_base, int divisor) +{ + int i; + + if (baud_base != MFP_BAUD_BASE) + return -1; + + /* divisor must be a multiple of 2 or 5 (because of timer modes) */ + if (divisor == 0 || ((divisor & 1) && (divisor % 5) != 0)) return( -1 ); + + /* Determine what timer mode would be selected and look if the + * timer value would be greater than 256 + */ + for( i = sizeof(MFP_timer_modes)/sizeof(*MFP_timer_modes)-1; i >= 0; --i ) + if (divisor % MFP_timer_modes[i] == 0) break; + if (i < 0) return( -1 ); /* no suitable timer mode found */ + + return (divisor / MFP_timer_modes[i] > 256); +} + + +static void MFPser_change_speed( struct m68k_async_struct *info ) +{ + unsigned cflag, baud, chsize, stopb, parity, aflags; + unsigned div = 0, timer_val; + int timer_mode; + unsigned long ipl; + INIT_currMFP(info); + + if (!info->tty || !info->tty->termios) return; + + cflag = info->tty->termios->c_cflag; + baud = cflag & CBAUD; + chsize = cflag & CSIZE; + stopb = cflag & CSTOPB; + parity = cflag & (PARENB | PARODD); + aflags = info->flags & ASYNC_SPD_MASK; + + if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; + else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + if (baud < 1 || baud > 4) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + if (info->hub6 > MFP_WITH_PLL) /* speeder detected? */ + baud += 15; + } + if ((info->hub6 > MFP_WITH_PLL) && (baud == 15)) { + /* only for speeders... */ + switch (aflags) { + case ASYNC_SPD_HI: + baud += 1; /* 134 Baud, with RSVE = 57600 */ + break; + case ASYNC_SPD_VHI: + baud += 2; /* 150 Baud, with RSVE = 115200 */ + break; + case ASYNC_SPD_SHI: + baud += 3; /* with RSFI: 230400 Baud */ + break; + case ASYNC_SPD_WARP: + baud += 4; + break; + case ASYNC_SPD_CUST: + div = info->custom_divisor; + break; + } + } + if (!div) { + /* max. tableentries depending on speeder type */ + static int maxbaud[] = { 14, 14, 14, 17, 18}; + if (baud > maxbaud[info->hub6]) + baud = maxbaud[info->hub6]; + div = MFP_baud_table[baud]; + } + + if (div) { + /* turn on DTR */ + MFPser_DTRon (); + } else { + /* speed == 0 -> drop DTR */ + MFPser_DTRoff(); + return; + } + + /* compute timer value and timer mode (garuateed to succeed, because + * the divisor was checked before by check_custom_divisor(), if it + * is used-supplied) + */ + for( timer_mode = sizeof(MFP_timer_modes)/sizeof(*MFP_timer_modes)-1; + timer_mode >= 0; --timer_mode ) + if (div % MFP_timer_modes[timer_mode] == 0) break; + timer_val = div / MFP_timer_modes[timer_mode]; + + save_flags (ipl); + cli(); + /* disable Rx and Tx while changing parameters */ + currMFP->rcv_stat = 0; + currMFP->trn_stat = TSR_SOMODE_HIGH; + +#ifdef RSFI_PLL_LOCK_DELAY + currMFP->int_en_b |= 0x10; + set_timer_D(currMFP, timer_val, timer_mode+1); + currMFP->int_en_b &= 0xef; +#else + /* stop timer D to set new timer value immediatly after re-enabling */ + currMFP->tim_ct_cd &= ~0x07; + currMFP->tim_dt_d = timer_val; + currMFP->tim_ct_cd |= (timer_mode+1); +#endif + { + unsigned shadow_ctr; + + shadow_ctr = ((parity & PARENB) ? + ((parity & PARODD) ? UCR_PARITY_ODD : UCR_PARITY_EVEN) : + UCR_PARITY_OFF ) | + ( chsize == CS5 ? UCR_CHSIZE_5 : + chsize == CS6 ? UCR_CHSIZE_6 : + chsize == CS7 ? UCR_CHSIZE_7 : UCR_CHSIZE_8 ) | + ( stopb ? UCR_ASYNC_2 : UCR_ASYNC_1 ); + + if ((baud < 15) || (info->hub6 != MFP_WITH_RSFI)) + shadow_ctr |= UCR_PREDIV; + currMFP->usart_ctr = shadow_ctr; + } + + /* re-enable Rx and Tx */ + currMFP->rcv_stat = RSR_RX_ENAB; + currMFP->trn_stat = (TSR_TX_ENAB | TSR_SOMODE_HIGH); + restore_flags (ipl); +} + +static void MFPctrl_throttle( struct m68k_async_struct *info, int status ) +{ + if (status) + MFPser_RTSoff(); + else + MFPser_RTSon(); +} + + +static void MFPbare_throttle( struct m68k_async_struct *info, int status ) +{ + /* no-op */ +} + + +static void MFPser_set_break( struct m68k_async_struct *info, int break_flag ) +{ + INIT_currMFP(info); + + if (break_flag) + currMFP->trn_stat |= TSR_SEND_BREAK; + else + currMFP->trn_stat &= ~TSR_SEND_BREAK; +} + + +static void MFPser_get_serial_info( struct m68k_async_struct *info, + struct serial_struct *retinfo ) +{ + retinfo->baud_base = info->baud_base; + retinfo->custom_divisor = info->custom_divisor; +} + + +static unsigned int MFPctrl_get_modem_info( struct m68k_async_struct *info ) +{ + unsigned gpip, gi; + unsigned int ri; + unsigned long ipl; + INIT_currMFP(info); + + save_flags (ipl); + cli(); + gpip = currMFP->par_dt_reg; + gi = GIACCESS( 0 ); + restore_flags (ipl); + + /* DSR is not connected on the Atari, assume it to be set; + * RI is tested by the RI bitpos field of info, because the RI is + * signalled at different ports on TT and Falcon + * ++andreas: the signals are inverted! + */ + /* If there is a SCC but no TT_MFP then RI on the ST_MFP is + used for SCC channel b */ + if (ATARIHW_PRESENT (SCC) && !ATARIHW_PRESENT (TT_MFP)) + ri = 0; + else if (currMFP == &mfp) + ri = gpip & GPIP_RI ? 0 : TIOCM_RNG; + else + ri = 0; + return (((gi & GI_RTS ) ? 0 : TIOCM_RTS) | + ((gi & GI_DTR ) ? 0 : TIOCM_DTR) | + ((gpip & GPIP_DCD) ? 0 : TIOCM_CAR) | + ((gpip & GPIP_CTS) ? 0 : TIOCM_CTS) | + TIOCM_DSR | ri); +} + + +static unsigned int MFPbare_get_modem_info( struct m68k_async_struct *info ) +{ + return( TIOCM_RTS | TIOCM_DTR | TIOCM_CAR | TIOCM_CTS | TIOCM_DSR ); +} + + +static int MFPctrl_set_modem_info( struct m68k_async_struct *info, + int new_dtr, int new_rts ) +{ + if (new_dtr == 0) + MFPser_DTRoff(); + else if (new_dtr == 1) + MFPser_DTRon(); + + if (new_rts == 0) + MFPser_RTSoff(); + else if (new_rts == 1) + MFPser_RTSon(); + + return( 0 ); +} + + +static int MFPbare_set_modem_info( struct m68k_async_struct *info, + int new_dtr, int new_rts ) +{ + /* no-op */ + + /* Is it right to return an error or should the attempt to change + * DTR or RTS be silently ignored? + */ + return( -EINVAL ); +} + +static void MFPser_stop_receive (struct m68k_async_struct *info) +{ + INIT_currMFP(info); + + /* disable rx and rxerr interrupt */ + currMFP->int_en_a &= ~0x18; + + /* disable receiver */ + currMFP->rcv_stat = 0; + /* disable transmitter */ + currMFP->trn_stat = TSR_SOMODE_HIGH; +} + +static int MFPser_trans_empty (struct m68k_async_struct *info) +{ + INIT_currMFP(info); + return (currMFP->trn_stat & TSR_LAST_BYTE_SENT) != 0; +} + +#ifdef MODULE +int init_module(void) +{ + return( atari_MFPser_init() ); +} + +void cleanup_module(void) +{ + if (stmfp_line >= 0) { + MFPser_deinit_port( &rs_table[stmfp_line], 0 ); + unregister_serial( stmfp_line ); + } + if (ttmfp_line >= 0) { + MFPser_deinit_port( &rs_table[ttmfp_line], 1 ); + unregister_serial( ttmfp_line ); + } +} +#endif diff --git a/drivers/char/atari_MFPser.h b/drivers/char/atari_MFPser.h new file mode 100644 index 000000000000..809e44c59538 --- /dev/null +++ b/drivers/char/atari_MFPser.h @@ -0,0 +1,131 @@ + +/* + * atari_MFPser.h: Definitions for MFP serial ports + * + * Copyright 1994 Roman Hodek + * + * 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. + * + */ + + +#ifndef _ATARI_MFPSER_H +#define _ATARI_MFPSER_H + +#define INIT_currMFP(info) \ + volatile struct MFP *currMFP = (volatile struct MFP *)(info->port) + +/* MFP input frequency, divided by 16 (USART prediv for async modes) + * and 2 because of each timer run _toggles_ the output, resulting in + * half the frequency, and 2 as greatest common divisor of all timer + * modes. + */ +#define MFP_BAUD_BASE (2457600/16/2/2) + +/* USART Control Register */ + +#define UCR_PARITY_MASK 0x06 +#define UCR_PARITY_OFF 0x00 +#define UCR_PARITY_ODD 0x04 +#define UCR_PARITY_EVEN 0x06 + +#define UCR_MODE_MASK 0x18 +#define UCR_SYNC_MODE 0x00 +#define UCR_ASYNC_1 0x08 +#define UCR_ASNYC_15 0x10 +#define UCR_ASYNC_2 0x18 + +#define UCR_CHSIZE_MASK 0x60 +#define UCR_CHSIZE_8 0x00 +#define UCR_CHSIZE_7 0x20 +#define UCR_CHSIZE_6 0x40 +#define UCR_CHSIZE_5 0x60 + +#define UCR_PREDIV 0x80 + +/* Receiver Status Register */ + +#define RSR_RX_ENAB 0x01 +#define RSR_STRIP_SYNC 0x02 +#define RSR_SYNC_IN_PRGR 0x04 /* sync mode only */ +#define RSR_CHAR_IN_PRGR 0x04 /* async mode only */ +#define RSR_SYNC_SEARCH 0x08 /* sync mode only */ +#define RSR_BREAK_DETECT 0x08 /* async mode only */ +#define RSR_FRAME_ERR 0x10 +#define RSR_PARITY_ERR 0x20 +#define RSR_OVERRUN_ERR 0x40 +#define RSR_CHAR_AVAILABLE 0x80 + +/* Transmitter Status Register */ + +#define TSR_TX_ENAB 0x01 + +#define TSR_SOMODE_MASK 0x06 +#define TSR_SOMODE_OPEN 0x00 +#define TSR_SOMODE_LOW 0x02 +#define TSR_SOMODE_HIGH 0x04 +#define TSR_SOMODE_LOOP 0x06 + +#define TSR_SEND_BREAK 0x08 +#define TSR_LAST_BYTE_SENT 0x10 +#define TSR_HALF_DUPLEX 0x20 +#define TSR_UNDERRUN 0x40 +#define TSR_BUF_EMPTY 0x80 + +/* Control signals in the GPIP */ + +#define GPIP_DCD 0x02 +#define GPIP_CTS 0x04 +#define GPIP_RI 0x40 + +/* MFP speeders */ +#define MFP_WITH_WEIRED_CLOCK 0x00 +#define MFP_STANDARD 0x01 +#define MFP_WITH_PLL 0x02 +#define MFP_WITH_RSVE 0x03 +#define MFP_WITH_RSFI 0x04 + + +/* Convenience routine to access RTS and DTR in the Soundchip: It sets + * the register to (oldvalue & mask) if mask is negative or (oldvalue + * | mask) if positive. It returns the old value. I guess the + * parameters will be constants most of the time, so gcc can throw + * away the if statement if it isn't needed. + */ + +static __inline__ unsigned char GIACCESS( int mask ) +{ + unsigned long cpu_status; + unsigned char old; + + save_flags(cpu_status); + cli(); + + sound_ym.rd_data_reg_sel = 14; + old = sound_ym.rd_data_reg_sel; + + if (mask) { + if (mask < 0) + sound_ym.wd_data = old & mask; + else + sound_ym.wd_data = old | mask; + } + + restore_flags(cpu_status); + return( old ); +} + +#define GI_RTS 0x08 +#define GI_DTR 0x10 + +#define MFPser_RTSon() GIACCESS( ~GI_RTS ) +#define MFPser_RTSoff() GIACCESS( GI_RTS ) +#define MFPser_DTRon() GIACCESS( ~GI_DTR ) +#define MFPser_DTRoff() GIACCESS( GI_DTR ) + + +int atari_MFPser_init( void ); + +#endif /* _ATARI_MFPSER_H */ diff --git a/drivers/char/atari_MIDI.c b/drivers/char/atari_MIDI.c new file mode 100644 index 000000000000..e5459a4cc4d8 --- /dev/null +++ b/drivers/char/atari_MIDI.c @@ -0,0 +1,567 @@ +/* + * drivers/char/atari_MIDI.c: Atari MIDI driver as serial port + * + * Copyright 1994 Roman Hodek + * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o + * + * 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. + * + * Modified for midi by Martin Schaller + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "atari_MIDI.h" + + +/* Defines */ + +/* #define _DEBUG_MIDI_ */ + +#define DEFAULT_MIDI_LINE 5 /* ttyS5 */ +#define MIDI_BAUD_BASE 500000 /* 31250 */ + +#define STOP_SHIFT 0 +#define PAR_SHIFT 4 +#define CS_SHIFT 8 + +/* Prototypes */ +static void MIDI_init_port(struct m68k_async_struct* info, int type, int tt_flag); +static void MIDI_int(void); /* called from keyboard int handler */ +static void MIDI_init( struct m68k_async_struct * info ); +static void MIDI_deinit( struct m68k_async_struct * info, int leave_dtr ); +static void MIDI_enab_tx_int( struct m68k_async_struct * info, int enab_flag ); +static int MIDI_check_custom_divisor(struct m68k_async_struct* info, int baud_base, int divisor); +static void MIDI_change_speed( struct m68k_async_struct * info ); +static void MIDI_throttle( struct m68k_async_struct * info, int status ); +static void MIDI_set_break( struct m68k_async_struct * info, int break_flag ); +static unsigned int MIDI_get_modem_info( struct m68k_async_struct * info ); +static void MIDI_get_serial_info( struct m68k_async_struct* info, struct serial_struct* retinfo); +static int MIDI_set_modem_info( struct m68k_async_struct* info, int new_dtr, int new_rts ); +static void MIDI_stop_receive(struct m68k_async_struct * info); +static int MIDI_trans_empty(struct m68k_async_struct * info); + + +struct m68k_async_struct* midi_info; +static unsigned char mid_ctrl_shadow; /* Bernd Harries, 960525 */ +static unsigned char mid_stat_shadow; /* Bernd Harries, 961207 */ + + +#ifdef _BHA_MIDI_CHAR_FMT +static int MIDI_char_fmt[8] = { + (CS7 << CS_SHIFT) | (PARENB << PAR_SHIFT) | (2 << STOP_SHIFT), + (CS7 << CS_SHIFT) | ((PARENB | PARODD) << PAR_SHIFT) | (2 << STOP_SHIFT), + (CS7 << CS_SHIFT) | (PARENB << PAR_SHIFT) | (1 << STOP_SHIFT), + (CS7 << CS_SHIFT) | ((PARENB | PARODD) << PAR_SHIFT) | (1 << STOP_SHIFT), + (CS8 << CS_SHIFT) | (0 << PAR_SHIFT) | (2 << STOP_SHIFT), + (CS8 << CS_SHIFT) | (0 << PAR_SHIFT) | (1 << STOP_SHIFT), + (CS8 << CS_SHIFT) | (PARENB << PAR_SHIFT) | (1 << STOP_SHIFT), + (CS8 << CS_SHIFT) | ((PARENB | PARODD) << PAR_SHIFT) | (1 << STOP_SHIFT), +}; +#endif + + /* index = P * 4 + L * 2 + S * 1 */ + /* S = (2 == Stopbits) */ + /* L = (8 == Databits) */ + /* P = Parity {Odd, Even, None} */ + +static char ACIA_char_fmt[16] = { + ACIA_D7O1S, + ACIA_D7O2S, + ACIA_D8O1S, + -1, + ACIA_D7E1S, + ACIA_D7E2S, + ACIA_D8E1S, + -1, + -1, + -1, + ACIA_D8N1S, + ACIA_D8N2S, + -2 +}; + +/* Divisors for standard speeds & RSVE Bernd Harries 961127 */ +static int MIDI_baud_table[20] = { + /* B0 */ 0, /* invalid */ + /* B50 */ 0, + /* B75 */ 0, + /* B110 */ 0, + /* B134 */ 0, + /* B150 */ 0, + /* B200 */ 0, + /* B300 */ 0, + /* B600 */ 0, + /* B1200 */ 0, + /* B1800 */ 0, + /* B2400 */ 0, + /* B4800 */ 0, /* invalid */ + /* B9600 */ 64, /* really 7812.5 bps */ + /* B19200 */ 0, /* invalid */ + /* B38400 */ 16, /* really 31250 bps */ + /* B57600 */ 0, /* invalid */ + /* B115200 */ 0, /* invalid */ + /* B230400 */ 0, /* invalid */ + /* B460800 */ 1 /* really 500000 bps */ +}; + + /* The following 2 arrays must be congruent! */ +static int ACIA_prescaler_modes[] = { 1, 16, 64 }; +static char ACIA_baud_masks[] = { ACIA_DIV1, ACIA_DIV16, ACIA_DIV64 }; + + /* + * SERIALSWITCH structures for MIDI port + */ + +static SERIALSWITCH MIDI_switch = { + MIDI_init, + MIDI_deinit, + MIDI_enab_tx_int, + MIDI_check_custom_divisor, + MIDI_change_speed, + MIDI_throttle, + MIDI_set_break, + MIDI_get_serial_info, + MIDI_get_modem_info, + MIDI_set_modem_info, + NULL, /* MIDI_ioctl, */ + MIDI_stop_receive, + MIDI_trans_empty, + NULL /* MIDI_check_open */ +}; + + +__initfunc(int atari_MIDI_init( void )) +{ + extern char m68k_debug_device[]; + static struct serial_struct req; + int midi_line; + int nr = 0; + +#ifdef _DEBUG_MIDI_ + printk(" atari_MIDI_init() \n"); +#endif + + if (!strcmp( m68k_debug_device, "midi" )) + printk(KERN_NOTICE "MIDI serial port used as debug device\n" ); + else { + req.line = DEFAULT_MIDI_LINE; + req.type = SER_MIDI; + req.port = (int) &acia.mid_ctrl; + + midi_line = register_serial( &req ); + if (midi_line >= 0) { + MIDI_init_port(&rs_table[midi_line], req.type, 0); + ++nr; + } + else { + printk(KERN_WARNING "Cannot allocate ttyS%d for MIDI\n", req.line ); + } + } + return( nr > 0 ? 0 : -ENODEV ); +} + + +static void MIDI_init_port(struct m68k_async_struct * info, int type, int tt_flag) +{ + midi_info = info; /* modulglobal !!!!! */ + + info->sw = &MIDI_switch; + info->custom_divisor = 16; /* 31250 Baud */ + info->baud_base = MIDI_BAUD_BASE; + info->sw = &MIDI_switch; + + + /* set ISRs, but don't enable interrupts yet (done in init()); + * all ints are choosen of type FAST, and they're really quite fast. + * Furthermore, we have to account for the fact that these are three ints, + * and one can interrupt another. So better protect them against one + * another... + */ + /* + request_irq(IRQ_MIDI_SEREMPT, MIDI_tx_int, IRQ_TYPE_FAST, "MIDI TX", info); + request_irq(IRQ_MIDI_RECFULL, MIDI_rx_int, IRQ_TYPE_FAST, "MIDI RX", info); + request_irq(IRQ_MIDI_RECERR, + MIDI_rxerr_int, + IRQ_TYPE_FAST, + "MIDI RX error", + info); + */ + + /* Tx_err interrupt unused (it signals only that the Tx shift reg + * is empty) + */ + /* Leave RTS high for now if selected as a switch, so that it's still valid + * as long as the port isn't opened. + */ + mid_ctrl_shadow = ACIA_DIV16 | ACIA_D8N1S | + ((atari_switches&ATARI_SWITCH_MIDI) ? + ACIA_RHTID : ACIA_RLTID); + acia.mid_ctrl = mid_ctrl_shadow; + + atari_MIDI_interrupt_hook = MIDI_int; +} + + +static void MIDI_int(void) /* called from keyboard int handler */ +{ + static int err; +/* register int stat; */ + register int ch; + + mid_stat_shadow = acia.mid_ctrl; + + /* if == Rx Data Reg Full -> Interrupt */ + if (mid_stat_shadow & (ACIA_RDRF | ACIA_FE | ACIA_OVRN)) { + ch = acia.mid_data; + err = 0; + if(mid_stat_shadow & ACIA_FE) err = TTY_FRAME; + if(mid_stat_shadow & ACIA_OVRN) err = TTY_OVERRUN; + rs_receive_char(midi_info, ch, err); + /* printk("R"); */ + } + + if (acia.mid_ctrl & ACIA_TDRE) { /* Tx Data Reg Empty Transmit Interrupt */ + ch = rs_get_tx_char( midi_info ); + + if (ch >= 0) { /* 32 Bit */ + acia.mid_data = ch; + /* printk("_%c", ch); */ + } + + if (ch == -1 || rs_no_more_tx( midi_info )) { + /* disable tx interrupts %x01xxxxx ==> %x00xxxxx */ + /* RTS Low, Tx IRQ Disabled */ + mid_ctrl_shadow &= ~ACIA_RLTIE; + acia.mid_ctrl = mid_ctrl_shadow; + /* printk("T"); */ + } + } +} + +static void MIDI_init( struct m68k_async_struct * info ) +{ +#ifdef _DEBUG_MIDI_ + printk(" MIDI_init() \n"); +#endif + /* Baud = DIV16, 8N1, denable rx interrupts */ + mid_ctrl_shadow = ACIA_DIV16 | ACIA_D8N1S | ACIA_RIE; + acia.mid_ctrl = mid_ctrl_shadow; + + MOD_INC_USE_COUNT; +} + +static void MIDI_deinit( struct m68k_async_struct *info, int leave_dtr ) +{ +#ifdef _DEBUG_MIDI_ + printk(" MIDI_deinit() \n"); +#endif + + /* seems like the status register changes on read */ +#ifdef _MIDI_WAIT_FOR_TX_EMPTY_ + while(!(mid_stat_shadow & ACIA_TDRE)) { /* wait for TX empty */ + ; + /* printk("m"); */ + } +#endif + + /* Baud = DIV16, 8N1, disable Rx and Tx interrupts */ + mid_ctrl_shadow = ACIA_DIV16 | ACIA_D8N1S; + acia.mid_ctrl = mid_ctrl_shadow; + + /* read Rx status and data to clean up */ + (void)acia.mid_ctrl; + (void)acia.mid_data; + + MOD_DEC_USE_COUNT; +} + + +/* + * ACIA Control Register can only be written! Read accesses Status Register! + * Shadowing may be nescessary here like for SCC + * + * Bernd Harries, 960525 Tel.: +49-421-804309 + * harries@asrv01.atlas.de + * Bernd_Harries@hb2.maus.de + */ + +static void MIDI_enab_tx_int( struct m68k_async_struct * info, int enab_flag ) +{ + unsigned long cpu_status; + + if (enab_flag) { + register int ch; + + save_flags(cpu_status); + cli(); + /* RTS Low, Tx IRQ Enabled */ + mid_ctrl_shadow |= ACIA_RLTIE; + acia.mid_ctrl = mid_ctrl_shadow; + /* restarted the transmitter */ + /* + * These extensions since 0.9.5 are only allowed, if the + * Tx Data Register is Empty! + * In 1.2.13pl10 this did not work. So I added the if(). + * + * Bernd Harries 960530 + * harries@atlas.de + * harries@asrv01.atlas.de + * Bernd_Harries@hb2.maus.de + */ + if (acia.mid_ctrl & ACIA_TDRE) { /* If last Char since disabling is gone */ + ch = rs_get_tx_char( midi_info ); + if (ch >= 0) { + acia.mid_data = ch; + /* printk("=%c", ch); */ + } + + if (ch == -1 || rs_no_more_tx( midi_info )) { + /* disable tx interrupts */ + /* RTS Low, Tx IRQ Disabled */ + mid_ctrl_shadow &= ~ACIA_RLTIE; + acia.mid_ctrl = mid_ctrl_shadow; + + } + } + restore_flags(cpu_status); + } else { + save_flags(cpu_status); + cli(); + /* RTS Low, Tx IRQ Disabled */ + mid_ctrl_shadow &= ~ACIA_RLTIE; + acia.mid_ctrl = mid_ctrl_shadow; + restore_flags(cpu_status); + } +} + + +static int MIDI_check_custom_divisor(struct m68k_async_struct* info, int baud_base, int divisor) +{ +#ifdef _DEBUG_MIDI_ + printk(" MIDI_check_custom_divisor() \n"); +#endif + + if (baud_base != MIDI_BAUD_BASE) return -1; + + /* divisor must be a multiple of 1, 16, 64 */ + + switch (divisor) { + case 1: + case 16: + case 64: return(0); + } + + return(-1); +} + + +static void MIDI_change_speed( struct m68k_async_struct *info ) +{ + unsigned long cpu_status; + unsigned int baud, stopb, parity, aflags; + unsigned int div, cflag, chsize; + int timer_mode; + int index; + + unsigned char mid_ctrl_new; + unsigned char baud_mask; + unsigned char chsize_mask; + unsigned char fmt_mask; + +#ifdef _DEBUG_MIDI_ + printk(" MIDI_change_speed() \n"); +#endif + + div = 0; + cflag = info->tty->termios->c_cflag; + baud = cflag & CBAUD; + chsize = cflag & CSIZE; + stopb = cflag & CSTOPB; + parity = cflag & (PARENB | PARODD); + aflags = info->flags & ASYNC_SPD_MASK; + + if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; + else + info->flags &= ~ASYNC_CTS_FLOW; + + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + if (baud < 1 || baud > 4) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; + } + if (baud == 15) { + switch (aflags) { + case ASYNC_SPD_HI: + baud = 16; /* 134 Baud, with RSVE = 57600 */ + break; + case ASYNC_SPD_VHI: + baud = 17; /* 150 Baud, with RSVE = 115200 */ + break; + case ASYNC_SPD_SHI: + baud = 18; + break; + case ASYNC_SPD_WARP: + baud = 19; + break; + case ASYNC_SPD_CUST: + div = info->custom_divisor; + break; + } + } + + if (!div) { + /* Maximum MIDI speed is 500000 */ + if (baud > 19) baud = 19; + div = MIDI_baud_table[baud]; + } + + if (div) { + /* turn on DTR */ + /* MIDI_DTRon(); */ + } else { + /* speed == 0 -> drop DTR */ + /* MIDI_DTRoff(); */ + return; + } + + mid_ctrl_new = 0; + for (timer_mode = 2; timer_mode >= 0; timer_mode--) + if (ACIA_prescaler_modes[timer_mode] == div) + break; + + baud_mask = ACIA_baud_masks[timer_mode]; + + chsize_mask = 0; + if (chsize == CS8) chsize_mask = ACIA_D8N2S; + + index = 0; + if (stopb) index |= (1 << 0); + if (chsize == CS8) index |= (1 << 1); + if (parity == 0) + index |= (2 << 2); + else if (parity == PARENB) + index |= (1 << 2); + else { /* if(parity == PARENB | PARODD) */ + /* index |= (0 << 2); */ + } + + fmt_mask = ACIA_char_fmt[index]; + + /* Now we have all parameters and can go to set them: */ + save_flags(cpu_status); + cli(); + + /* disable Rx and Tx while changing parameters + * stop timer D to set new timer value immediatly after re-enabling + */ + mid_ctrl_shadow &= ~ACIA_RESET; /* MASK_OUT significant bits */ + mid_ctrl_shadow |= baud_mask; + mid_ctrl_shadow &= ~ACIA_D8O1S; + mid_ctrl_shadow |= fmt_mask; + + acia.mid_ctrl = mid_ctrl_shadow; /* Write only */ + restore_flags(cpu_status); + +#ifdef _DEBUG_MIDI_ + printk(" MIDI_change_speed() done. \n"); +#endif +} + + +static void MIDI_throttle( struct m68k_async_struct * info, int status ) +{ + if (status) ; /* MIDI_RTSoff(); */ + else ; /* MIDI_RTSon(); */ +} + + +static void MIDI_set_break( struct m68k_async_struct * info, int break_flag ) +{ +} + + +static unsigned int MIDI_get_modem_info( struct m68k_async_struct *info ) +{ + return( TIOCM_RTS | TIOCM_DTR | TIOCM_CAR | TIOCM_CTS | TIOCM_DSR ); +} + + +static void MIDI_get_serial_info( struct m68k_async_struct* info, struct serial_struct* retinfo ) +{ +#ifdef _DEBUG_MIDI_ + printk(" MIDI_get_serial_info() \n"); +#endif + + retinfo->baud_base = info->baud_base; + retinfo->custom_divisor = info->custom_divisor; +} + + +static int MIDI_set_modem_info( struct m68k_async_struct *info, int new_dtr, int new_rts ) +{ + /* Is it right to return an error or should the attempt to change + * DTR or RTS be silently ignored? + */ + return( -EINVAL ); +} + + +static void MIDI_stop_receive (struct m68k_async_struct *info) +{ + unsigned long cpu_status; + + save_flags(cpu_status); + cli(); + + /* disable receive interrupts */ + mid_ctrl_shadow &= ~ACIA_RIE; + acia.mid_ctrl = mid_ctrl_shadow; + restore_flags(cpu_status); +} + + +static int MIDI_trans_empty (struct m68k_async_struct *info) +{ + return (acia.mid_ctrl & ACIA_TDRE) != 0; +} + + +#ifdef MODULE +int init_module(void) +{ + return( atari_MIDI_init() ); +} + +void cleanup_module(void) +{ + atari_MIDI_interrupt_hook = NULL; + unregister_serial( midi_info->line ); +} +#endif + diff --git a/drivers/char/atari_MIDI.h b/drivers/char/atari_MIDI.h new file mode 100644 index 000000000000..49f786dab707 --- /dev/null +++ b/drivers/char/atari_MIDI.h @@ -0,0 +1,32 @@ +/* + * atari_MIDI.h: Definitions for the MIDI serial port + * + * 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. + * + */ + +#ifndef _ATARI_MIDI_H +#define _ATARI_MIDI_H + +int atari_MIDI_init( void ); + + +#ifdef _MIDI_HSK_LINES_ + +#define MIDI_RTSon() +#define MIDI_RTSoff() +#define MIDI_DTRon() +#define MIDI_DTRoff() + +else + +#define MIDI_RTSon +#define MIDI_RTSoff +#define MIDI_DTRon +#define MIDI_DTRoff + +#endif + +#endif /* _ATARI_MIDI_H */ diff --git a/drivers/char/atari_SCC.README b/drivers/char/atari_SCC.README new file mode 100644 index 000000000000..30d000b78a3f --- /dev/null +++ b/drivers/char/atari_SCC.README @@ -0,0 +1,194 @@ +atari_SCC.README +================ + + This is the README covering the new DMA features of the Atari SCC driver. It +works reasonably well for me, but you may disagree. If so, please contact: + +TeSche (Torsten Scherer) +itschere@techfak.uni-bielefeld.de + + +0) Intro + + As a funny intro let me cite the AMD Am8530H technical manual: "Use of a DMA +controller is suggested for any system that transmits above 500 kb/s". Harhar, +good joke... + + +1) Supported Hardware / Software + + That's simple to answer: Currently only the TT is supported, and since all +other Atari machines lack DMA support for the SCC this isn't likely to +change. There have been reports about some Falcons with Afterburner card(?) +claiming they have DMA, but they don't, believe me. I've strengthened the +condition to try DMA to 'DMA available and TT-MFP available', so you should be +on the safer side. However, for the severe cases it can be completely disabled +by a config option. + + With the same hardware reason all numbers or ratings you may occasionally read +in this file are measured on a stock TT with monochrome monitor. + + The SCC_DMA driver has been tested with speeds up to 115k2 baud. It may work +with something higher, but I'm not sure about the 230k4 and 560k8 that have +recently been added, meaning, I haven't tested it because I can't. In theory it +should work with 230k4 baud with the default parameters (see below), but not +with 560k8. + + +2) Basic Principle of Operation + + This driver uses DMA to support receiving of data. It does not use DMA to +transmit data. Roman Hodek once philosophized about doing a little statistic +comparing how much data was received and how much was transmitted to decide +whether to use DMA for the next receive or the next transmit each time it +becomes vacant. That's a) horribly complicated and b) discards the fact that we +all have problems with receiver overruns, and not with transmit underruns. So +only DMA receive is supported. And, hey, I said he philosophized, I didn't say +he was thinking about it seriously. :) + + There's another problem with general purpose communication: You don't know in +advance how many data will arrive. Ok, in case of IP you do after you've read +the header, but that's just a special case. In general this leads to that the +driver doesn't know how many data is to be expected and thus can't set up the +DMA for exactly that amount of data. What it does is set up DMA on a buffer +that it thinks it large enough and let a timer routine flush this buffer +asyncronously. + + This timer routine must stop DMA, read restbytes and restart DMA on another +buffer very, very quickly. It doesn't even flush the buffers on its own, but +uses a bottom half handler for this. Since this one can be delayed for many +reasons it's not enough to have just two buffers to switch between (like TTY +flip buffers), but instead you need more. + + These buffers are directly flushed to the TTY's receive function, may that be +a PPP line discipline or whatever. The whole concept of flip buffers is totally +circumvented, as these buffers are very likely to run over when switching VTYs, +in fact I'd say you can regard this as a law. :( + + A nice little side effect of DMA support is that you save a lot of +cycles. When the whole stuff of delivering data (avoiding the byte-oriented +flip buffer interface) and waking up listening programs has only to be done +once for every 1k of data that saves you an awful lot of computing power. You +can easily see that if you compare DMA supported download with uploads, which +still operate the normal way. + + If you should see a message like: + +Sep 25 22:27:26 zaphod kernel: SCC-A: 1 overrun, 0 parity, 0 frame errors + + that means there have been errors in the serial link. Since 99% of all +possible errors (given a correct serial parameter setting) are likely to be +overrun errors and the main task of DMA support is exactly to get rid of them I +chosed to print these information unconditionally each time an error +occurs. You shouldn't really see a lot of them, in theory: None. If you see +*some* be happy that they're less than with ordinary serial ports. If you don't +think they're less, complain. + + If you should see a message like: + +Sep 25 22:26:08 zaphod kernel: SCC-A: fatal buffer overflow, data lost! + + that means that execution of the bottom half flusher was delayed so long that +the timer routine didn't find a free buffer. This can only be caused by +exhaustive VTY switching I guess, all other things that could happen are +unlikely to block your system for so long. However, if it happens there's not +very much to do besides increasing the number of buffers (see below). + + If you should see a message like: + +Sep 26 10:39:32 zaphod kernel: SCC-A: DMA-INT occured, data lost! + + that means that the timer routine itself has been delayed so long that the DMA +counter went to zero already. There's not very much to do about this, because +since both the timer and the end-of-dma INT are IPL6 the latter one is probably +also delayed and thus data is already lost. The only thing you can do is +increase the size of the buffers. + + So there are three basic parameters: The number of buffers to use, their size +and the flushing frequency. + + +3) Operating Parameters + + Somewhere at the beginning of atari_SCC.c you'll find a short section that +defines some parameters of the DMA support, namely the number and size of +buffers. The flushing rate is encoded a bit more implicitly more downwards. My +defaults for a TT are 8 buffers of 2k and a flushing rate of 12Hz. + + 12Hz flushing rate at a receive speed of 115k2 baud leads to 960 chars per +buffer. Choosing 1k looked to be a good idea, but extensive SCSI activity +ruined that one. Since the buffer size must be a power of two I've chosen 2k. +If the bottom half flusher finds a buffer which is more than 95% full it prints +a warning. If you should see lots of these messages it can only mean you're +running something very much higher than 115k2 baud, in which case you should +increase the buffer size yet more (to 4k). If you're not running at higher +rates you've got the problem that the flusher gets extremely delayed. You can +still increase the buffer size, but you should better find out what's delaying +the flusher and fix that. + + The number of buffers is also experimental: I've seen that on a TT with +monochrome screen a VTY switching can 'use' 3 or 4 buffers, sometimes 5. So +with 8 buffers you should be on the safer side. You can activate the diagnostic +printk in SCC_flush() if you want to check that out. You may choose any number +of buffers, no restriction on this one. + + I might add code to dynamically select these parameters depending upon the +current speed later, but for the moment this should work nicely. + + +4) Debugging + + It is possible to define DEBUG_DMA at the beginning of atari_SCC.c. In that +case the driver will print lots of information about what it is doing with or +getting from the DMA. It then uses a flushing frequency of 0.48Hz to allow you +to follow the output with your eyes, but this also means that with a buffer +size of 1k you shouldn't use more than 19k2 baud or else you'll get problems. + + +5) Performance + + I've written a small program which opens a tty in raw mode and dumps all data +that arrives into a file while printing how fast data arrived. Using this +program I can receive data at 115k2 baud without a single error, even while +switching VTYs or SCSI disc accesses. This costs me almost no cycle. :) + + When being logged in via the SCC and downloading data at 115k2 baud with rz I +get an average transfer rate of 9800 cps over 10 megs, including 12 errors +(whilst heaviest disc activity). No disc activity yields 10600 cps and 0 +errors. This costs me nearly 40% of my cycles, probably because that version of +rz isn't very clever. + + On a 115k2 baud PPP link I get an average paket loss of less than 2%, yielding +at worst 7k/s. When dialing into our university network with a 28.8 modem +running 115k2 baud I get less than 1% paket loss, yielding at worst 2.5k/s +(best case so far: 3.2k/s). In either case you can verify that ftp download +only costs you some 5% of your cycles, whereas an upload costs you almost any +cycle you've got. + + +6) Problems + + There're a couple of words to say about why I still get paket loss: + + The paket loss is caused by 'bad fcs' fields, as you can see when you enable +'kdebug' in pppd. I've tried dumping the bad pakets in the PPP layer, and it +showed me that the normal data stream was interrupted by some trash data being +inserted. I've also tried dumping the data at SCC level, and the trash was +present there too. + + Numerous experiments have shown that these errors occur when the SCC *sends* +data while receiving, which is perfectly consistent with the fact that I don't +get a single error with my own just-dump-it program. Anything you do which +forces data to be send increases probability of these errors. In case of a PPP +link running ftp chose a big MTU/MRU rate, as this reduces the amount of +acknowledge packets to be send back and you can see that the error rate drops. + + Since I couldn't find anything in the tx_int function which might cause this I +think it's a hardware bug. It wouldn't be the first one my TT has got... :( + + BUT: Whereever these problems may come from, they're *far* less disturbing +than the SCC driver without DMA support, so I think this state is legal for the +moment. :) + +04.Dec.1966, +TeSche diff --git a/drivers/char/atari_SCC.c b/drivers/char/atari_SCC.c new file mode 100644 index 000000000000..965beaeaaad1 --- /dev/null +++ b/drivers/char/atari_SCC.c @@ -0,0 +1,2624 @@ +/* + * drivers/char/atari_SCC.c: Atari SCC serial ports implementation + * + * Copyright 1994-95 Roman Hodek + * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o + * + * Some parts were taken from the (incomplete) SCC driver by Lars Brinkhoff + * + * + * Adapted to 1.2 by Andreas Schwab + * + * Adapted to support MVME147, MVME162 and BVME6000 by Richard Hirst + * + * 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. + * + * Description/Notes: + * + * - This driver currently handles the asynchronous modes of the SCC ports + * only. Synchronous operation or packet modes aren't implemented yet. + * + * - Since there are many variations how the SCC can be integrated, the + * driver offers the possibility to provide the frequencies attached to the + * various clock inputs via an ioctl (along with an externally calculated + * baud table). + * + * - I haven't spent much time for optimizations yet... + * + * - Channel A is accessible via two different devices: ttyS3 and ttyS4. The + * former is the RS232 "Serial2" port, the latter the RS422 "LAN" port. + * Only one of these devices can be open at one time. + * + * - ++TeSche 12/96: DMA support for channel A, see atari_SCC.README for details + * send comments/problems to: itschere@techfak.uni-bielefeld.de + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef CONFIG_MVME147_SCC +#include +#endif +#ifdef CONFIG_MVME162_SCC +#include +#endif +#ifdef CONFIG_BVME6000_SCC +#include +#endif +#ifdef CONFIG_ATARI +#include +#include +#endif +#include +#include + +#include "atari_SCC.h" + + +#if defined CONFIG_ATARI_SCC || defined CONFIG_ATARI_SCC_MODULE +#define ENABLE_ATARI_SCC +#endif + +#define DEBUG_INT 0x01 +#define DEBUG_INIT 0x02 +#define DEBUG_THROTTLE 0x04 +#define DEBUG_INFO 0x08 +#define DEBUG_SPEED 0x10 +#define DEBUG_OPEN 0x20 +#define DEBUG_OVERRUNS 0x40 +/* warning: DEBUG_DMA will lead to a driver which is not able to operate at + * more than 19200 bps without causing overruns! + */ +#define DEBUG_DMA 0x80 + +#define DEBUG_ALL 0xffffffff +#define DEBUG_NONE 0 + +#define DEBUG DEBUG_NONE + +#define CHANNEL_A 0 +#define CHANNEL_B 1 + + +/* Shadows for all SCC write registers */ +static unsigned char SCC_shadow[2][16]; + +/* Location to access for SCC register access delay */ +static volatile unsigned char *scc_del; + +/* To keep track of STATUS_REG state for detection of Ext/Status int source */ +static unsigned char SCC_last_status_reg[2]; + +/* This array tells the clocks connected to RTxC or TRxC, resp., (2nd + * index) for each channel (1st index). + * + * This table is initialzed for the TT. If we run on another machine, + * the values are changed by the initialization function. + */ + +static unsigned SCC_clocks[2][2] = { + /* RTxC */ /* TRxC */ + { SCC_BAUD_BASE_PCLK4, SCC_BAUD_BASE_NONE }, /* Channel A */ + { SCC_BAUD_BASE_TIMC, SCC_BAUD_BASE_BCLK } /* Channel B */ +}; + +/* The SCC's master clock (as variable, in case someone has unusual + * hardware) + */ + +static unsigned SCC_PCLK = SCC_BAUD_BASE_PCLK; + + +/* BRG values for the standard speeds and the various clock sources */ + +typedef struct { + unsigned clksrc; /* clock source to use or -1 for not possible */ + unsigned div; /* divisor: 1, 2 and 4 correspond to + * direct 1:16, 1:32 and 1:64 modes, + * divisors >= 4 yield a BRG value of + * div/2-2 (in 1:16 mode) + */ +} BAUD_ENTRY; + +/* A pointer for each channel to the current baud table */ +static BAUD_ENTRY *SCC_baud_table[2]; + +/* Baud table format: + * + * Each entry consists of the clock source (CLK_RTxC, CLK_TRxC or + * CLK_PCLK) and a divisor. The following rules apply to the divisor: + * + * - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use + * the BRG) + * + * - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible) + * + * - CLK_PCLK: >= 4 and even (no direct modes, only BRG) + * + */ + +#ifdef ENABLE_ATARI_SCC +/* This table is used if RTxC = 3.672 MHz. This is the case for TT's + * channel A and for both channels on the Mega STE/Falcon. (TRxC is unused) + */ + +static BAUD_ENTRY bdtab_norm[20] = { + /* B0 */ { 0, 0 }, + /* B50 */ { CLK_RTxC, 4590 }, + /* B75 */ { CLK_RTxC, 3060 }, + /* B110 */ { CLK_PCLK, 4576 }, + /* B134 */ { CLK_PCLK, 3756 }, + /* B150 */ { CLK_RTxC, 1530 }, + /* B200 */ { CLK_PCLK, 2516 }, + /* B300 */ { CLK_PCLK, 1678 }, + /* B600 */ { CLK_PCLK, 838 }, + /* B1200 */ { CLK_PCLK, 420 }, + /* B1800 */ { CLK_PCLK, 280 }, + /* B2400 */ { CLK_PCLK, 210 }, + /* B4800 */ { CLK_RTxC, 48 }, + /* B9600 */ { CLK_RTxC, 24 }, + /* B19200 */ { CLK_RTxC, 12 }, + /* B38400 */ { CLK_RTxC, 6 }, /* #15 spd_extra */ + /* B57600 */ { CLK_RTxC, 4 }, /* #16 spd_hi */ + /* B115200 */ { CLK_RTxC, 2 }, /* #17 spd_vhi */ + /* B230400 */ { CLK_RTxC, 1 }, /* #18 spd_shi */ + /* B460800 */ { 0, 0 } /* #19 spd_warp: Impossible */ +}; + +/* This is a special table for the TT channel B with 307.2 kHz at RTxC + * and 2.4576 MHz at TRxC + */ +static BAUD_ENTRY bdtab_TTChB[20] = { + /* B0 */ { 0, 0 }, + /* B50 */ { CLK_RTxC, 384 }, + /* B75 */ { CLK_RTxC, 256 }, + /* B110 */ { CLK_PCLK, 4576 }, + /* B134 */ { CLK_PCLK, 3756 }, + /* B150 */ { CLK_RTxC, 128 }, + /* B200 */ { CLK_RTxC, 96 }, + /* B300 */ { CLK_RTxC, 64 }, + /* B600 */ { CLK_RTxC, 32 }, + /* B1200 */ { CLK_RTxC, 16 }, + /* B1800 */ { CLK_PCLK, 280 }, + /* B2400 */ { CLK_RTxC, 8 }, + /* B4800 */ { CLK_RTxC, 4 }, + /* B9600 */ { CLK_RTxC, 2 }, + /* B19200 */ { CLK_RTxC, 1 }, + /* B38400 */ { CLK_TRxC, 4 }, + /* B57600 */ { CLK_TRxC, 2 }, /* 57600 is not possible, use 76800 instead */ + /* B115200 */ { CLK_TRxC, 1 }, /* 115200 is not possible, use 153600 instead */ + /* B230400 */ { 0, 0 }, /* #18 spd_shi: Impossible */ + /* B460800 */ { 0, 0 } /* #19 spd_warp: Impossible */ +}; +#endif + +#ifdef CONFIG_MVME147_SCC +/* This table is used if RTxC = pCLK = 5 MHz. This is the case for MVME147 + */ +static BAUD_ENTRY bdtab_m147[19] = { + /* B0 */ { 0, 0 }, + /* B50 */ { CLK_PCLK, 6250 }, + /* B75 */ { CLK_PCLK, 4166 }, + /* B110 */ { CLK_PCLK, 2814 }, + /* B134 */ { CLK_PCLK, 2322 }, + /* B150 */ { CLK_PCLK, 2084 }, + /* B200 */ { CLK_PCLK, 1562 }, + /* B300 */ { CLK_PCLK, 1040 }, + /* B600 */ { CLK_PCLK, 520 }, + /* B1200 */ { CLK_PCLK, 260 }, + /* B1800 */ { CLK_PCLK, 194 }, + /* B2400 */ { CLK_PCLK, 130 }, + /* B4800 */ { CLK_PCLK, 64 }, + /* B9600 */ { CLK_PCLK, 32 }, + /* B19200 */ { CLK_PCLK, 16 }, + /* B38400 */ { CLK_PCLK, 8 }, + /* B57600 */ { CLK_PCLK, 4 }, + /* B115200 */ { CLK_PCLK, 2 }, + /* B230400 */ { CLK_PCLK, 1 }, /* #18 spd_shi */ +}; +#endif + +#ifdef CONFIG_MVME162_SCC +/* This table is used if RTxC = pCLK = 10 MHz. This is the case for MVME162 + */ +static BAUD_ENTRY bdtab_mvme[20] = { + /* B0 */ { 0, 0 }, + /* B50 */ { CLK_PCLK, 12500 }, + /* B75 */ { CLK_PCLK, 8332 }, + /* B110 */ { CLK_PCLK, 5682 }, + /* B134 */ { CLK_PCLK, 4646 }, + /* B150 */ { CLK_PCLK, 4166 }, + /* B200 */ { CLK_PCLK, 3124 }, + /* B300 */ { CLK_PCLK, 2082 }, + /* B600 */ { CLK_PCLK, 1042 }, + /* B1200 */ { CLK_PCLK, 520 }, + /* B1800 */ { CLK_PCLK, 390 }, + /* B2400 */ { CLK_PCLK, 260 }, + /* B4800 */ { CLK_PCLK, 130 }, + /* B9600 */ { CLK_PCLK, 64 }, + /* B19200 */ { CLK_PCLK, 32 }, + /* B38400 */ { CLK_PCLK, 16 }, + /* B57600 */ { CLK_PCLK, 8 }, + /* B115200 */ { CLK_PCLK, 4 }, + /* B230400 */ { CLK_PCLK, 2 }, /* #18 spd_shi */ + /* B460800 */ { CLK_PCLK, 1 } /* #19 spd_warp */ +}; +#endif + +#ifdef CONFIG_BVME6000_SCC + +/* This table is used if RTxC = 7.3728 MHz. This is the case for BVMs + */ +static BAUD_ENTRY bdtab_bvme[18] = { + /* B0 */ { 0, 0 }, + /* B50 */ { CLK_RTxC, 9216 }, + /* B75 */ { CLK_RTxC, 6144 }, + /* B110 */ { CLK_RTxC, 4188 }, + /* B134 */ { CLK_RTxC, 3424 }, + /* B150 */ { CLK_RTxC, 3072 }, + /* B200 */ { CLK_RTxC, 2304 }, + /* B300 */ { CLK_RTxC, 1536 }, + /* B600 */ { CLK_RTxC, 768 }, + /* B1200 */ { CLK_RTxC, 384 }, + /* B1800 */ { CLK_RTxC, 256 }, + /* B2400 */ { CLK_RTxC, 192 }, + /* B4800 */ { CLK_RTxC, 96 }, + /* B9600 */ { CLK_RTxC, 48 }, + /* B19200 */ { CLK_RTxC, 24 }, + /* B38400 */ { CLK_RTxC, 12 }, + /* B57600 */ { CLK_RTxC, 8 }, + /* B115200 */ { CLK_RTxC, 4 } +}; +#endif + + +/* User settable tables */ +static BAUD_ENTRY bdtab_usr[2][20]; + + +/* + Here are the values to compute the tables above. For each base + clock, the BRG values for the common bps rates are listed. The + divisor is (BRG+2)*2. For each clock, the 1:16 and 1:32 are also + usable (and the BRG isn't used). 1:64 is the same as BRG with + k==0. If more than clock source was possible for a bps rate I've + choosen the one with the smallest error. + + For 307.2 kHz == base 19200: + 50 bps -> 190 + 75 bps -> 126 + 110 bps -> 85 (really 110.34 bps, error 0.31 %) + 134 bps -> 70 (really 133.33 bps, error 0.49 %) + 150 bps -> 62 + 200 bps -> 46 + 300 bps -> 30 + 600 bps -> 14 + 1200 bps -> 6 + 1800 bps -> 3 (really 1920 bps, error 6.7 %) + 2400 bps -> 2 + 4800 bps -> 0 + + For 2.4576 MHz == base 153600: + 50 bps -> 1534 + 75 bps -> 1022 + 110 bps -> 696 (really 110.03 bps, error 0.027 %) + 134 bps -> 571 (really 134.03 bps, error 0.022 %) + 150 bps -> 510 + 200 bps -> 382 + 300 bps -> 254 + 600 bps -> 126 + 1200 bps -> 62 + 1800 bps -> 41 (really 1786.1 bps, error 0.77 %) + 2400 bps -> 30 + 4800 bps -> 14 + 9600 bps -> 6 + 19200 bps -> 2 + 38400 bps -> 0 + + For 3.672 MHz == base 229500: + 50 bps -> 2293 + 75 bps -> 1528 + 110 bps -> 1041 + 134 bps -> 854 + 150 bps -> 763 + 200 bps -> 572 + 300 bps -> 381 + 600 bps -> 189 + 1200 bps -> 94 + 1800 bps -> 62 + 2400 bps -> 46 + 4800 bps -> 22 + 9600 bps -> 10 + 19200 bps -> 4 + 38400 bps -> 1 + 57600 bps -> 0 + + For 8.053976 MHz == base 503374: + 0 bps -> 0 + 50 bps -> 5032 + 75 bps -> 3354 + 110 bps -> 2286 + 134 bps -> 1876 + 150 bps -> 1676 + 200 bps -> 1256 + 300 bps -> 837 + 600 bps -> 417 + 1200 bps -> 208 + 1800 bps -> 138 + 2400 bps -> 103 + 4800 bps -> 50 + 9600 bps -> 24 + 19200 bps -> 11 + 31500 bps -> 6 (really 31461 bps) + 50000 bps -> 3 + 125000 bps -> 0 + +*/ + + +/* Is channel A switchable between two hardware ports? (Serial2/LAN) */ + +#define SCCA_SWITCH_SERIAL2_ONLY 0 /* only connected to Serial2 */ +#define SCCA_SWITCH_LAN_ONLY 1 /* only connected to LAN */ +#define SCCA_SWITCH_BOTH 2 /* connected to both, switch by + * IO7 in the PSG */ + +static int SCC_chan_a_switchable; + +/* Is channel A (two ports!) already open? */ +static int SCC_chan_a_open; + +/* For which line has channel A been opened? */ +static int SCC_chan_a_line; + +/* info pointer for SCC_chan_a_line */ +static struct m68k_async_struct *SCC_chan_a_info; + +/* Are the register addresses for the channels reversed? (B before A). This is + * the case for the ST_ESCC. */ +static int ChannelsReversed; + +/* This macro sets up the 'info' pointer for the interrupt functions of + * channel A. It addresses the following problem: The isrs were registered + * with callback_data == &rs_table[3] (= Serial2). But they can also be for + * &rs_table[4] (LAN), if this port is the open one. SETUP_INFO() thus + * advances the pointer if info->line == 3. + */ + +#define DEFAULT_CHANNEL_B_LINE 1 /* ttyS1 */ +#define DEFAULT_CHANNEL_A232_LINE 3 /* ttyS3 */ +#define DEFAULT_CHANNEL_A422_LINE 4 /* ttyS4 */ + +static int chb_line = -1, cha232_line = -1, cha422_line = -1; + +#ifndef CONFIG_ATARI_SCC_DMA +#define scca_dma 0 /* No DMA support */ +#else +static int scca_dma = 0; /* whether DMA is supported at all */ + +/* ++TeSche: these next few things are for DMA support on channel A. both + * BUFFERS and BUFSIZE must be a power of two (because of speed reasons)! + */ +#define SCCA_DMA_BUFFERS 8 +#define SCCA_DMA_BUFSIZE 2048 +typedef struct { + u_char *buf, *pbuf, *err; + int inbuf; + short cntOver, cntPar, cntFrame; + unsigned active:1; /* whether a DMA transfer is using this buffer */ + unsigned needsFlushing:1; /* whether it's dirty (even when inbuf==0) */ +} DMABUF; +static DMABUF scca_dma_buf[SCCA_DMA_BUFFERS]; +static DMABUF *scca_dma_head, *scca_dma_tail; +static DMABUF *scca_dma_end = &scca_dma_buf[SCCA_DMA_BUFFERS]; + +#endif + +#define SETUP_INFO(info) \ + do { \ + if (info->line == cha232_line) \ + info = SCC_chan_a_info; \ + } while(0) + + +/***************************** Prototypes *****************************/ + +#ifdef ENABLE_ATARI_SCC +static void SCC_init_port( struct m68k_async_struct *info, int type, int channel ); +#endif +#ifdef CONFIG_MVME147_SCC +static void m147_init_port( struct m68k_async_struct *info, int type, int channel ); +#endif +#ifdef CONFIG_MVME162_SCC +static void mvme_init_port( struct m68k_async_struct *info, int type, int channel ); +#endif +#ifdef CONFIG_BVME6000_SCC +static void bvme_init_port( struct m68k_async_struct *info, int type, int channel ); +#endif +#ifdef MODULE +static void SCC_deinit_port( struct m68k_async_struct *info, int channel ); +#endif +static void SCC_rx_int (int irq, void *data, struct pt_regs *fp); +static void SCC_spcond_int (int irq, void *data, struct pt_regs *fp); +static void SCC_tx_int (int irq, void *data, struct pt_regs *fp); +static void SCC_stat_int (int irq, void *data, struct pt_regs *fp); +#ifdef ENABLE_ATARI_SCC +static void SCC_ri_int (int irq, void *data, struct pt_regs *fp); +#endif +static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct + *tty, struct file *file ); +static void SCC_init( struct m68k_async_struct *info ); +static void SCC_deinit( struct m68k_async_struct *info, int leave_dtr ); +static void SCC_enab_tx_int( struct m68k_async_struct *info, int enab_flag ); +static int SCC_check_custom_divisor( struct m68k_async_struct *info, int baud_base, + int divisor ); +static void SCC_change_speed( struct m68k_async_struct *info ); +static int SCC_clocksrc( unsigned baud_base, unsigned channel ); +static void SCC_throttle( struct m68k_async_struct *info, int status ); +static void SCC_set_break( struct m68k_async_struct *info, int break_flag ); +static void SCC_get_serial_info( struct m68k_async_struct *info, struct + serial_struct *retinfo ); +static unsigned int SCC_get_modem_info( struct m68k_async_struct *info ); +static int SCC_set_modem_info( struct m68k_async_struct *info, int new_dtr, int + new_rts ); +static int SCC_ioctl( struct tty_struct *tty, struct file *file, struct + m68k_async_struct *info, unsigned int cmd, unsigned long + arg ); +static void SCC_stop_receive (struct m68k_async_struct *info); +static int SCC_trans_empty (struct m68k_async_struct *info); +#ifdef CONFIG_ATARI_SCC_DMA +static void SCC_timer_int (int irq, void *data, struct pt_regs *fp); +static void SCC_dma_int (int irq, void *data, struct pt_regs *fp); +#endif + +/************************* End of Prototypes **************************/ + + +static SERIALSWITCH SCC_switch = { + SCC_init, SCC_deinit, SCC_enab_tx_int, + SCC_check_custom_divisor, SCC_change_speed, + SCC_throttle, SCC_set_break, + SCC_get_serial_info, SCC_get_modem_info, + SCC_set_modem_info, SCC_ioctl, SCC_stop_receive, SCC_trans_empty, + SCC_check_open +}; + +extern int atari_SCC_init_done; +extern int atari_SCC_reset_done; + + +#ifdef CONFIG_BVME6000_SCC + +int bvme_SCC_init( void ) +{ + struct serial_struct req; + int nr = 0; + + if (!MACH_IS_BVME6000) + return (-ENODEV); + + scc_del = (unsigned char *)0; + + SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY; + SCC_PCLK = SCC_BAUD_BASE_BVME_PCLK; + + /* General initialization */ + ChannelsReversed = 8; + SCC_chan_a_open = 0; + + req.line = DEFAULT_CHANNEL_B_LINE; + req.type = SER_SCC_BVME; + req.port = BVME_SCC_B_ADDR; + if ((chb_line = register_serial( &req )) >= 0) { + bvme_init_port( &rs_table[chb_line], req.type, CHANNEL_B ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line ); + + /* Init channel A, RS232 part (Serial2) */ + req.line = 0; + req.type = SER_SCC_BVME; + req.port = BVME_SCC_A_ADDR; + if ((cha232_line = register_serial( &req )) >= 0) { + bvme_init_port( &rs_table[cha232_line], req.type, CHANNEL_A ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line ); + + return( nr > 0 ? 0 : -ENODEV ); +} + + +static void bvme_init_port( struct m68k_async_struct *info, int type, int channel ) +{ + static int called = 0, ch_a_inited = 0; + SCC_ACCESS_INIT(info); + + info->sw = &SCC_switch; + + /* set ISRs, but don't enable interrupts yet (done in init()); + */ + if (channel == CHANNEL_B || !ch_a_inited) { + request_irq(channel ? BVME_IRQ_SCCB_TX : BVME_IRQ_SCCA_TX, + SCC_tx_int, BVME_IRQ_TYPE_PRIO, + channel ? "SCC-B TX" : "SCC-A TX", info); + request_irq(channel ? BVME_IRQ_SCCB_STAT : BVME_IRQ_SCCA_STAT, + SCC_stat_int, BVME_IRQ_TYPE_PRIO, + channel ? "SCC-B status" : "SCC-A status", info); + request_irq(channel ? BVME_IRQ_SCCB_RX : BVME_IRQ_SCCA_RX, + SCC_rx_int, BVME_IRQ_TYPE_PRIO, + channel ? "SCC-B RX" : "SCC-A RX", info); + request_irq(channel ? BVME_IRQ_SCCB_SPCOND : BVME_IRQ_SCCA_SPCOND, + SCC_spcond_int, BVME_IRQ_TYPE_PRIO, + channel ? "SCC-B special cond" : "SCC-A special cond", info); + + } + + /* Hardware initialization */ + + if (!called) { + /* Set the interrupt vector */ + SCCwrite( INT_VECTOR_REG, BVME_IRQ_SCC_BASE ); + + /* Interrupt parameters: vector includes status, status low */ + SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT ); + + /* Set the baud tables */ + SCC_baud_table[CHANNEL_A] = bdtab_bvme; + SCC_baud_table[CHANNEL_B] = bdtab_bvme; + + /* Set the clocks */ + SCC_clocks[CHANNEL_A][CLK_RTxC] = SCC_BAUD_BASE_BVME; + SCC_clocks[CHANNEL_A][CLK_TRxC] = SCC_BAUD_BASE_NONE; + SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_BVME; + SCC_clocks[CHANNEL_B][CLK_TRxC] = SCC_BAUD_BASE_NONE; + + SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB ); + } + + /* disable interrupts for this channel */ + SCCwrite( INT_AND_DMA_REG, 0 ); + + called = 1; + if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1; +} + +#endif + + +#ifdef CONFIG_MVME147_SCC + +int m147_SCC_init( void ) +{ + struct serial_struct req; + int nr = 0; + + if (!MACH_IS_MVME147) + return (-ENODEV); + + scc_del = (unsigned char *)0; + + SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY; + SCC_PCLK = SCC_BAUD_BASE_M147_PCLK; + + /* General initialization */ + ChannelsReversed = 2; + SCC_chan_a_open = 0; + + req.line = DEFAULT_CHANNEL_B_LINE; + req.type = SER_SCC_MVME; + req.port = M147_SCC_B_ADDR; + if ((chb_line = register_serial( &req )) >= 0) { + m147_init_port( &rs_table[chb_line], req.type, CHANNEL_B ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line ); + + /* Init channel A, RS232 part (Serial2) */ + req.line = 0; + req.type = SER_SCC_MVME; + req.port = M147_SCC_A_ADDR; + if ((cha232_line = register_serial( &req )) >= 0) { + m147_init_port( &rs_table[cha232_line], req.type, CHANNEL_A ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line ); + /* + * Ensure interrupts are enabled in the PCC chip + */ + m147_pcc->serial_cntrl=PCC_LEVEL_SERIAL|PCC_INT_ENAB; + + return( nr > 0 ? 0 : -ENODEV ); +} + + +static void m147_init_port( struct m68k_async_struct *info, int type, int channel ) +{ + static int called = 0, ch_a_inited = 0; + SCC_ACCESS_INIT(info); + + info->sw = &SCC_switch; + + /* set ISRs, but don't enable interrupts yet (done in init()); + */ + if (channel == CHANNEL_B || !ch_a_inited) { + request_irq(channel ? MVME147_IRQ_SCCB_TX : MVME147_IRQ_SCCA_TX, + SCC_tx_int, MVME147_IRQ_TYPE_PRIO, + channel ? "SCC-B TX" : "SCC-A TX", info); + request_irq(channel ? MVME147_IRQ_SCCB_STAT : MVME147_IRQ_SCCA_STAT, + SCC_stat_int, MVME147_IRQ_TYPE_PRIO, + channel ? "SCC-B status" : "SCC-A status", info); + request_irq(channel ? MVME147_IRQ_SCCB_RX : MVME147_IRQ_SCCA_RX, + SCC_rx_int, MVME147_IRQ_TYPE_PRIO, + channel ? "SCC-B RX" : "SCC-A RX", info); + request_irq(channel ? MVME147_IRQ_SCCB_SPCOND : MVME147_IRQ_SCCA_SPCOND, + SCC_spcond_int, MVME147_IRQ_TYPE_PRIO, + channel ? "SCC-B special cond" : "SCC-A special cond", info); + + } + + /* Hardware initialization */ + + if (!called) { + /* Set the interrupt vector */ + SCCwrite( INT_VECTOR_REG, MVME147_IRQ_SCC_BASE ); + + /* Interrupt parameters: vector includes status, status low */ + SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT ); + + /* Set the baud tables */ + SCC_baud_table[CHANNEL_A] = bdtab_m147; + SCC_baud_table[CHANNEL_B] = bdtab_m147; + + /* Set the clocks */ + SCC_clocks[CHANNEL_A][CLK_RTxC] = SCC_BAUD_BASE_M147; + SCC_clocks[CHANNEL_A][CLK_TRxC] = SCC_BAUD_BASE_NONE; + SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_M147; + SCC_clocks[CHANNEL_B][CLK_TRxC] = SCC_BAUD_BASE_NONE; + + SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB ); + } + + /* disable interrupts for this channel */ + SCCwrite( INT_AND_DMA_REG, 0 ); + + called = 1; + if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1; +} + +#endif + +#ifdef CONFIG_MVME162_SCC + +int mvme_SCC_init( void ) +{ + struct serial_struct req; + int nr = 0; + + if (!MACH_IS_MVME16x || !(mvme16x_config & MVME16x_CONFIG_GOT_SCCA)) + return (-ENODEV); + + scc_del = (unsigned char *)0; + + SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY; + SCC_PCLK = SCC_BAUD_BASE_MVME_PCLK; + + /* General initialization */ + ChannelsReversed = 4; + SCC_chan_a_open = 0; + + req.line = DEFAULT_CHANNEL_B_LINE; + req.type = SER_SCC_MVME; + req.port = MVME_SCC_B_ADDR; + if ((chb_line = register_serial( &req )) >= 0) { + mvme_init_port( &rs_table[chb_line], req.type, CHANNEL_B ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line ); + + /* Init channel A, RS232 part (Serial2) */ + req.line = 0; + req.type = SER_SCC_MVME; + req.port = MVME_SCC_A_ADDR; + if ((cha232_line = register_serial( &req )) >= 0) { + mvme_init_port( &rs_table[cha232_line], req.type, CHANNEL_A ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line ); + /* + * Ensure interrupts are enabled in the MC2 chip + */ + *(volatile char *)0xfff4201d = 0x14; + + return( nr > 0 ? 0 : -ENODEV ); +} + + +static void mvme_init_port( struct m68k_async_struct *info, int type, int channel ) +{ + static int called = 0, ch_a_inited = 0; + SCC_ACCESS_INIT(info); + + info->sw = &SCC_switch; + + /* set ISRs, but don't enable interrupts yet (done in init()); + */ + if (channel == CHANNEL_B || !ch_a_inited) { + request_irq(channel ? MVME162_IRQ_SCCB_TX : MVME162_IRQ_SCCA_TX, + SCC_tx_int, MVME162_IRQ_TYPE_PRIO, + channel ? "SCC-B TX" : "SCC-A TX", info); + request_irq(channel ? MVME162_IRQ_SCCB_STAT : MVME162_IRQ_SCCA_STAT, + SCC_stat_int, MVME162_IRQ_TYPE_PRIO, + channel ? "SCC-B status" : "SCC-A status", info); + request_irq(channel ? MVME162_IRQ_SCCB_RX : MVME162_IRQ_SCCA_RX, + SCC_rx_int, MVME162_IRQ_TYPE_PRIO, + channel ? "SCC-B RX" : "SCC-A RX", info); + request_irq(channel ? MVME162_IRQ_SCCB_SPCOND : MVME162_IRQ_SCCA_SPCOND, + SCC_spcond_int, MVME162_IRQ_TYPE_PRIO, + channel ? "SCC-B special cond" : "SCC-A special cond", info); + + } + + /* Hardware initialization */ + + if (!called) { + /* Set the interrupt vector */ + SCCwrite( INT_VECTOR_REG, MVME162_IRQ_SCC_BASE ); + + /* Interrupt parameters: vector includes status, status low */ + SCCwrite( MASTER_INT_CTRL, MIC_VEC_INCL_STAT ); + + /* Set the baud tables */ + SCC_baud_table[CHANNEL_A] = bdtab_mvme; + SCC_baud_table[CHANNEL_B] = bdtab_mvme; + + /* Set the clocks */ + SCC_clocks[CHANNEL_A][CLK_RTxC] = SCC_BAUD_BASE_MVME; + SCC_clocks[CHANNEL_A][CLK_TRxC] = SCC_BAUD_BASE_NONE; + SCC_clocks[CHANNEL_B][CLK_RTxC] = SCC_BAUD_BASE_MVME; + SCC_clocks[CHANNEL_B][CLK_TRxC] = SCC_BAUD_BASE_NONE; + + SCCmod( MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB ); + } + + /* disable interrupts for this channel */ + SCCwrite( INT_AND_DMA_REG, 0 ); + + called = 1; + if (CHANNR(info) == CHANNEL_A) ch_a_inited = 1; +} + +#endif + +#ifdef ENABLE_ATARI_SCC + +int atari_SCC_init( void ) +{ + struct serial_struct req; + int escc = ATARIHW_PRESENT(ST_ESCC); + int nr = 0; + extern char m68k_debug_device[]; + + /* SCC present at all? */ + if (!(ATARIHW_PRESENT(SCC) || ATARIHW_PRESENT(ST_ESCC))) + return( -ENODEV ); + + scc_del = &mfp.par_dt_reg; + +#ifdef CONFIG_ATARI_SCC_DMA + /* strengthen the condition a bit to be on the safer side... + */ + scca_dma = ATARIHW_PRESENT(SCC_DMA) && ATARIHW_PRESENT (TT_MFP); +#endif + + /* Channel A is switchable on the TT, MegaSTE and Medusa (extension), i.e. + * all machines with an SCC except the Falcon. If there's a machine where + * channel A is fixed to a RS-232 Serial2, add code to set to + * SCCA_SWITCH_SERIAL2_ONLY. + */ + if (MACH_IS_FALCON) + SCC_chan_a_switchable = SCCA_SWITCH_LAN_ONLY; + else if (ATARIHW_PRESENT(TT_MFP) || MACH_IS_MSTE) + SCC_chan_a_switchable = SCCA_SWITCH_BOTH; + else + SCC_chan_a_switchable = SCCA_SWITCH_SERIAL2_ONLY; + + /* General initialization */ + ChannelsReversed = escc ? 4 : 0; + SCC_chan_a_open = 0; + + /* Init channel B */ + if (!strcmp( m68k_debug_device, "ser2" )) + printk(KERN_NOTICE "SCC channel B: used as debug device\n" ); + else { + req.line = DEFAULT_CHANNEL_B_LINE; + req.type = SER_SCC_NORM; + req.port = (int)(escc ? &st_escc.cha_b_ctrl : &scc.cha_b_ctrl); + if ((chb_line = register_serial( &req )) >= 0) { + SCC_init_port( &rs_table[chb_line], req.type, CHANNEL_B ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel B\n", req.line ); + } + + /* Init channel A, RS232 part (Serial2) */ + if (SCC_chan_a_switchable != SCCA_SWITCH_LAN_ONLY) { + req.line = DEFAULT_CHANNEL_A232_LINE; + req.type = scca_dma ? SER_SCC_DMA : SER_SCC_NORM; + req.port = (int)(escc ? &st_escc.cha_a_ctrl : &scc.cha_a_ctrl); + if ((cha232_line = register_serial( &req )) >= 0) { + SCC_init_port( &rs_table[cha232_line], req.type, CHANNEL_A ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line ); + } + + /* Init channel A, RS422 part (LAN) */ + if (SCC_chan_a_switchable != SCCA_SWITCH_SERIAL2_ONLY) { + req.line = DEFAULT_CHANNEL_A422_LINE; + req.type = scca_dma ? SER_SCC_DMA : SER_SCC_NORM; + req.port = (int)(escc ? &st_escc.cha_a_ctrl : &scc.cha_a_ctrl); + if ((cha422_line = register_serial( &req )) >= 0) { + SCC_init_port( &rs_table[cha422_line], req.type, CHANNEL_A ); + ++nr; + } + else + printk(KERN_WARNING "Cannot allocate ttyS%d for SCC channel A\n", req.line ); + } + + return( nr > 0 ? 0 : -ENODEV ); +} + + +static void SCC_init_port( struct m68k_async_struct *info, int type, int channel ) +{ + static int called = 0, ch_a_inited = 0; + SCC_ACCESS_INIT(info); + + info->sw = &SCC_switch; + + /* set ISRs, but don't enable interrupts yet (done in init()); + * All interrupts are of type PRIORITIZED, which means they can be + * interrupted by all level 6 ints, but not by another SCC (or other level + * 5) int. I see no races with any MFP int, but I'm not quite sure yet + * whether longer delays in between the two-stage SCC register access can + * break things... + */ + if (channel == CHANNEL_B || !ch_a_inited) { + request_irq(channel ? IRQ_SCCB_TX : IRQ_SCCA_TX, + SCC_tx_int, IRQ_TYPE_PRIO, + channel ? "SCC-B TX" : "SCC-A TX", info); + request_irq(channel ? IRQ_SCCB_STAT : IRQ_SCCA_STAT, + SCC_stat_int, IRQ_TYPE_PRIO, + channel ? "SCC-B status" : "SCC-A status", info); + request_irq(channel ? IRQ_SCCB_RX : IRQ_SCCA_RX, + SCC_rx_int, IRQ_TYPE_PRIO, + channel ? "SCC-B RX" : "SCC-A RX", info); + request_irq(channel ? IRQ_SCCB_SPCOND : IRQ_SCCA_SPCOND, + SCC_spcond_int, IRQ_TYPE_PRIO, + channel ? "SCC-B special cond" : "SCC-A special cond", info); + + if (channel != 0 && ATARIHW_PRESENT (TT_MFP)) + request_irq(IRQ_TT_MFP_RI, SCC_ri_int, IRQ_TYPE_SLOW, + "TT-MFP ring indicator (modem 2)", info); + +#ifdef CONFIG_ATARI_SCC_DMA + if (channel == CHANNEL_A && !ch_a_inited && type == SER_SCC_DMA && scca_dma) { + + int i, size = SCCA_DMA_BUFFERS * SCCA_DMA_BUFSIZE; + + if (!(scca_dma_buf[0].err = kmalloc (size, GFP_KERNEL))) { + printk ("SCC-A: Cannot allocate buffers, DMA support disabled\n"); + scca_dma = 0; + } + + if (scca_dma) { + size = (size + PAGE_SIZE - 1) >> 12; + if (!(scca_dma_buf[0].buf = (u_char *)__get_dma_pages (GFP_KERNEL, size))) { + printk ("SCC-A: Cannot allocate buffers, DMA support disabled\n"); + kfree (scca_dma_buf[0].err); + scca_dma = 0; + } + } + + if (scca_dma) + if (tt_mfp.int_en_a & tt_mfp.int_mk_a & 0x20) { + printk ("SCC-A: TT_MFP Timer A already in use, DMA support disabled\n"); + free_pages ((unsigned long)scca_dma_buf[0].buf, size); + kfree (scca_dma_buf[0].err); + scca_dma = 0; + } + + if (scca_dma) { + + printk ("SCC-A: using %d buffers a %d bytes for DMA\n", SCCA_DMA_BUFFERS, SCCA_DMA_BUFSIZE); + + size = SCCA_DMA_BUFSIZE; + for (i=1; i> 12); + kfree (scca_dma_buf[0].err); + } +#endif + } +#endif +#ifdef CONFIG_MVME147 + if (MACH_IS_MVME147) { + free_irq(channel ? MVME147_IRQ_SCCB_TX : MVME147_IRQ_SCCA_TX, info); + free_irq(channel ? MVME147_IRQ_SCCB_STAT : MVME147_IRQ_SCCA_STAT, info); + free_irq(channel ? MVME147_IRQ_SCCB_RX : MVME147_IRQ_SCCA_RX, info); + free_irq(channel ? MVME147_IRQ_SCCB_SPCOND : MVME147_IRQ_SCCA_SPCOND, + info); + } +#endif +#ifdef CONFIG_MVME16x + if (MACH_IS_MVME16x) { + free_irq(channel ? MVME162_IRQ_SCCB_TX : MVME162_IRQ_SCCA_TX, info); + free_irq(channel ? MVME162_IRQ_SCCB_STAT : MVME162_IRQ_SCCA_STAT, info); + free_irq(channel ? MVME162_IRQ_SCCB_RX : MVME162_IRQ_SCCA_RX, info); + free_irq(channel ? MVME162_IRQ_SCCB_SPCOND : MVME162_IRQ_SCCA_SPCOND, + info); + } +#endif +#ifdef CONFIG_BVME6000 + if (MACH_IS_BVME6000) { + free_irq(channel ? BVME_IRQ_SCCB_TX : BVME_IRQ_SCCA_TX, info); + free_irq(channel ? BVME_IRQ_SCCB_STAT : BVME_IRQ_SCCA_STAT, info); + free_irq(channel ? BVME_IRQ_SCCB_RX : BVME_IRQ_SCCA_RX, info); + free_irq(channel ? BVME_IRQ_SCCB_SPCOND : BVME_IRQ_SCCA_SPCOND, + info); + } +#endif +} +#endif + + +#ifdef CONFIG_ATARI_SCC_DMA + +/*****************************************************************************/ + +/* + * TeSche's high-speed debugging helpers. it has proven (not only at this + * place) that ordinary printk()s cause much more problems than they solve when + * debugging interrupt handlers. these ones are not nice, but fast. + */ + +#if DEBUG & DEBUG_DMA + +#define DEBUGBUFSIZE 1024 +static char debugBuf[DEBUGBUFSIZE]; +static char *debugPtr = &debugBuf[0]; +static char *debugEndPtr = &debugBuf[DEBUGBUFSIZE-1]; + +static inline void debugString (char *s) +{ + while (*s && (debugPtr != debugEndPtr)) + *debugPtr++ = *s++; +} + +static inline void debugInt (int i) +{ + char *tmp = "0123456789"; /* maxint (unsigned) = 4294967296, 10 digits */ + short cnt = 10; + + while (--cnt > 0) { + tmp[cnt] = '0' + (i % 10); + i /= 10; + } + + while (*tmp == '0') + tmp++; + + while (*tmp && (debugPtr != debugEndPtr)) + *debugPtr++ = *tmp++; +} + +static char int2hex[] = "0123456789abcdef"; + +static inline void debugHex (unsigned int h) +{ + char *tmp = "01234567"; + short cnt = 8; + + while (--cnt > 0) { + tmp[cnt] = int2hex[h & 15]; + h >>= 4; + } + + while (*tmp == '0') + tmp++; + + while (*tmp && (debugPtr != debugEndPtr)) + *debugPtr++ = *tmp++; +} + +static inline void debugFlush (void) +{ + if (debugPtr != &debugBuf[0]) { + *debugPtr = 0; + printk ("%s\n", debugBuf); + debugPtr = &debugBuf[0]; + } +} + +#endif /* DEBUG & DEBUG_DMA */ + + +/*****************************************************************************/ + +/* + * these functions are for DMA support. they all assume that they're called + * with INTs off so that they can play with their data structures undisturbed. + */ + +static ulong dmaStartAddr = 0; /* 0 means DMA not running */ +static ulong dmaSize; + + +/* start DMA on current (scca_head) write buffer + */ +static inline void dma_start (void) +{ + if ((dmaSize = SCCA_DMA_BUFSIZE - scca_dma_head->inbuf) > 0) { + dmaStartAddr = (ulong)(scca_dma_head->pbuf + scca_dma_head->inbuf); /* needs no virt_to_phys() */ +#if DEBUG & DEBUG_DMA + debugString ("[start@0x"); + debugHex (dmaStartAddr & 0xffff); + debugString ("/"); + debugInt (dmaSize); + debugString ("] "); +#endif + tt_scc_dma.dma_ctrl &= ~3; + __asm__ __volatile__ ("movep.l %0,%1@(0)\n\t" + "movep.l %2,%3@(0)\n\t" + : /* no outputs */ + : "d"(dmaStartAddr), "a"(&tt_scc_dma.dma_addr_hi), + "d"(dmaSize), "a"(&tt_scc_dma.dma_cnt_hi) + : "memory"); + tt_scc_dma.dma_ctrl |= 2; + } else { + dmaStartAddr = 0; + } +} + + +/* stop DMA, read restbytes and adjust buffer counters + */ +static inline void dma_stop (void) +{ + register short rest; + ulong size = 0, endaddr; + unsigned char *from = (unsigned char *)&tt_scc_dma.dma_restdata, *to; + + if (!dmaStartAddr) + return; + + tt_scc_dma.dma_ctrl &= ~3; /* stop DMA */ + + /* ++TeSche: I've had tremendous problems with looking at dma_addr to see + * how many bytes were received rather than looking at dma_cnt. To me it + * looks like there are cases when dma_addr is not properly updated when + * DMA is aborted, so I ended up with calculating less bytes than actually + * were received. Dma_cnt seems to be ok for me, so this is the way I go. + * + * sigh, yet one more unspecified hardware feature? is it at all anywhere + * specified what happens when a DMA is aborted before it ends? + */ + __asm__ __volatile__ ("movep.l %1@(0),%0\n\t" + : "=d"(size) + : "a"(&tt_scc_dma.dma_cnt_hi) + : "memory"); + size = dmaSize - size; + +#if DEBUG & DEBUG_DMA + __asm__ __volatile__ ("movep.l %1@(0),%0\n\t" + : "=d"(endaddr) + : "a"(&tt_scc_dma.dma_addr_hi) + : "memory"); + if (endaddr - dmaStartAddr != size) { + debugString ("[size="); + debugInt (size); + debugString (",addr="); + debugInt (endaddr-dmaStartAddr); + debugString ("] "); + } +#endif + + endaddr = dmaStartAddr + size; + + if ((dmaStartAddr & ~3) != (endaddr & ~3)) { + /* at least one long was written. lower two bits of endaddress are + * number of restbytes. write them left-justified to the long the + * endaddress is in. + */ + rest = endaddr & 3; + to = scca_dma_head->buf + (endaddr & (SCCA_DMA_BUFSIZE-1) & ~3); /* needs no PTOV */ + } else { + /* no long written. number of restbytes is endaddress - + * startaddress. write them to the startaddress. + */ + rest = size; /* must and will be 0..3 */ + from += dmaStartAddr & 3; + to = scca_dma_head->buf + (dmaStartAddr & (SCCA_DMA_BUFSIZE-1)); /* needs no PTOV */ + } + +#if DEBUG & DEBUG_DMA + debugString ("[stop@0x"); + debugHex (endaddr & 0xffff); + debugString ("/"); + debugInt (size); + if (rest) { + debugString (",rest="); + debugInt (rest); + debugString ("@0x"); + debugHex ((unsigned int)from); + debugString ("->"); + debugHex (((unsigned int)to) & 0xffff); + } + debugString ("] "); +#endif + + while (--rest >= 0) + *to++ = *from++; + + scca_dma_head->inbuf += size; +} + + +/* used by the SPCOND INT handler to deliver error codes. it's not very clean, + * maybe overwrites older values, but since that only happens when something + * has already gone wrong I don't consider this a problem. + */ +static inline void dma_fake_receive (u_char data, u_char err) +{ + scca_dma_head->buf[scca_dma_head->inbuf] = data; + scca_dma_head->err[scca_dma_head->inbuf] = err; + + if (scca_dma_head->inbuf < SCCA_DMA_BUFSIZE-1) + scca_dma_head->inbuf++; + + switch (err) { + case TTY_OVERRUN: + scca_dma_head->cntOver++; + break; + case TTY_PARITY: + scca_dma_head->cntPar++; + break; + case TTY_FRAME: + scca_dma_head->cntFrame++; + } +} + + +/*****************************************************************************/ + +/* + * these functions are for high-level DMA support. they make no assumptions + * about current INT status when called. + */ + +/* can't be called more than once due to tqueue handling + clever (?:) variable + * design -> no cli/sti. + */ +static void SCC_flush (struct tty_struct *tty) +{ + int loops = 0; + static int highWater = (95 * SCCA_DMA_BUFSIZE) / 100; + + /* a potential endless loop, but that's *really* unlikely... + */ + while (scca_dma_tail->needsFlushing) { + + /* ...anyway, be save + */ + if (++loops > SCCA_DMA_BUFFERS) { + printk ("SCC-A: flush loop overrun\n"); + break; + } + +#if DEBUG & DEBUG_DMA + if (scca_dma_tail->cntOver || scca_dma_tail->cntPar || scca_dma_tail->cntFrame) { + debugString ("[ovr="); + debugInt (scca_dma_tail->cntOver); + debugString (",par="); + debugInt (scca_dma_tail->cntPar); + debugString (",frm="); + debugInt (scca_dma_tail->cntFrame); + debugString ("] "); + } +#else + if (scca_dma_tail->cntOver || scca_dma_tail->cntPar || scca_dma_tail->cntFrame) + printk ("SCC-A: %d overrun, %d parity, %d frame errors\n", + scca_dma_tail->cntOver, scca_dma_tail->cntPar, scca_dma_tail->cntFrame); +#endif + + if (scca_dma_tail->inbuf > highWater) + printk ("SCC-A: warning: buffer usage: %d/%d chars\n", scca_dma_tail->inbuf, SCCA_DMA_BUFSIZE); + +#if 0 + if (scca_dma_tail->inbuf > 1) { + scca_dma_tail->buf[0] |= 0x1; + scca_dma_tail->buf[scca_dma_tail->inbuf-1] |= 0x2; + } +#endif + + tty->ldisc.receive_buf (tty, scca_dma_tail->buf, scca_dma_tail->err, scca_dma_tail->inbuf); + + scca_dma_tail->cntOver = 0; + scca_dma_tail->cntPar = 0; + scca_dma_tail->cntFrame = 0; + scca_dma_tail->inbuf = 0; + scca_dma_tail->needsFlushing = 0; + + if (++scca_dma_tail == scca_dma_end) + scca_dma_tail = scca_dma_buf; + } + +#if DEBUG & DEBUG_DMA + debugFlush (); +#endif +} + + +static struct tq_struct SCC_flush_tqueue = { + NULL, /* next */ + 0, /* sync */ + (void (*)(void*)) SCC_flush, /* routine, must have (void *) arg... */ + NULL /* data */ +}; + + +/* the 48Hz timer to flush data. runs in fact only every 4th call, say at 12Hz. + */ +static void SCC_timer_int (int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + static int delay = 4; + ulong flags; + SCC_ACCESS_INIT(info); + + /* if 'fp' is NULL we're called from SCC_dma_int, in which case we must + * respond immediately! + */ + if (fp && --delay > 0) + return; + +#if DEBUG & DEBUG_DMA + delay = 100; /* 0.48Hz for better debugging, no more than 19k2b! */ +#else + delay = 4; /* 12Hz delivery frequency (960 bytes / delivery @ 115k2b) */ +#endif + + if (!SCC_flush_tqueue.data) + return; /* no program listening... */ + + save_flags (flags); + cli (); + + if (scca_dma_head->active) { + SCCmod (INT_AND_DMA_REG, ~(IDR_RX_INT_MASK|IDR_WAITREQ_ENAB), 0x00); + dma_stop (); + scca_dma_head->needsFlushing = 1; + scca_dma_head->active = 0; + if (++scca_dma_head == scca_dma_end) + scca_dma_head = scca_dma_buf; + } + + if (!scca_dma_head->needsFlushing) { + scca_dma_head->active = 1; + dma_start (); + SCCmod (INT_AND_DMA_REG, 0xff, IDR_RX_INT_SPCOND|IDR_WAITREQ_ENAB); + /* this must *happen* after re-starting DMA for speed reasons. + */ + memset (scca_dma_head->err, 0x00, SCCA_DMA_BUFSIZE); + } else { + printk ("SCC-A: fatal buffer overflow, data lost!\n"); + } + + queue_task (&SCC_flush_tqueue, &tq_immediate); + mark_bh (IMMEDIATE_BH); + + restore_flags (flags); +} + + +/* DMA finished before timer occured? + */ +static void SCC_dma_int (int irq, void *data, struct pt_regs *fp) +{ + printk ("SCC-A: DMA-INT occured, data lost!\n"); +#if 0 + /* is there any reason why we should call this? if the timer INT was + * delayed so long that this happened then this INT was delayed too, so + * it's already too late. + */ + SCC_timer_int (irq, (struct m68k_async_struct *)data, NULL); +#endif +} + +/*****************************************************************************/ + +#endif + + +#if DEBUG & DEBUG_OVERRUNS +static int SCC_ch_cnt[2] = { 0, 0 }, SCC_ch_ovrrun[2] = { 0, 0 }; +#endif + +static void SCC_rx_int( int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + unsigned char ch; + SCC_ACCESS_INIT(info); + + SETUP_INFO(info); + + ch = SCCread_NB( RX_DATA_REG ); +#if DEBUG & DEBUG_INT + printk( "SCC ch %d rx int: char %02x\n", CHANNR(info), ch ); +#endif + rs_receive_char (info, ch, 0); +#if DEBUG & DEBUG_OVERRUNS + { int channel = CHANNR(info); + if (++SCC_ch_cnt[channel] == 10000) { + printk( "SCC ch. %d: overrun rate %d.%02d\n", channel, + SCC_ch_ovrrun[channel] / 100, + SCC_ch_ovrrun[channel] % 100 ); + SCC_ch_cnt[channel] = SCC_ch_ovrrun[channel] = 0; + } + } +#endif + + /* Check if another character is already ready; in that case, the + * spcond_int() function must be used, because this character may have an + * error condition that isn't signalled by the interrupt vector used! + */ + if (SCCread( INT_PENDING_REG ) & + (CHANNR(info) == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) { + SCC_spcond_int (0, info, 0); + return; + } + +#ifndef ATARI_USE_SOFTWARE_EOI + SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET ); +#endif +} + + +static void SCC_spcond_int( int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + unsigned char stat, ch, err; + int int_pending_mask = CHANNR(info) == CHANNEL_A ? + IPR_A_RX : IPR_B_RX; +#ifdef CONFIG_ATARI_SCC_DMA + int isdma = (CHANNR(info) == CHANNEL_A) && scca_dma; + ulong flags = 0; +#endif + SCC_ACCESS_INIT(info); + + SETUP_INFO(info); + +#ifdef CONFIG_ATARI_SCC_DMA + if (isdma) { + save_flags (flags); + cli (); + SCCmod (INT_AND_DMA_REG, ~(IDR_RX_INT_MASK|IDR_WAITREQ_ENAB), 0); + dma_stop (); + } +#endif + + do { + stat = SCCread( SPCOND_STATUS_REG ); + ch = SCCread_NB(RX_DATA_REG); +#if DEBUG & DEBUG_INT + printk( "SCC ch %d spcond int: char %02x stat %02x\n", + CHANNR(info), ch, stat ); +#endif + + if (stat & SCSR_RX_OVERRUN) + err = TTY_OVERRUN; + else if (stat & SCSR_PARITY_ERR) + err = TTY_PARITY; + else if (stat & SCSR_CRC_FRAME_ERR) + err = TTY_FRAME; + else + err = 0; + +#ifdef CONFIG_ATARI_SCC_DMA + if (isdma) + dma_fake_receive (ch, err); + else +#endif + rs_receive_char (info, ch, err); + + /* ++TeSche: *All* errors have to be cleared manually, + * else the condition persists for the next chars + */ + if (err) + SCCwrite(COMMAND_REG, CR_ERROR_RESET); + +#if DEBUG & DEBUG_OVERRUNS + { int channel = CHANNR(info); + if (err == TTY_OVERRUN) SCC_ch_ovrrun[channel]++; + if (++SCC_ch_cnt[channel] == 10000) { + printk( "SCC ch. %d: overrun rate %d.%02d %%\n", channel, + SCC_ch_ovrrun[channel] / 100, + SCC_ch_ovrrun[channel] % 100 ); + SCC_ch_cnt[channel] = SCC_ch_ovrrun[channel] = 0; + } + } +#endif + + } while( SCCread( INT_PENDING_REG ) & int_pending_mask ); + +#ifdef CONFIG_ATARI_SCC_DMA + if (isdma) { + dma_start (); + SCCmod (INT_AND_DMA_REG, 0xff, IDR_RX_INT_SPCOND|IDR_WAITREQ_ENAB); + restore_flags (flags); + } +#endif + +#ifndef ATARI_USE_SOFTWARE_EOI + SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET ); +#endif +} + + +#ifdef ENABLE_ATARI_SCC +static void SCC_ri_int(int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + /* update input line counter */ + info->icount.rng++; + wake_up_interruptible(&info->delta_msr_wait); +} +#endif + + +static void SCC_tx_int( int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + int ch; + SCC_ACCESS_INIT(info); + + SETUP_INFO(info); + + while( (SCCread_NB( STATUS_REG ) & SR_TX_BUF_EMPTY) && + (ch = rs_get_tx_char( info )) >= 0 ) { + SCCwrite( TX_DATA_REG, ch ); +#if DEBUG & DEBUG_INT + printk( "SCC ch. %d tx int: sent char %02x\n", CHANNR(info), ch ); +#endif + } + + if (rs_no_more_tx( info )) { + /* disable tx interrupts */ + SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); + SCCwrite( COMMAND_REG, CR_TX_PENDING_RESET ); /* disable tx_int on next tx underrun? */ +#if DEBUG & DEBUG_INT + printk ("SCC ch %d tx int: no more chars after %d sent\n", + CHANNR (info), total); +#endif + } + +#ifndef ATARI_USE_SOFTWARE_EOI + SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET ); +#endif +} + + +static void SCC_stat_int( int irq, void *data, struct pt_regs *fp) +{ + struct m68k_async_struct *info = data; + unsigned channel = CHANNR(info); + unsigned char last_sr, sr, changed; + SCC_ACCESS_INIT(info); + + SETUP_INFO(info); + + last_sr = SCC_last_status_reg[channel]; + sr = SCC_last_status_reg[channel] = SCCread_NB( STATUS_REG ); + changed = last_sr ^ sr; +#if DEBUG & DEBUG_INT + printk( "SCC ch %d stat int: sr=%02x last_sr=%02x\n", + CHANNR(info), sr, last_sr ); +#endif + + if (changed & SR_DCD) + rs_dcd_changed( info, sr & SR_DCD ); + + if (changed & SR_CTS) { +#if DEBUG & DEBUG_THROTTLE + printk( "SCC ch. %d: now CTS=%d\n", CHANNR(info), !!(sr & SR_CTS) ); +#endif + rs_check_cts( info, sr & SR_CTS ); + } + + if (changed & SR_SYNC_ABORT) { /* Data Set Ready */ + /* update input line counter */ + info->icount.dsr++; + wake_up_interruptible(&info->delta_msr_wait); + } + + SCCwrite( COMMAND_REG, CR_EXTSTAT_RESET ); +#ifndef ATARI_USE_SOFTWARE_EOI + SCCwrite_NB( COMMAND_REG, CR_HIGHEST_IUS_RESET ); +#endif +} + + +static int SCC_check_open( struct m68k_async_struct *info, struct tty_struct *tty, + struct file *file ) +{ + /* If channel A is opened, check if one of the compounded ports (ttyS3 and + * ttyS4) is already open, else activate the appropriate port hardware. + */ + +#if DEBUG & DEBUG_OPEN + printk( "SCC: about to open channel %d as line %d\n", + CHANNR(info), info->line ); +#endif + + if (CHANNR(info) == CHANNEL_A) { + + if (SCC_chan_a_open) { + if (SCC_chan_a_line != info->line) { +#if DEBUG & DEBUG_OPEN + printk("SCC: channel 0 was already open\n"); +#endif + return -EBUSY; + } + else + return 0; + } + + if ((info->line == cha232_line && + SCC_chan_a_switchable == SCCA_SWITCH_LAN_ONLY) || + (info->line == cha422_line && + SCC_chan_a_switchable == SCCA_SWITCH_SERIAL2_ONLY)) + return( -ENODEV ); + + SCC_chan_a_open = 1; + SCC_chan_a_line = info->line; + SCC_chan_a_info = &rs_table[info->line]; +#ifdef ENABLE_ATARI_SCC + if (SCC_chan_a_switchable == SCCA_SWITCH_BOTH) { + unsigned long flags; + unsigned char tmp; + + save_flags(flags); + cli(); + sound_ym.rd_data_reg_sel = 14; + tmp = sound_ym.rd_data_reg_sel; + sound_ym.wd_data = (info->line == cha232_line + ? tmp | 0x80 + : tmp & 0x7f); +#if DEBUG & DEBUG_OPEN + printk( "SCC: set PSG IO7 to %02x (was %02x)\n", + (info->line & 1) ? (tmp | 0x80) : (tmp & 0x7f), + tmp ); +#endif + restore_flags(flags); + } +#endif + } + return( 0 ); +} + + +static void SCC_init( struct m68k_async_struct *info ) +{ + int i, channel = CHANNR(info); + unsigned long flags; + SCC_ACCESS_INIT(info); +#ifdef ENABLE_ATARI_SCC + static const struct { + unsigned reg, val; + } init_tab[] = { + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x64 }, + /* parity error is special cond, ints disabled, no DMA */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + /* RTxC is XTAL, TRxC is input, both clocks = RTxC */ + { CLK_CTRL_REG, CCR_TRxCOUT_XTAL | CCR_TXCLK_RTxC | CCR_RXCLK_RTxC }, + { DPLL_CTRL_REG, 0 }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: CTS, DCD, SYNC (DSR) */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* Rx int always, TX int off, Ext/Stat int on */ + { INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB | + IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL } + }; +#ifdef CONFIG_ATARI_SCC_DMA + static const struct { + unsigned reg, val; + } init_withdma_tab[] = { + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x64 }, + /* parity error is special cond, ints disabled, DMA receive but disabled */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB | + IDR_WAITREQ_RX | IDR_WAITREQ_IS_REQ}, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + /* RTxC is XTAL, TRxC is input, both clocks = RTxC */ + { CLK_CTRL_REG, CCR_TRxCOUT_XTAL | CCR_TXCLK_RTxC | CCR_RXCLK_RTxC }, + { DPLL_CTRL_REG, 0 }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: CTS, DCD, SYNC (DSR) */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* parity error is special cond, Tx & SPcond ints enabled, Rx int disabled, DMA receive but disabled */ + { INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB | IDR_PARERR_AS_SPCOND | + IDR_RX_INT_DISAB | IDR_WAITREQ_RX | IDR_WAITREQ_IS_REQ} + }; +#endif +#endif +#ifdef CONFIG_MVME147_SCC + static const struct { + unsigned reg, val; + } m147_init_tab[] = { + /* Values for MVME147 */ + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, + /* parity error is special cond, ints disabled, no DMA */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + { CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG }, + { DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: CTS, DCD, SYNC (DSR) */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* Rx int always, TX int off, Ext/Stat int on */ + { INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB | + IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL } + }; +#endif +#ifdef CONFIG_MVME162_SCC + static const struct { + unsigned reg, val; + } mvme_init_tab[] = { + /* Values for MVME162 */ + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, + /* parity error is special cond, ints disabled, no DMA */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + { CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG }, + { DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: CTS, DCD, SYNC (DSR) */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* Rx int always, TX int off, Ext/Stat int on */ + { INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB | + IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL } + }; +#endif +#ifdef CONFIG_BVME6000_SCC + static const struct { + unsigned reg, val; + } bvme_init_tab[] = { + /* Values for BVME6000 */ + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, + /* parity error is special cond, ints disabled, no DMA */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + { CLK_CTRL_REG, CCR_RTxC_XTAL | CCR_RXCLK_BRG | CCR_TXCLK_BRG }, + { DPLL_CTRL_REG, DCR_BRG_ENAB }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: CTS, DCD, SYNC (DSR) */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* Rx int always, TX int off, Ext/Stat int on */ + { INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB | + IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL } + }; +#endif + save_flags(flags); + cli(); + + if (!MACH_IS_MVME16x && !MACH_IS_BVME6000 && !MACH_IS_MVME147) { + SCCmod( MASTER_INT_CTRL, 0x3f, + channel == 0 ? MIC_CH_A_RESET : MIC_CH_B_RESET ); + udelay(40); /* extra delay after a reset */ + } + +#ifdef ENABLE_ATARI_SCC + if (MACH_IS_ATARI) { +#ifdef CONFIG_ATARI_SCC_DMA + if (channel == CHANNEL_A && scca_dma) { + + for (i=0; iactive = 1; + dma_start (); + SCCmod (INT_AND_DMA_REG, 0xff, IDR_RX_INT_SPCOND|IDR_WAITREQ_ENAB); + + SCC_flush_tqueue.data = ((struct m68k_async_struct *)info)->tty; + + } else +#endif + { + for (i=0; i 65535) + return( -1 ); + + switch( clksrc ) { + + case CLK_PCLK: + /* The master clock can only be used with the BRG, divisors + * range from 4 and must be a multiple of 2 + */ + return( !(divisor >= 4 && (divisor & 1) == 0) ); + + case CLK_RTxC: + /* The RTxC clock can either be used for the direct 1:16, 1:32 + * or 1:64 modes (divisors 1, 2 or 4, resp.) or with the BRG + * (divisors from 4 and a multiple of 2) + */ + return( !(divisor >= 1 && (divisor == 1 || (divisor & 1) == 0)) ); + + case CLK_TRxC: + /* The TRxC clock can only be used for direct 1:16, 1:32 or + * 1:64 modes + */ + return( !(divisor == 1 || divisor == 2 || divisor == 4) ); + + } + return( -1 ); +} + + +static void SCC_change_speed( struct m68k_async_struct *info ) +{ + /* the SCC has char sizes 5,7,6,8 in that order! */ + static int chsize_map[4] = { 0, 2, 1, 3 }; + unsigned cflag, baud, chsize, aflags; + unsigned channel, div = 0, clkmode, brgmode, brgval; + int clksrc = 0; + unsigned long flags; + SCC_ACCESS_INIT(info); + + if (!info->tty || !info->tty->termios) return; + + channel = CHANNR(info); + + if (MACH_IS_MVME147 && channel == CHANNEL_A) + return; /* Settings controlled by 147Bug */ + if (MACH_IS_MVME16x && channel == CHANNEL_A) + return; /* Settings controlled by 162Bug */ + if (MACH_IS_BVME6000 && channel == CHANNEL_A) + return; /* Settings controlled by BVMBug */ + + cflag = info->tty->termios->c_cflag; + baud = cflag & CBAUD; + chsize = (cflag & CSIZE) >> 4; + aflags = info->flags & ASYNC_SPD_MASK; + + if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; + else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + +#if DEBUG & DEBUG_SPEED + printk( "SCC channel %d: doing new settings:\n", CHANNR(info) ); + printk( " baud=%d chsize=%d aflags=%04x base_baud=%d divisor=%d\n", + baud, chsize, aflags, info->baud_base, info->custom_divisor ); +#endif + + if (baud == 0 && !aflags) { + /* speed == 0 -> drop DTR */ + save_flags(flags); + cli(); + SCCmod( TX_CTRL_REG, ~TCR_DTR, 0 ); + restore_flags(flags); + return; + } + + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + if (baud < 1 || baud > (MACH_IS_MVME16x ? 2 : 4)) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; + } + if (baud == 15 && aflags) { + switch( aflags) { + case ASYNC_SPD_HI: + baud = 16; + break; + case ASYNC_SPD_VHI: + baud = 17; + break; + case ASYNC_SPD_SHI: + baud = 18; + break; + case ASYNC_SPD_WARP: + baud = 19; + break; + case ASYNC_SPD_CUST: + /* Custom divisor: Compute clock source from the base_baud + * field */ + if ((clksrc = SCC_clocksrc( info->baud_base, channel )) < 0) + /* This shouldn't happen... the baud_base has been checked + * before by check_custom_divisor() */ + return; + div = info->custom_divisor; + } + } + + if (!div) { + if (baud > 19) baud = 19; + clksrc = SCC_baud_table[channel][baud].clksrc; + div = SCC_baud_table[channel][baud].div; + if(!div) + { + printk(" SCC_change_speed: divisor = 0 !!!"); + return; + } + } + + /* compute the SCC's clock source, clock mode, BRG mode and BRG + * value from clksrc and div + */ + if (div <= 4) { + clkmode = (div == 1 ? A1CR_CLKMODE_x16 : + div == 2 ? A1CR_CLKMODE_x32 : + A1CR_CLKMODE_x64); + clksrc = (clksrc == CLK_RTxC + ? CCR_TXCLK_RTxC | CCR_RXCLK_RTxC + : CCR_TXCLK_TRxC | CCR_RXCLK_TRxC); + brgmode = 0; /* off */ + brgval = 0; + } + else { + brgval = div/2 - 2; + brgmode = (DCR_BRG_ENAB | + (clksrc == CLK_PCLK ? DCR_BRG_USE_PCLK : 0)); + clkmode = A1CR_CLKMODE_x16; + clksrc = CCR_TXCLK_BRG | CCR_RXCLK_BRG; + } + + /* Now we have all parameters and can go to set them: */ + save_flags(flags); + cli(); +#if DEBUG & DEBUG_SPEED + printk( " brgval=%d brgmode=%02x clkmode=%02x clksrc=%02x\n", + brgval, brgmode, clkmode, clksrc ); +#endif + + /* receiver's character size */ + SCCmod( RX_CTRL_REG, ~RCR_CHSIZE_MASK, chsize_map[chsize] << 6 ); +#if DEBUG & DEBUG_SPEED + printk( " RX_CTRL_REG <- %02x\n", SCCread( RX_CTRL_REG ) ); +#endif + + /* parity and stop bits (both, Tx and Rx) and clock mode */ + SCCmod (AUX1_CTRL_REG, + ~(A1CR_PARITY_MASK | A1CR_MODE_MASK | A1CR_CLKMODE_MASK), + ((cflag & PARENB + ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN) + : A1CR_PARITY_NONE) + | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1) + | clkmode)); +#if DEBUG & DEBUG_SPEED + printk( " AUX1_CTRL_REG <- %02x\n", SCCread( AUX1_CTRL_REG ) ); +#endif + + /* sender's character size */ + /* Set DTR for valid baud rates! Tnx to jds@kom.auc.dk */ + SCCmod( TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize_map[chsize] << 5 | TCR_DTR ); +#if DEBUG & DEBUG_SPEED + printk( " TX_CTRL_REG <- %02x\n", SCCread( TX_CTRL_REG ) ); +#endif + + /* clock sources */ + SCCmod( CLK_CTRL_REG, ~(CCR_TXCLK_MASK | CCR_RXCLK_MASK), clksrc ); +#if DEBUG & DEBUG_SPEED + printk( " CLK_CTRL_REG <- %02x\n", SCCread( CLK_CTRL_REG ) ); +#endif + + /* disable BRG before changing the value */ + SCCmod( DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0 ); + + /* BRG value */ + SCCwrite( TIMER_LOW_REG, brgval & 0xff ); + SCCwrite( TIMER_HIGH_REG, (brgval >> 8) & 0xff ); + + /* BRG enable and clock source */ + SCCmod( DPLL_CTRL_REG, ~(DCR_BRG_ENAB | DCR_BRG_USE_PCLK), brgmode ); +#if DEBUG & DEBUG_SPEED + printk( " TIMER_LOW_REG <- %02x\n", SCCread( TIMER_LOW_REG ) ); + printk( " TIMER_HIGH_REG <- %02x\n", SCCread( TIMER_HIGH_REG ) ); +#endif +#if DEBUG & DEBUG_SPEED + printk( " DPLL_CTRL_REG <- %02x\n", SCCread( DPLL_CTRL_REG ) ); +#endif + + restore_flags(flags); +} + + +static int SCC_clocksrc( unsigned baud_base, unsigned channel ) +{ + if (baud_base == SCC_PCLK) + return( CLK_PCLK ); + else if (SCC_clocks[channel][CLK_RTxC] != SCC_BAUD_BASE_NONE && + baud_base == SCC_clocks[channel][CLK_RTxC]) + return( CLK_RTxC ); + else if (SCC_clocks[channel][CLK_TRxC] != SCC_BAUD_BASE_NONE && + baud_base == SCC_clocks[channel][CLK_TRxC]) + return( CLK_TRxC ); + else + return( -1 ); +} + +static void SCC_throttle( struct m68k_async_struct *info, int status ) +{ + unsigned long flags; + SCC_ACCESS_INIT(info); + +#if DEBUG & DEBUG_THROTTLE + printk( "SCC channel %d: throttle %s\n", + CHANNR(info), status ? "full" : "avail" ); +#endif + save_flags(flags); + cli(); + + if (status) + SCCmod( TX_CTRL_REG, ~TCR_RTS, 0 ); + else + SCCmod( TX_CTRL_REG, 0xff, TCR_RTS ); + +#if DEBUG & DEBUG_THROTTLE + printk( " now TX_CTRL_REG = %02x\n", SCCread( TX_CTRL_REG ) ); +#endif + + restore_flags(flags); +} + + +static void SCC_set_break( struct m68k_async_struct *info, int break_flag ) +{ + unsigned long flags; + SCC_ACCESS_INIT(info); + + save_flags(flags); + cli(); + + if (break_flag) { + SCCmod( TX_CTRL_REG, 0xff, TCR_SEND_BREAK ); + } else { + SCCmod( TX_CTRL_REG, ~TCR_SEND_BREAK, 0 ); + } + + restore_flags(flags); +} + + +static void SCC_get_serial_info( struct m68k_async_struct *info, + struct serial_struct *retinfo ) +{ + retinfo->baud_base = info->baud_base; + retinfo->custom_divisor = info->custom_divisor; +} + + +static unsigned int SCC_get_modem_info( struct m68k_async_struct *info ) +{ + unsigned sr, tcr, ri = 0, dsr = 0; + unsigned long flags; + SCC_ACCESS_INIT(info); + + save_flags(flags); + cli(); + sr = SCCread( STATUS_REG ); + tcr = SCCread( TX_CTRL_REG ); + restore_flags(flags); +#if DEBUG & DEBUG_INFO + printk( "SCC channel %d: get info, sr=%02x tcr=%02x\n", + CHANNR(info), sr, tcr ); +#endif +#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC) + if (MACH_IS_MVME147 || MACH_IS_MVME16x || MACH_IS_BVME6000) { + ri = 0; + dsr = sr & SR_SYNC_ABORT ? TIOCM_DSR : 0; + } +#endif +#ifdef ENABLE_ATARI_SCC + if (MACH_IS_ATARI) { + if (CHANNR (info) == 0) + ri = 0; + else if (ATARIHW_PRESENT (TT_MFP)) + ri = tt_mfp.par_dt_reg & (1 << 3) ? 0 : TIOCM_RNG; + else + ri = mfp.par_dt_reg & (1 << 6) ? 0 : TIOCM_RNG; + + if (ATARIHW_PRESENT (ST_ESCC)) + dsr = st_escc_dsr & (1 << (3 - CHANNR(info))) ? TIOCM_DSR : 0; + else + dsr = sr & SR_SYNC_ABORT ? TIOCM_DSR : 0; + } +#endif + return (((tcr & TCR_RTS) ? TIOCM_RTS : 0) | + ((tcr & TCR_DTR) ? TIOCM_DTR : 0) | + ((sr & SR_DCD ) ? TIOCM_CAR : 0) | + ((sr & SR_CTS ) ? TIOCM_CTS : 0) | + dsr | ri); +} + + +static int SCC_set_modem_info( struct m68k_async_struct *info, + int new_dtr, int new_rts ) +{ + unsigned long flags; + SCC_ACCESS_INIT(info); + + save_flags(flags); + cli(); + + if (new_dtr == 0) { + SCCmod( TX_CTRL_REG, ~TCR_DTR, 0 ); + } else if (new_dtr == 1) { + SCCmod( TX_CTRL_REG, 0xff, TCR_DTR ); + } + + if (new_rts == 0) { + SCCmod( TX_CTRL_REG, ~TCR_RTS, 0 ); + } else if (new_rts == 1) { + SCCmod( TX_CTRL_REG, 0xff, TCR_RTS ); + } + +#if DEBUG & DEBUG_INFO + printk( "SCC channel %d: set info (dtr=%d,rts=%d), now tcr=%02x\n", + CHANNR(info), new_dtr, new_rts, SCCread( TX_CTRL_REG ) ); +#endif + + restore_flags(flags); + return( 0 ); +} + +static void SCC_stop_receive (struct m68k_async_struct *info) +{ + SCC_ACCESS_INIT(info); + +#ifdef CONFIG_ATARI_SCC_DMA + dma_stop (); +#endif + + /* disable Rx interrupts */ + SCCmod (INT_AND_DMA_REG, ~IDR_RX_INT_MASK, 0); + + /* disable Rx */ + if (!((MACH_IS_MVME16x || MACH_IS_BVME6000) && CHANNR(info) == CHANNEL_A)) + SCCmod (RX_CTRL_REG, ~RCR_RX_ENAB, 0); +} + +static int SCC_trans_empty (struct m68k_async_struct *info) +{ + SCC_ACCESS_INIT(info); + + return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) != 0; +} + +static int SCC_ioctl( struct tty_struct *tty, struct file *file, + struct m68k_async_struct *info, unsigned int cmd, + unsigned long arg ) +{ + struct atari_SCCserial *cp = (void *)arg; + int error; + unsigned channel = CHANNR(info), i, clk, div, rtxc, trxc, pclk; + + switch( cmd ) { + + case TIOCGATSCC: + + error = verify_area( VERIFY_WRITE, (void *)arg, + sizeof(struct atari_SCCserial) ); + if (error) + return error; + + put_user(SCC_clocks[channel][CLK_RTxC], &cp->RTxC_base); + put_user(SCC_clocks[channel][CLK_TRxC], &cp->TRxC_base); + put_user(SCC_PCLK, &cp->PCLK_base); + copy_to_user(cp->baud_table, SCC_baud_table[channel] + 1, + sizeof(cp->baud_table)); + + return( 0 ); + + case TIOCSATSCC: + + if (!suser()) return( -EPERM ); + + error = verify_area(VERIFY_READ, (void *)arg, + sizeof(struct atari_SCCserial) ); + if (error) + return error; + + get_user(rtxc, &cp->RTxC_base); + get_user(trxc, &cp->TRxC_base); + get_user(pclk, &cp->PCLK_base); + + if (pclk == SCC_BAUD_BASE_NONE) + /* This is really not possible :-) */ + return( -EINVAL ); + + /* Check the baud table for consistency */ + for( i = 0; i < sizeof(cp->baud_table)/sizeof(cp->baud_table[0]); ++i ) { + + get_user(clk, &cp->baud_table[i].clksrc); + get_user(div, &cp->baud_table[i].divisor); + + switch( clk ) { + case CLK_RTxC: + if (rtxc == SCC_BAUD_BASE_NONE) + return( -EINVAL ); + if (((div & 1) && div != 1) || + (div >= 4 && div/2-2 > 65535)) + return( -EINVAL ); + break; + case CLK_TRxC: + if (trxc == SCC_BAUD_BASE_NONE) + return( -EINVAL ); + if (div != 1 && div != 2 && div != 4) + return( -EINVAL ); + break; + case CLK_PCLK: + if (div < 4 || (div & 1) || div/2-2 > 65535) + return( -EINVAL ); + break; + default: + /* invalid valid clock source */ + return( -EINVAL ); + } + } + + /* After all the checks, set the values */ + + SCC_clocks[channel][CLK_RTxC] = rtxc; + SCC_clocks[channel][CLK_TRxC] = trxc; + SCC_PCLK = pclk; + + copy_from_user(bdtab_usr[channel] + 1, cp->baud_table, + sizeof(cp->baud_table)); + /* Now use the user supplied baud table */ + SCC_baud_table[channel] = bdtab_usr[channel]; + + return( 0 ); + + case TIOCDATSCC: + + if (!suser()) return( -EPERM ); +#ifdef ENABLE_ATARI_SCC + if (!MACH_IS_ATARI) + return 0; /* XXX */ + + if (ATARIHW_PRESENT(TT_MFP)) { + SCC_clocks[channel][CLK_RTxC] = + (channel == CHANNEL_A) ? + SCC_BAUD_BASE_PCLK4 : + SCC_BAUD_BASE_TIMC; + SCC_clocks[channel][CLK_TRxC] = + (channel == CHANNEL_A) ? + SCC_BAUD_BASE_NONE : + SCC_BAUD_BASE_BCLK; + } + else { + SCC_clocks[channel][CLK_RTxC] = SCC_BAUD_BASE_PCLK4; + SCC_clocks[channel][CLK_TRxC] = + (channel == CHANNEL_A) ? + SCC_BAUD_BASE_NONE : + SCC_BAUD_BASE_BCLK; + } + + SCC_PCLK = SCC_BAUD_BASE_PCLK; + SCC_baud_table[channel] = + ((ATARIHW_PRESENT(TT_MFP) && channel == 1) ? + bdtab_TTChB : bdtab_norm); +#endif + return( 0 ); + + } + return( -ENOIOCTLCMD ); +} + + + + +#ifdef MODULE +int init_module(void) +{ +#ifdef ENABLE_ATARI_SCC + if (MACH_IS_ATARI) + return atari_SCC_init(); +#endif + return -ENODEV; +} + +void cleanup_module(void) +{ + if (chb_line >= 0) { + SCC_deinit_port( &rs_table[chb_line], CHANNEL_B ); + unregister_serial( chb_line ); + } + + /* ++Juergen Starek: use proper structure to deinitialize port + * because atari_free_irq relies on the valid + * `dev_id` parameter! + * If we use only the cha232_line, unloading a + * module causes a damaged irq list! + */ + /* We must deinit channel A only once! ++Andreas. */ + if (cha232_line >= 0) + SCC_deinit_port(&rs_table[cha232_line], CHANNEL_A); + else if (cha422_line >= 0) + SCC_deinit_port(&rs_table[cha422_line], CHANNEL_A); + + if (cha232_line >= 0) + unregister_serial( cha232_line ); + if (cha422_line >= 0) + unregister_serial( cha422_line ); +} +#endif + +/* + * Local variables: + * c-indent-level: 4 + * tab-width: 4 + * End: + */ diff --git a/drivers/char/atari_SCC.h b/drivers/char/atari_SCC.h new file mode 100644 index 000000000000..892cd127d5c8 --- /dev/null +++ b/drivers/char/atari_SCC.h @@ -0,0 +1,616 @@ +/* + * atari_SCC.h: Definitions for the Am8530 Serial Communications Controller + * + * Copyright 1994 Roman Hodek + * + * 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. + * + */ + + +#ifndef _ATARI_SCC_H +#define _ATARI_SCC_H + +#include +#include + +#ifdef CONFIG_MVME147_SCC +#include +#endif +#ifdef CONFIG_MVME162_SCC +#include +#endif +#ifdef CONFIG_BVME6000_SCC +#include +#endif +#ifdef CONFIG_ATARI +#include +#endif + +/***********************************************************************/ +/* */ +/* Register Names */ +/* */ +/***********************************************************************/ + +/* The SCC documentation gives no explicit names to the registers, + * they're just called WR0..15 and RR0..15. To make the source code + * better readable and make the transparent write reg read access (see + * below) possible, I christen them here with self-invented names. + * Note that (real) read registers are assigned numbers 16..31. WR7' + * has number 33. + */ + +#define COMMAND_REG 0 /* wo */ +#define INT_AND_DMA_REG 1 /* wo */ +#define INT_VECTOR_REG 2 /* rw, common to both channels */ +#define RX_CTRL_REG 3 /* rw */ +#define AUX1_CTRL_REG 4 /* rw */ +#define TX_CTRL_REG 5 /* rw */ +#define SYNC_ADR_REG 6 /* wo */ +#define SYNC_CHAR_REG 7 /* wo */ +#define SDLC_OPTION_REG 33 /* wo */ +#define TX_DATA_REG 8 /* wo */ +#define MASTER_INT_CTRL 9 /* wo, common to both channels */ +#define AUX2_CTRL_REG 10 /* rw */ +#define CLK_CTRL_REG 11 /* wo */ +#define TIMER_LOW_REG 12 /* rw */ +#define TIMER_HIGH_REG 13 /* rw */ +#define DPLL_CTRL_REG 14 /* wo */ +#define INT_CTRL_REG 15 /* rw */ + +#define STATUS_REG 16 /* ro */ +#define SPCOND_STATUS_REG 17 /* wo */ +/* RR2 is WR2 for Channel A, Channel B gives vector + current status: */ +#define CURR_VECTOR_REG 18 /* Ch. B only, Ch. A for rw */ +#define INT_PENDING_REG 19 /* Channel A only! */ +/* RR4 is WR4, if b6(MR7') == 1 */ +/* RR5 is WR5, if b6(MR7') == 1 */ +#define FS_FIFO_LOW_REG 22 /* ro */ +#define FS_FIFO_HIGH_REG 23 /* ro */ +#define RX_DATA_REG 24 /* ro */ +/* RR9 is WR3, if b6(MR7') == 1 */ +#define DPLL_STATUS_REG 26 /* ro */ +/* RR11 is WR10, if b6(MR7') == 1 */ +/* RR12 is WR12 */ +/* RR13 is WR13 */ +/* RR14 not present */ +/* RR15 is WR15 */ + + +/***********************************************************************/ +/* */ +/* Register Values */ +/* */ +/***********************************************************************/ + + +/* WR0: COMMAND_REG "CR" */ + +#define CR_RX_CRC_RESET 0x40 +#define CR_TX_CRC_RESET 0x80 +#define CR_TX_UNDERRUN_RESET 0xc0 + +#define CR_EXTSTAT_RESET 0x10 +#define CR_SEND_ABORT 0x18 +#define CR_ENAB_INT_NEXT_RX 0x20 +#define CR_TX_PENDING_RESET 0x28 +#define CR_ERROR_RESET 0x30 +#define CR_HIGHEST_IUS_RESET 0x38 + + +/* WR1: INT_AND_DMA_REG "IDR" */ + +#define IDR_EXTSTAT_INT_ENAB 0x01 +#define IDR_TX_INT_ENAB 0x02 +#define IDR_PARERR_AS_SPCOND 0x04 + +#define IDR_RX_INT_DISAB 0x00 +#define IDR_RX_INT_FIRST 0x08 +#define IDR_RX_INT_ALL 0x10 +#define IDR_RX_INT_SPCOND 0x18 +#define IDR_RX_INT_MASK 0x18 + +#define IDR_WAITREQ_RX 0x20 +#define IDR_WAITREQ_IS_REQ 0x40 +#define IDR_WAITREQ_ENAB 0x80 + + +/* WR3: RX_CTRL_REG "RCR" */ + +#define RCR_RX_ENAB 0x01 +#define RCR_DISCARD_SYNC_CHARS 0x02 +#define RCR_ADDR_SEARCH 0x04 +#define RCR_CRC_ENAB 0x08 +#define RCR_SEARCH_MODE 0x10 +#define RCR_AUTO_ENAB_MODE 0x20 + +#define RCR_CHSIZE_MASK 0xc0 +#define RCR_CHSIZE_5 0x00 +#define RCR_CHSIZE_6 0x40 +#define RCR_CHSIZE_7 0x80 +#define RCR_CHSIZE_8 0xc0 + + +/* WR4: AUX1_CTRL_REG "A1CR" */ + +#define A1CR_PARITY_MASK 0x03 +#define A1CR_PARITY_NONE 0x00 +#define A1CR_PARITY_ODD 0x01 +#define A1CR_PARITY_EVEN 0x03 + +#define A1CR_MODE_MASK 0x0c +#define A1CR_MODE_SYNCR 0x00 +#define A1CR_MODE_ASYNC_1 0x04 +#define A1CR_MODE_ASYNC_15 0x08 +#define A1CR_MODE_ASYNC_2 0x0c + +#define A1CR_SYNCR_MODE_MASK 0x30 +#define A1CR_SYNCR_MONOSYNC 0x00 +#define A1CR_SYNCR_BISYNC 0x10 +#define A1CR_SYNCR_SDLC 0x20 +#define A1CR_SYNCR_EXTCSYNC 0x30 + +#define A1CR_CLKMODE_MASK 0xc0 +#define A1CR_CLKMODE_x1 0x00 +#define A1CR_CLKMODE_x16 0x40 +#define A1CR_CLKMODE_x32 0x80 +#define A1CR_CLKMODE_x64 0xc0 + + +/* WR5: TX_CTRL_REG "TCR" */ + +#define TCR_TX_CRC_ENAB 0x01 +#define TCR_RTS 0x02 +#define TCR_USE_CRC_CCITT 0x00 +#define TCR_USE_CRC_16 0x04 +#define TCR_TX_ENAB 0x08 +#define TCR_SEND_BREAK 0x10 + +#define TCR_CHSIZE_MASK 0x60 +#define TCR_CHSIZE_5 0x00 +#define TCR_CHSIZE_6 0x20 +#define TCR_CHSIZE_7 0x40 +#define TCR_CHSIZE_8 0x60 + +#define TCR_DTR 0x80 + + +/* WR7': SLDC_OPTION_REG "SOR" */ + +#define SOR_AUTO_TX_ENAB 0x01 +#define SOR_AUTO_EOM_RESET 0x02 +#define SOR_AUTO_RTS_MODE 0x04 +#define SOR_NRZI_DISAB_HIGH 0x08 +#define SOR_ALT_DTRREQ_TIMING 0x10 +#define SOR_READ_CRC_CHARS 0x20 +#define SOR_EXTENDED_REG_ACCESS 0x40 + + +/* WR9: MASTER_INT_CTRL "MIC" */ + +#define MIC_VEC_INCL_STAT 0x01 +#define MIC_NO_VECTOR 0x02 +#define MIC_DISAB_LOWER_CHAIN 0x04 +#define MIC_MASTER_INT_ENAB 0x08 +#define MIC_STATUS_HIGH 0x10 +#define MIC_IGN_INTACK 0x20 + +#define MIC_NO_RESET 0x00 +#define MIC_CH_A_RESET 0x40 +#define MIC_CH_B_RESET 0x80 +#define MIC_HARD_RESET 0xc0 + + +/* WR10: AUX2_CTRL_REG "A2CR" */ + +#define A2CR_SYNC_6 0x01 +#define A2CR_LOOP_MODE 0x02 +#define A2CR_ABORT_ON_UNDERRUN 0x04 +#define A2CR_MARK_IDLE 0x08 +#define A2CR_GO_ACTIVE_ON_POLL 0x10 + +#define A2CR_CODING_MASK 0x60 +#define A2CR_CODING_NRZ 0x00 +#define A2CR_CODING_NRZI 0x20 +#define A2CR_CODING_FM1 0x40 +#define A2CR_CODING_FM0 0x60 + +#define A2CR_PRESET_CRC_1 0x80 + + +/* WR11: CLK_CTRL_REG "CCR" */ + +#define CCR_TRxCOUT_MASK 0x03 +#define CCR_TRxCOUT_XTAL 0x00 +#define CCR_TRxCOUT_TXCLK 0x01 +#define CCR_TRxCOUT_BRG 0x02 +#define CCR_TRxCOUT_DPLL 0x03 + +#define CCR_TRxC_OUTPUT 0x04 + +#define CCR_TXCLK_MASK 0x18 +#define CCR_TXCLK_RTxC 0x00 +#define CCR_TXCLK_TRxC 0x08 +#define CCR_TXCLK_BRG 0x10 +#define CCR_TXCLK_DPLL 0x18 + +#define CCR_RXCLK_MASK 0x60 +#define CCR_RXCLK_RTxC 0x00 +#define CCR_RXCLK_TRxC 0x20 +#define CCR_RXCLK_BRG 0x40 +#define CCR_RXCLK_DPLL 0x60 + +#define CCR_RTxC_XTAL 0x80 + + +/* WR14: DPLL_CTRL_REG "DCR" */ + +#define DCR_BRG_ENAB 0x01 +#define DCR_BRG_USE_PCLK 0x02 +#define DCR_DTRREQ_IS_REQ 0x04 +#define DCR_AUTO_ECHO 0x08 +#define DCR_LOCAL_LOOPBACK 0x10 + +#define DCR_DPLL_EDGE_SEARCH 0x20 +#define DCR_DPLL_ERR_RESET 0x40 +#define DCR_DPLL_DISAB 0x60 +#define DCR_DPLL_CLK_BRG 0x80 +#define DCR_DPLL_CLK_RTxC 0xa0 +#define DCR_DPLL_FM 0xc0 +#define DCR_DPLL_NRZI 0xe0 + + +/* WR15: INT_CTRL_REG "ICR" */ + +#define ICR_OPTIONREG_SELECT 0x01 +#define ICR_ENAB_BRG_ZERO_INT 0x02 +#define ICR_USE_FS_FIFO 0x04 +#define ICR_ENAB_DCD_INT 0x08 +#define ICR_ENAB_SYNC_INT 0x10 +#define ICR_ENAB_CTS_INT 0x20 +#define ICR_ENAB_UNDERRUN_INT 0x40 +#define ICR_ENAB_BREAK_INT 0x80 + + +/* RR0: STATUS_REG "SR" */ + +#define SR_CHAR_AVAIL 0x01 +#define SR_BRG_ZERO 0x02 +#define SR_TX_BUF_EMPTY 0x04 +#define SR_DCD 0x08 +#define SR_SYNC_ABORT 0x10 +#define SR_CTS 0x20 +#define SR_TX_UNDERRUN 0x40 +#define SR_BREAK 0x80 + + +/* RR1: SPCOND_STATUS_REG "SCSR" */ + +#define SCSR_ALL_SENT 0x01 +#define SCSR_RESIDUAL_MASK 0x0e +#define SCSR_PARITY_ERR 0x10 +#define SCSR_RX_OVERRUN 0x20 +#define SCSR_CRC_FRAME_ERR 0x40 +#define SCSR_END_OF_FRAME 0x80 + + +/* RR3: INT_PENDING_REG "IPR" */ + +#define IPR_B_EXTSTAT 0x01 +#define IPR_B_TX 0x02 +#define IPR_B_RX 0x04 +#define IPR_A_EXTSTAT 0x08 +#define IPR_A_TX 0x10 +#define IPR_A_RX 0x20 + + +/* RR7: FS_FIFO_HIGH_REG "FFHR" */ + +#define FFHR_CNT_MASK 0x3f +#define FFHR_IS_FROM_FIFO 0x40 +#define FFHR_FIFO_OVERRUN 0x80 + + +/* RR10: DPLL_STATUS_REG "DSR" */ + +#define DSR_ON_LOOP 0x02 +#define DSR_ON_LOOP_SENDING 0x10 +#define DSR_TWO_CLK_MISSING 0x40 +#define DSR_ONE_CLK_MISSING 0x80 + + + +/***************************** Prototypes *****************************/ + +int atari_SCC_init( void ); + +/************************* End of Prototypes **************************/ + +/* Compute the channel number from the base address */ + +#define CHANNR(info) (((info)->port & (MACH_IS_BVME6000 ? 8 : (MACH_IS_MVME147 ? 2 : 4))) != ChannelsReversed) + + +/***********************************************************************/ +/* */ +/* Constants */ +/* */ +/***********************************************************************/ + + +/***********************************************************************/ +/* */ +/* Register Access */ +/* */ +/***********************************************************************/ + + +/* The SCC needs 3.5 PCLK cycles recovery time between to register + * accesses. PCLK runs with 8 MHz on an Atari, so this delay is 3.5 * + * 125 ns = 437.5 ns. Since this is too short for udelay(), it is + * implemented by some nop's. I think that a nop needs 4 cycles (but + * I'm not sure, correct me please!), that gives 4 nops for a TT (32 + * MHz) (2 would be sufficient for the Falcon (16 MHz), but looking at + * boot_info.bi_atari.model at runtime takes longer than 2 nop's...) + * ++andreas: nop needs only 2 cycles, seven of them are needed. + */ + +/* 10/16/95: A tstb mfp.par_dt_reg takes 600ns (sure?) and thus should be + * quite right + */ + +#define scc_reg_delay() \ + do { \ + if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) \ + udelay(1); \ + else \ + __asm__ __volatile__ ( "tstb %0" : : "g" (*_scc_del) : "cc" );\ + } while (0) + +/* Another version with only 3 nop's for cases when some other + * statement intervenes between the two SCC accesses + * ++andreas: 3 nop's added + * 10/16/95: use MFPDELAY, too. + */ + +#define scc_reg3_delay() \ + if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) \ + udelay(1); \ + else \ + __asm__ __volatile__ ( "tstb %0" : : "g" (*_scc_del) : "cc" ); + + +struct PARTIAL_SCC { /* just one channel */ + unsigned char ctrl; + unsigned char dummy; + unsigned char data; +}; + + +extern unsigned char SCC_shadow[2][16]; +extern int ChannelsReversed; + + +/* The following functions should relax the somehow complicated + * register access of the SCC. _SCCwrite() stores all written values + * (except for WR0 and WR8) in shadow registers for later recall. This + * removes the burden of remembering written values as needed. The + * extra work of storing the value doesn't count, since a delay is + * needed after a SCC access anyway. Additionally, _SCCwrite() manages + * writes to WR0 and WR8 differently, because these can be accessed + * directly with less overhead. Another special case are WR7 and WR7'. + * _SCCwrite automatically checks what of this registers is selected + * and changes b0 of WR15 if needed. + * + * _SCCread() for standard read registers is straightforward, except + * for RR2 (split into two "virtual" registers: one for the value + * written to WR2 (from the shadow) and one for the vector including + * status from RR2, Ch. B) and RR3. The latter must be read from + * Channel A, because it reads as all zeros on Ch. B. RR0 and RR8 can + * be accessed directly as before. + * + * The two inline function contain complicated switch statements. But + * I rely on regno and final_delay being constants, so gcc can reduce + * the whole stuff to just some assembler statements. + * + * _SCCwrite and _SCCread aren't intended to be used directly under + * normal circumstances. The macros SCCread[_ND] and SCCwrite[_ND] are + * for that purpose. They assume that a local variable 'info' is + * declared and pointing to the port's m68k_async_struct entry. The + * variants with "_NB" appended should be used if no other SCC + * accesses follow immediatly (within 0.5 usecs). They just skip the + * final delay nops. + * + * Please note that accesses to SCC registers should only take place + * when interrupts are turned off (at least if SCC interrupts are + * enabled). Otherwise, an interrupt could interfere with the + * two-stage accessing process. + * + */ + + +static __inline__ void _SCCwrite( + volatile struct PARTIAL_SCC *sc, + unsigned char *shadow, + volatile unsigned char *_scc_del, + int regno, + unsigned char val, int final_delay ) +{ + switch( regno ) { + + case COMMAND_REG: + /* WR0 can be written directly without pointing */ + sc->ctrl = val; + break; + + case SYNC_CHAR_REG: + /* For WR7, first set b0 of WR15 to 0, if needed */ + if (shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT) { + sc->ctrl = 15; + shadow[INT_CTRL_REG] &= ~ICR_OPTIONREG_SELECT; + scc_reg3_delay(); + sc->ctrl = shadow[INT_CTRL_REG]; + scc_reg_delay(); + } + goto normal_case; + + case SDLC_OPTION_REG: + /* For WR7', first set b0 of WR15 to 1, if needed */ + if (!(shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT)) { + sc->ctrl = 15; + shadow[INT_CTRL_REG] |= ICR_OPTIONREG_SELECT; + scc_reg3_delay(); + sc->ctrl = shadow[INT_CTRL_REG]; + scc_reg_delay(); + } + sc->ctrl = 7; + shadow[8] = val; /* WR7' shadowed at WR8 */ + scc_reg3_delay(); + sc->ctrl = val; + break; + + case TX_DATA_REG: /* WR8 */ + /* TX_DATA_REG can be accessed directly on some h/w */ + if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) + { + sc->ctrl = regno; + scc_reg_delay(); + sc->ctrl = val; + } + else + sc->data = val; + break; + + case MASTER_INT_CTRL: + sc->ctrl = regno; + val &= 0x3f; /* bits 6..7 are the reset commands */ + SCC_shadow[0][regno] = val; + scc_reg3_delay(); + sc->ctrl = val; + break; + + case DPLL_CTRL_REG: + sc->ctrl = regno; + val &= 0x1f; /* bits 5..7 are the DPLL commands */ + shadow[regno] = val; + scc_reg3_delay(); + sc->ctrl = val; + break; + + case 1 ... 6: + case 10 ... 13: + case 15: + normal_case: + sc->ctrl = regno; + shadow[regno] = val; + scc_reg3_delay(); + sc->ctrl = val; + break; + + default: + printk( "Bad SCC write access to WR%d\n", regno ); + break; + + } + + if (final_delay) + scc_reg_delay(); +} + + +static __inline__ unsigned char _SCCread( + volatile struct PARTIAL_SCC *sc, + unsigned char *shadow, + volatile unsigned char *_scc_del, + int regno, int final_delay ) +{ + unsigned char rv; + + switch( regno ) { + + /* --- real read registers --- */ + case STATUS_REG: + rv = sc->ctrl; + break; + + case INT_PENDING_REG: + /* RR3: read only from Channel A! */ + sc = (volatile struct PARTIAL_SCC *) + (((unsigned long)sc & ~(MACH_IS_BVME6000 ? 8 : (MACH_IS_MVME147 ? 2 : 4))) ^ ChannelsReversed); + goto normal_case; + + case RX_DATA_REG: + /* RR8 can be accessed directly on some h/w */ + if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) + { + sc->ctrl = 8; + scc_reg_delay(); + rv = sc->ctrl; + } + else + rv = sc->data; + break; + + case CURR_VECTOR_REG: + /* RR2 (vector including status) from Ch. B */ + sc = (volatile struct PARTIAL_SCC *) + (((unsigned long)sc | (MACH_IS_BVME6000 ? 8 : (MACH_IS_MVME147 ? 2 : 4))) ^ ChannelsReversed); + goto normal_case; + + /* --- reading write registers: access the shadow --- */ + case 1 ... 7: + case 10 ... 15: + return shadow[regno]; /* no final delay! */ + + /* WR7' is special, because it is shadowed at the place of WR8 */ + case SDLC_OPTION_REG: + return shadow[8]; /* no final delay! */ + + /* WR9 is special too, because it is common for both channels */ + case MASTER_INT_CTRL: + return SCC_shadow[0][9]; /* no final delay! */ + + default: + printk( "Bad SCC read access to %cR%d\n", (regno & 16) ? 'R' : 'W', + regno & ~16 ); + break; + + case SPCOND_STATUS_REG: + case FS_FIFO_LOW_REG: + case FS_FIFO_HIGH_REG: + case DPLL_STATUS_REG: + normal_case: + sc->ctrl = regno & 0x0f; + scc_reg_delay(); + rv = sc->ctrl; + break; + + } + + if (final_delay) + scc_reg_delay(); + return rv; +} + +/* + * The BVME6000 maps the two halves of the SCC 8 bytes apart rather than 4. + * The MVME147 maps the two halves of the SCC 2 bytes apart rather than 4. + */ + +#define SCC_ACCESS_INIT(info) \ + volatile struct PARTIAL_SCC *_SCC_p = \ + (volatile struct PARTIAL_SCC *)info->port; \ + unsigned char *_SCC_shadow = &SCC_shadow[(info->port >> \ + (MACH_IS_BVME6000 ? 3 : (MACH_IS_MVME147 ? 1 : 2))) & 1][0] + +#define SCCwrite(reg,val) _SCCwrite(_SCC_p,_SCC_shadow,scc_del,(reg),(val),1) +#define SCCwrite_NB(reg,val) _SCCwrite(_SCC_p,_SCC_shadow,scc_del,(reg),(val),0) +#define SCCread(reg) _SCCread(_SCC_p,_SCC_shadow,scc_del,(reg),1) +#define SCCread_NB(reg) _SCCread(_SCC_p,_SCC_shadow,scc_del,(reg),0) + +#define SCCmod(reg,and,or) SCCwrite((reg),(SCCread(reg)&(and))|(or)) + +#endif /* _ATARI_SCC_H */ diff --git a/drivers/char/lp_mfc.c b/drivers/char/lp_mfc.c new file mode 100644 index 000000000000..fbd4b7c701a7 --- /dev/null +++ b/drivers/char/lp_mfc.c @@ -0,0 +1,174 @@ +/* + * lp driver for the parallel port of a Multiface Card III + * 6.11.95 Joerg Dorchain (dorchain@mpi-sb.mpg.de) + * + * ported to 2.0.18 and modularised + * 25.9.96 Joerg Dorchain + * + * ported to 2.1.42 + * 24.6.97 Joerg Dorchain (jdorchain@i-con.de) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "multiface.h" + +static void lp_mfc_out(int,int); +static int lp_mfc_busy(int); +static int lp_mfc_pout(int); +static int lp_mfc_online(int); +static void lp_mfc_interrupt(int, void *, struct pt_regs *); + +static inline struct pia *pia(int); + +static volatile int dummy; /* for trigger reads */ +static int minor[MAX_LP] = { -1, -1, -1, -1, -1 }; +MODULE_PARM(minor,"1-" __MODULE_STRING(MAX_LP) "i"); +#ifdef MODULE +static unsigned int board_key[MAX_LP]; +#endif + +static void lp_mfc_out(int c, int dev) +{ +int wait = 0; + +while (wait != lp_table[dev]->wait) wait++; +dummy = pia(dev)->pprb; /* trigger read clears irq bit*/ +pia(dev)->pprb = c; /* strobe goes down by hardware */ +} + +static int lp_mfc_busy(int dev) +{ +return pia(dev)->ppra&1; +} + +static int lp_mfc_pout(int dev) +{ +return pia(dev)->ppra&2; +} + +static int lp_mfc_online(int dev) +{ +return pia(dev)->ppra&4; +} + +static void lp_mfc_interrupt(int irq,void *data,struct pt_regs *fp) +{ +int i; + +for( i=0; icrb&128) { /* has an irq? */ + dummy = pia(minor[i])->pprb; /* clear bit */ + lp_interrupt(minor[i]); + } +} + +static inline struct pia *pia(int dev) +{ +return lp_table[dev]->base; +} + +static int lp_mfc_open(int dev) +{ +MOD_INC_USE_COUNT; +return 0; +} + +static void lp_mfc_release(int dev) +{ +MOD_DEC_USE_COUNT; +} + +static struct lp_struct tab[MAX_LP] = {{0,},}; + +__initfunc(int lp_mfc_init(void)) +{ +int pias; +struct pia *pp; +unsigned int key = 0; +const struct ConfigDev *cd; + +if (!MACH_IS_AMIGA) + return -ENODEV; + +pias = 0; +while((key = zorro_find(ZORRO_PROD_BSC_MULTIFACE_III, 0 , key))) { + cd = zorro_get_board( key ); + pp = (struct pia *)ZTWO_VADDR((((u_char *)cd->cd_BoardAddr)+PIABASE)); + if (pias < MAX_LP) { + pp->crb = 0; + pp->pddrb = 255; /* all pins output */ + pp->crb = PIA_DDR|32|8; + dummy = pp->pprb; + pp->crb |=(lp_irq!=0)?PIA_C1_ENABLE_IRQ:0; + pp->cra = 0; + pp->pddra = 0xe0; /* /RESET, /DIR ,/AUTO-FEED output */ + pp->cra = PIA_DDR; + pp->ppra = 0; /* reset printer */ + udelay(5); + pp->ppra |= 128; + tab[pias].name="Multiface III LP"; + tab[pias].lp_out=lp_mfc_out; + tab[pias].lp_is_busy=lp_mfc_busy; + tab[pias].lp_has_pout=lp_mfc_pout; + tab[pias].lp_is_online=lp_mfc_online; + tab[pias].lp_ioctl=NULL; + tab[pias].lp_open=lp_mfc_open; + tab[pias].lp_release=lp_mfc_release; + tab[pias].flags=LP_EXIST; + tab[pias].chars=LP_INIT_CHAR; + tab[pias].time=LP_INIT_TIME; + tab[pias].wait=LP_INIT_WAIT; + tab[pias].lp_wait_q=NULL; + tab[pias].base=pp; + tab[pias].type=LP_MFC; + if ((minor[pias] = register_parallel(tab + pias, minor[pias] )) >= 0) { + zorro_config_board( key, 0 ); +#ifdef MODULE + board_key[minor[pias]] = key; +#endif + pias++; + } + else + printk("mfc_init: cant get a minor for pia at 0x%08lx\n",(long)pp); + } +} +if ((pias != 0) && (lp_irq != 0)) + request_irq(IRQ_AMIGA_PORTS, lp_mfc_interrupt, 0, + "Multiface III printer", lp_mfc_interrupt); + +return (pias==0)?-ENODEV:0; +} + +#ifdef MODULE +int init_module(void) +{ +return lp_mfc_init(); +} + +void cleanup_module(void) +{ +int i; + +if (lp_irq) + free_irq(IRQ_AMIGA_PORTS, lp_mfc_interrupt); +for(i = 0; i < MAX_LP; i++) + if ((lp_table[i] != NULL) && (lp_table[i]->type == LP_MFC)) { + unregister_parallel(i); + zorro_unconfig_board(board_key[i], 0); + } +} +#endif diff --git a/drivers/char/m68kserial.c b/drivers/char/m68kserial.c new file mode 100644 index 000000000000..b22fc66e35ad --- /dev/null +++ b/drivers/char/m68kserial.c @@ -0,0 +1,1861 @@ +/* + * linux/drivers/char/m68kserial.c + * + * + * Copyright 1994 Roman Hodek + * + * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o + * + * 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) 1991, 1992 Linus Torvalds + * + * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now + * much more extensible to support other serial cards based on the + * 16450/16550A UART's. Added support for the AST FourPort and the + * Accent Async board. + * + * set_serial_info fixed to set the flags, custom divisor, and uart + * type fields. Fix suggested by Michael K. Johnson 12/12/92. + * + * This module exports the following rs232 io functions: + * + * long m68k_rs_init(void); + */ + +/* + * Notes and Design Goals: + * ----------------------- + * The PC serial drivers can rely on the fact that all the serial + * hardware is very similar to program for all ports. Unfortunately, + * this is not true for m68k machines, especially the Atari. Here it is + * nearly the other way 'round: All ports need different treatment for + * the low-level stuff. + * + * For this reason, I've split the serial driver code into a + * port-independent part (serial.c) and port-specific parts (atari_*.c, + * ...). The first manages all what can be done without accessing the + * hardware directly, i.e. interfacing with the high-level tty drivers, + * wait queues, managing existing ports and the like. The latter do the + * actual hardware programming and are accessed by the hardware + * independent part by a "switch" structure, that contains pointers to + * functions for specific tasks. See the comment before the definition + * of the SERIALSWITCH structure in for more details. + * + * The port-independent code should be usable by other machines than + * m68k ones, too, if there are similar circumstances with different + * serial port hardware. Feel free to use it, but please inform me if + * you have to do changes to it. I'll try to keep it really + * device-independent. + * + * Roman + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PROC_FS +#include +#endif +#ifdef CONFIG_SERIAL_CONSOLE +#include +#endif + +#include +#include +#include +#include +#include + +#ifdef CONFIG_ATARI +#include "atari_SCC.h" +#include "atari_MFPser.h" +#include "atari_MIDI.h" +#endif + +#ifdef CONFIG_KMOD +#include +#endif + +#ifdef CONFIG_MAC_SCC +int mac_SCC_init(void); +#endif + +#ifdef CONFIG_AMIGA_BUILTIN_SERIAL +int amiga_serinit (void); +#endif + +#ifdef CONFIG_GVPIOEXT +int ioext_init (void); +#endif + +#ifdef CONFIG_MULTIFACE_III_TTY +int multiface_init(void); +#endif + +#ifdef CONFIG_MVME147_SCC +int m147_SCC_init(void); +#endif + +#ifdef CONFIG_MVME162_SCC +int mvme_SCC_init(void); +#endif + +#ifdef CONFIG_BVME6000_SCC +int bvme_SCC_init(void); +#endif + +#ifdef CONFIG_WHIPPET +int whippet_init (void); +#endif + +#ifdef CONFIG_HYPERCOM1 +int hypercom1_init(void); +#endif + +#ifdef CONFIG_HPDCA +int hpdca_init(void); +#endif + +DECLARE_TASK_QUEUE(tq_serial); + +struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +/* serial subtype definitions */ +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +#define SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW + +#define _INLINE_ inline + +#define NR_PORTS 6 + +struct m68k_async_struct rs_table[NR_PORTS]; +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; +#ifdef CONFIG_SERIAL_CONSOLE +static struct console sercons; +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +static char *serialtypes[] = { + "unknown", "8250", "16450", "16550", "16550A" +}; +#define PORT_MAX (sizeof(serialtypes)/sizeof(*serialtypes)) +#if defined(__mc68000__) || defined(CONFIG_APUS) +static char *serialtypes68k[] = { + "SCC w/o DMA", "SCC w/ DMA", + "MFP", "MFP w/o ctrl lines", + "MIDI", + "Amiga builtin", "GVP IO-Extender (16c552)", "BSC MultiFaceCard III", + "Hisoft Whippet", + "SCC on MVME", + "8350 ESCC w/o DMA", + "HP DCA", + "SCC on BVME", "A1200 HyperCOM1" +}; +#define M68K_PORT_MAX (sizeof(serialtypes68k)/sizeof(*serialtypes68k)) +#endif + +#ifdef CONFIG_PROC_FS +static int rs_read_proc (char *buffer, char **start, off_t offset, int size, + int *eof, void *data) +{ + int len, i; + off_t begin = 0; + char *name; + + len = sprintf (buffer, "Serial ports:\n"); + for (i = 0; i < NR_PORTS; ++i) { + struct m68k_async_struct *info = &rs_table[i]; + + if (!info->port) continue; + if (info->type >= 0 && info->type < PORT_MAX) + name = serialtypes[info->type]; +#if defined(__mc68000__) || defined(CONFIG_APUS) + else if (info->type >= 100 && info->type < 100 + M68K_PORT_MAX) + name = serialtypes68k[info->type - 100]; +#endif + else + name = "unknown"; + len += sprintf (buffer + len, "%d: name:%s port:0x%08x tx:%d rx:%d", + i, name, info->port, info->icount.tx, info->icount.rx); + + if (info->icount.frame) + len += sprintf(buffer + len, " fe:%d", info->icount.frame); + if (info->icount.parity) + len += sprintf(buffer + len, " pe:%d", info->icount.parity); + if (info->icount.brk) + len += sprintf(buffer + len, " brk:%d", info->icount.brk); + if (info->icount.overrun) + len += sprintf(buffer + len, " oe:%d", info->icount.overrun); + len += sprintf(buffer + len, "\n"); + + if (len + begin > offset + size) + goto done; + if (len + begin < offset) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (offset >= len + begin) + return 0; + *start = buffer + (begin - offset); + return (size < begin + len - offset ? size : begin + len - offset); +} +#endif + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf = 0; +static struct semaphore tmp_buf_sem = MUTEX; + +static inline int serial_paranoia_check(struct m68k_async_struct *info, + kdev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + const char *badmagic = + "Warning: bad magic number for serial struct (%s) in %s\n"; + const char *badinfo = + "Warning: null m68k_async_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +void rs_stop(struct tty_struct *tty) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_stop")) + return; + + info->sw->enab_tx_int( info, 0 ); +} + +void rs_start(struct tty_struct *tty) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_start")) + return; + + info->sw->enab_tx_int( info, 1 ); +} + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using rs_sched_event(), and they get done here. + */ +static void do_serial_bh(void) +{ + run_task_queue(&tq_serial); +} + +static void do_softint(void *private_) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + +/* + * --------------------------------------------------------------- + * Low level utility subroutines for the serial driver: routines to + * figure out the appropriate timeout for an interrupt chain, routines + * to initialize and startup a serial port, and routines to shutdown a + * serial port. Useful stuff like that. + * --------------------------------------------------------------- + */ + +static int startup(struct m68k_async_struct * info) +{ + unsigned long flags; + unsigned long page; + + page = get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + restore_flags(flags); + return 0; + } + + if (!info->port || !info->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + restore_flags(flags); + return 0; + } + if (info->xmit_buf) + free_page(page); + else + info->xmit_buf = (unsigned char *) page; + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttyS%d...", info->line); +#endif + + /* initialize the hardware specific stuff for the port */ + info->sw->init(info); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + /* + * Set the speed and other port parameters. + */ + info->sw->change_speed(info); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(struct m68k_async_struct * info) +{ + unsigned long flags; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d\n", info->line); +#endif + + save_flags(flags); cli(); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* do hardware specific deinitialization for the port */ + info->sw->deinit( info, info->tty && + !(info->tty->termios->c_cflag & HUPCL) ); + + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +static void rs_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_put_char")) + return; + + if (!tty || !info->xmit_buf) + return; + + save_flags(flags); cli(); + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE-1; + info->xmit_cnt++; + restore_flags(flags); +} + +static void rs_flush_chars(struct tty_struct *tty) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) + return; + + info->sw->enab_tx_int( info, 1 ); +} + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, total = 0; + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty || !info->xmit_buf || !tmp_buf) + return 0; + + if (from_user) + down(&tmp_buf_sem); + save_flags(flags); + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + if (from_user) { + restore_flags(flags); + copy_from_user(tmp_buf, buf, c); + cli(); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + if (from_user) + up(&tmp_buf_sem); + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) + info->sw->enab_tx_int( info, 1 ); + restore_flags(flags); + return total; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + int ret; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + +static int rs_chars_in_buffer(struct tty_struct *tty) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) + return 0; + return info->xmit_cnt; +} + +static void rs_flush_buffer(struct tty_struct *tty) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) + return; + save_flags(flags); + cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void rs_throttle(struct tty_struct * tty) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_throttle")) + return; + + if (I_IXOFF(tty)) { + info->x_char = STOP_CHAR(tty); + /* make sure the XOFF char is sent out if the TX was idle */ + info->sw->enab_tx_int( info, 1 ); + } + + if (C_CRTSCTS(tty)) + info->sw->throttle( info, 1 ); +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + unsigned long flags; + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) + return; + + if (I_IXOFF(tty)) { + /* Protect against an tx int between the test and resetting x_char. + * This would cause the XON to be lost. */ + save_flags(flags); + cli(); + if (info->x_char) { + info->x_char = 0; + restore_flags(flags); + } + else { + restore_flags(flags); + info->x_char = START_CHAR(tty); + /* make sure the XOFF char is sent out if the TX was idle */ + info->sw->enab_tx_int( info, 1 ); + } + } + + if (C_CRTSCTS(tty)) + info->sw->throttle( info, 0 ); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +static int get_serial_info(struct m68k_async_struct * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->line; + tmp.port = 0; /* meaningless for non-Intel */ + tmp.irq = 0; /* meaningless for non-Intel */ + tmp.flags = info->flags; + /* tmp.baud_base = info->baud_base;*/ + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + tmp.custom_divisor = info->custom_divisor; + tmp.hub6 = 0; /* meaningless for non-Intel */ + + /* At least baud_base and costum_divisor set by port-specific + * function + */ + info->sw->get_serial_info( info, &tmp ); + + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct m68k_async_struct * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct m68k_async_struct old_info; + int retval = 0; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + old_info = *info; + + /* If a new custom divisor is to be set, let it check by the hardware + * specific code first! (It is given the new info struct in case the + * baud_base has also changed and valid divisors depend on the baud_base.) + */ + if (new_serial.custom_divisor != info->custom_divisor || + new_serial.baud_base != info->baud_base) { + if (info->sw->check_custom_divisor( info, new_serial.baud_base, + new_serial.custom_divisor )) + return( -EINVAL ); + } + + if (((new_serial.type != info->type) || + (new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (info->flags & ~ASYNC_USR_MASK))) + && !capable(CAP_SYS_ADMIN)) + return -EPERM; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->baud_base = new_serial.baud_base; + info->flags = ((info->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->custom_divisor = new_serial.custom_divisor; + info->type = new_serial.type; + info->close_delay = new_serial.close_delay * HZ/100; + info->closing_wait = new_serial.closing_wait * HZ/100; + + if (!info->port || !info->type) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if (((old_info.flags & ASYNC_SPD_MASK) != + (info->flags & ASYNC_SPD_MASK)) || + (old_info.custom_divisor != info->custom_divisor) || + (old_info.baud_base != info->baud_base)) + info->sw->change_speed(info); + } else + retval = startup(info); + return retval; +} + + +static int get_modem_info(struct m68k_async_struct * info, unsigned int *value) +{ + unsigned int result; + + result = info->sw->get_modem_info (info); + return put_user(result,value); +} + +static int set_modem_info(struct m68k_async_struct * info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + int new_dtr = -1, new_rts = -1; + + if (get_user(arg, value)) + return -EFAULT; + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + new_rts = 1; + if (arg & TIOCM_DTR) + new_dtr = 1; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + new_rts = 0; + if (arg & TIOCM_DTR) + new_dtr = 0; + break; + case TIOCMSET: + new_dtr = !!(arg & TIOCM_DTR); + new_rts = !!(arg & TIOCM_RTS); + break; + default: + return -EINVAL; + } + return( info->sw->set_modem_info( info, new_dtr, new_rts ) ); +} + +/* + * rs_break() --- routine which turns the break handling on or off + */ +static void rs_break(struct tty_struct *tty, int break_state) +{ + struct m68k_async_struct * info = (struct m68k_async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) + return; + + if (!info->port) + return; + save_flags(flags); + cli(); + if (break_state == -1) + info->sw->set_break(info, 1); + else + info->sw->set_break(info, 0); + restore_flags(flags); +} + +static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct m68k_async_struct * info = (struct m68k_async_struct *)tty->driver_data; + struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERCONFIG: + /* return do_autoconfig(info); */ + return ( 0 ); + + case TIOCSERGETLSR: /* Get line status register */ + return put_user(0, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct m68k_async_struct *) arg, + info, sizeof(struct m68k_async_struct))) + return -EFAULT; + return 0; + + case TIOCSERGETMULTI: + case TIOCSERSETMULTI: + return -EINVAL; + + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + save_flags(flags); + cli(); + cprev = info->icount; /* note the counters on entry */ + restore_flags(flags); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + save_flags(flags); + cli(); + cnow = info->icount; /* atomic copy */ + restore_flags(flags); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + save_flags(flags); + cli(); + cnow = info->icount; + restore_flags(flags); + p_cuser = (struct serial_icounter_struct *) arg; + if (put_user(cnow.cts, &p_cuser->cts) || + put_user(cnow.dsr, &p_cuser->dsr) || + put_user(cnow.rng, &p_cuser->rng) || + put_user(cnow.dcd, &p_cuser->dcd)) + return -EFAULT; + return 0; + + default: + if (info->sw->ioctl) + return( info->sw->ioctl( tty, file, info, cmd, arg ) ); + else + return -ENOIOCTLCMD; + } + return 0; +} + +static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct m68k_async_struct *info = (struct m68k_async_struct *)tty->driver_data; + + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + + info->sw->change_speed(info); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_start(tty); + } + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void rs_close(struct tty_struct *tty, struct file * filp) +{ + struct m68k_async_struct * info = (struct m68k_async_struct *)tty->driver_data; + unsigned long flags; + unsigned long timeout; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close ttys%d, count = %d\n", info->line, info->count); +#endif + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + if (--info->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, info->count); + info->count = 0; + } + if (info->count) { + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + if (info->flags & ASYNC_INITIALIZED) { + info->sw->stop_receive(info); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies+HZ; + while (!(info->sw->trans_empty(info))) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(info->timeout); + if (signal_pending(current)) + break; + if (time_after(jiffies, timeout)) + break; + } + current->state = TASK_RUNNING; + } + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; + restore_flags(flags); +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_hangup(struct tty_struct *tty) +{ + struct m68k_async_struct * info = (struct m68k_async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_hangup")) + return; + + rs_flush_buffer(tty); + shutdown(info); + info->event = 0; + info->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct m68k_async_struct *info) +{ + struct wait_queue wait = { current, NULL }; + int retval; + int do_clocal = 0; + unsigned long flags; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttys%d, count = %d\n", + info->line, info->count); +#endif + save_flags(flags); + cli(); + if (!tty_hung_up_p(filp)) + info->count--; + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); + cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE)) + info->sw->set_modem_info( info, 1, 1 ); + restore_flags(flags); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (info->sw->get_modem_info( info ) & TIOCM_CAR))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, info->count); +#endif + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + info->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, info->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +static int rs_open(struct tty_struct *tty, struct file * filp) +{ + struct m68k_async_struct *info; + int retval, line; + unsigned long page; + + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) + return -ENXIO; + info = rs_table + line; +#ifdef CONFIG_KMOD + if (!info->port) { + char modname[30]; + sprintf(modname, "char-major-%d-%d", TTY_MAJOR, MINOR(tty->device)); + request_module(modname); + } +#endif + if (!info->port) + return -ENXIO; + if (serial_paranoia_check(info, tty->device, "rs_open")) + return -ENODEV; + + if (info->sw->check_open) { + if ((retval = info->sw->check_open( info, tty, filp ))) + return( retval ); + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->count); +#endif + info->count++; + tty->driver_data = info; + info->tty = tty; + + if (!tmp_buf) { + page = get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + + /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * Start up serial port + */ + retval = startup(info); + if (retval) + return retval; + + MOD_INC_USE_COUNT; + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + info->sw->change_speed(info); + } +#ifdef CONFIG_SERIAL_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + info->sw->change_speed(info); + } +#endif + + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif + return 0; +} + +/* + * --------------------------------------------------------------------- + * rs_init() and friends + * + * rs_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +/* + * Boot-time initialization code: Init the serial ports present on + * this machine type. + * + * Current HW Port to minor/device name mapping: + * + * Atari + * TT | Falcon || minor | name + * -----------------------+---------------------------++-------+------- + * ST-MFP port (Modem1) | (Modem1, internal only) || 64 | ttyS0 + * SCC B (Modem2) | SCC B (Modem2) || 65 | ttyS1 + * TT-MFP port (Serial1) | || 66 | ttyS2 + * SCC A (Serial2) | || 67 | ttyS3 + * SCC A (LAN) | SCC A (LAN) || 68 | ttyS4 + * Atari MIDI | Atari MIDI || 69 | ttyS5 + * + * New 22/12/94: Midi is implemented as a serial device to make use of + * MIDI networks easier (SLIP on ttyS5). If there is need for MIDI + * specific high-level code, it can be implemented as a line + * discipline in future. ttyS4 is reserved to make space for the LAN + * device. It seems to be the better solution to split Serial2 and LAN + * into separate minors. And maybe the port to device mapping will + * change in future... + * + * 02/08/95: Now the mapping has changed! My old mapping scheme of the serial + * ports was a flaw in respect to order the devices following their + * capabilities. And it confused the users, to... The "new" order is the same + * as the one used by TOS. The only difference is that Serial2/LAN is split + * into two devices, only one of which can be open at one time. If one of + * these devices is opened, the respective port's hardware is activated via + * IO7 of the PSG (if connected...) + * + * Amiga + * Amiga built-in serial port || 64 | ttyS0 + * + * New 13/07/95: Support for multiple serial-boards of the same kind, + * and boards with dual (or higher) uarts. This is done by letting the + * drivers 'allocate' tty's in the rs_table[rs_count] array. A counter + * (rs_count) is therefore passed to all serial-init routines, that + * adds the amount of uarts detected, and returns the new value. + * I think this is the best way to manage the various serial-boards + * available (atleast for the Amiga), and should cause no harm to the + * old drivers. - Jes Sorensen (jds@kom.auc.dk) + * + */ + +/* + * This routine prints out the appropriate serial driver version + * number, and identifies which options were configured into this + * driver. + */ +static inline void show_serial_version(void) +{ + printk(KERN_INFO "M68K Serial driver version 1.01\n"); +} + +EXPORT_SYMBOL(rs_table); +EXPORT_SYMBOL(tq_serial); +EXPORT_SYMBOL(m68k_register_serial); +EXPORT_SYMBOL(m68k_unregister_serial); +EXPORT_SYMBOL(rs_start); +EXPORT_SYMBOL(rs_stop); + +/* + * The serial driver boot-time initialization code! + */ +int __init m68k_rs_init(void) +{ + int i, rs_count; + struct m68k_async_struct * info; + +#ifdef CONFIG_Q40 + if (MACH_IS_Q40) + return 0; +#endif + rs_count = 0; /* All machines start at rs_table[0] (minor 64). */ + + init_bh(SERIAL_BH, do_serial_bh); + +#ifdef CONFIG_SERIAL_CONSOLE + /* + * The interrupt of the serial console port + * can't be shared. + */ + if (sercons.flags & CON_CONSDEV) { + for(i = 0; i < NR_PORTS; i++) + if (i != sercons.index && + rs_table[i].irq == rs_table[sercons.index].irq) + rs_table[i].irq = 0; + } +#endif + show_serial_version(); + + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; + serial_driver.driver_name = "serial"; + serial_driver.name = "ttyS"; + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64; + serial_driver.num = NR_PORTS; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = rs_open; + serial_driver.close = rs_close; + serial_driver.write = rs_write; + serial_driver.put_char = rs_put_char; + serial_driver.flush_chars = rs_flush_chars; + serial_driver.write_room = rs_write_room; + serial_driver.chars_in_buffer = rs_chars_in_buffer; + serial_driver.flush_buffer = rs_flush_buffer; + serial_driver.ioctl = rs_ioctl; + serial_driver.throttle = rs_throttle; + serial_driver.unthrottle = rs_unthrottle; + serial_driver.set_termios = rs_set_termios; + serial_driver.stop = rs_stop; + serial_driver.start = rs_start; + serial_driver.hangup = rs_hangup; + serial_driver.break_ctl = rs_break; + /* TODO: serial_driver.wait_until_sent = rs_wait_until_sent; */ +#ifdef CONFIG_PROC_FS + serial_driver.read_proc = rs_read_proc; +#endif + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cua"; + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; + callout_driver.read_proc = 0; + + if (tty_register_driver(&serial_driver)) { + printk("Couldn't register serial driver\n"); + return -EIO; + } + if (tty_register_driver(&callout_driver)) { + tty_unregister_driver(&serial_driver); + printk("Couldn't register callout driver\n"); + return -EIO; + } + + for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { + info->magic = SERIAL_MAGIC; + info->line = i; + info->port = 0; + info->tty = 0; + info->type = PORT_UNKNOWN; + info->custom_divisor = 0; + info->close_delay = 5*HZ/10; + info->closing_wait = 30*HZ; + info->x_char = 0; + info->event = 0; + info->count = 0; + info->blocked_open = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios =callout_driver.init_termios; + info->normal_termios = serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + info->delta_msr_wait = 0; + info->icount.cts = info->icount.dsr = 0; + info->icount.rng = info->icount.dcd = 0; + info->icount.rx = info->icount.tx = 0; + info->icount.frame = info->icount.parity = 0; + info->icount.overrun = info->icount.brk = 0; + info->next_port = 0; + info->prev_port = 0; + } + +#ifndef MODULE + switch (m68k_machtype) { + case MACH_ATARI: +#ifdef CONFIG_ATARI_SCC + atari_SCC_init(); +#endif +#ifdef CONFIG_ATARI_MFPSER + atari_MFPser_init(); +#endif +#ifdef CONFIG_ATARI_MIDI + atari_MIDI_init(); +#endif + break; + + case MACH_AMIGA: +#ifdef CONFIG_AMIGA_BUILTIN_SERIAL + amiga_serinit(); +#endif +#ifdef CONFIG_GVPIOEXT + ioext_init(); +#endif +#ifdef CONFIG_MULTIFACE_III_TTY + multiface_init(); +#endif +#ifdef CONFIG_WHIPPET_SERIAL + whippet_init(); +#endif +#ifdef CONFIG_HYPERCOM1 + hypercom1_init(); +#endif + break; + case MACH_MVME147: +#ifdef CONFIG_MVME147_SCC + m147_SCC_init(); +#endif + break; + case MACH_MVME16x: +#ifdef CONFIG_MVME162_SCC + mvme_SCC_init(); +#endif + break; + case MACH_BVME6000: +#ifdef CONFIG_BVME6000_SCC + bvme_SCC_init(); +#endif + break; + case MACH_MAC: +#ifdef CONFIG_MAC_SCC + mac_SCC_init(); +#endif + case MACH_HP300: +#ifdef CONFIG_HPDCA + hpdca_init(); +#endif + } /* end switch on machine type */ +#endif + + return 0; +} + +/* + * register_serial and unregister_serial allows for serial ports to be + * configured at run-time, to support PCMCIA modems. + * + * ++roman: Removed PCish stuff from here... New calling interface: The caller + * can set req->line to a value >= 0 to request a particular slot. If + * req->line is < 0, the first free slot is assigned. Other fields that must + * be filled in before the call are 'port' and 'type'. + */ +int m68k_register_serial(struct serial_struct *req) +{ + int i; + unsigned long flags; + struct m68k_async_struct *info; + + save_flags(flags); + cli(); +#if !defined(__mc68000__) && !defined(CONFIG_APUS) + for (i = 0; i < NR_PORTS; i++) { + if (rs_table[i].port == req->port) + break; + } +#else + if (req->line >= 0) { + if (req->line >= NR_PORTS) + return -EINVAL; + if (rs_table[req->line].type != PORT_UNKNOWN || + rs_table[req->line].count > 0) { + /* already allocated */ + restore_flags(flags); + return -EBUSY; + } + i = req->line; + } + else { + for (i = 0; i < NR_PORTS; i++) + if ((rs_table[i].type == PORT_UNKNOWN) && + (rs_table[i].count == 0)) + break; + } +#endif + if (i == NR_PORTS) { + restore_flags(flags); + return -EAGAIN; + } + info = &rs_table[i]; + if (rs_table[i].count) { + restore_flags(flags); + printk("Couldn't configure serial #%d (port=%d,irq=%d): " + "device already open\n", i, req->port, req->irq); + return -EBUSY; + } +#if !defined(__mc68000__) && !defined(CONFIG_APUS) + info->irq = req->irq; + info->port = req->port; + autoconfig(info); + if (info->type == PORT_UNKNOWN) { + restore_flags(flags); + printk("register_serial(): autoconfig failed\n"); + return -1; + } + printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line, + info->port, info->irq); +#else + info->port = req->port; + info->type = req->type; + printk(KERN_INFO "ttyS%d at 0x%08x: ", info->line, info->port); +#endif + if (info->type >= 0 && info->type < PORT_MAX) + printk( "%s\n", serialtypes[info->type] ); +#if defined(__mc68000__) || defined(CONFIG_APUS) + else if (info->type >= 100 && info->type < 100 + M68K_PORT_MAX) + printk( "%s\n", serialtypes68k[info->type - 100] ); +#endif + else + printk( "\n" ); + restore_flags(flags); + return info->line; +} + +void m68k_unregister_serial(int line) +{ + unsigned long flags; + struct m68k_async_struct *info = &rs_table[line]; + + save_flags(flags); + cli(); + if (info->tty) + tty_hangup(info->tty); + info->type = PORT_UNKNOWN; +#if defined(__mc68000__) || defined(CONFIG_APUS) + info->port = 0; +#endif + printk(KERN_INFO "tty%02d unloaded\n", info->line); + restore_flags(flags); +} + +#ifdef MODULE +int init_module(void) +{ + return m68k_rs_init(); +} + +void cleanup_module(void) +{ + if (tty_unregister_driver(&serial_driver)) + printk("SERIAL: failed to unregister serial driver\n"); + if (tty_unregister_driver(&callout_driver)) + printk("SERIAL: failed to unregister callout driver\n"); +} +#endif /* MODULE */ + + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ +#ifdef CONFIG_SERIAL_CONSOLE + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +extern void amiga_serial_console_write(struct console *co, const char *s, unsigned int count); +extern int amiga_serial_console_wait_key(struct console *co); +extern void amiga_init_serial_console(struct async_struct *info, int cflag); + +extern void atari_mfp_console_write (struct console *co, const char *str, unsigned int count); +extern int atari_mfp_console_wait_key(struct console *co); +extern void atari_init_mfp_port( int cflag ); + +extern void atari_scc_console_write (struct console *co, const char *str, unsigned int count); +extern int atari_scc_console_wait_key(struct console *co); +extern void atari_init_scc_port( int cflag ); + +extern void atari_midi_console_write (struct console *co, const char *str, unsigned int count); +extern int atari_midi_console_wait_key(struct console *co); +extern void atari_init_midi_port( int cflag ); + +extern void mac_sccb_console_write (struct console *co, const char *str, unsigned int count); +extern int mac_sccb_console_wait_key(struct console *co); +extern void mac_init_sccb_port( int cflag ); + +extern void mac_scca_console_write (struct console *co, const char *str, unsigned int count); +extern int mac_scca_console_wait_key(struct console *co); +extern void mac_init_scca_port( int cflag ); + +extern void mvme147_init_console_port (struct console *co, int cflag); +extern void mvme16x_init_console_port (struct console *co, int cflag); +extern void bvme6000_init_console_port (struct console *co, int cflag); + +extern void hpdca_serial_console_write(struct console *co, const char *str, unsigned int count); +extern int hpdca_serial_console_wait_key(struct console *co); +extern int hpdca_init_serial_console(int cflag); + +/* + * Setup initial baud/bits/parity. + */ +__initfunc(static int serial_console_setup(struct console *co, char *options)) +{ + char *s; + int baud = 0, bits, parity; + int cflag = CREAD | HUPCL | CLOCAL; + + bits = 8; + parity = 'n'; + + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s - '0'; + } + + /* Now construct a cflag setting. */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 9600: + default: + cflag |= B9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + case 8: + default: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARENB|PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + /* Initialization of the port and filling in the missing fields of the + * sercons struct must happen here, since register_console() already uses + * write to print the log buffer. */ + + /* Currently this supports the Amiga builtin port only */ + if (MACH_IS_AMIGA && co->index == 0) { + co->write = amiga_serial_console_write; + co->wait_key = amiga_serial_console_wait_key; + /* no initialization yet */ + /* amiga_init_serial_console(rs_table+co->index, */ + /* serial_console_cflag); */ + } + /* On Atari, Modem1 (ttyS0), Modem2 (ttyS1) and MIDI (ttyS5) are supported + * Note: On a TT, 57.6 and 115.2 kbps are not possible and are replaced by + * 76.8 and 153.6 kbps. + * Note2: On MIDI, 7812.5 bps is selected by 4800 on the command line, and + * 500 kbps by 115200. All other rates give standard 31250bps. Mode 7N is + * not possible and replaced by 7O2 ... */ + else if (MACH_IS_ATARI && co->index == 0) { + co->write = atari_mfp_console_write; + co->wait_key = atari_mfp_console_wait_key; + atari_init_mfp_port( cflag ); + } + else if (MACH_IS_ATARI && co->index == 1) { + co->write = atari_scc_console_write; + co->wait_key = atari_scc_console_wait_key; + atari_init_scc_port( cflag ); + } + else if (MACH_IS_ATARI && co->index == 5) { + co->write = atari_midi_console_write; + co->wait_key = atari_midi_console_wait_key; + atari_init_midi_port( cflag ); + } + else if (MACH_IS_MVME147 && co->index == 0) { + mvme147_init_console_port (co, cflag); + } + else if (MACH_IS_MAC && co->index == 0) { + co->write = mac_scca_console_write; + co->wait_key = mac_scca_console_wait_key; + mac_init_scca_port( cflag ); + } + else if (MACH_IS_MAC && co->index == 1) { + co->write = mac_sccb_console_write; + co->wait_key = mac_sccb_console_wait_key; + mac_init_sccb_port( cflag ); + } + else if (MACH_IS_MVME16x && co->index == 0) { + mvme16x_init_console_port (co, cflag); + } + else if (MACH_IS_BVME6000 && co->index == 0) { + bvme6000_init_console_port (co, cflag); + } + return( 0 ); +} + +static void dummy_console_write( struct console *co, const char *str, + unsigned int count ) +{ +} + +static int dummy_wait_key( struct console *co ) +{ + return( '\r' ); +} + +static struct console sercons = { + "ttyS", + dummy_console_write, /* filled in by serial_console_setup */ + NULL, + serial_console_device, + dummy_wait_key, /* filled in by serial_console_setup */ + NULL, + serial_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + +/* + * This is here to set the speed etc. for a non-initialized + * line. We have no termios struct yet, so we just use "cflag". + */ +long m68k_serial_console_init(long kmem_start, long kmem_end) +{ + /* I've opted to init the HP serial console here. Doing it in + * serial_console_setup (above) is clearly far too late, + * and in the machine-dependent setup is too early. + * This may not be precisely correct or very clean but it's + * easy, and I'm not going to spend any time fixing m68kserial.c + * because it's going to go away soon. + * -- Peter Maydell + */ +#ifdef CONFIG_HPDCA + if (MACH_IS_HP300) { + if (hpdca_init_serial_console(B9600|CS8)) { + sercons.write = hpdca_serial_console_write; + sercons.wait_key = hpdca_serial_console_wait_key; + } + } +#endif + register_console(&sercons); + return kmem_start; +} +#endif /* CONFIG_SERIAL_CONSOLE */ diff --git a/drivers/char/mac_SCC.c b/drivers/char/mac_SCC.c index c20e7a9f0e05..6dabe7c4a8ed 100644 --- a/drivers/char/mac_SCC.c +++ b/drivers/char/mac_SCC.c @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include #include #include @@ -61,6 +61,7 @@ #include #include #include +#include #ifndef CONFIG_MAC #include #endif @@ -708,8 +709,6 @@ static void SCC_init( struct m68k_async_struct *info ) static void SCC_init_port( struct m68k_async_struct *info, int type, int channel ) { - static int got_autovector = 0; - #ifdef SCC_DEBUG printk("mac_SCC: init_port, info %x \n", info); #endif @@ -729,16 +728,6 @@ static void SCC_init_port( struct m68k_async_struct *info, int type, int channel * mac_SCC_interrupt with the proper arguments ... */ - if (!got_autovector) { - if(sys_request_irq(IRQ4, mac_SCC_handler, 0, "SCC master", info)) - panic("macserial: can't get irq %d", IRQ4); -#ifdef SCC_DEBUG - printk("mac_SCC: got SCC master interrupt %d, channel %d info %p\n", - IRQ4, channel, info); -#endif - got_autovector = 1; - } - if (info->private->zs_chan_a == info->private->zs_channel) { /* Channel A */ if (request_irq(IRQ_SCCA, mac_SCC_interrupt, 0, "SCC A", info)) @@ -1129,7 +1118,14 @@ static void SCC_throttle(struct m68k_async_struct *info, int status) static void SCC_get_serial_info(struct m68k_async_struct * info, struct serial_struct * retinfo) { + retinfo->type = info->type; + retinfo->line = info->line; + retinfo->port = info->port; + retinfo->irq = info->irq; + retinfo->flags= info->flags; retinfo->baud_base = info->baud_base; + retinfo->close_delay=info->close_delay; + retinfo->closing_wait=info->closing_wait; retinfo->custom_divisor = info->custom_divisor; } @@ -1234,7 +1230,20 @@ static int SCC_ioctl(struct tty_struct *tty, struct file * file, copy_to_user((struct m68k_async_struct *) arg, info, sizeof(struct m68k_async_struct)); return 0; - + case TIOCGSERIAL: + { + struct serial_struct tmp; + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct serial_struct)); + if (error) + return error; + if (!arg) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + SCC_get_serial_info(info, &tmp); + return copy_to_user((struct serial_struct *)arg, + &tmp, sizeof(tmp)); + } default: return -ENOIOCTLCMD; } diff --git a/drivers/char/q40_keyb.c b/drivers/char/q40_keyb.c new file mode 100644 index 000000000000..ea9d7438f3b7 --- /dev/null +++ b/drivers/char/q40_keyb.c @@ -0,0 +1,471 @@ +/* + * linux/drivers/char/q40_keyb.c + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Some configuration switches are present in the include file... */ + +#define KBD_REPORT_ERR + +/* Simple translation table for the SysRq keys */ + +#define SYSRQ_KEY 0x54 + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char q40kbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +/* Q40 uses AT scancodes - no way to change it. so we have to translate ..*/ +/* 0x00 means not a valid entry or no conversion known */ + +unsigned static char q40cl[256] = +{/* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, */ + 0x00,0x43,0x00,0x3f,0x3d,0x3b,0x3c,0x58,0x00,0x44,0x42,0x40,0x3e,0x0f,0x29,0x00, /* 0x00 - 0x0f */ + 0x00,0x38,0x2a,0x00,0x1d,0x10,0x02,0x00,0x00,0x00,0x2c,0x1f,0x1e,0x11,0x03,0x00, /* 0x10 - 0x1f */ + 0x00,0x2e,0x2d,0x20,0x12,0x05,0x04,0x00,0x21,0x39,0x2f,0x21,0x14,0x13,0x06,0x00, /* 0x20 - 0x2f 'f' is at 0x2b, what is 0x28 ???*/ + 0x00,0x31,0x30,0x23,0x22,0x15,0x07,0x00,0x24,0x00,0x32,0x24,0x16,0x08,0x09,0x00, /* 0x30 - 0x3f */ + 0x00,0x33,0x25,0x17,0x18,0x0b,0x0a,0x00,0x00,0x34,0x35,0x26,0x27,0x19,0x0c,0x00, /* 0x40 - 0x4f */ + 0x00,0x00,0x28,0x00,0x1a,0x0d,0x00,0x00,0x3a,0x36,0x1c,0x1b,0x00,0x2b,0x00,0x00, /* 0x50 - 0x5f*/ + 0x00,0x56,0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x4f,0x00,0x4b,0x47,0x00,0x00,0x00, /* 0x60 - 0x6f */ + 0x52,0x53,0x50,0x4c,0x4d,0x48,0x01,0x45,0x57,0x4e,0x51,0x4a,0x37,0x49,0x46,0x00, /* 0x70 - 0x7f */ + 0x00,0x00,0x00,0x41,0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x80 - 0x8f 0x84/0x37 is SySrq*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x90 - 0x9f */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xa0 - 0xaf */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xb0 - 0xbf */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xc0 - 0xcf */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xd0 - 0xdf */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xe0 - 0xef */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xf0 - 0xff */ +}; + +/* another table, AT 0xe0 codes to PC 0xe0 codes, + 0xff special entry for SysRq - DROPPED right now */ +static unsigned char q40ecl[]= +{/* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x00 - 0x0f*/ + 0x00,0x38,0x2a,0x00,0x1d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x10 - 0x1f */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x20 - 0x2f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x30 - 0x3f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x00,0x00, /* 0x40 - 0x4f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x1c,0x00,0x00,0x00,0x00,0x00, /* 0x50 - 0x5f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0x00,0x4b,0x47,0x00,0x00,0x00, /* 0x60 - 0x6f*/ + 0x52,0x53,0x50,0x00,0x4d,0x48,0x00,0x00,0x00,0x00,0x51,0x00,0x00,0x49,0x00,0x00, /* 0x70 - 0x7f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x80 - 0x8f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x90 - 0x9f*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xa0 - 0xaf*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xb0 - 0xbf*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xc0 - 0xcf*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xd0 - 0xdf*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0xe0 - 0xef*/ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 /* 0xf0 - 0xff*/ +}; + + +spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; + + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* + * The keycodes below are randomly located in 89-95,112-118,120-127. + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* tfj@olivia.ping.dk: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ +}; + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +/* this can be changed using setkeys : */ +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +static unsigned int prev_scancode = 0; /* remember E0, E1 */ + +int q40kbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 127) + return -EINVAL; + if (scancode < 128) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 128] = keycode; + return 0; +} + +int q40kbd_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < 128) ? high_keys[scancode - SC_LIM] : + e0_keys[scancode - 128]; +} + + +#define disable_keyboard() +#define enable_keyboard() + + + +int q40kbd_pretranslate(unsigned char scancode, char raw_mode) +{ + if (scancode == 0xff) { + /* in scancode mode 1, my ESC key generates 0xff */ + /* the calculator keys on a FOCUS 9000 generate 0xff */ +#ifndef KBD_IS_FOCUS_9000 +#ifdef KBD_REPORT_ERR + if (!raw_mode) + printk(KERN_DEBUG "Keyboard error\n"); +#endif +#endif + prev_scancode = 0; + return 0; + } + + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + return 1; +} + +int q40kbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + /*printk("translate ...\n");*/ + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else if (scancode >= SC_LIM) { + /* This happens with the FOCUS 9000 keyboard + Its keys PF1..PF12 are reported to generate + 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f + Moreover, unless repeated, they do not generate + key-down events, so we have to zero up_flag below */ + /* Also, Japanese 86/106 keyboards are reported to + generate 0x73 and 0x7d for \ - and \ | respectively. */ + /* Also, some Brazilian keyboard is reported to produce + 0x73 and 0x7e for \ ? and KP-dot, respectively. */ + + *keycode = high_keys[scancode - SC_LIM]; + + if (!*keycode) { + if (!raw_mode) { +#ifdef KBD_REPORT_UNKN + printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" + " - ignored\n", scancode); +#endif + } + return 0; + } + } else + *keycode = scancode; + return 1; +} + +char q40kbd_unexpected_up(unsigned char keycode) +{ + /* unexpected, but this can happen: maybe this was a key release for a + FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */ + if (keycode >= SC_LIM || keycode == 85) + return 0; + else + return 0200; +} + +static int keyup=0; +static int qprev=0; + +static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + unsigned char status; + + disable_keyboard(); + spin_lock_irqsave(&kbd_controller_lock, flags); + kbd_pt_regs = regs; + + status = IRQ_KEYB_MASK & master_inb(INTERRUPT_REG); + if (status ) + { + unsigned char scancode,qcode; + + qcode = master_inb(KEYCODE_REG); + + if (qcode != 0xf0) + { + if (qcode == 0xe0) + { + qprev=0xe0; + handle_scancode(qprev); + goto exit; + } + + scancode=qprev ? q40ecl[qcode] : q40cl[qcode]; +#if 0 +/* next line is last resort to hanlde some oddities */ + if (qprev && !scancode) scancode=q40cl[qcode]; +#endif + qprev=0; + if (!scancode) + { + printk("unknown scancode %x\n",qcode); + goto exit; + } + if (scancode==0xff) /* SySrq */ + scancode=SYSRQ_KEY; + + handle_scancode(scancode | (keyup ? 0200 : 0)); + keyup=0; + mark_bh(KEYBOARD_BH); + + } + else + keyup=1; + } +exit: + spin_unlock_irqrestore(&kbd_controller_lock, flags); + master_outb(-1,KEYBOARD_UNLOCK_REG); /* keyb ints reenabled herewith */ + enable_keyboard(); +} + + + + +#ifdef CONFIG_MAGIC_SYSRQ +int kbd_is_sysrq(unsigned char keycode) +{ + return( keycode == SYSRQ_KEY ); +} +#endif /* CONFIG_MAGIC_SYSRQ */ + + + + +#define KBD_NO_DATA (-1) /* No data */ +#define KBD_BAD_DATA (-2) /* Parity or other error */ + +static int __init kbd_read_input(void) +{ + int retval = KBD_NO_DATA; + unsigned char status; + + status = IRQ_KEYB_MASK & master_inb(INTERRUPT_REG); + if (status) { + unsigned char data = master_inb(KEYCODE_REG); + + retval = data; + master_outb(-1,KEYBOARD_UNLOCK_REG); + } + return retval; +} + +extern void q40kbd_leds(unsigned char leds) +{ /* nothing can be done */ } + +static void __init kbd_clear_input(void) +{ + int maxread = 100; /* Random number */ + + do { + if (kbd_read_input() == KBD_NO_DATA) + break; + } while (--maxread); +} + + +void __init q40kbd_init_hw(void) +{ +#if 0 + /* Get the keyboard controller registers (incomplete decode) */ + request_region(0x60, 16, "keyboard"); +#endif + /* Flush any pending input. */ + kbd_clear_input(); + + /* Ok, finally allocate the IRQ, and off we go.. */ + request_irq(Q40_IRQ_KEYBOARD, keyboard_interrupt, 0, "keyboard", NULL); + master_outb(-1,KEYBOARD_UNLOCK_REG); + master_outb(1,KEY_IRQ_ENABLE_REG); + +} + diff --git a/drivers/char/ser_hpdca.c b/drivers/char/ser_hpdca.c new file mode 100644 index 000000000000..d4378c15fd67 --- /dev/null +++ b/drivers/char/ser_hpdca.c @@ -0,0 +1,997 @@ +/* + * Driver for the 98626/98644/internal serial interface on hp300/hp400 + * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs) + * + * This driver was written by Peter Maydell + * based on informaition gleaned from the NetBSD driver and the ser_ioext + * driver. Copyright(C) Peter Maydell 05/1998. + * The most significant difference between us and ser_ioext is that + * we have to worry about UARTs with no FIFO (ignoring the Amiga vs HP differences...) + * + * We worry about multiple boards in a system because ser_ioext does and + * it seems the sensible thing to do. It's untested, though. Also, are we + * duplicating work done by the hardware-independent m68k serial layer? + * (multiple devices seems like an obvious thing to go for...) + * + * The driver is called hpdca because the NetBSD driver is 'dca' and + * I wanted something less generic than hp300... + * + * N.B. On the hp700 and some hp300s, there is a "secret bit" with + * undocumented behavior. The third bit of the Modem Control Register + * (MCR_IEN == 0x08) must be set to enable interrupts. Failure to do + * so can result in deadlock on those machines, whereas there don't seem to + * be any harmful side-effects from setting this bit on non-affected + * machines. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include /* hwreg_present() */ +#include /* readb(), writeb() */ +#include + +#include "ser_hpdca.h" + +/* Set these to 0 when the driver is finished */ +#define HPDCA_DEBUG 1 +#define DEBUG_IRQ 1 +#define DEBUG 1 +#define DEBUG_FLOW 1 + +#define FIFO_TRIGGER_LEVEL FIFO_TRIG_8 + +/***************************** Prototypes *****************************/ +static void hpdca_ser_interrupt(volatile struct uart_16c550 *uart, int line, + int hasfifo, int *spurious_count); +static void ser_init( struct m68k_async_struct *info ); +static void ser_deinit( struct m68k_async_struct *info, int leave_dtr ); +static void ser_enab_tx_int( struct m68k_async_struct *info, int enab_flag ); +static int ser_check_custom_divisor(struct m68k_async_struct *info, + int baud_base, int divisor); +static void ser_change_speed( struct m68k_async_struct *info ); +static void ser_throttle( struct m68k_async_struct *info, int status ); +static void ser_set_break( struct m68k_async_struct *info, int break_flag ); +static void ser_get_serial_info( struct m68k_async_struct *info, + struct serial_struct *retinfo ); +static unsigned int ser_get_modem_info( struct m68k_async_struct *info ); +static int ser_set_modem_info( struct m68k_async_struct *info, int new_dtr, + int new_rts ); +static void ser_stop_receive(struct m68k_async_struct *info); +static int ser_trans_empty(struct m68k_async_struct *info); +/************************* End of Prototypes **************************/ + +/* + * SERIALSWITCH structure for the dca + */ + +static SERIALSWITCH hpdca_ser_switch = { + ser_init, + ser_deinit, + ser_enab_tx_int, + ser_check_custom_divisor, + ser_change_speed, + ser_throttle, + ser_set_break, + ser_get_serial_info, + ser_get_modem_info, + ser_set_modem_info, + NULL, + ser_stop_receive, ser_trans_empty, NULL +}; + +/* table of divisors to use for various baud rates. DCABRD() is a macro + * which gives the correct ratio for the clock speed (hp300 vs hp700) + */ +static int hpdca_baud_table[16] = { + /* B0 */ 0, /* Never use this value !!! */ + /* B50 */ DCABRD(50), + /* B75 */ DCABRD(75), + /* B110 */ DCABRD(110), + /* B134 */ DCABRD(134), + /* B150 */ DCABRD(150), + /* B200 */ DCABRD(200), + /* B300 */ DCABRD(300), + /* B600 */ DCABRD(600), + /* B1200 */ DCABRD(1200), + /* B1800 */ DCABRD(1800), + /* B2400 */ DCABRD(2400), + /* B4800 */ DCABRD(4800), + /* B9600 */ DCABRD(9600), + /* B19200 */ DCABRD(19200), + /* B38400 */ DCABRD(38400) /* The last of the standard rates. */ +}; + +int hpdca_num; +hpdcaInfoType hpdca_info[MAX_HPDCA]; + +/* + * Functions + * + * dev_id = hpdca_info. + * + */ +static void hpdca_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* We have to take this interrupt and work out which board it's for. */ + int b; + + if (hpdca_num == 0) { + /* This interrupt can't be for us */ + return; + } + + for (b = 0; b < hpdca_num; b++) { + hpdca_struct *board = hpdca_info[b].board; + hpdcaInfoType *board_info = &hpdca_info[b]; + + if ((board->dca_ic & (IC_IR|IC_IE)) != (IC_IR|IC_IE)) + /* interrupts not enabled, not for us */ + continue; + + /* Service any uart irqs. */ + hpdca_ser_interrupt(board_info->uart, board_info->line, + board_info->hasfifo, &board_info->spurious_count); + + if (board_info->spurious_count > 10000) { + board->dca_ic &= ~IC_IE; + printk("Too many spurious interrupts, disabling board irq\n"); + board_info->spurious_count = 0; + } + } /* for b */ +} + +/* +** hpdca_ser_interrupt() +** +** Check for and handle interrupts for this uart. +*/ +static void hpdca_ser_interrupt(volatile struct uart_16c550 *uart, int line, + int hasfifo, int *spurious_count) +{ + struct m68k_async_struct *info = rs_table + line; + + u_char iir; + u_char lsr; + int ch; + + iir = uart->IIR; + + ++*spurious_count; + +#if DEBUG_IRQ + printk("ttyS%d: IER=%02X, IIR=%02X, LSR=%02X, MSR=%02X\n", + line, uart->IER, iir, uart->LSR, uart->MSR); +#endif + + while (!(iir & IRQ_PEND)) { + /* IRQ for this uart */ +#if DEBUG_IRQ + printk("IRQ_PEND on ttyS%d...\n", line); +#endif + + /* This really is our interrupt */ + *spurious_count = 0; + + switch (iir & (IRQ_ID1 | IRQ_ID2 | IRQ_ID3)) { + case IRQ_RLS: /* Receiver Line Status */ +#if DEBUG_FLOW + printk("RLS irq on ttyS%d\n", line); +#endif + case IRQ_CTI: /* Character Timeout */ + case IRQ_RDA: /* Received Data Available */ +#if DEBUG_IRQ + printk("irq (IIR=%02X) on ttyS%d\n", line, iir); +#endif + /* + * Copy chars to the tty-queue ... + * Be careful that we aren't passing one of the + * Receiver Line Status interrupt-conditions without noticing. + */ + if (!hasfifo) + { + /* with no FIFO reads are trivial */ + ch = uart->RBR; + rs_receive_char(info, ch, 0); +#ifdef DEBUG_IRQ + printk("Read a char from FIFOless uart: %02X\n", ch); +#endif + } + else + { + int got = 0; + + lsr = uart->LSR; +#if DEBUG_IRQ + printk("uart->LSR & DR = %02X\n", lsr & DR); +#endif + while (lsr & DR) { + u_char err = 0; + ch = uart->RBR; +#if DEBUG_IRQ + printk("Read a char from the uart: %02X, lsr=%02X\n", + ch, lsr); +#endif + if (lsr & BI) { + err = TTY_BREAK; + } + else if (lsr & PE) { + err = TTY_PARITY; + } + else if (lsr & OE) { + err = TTY_OVERRUN; + } + else if (lsr & FE) { + err = TTY_FRAME; + } +#if DEBUG_IRQ + printk("rs_receive_char(ch=%02X, err=%02X)\n", ch, err); +#endif + rs_receive_char(info, ch, err); + got++; + lsr = uart->LSR; + } +#if DEBUG_FLOW + printk("[%d<]", got); +#endif + } + break; + + case IRQ_THRE: /* Transmitter holding register empty */ + { + int fifo_space = (hasfifo ? 16 : 1); /* no FIFO => send one char */ + int sent = 0; + +#if DEBUG_IRQ + printk("THRE-irq for ttyS%d\n", line); +#endif + + /* If the uart is ready to receive data and there are chars in */ + /* the queue we transfer all we can to the uart's FIFO */ + if (info->xmit_cnt <= 0 || info->tty->stopped || + info->tty->hw_stopped) { + /* Disable transmitter empty interrupt */ + uart->IER &= ~(ETHREI); + /* Need to send a char to acknowledge the interrupt */ + uart->THR = 0; +#if DEBUG_FLOW + if (info->tty->hw_stopped) { + printk("[-]"); + } + if (info->tty->stopped) { + printk("[*]"); + } +#endif + break; + } + + /* Handle software flow control */ + if (info->x_char) { +#if DEBUG_FLOW + printk("[^%c]", info->x_char + '@'); +#endif + uart->THR = info->x_char; + info->x_char = 0; + fifo_space--; + sent++; + } + + /* Fill the fifo */ + while (fifo_space > 0) { + fifo_space--; +#if DEBUG_IRQ + printk("Sending %02x to the uart.\n", + info->xmit_buf[info->xmit_tail]); +#endif + uart->THR = info->xmit_buf[info->xmit_tail++]; + sent++; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--info->xmit_cnt == 0) { + break; + } + } +#if DEBUG_FLOW + printk("[>%d]", sent); +#endif + + if (info->xmit_cnt == 0) { +#if DEBUG_IRQ + printk("Sent last char - turning off THRE interrupts\n"); +#endif + /* Don't need THR interrupts any more */ + uart->IER &= ~(ETHREI); + } + + if (info->xmit_cnt < WAKEUP_CHARS) { + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + } + } + break; + + case IRQ_MS: /* Must be modem status register interrupt? */ + { + u_char msr = uart->MSR; +#if DEBUG_IRQ + printk("MS-irq for ttyS%d: %02x\n", line, msr); +#endif + + if (info->flags & ASYNC_INITIALIZED) { + if (msr & DCTS) { + rs_check_cts(info, (msr & CTS)); /* active high */ +#if DEBUG_FLOW + printk("[%c-%d]", (msr & CTS) ? '^' : 'v', + info->tty ? info->tty->hw_stopped : -1); +#endif + } + if (msr & DDCD) { +#if DEBUG + printk("rs_dcd_changed(%d)\n", !(msr & DCD)); +#endif + rs_dcd_changed(info, !(msr & DCD)); /* active low */ + } + } + } + break; + + default: +#if DEBUG_IRQ + printk("Unexpected irq for ttyS%d\n", line); +#endif + break; + } /* switch (iir) */ + iir = uart->IIR; + } /* while IRQ_PEND */ +} + +static void ser_init(struct m68k_async_struct *info) +{ +#if DEBUG + printk("ser_init\n"); +#endif + + while ((curruart(info)->LSR) & DR) { +#if HPDCA_DEBUG + printk("Emptying uart\n"); +#endif + (void)curruart(info)->RBR; + } + + /* Set DTR and RTS */ + curruart(info)->MCR |= (DTR | RTS | OUT2); + + /* Enable interrupts. IF_EXTER irq has already been enabled in hpdca_init()*/ + /* DON'T enable ETHREI here because there is nothing to send yet (murray) */ + curruart(info)->IER |= (ERDAI | ELSI | EMSI); + + MOD_INC_USE_COUNT; +} + + +static void ser_deinit(struct m68k_async_struct *info, int leave_dtr) +{ +#if DEBUG + printk("ser_deinit\n"); +#endif + + /* Wait for the uart to get empty */ + while(!(curruart(info)->LSR & TEMT)) { +#if HPDCA_DEBUG + printk("Waiting for the transmitter to finish\n"); +#endif + } + + while(curruart(info)->LSR & DR) { +#if HPDCA_DEBUG + printk("Uart not empty - flushing!\n"); +#endif + (void)curruart(info)->RBR; + } + + /* No need to disable UART interrupts since this will already + * have been done via ser_enab_tx_int() and ser_stop_receive() + */ + + ser_RTSoff(info); + if (!leave_dtr) { + ser_DTRoff(info); + } + + MOD_DEC_USE_COUNT; +} + +/* +** ser_enab_tx_int() +** +** Enable or disable tx interrupts. +** Note that contrary to popular belief, it is not necessary to +** send a character to cause an interrupt to occur. Whenever the +** THR is empty and THRE interrupts are enabled, an interrupt will occur. +** (murray) +*/ +static void ser_enab_tx_int(struct m68k_async_struct *info, int enab_flag) +{ + if (enab_flag) { + curruart(info)->IER |= ETHREI; + } + else { + curruart(info)->IER &= ~(ETHREI); + } +} + +static int ser_check_custom_divisor(struct m68k_async_struct *info, + int baud_base, int divisor) +{ + /* Always return 0 or else setserial spd_hi/spd_vhi doesn't work */ + return 0; +} + +static void ser_change_speed(struct m68k_async_struct *info) +{ + unsigned int cflag, baud, chsize, stopb, parity, aflags; + unsigned int div = 0, ctrl = 0; + +#if DEBUG + printk("ser_change_speed\n"); +#endif + + if (!info->tty || !info->tty->termios) + return; + + cflag = info->tty->termios->c_cflag; + baud = cflag & CBAUD; + chsize = cflag & CSIZE; + stopb = cflag & CSTOPB; + parity = cflag & (PARENB | PARODD); + aflags = info->flags & ASYNC_SPD_MASK; + + if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; + else + info->flags &= ~ASYNC_CTS_FLOW; + + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + +#if DEBUG + printk("Changing to baud-rate %i\n", baud); +#endif + + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + if (baud < 1 || baud > 4) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; + } + + /* Maximum speed is 38400 */ + if (baud > 15) + baud = 15; + div = hpdca_baud_table[baud]; + + if (!div) { + /* speed == 0 -> drop DTR */ +#if DEBUG + printk("Dropping DTR\n"); +#endif + ser_DTRoff(info); + return; + } + + /* + * We have to set DTR when a valid rate is chosen, otherwise DTR + * might get lost when programs use this sequence to clear the line: + * + * change_speed(baud = B0); + * sleep(1); + * change_speed(baud = Bx); x != 0 + * + * The pc-guys do this as well. + */ + ser_DTRon(info); + + if (chsize == CS8) { +#if DEBUG + printk("Setting serial word-length to 8-bits\n"); +#endif + ctrl |= data_8bit; + } + else if (chsize == CS7) { +#if DEBUG + printk("Setting serial word-length to 7-bits\n"); +#endif + ctrl |= data_7bit; + } + else if (chsize == CS6) { +#if DEBUG + printk("Setting serial word-length to 6-bits\n"); +#endif + ctrl |= data_6bit; + } + else if (chsize == CS5) { +#if DEBUG + printk("Setting serial word-length to 5-bits\n"); +#endif + ctrl |= data_5bit; + }; + + + /* If stopb is true we set STB which means 2 stop-bits - */ + /* otherwise we only get 1 stop-bit. */ + ctrl |= (stopb ? STB : 0); + + /* if parity disabled, ctrl |= 0 + * if odd parity, ctrl |= PEN + * if even parity, ctrl |= PEN|EPS + * Not my code -- PMM :-> + */ + ctrl |= ((parity & PARENB) ? + ((parity & PARODD) ? (PEN) : (PEN | EPS)) : 0x00 ); + +#if DEBUG + printk ("Storing serial-divisor %i\n", div); +#endif + + curruart(info)->LCR = (ctrl | DLAB); + + /* Store high byte of divisor */ + curruart(info)->DLM = ((div >> 8) & 0xff); + + /* Store low byte of divisor */ + + curruart(info)->DLL = (div & 0xff); + + curruart(info)->LCR = ctrl; +} + + +static void ser_throttle(struct m68k_async_struct *info, int status){ + +#if DEBUG + printk("ser_throttle\n"); +#endif + if (status){ + ser_RTSoff(info); + } + else{ + ser_RTSon(info); + } +} + + +static void ser_set_break(struct m68k_async_struct *info, int break_flag) +{ +#if HPDCA_DEBUG + printk("ser_set_break\n"); +#endif + if (break_flag) + curruart(info)->LCR |= SET_BREAK; + else + curruart(info)->LCR &= ~SET_BREAK; +} + + +static void ser_get_serial_info(struct m68k_async_struct *info, + struct serial_struct *retinfo) +{ + int b; + +#if DEBUG + printk("ser_get_serial_info\n"); +#endif + + retinfo->baud_base = HPDCA_BAUD_BASE; + /* This field is currently ignored by the upper layers of + * the serial driver, but we set it anyway :-> + * Is this really the best way to find out which board our caller + * is referring to??? + */ + for (b = 0; b < hpdca_num; b++) + if (hpdca_info[b].uart == curruart(info)) + retinfo->xmit_fifo_size = (hpdca_info[b].hasfifo ? 16 : 1); + + retinfo->custom_divisor = info->custom_divisor; +} + +static unsigned int ser_get_modem_info(struct m68k_async_struct *info) +{ + unsigned char msr, mcr; + +#if DEBUG + printk("ser_get_modem_info\n"); +#endif + + msr = curruart(info)->MSR; + mcr = curruart(info)->MCR; /* The DTR and RTS are located in the */ + /* ModemControlRegister ... */ + + return( + ((mcr & DTR) ? TIOCM_DTR : 0) | + ((mcr & RTS) ? TIOCM_RTS : 0) | + + ((msr & DCD) ? 0 : TIOCM_CAR) | /* DCD is active low */ + ((msr & CTS) ? TIOCM_CTS : 0) | + ((msr & DSR) ? TIOCM_DSR : 0) | + ((msr & RING_I) ? TIOCM_RNG : 0) + ); +} + +static int ser_set_modem_info(struct m68k_async_struct *info, int new_dtr, + int new_rts) +{ +#if DEBUG + printk("ser_set_modem_info new_dtr=%i new_rts=%i\n", new_dtr, new_rts); +#endif + if (new_dtr == 0) + ser_DTRoff(info); + else if (new_dtr == 1) + ser_DTRon(info); + + if (new_rts == 0) + ser_RTSoff(info); + else { + if (new_rts == 1) + ser_RTSon(info); + } + + return 0; +} + +static void ser_stop_receive (struct m68k_async_struct *info) +{ + /* Disable uart receive and status interrupts */ + curruart(info)->IER &= ~(ERDAI | ELSI | EMSI); +} + +static int ser_trans_empty (struct m68k_async_struct *info) +{ + return (curruart(info)->LSR & THRE); +} + +/* +** init_hpdca_uart(): init the uart. Returns 1 if UART has a FIFO, 0 otherwise +** +*/ +static int init_hpdca_uart(struct uart_16c550 *uart) +{ + /* Wait for the uart to get empty */ + while (!(uart->LSR & TEMT)) { +#if HPDCA_DEBUG + printk("Waiting for transmitter to finish\n"); +#endif + } + + /* + * Disable all uart interrups (they will be re-enabled in + * ser_init when they are needed). + */ + uart->IER = 0; + /* Master interrupt enable - National semconductors doesn't like + * to follow standards, so their version of the chip is + * different and I only had a copy of their data-sheets :-( + */ + uart->MCR = OUT2; + + /* + * Set the uart to a default setting of 8N1 - 9600 + */ + uart->LCR = (data_8bit | DLAB); + uart->DLM = 0; + uart->DLL = 48; + uart->LCR = (data_8bit); + + /* Enable + reset the tx and rx FIFO's. Set the rx FIFO-trigger count. + * If we then find that the bits in IIR that indicate enabling of + * the FIFOs are zero, we conclude that this uart has no FIFOs. + */ + uart->FCR = (FIFO_ENA | RCVR_FIFO_RES | XMIT_FIFO_RES | FIFO_TRIGGER_LEVEL); + udelay(100); /* wait for uart to get its act together */ + return (uart->IIR & (FIFO_ENA1 | FIFO_ENA0)); +} + +#ifdef CONFIG_SERIAL_CONSOLE + +/* + * Support for a serial console on the DCA port. We used to do this in config.c + * so it got set up early (and even if you didn't ask for serial console), but we + * don't any more. (PB) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * [I don't believe this!-- PMM] + * + * However, we still want it set up earlier than the normal serial device driver, + * so I've added a hook in m68kserial.c's serial_console_init() which is IMHO the + * right place for it. -- PMM + */ +static volatile struct uart_16c550 *uart = NULL; +static int conscode; + +/* Warning: we get called very early, so you can't allocate memory! + * [we could pass this function kmem_start, kmem_end if necessary] + */ +__initfunc(int hpdca_init_serial_console(int cflag)) +{ + /* here cflag encodes rqd baud rate, parity, etc */ + int baud = cflag & CBAUD; + int ctrl = 0; + int div, scode; + u_char stat; + int dly; + + switch (cflag & CSIZE) + { + case CS5: + ctrl = data_5bit; + break; + case CS6: + ctrl = data_6bit; + break; + case CS7: + ctrl = data_7bit; + break; + case CS8: + ctrl = data_8bit; + break; + } + + ctrl |= ((cflag & PARENB) ? ((cflag & PARODD) ? 0x8 : 0x18) : 0x00); + + if (baud < B1200 || baud > B38400) + baud = B9600; /* sensible default for unimplemented rates */ + + div = hpdca_baud_table[baud]; + /* Unfortunately we have to do a complete scan of the DIO bus in + * order to locate the serial port... + */ + for (scode = 0; scode < DIO_SCMAX; ++scode) + { + u_char *va; + u_char id; + + /* skip the hole and the internal HPIB controller */ + if (DIO_SCINHOLE(scode) || DIO_ISIHPIB(scode)) + continue; + + va = dio_scodetoviraddr(scode); + if (!va) + continue; + + if (!hwreg_present(va + DIO_IDOFF)) /* nothing there */ + continue; + + id = DIO_ID(va); /* get the ID byte */ + + if (id != DIO_ID_DCA0 && id != DIO_ID_DCA0REM + && id != DIO_ID_DCA1 && id != DIO_ID_DCA1REM) + continue; + + printk("Console is HP DCA at select code %d\n", scode); + /* OK, this is the DCA, set it up */ + writeb(0xff, va); /* DCA reset */ + /* should udelay(100) here */ + for(dly = 0; dly < 10000; dly++) + barrier(); /* defeat GCC optimisation! */ + + uart = (struct uart_16c550 *)(va+16); /* offset of the UART on the board */ + conscode = scode; /* save so we don't init this board again */ + + break; + } + + if (!uart) /* no DCA detected, abort */ + return 0; + + uart->IER = 0; /* disable interrupts */ + uart->MCR = OUT2; /* master interrupt enable */ + uart->LCR = (ctrl | DLAB); /* usual messing around to set divisor */ + uart->DLM = ((div >> 8) & 0xff); + uart->DLL = (div & 0xff); + uart->LCR = ctrl; + for(dly = 0; dly < 10000; dly++) + barrier(); /* defeat GCC optimisation! */ + stat = uart->IIR; /* ack any interrupts raised */ + + return 1; /* success */ +} + +static inline void hpdca_out(char c) +{ + u_char stat; + int timo; + + timo = 50000; + while (!(uart->LSR & THRE) && --timo) + barrier(); + uart->THR = c; + timo = 1500000; + while (!(uart->LSR & THRE) && --timo) /* wait for Tx to complete */ + barrier(); + stat = uart->IIR; /* ack any generated interrupts */ +} + + +void hpdca_serial_console_write(struct console *co, const char *str, + unsigned int count) +{ + while (count--) { + if (*str == '\n') + hpdca_out('\r'); + hpdca_out(*str++); + } +} + +int hpdca_serial_console_wait_key(struct console *co) +{ + u_char stat; + + while(!(uart->LSR & DR)) + barrier(); + stat = uart->IIR; /* clear any generated ints */ + return(uart->RBR); +} + +#ifdef UNDEF +static struct console hpdca_console_driver = { + "debug", + hpdca_serial_console_write, + NULL, /* read */ + NULL, /* device */ + hpdca_serial_console_wait_key, + NULL, /* unblank */ + NULL, /* setup */ + CON_PRINTBUFFER, + -1, + 0, + NULL +}; +#endif /* UNDEF */ +#endif /* CONFIG_SERIAL_CONSOLE */ + +/* + * Detect and initialize all HP dca boards in this system. + */ +__initfunc(int hpdca_init(void)) +{ + int isr_installed = 0; + unsigned int scode = 0; + static char support_string[50] = "HP 98644A dca serial"; + int ipl; + + if (!MACH_IS_HP300) + return -ENODEV; + + memset(hpdca_info, 0, sizeof(hpdca_info)); + + for (;;) { + hpdca_struct *board; + int line; + struct uart_16c550 *uart; + struct serial_struct req; + + /* We detect boards by looking for DIO boards which match a + * given subset of IDs. dio_find() returns the board's scancode. + * The scancode to physaddr mapping is a property of the hardware, + * as is the scancode to IPL (interrupt priority) mapping. + */ + scode = dio_find(DIO_ID_DCA0); + if (!scode) + scode = dio_find(DIO_ID_DCA0REM); + if (!scode) + scode = dio_find(DIO_ID_DCA1); + if (!scode) + scode = dio_find(DIO_ID_DCA1REM); + if (!scode) + break; /* no, none at all */ + +#ifdef HPDCA_DEBUG + printk("Found DCA scode %d",scode); +#endif + board = (hpdca_struct *) dio_scodetoviraddr(scode); + ipl = dio_scodetoipl(scode); +#ifdef HPDCA_DEBUG + printk(" at viraddr %08lX, ipl %d\n",(u_long)board, ipl); +#endif + hpdca_info[hpdca_num].board = board; + hpdca_info[hpdca_num].scode = scode; +#ifdef CONFIG_SERIAL_CONSOLE + if (scode == conscode) + { + printk("Whoops, that's the console!\n"); + dio_config_board(scode); + /* What are we actually supposed to do here??? */ + continue; + } +#endif + /* Reset the DCA */ + board->dca_reset = 0xff; + udelay(100); + + /* Register the serial port device. */ + uart = &board->uart; + + req.line = -1; /* first free ttyS? device */ + req.type = SER_HPDCA; + req.port = (int)uart; /* yuck */ + if ((line = register_serial(&req)) < 0) { + printk( "Cannot register HP dca serial port: no free device\n" ); + break; + } + + /* Add this uart to our hpdca_info[] table */ + hpdca_info[hpdca_num].line = line; + hpdca_info[hpdca_num].uart = uart; + + rs_table[line].sw = &hpdca_ser_switch; + + /* We don't use these values */ + rs_table[line].nr_uarts = 0; + rs_table[line].board_base = board; + + /* init the UART proper and find out if it's got a FIFO */ + hpdca_info[hpdca_num].hasfifo = init_hpdca_uart(uart); + + /* Install ISR if it hasn't been installed already */ + if (!isr_installed) { + request_irq(ipl, hpdca_interrupt, 0, + support_string, hpdca_info); + isr_installed++; + } + hpdca_num++; + + /* tell the DIO code that this board is configured */ + dio_config_board(scode); + } + + return(0); +} + +#ifdef MODULE +int init_module(void) +{ + return(hpdca_init()); +} + +void cleanup_module(void) +{ + int i; + + for (i = 0; i < hpdca_num; i++) { + hpdca_struct *board = hpdca_info[i].board; + int j; + + /* Disable board-interrupts */ + board->dca_ic &= ~IC_IE; + + /* Disable "master" interrupt select on uart */ + board->uart.MCR = 0; + + /* Disable all uart interrupts */ + board->uart.IER = 0; + + unregister_serial(hpdca_info[i].line); + + dio_unconfig_board(board->scode); + } + + if (hpdca_num != 0) { + /* Indicate to the IRQ handler that nothing needs to be serviced */ + hpdca_num = 0; + free_irq(dio_scodetoipl(hpdca_info[0].scode), hpdca_info); + } +} +#endif diff --git a/drivers/char/ser_hpdca.h b/drivers/char/ser_hpdca.h new file mode 100644 index 000000000000..f053e0a37b9d --- /dev/null +++ b/drivers/char/ser_hpdca.h @@ -0,0 +1,91 @@ +/* + * Defines for the 98626/98644/internal serial interface on hp300/hp400 + * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs) + * + * This driver was written by Peter Maydell + * based on informaition gleaned from the NetBSD driver and the ser_ioext + * driver. Copyright(C) 05/1998. + * + * The driver is called hpdca because the NetBSD driver is 'dca' and + * I wanted something less generic than hp300... + * + * The NetBSD driver works for hp700 as well. I don't have one so I + * took the easy route and dropped support. However, this might be a good + * place to start if you're adding it back in again... + * + * N.B. On the hp700 and some hp300s, there is a "secret bit" with + * undocumented behavior. The third bit of the Modem Control Register + * (MCR_IEN == 0x08) must be set to enable interrupts. Failure to do + * so can result in deadlock on those machines, whereas there don't seem to + * be any harmful side-effects from setting this bit on non-affected + * machines. + */ + +#ifndef _SER_HPDCA_H_ +#define _SER_HPDCA_H_ + +/* 16bit baud rate divisor (lower byte in dca_data, upper in dca_ier). + * NB:the hp300 constant is for a clk frequency of 2.4576 + * ie HPDCA_BAUD_BASE = 2457600 / 16 + */ +#define HPDCA_BAUD_BASE 153600 +#define DCABRD(x) (HPDCA_BAUD_BASE / (x)) + +/* interface reset/id (300 only) */ +#define DCAID0 0x02 +#define DCAREMID0 0x82 +#define DCAID1 0x42 +#define DCAREMID1 0xC2 + +/* interrupt control (300 only) */ +#define DCAIPL(x) ((((x) >> 4) & 3) + 3) +#define IC_IR 0x40 +#define IC_IE 0x80 + +/* the 16c552 is two 16c550s and a parallel port, so its definitions + * of various register bits are OK for us. + */ +#include + +/* This is what the register layout looks like: */ +typedef struct +{ + /* card registers */ + u_char gap0; + volatile u_char dca_id; /* (read) */ +#define dca_reset dca_id /* (write) */ + u_char gap1; + volatile u_char dca_ic; /* interrupt control */ + u_char gap2; + volatile u_char dca_ocbrc; + u_char gap3; + volatile u_char dca_lcsm; + u_char gap4[8]; + /* chip registers */ + struct uart_16c550 uart; +} hpdca_struct; + +/* purely because the IOEXT code I'm copying uses 5 :-> */ +#define MAX_HPDCA 5 + +typedef struct +{ + volatile struct uart_16c550 *uart; + int hasfifo; /* does this UART have a FIFO? */ + hpdca_struct *board; + int scode; /* select code of this board */ + int spurious_count; + int line; +} hpdcaInfoType; + +extern int hpdca_num; /* number of detected boards */ +extern hpdcaInfoType hpdca_info[MAX_HPDCA]; + +#define curruart(info) ((struct uart_16c550 *)(info->port)) + +#define ser_DTRon(info) curruart(info)->MCR |= DTR +#define ser_RTSon(info) curruart(info)->MCR |= RTS +#define ser_DTRoff(info) curruart(info)->MCR &= ~DTR +#define ser_RTSoff(info) curruart(info)->MCR &= ~RTS + +#endif /* ndef _SER_HPDCA_H_ */ diff --git a/drivers/char/ser_hypercom1.c b/drivers/char/ser_hypercom1.c new file mode 100644 index 000000000000..35f8e451d163 --- /dev/null +++ b/drivers/char/ser_hypercom1.c @@ -0,0 +1,658 @@ +#define HYPERCOM1_VERSION 1 +#define HYPERCOM1_REV 13 +#define HYPERCOM1_DATE "8.7.99" +/* + * HyperCOM1 driver for LinuxAPUS/M68K + * Project started 25-May-1999 by Gordon Huby + * Email: + * --------------------------------------------------------------------------- + * This code is based on ser_ioext.c by Jes Sorensen (jds@kom.auc.dk) + * and ser_whippet.c by Chris Sumner (chris@cpsumner.freeserve.co.uk) + * --------------------------------------------------------------------------- + * $Id: ser_hypercom1.c,v 1.13 1999/07/08 17:41:48 gordon Exp $ + * --------------------------------------------------------------------------- + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + * --------------------------------------------------------------------------- + */ + +/* #include */ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ser_hypercom1.h" + +/* comment out for no debuging */ +/* #define HYPERCOM1_DEBUG 1 */ + +/* port number */ +#ifndef SER_HYPERCOM1 +#define SER_HYPERCOM1 113 +#endif + +/* Some usefull macros */ +#define ser_DTRon(uart) ((uart->MCR) |= MCR_DTR) +#define ser_DTRoff(uart) ((uart->MCR) &= ~(MCR_DTR)) +#define ser_RTSon(uart) ((uart->MCR) |= MCR_RTS) +#define ser_RTSoff(uart) ((uart->MCR) &= ~(MCR_RTS)) + +/***************************** Prototypes *****************************/ +static void ser_init(struct m68k_async_struct *info); +static void ser_deinit(struct m68k_async_struct *info,int leave_dtr); +static void ser_enab_tx_int(struct m68k_async_struct *info,int enab_flag); +static int ser_check_custom_divisor(struct m68k_async_struct *info,int baud_base,int divisor); +static void ser_change_speed(struct m68k_async_struct *info); +static void ser_throttle(struct m68k_async_struct *info,int status); +static void ser_break(struct m68k_async_struct *info,int break_flag); +static void ser_get_serial_info(struct m68k_async_struct *info,struct serial_struct *retinfo); +static unsigned int ser_get_modem_info(struct m68k_async_struct *info); +static int ser_set_modem_info(struct m68k_async_struct *info,int new_dtr,int new_rts); +static int ser_ioctl(struct tty_struct *tty,struct file *file, + struct m68k_async_struct *info,unsigned int cmd,unsigned long arg); +static void ser_stop_receive(struct m68k_async_struct *info); +static int ser_trans_empty(struct m68k_async_struct *info); +/************************* End of Prototypes **************************/ + +/* + * SERIALSWITCH structure for HyperCOM1 serial port + */ + +static SERIALSWITCH hypercom1_ser_switch = { + ser_init, + ser_deinit, + ser_enab_tx_int, + ser_check_custom_divisor, + ser_change_speed, + ser_throttle, + ser_break, + ser_get_serial_info, + ser_get_modem_info, + ser_set_modem_info, + ser_ioctl, + ser_stop_receive, + ser_trans_empty, + NULL, +}; + +static u_int hypercom1_baud_table[20] = { + 0, + 50, + 75, + 110, + 134, + 150, + 200, + 300, + 600, + 1200, + 1800, + 2400, + 4800, + 9600, + 19200, + 38400, /* The last of the standard rates. */ + 57600, /* ASYNC_SPD_HI */ + 115200, /* ASYNC_SPD_VHI */ + 230400, /* ASYNC_SPD_SHI */ + 480600, /* ASYNC_SPD_WARP */ +}; + +/***************************************************************************/ +static const u_char fifo_trigger_level = TX_FIFO_TRIG_16 | RX_FIFO_TRIG_28; /* changeable */ +/*-------------------------------------------------------------------------*/ +static struct STARTECH_16C650 *hypercom1; +static struct m68k_async_struct *amiga_info; +static int line; +/***************************************************************************/ + +/* + * ser_interrupt() + */ +static void ser_interrupt(int irq, void *data, struct pt_regs *regs) +{ + struct m68k_async_struct *info = data; + u_char lsr,isr,ier; + +#ifdef HYPERCOM1_DEBUG + printk("ser_interrupt()\n"); +#endif + isr = hypercom1->ISR; + ier = hypercom1->IER; + + while (!(isr & IRQ_PEND)) { /* loop until no more ints */ + + switch (isr & IRQ_MASK) { + case IRQ_RLS: /* Receiver Line Status */ + case IRQ_RDTO: /* Receiver Data Timeout */ + case IRQ_RDR: /* Receiver Data Ready */ + /* + * Copy chars to the tty-queue ... + * Be careful that we aren't passing one of the + * Receiver Line Status interrupt-conditions without noticing. + */ + { + int ch; + + lsr = hypercom1->LSR; + while (lsr & LSR_RDR) { + u_char err; + ch = hypercom1->RHR; + + if (lsr & LSR_BI) err = TTY_BREAK; + else if (lsr & LSR_PE) err = TTY_PARITY; + else if (lsr & LSR_ORE) err = TTY_OVERRUN; + else if (lsr & LSR_FE) err = TTY_FRAME; + else err = 0; + + rs_receive_char(info, ch, err); + lsr = hypercom1->LSR; + } + } + break; + case IRQ_THRE: /* Transmitter holding register empty */ + { + int fifo_space = FIFO_SIZE; + int ch; + + while (fifo_space > 0) { + fifo_space--; + if ((ch = rs_get_tx_char(info)) >= 0) + hypercom1->THR = (u_char)ch; + if (ch == -1 || rs_no_more_tx(info)) { + ier &= ~(IER_THR); + break; + } + } +#ifdef HYPERCOM1_DEBUG + printk("fifo_space=%d\n",fifo_space); +#endif + } + break; + case IRQ_MSR: /* Must be modem status register interrupt? */ + { + u_char msr = hypercom1->MSR; + + if (info->flags & ASYNC_INITIALIZED) { + if (msr & MSR_DCTS) { +#ifdef HYPERCOM1_DEBUG + printk("CTS = %s\n",(msr & MSR_CTS) ? "ON" : "OFF"); +#endif + rs_check_cts(info, (msr & MSR_CTS)); + } + if (msr & MSR_DCD) + rs_dcd_changed(info, (msr & MSR_CD)); + } + } + break; + + } /* switch (iir) */ + + isr = hypercom1->ISR; + } /* while IRQ_PEND */ + +/* Re-enable UART interrupts */ + hypercom1->IER = ier; +} + +/*-------------------------------------------------------------------------*/ + +/* + * ser_init() init the port as necessary, set RTS and DTR and enable interrupts + * It does not need to set the speed and other parameters, because change_speed, + * is called too. + */ + +static void ser_init(struct m68k_async_struct *info) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_init()\n"); +#endif + /* Enable DTR and RTS */ + hypercom1->MCR |= (MCR_DTR | MCR_RTS | MCR_OP2); + + hypercom1->FCR = FCR_FE | FCR_RFR | FCR_XFR | fifo_trigger_level; + +/* Enable interrupts. IF_EXTER irq has already been enabled in hypercom1_init()*/ +/* DON'T enable IER_THR here because there is nothing to send yet (murray) */ + + hypercom1->IER |= (IER_RHR | IER_RLS | IER_MSI); + + MOD_INC_USE_COUNT; +} + +/*-------------------------------------------------------------------------*/ + +/* + * deinit(): Stop and shutdown the port (e.g. disable interrupts, ...) + */ +static void ser_deinit(struct m68k_async_struct *info,int leave_dtr) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_deinit()\n"); +#endif + hypercom1->IER=0x00; + + hypercom1->FCR = FCR_FE | FCR_RFR | FCR_XFR | fifo_trigger_level; + + ser_RTSoff(hypercom1); + if (!leave_dtr) ser_DTRoff(hypercom1); + + MOD_DEC_USE_COUNT; +} + +/*-------------------------------------------------------------------------*/ + +/* + * enab_tx_int(): Enable or disable the Tx Buffer Empty interrupt + * independently from other interrupt sources. If the int is + * enabled, the transmitter should also be restarted, i.e. if there + * are any chars to be sent, they should be put into the Tx + * register. The real en/disabling of the interrupt may be a no-op + * if there is no way to do this or it is too complex. This Tx ints + * are just disabled to save some interrupts if the transmitter is + * stopped anyway. But the restarting must be implemented! + */ +static void ser_enab_tx_int(struct m68k_async_struct *info,int enab_flag) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_enab_tx_int(%s)\n",(enab_flag) ? "ON" : "OFF"); +#endif + if (enab_flag) hypercom1->IER |= IER_THR; + else hypercom1->IER &= ~(IER_THR); +} + +/*-------------------------------------------------------------------------*/ + +/* + * check_custom_divisor(): Check the given custom divisor for legality + * and return 0 if OK, non-zero otherwise. + */ +static int ser_check_custom_divisor(struct m68k_async_struct *info,int baud_base,int divisor) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_check_custom_divisor()\n"); +#endif + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* + * change_speed(): Set port speed, character size, number of stop + * bits and parity from the termios structure. If the user wants + * to set the speed with a custom divisor, he is required to + * check the baud_base first! + */ +static void ser_change_speed(struct m68k_async_struct *info) +{ + u_int cflag, real_baud, baud, chsize, stopb, parity, aflags; + u_int div = 0, ctrl = 0; + +#ifdef HYPERCOM1_DEBUG + printk("ser_change_speed()\n"); +#endif + if (!info->tty || !info->tty->termios) return; + + cflag = info->tty->termios->c_cflag; + baud = cflag & CBAUD; + chsize = cflag & CSIZE; + stopb = cflag & CSTOPB; + parity = cflag & (PARENB | PARODD); + aflags = info->flags & ASYNC_SPD_MASK; + + if (cflag & CRTSCTS) info->flags |= ASYNC_CTS_FLOW; + else info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; + else info->flags |= ASYNC_CHECK_CD; + + if (baud & CBAUDEX) + baud = baud - (B57600 - B38400 -1); + +#ifdef HYPERCOM1_DEBUG + printk("Baud=%d\n",baud); +#endif + if (baud == 15) { + switch (aflags) { + case ASYNC_SPD_HI: + baud += 1; + break; + case ASYNC_SPD_VHI: + baud += 2; + break; + case ASYNC_SPD_SHI: + baud += 3; + break; + case ASYNC_SPD_WARP: + baud += 4; + break; + case ASYNC_SPD_CUST: + div = info->custom_divisor; + break; + } + } + + if (!div) { + /* Maximum speed is 460800 */ + if (baud > 19) baud = 19; + real_baud = hypercom1_baud_table[baud]; + if (real_baud==0) div=0; + else div = HYPERCOM1_BAUD_BASE/real_baud; + } + +#ifdef HYPERCOM1_DEBUG + printk("divisor=%d, baud rate=%d\n",div,real_baud); +#endif + if (!div) { + /* speed == 0 -> drop DTR */ + ser_DTRoff(hypercom1); + return; + } + +/* + * We have to set DTR when a valid rate is chosen, otherwise DTR + * might get lost when programs use this sequence to clear the line: + * + * change_speed(baud = B0); + * sleep(1); + * change_speed(baud = Bx); x != 0 + * + * The pc-guys do this aswell. + */ + ser_DTRon(hypercom1); + + if (chsize == CS8) ctrl |= data_8bit; + else if (chsize == CS7) ctrl |= data_7bit; + else if (chsize == CS6) ctrl |= data_6bit; + else if (chsize == CS5) ctrl |= data_5bit; + +/* If stopb is true we set STB which means 2 stop-bits */ +/* otherwise we only get 1 stop-bit. */ + + ctrl |= (stopb ? LCR_SB : 0); + + ctrl |= ((parity & PARENB) ? ((parity & PARODD) ? (LCR_PE) : (LCR_PE | LCR_EP)) : 0x00 ); + + hypercom1->LCR = (ctrl | LCR_DLE); + + /* Store high byte of divisor */ + hypercom1->DLM = ((div >> 8) & 0xff); + +#ifdef HYPERCOM1_DEBUG + printk("DLM = 0x%.2X\n",((div >> 8) & 0xff)); +#endif + /* Store low byte of divisor */ + hypercom1->DLL = (div & 0xff); + +#ifdef HYPERCOM1_DEBUG + printk("DLL = 0x%.2X\n",(div & 0xff)); +#endif + hypercom1->LCR = ctrl; +} + +/*-------------------------------------------------------------------------*/ + +/* + * throttle(): Set or clear the RTS line according to 'status'. + */ +static void ser_throttle(struct m68k_async_struct *info,int status) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_throttle(rts=%s)\n", (status) ? "OFF" : "ON"); +#endif + if (status) ser_RTSoff(hypercom1); + else ser_RTSon(hypercom1); +} + +/*-------------------------------------------------------------------------*/ + +/* + * set_break(): Set or clear the 'Send a Break' flag. + */ +static void ser_break(struct m68k_async_struct *info,int break_flag) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_set_break(%s)\n", (break_flag) ? "ON" : "OFF"); +#endif + if (break_flag) hypercom1->LCR |= LCR_SETB; + else hypercom1->LCR &= ~LCR_SETB; + + return; +} + +/*-------------------------------------------------------------------------*/ + +/* + * get_serial_info(): Fill in the baud_base and custom_divisor + * fields of a serial_struct. It may also modify other fields, if + * needed. + */ +static void ser_get_serial_info(struct m68k_async_struct *info,struct serial_struct *retinfo) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_get_serial_info()\n"); +#endif + retinfo->baud_base = HYPERCOM1_BAUD_BASE; + retinfo->xmit_fifo_size = FIFO_SIZE; /* This field is currently ignored, */ + /* by the upper layers of the */ + /* serial-driver. */ + retinfo->custom_divisor = info->custom_divisor; +} + +/*-------------------------------------------------------------------------*/ + +/* + * get_modem_info(): Return the status of RTS, DTR, DCD, RI, DSR and CTS. + */ +static unsigned int ser_get_modem_info(struct m68k_async_struct *info) +{ + u_char msr, mcr; + +#ifdef HYPERCOM1_DEBUG + printk("ser_get_modem()\n"); +#endif + + msr = hypercom1->MSR; + mcr = hypercom1->MCR; /* The DTR and RTS are located in the */ + /* ModemControlRegister ... */ + return ( + ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | + ((mcr & MCR_RTS) ? TIOCM_RTS : 0) | + + ((msr & MSR_CD) ? TIOCM_CAR : 0) | + ((msr & MSR_CTS) ? TIOCM_CTS : 0) | + ((msr & MSR_DSR) ? TIOCM_DSR : 0) | + ((msr & MSR_RING) ? TIOCM_RNG : 0) + ); +} + +/*-------------------------------------------------------------------------*/ + +/* + * set_modem_info(): Set the status of RTS and DTR according to + * 'new_dtr' and 'new_rts', resp. 0 = clear, 1 = set, -1 = don't change + */ +static int ser_set_modem_info(struct m68k_async_struct *info,int new_dtr,int new_rts) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_set_modem(dtr=%s, rts=%s)\n",(new_dtr == 0) ? "OFF" : "ON",(new_rts == 0) ? "OFF" : "ON"); +#endif + + if (new_dtr == 0) ser_DTRoff(hypercom1); + else if (new_dtr == 1) ser_DTRon(hypercom1); + + if (new_rts == 0) ser_RTSoff(hypercom1); + else if (new_rts == 1) ser_RTSon(hypercom1); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* + * ioctl(): Process any port-specific ioctl's. This pointer may be + * NULL, if the port has no own ioctl's. + */ +static int ser_ioctl(struct tty_struct *tty,struct file *file, + struct m68k_async_struct *info,unsigned int cmd,unsigned long arg) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_ioctl()\n"); +#endif + return -ENOIOCTLCMD; +} + +/*-------------------------------------------------------------------------*/ + +/* + * stop_receive(): Turn off the Rx part of the port, so no more characters + * will be received. This is called before shutting the port down. + */ +static void ser_stop_receive(struct m68k_async_struct *info) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_stop_receive()\n"); +#endif + /* Disable uart receive and status interrupts */ + hypercom1->IER &= ~(IER_RHR | IER_RLS | IER_MSI); +} + +/*-------------------------------------------------------------------------*/ + +/* + * trans_empty(): Return !=0 if there are no more characters still to be + * sent out (Tx buffer register and FIFOs empty) + */ +static int ser_trans_empty(struct m68k_async_struct *info) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_trans_empty()\n"); +#endif + return (hypercom1->LSR & LSR_THE); +} + +/*-------------------------------------------------------------------------*/ + +static void ser_reset_port(void) +{ +#ifdef HYPERCOM1_DEBUG + printk("ser_reset_port()\n"); +#endif + hypercom1->IER=0x00; /* disable interrupts */ + hypercom1->MCR=MCR_OP2; /* master int */ + + (void)hypercom1->ISR; + (void)hypercom1->LSR; + (void)hypercom1->MSR; + + hypercom1->LCR=LCR_DLE; + hypercom1->DLL=0x30; + hypercom1->DLM=0x00; + + hypercom1->LCR=0x03; /* 8Bits, no parity, 1 stop bits, and disable latch enable */ + hypercom1->FCR=FCR_FE | FCR_RFR | FCR_XFR | fifo_trigger_level; +} + +/*-------------------------------------------------------------------------*/ + +static int check_port(void) +{ + int port_found=0; + + /* A write to SCRATCH with any byte, selects hypercom3/3+ first uart. */ + hypercom1->SCR=0xaa; + + if (hypercom1->LSR & LSR_TE) + port_found=1; + + return port_found; +} + +/*--------------------------------------------------------------------------*/ + +int hypercom1_init(void) +{ + unsigned long flags; + struct serial_struct req; + struct m68k_async_struct *info; + +#ifdef HYPERCOM1_DEBUG + printk("hypercom1_init\n"); +#endif + if (!(MACH_IS_AMIGA)) + return -ENODEV; + + /* Check if amiga1200 */ + if (amiga_model != AMI_1200) + return -ENODEV; + + /* init hypercom1 pointer */ + hypercom1 = (struct STARTECH_16C650 *) (zTwoBase + HYPERCOM1_PHYSADDR); + + /* Check if port exists */ + printk("Probing for HyperCOM1, version %d.%d date %s\n",HYPERCOM1_VERSION,HYPERCOM1_REV,HYPERCOM1_DATE); + if (check_port() == 0) { + printk("HyperCOM1 A1200 serial port not found\n"); + return -ENODEV; + } + printk("Found HyperCOM1 A1200 serial port\n"); + + ser_reset_port(); + + req.line = -1; + req.type = SER_HYPERCOM1; + req.port = (int) &(hypercom1->RHR); + + if ((line = register_serial( &req )) < 0) { + printk("Cannot register Hypercom1 serial port: no free device\n"); + return -EBUSY; + } + info = &rs_table[line]; + info->nr_uarts = 1; + info->sw = &hypercom1_ser_switch; + + amiga_info = info; /* static variable */ + + save_flags(flags); + cli(); + + /* Install handler for EXTER interupts */ + request_irq(IRQ_AMIGA_EXTER, ser_interrupt, 0, "hypercom1 serial port", info); + + restore_flags(flags); + return 0; +} + +/************************* Module Functions ********************************/ + +#ifdef MODULE +int init_module(void) +{ + return hypercom1_init(); +} + +void cleanup_module(void) +{ +#ifdef HYPERCOM1_DEBUG + printk("Closing HyperCOM1 Device!\n"); +#endif + unregister_serial(line); + + ser_reset_port(); + + free_irq(IRQ_AMIGA_EXTER,amiga_info); +} +#endif diff --git a/drivers/char/ser_hypercom1.h b/drivers/char/ser_hypercom1.h new file mode 100644 index 000000000000..8e04d86f7ea5 --- /dev/null +++ b/drivers/char/ser_hypercom1.h @@ -0,0 +1,172 @@ +/* + * Serial driver for VMC's HyperCOM1 Serial Card for the Amiga1200. + * Copyright Gordon Huby 25-May-1999 + * Email: + * --------------------------------------------------------------------------- + * Hypercom1 == StarTech 16c650 UART + * --------------------------------------------------------------------------- + * $Id: ser_hypercom1.h,v 1.12 1999/07/08 18:51:03 gordon Exp $ + * --------------------------------------------------------------------------- + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + * --------------------------------------------------------------------------- + */ + +#ifndef _SER_HYPERCOM1_H_ +#define _SER_HYPERCOM1_H_ + +#define HYPERCOM1_CLOCK_RATE (7372800) +#define HYPERCOM1_BAUD_BASE (HYPERCOM1_CLOCK_RATE/16) +#define HYPERCOM1_PHYSADDR (0xD80021) + +#define FIFO_SIZE (32) + +#ifndef PAD_S +#define PAD_S 3 +#endif + +struct STARTECH_16C650 { + u_char RHR; /* Reciever Holding Register */ + u_char pad0[PAD_S]; + u_char IER; /* Interupt Enable Register */ + u_char pad1[PAD_S]; + u_char FCR; /* FIFO control register */ + u_char pad2[PAD_S]; + u_char LCR; /* Line control register */ + u_char pad3[PAD_S]; + u_char MCR; /* Modem Control Register */ + u_char pad4[PAD_S]; + u_char LSR; /* Line Status Register */ + u_char pad5[PAD_S]; + u_char MSR; /* Modem Status Register */ + u_char pad6[PAD_S]; + u_char SCR; /* Scratchpad Register */ +}; + +#define THR RHR /* Transmit Holding Register */ +#define ISR FCR /* Interrupt Status Register */ + +#define LSB RHR /* Divisor latch lower address. Enabled only when bit 7 of MCR = 1 */ +#define MSB IER /* Divisor latch middle address. Enabled only when bit 7 of MC1 = 1 */ + +#define DLL LSB +#define DLM MSB + +#define EFR RHR /* Enhanced Feature Register Set, Enabled only when LCR is set to 0xBF */ +#define XON1 IER +#define XON2 FCR +#define XOFF1 LCR +#define XOFF2 MCR + +/* + * BIT DEFS FOR ST 16c650 + */ + +/* Interrupt Enable Register */ /* From Page 22 of st16c650.pdf */ +#define IER_RHR (1<<0) /* Recieve Holding Register */ +#define IER_THR (1<<1) /* Transmit Holding Register */ +#define IER_RLS (1<<2) /* Recieve Line Status Interrupt */ +#define IER_MSI (1<<3) /* Modem Status Interrupt */ +#define IER_SM (1<<4) /* Sleep Mode */ +#define IER_XOI (1<<5) /* Xoff Interrupt */ +#define IER_RTS (1<<6) /* RTS Interrupt */ +#define IER_CTS (1<<7) /* CTS Interupt */ + +/* Fifo control register */ +#define FCR_FE (1<<0) /* Fifo Enable */ +#define FCR_RFR (1<<1) /* Recieve Fifo reset */ +#define FCR_XFR (1<<2) /* XMIT Fifo Reset */ +#define FCR_DMS (1<<3) /* DMA Mode Select */ +#define FCR_TX_TRIG_LSB (1<<4) /* TX Trigger LSB */ +#define FCR_TX_TRIG_MSB (1<<5) /* TX Trigger MSB */ +#define FCR_RX_TRIG_LSB (1<<6) /* RX Trigger LSB */ +#define FCR_RX_TRIG_MSB (1<<7) /* RX Trigger MSB */ + +#define TX_FIFO_TRIG_16 (0x00) +#define TX_FIFO_TRIG_8 (FCR_TX_TRIG_LSB) +#define TX_FIFO_TRIG_24 (FCR_TX_TRIG_MSB) +#define TX_FIFO_TRIG_30 (FCR_TX_TRIG_LSB | FCR_TX_TRIG_MSB) + +#define RX_FIFO_TRIG_8 (0x00) +#define RX_FIFO_TRIG_16 (FCR_RX_TRIG_LSB) +#define RX_FIFO_TRIG_24 (FCR_RX_TRIG_MSB) +#define RX_FIFO_TRIG_28 (FCR_RX_TRIG_LSB | FCR_RX_TRIG_MSB) + +/* Interupt Status Register */ +#define ISR_IS (1<<0) /* INT status */ +#define ISR_IPB0 (1<<1) /* INT priority bit 0 */ +#define ISR_IPB1 (1<<2) /* INT priority bit 1 */ +#define ISR_IPB2 (1<<3) /* INT prioroty bit 2 */ +#define ISR_IPB3 (1<<4) /* INT priority bit 3 */ +#define ISR_IPB4 (1<<5) /* INT priority bit 4 */ +#define ISR_FE1 (1<<6) /* Fifo's enabled */ +#define ISR_FE2 (1<<7) /* Fifo's enabled */ + +#define IRQ_PEND ISR_IS /* Logic 0 = pending int, Logic 1 = no pending int */ +#define IRQ_RLS (0x06) /* Reciever line status register */ +#define IRQ_RDR (0x04) /* Recieved Data Ready */ +#define IRQ_RDTO (0x0C) /* Recieve Data time out */ +#define IRQ_THRE (0x02) /* Transmitter Holding Register Empty */ +#define IRQ_MSR (0x00) /* Modem Status Register */ +#define IRQ_RXOFF (0x10) /* (Recieved Xoff signal) / Special character */ +#define IRQ_CTSRTS (0x20) /* CTS, RTS change of state */ + +#define IRQ_MASK (ISR_IPB0 | ISR_IPB1 | ISR_IPB2 | ISR_IPB3 | ISR_IPB4) + +/* Line Control Register */ +#define LCR_WLB0 (1<<0) /* Word Length Bit 0 */ +#define LCR_WLB1 (1<<1) /* Word Length Bit 1 */ +#define LCR_SB (1<<2) /* Stop Bits */ +#define LCR_PE (1<<3) /* Parity enable */ +#define LCR_EP (1<<4) /* Even Parity */ +#define LCR_SP (1<<5) /* Set Parity */ +#define LCR_SETB (1<<6) /* Set Break */ +#define LCR_DLE (1<<7) /* Divisor Latch Enable */ + +#define data_5bit (0x00) +#define data_6bit (0x01) +#define data_7bit (0x02) +#define data_8bit (0x03) + +/* Modem Control Register */ +#define MCR_DTR (1<<0) /* DTR */ +#define MCR_RTS (1<<1) /* RTS */ +#define MCR_OP1 (1<<2) /* OP1 */ +#define MCR_OP2 (1<<3) /* OP1 / IRQx enable */ +#define MCR_LB (1<<4) /* Loop Back */ +#define MCR_ITS (1<<5) /* INT type select */ +#define MCR_IRE (1<<6) /* IR enable */ +#define MCR_CS (1<<7) /* Clock Select. logic 0=normal logic 1=4*baudrate*/ + +/* Line Status Register */ +#define LSR_RDR (1<<0) /* Recieve Data Ready */ +#define LSR_ORE (1<<1) /* OverRun Error */ +#define LSR_PE (1<<2) /* Parity Error */ +#define LSR_FE (1<<3) /* Framing Error */ +#define LSR_BI (1<<4) /* Break Interupt */ +#define LSR_THE (1<<5) /* Trans Holding Empty */ +#define LSR_TE (1<<6) /* Trans Empty */ +#define LSR_FDE (1<<7) /* Fifo Data Error */ + +/* Modem Status Register */ +#define MSR_DCTS (1<<0) /* Delta CTS */ +#define MSR_DDSR (1<<1) /* Delta DSR */ +#define MSR_DRI (1<<2) /* Delta RI */ +#define MSR_DCD (1<<3) /* Delta CD */ +#define MSR_CTS (1<<4) /* CTS */ +#define MSR_DSR (1<<5) /* DSR */ +#define MSR_RING (1<<6) /* RI */ +#define MSR_CD (1<<7) /* CD */ + +/* Enhanced Feature Register */ +#define EFR_CONT0 (1<<0) /* Cont0 TX RX Control */ +#define EFR_CONT1 (1<<1) /* Cont1 TX RX Control */ +#define EFR_CONT2 (1<<2) /* Cont2 TX RX Control */ +#define EFR_CONT3 (1<<3) /* Cont3 TX RX Control */ +#define EFR_ENA (1<<4) /* Enable IER bits 4-7, ISR FCR bits 4-5, MCR bits 5-7 */ +#define EFR_SCS (1<<5) /* Special Char Select */ +#define EFR_AUTORTS (1<<6) /* Auto RTS */ +#define EFR_AUTOCTS (1<<7) /* Auto CTS */ + +#endif /* _SER_HYPERCOM1_H_ */ diff --git a/drivers/char/ser_mfc.c b/drivers/char/ser_mfc.c new file mode 100644 index 000000000000..268b244e1a9a --- /dev/null +++ b/drivers/char/ser_mfc.c @@ -0,0 +1,726 @@ + +/* serial driver for the two serial ports of a Multiface Card III + * + * Might also work with a SerialMaster or Multiface II, as far as I know + * the only difference is the frequency of the used quartz. + * Test it, and let me know if it works. Changing the baud table isn't worth a + * whole new driver. + * (To hardware hackers: It should be possible to get 115200 Baud with the older + * card by replacing the 3.6864 MHz quartz with a 7.3728 MHz one. But beware! + * The only one who knows that now effectively all baudrates are doubled is you + * and not any application. Besides, you should know what you are doing and you + * do it at your own risk.) + * + * Due to hardware constraints I decided to make the two ports of the card different. + * Port0 (the 9-pin-one) is only able to handle the following baud rates: + * 150, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400 + * (MFC II and Serial Master can do all standard rates.) + * Port1 (the 25-pin-one) can handle any approbriate custom baud rate. + * Tell me if you would prefer it the other way and why. Perhaps I'll make it + * configurable. + * + * Another solution would be to handle both ports the same way. + * But that would mean to support only standard baud rates or to get into + * big difficulties. On the other hand, you have more standard rates available. + * + * MIDI is missed with 5%. Can anyone tell me how to figure out which quartz + * is attached by software? I think the duart.device simply assumes 4.000MHz + * when you select MIDI baudrate. + * + * Another Hardware lack: If you select 5 bit character size a halve stop bit + * is sent too much. + * + * Hint to BSC: If they had used the IP2/3 pins for DCD and IP4/5 for DSR, + * I could use the change state interrupt feature of the duart instead of polling + * the DCD every vblank-interrupt. (Or does someone want to be interrupted when + * DSR changes state (i.e. modem switched on/off) instead of knowing when a + * connection is established/lost?) + * + * Note: The duart is able to handle the RTS/CTS-protocol in hardware. + * I use this feature to prevent receiver overruns by letting the DUART drop RTS + * when the FIFO is full + * + * created 15.11.95 Joerg Dorchain (dorchain@mpi-sb.mpg.de) + * + * adapted 11.12.95 for 1.2.13 Joerg Dorchain + * + * improved 6. 2.96 Joerg Dorchain + * - fixed restart bug + * + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "multiface.h" +#include "mc68681.h" + +/* how many cards are to be supported */ +#define MAX_CARD 5 + +/***************************** Prototypes *****************************/ + +static void mfc_interrupt(int irq, void *data, struct pt_regs *fp); +static void mfc_vbl_inter(int irq, void *data, struct pt_regs *fp); +static void mfc_init( struct m68k_async_struct *info ); +static void mfc_deinit( struct m68k_async_struct *info, int leave_dtr ); +static void mfc_enab_tx_int( struct m68k_async_struct *info, int enab_flag ); +static int mfc_check_custom_divisor( struct m68k_async_struct *info, int + baud_base, int divisor ); +static void mfc_change_speed( struct m68k_async_struct *info ); +static void mfc_throttle( struct m68k_async_struct *info, int status ); +static void mfc_set_break( struct m68k_async_struct *info, int break_flag ); +static void mfc_get_serial_info( struct m68k_async_struct *info, + struct serial_struct *retinfo ); +static unsigned int mfc_get_modem_info( struct m68k_async_struct *info ); +static int mfc_set_modem_info( struct m68k_async_struct *info, int new_dtr, + int new_rts ); +static void mfc_stop_receive(struct m68k_async_struct *info); +static int mfc_trans_empty(struct m68k_async_struct *info); + +/************************* End of Prototypes **************************/ + + + +/* SERIALSWITCH structure for the Multiface serial ports */ + +static SERIALSWITCH mfc_ser_switch = { + mfc_init, mfc_deinit, mfc_enab_tx_int, + mfc_check_custom_divisor, mfc_change_speed, + mfc_throttle, mfc_set_break, + mfc_get_serial_info, mfc_get_modem_info, + mfc_set_modem_info, NULL, + mfc_stop_receive, mfc_trans_empty, NULL +}; + + + +/* These value go into the csr (Clock Select Registers) */ +/* 0xff means not available, B0 is handle special anyway */ + static u_char fixed_baud_III[16] = { + 0, /* B0 */ + 0xff, /* B50 */ + 0xff, /* B75 */ + 0xff, /* B110 */ + 0xff, /* B134 */ + 0xff, /* B150 */ + 0xff, /* B200 */ + 0x33, /* B300 */ + 0x44, /* B600 */ + 0x55, /* B1200 */ + 0xff, /* B1800 */ + 0x66, /* B2400 */ + 0x88, /* B4800 */ + 0x99, /* B9600 */ + 0xbb, /* B19200 */ + 0xcc /* B38400 */ + }; +/* These are precalculated timer values for standart rates */ + static u_short custom_baud_III[18] = { + 0, /* B0 */ + 4608, /* B50 */ + 3072, /* B75 */ + 2095, /* B110 - a little bit off (0.02%) */ + 1713, /* B134 - off by 0.0006% */ + 1536, /* B150 */ + 1152, /* B200 */ + 768, /* B300 */ + 384, /* B600 */ + 192, /* B1200 */ + 128, /* B1800 */ + 96, /* B2400 */ + 48, /* B4800 */ + 24, /* B9600 */ + 12, /* B19200 */ + 6, /* B38400 */ + 4, /* B57600 */ + 2 /* B115200 */ + }; + +static int nr_mfc; /* nr of ports configured */ +static int lines[MAX_CARD * 2]; /* accociated tty lines (index in rs_table) */ +static unsigned int board_index[MAX_CARD]; /* nr. of zorro slot */ +static unsigned char imask[MAX_CARD]; +static unsigned char acmask[MAX_CARD]; + +static void mfc_fill_tx(struct m68k_async_struct *info) +{ +struct duart *dp; +struct duarthalf *d; +int sta,i,mask,ch; + +dp=info->board_base; +d=(struct duarthalf *)info->port; +i=(info->nr_uarts-1)/2; +mask=(((info->nr_uarts-1)%2)==0)?1:16; +sta=d->sr_csr.sr; +while ((sta & 4) != 0) { /* fill the buffer */ + if ((ch = rs_get_tx_char( info )) >= 0) + d->hr.thr = ch; + else + break; + sta=d->sr_csr.sr; +} +if (rs_no_more_tx( info )) + imask[i] &= ~mask; +else + imask[i] |= mask; +dp->ir.imr = imask[i]; +} + +static u_char isr_entered = 0; + +static void mfc_interrupt(int irq, void *data, struct pt_regs *fp) +{ +int i; +int ireq; +struct duart *dp; +int ch; +int status; +int err; +int ipch; +u_short intenar; + + +intenar = (custom.intenar & IF_EXTER); +custom.intena = IF_EXTER; +if (isr_entered) { + printk("Reentering MFC isr.\n"); + custom.intena = (intenar | IF_SETCLR); + return; +} +isr_entered = 1; + +custom.intena = (intenar | IF_SETCLR); + +for (i = 0; i < nr_mfc; i += 2) { + dp = rs_table[lines[i]].board_base; + ireq = dp->ir.isr; + if ((ireq & imask[i/2]) !=0) { /* if it is "our" interrupt */ + /* first the transmitter */ + mfc_fill_tx(rs_table + lines[i]); /* channel A */ + mfc_fill_tx(rs_table + lines[i + 1]); /* channel B */ + { /* test for receiver ready */ + { + status = dp->pa.sr_csr.sr; /* empty the FIFO */ + while ((status & 1) != 0) { + ch = dp->pa.hr.rhr; + err = 0; + if ((status & SR_BREAK) != 0) + err |= TTY_BREAK; + if ((status & SR_FRAMING) != 0) + err |= TTY_FRAME; + if ((status & SR_PARITY) != 0) + err |= TTY_PARITY; + if ((status & SR_OVERRUN) != 0) + err |= TTY_OVERRUN; + rs_receive_char(rs_table + lines[i], ch, err); + status = dp->pa.sr_csr.sr; + } + } + { + status = dp->pb.sr_csr.sr; + while (( status & 1) != 0) { + ch = dp->pb.hr.rhr; + err = 0; + if ((status & SR_BREAK) != 0) + err |= TTY_BREAK; + if ((status & SR_FRAMING) != 0) + err |= TTY_FRAME; + if ((status & SR_PARITY) != 0) + err |= TTY_PARITY; + if ((status & SR_OVERRUN) != 0) + err |= TTY_OVERRUN; + rs_receive_char(rs_table + lines[i + 1], ch, err); + status = dp->pb.sr_csr.sr; + } + } + } + { /* CTS changed */ + ipch = dp->ipcr_acr.ipcr; + if ((ipch & 16) !=0) /* port a */ + rs_check_cts(rs_table + lines[i], !(ipch & 1)); + if ((ipch & 32) !=0) + rs_check_cts(rs_table + lines[i + 1], !(ipch & 2)); + } + } +} +custom.intreq = IF_EXTER; +isr_entered = 0; +} + +static u_char curr_dcd[MAX_CARD]; + +static void mfc_vbl_inter(int irq, void *data, struct pt_regs *fp) +{ +int i; +struct duart *dp; +u_char bits; + +for (i = 0; i < nr_mfc; i += 2) { + if (rs_table[lines[i]].flags & ASYNC_INITIALIZED) { + dp = rs_table[lines[i]].board_base; + bits = dp->ipr_opcr.ipr & 48; + if (bits ^ curr_dcd[i/2]) { + if (((bits ^ curr_dcd[i/2]) & 16) != 0) + rs_dcd_changed(rs_table + lines[i], !(bits & 16)); + if (((bits ^ curr_dcd[i/2]) & 32) != 0) + rs_dcd_changed(rs_table + lines[i + 1], !(bits & 32)); + } + curr_dcd[i/2] = bits; + } +} +} + +static void mfc_init( struct m68k_async_struct *info) +{ +struct duart *dp; +struct duarthalf *d; + +dp = info->board_base; +d = (struct duarthalf *)info->port; +if (((info->nr_uarts-1)%2) == 0) { /* port A */ + dp->start_sopc.sopc = 5; /* DTR & RTS on */ + imask[(info->nr_uarts-1)/2] |= 3|128; /* interrupts on */ + acmask[(info->nr_uarts-1)/2] |= 1; +} +else { + dp->start_sopc.sopc = 10; + imask[(info->nr_uarts-1)/2] |= 48|128; + acmask[(info->nr_uarts-1)/2] |= 2; +} +d->cr = CR_RESET_RX; +d->cr = CR_RESET_TX; +d->cr = CR_RESET_ERR; +d->cr = CR_RESET_BREAK; +d->cr = CR_STOP_BREAK; +d->cr = CR_RX_ON; +d->cr = CR_TX_ON; +curr_dcd[(info->nr_uarts-1)/2] = dp->ipr_opcr.ipr & 48; +custom.intena = IF_EXTER; +/* enable interrupts */ +dp->ir.imr = imask[(info->nr_uarts-1)/2]; +dp->ipcr_acr.acr = acmask[(info->nr_uarts-1)/2]; +custom.intena = (IF_EXTER | IF_SETCLR); +MOD_INC_USE_COUNT; +} + +static void mfc_deinit( struct m68k_async_struct *info, int leave_dtr) +{ +struct duart *dp; +struct duarthalf *d; +u_short intenar; + +dp = info->board_base; +d = (struct duarthalf *)info->port; +/* turn off interrupts CTS and DTR if required */ +if (((info->nr_uarts-1)%2) == 0) { + dp->stop_ropc.ropc = 1; + if (!leave_dtr) + dp->stop_ropc.ropc = 4; + imask[(info->nr_uarts-1)/2] &= ~3; + acmask[(info->nr_uarts-1)/2] &= ~1; +} +else { + dp->stop_ropc.ropc = 2; + if (!leave_dtr) + dp->stop_ropc.ropc = 8; + imask[(info->nr_uarts-1)/2] &= ~48; + acmask[(info->nr_uarts-1)/2] &= ~2; +} +intenar = (custom.intenar & IF_EXTER); +custom.intena = IF_EXTER; +dp->ir.imr = imask[(info->nr_uarts-1)/2]; +dp->ipcr_acr.acr = acmask[(info->nr_uarts-1)/2]; +custom.intena = (intenar | IF_SETCLR); +/* disable transmitter and receiver after current character */ +d->cr = CR_RX_OFF; +d->cr = CR_TX_OFF; +MOD_DEC_USE_COUNT; +} + +static void mfc_enab_tx_int(struct m68k_async_struct *info, int enab_flag) +{ +struct duart *dp; + +if (enab_flag) + mfc_fill_tx(info); /* fills the tx buf and enables interrupts */ +else { /* disable interrupts */ + /* we could also disable the transmitter at all */ + dp = info->board_base; + if (((info->nr_uarts-1)%2) == 0) + imask[(info->nr_uarts-1)/2] &= ~1; + else + imask[(info->nr_uarts-1)/2] &= ~16; + dp->ir.imr=imask[(info->nr_uarts-1)/2]; +} +} + +static int mfc_check_custom_divisor(struct m68k_async_struct *info, + int baud_base, int divisor) +{ +if (((info->nr_uarts-1)%2) == 0) + return 1; +if (baud_base != 230400) /* change for MFC II */ + return 1; +if (divisor < 2) + return 1; +return 0; +} + +static void mfc_change_speed(struct m68k_async_struct *info) +{ +u_int cflag, baud, chsize, stopb, parity, aflags; +u_int ctrl = 0; +u_short div = 0; +struct duart *dp; +struct duarthalf *d; +int dummy; + +if (!info->tty || ! info->tty->termios) + return; +dp = info->board_base; +d = (struct duarthalf *)info->port; +cflag = info->tty->termios->c_cflag; +baud = cflag & CBAUD; +chsize = cflag & CSIZE; +stopb = cflag & CSTOPB; +parity = cflag & (PARENB | PARODD); +aflags = info->flags & ASYNC_SPD_MASK; + +if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; +else + info->flags &= ~ASYNC_CTS_FLOW; +if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; +else + info->flags |= ASYNC_CHECK_CD; + +if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + if (baud < 1 || baud > 4) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; +} + +if (baud == 15) { + switch (aflags) { + case ASYNC_SPD_HI: + baud += 1; + break; + case ASYNC_SPD_VHI: + baud += 2; + break; + case ASYNC_SPD_SHI: + baud += 3; + break; + case ASYNC_SPD_WARP: + baud += 4; + break; + case ASYNC_SPD_CUST: + div = info->custom_divisor; + break; + } +} +if (!div) { + if (baud > 17) + baud = 17; + div = custom_baud_III[baud]; +} + +if (!div) { /* drop DTR */ + if (((info->nr_uarts-1)%2) == 0) + dp->stop_ropc.ropc = 4; + else + dp->stop_ropc.ropc = 8; + return; +} + +if (((info->nr_uarts-1)%2) == 0) { + dp->start_sopc.sopc = 4; /* set DTR */ + if (baud > 15) + baud =15; + if (fixed_baud_III[baud] != 0xff) + d->sr_csr.csr = fixed_baud_III[baud]; + else + printk("ttyS%d: ignored illegal baudrate %d\n", info->line,baud); +} +else { + dp->start_sopc.sopc = 8; + dp->ctu = div / 256; + dp->ctl = div % 256; + dummy = dp->start_sopc.start; +} +d->mr.mr2 = (stopb) ? MR2_2STOP: MR2_1STOP; + +if (chsize == CS8) + ctrl |= MR1_8BITS; +else if (chsize == CS7) + ctrl |= MR1_7BITS; +else if (chsize == CS6) + ctrl |= MR1_6BITS; +else if (chsize == CS5) + ctrl |= MR1_5BITS; + +if (parity & PARENB) + ctrl |= MR1_PARITY_WITH; +else + ctrl |= MR1_PARITY_NO; +if (parity & PARODD) + ctrl |= MR1_PARITY_ODD; + +d->cr = CR_RESET_MR; +d->mr.mr1 = ctrl|MR1_RxRTS_ON; +} + +static void mfc_throttle (struct m68k_async_struct *info, int status) +{ +struct duart *dp; + +dp = info->board_base; +if (((info->nr_uarts-1)%2) == 0) { + if (status) + dp->stop_ropc.ropc = 1; + else + dp->start_sopc.sopc = 1; +} +else { + if (status) + dp->stop_ropc.ropc = 2; + else + dp->start_sopc.sopc = 2; +} +} + +static void mfc_set_break (struct m68k_async_struct *info, int break_flag) +{ +struct duarthalf *d; + +d = (struct duarthalf *)info->port; +if (break_flag) + d->cr = CR_START_BREAK; +else + d->cr = CR_STOP_BREAK; +} + +static void mfc_get_serial_info( struct m68k_async_struct *info, + struct serial_struct *retinfo) +{ +if (((info->nr_uarts-1)%2) == 0) { + retinfo->baud_base = 0; + retinfo->custom_divisor = 0; +} +else { + retinfo->baud_base = 230400; + retinfo->custom_divisor = info->custom_divisor; +} +} + +static unsigned int mfc_get_modem_info(struct m68k_async_struct *info) +{ +struct duart *dp; +u_char inf; + +dp = info->board_base; +inf = dp->ipr_opcr.ipr; +if (((info->nr_uarts-1)%2) == 0) + return ((inf & 1) ? 0 : TIOCM_CTS) | + ((inf & 4) ? 0 : TIOCM_DSR) | + ((inf & 16) ? 0 : TIOCM_CAR) | + ((dp->pa.ri & 1) ? 0 : TIOCM_RNG); +else + return ((inf & 2) ? 0 : TIOCM_CTS) | + ((inf & 8) ? 0 : TIOCM_DSR) | + ((inf & 32) ? 0 : TIOCM_CAR) | + ((dp->pb.ri & 2) ? 0 : TIOCM_RNG); +/* No chance to check DTR & RTS easily */ +} + +static int mfc_set_modem_info(struct m68k_async_struct *info, int new_dtr, int new_rts) +{ +struct duart *dp; + +dp = info->board_base; +if (((info->nr_uarts-1)%2) == 0) { + if (new_dtr == 0) + dp->stop_ropc.ropc = 4; + else if (new_dtr == 1) + dp->start_sopc.sopc = 4; + if (new_rts == 0) + dp->stop_ropc.ropc = 1; + else if (new_rts == 1) + dp->start_sopc.sopc = 1; +} +else { + if (new_dtr == 0) + dp->stop_ropc.ropc = 8; + else if (new_dtr == 1) + dp->start_sopc.sopc = 8; + if (new_rts == 0) + dp->stop_ropc.ropc = 2; + else if (new_rts == 1) + dp->start_sopc.sopc = 2; +} +return 0; +} + +static void mfc_stop_receive(struct m68k_async_struct *info) +{ +struct duart *dp; + +dp = info->board_base; +if (((info->nr_uarts-1)%2) == 0) + imask[(info->nr_uarts-1)/2] &= ~2; +else + imask[(info->nr_uarts-1)/2] &= ~32; +dp-> ir.imr = imask[(info->nr_uarts-1)/2]; +} + +static int mfc_trans_empty(struct m68k_async_struct *info) +{ +struct duarthalf *dh; + +dh = (struct duarthalf *)info->port; +return (dh->sr_csr.sr & 8); +} + +int multiface_init(void) +{ +unsigned int key; +int line1, line2; +struct duart *dp; +int dummy; +const struct ConfigDev *cd; +struct serial_struct req; +struct m68k_async_struct *info; + +if (!MACH_IS_AMIGA) + return -ENODEV; + +nr_mfc = 0; +/* add MFC_II and serial master for test purposes */ +while((key=zorro_find(ZORRO_PROD_BSC_MULTIFACE_III,1, 0))) { + board_index[nr_mfc/2] = key; + cd = zorro_get_board(key); + dp = (struct duart *)ZTWO_VADDR((((u_char *)cd->cd_BoardAddr)+DUARTBASE)); + + req.line = -1; /* first free ttyS? device */ + req.type = SER_MFC_III; + req.port = (int)&dp->pa; + if ((line1 = m68k_register_serial( &req )) < 0) { + printk( "Cannot register MFC III serial port: no free device\n" ); + return -EBUSY; + } + lines[nr_mfc++] = line1; + info = &rs_table[line1]; + info->sw = &mfc_ser_switch; + info->nr_uarts = nr_mfc; + info->board_base = dp; + + req.line = -1; /* first free ttyS? device */ + req.type = SER_MFC_III; + req.port = (int)&dp->pb; + if ((line2 = m68k_register_serial( &req )) < 0) { + printk( "Cannot register MFC III serial port: no free device\n" ); + m68k_unregister_serial( line1 ); + return -EBUSY; + } + lines[nr_mfc++] = line2; + info = &rs_table[line2]; + info->sw = &mfc_ser_switch; + info->nr_uarts = nr_mfc--; + info->board_base = dp; + + if (nr_mfc < 4) { + request_irq(IRQ_AMIGA_EXTER, mfc_interrupt, 0, + "Multiface III serial", mfc_interrupt); + request_irq(IRQ_AMIGA_VERTB, mfc_vbl_inter, 0, + "Multiface III serial VBL", mfc_vbl_inter); + } + + dp->ir.imr = 0; /* turn off all interrupts */ + imask[nr_mfc/2] = 0; + dummy = dp->ir.isr; /* clear peding interrupts */ + dummy = dp->ipcr_acr.ipcr; + dp->stop_ropc.ropc = 255; /* all outputs off */ + dp->ipr_opcr.opcr = 0; /* change for serial-master */ + dp->pa.cr = CR_RX_OFF; /* disable receiver */ + dp->pb.cr = CR_RX_OFF; + dp->pa.cr = CR_TX_OFF; /* disable transmitter */ + dp->pb.cr = CR_TX_OFF; + dp->pa.cr = CR_RESET_RX; + dp->pb.cr = CR_RESET_RX; + dp->pa.cr = CR_RESET_TX; + dp->pb.cr = CR_RESET_TX; + dp->pa.cr = CR_RESET_ERR; + dp->pb.cr = CR_RESET_ERR; + dp->pa.cr = CR_RESET_BREAK; + dp->pb.cr = CR_RESET_BREAK; + dp->pa.cr = CR_STOP_BREAK; + dp->pb.cr = CR_STOP_BREAK; + dp->pa.cr = CR_RESET_MR; + dp->pb.cr = CR_RESET_MR; + /* set default 9600 8 N 1 */ + dp->pa.mr.mr1 = MR1_8BITS|MR1_PARITY_NO|MR1_RxRTS_ON; + dp->pb.mr.mr1 = MR1_8BITS|MR1_PARITY_NO|MR1_RxRTS_ON; + dp->pa.mr.mr2 = MR2_1STOP; + dp->pb.mr.mr2 = MR2_1STOP; + + dp->ipcr_acr.acr = 0xe0; /* BRG set 2, timer 1X crystal */ + acmask[nr_mfc/2] = 0xe0; + /* change the following baudrate stuff for MFC_II */ + dp->pa.sr_csr.csr = 0x99; /* 9600 from BRG */ + dp->pb.sr_csr.csr = 0xdd; /* from timer */ + dp->ctu = 0; + dp->ctl = 24; /* values for 9600 */ + dummy = dp->start_sopc.start; /* load timer with new values */ + nr_mfc++; + + zorro_config_board(key,1); +} +return 0; +} + +#ifdef MODULE +int init_module(void) +{ +return multiface_init(); +} + +void cleanup_module(void) +{ +int i; +struct duart *dp; + +for (i = 0; i < nr_mfc; i += 2) { + dp = rs_table[lines[i]].board_base; + dp->pa.cr = CR_RX_OFF | CR_TX_OFF; /* disable duart */ + dp->pb.cr = CR_RX_OFF | CR_TX_OFF; + dp->ir.imr = 0; /* turn off interrupts */ + m68k_unregister_serial(lines[i]); + m68k_unregister_serial(lines[i+1]); + zorro_unconfig_board(board_index[i/2], 1); +} +free_irq(IRQ_AMIGA_EXTER, mfc_interrupt); +free_irq(IRQ_AMIGA_VERTB, mfc_vbl_inter); +} +#endif + + diff --git a/drivers/char/ser_whippet.c b/drivers/char/ser_whippet.c new file mode 100644 index 000000000000..20ecedce0b0b --- /dev/null +++ b/drivers/char/ser_whippet.c @@ -0,0 +1,837 @@ +#define WHIPPET_VER "2.2.0pre7" +#define WHIPPET_REV 0 +#define WHIPPET_DATE "31/Jan/1999" + +/* + * ser_whippet.c - version info as above + * + * Copyright (C) 1997,98,99 Chris Sumner (chris@cpsumner.freeserve.co.uk) + * + * This is a driver for the Hisoft Whippet PCMCIA serial port for + * the Amiga. (16c550b UART) + * + * The code is mostly based on ser_ioext.c by Jes Sorensen, + * (jds@kom.auc.dk) but has been modified to cope with the different + * hardware footprint of the Whippet. + * + * Modified: + * + * 11/Feb/98 - General tidying up + * 31/Mar/98 - More tidying up + * 7/Apr/98 - Fixed nasty little bug concerning gayle ints + * 8/Apr/98 - Changed PCMCIA access timings to 100ns + * 31/Jul/98 - Changed email address + * 2/Nov/98 - Fixed to work with 2.1.124 + * 7/Nov/98 - Added support for ASYNC_SPD_SHI and _WARP + * 8/Nov/98 - Fixed a few small bugs + * 20/Nov/98 - Re-structured code a bit and tidied up + * 21/Nov/98 - Added support for modules + * 22/Nov/98 - Fixed inverted DCD bug + * 23/Nov/98 - Re-structured interrupt code and fixed a few bugs - (2.1.127) + * 31/Jan/99 - Changed email (again) and modified for 2.2.0pre7 + * + * To Do: + * + * - Test at 230k4 and 460k8 rates + * - Dynamic changing of fifo trigger level (via ioctl? via overrun count?) + * - Handling of card insertion and removal (more than just shout at user!) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef gayle +#undef gayle /* We use our own definition */ +#endif + +#include "ser_whippet.h" + +#define WHIPPET_DEBUG 0 /* debug level */ + +#define ser_DTRon(uart) (uart->MCR |= DTR) +#define ser_RTSon(uart) (uart->MCR |= RTS) +#define ser_DTRoff(uart) (uart->MCR &= ~DTR) +#define ser_RTSoff(uart) (uart->MCR &= ~RTS) + + +/***************************** Prototypes *****************************/ +static void ser_init(struct m68k_async_struct *info); +static void ser_deinit(struct m68k_async_struct *info, int leave_dtr); +static void ser_enab_tx_int(struct m68k_async_struct *info, int enab_flag); +static int ser_check_custom_divisor(struct m68k_async_struct *info, + int baud_base, int divisor); +static void ser_change_speed(struct m68k_async_struct *info); +static void ser_throttle(struct m68k_async_struct *info, int status); +static void ser_set_break(struct m68k_async_struct *info, int break_flag); +static void ser_get_serial_info(struct m68k_async_struct *info, + struct serial_struct *retinfo); +static unsigned int ser_get_modem_info(struct m68k_async_struct *info); +static int ser_set_modem_info(struct m68k_async_struct *info, int new_dtr, + int new_rts); +static int ser_ioctl(struct tty_struct *tty, struct file *file, + struct m68k_async_struct *info, unsigned int cmd, + unsigned long arg); +static void ser_stop_receive(struct m68k_async_struct *info); +static int ser_trans_empty(struct m68k_async_struct *info); +/************************* End of Prototypes **************************/ + +/* + * SERIALSWITCH structure for the Whippet serial interface. + */ + +static SERIALSWITCH whippet_ser_switch = { + ser_init, + ser_deinit, + ser_enab_tx_int, + ser_check_custom_divisor, + ser_change_speed, + ser_throttle, + ser_set_break, + ser_get_serial_info, + ser_get_modem_info, + ser_set_modem_info, + ser_ioctl, + ser_stop_receive, + ser_trans_empty, + NULL +}; + +static int whippet_baud_table[20] = { + /* B0 */ 0, + /* B50 */ 9216, + /* B75 */ 6144, + /* B110 */ 4189, /* 110.00238 */ + /* B134.5 */ 3426, /* 134.50087 */ + /* B150 */ 3072, + /* B200 */ 2304, + /* B300 */ 1536, + /* B600 */ 768, + /* B1200 */ 384, + /* B1800 */ 256, + /* B2400 */ 192, + /* B4800 */ 96, + /* B9600 */ 48, + /* B19200 */ 24, + /* B38400 */ 12, /* The last of the standard rates. */ + /* B57600 */ 8, /* ASYNC_SPD_HI */ + /* B115K2 */ 4, /* ASYNC_SPD_VHI */ + /* B230K4 */ 2, /* ASYNC_SPD_SHI */ + /* B460K8 */ 1 /* ASYNC_SPD_WARP */ +}; + + +static volatile struct GAYLE *gayle; /* gayle register struct */ +static volatile struct WHIPPET *whippet; /* whippet regs struct */ + +static int fifo_trig_level=FIFO_TRIG_4; /* can be changed */ + +/* + * There are 4 receiver FIFO-interrupt trigger levels (FIFO_TRIG_x), that + * indicates how many bytes are to be allowed in the receiver-FIFO before + * an interrupt is generated: + * x = 1 = 1 byte + * x = 4 = 4 bytes + * x = 8 = 8 bytes + * x = 14 = 14 bytes + * If you keep getting overruns try lowering this value one step at a time. + */ + +/***** start of ser_interrupt() - Handler for serial interrupt. *****/ + +static void ser_interrupt(int irq, void *data, struct pt_regs *regs) +{ +struct m68k_async_struct *info = data; +u_char iir,ier,lsr,gayleirq = gayle->intreq; + + if ((gayleirq & 0x7c)==0) return; /* quick exit */ + + if (gayleirq & 0x5c) { + gayle->intreq = ((gayleirq & 0x5c) ^ 0x5c) | (GAYLE_IRQ_IDE | GAYLE_IRQ_SC); + printk("gayle->intreq = 0x%02x; gayle->inten = 0x%02x\n",gayleirq, gayle->inten); + if (gayleirq & GAYLE_IRQ_CCDET) { + if (gayle->cardstatus & GAYLE_CS_CCDET) { + printk("Card inserted! Don't do that!\n"); + } else { + printk("Card removed! Don't do that!\n"); + } + } + } + + if ((gayleirq & GAYLE_IRQ_SC)==0) return; + +/* If we got here, then there is an interrupt waiting for us to service */ + + iir = whippet->IIR; + +/* Disable UART interrupts for now... */ + + ier = whippet->IER; whippet->IER = 0; + + while (!(iir & IRQ_PEND)) { /* loop until no more ints */ + + switch (iir & (IRQ_ID1 | IRQ_ID2 | IRQ_ID3)) { + case IRQ_RLS: /* Receiver Line Status */ + case IRQ_CTI: /* Character Timeout */ + case IRQ_RDA: /* Received Data Available */ + /* + * Copy chars to the tty-queue ... + * Be careful that we aren't passing one of the + * Receiver Line Status interrupt-conditions without noticing. + */ + { + int ch; + + lsr = whippet->LSR; + while (lsr & DR) { + u_char err; + ch = whippet->RBR; + + if (lsr & BI) err = TTY_BREAK; + else if (lsr & PE) err = TTY_PARITY; + else if (lsr & OE) err = TTY_OVERRUN; + else if (lsr & FE) err = TTY_FRAME; + else err = 0; + + rs_receive_char(info, ch, err); + lsr = whippet->LSR; + } + } + break; + + case IRQ_THRE: /* Transmitter holding register empty */ + { + int fifo_space = FIFO_SIZE; + + /* If the uart is ready to receive data and there are chars in */ + /* the queue we transfer all we can to the uart's FIFO */ + + if (rs_no_more_tx(info)) { +#if WHIPPET_DEBUG + printk("rs_no_more_tx()\n"); +#endif + /* Disable transmitter empty interrupt */ + ier &= ~(ETHREI); + + /* Read IIR to acknowledge the interrupt */ + (void)whippet->IIR; + break; + } + + /* Handle software flow control */ + if (info->x_char) { +#if WHIPPET_DEBUG + printk("Flow: X%s\n",(info->x_char == 19) ? "OFF" : "ON"); +#endif + whippet->THR = info->x_char; + info->icount.tx++; + info->x_char = 0; + fifo_space--; + } + + /* Fill the fifo */ + while (fifo_space > 0) { + fifo_space--; + whippet->THR = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail &= (SERIAL_XMIT_SIZE-1); + info->icount.tx++; + if (--info->xmit_cnt == 0) break; + } +#if WHIPPET_DEBUG + if (fifo_space == 0) printk("fifo full\n"); +#endif + /* Don't need THR interrupts any more */ + if (info->xmit_cnt == 0) { +#if WHIPPET_DEBUG + printk("TX ints OFF\n"); +#endif + ier &= ~(ETHREI); + } + + if (info->xmit_cnt < WAKEUP_CHARS) { +#if WHIPPET_DEBUG + printk("rs_sched_event()\n"); +#endif + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + } + } + break; + + case IRQ_MS: /* Must be modem status register interrupt? */ + { + u_char msr = whippet->MSR; + + if (info->flags & ASYNC_INITIALIZED) { + if (msr & DCTS) { +#if WHIPPET_DEBUG + printk("CTS = %s\n",(msr & CTS) ? "ON" : "OFF"); +#endif + rs_check_cts(info, (msr & CTS)); + } + if (msr & DDCD) + rs_dcd_changed(info, (msr & DCD)); + } + } + break; + + } /* switch (iir) */ + + iir = whippet->IIR; + } /* while IRQ_PEND */ + +/* Acknowledge gayle STATUS_CHANGE interrupt */ + + gayle->intreq = ((gayleirq & GAYLE_IRQ_SC) ^ GAYLE_IRQ_SC) | GAYLE_IRQ_IDE; + +/* Re-enable UART interrupts */ + + whippet->IER = ier; +} + +/***** end of ser_interrupt() *****/ + + +/***** start of ser_init() *****/ + +static void ser_init(struct m68k_async_struct *info) +{ +#if WHIPPET_DEBUG + printk("ser_init()\n"); +#endif + while ((whippet->LSR) & DR) + (void)whippet->RBR; /* read a byte */ + +/* Set DTR and RTS */ + whippet->MCR |= (DTR | RTS); + +/* Enable interrupts. IF_PORTS irq has already been enabled in whippet_init()*/ +/* DON'T enable ETHREI here because there is nothing to send yet (murray) */ + whippet->IER |= (ERDAI | ELSI | EMSI); + + MOD_INC_USE_COUNT; +} + +/***** end of ser_init() *****/ + + +/***** start of ser_deinit() *****/ + +static void ser_deinit(struct m68k_async_struct *info, int leave_dtr) +{ +#if WHIPPET_DEBUG + printk("ser_deinit()\n"); +#endif + /* Wait for the uart to get empty */ + while (!((whippet->LSR) & TEMT)) { + } + + while ((whippet->LSR) & DR) { + (void)whippet->RBR; + } + +/* No need to disable UART interrupts since this will already + * have been done via ser_enab_tx_int() and ser_stop_receive() + */ + + ser_RTSoff(whippet); + if (!leave_dtr) ser_DTRoff(whippet); + + MOD_DEC_USE_COUNT; +} + +/***** end of ser_deinit() *****/ + + +/***** start of ser_enab_tx_int() *****/ + +/* +** Enable or disable tx interrupts. +** Note that contrary to popular belief, it is not necessary to +** send a character to cause an interrupt to occur. Whenever the +** THR is empty and THRE interrupts are enabled, an interrupt will occur. +** (murray) +*/ +static void ser_enab_tx_int(struct m68k_async_struct *info, int enab_flag) +{ +#if WHIPPET_DEBUG + printk("ser_enab_tx_int(%s)\n",(enab_flag) ? "ON" : "OFF"); +#endif + if (enab_flag) whippet->IER |= ETHREI; + else whippet->IER &= ~(ETHREI); +} + +/***** end of ser_enab_tx_int() *****/ + + +static int ser_check_custom_divisor(struct m68k_async_struct *info, + int baud_base, int divisor) +{ +#if WHIPPET_DEBUG + printk("ser_check_custom_divisor()\n"); +#endif + /* Always return 0 or else setserial spd_hi/spd_vhi doesn't work */ + return 0; +} + + +/***** start of ser_change_speed() *****/ + +static void ser_change_speed(struct m68k_async_struct *info) +{ +u_int cflag, baud, chsize, stopb, parity, aflags; +u_int div = 0, ctrl = 0; + +#if WHIPPET_DEBUG + printk("ser_change_speed()\n"); +#endif + + if (!info->tty || !info->tty->termios) return; + + cflag = info->tty->termios->c_cflag; + baud = cflag & CBAUD; + chsize = cflag & CSIZE; + stopb = cflag & CSTOPB; + parity = cflag & (PARENB | PARODD); + aflags = info->flags & ASYNC_SPD_MASK; + + if (cflag & CRTSCTS) info->flags |= ASYNC_CTS_FLOW; + else info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; + else info->flags |= ASYNC_CHECK_CD; + + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + if (baud < 1 || baud > 2) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; + } + if (baud == 15) { + if (aflags == ASYNC_SPD_HI) /* 57k6 */ + baud += 1; + if (aflags == ASYNC_SPD_VHI) /* 115k2 */ + baud += 2; + if (aflags == ASYNC_SPD_SHI) /* 230k4 */ + baud += 3; + if (aflags == ASYNC_SPD_WARP) /* 460k8 */ + baud += 4; + if (aflags == ASYNC_SPD_CUST) + div = info->custom_divisor; + } + if (!div) { + /* Maximum speed is 460800 */ + if (baud > 19) baud = 19; + div = whippet_baud_table[baud]; + } + +#if WHIPPET_DEBUG + printk("divisor=%d, baud rate=%d\n",div,(div==0)? -1 : WHIPPET_BAUD_BASE/div); +#endif + + if (!div) { + /* speed == 0 -> drop DTR */ + ser_DTRoff(whippet); + return; + } + +/* + * We have to set DTR when a valid rate is chosen, otherwise DTR + * might get lost when programs use this sequence to clear the line: + * + * change_speed(baud = B0); + * sleep(1); + * change_speed(baud = Bx); x != 0 + * + * The pc-guys do this aswell. + */ + ser_DTRon(whippet); + + if (chsize == CS8) ctrl |= data_8bit; + else if (chsize == CS7) ctrl |= data_7bit; + else if (chsize == CS6) ctrl |= data_6bit; + else if (chsize == CS5) ctrl |= data_5bit; + +/* If stopb is true we set STB which means 2 stop-bits */ +/* otherwise we only get 1 stop-bit. */ + + ctrl |= (stopb ? STB : 0); + ctrl |= ((parity & PARENB) ? ((parity & PARODD) ? (PEN) : (PEN | + EPS)) : 0x00 ); + + whippet->LCR = (ctrl | DLAB); + + /* Store high byte of divisor */ + + whippet->DLM = ((div >> 8) & 0xff); + + /* Store low byte of divisor */ + + whippet->DLL = (div & 0xff); + whippet->LCR = ctrl; +} + +/***** end of ser_change_speed() *****/ + + +/***** start of ser_throttle() *****/ + +static void ser_throttle(struct m68k_async_struct *info, int status) +{ +#if WHIPPET_DEBUG + printk("ser_throttle(rts=%s)\n", (status) ? "OFF" : "ON"); +#endif + if (status) ser_RTSoff(whippet); + else ser_RTSon(whippet); +} + +/***** end of ser_throttle() *****/ + + +/***** start of ser_set_break() *****/ + +static void ser_set_break(struct m68k_async_struct *info, int break_flag) +{ +#if WHIPPET_DEBUG + printk("ser_set_break(%s)\n", (break_flag) ? "ON" : "OFF"); +#endif + if (break_flag) whippet->LCR |= SET_BREAK; + else whippet->LCR &= ~SET_BREAK; +} + +/***** end of ser_set_break() *****/ + + +/***** start of ser_get_serial_info() *****/ + +static void ser_get_serial_info(struct m68k_async_struct *info, + struct serial_struct *retinfo) +{ +#if WHIPPET_DEBUG + printk("ser_get_serial_info()\n"); +#endif + + retinfo->baud_base = WHIPPET_BAUD_BASE; + retinfo->xmit_fifo_size = FIFO_SIZE; /* This field is currently ignored, */ + /* by the upper layers of the */ + /* serial-driver. */ + retinfo->custom_divisor = info->custom_divisor; +} + +/***** end of ser_get_serial_info() *****/ + + +/***** start of ser_get_modem() *****/ + +static unsigned int ser_get_modem_info(struct m68k_async_struct *info) +{ +unsigned char msr, mcr; + +#if WHIPPET_DEBUG > 1 + printk("ser_get_modem()\n"); +#endif + + msr = whippet->MSR; + mcr = whippet->MCR; /* The DTR and RTS are located in the */ + /* ModemControlRegister ... */ + + return ( + ((mcr & DTR) ? TIOCM_DTR : 0) | + ((mcr & RTS) ? TIOCM_RTS : 0) | + + ((msr & DCD) ? TIOCM_CAR : 0) | + ((msr & CTS) ? TIOCM_CTS : 0) | + ((msr & DSR) ? TIOCM_DSR : 0) | + ((msr & RING_I) ? TIOCM_RNG : 0) + ); +} + +/***** end of ser_get_modem() *****/ + + +/***** start of ser_set_modem_info() *****/ + +static int ser_set_modem_info(struct m68k_async_struct *info, int new_dtr, + int new_rts) +{ +#if WHIPPET_DEBUG + printk("ser_set_modem(dtr=%s, rts=%s)\n",(new_dtr == 0) ? "OFF" : "ON",(new_rts == 0) ? "OFF" : "ON"); +#endif + + if (new_dtr == 0) ser_DTRoff(whippet); + else if (new_dtr == 1) ser_DTRon(whippet); + + if (new_rts == 0) ser_RTSoff(whippet); + else if (new_rts == 1) ser_RTSon(whippet); + + return 0; +}; + +/***** end of ser_set_modem_info() *****/ + + +/***** start of ser_stop_receive() *****/ + +static void ser_stop_receive (struct m68k_async_struct *info) +{ +#if WHIPPET_DEBUG + printk("ser_stop_receive()\n"); +#endif + /* Disable uart receive and status interrupts */ + whippet->IER &= ~(ERDAI | ELSI | EMSI); +} + +/***** end of ser_stop_receive() *****/ + + +/***** start of ser_trans_empty() *****/ + +static int ser_trans_empty (struct m68k_async_struct *info) +{ +#if WHIPPET_DEBUG + printk("ser_trans_empty()\n"); +#endif + return (whippet->LSR & THRE); +} + +/***** end of ser_trans_empty() *****/ + + +/***** start of ser_ioctl() *****/ +static int ser_ioctl(struct tty_struct *tty, struct file *file, + struct m68k_async_struct *info, unsigned int cmd, + unsigned long arg) +{ +/* switch(cmd) { + case: + }*/ + return -ENOIOCTLCMD; +} + +/***** end of ser_ioctl() *****/ + + +/***** start of ser_reset_port() *****/ + +void ser_reset_port(void) +{ +#if WHIPPET_DEBUG + printk("ser_reset_port()\n"); +#endif +/* + * Try and reset the serial port to a default state + */ + whippet->IER = 0x00; /* disable interrupts */ + (void)whippet->IIR; /* clear any pending misc interrupts */ + (void)whippet->LSR; /* clear any pending LSR interrupts */ + (void)whippet->MSR; /* clear any pending MSR interrupts */ + +/* + * Set the serial port to a default setting of 8N1 - 9600 + */ + whippet->LCR = (data_8bit | DLAB); + whippet->DLM = 0; + whippet->DLL = 48; + whippet->LCR = (data_8bit); + +/* + * Set the rx FIFO-trigger count. + */ + whippet->FCR = (RCVR_FIFO_RES | FIFO_ENA | + XMIT_FIFO_RES | fifo_trig_level ); + whippet->MCR = 0; +} + +/***** end of ser_reset_port() *****/ + + +/***** start of whippet_init() *****/ + +/* + * Detect and initialize any Whippet found in the system. + */ + +static int line; /* The line assigned to us by register_serial() */ + +static struct m68k_async_struct *amiga_info; /* our async struct */ + +int whippet_init(void) +{ +unsigned long flags; +struct serial_struct req; +struct m68k_async_struct *info; + +#if WHIPPET_DEBUG + printk("whippet_init()\n"); +#endif + if (!(MACH_IS_AMIGA)) + return -ENODEV; + + if ((amiga_model!=AMI_1200) && (amiga_model!=AMI_600)) + return -ENODEV; + + if (!(AMIGAHW_PRESENT(PCMCIA))) /* might be missing, you never know! */ + return -ENODEV; + +/* + * Initialise hardware structure pointers + */ + gayle = (struct GAYLE *)(zTwoBase + GAYLE_ADDRESS); + whippet = (struct WHIPPET *)(zTwoBase + WHIPPET_PHYSADDR); + +#if WHIPPET_DEBUG + printk("gayle->cardstatus = 0x%02x\n",gayle->cardstatus); + printk("gayle->intreq = 0x%02x\n",gayle->intreq); + printk("gayle->inten = 0x%02x\n",gayle->inten); + printk("gayle->config = 0x%02x\n",gayle->config); +#endif + printk("Probing for Whippet serial port... (v%s r%i - %s)\n",WHIPPET_VER, WHIPPET_REV, WHIPPET_DATE); + +/* + * Test gayle cardstatus bits for presence of a card + */ + if (!(gayle->cardstatus & GAYLE_CS_CCDET)) { + printk("No PCMCIA Card detected\n"); + return -ENODEV; +#if WHIPPET_DEBUG + } else { printk("PCMCIA Card detected\n"); +#endif + } + +/* + * Card detected, but is it a Whippet??? Let's try and find out... + */ + { + u_char ch1,ch2; + whippet->SCR = 0x42; + whippet->IER = 0x00; + ch1=whippet->SCR; /* should be 0x42 */ + whippet->SCR = 0x99; + whippet->IER = 0x00; + ch2=whippet->SCR; /* should be 0x99 */ + if ((ch1!=0x42) || (ch2!=0x99)) return -ENODEV; + } + + ser_reset_port(); /* initialise the serial port */ + +/* + * Set the necessary tty-stuff. + */ + req.line = -1; /* first free ttyS? device */ + req.type = SER_WHIPPET; + req.port = (int) &(whippet->RBR); + + if ((line = register_serial( &req )) < 0) { + printk( "Cannot register Whippet serial port: no free device\n" ); + return -EBUSY; + } + + info = &rs_table[line]; /* set info == struct *m68k_async_struct */ + + info->nr_uarts = 1; /* one UART */ + info->sw = &whippet_ser_switch; /* switch functions */ + info->icount.cts = info->icount.dsr = 0; + info->icount.rng = info->icount.dcd = 0; + info->icount.rx = info->icount.tx = 0; + info->icount.frame = info->icount.parity = 0; + info->icount.overrun = info->icount.brk = 0; + + amiga_info = info; /* initialise our static async struct */ + +/* + * Clear any spurious interrupts in gayle + */ + gayle->intreq = ((gayle->intreq & 0x6c) ^ 0x6c) | GAYLE_IRQ_IDE; + +/* + * Install ISR - level 2 - data is struct *m68k_async_struct + */ + request_irq(IRQ_AMIGA_PORTS, ser_interrupt, 0, "whippet serial", info); + + save_flags(flags); + cli(); + +#if WHIPPET_DEBUG + printk("gayle->cardstatus = 0x%02x\n",gayle->cardstatus); + printk("gayle->intreq = 0x%02x\n",gayle->intreq); + printk("gayle->inten = 0x%02x\n",gayle->inten); + printk("gayle->config = 0x%02x\n",gayle->config); +#endif + +/* + * Enable status_change interrupts in gayle + */ + + gayle->inten |= GAYLE_IRQ_SC; + gayle->cardstatus = GAYLE_CS_WR | GAYLE_CS_DA; + gayle->config = 0; + +#if WHIPPET_DEBUG + printk("gayle->cardstatus = 0x%02x\n",gayle->cardstatus); + printk("gayle->intreq = 0x%02x\n",gayle->intreq); + printk("gayle->inten = 0x%02x\n",gayle->inten); + printk("gayle->config = 0x%02x\n",gayle->config); +#endif + + restore_flags(flags); + +#if WHIPPET_DEBUG + printk("Detected Whippet Serial Port at 0x%08x (ttyS%i)\n",(int)whippet,line); +#endif + return 0; +} + +/***** end of whippet_init() *****/ + + +/***** Module functions *****/ + +#ifdef MODULE +int init_module(void) +{ + return whippet_init(); +} + +void cleanup_module(void) +{ +#if WHIPPET_DEBUG + printk("Closing Whippet Device!\n"); +#endif + unregister_serial(line); + +#if WHIPPET_DEBUG + printk("gayle->cardstatus = 0x%02x\n",gayle->cardstatus); + printk("gayle->intreq = 0x%02x\n",gayle->intreq); + printk("gayle->inten = 0x%02x\n",gayle->inten); + printk("gayle->config = 0x%02x\n",gayle->config); +#endif + ser_reset_port(); + + gayle->cardstatus = 0; + gayle->intreq = ((gayle->intreq & 0x6c) ^ 0x6c) | GAYLE_IRQ_IDE; + gayle->inten &= GAYLE_IRQ_IDE; + +#if WHIPPET_DEBUG + printk("gayle->cardstatus = 0x%02x\n",gayle->cardstatus); + printk("gayle->intreq = 0x%02x\n",gayle->intreq); + printk("gayle->inten = 0x%02x\n",gayle->inten); + printk("gayle->config = 0x%02x\n",gayle->config); +#endif + free_irq(IRQ_AMIGA_PORTS,amiga_info); +} +#endif + +/***** end of Module functions *****/ diff --git a/drivers/char/ser_whippet.h b/drivers/char/ser_whippet.h new file mode 100644 index 000000000000..f150562fdbf3 --- /dev/null +++ b/drivers/char/ser_whippet.h @@ -0,0 +1,130 @@ +/* + * Defines for the Hisoft Whippet serial port for the Amiga 600/1200 + * range of computers. + * + * This code is (C) 1997,98 Chris Sumner (chris@cpsumner.freeserve.co.uk), + * based on 16c552.h and ser_ioext.h which are (C) 1995 Jes Sorensen. + * (jds@kom.auc.dk) + */ + +#ifndef _SER_WHIPPET_H_ +#define _SER_WHIPPET_H_ + +#define UART_CLK 7372800 +#define WHIPPET_BAUD_BASE (UART_CLK / 16) + +#define WHIPPET_PHYSADDR (0xA30600) /* from whippet.device */ + +struct WHIPPET { + u_char RBR; /* Reciever Buffer Register */ + u_char pad0[0xfff]; + u_char IER; /* Interrupt Enable Register */ + u_char pad1[0xfff]; + u_char IIR; /* Interrupt Identification Register */ + u_char pad2[0xfff]; + u_char LCR; /* Line Control Register */ + u_char pad3[0xfff]; + u_char MCR; /* Modem Control Register */ + u_char pad4[0xfff]; + u_char LSR; /* Line Status Register */ + u_char pad5[0xfff]; + u_char MSR; /* Modem Status Register */ + u_char pad6[0xfff]; + u_char SCR; /* Scratch Register */ +}; + +#define THR RBR /* Transmitter Holding Register */ +#define FCR IIR /* FIFO Control Register */ +#define DLL RBR /* Divisor Latch - LSB */ +#define DLM IER /* Divisor Latch - MSB */ + +/* + * Bit-defines for the various registers. + */ + +/* IER - Interrupt Enable Register */ + +#define ERDAI (1<<0) +#define ETHREI (1<<1) +#define ELSI (1<<2) +#define EMSI (1<<3) + +/* IIR - Interrupt Ident. Register */ + +#define IRQ_PEND (1<<0) /* NOTE: IRQ_PEND=0 implies irq pending */ +#define IRQ_ID1 (1<<1) +#define IRQ_ID2 (1<<2) +#define IRQ_ID3 (1<<3) +#define FIFO_ENA0 (1<<6) /* Both these are set when FCR(1<<0)=1 */ +#define FIFO_ENA1 (1<<7) + +#define IRQ_RLS (IRQ_ID1 | IRQ_ID2) +#define IRQ_RDA IRQ_ID2 +#define IRQ_CTI (IRQ_ID2 | IRQ_ID3) +#define IRQ_THRE IRQ_ID1 +#define IRQ_MS 0 + +/* FCR - FIFO Control Register */ + +#define FIFO_ENA (1<<0) +#define RCVR_FIFO_RES (1<<1) +#define XMIT_FIFO_RES (1<<2) +#define DMA_MODE_SEL (1<<3) +#define RCVR_TRIG_LSB (1<<6) +#define RCVR_TRIG_MSB (1<<7) + +#define FIFO_TRIG_1 0x00 +#define FIFO_TRIG_4 RCVR_TRIG_LSB +#define FIFO_TRIG_8 RCVR_TRIG_MSB +#define FIFO_TRIG_14 (RCVR_TRIG_LSB | RCVR_TRIG_MSB) + +#define FIFO_SIZE 16 + +/* LCR - Line Control Register */ + +#define WLS0 (1<<0) +#define WLS1 (1<<1) +#define STB (1<<2) +#define PEN (1<<3) +#define EPS (1<<4) +#define STICK_PARITY (1<<5) +#define SET_BREAK (1<<6) +#define DLAB (1<<7) + +#define data_5bit 0x00 +#define data_6bit 0x01 +#define data_7bit 0x02 +#define data_8bit 0x03 + + +/* MCR - Modem Control Register */ + +#define DTR (1<<0) +#define RTS (1<<1) +#define OUT1 (1<<2) +#define OUT2 (1<<3) +#define LOOP (1<<4) + +/* LSR - Line Status Register */ + +#define DR (1<<0) +#define OE (1<<1) +#define PE (1<<2) +#define FE (1<<3) +#define BI (1<<4) +#define THRE (1<<5) +#define TEMT (1<<6) +#define RCVR_FIFO_ERR (1<<7) + +/* MSR - Modem Status Register */ + +#define DCTS (1<<0) +#define DDSR (1<<1) +#define TERI (1<<2) +#define DDCD (1<<3) +#define CTS (1<<4) +#define DSR (1<<5) +#define RING_I (1<<6) +#define DCD (1<<7) + +#endif diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index a719786fcd91..4a693720a779 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include @@ -2790,6 +2790,57 @@ void serial167_write(struct console *co, const char *str, unsigned count) restore_flags(flags); } +/* This is a hack; if there are multiple chars waiting in the chip we + * discard all but the last one, and return that. The cd2401 is not really + * designed to be driven in polled mode. + */ + +int serial167_wait_key(struct console *co) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + volatile u_char sink; + u_char ier; + int port; + int keypress = 0; + + save_flags(flags); cli(); + + /* Ensure receiver is enabled! */ + + port = 0; + base_addr[CyCAR] = (u_char)port; + while (base_addr[CyCCR]) + ; + base_addr[CyCCR] = CyENB_RCVR; + ier = base_addr[CyIER]; + base_addr[CyIER] = CyRxData; + + while (!keypress) { + if (pcc2chip[PccSCCRICR] & 0x20) + { + /* We have an Rx int. Acknowledge it */ + sink = pcc2chip[PccRPIACKR]; + if ((base_addr[CyLICR] >> 2) == port) { + int cnt = base_addr[CyRFOC]; + while (cnt-- > 0) + { + keypress = base_addr[CyRDR]; + } + base_addr[CyREOIR] = 0; + } + else + base_addr[CyREOIR] = CyNOTRANS; + } + } + + base_addr[CyIER] = ier; + + restore_flags(flags); + + return keypress; +} + #ifdef CONFIG_REMOTE_DEBUG void putDebugChar (int c) { diff --git a/drivers/char/sx.c b/drivers/char/sx.c index 793f055f701b..aef6223d0bfb 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -2523,6 +2523,7 @@ int sx_init(void) else pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, &tint); + board->hw_base = tint & PCI_BASE_ADDRESS_MEM_MASK; board->base2 = board->base = (ulong) ioremap(board->hw_base, WINDOW_LEN (board)); if (!board->base) { diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c index f152792532ad..158d9463ed78 100644 --- a/drivers/isdn/act2000/module.c +++ b/drivers/isdn/act2000/module.c @@ -279,9 +279,7 @@ act2000_poll(unsigned long data) act2000_receive(card); save_flags(flags); cli(); - del_timer(&card->ptimer); - card->ptimer.expires = jiffies + 3; - add_timer(&card->ptimer); + mod_timer(&card->ptimer, jiffies+3); restore_flags(flags); } diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index 819797b2d585..c19d25e6b19e 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -602,9 +602,7 @@ icn_pollbchan(unsigned long data) /* schedule b-channel polling again */ save_flags(flags); cli(); - del_timer(&card->rb_timer); - card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD; - add_timer(&card->rb_timer); + mod_timer(&card->rb_timer, jiffies+ICN_TIMER_BCREAD); card->flags |= ICN_FLAGS_RBTIMER; restore_flags(flags); } else @@ -905,9 +903,7 @@ icn_polldchan(unsigned long data) /* schedule again */ save_flags(flags); cli(); - del_timer(&card->st_timer); - card->st_timer.expires = jiffies + ICN_TIMER_DCREAD; - add_timer(&card->st_timer); + mod_timer(&card->st_timer, jiffies+ICN_TIMER_DCREAD); restore_flags(flags); } diff --git a/drivers/isdn/isdn_audio.c b/drivers/isdn/isdn_audio.c index 3b330f38c823..e8850ee95107 100644 --- a/drivers/isdn/isdn_audio.c +++ b/drivers/isdn/isdn_audio.c @@ -183,9 +183,9 @@ static char isdn_audio_ulaw_to_alaw[] = }; #define NCOEFF 16 /* number of frequencies to be analyzed */ -#define DTMF_TRESH 50000 /* above this is dtmf */ -#define SILENCE_TRESH 100 /* below this is silence */ -#define H2_TRESH 10000 /* 2nd harmonic */ +#define DTMF_TRESH 25000 /* above this is dtmf */ +#define SILENCE_TRESH 200 /* below this is silence */ +#define H2_TRESH 20000 /* 2nd harmonic */ #define AMP_BITS 9 /* bits per sample, reduced to avoid overflow */ #define LOGRP 0 #define HIGRP 1 @@ -225,38 +225,25 @@ static char dtmf_matrix[4][4] = {'*', '0', '#', 'D'} }; - -/* - * egcs 2.95 complain about invalid asm statement: - * "fixed or forbidden register 2 (cx) was spilled for class CREG." - */ -#if ((CPU == 386) || (CPU == 486) || (CPU == 586)) && defined(__GNUC__) -#if __GNUC__ == 2 && __GNUC_MINOR__ < 95 -#define ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT -#endif -#endif - -#ifdef ISDN_AUDIO_OPTIMIZE_ON_X386_WITH_ASM_IF_GCC_ALLOW_IT static inline void -isdn_audio_tlookup(const void *table, void *buff, unsigned long n) +isdn_audio_tlookup(const u_char *table, u_char *buff, unsigned long n) { - __asm__("cld\n" +#ifdef __i386__ + unsigned long d0, d1, d2, d3; + __asm__ __volatile__( + "cld\n" "1:\tlodsb\n\t" "xlatb\n\t" "stosb\n\t" "loop 1b\n\t" - : : "b"((long) table), "c"(n), "D"((long) buff), "S"((long) buff) - : "bx", "cx", "di", "si", "ax"); -} - + : "=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3) + : "0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff) + : "memory", "ax"); #else -static inline void -isdn_audio_tlookup(const char *table, char *buff, unsigned long n) -{ while (n--) *buff++ = table[*(unsigned char *)buff]; -} #endif +} void isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len) diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index 96df8f52a133..60e2664399d3 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -69,6 +69,7 @@ isdn_divert_if *divert_if = NULL; /* interface to diversion module */ static int isdn_writebuf_stub(int, int, const u_char *, int, int); static void set_global_features(void); +static int isdn_wildmat(char *s, char *p); void isdn_MOD_INC_USE_COUNT(void) @@ -150,7 +151,7 @@ isdn_star(char *s, char *p) * [^xyz] matches any single character not in the set of characters */ -int +static int isdn_wildmat(char *s, char *p) { register int last; @@ -196,6 +197,23 @@ isdn_wildmat(char *s, char *p) return (*s == '\0')?0:nostar; } +int isdn_msncmp( const char * msn1, const char * msn2 ) +{ + char TmpMsn1[ ISDN_MSNLEN ]; + char TmpMsn2[ ISDN_MSNLEN ]; + char *p; + + for ( p = TmpMsn1; *msn1 && *msn1 != ':'; ) // Strip off a SPID + *p++ = *msn1++; + *p = '\0'; + + for ( p = TmpMsn2; *msn2 && *msn2 != ':'; ) // Strip off a SPID + *p++ = *msn2++; + *p = '\0'; + + return isdn_wildmat( TmpMsn1, TmpMsn2 ); +} + static void isdn_free_queue(struct sk_buff_head *queue) { @@ -267,9 +285,7 @@ isdn_timer_funct(ulong dummy) save_flags(flags); cli(); - del_timer(&dev->timer); - dev->timer.expires = jiffies + ISDN_TIMER_RES; - add_timer(&dev->timer); + mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES); restore_flags(flags); } } @@ -290,11 +306,8 @@ isdn_timer_ctrl(int tf, int onoff) dev->tflags |= tf; else dev->tflags &= ~tf; - if (dev->tflags) { - if (!del_timer(&dev->timer)) /* del_timer is 1, when active */ - dev->timer.expires = jiffies + ISDN_TIMER_RES; - add_timer(&dev->timer); - } + if (dev->tflags) + mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES); restore_flags(flags); } @@ -472,7 +485,7 @@ isdn_status_callback(isdn_ctrl * c) return 0; } /* Try to find a network-interface which will accept incoming call */ - r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, c->parm.setup)); + r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, &c->parm.setup)); switch (r) { case 0: /* No network-device replies. @@ -481,7 +494,7 @@ isdn_status_callback(isdn_ctrl * c) * 3 on eventually match, if CID is longer. */ if (c->command == ISDN_STAT_ICALL) - if ((retval = isdn_tty_find_icall(di, c->arg, c->parm.setup))) return(retval); + if ((retval = isdn_tty_find_icall(di, c->arg, &c->parm.setup))) return(retval); #ifdef CONFIG_ISDN_DIVERSION if (divert_if) if ((retval = divert_if->stat_callback(c))) @@ -1027,7 +1040,7 @@ isdn_read(struct file *file, char *buf, size_t count, loff_t * off) } static loff_t -isdn_lseek(struct file *file, loff_t offset, int orig) +isdn_llseek(struct file *file, loff_t offset, int orig) { return -ESPIPE; } @@ -1661,7 +1674,7 @@ isdn_close(struct inode *ino, struct file *filep) static struct file_operations isdn_fops = { - llseek: isdn_lseek, + llseek: isdn_llseek, read: isdn_read, write: isdn_write, poll: isdn_poll, @@ -1846,7 +1859,6 @@ isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb) skb_pull(nskb, sizeof(int)); if (!nskb->len) { dev_kfree_skb(nskb); - dev_kfree_skb(skb); return v110_ret; } /* V.110 must always be acknowledged */ @@ -1885,9 +1897,10 @@ isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb) atomic_inc(&dev->v110use[idx]); dev->v110[idx]->skbuser++; atomic_dec(&dev->v110use[idx]); - dev_kfree_skb(skb); /* For V.110 return unencoded data length */ ret = v110_ret; + /* if the complete frame was send we free the skb; + if not upper function will requeue the skb */ if (ret == skb->len) dev_kfree_skb(skb); } diff --git a/drivers/isdn/isdn_common.h b/drivers/isdn/isdn_common.h index 66a430712d55..b9bfee6986a9 100644 --- a/drivers/isdn/isdn_common.h +++ b/drivers/isdn/isdn_common.h @@ -50,7 +50,7 @@ extern int isdn_readbchan(int, int, u_char *, u_char *, int, struct wait_queue** extern int isdn_get_free_channel(int, int, int, int, int, char *); extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *); extern int register_isdn(isdn_if * i); -extern int isdn_wildmat(char *, char *); +extern int isdn_msncmp( const char *, const char *); extern int isdn_add_channels(driver *, int, int, int); #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) extern void isdn_dumppkt(char *, u_char *, int, int); diff --git a/drivers/isdn/isdn_net.c b/drivers/isdn/isdn_net.c index 54694b8002a1..8787e34a7a2a 100644 --- a/drivers/isdn/isdn_net.c +++ b/drivers/isdn/isdn_net.c @@ -1783,7 +1783,7 @@ isdn_net_swap_usage(int i1, int i2) * would eventually match if CID was longer. */ int -isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) +isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) { char *eaz; int si1; @@ -1799,19 +1799,19 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) /* Search name in netdev-chain */ save_flags(flags); cli(); - if (!setup.phone[0]) { + if (!setup->phone[0]) { nr[0] = '0'; nr[1] = '\0'; printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n"); } else - strcpy(nr, setup.phone); - si1 = (int) setup.si1; - si2 = (int) setup.si2; - if (!setup.eazmsn[0]) { + strcpy(nr, setup->phone); + si1 = (int) setup->si1; + si2 = (int) setup->si2; + if (!setup->eazmsn[0]) { printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n"); eaz = "0"; } else - eaz = setup.eazmsn; + eaz = setup->eazmsn; if (dev->net_verbose > 1) printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz); /* Accept only calls with Si1 = 7 (Data-Transmission) */ @@ -1842,7 +1842,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) break; } swapped = 0; - if (!(matchret = isdn_wildmat(eaz, isdn_map_eaz2msn(lp->msn, di)))) + if (!(matchret = isdn_msncmp(eaz, isdn_map_eaz2msn(lp->msn, di)))) ematch = 1; /* Remember if more numbers eventually can match */ if (matchret > wret) @@ -1934,7 +1934,7 @@ isdn_net_find_icall(int di, int ch, int idx, setup_parm setup) n = lp->phone[0]; if (lp->flags & ISDN_NET_SECURE) { while (n) { - if (!isdn_wildmat(nr, n->num)) + if (!isdn_msncmp(nr, n->num)) break; n = (isdn_net_phone *) n->next; } diff --git a/drivers/isdn/isdn_net.h b/drivers/isdn/isdn_net.h index 9159f18ac197..88cacea633e4 100644 --- a/drivers/isdn/isdn_net.h +++ b/drivers/isdn/isdn_net.h @@ -75,7 +75,7 @@ extern int isdn_net_addphone(isdn_net_ioctl_phone *); extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *); extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone *); extern int isdn_net_delphone(isdn_net_ioctl_phone *); -extern int isdn_net_find_icall(int, int, int, setup_parm); +extern int isdn_net_find_icall(int, int, int, setup_parm *); extern void isdn_net_hangup(struct device *); extern void isdn_net_dial(void); extern void isdn_net_autohup(void); diff --git a/drivers/isdn/isdn_tty.c b/drivers/isdn/isdn_tty.c index e2c62b528b83..2ec672ab02ad 100644 --- a/drivers/isdn/isdn_tty.c +++ b/drivers/isdn/isdn_tty.c @@ -2152,7 +2152,7 @@ isdn_tty_match_icall(char *cid, atemu *emu, int di) while (1) { if ((q = strchr(p, ';'))) *q = '\0'; - if ((tmp = isdn_wildmat(cid, isdn_map_eaz2msn(p, di))) > ret) + if ((tmp = isdn_msncmp(cid, isdn_map_eaz2msn(p, di))) > ret) ret = tmp; #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n", @@ -2171,7 +2171,7 @@ isdn_tty_match_icall(char *cid, atemu *emu, int di) return ret; } else { int tmp; - tmp = isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di)); + tmp = isdn_msncmp(cid, isdn_map_eaz2msn(emu->msn, di)); #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n", isdn_map_eaz2msn(emu->msn, di), tmp); @@ -2192,7 +2192,7 @@ isdn_tty_match_icall(char *cid, atemu *emu, int di) * CID is longer. */ int -isdn_tty_find_icall(int di, int ch, setup_parm setup) +isdn_tty_find_icall(int di, int ch, setup_parm *setup) { char *eaz; int i; @@ -2203,18 +2203,18 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) char *nr; ulong flags; - if (!setup.phone[0]) { + if (!setup->phone[0]) { nr = "0"; printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n"); } else - nr = setup.phone; - si1 = (int) setup.si1; - si2 = (int) setup.si2; - if (!setup.eazmsn[0]) { + nr = setup->phone; + si1 = (int) setup->si1; + si2 = (int) setup->si2; + if (!setup->eazmsn[0]) { printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n"); eaz = "0"; } else - eaz = setup.eazmsn; + eaz = setup->eazmsn; #ifdef ISDN_DEBUG_MODEM_ICALL printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2); #endif @@ -2256,8 +2256,8 @@ isdn_tty_find_icall(int di, int ch, setup_parm setup) strcpy(dev->num[idx], nr); strcpy(info->emu.cpn, eaz); info->emu.mdmreg[REG_SI1I] = si2bit[si1]; - info->emu.mdmreg[REG_PLAN] = setup.plan; - info->emu.mdmreg[REG_SCREEN] = setup.screen; + info->emu.mdmreg[REG_PLAN] = setup->plan; + info->emu.mdmreg[REG_SCREEN] = setup->screen; isdn_info_update(); restore_flags(flags); printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr, diff --git a/drivers/isdn/isdn_tty.h b/drivers/isdn/isdn_tty.h index 2921f9659486..ca1030262c1b 100644 --- a/drivers/isdn/isdn_tty.h +++ b/drivers/isdn/isdn_tty.h @@ -113,7 +113,7 @@ extern void isdn_tty_carrier_timeout(void); extern void isdn_tty_modem_xmit(void); extern int isdn_tty_modem_init(void); extern void isdn_tty_readmodem(void); -extern int isdn_tty_find_icall(int, int, setup_parm); +extern int isdn_tty_find_icall(int, int, setup_parm *); extern void isdn_tty_cleanup_xmit(modem_info *); extern int isdn_tty_stat_callback(int, isdn_ctrl *); extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *); diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 5a4c69613031..0e068813d874 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -319,6 +319,7 @@ tristate 'MultiGate (COMX) synchronous serial boards support' CONFIG_COMX dep_tristate ' Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX fi + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Generic HDLC driver (EXPERIMENTAL)' CONFIG_HDLC if [ "$CONFIG_HDLC" != "n" ]; then @@ -362,6 +363,11 @@ if [ "$CONFIG_WAN_ROUTER" != "n" ]; then fi fi +# +# Xpeed drivers +# +tristate 'Xpeed X200/X300 DSL NIC support' CONFIG_XPEED + endmenu # diff --git a/drivers/net/Makefile b/drivers/net/Makefile index f077ac82ad68..85e41fc3c81c 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -112,6 +112,11 @@ L_OBJS += daynaport.o CONFIG_8390_BUILTIN = y endif +ifeq ($(CONFIG_MAC8390), y) +L_OBJS += mac8390.o +CONFIG_8390_BUILTIN = y +endif + ifeq ($(CONFIG_APNE),y) L_OBJS += apne.o CONFIG_8390_BUILTIN = y @@ -976,6 +981,16 @@ else endif endif +ifeq ($(CONFIG_XPEED),y) + SUB_DIRS += xpds + MOD_IN_SUB_DIRS += xpds + L_OBJS += xpds/xpds-fr.o +else + ifeq ($(CONFIG_XPEED),m) + MOD_IN_SUB_DIRS += xpds + endif +endif + # If anything built-in uses Z85230, then build it into the kernel also. # If not, but a module uses it, build as a module. @@ -1101,6 +1116,17 @@ else M_OBJS += hplance.o endif endif + +ifeq ($(CONFIG_MVME147_NET),y) +L_OBJS += mvme147.o +CONFIG_7990_BUILTIN = y +else + ifeq ($(CONFIG_MVME147_NET),m) + CONFIG_7990_MODULE = y + M_OBJS += mvme147.o + endif +endif + # If we need generic LANCE support, either in the kernel or as a module, # build it in the appropriate way. ifdef CONFIG_7990_BUILTIN @@ -1233,6 +1259,14 @@ else endif endif +ifeq ($(CONFIG_MAC89x0),y) +L_OBJS += mac89x0.o +else + ifeq ($(CONFIG_MAC89x0),m) + M_OBJS += mac89x0.o + endif +endif + ifeq ($(CONFIG_LTPC),y) L_OBJS += ltpc.o else @@ -1273,6 +1307,18 @@ else endif endif +ifeq ($(CONFIG_MACMACE),y) +L_OBJS += macmace.o +endif + +ifeq ($(CONFIG_MACSONIC),y) +L_OBJS += macsonic.o +else + ifeq ($(CONFIG_MACSONIC),m) + M_OBJS += macsonic.o + endif +endif + ifeq ($(CONFIG_BMAC),y) L_OBJS += bmac.o else diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c index 36fed9730128..92075a95f92f 100644 --- a/drivers/net/ariadne.c +++ b/drivers/net/ariadne.c @@ -399,8 +399,8 @@ static void ariadne_interrupt(int irq, void *data, struct pt_regs *fp) board->Lance.RAP = CSR0; /* PCnet-ISA Controller Status */ - if (!(board->Lance.RDP & INTR)) /* Check if any interrupt has been - return; generated by the board. */ + if (!(board->Lance.RDP & INTR)) /* Check if any interrupt has been */ + return; /* generated by the board. */ if (dev->interrupt) printk("%s: Re-entering the interrupt handler.\n", dev->name); diff --git a/drivers/net/ariadne2.c b/drivers/net/ariadne2.c index c58a90932668..190e27c4d59c 100644 --- a/drivers/net/ariadne2.c +++ b/drivers/net/ariadne2.c @@ -178,6 +178,7 @@ __initfunc(static int ariadne2_init(struct device *dev, unsigned int key, name = "NE2000"; dev->base_addr = ioaddr; + dev->irq = IRQ_AMIGA_PORTS; /* Install the Interrupt handler */ if (request_irq(IRQ_AMIGA_PORTS, ei_interrupt, 0, "AriadNE2 Ethernet", @@ -192,7 +193,9 @@ __initfunc(static int ariadne2_init(struct device *dev, unsigned int key, ((struct ei_device *)dev->priv)->priv = key; for(i = 0; i < ETHER_ADDR_LEN; i++) { +#ifdef DEBUG printk(" %2.2x", SA_prom[i]); +#endif dev->dev_addr[i] = SA_prom[i]; } diff --git a/drivers/net/atari_bionet.c b/drivers/net/atari_bionet.c index f17950109b9c..b58839a6ddf3 100644 --- a/drivers/net/atari_bionet.c +++ b/drivers/net/atari_bionet.c @@ -446,7 +446,7 @@ bionet_send_packet(struct sk_buff *skb, struct device *dev) { } else { int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - unsigned long buf = VTOP(skb->data); + unsigned long buf = virt_to_phys(skb->data); int stat; stdma_lock(bionet_intr, NULL); diff --git a/drivers/net/atari_pamsnet.c b/drivers/net/atari_pamsnet.c index 269b7de15412..c28642750543 100644 --- a/drivers/net/atari_pamsnet.c +++ b/drivers/net/atari_pamsnet.c @@ -439,7 +439,7 @@ inquiry (target, buffer) unsigned char *buffer; { int ret = -1; - unsigned char *vbuffer = (unsigned char *)PTOV(buffer); + unsigned char *vbuffer = phys_to_virt((unsigned long)buffer); unsigned char cmd_buffer[5]; if (send_first(target, INQUIRY)) @@ -487,7 +487,7 @@ static HADDR !acsi_wait_for_IRQ(TIMEOUTDMA) || get_status()) goto bad; - ret = (HADDR *)PTOV(&(((DMAHWADDR *)buffer)->hwaddr)); + ret = phys_to_virt(&(((DMAHWADDR *)buffer)->hwaddr)); dma_cache_maintenance((unsigned long)buffer, 512, 0); bad: return (ret); @@ -707,7 +707,7 @@ pamsnet_send_packet(struct sk_buff *skb, struct device *dev) { } else { int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - unsigned long buf = VTOP(skb->data); + unsigned long buf = virt_to_phys(skb->data); int stat; stdma_lock(pamsnet_intr, NULL); diff --git a/drivers/net/daynaport.c b/drivers/net/daynaport.c index 777a1dcab5b6..838ac443ceb5 100644 --- a/drivers/net/daynaport.c +++ b/drivers/net/daynaport.c @@ -1,4 +1,4 @@ -/* mac_ns8390.c: A Macintosh 8390 based ethernet driver for linux. */ +/* daynaport.c: A Macintosh 8390 based ethernet driver for linux. */ /* Derived from code: @@ -15,14 +15,20 @@ The block output routines may be wrong for non Dayna cards - Reading MAC addresses -*/ + Fix this driver so that it will attempt to use the info + (i.e. iobase, iosize) given to it by the new and improved + NuBus code. + + Despite its misleading filename, this driver is not Dayna-specific + anymore. */ +/* Cabletron E6100 card support added by Tony Mantler (eek@escape.ca) April 1999 */ static const char *version = - "mac_ns8390.c:v0.01 7/5/97 Alan Cox (Alan.Cox@linux.org)\n"; + "daynaport.c: v0.02 1999-05-17 Alan Cox (Alan.Cox@linux.org) and others\n"; +static int version_printed = 0; #include - +#include #include #include #include @@ -31,20 +37,26 @@ static const char *version = #include #include #include +#include #include #include #include #include "8390.h" -int ns8390_probe1(struct device *dev, int word16, char *name, int id, int prom); +extern int console_loglevel; + +int ns8390_probe1(struct device *dev, int word16, char *name, int id, + int prom, struct nubus_dev *ndev); static int ns8390_open(struct device *dev); static void ns8390_no_reset(struct device *dev); static int ns8390_close_card(struct device *dev); +/* Interlan */ static void interlan_reset(struct device *dev); +/* Dayna */ static void dayna_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void dayna_block_input(struct device *dev, int count, @@ -52,6 +64,7 @@ static void dayna_block_input(struct device *dev, int count, static void dayna_block_output(struct device *dev, int count, const unsigned char *buf, const int start_page); +/* Sane (32-bit chunk memory read/write) */ static void sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void sane_block_input(struct device *dev, int count, @@ -59,6 +72,7 @@ static void sane_block_input(struct device *dev, int count, static void sane_block_output(struct device *dev, int count, const unsigned char *buf, const int start_page); +/* Slow Sane (16-bit chunk memory read/write) */ static void slow_sane_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void slow_sane_block_input(struct device *dev, int count, @@ -71,6 +85,10 @@ static void slow_sane_block_output(struct device *dev, int count, #define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */ #define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */ +#define CABLETRON_RX_START_PG 0x00 /* First page of RX buffer */ +#define CABLETRON_RX_STOP_PG 0x30 /* Last page +1 of RX ring */ +#define CABLETRON_TX_START_PG CABLETRON_RX_STOP_PG /* First page of TX buffer */ + #define DAYNA_MAC_BASE 0xf0007 #define DAYNA_8390_BASE 0x80000 /* 3 */ @@ -81,9 +99,14 @@ static void slow_sane_block_output(struct device *dev, int count, #define APPLE_8390_MEM 0xD0000 #define APPLE_MEMSIZE 8192 /* FIXME: need to dynamically check */ -#define KINETICS_8390_BASE 0x80003 -#define KINETICS_8390_MEM 0x00000 +#define KINETICS_MAC_BASE 0xf0004 /* first byte of each long */ +#define KINETICS_8390_BASE 0x80000 +#define KINETICS_8390_MEM 0x00000 /* first word of each long */ #define KINETICS_MEMSIZE 8192 /* FIXME: need to dynamically check */ +/*#define KINETICS_MEMSIZE (0x10000/2) * CSA: on the board I have, at least */ + +#define CABLETRON_8390_BASE 0x90000 +#define CABLETRON_8390_MEM 0x00000 static int test_8390(volatile char *ptr, int scale) { @@ -113,34 +136,59 @@ static int test_8390(volatile char *ptr, int scale) * Identify the species of NS8390 card/driver we need */ -#define NS8390_DAYNA 1 -#define NS8390_INTERLAN 2 -#define NS8390_KINETICS 3 -#define NS8390_APPLE 4 -#define NS8390_FARALLON 5 -#define NS8390_ASANTE 6 +enum mac8390_type { + NS8390_DAYNA, + NS8390_INTERLAN, + NS8390_KINETICS, + NS8390_APPLE, + NS8390_FARALLON, + NS8390_ASANTE, + NS8390_CABLETRON +}; -int ns8390_ident(struct nubus_type *nb) +__initfunc(int ns8390_ident(struct nubus_dev* ndev)) { - /* It appears anything with a software type of 0 is an apple - compatible - even if the hardware matches others */ - - if(nb->DrSW==0x0001 || nb->DrSW==0x0109 || nb->DrSW==0x0000 || nb->DrSW==0x0100) - return NS8390_APPLE; - + /* This really needs to be tested and tested hard. */ + + /* Summary of what we know so far -- + * SW: 0x0104 -- asante, 16 bit, back4_offsets + * SW: 0x010b -- daynaport, 16 bit, fwrd4_offsets + * SW: 0x010c -- farallon, 16 bit, back4_offsets, no long word access + * SW: 0x011a -- focus, [no details yet] + * SW: ?????? -- interlan, 16 bit, back4_offsets, funny reset + * SW: ?????? -- kinetics, 8 bit, back4_offsets + * -- so i've this hypothesis going that says DrSW&1 says whether the + * map is forward or backwards -- and maybe DrSW&256 says what the + * register spacing is -- for all cards that report a DrSW in some + * range. + * This would allow the "apple compatible" driver to drive many + * seemingly different types of cards. More DrSW info is needed + * to investigate this properly. [CSA, 21-May-1999] + */ /* Dayna ex Kinetics board */ - if(nb->DrHW==0x0103) + if(ndev->dr_sw == NUBUS_DRSW_DAYNA) return NS8390_DAYNA; - - /* Asante board */ - if(nb->DrHW==0x0104) + if(ndev->dr_sw == NUBUS_DRSW_ASANTE) return NS8390_ASANTE; - if(nb->DrHW==0x0100) - return NS8390_INTERLAN; - if(nb->DrHW==0x0106) - return NS8390_KINETICS; - if(nb->DrSW==0x010C) + if(ndev->dr_sw == NUBUS_DRSW_FARALLON) /* farallon or sonic systems */ return NS8390_FARALLON; + if(ndev->dr_sw == NUBUS_DRSW_KINETICS) + return NS8390_KINETICS; + /* My ATI Engineering card with this combination crashes the */ + /* driver trying to xmit packets. Best not touch it for now. */ + /* - 1999-05-20 (funaho@jurai.org) */ + if(ndev->dr_sw == NUBUS_DRSW_FOCUS) + return -1; + + /* Check the HW on this one, because it shares the same DrSW as + the on-board SONIC chips */ + if(ndev->dr_hw == NUBUS_DRHW_CABLETRON) + return NS8390_CABLETRON; + /* does anyone have one of these? */ + if(ndev->dr_hw == NUBUS_DRHW_INTERLAN) + return NS8390_INTERLAN; + + /* FIXME: what do genuine Apple boards look like? */ return -1; } @@ -148,7 +196,7 @@ int ns8390_ident(struct nubus_type *nb) * Memory probe for 8390 cards */ -int apple_8390_mem_probe(volatile unsigned short *p) +__initfunc(int apple_8390_mem_probe(volatile unsigned short *p)) { int i, j; /* @@ -192,61 +240,79 @@ int apple_8390_mem_probe(volatile unsigned short *p) /* * Probe for 8390 cards. * The ns8390_probe1() routine initializes the card and fills the - * station address field. On entry base_addr is set, irq is set - * (These come from the nubus probe code). dev->mem_start points + * station address field. + * + * The NuBus interface has changed! We now scan for these somewhat + * like how the PCI and Zorro drivers do. It's not clear whether + * this is actually better, but it makes things more consistent. + * + * dev->mem_start points * at the memory ring, dev->mem_end gives the end of it. */ -int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type *match) +__initfunc(int mac8390_probe(struct device *dev)) { - struct device *dev; + static int slots = 0; volatile unsigned short *i; volatile unsigned char *p; int plen; int id; + static struct nubus_dev* ndev = NULL; + + /* Find the first card that hasn't already been seen */ + while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, + NUBUS_TYPE_ETHERNET, ndev)) != NULL) { + /* Have we seen it already? */ + if (slots & (1<board->slot)) + continue; + slots |= 1<board->slot; + + /* Is it one of ours? */ + if ((id = ns8390_ident(ndev)) != -1) + break; + } - if(match->category!=NUBUS_CAT_NETWORK || match->type!=1) - return -ENODEV; - /* Ok so it is an ethernet network device */ - if((id=ns8390_ident(match))==-1) - { - printk("Ethernet but type unknown %d\n",match->DrHW); + /* Hm. No more cards, then */ + if (ndev == NULL) return -ENODEV; + + dev = init_etherdev(dev, 0); + + if (!version_printed) { + printk(KERN_INFO "%s", version); + version_printed = 1; } - dev = init_etherdev(0, 0); - if(dev==NULL) - return -ENOMEM; /* * Dayna specific init */ if(id==NS8390_DAYNA) { - dev->base_addr=(int)(nubus_slot_addr(slot)+DAYNA_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+DAYNA_8390_MEM); - dev->mem_end=dev->mem_start+DAYNA_MEMSIZE; /* 8K it seems */ + dev->base_addr = (int)(ndev->board->slot_addr+DAYNA_8390_BASE); + dev->mem_start = (int)(ndev->board->slot_addr+DAYNA_8390_MEM); + dev->mem_end = dev->mem_start+DAYNA_MEMSIZE; /* 8K it seems */ - printk("daynaport: testing board: "); - + printk(KERN_INFO "%s: daynaport. testing board: ", dev->name); + printk("memory - "); - - i=(void *)dev->mem_start; + + i = (void *)dev->mem_start; memset((void *)i,0xAA, DAYNA_MEMSIZE); while(i<(volatile unsigned short *)dev->mem_end) { if(*i!=0xAAAA) goto membad; - *i=0x5555; - if(*i!=0x5555) + *i=0x5678; /* make sure we catch byte smearing */ + if(*i!=0x5678) goto membad; i+=2; /* Skip a word */ } - + printk("controller - "); - + p=(void *)dev->base_addr; plen=0; - + while(plen<0x3FF00) { if(test_8390(p,0)==0) @@ -263,26 +329,71 @@ int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type * if(plen==0x3FF00) goto membad; printk("OK\n"); - dev->irq=slot; - if(ns8390_probe1(dev, 0, "dayna", id, -1)==0) - return 0; + dev->irq = SLOT2IRQ(ndev->board->slot); + if(ns8390_probe1(dev, 0, "dayna", id, -1, ndev)==0) + return 0; + } + /* Cabletron */ + if (id==NS8390_CABLETRON) { + int memsize = 16<<10; /* fix this */ + + dev->base_addr=(int)(ndev->board->slot_addr+CABLETRON_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+CABLETRON_8390_MEM); + dev->mem_end=dev->mem_start+memsize; + dev->irq = SLOT2IRQ(ndev->board->slot); + + /* The base address is unreadable if 0x00 has been written to the command register */ + /* Reset the chip by writing E8390_NODMA+E8390_PAGE0+E8390_STOP just to be sure */ + i = (void *)dev->base_addr; + *i = 0x21; + + printk(KERN_INFO "%s: cabletron: testing board: ", dev->name); + printk("%dK memory - ", memsize>>10); + i=(void *)dev->mem_start; + while(i<(volatile unsigned short *)(dev->mem_start+memsize)) + { + *i=0xAAAA; + if(*i!=0xAAAA) + goto membad; + *i=0x5555; + if(*i!=0x5555) + goto membad; + i+=2; /* Skip a word */ + } + printk("OK\n"); + + if(ns8390_probe1(dev, 1, "cabletron", id, -1, ndev)==0) + return 0; } /* Apple, Farallon, Asante */ - if(id==NS8390_APPLE|| id==NS8390_FARALLON || id==NS8390_ASANTE) + if(id==NS8390_APPLE || id==NS8390_FARALLON || id==NS8390_ASANTE) { int memsize; - - dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM); - + + dev->base_addr=(int)(ndev->board->slot_addr+APPLE_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+APPLE_8390_MEM); + memsize = apple_8390_mem_probe((void *)dev->mem_start); - + dev->mem_end=dev->mem_start+memsize; - dev->irq=slot; - printk("apple/clone: testing board: "); - + dev->irq = SLOT2IRQ(ndev->board->slot); + + switch(id) + { + case NS8390_FARALLON: + printk(KERN_INFO "%s: farallon: testing board: ", dev->name); + break; + case NS8390_ASANTE: + printk(KERN_INFO "%s: asante: testing board: ", dev->name); + break; + case NS8390_APPLE: + default: + printk(KERN_INFO "%s: apple/clone: testing board: ", dev->name); + break; + } + printk("%dK memory - ", memsize>>10); - + i=(void *)dev->mem_start; memset((void *)i,0xAA, memsize); while(i<(volatile unsigned short *)dev->mem_end) @@ -295,51 +406,75 @@ int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type * i+=2; /* Skip a word */ } printk("OK\n"); - - if(id==NS8390_FARALLON) + + switch (id) { - if(ns8390_probe1(dev, 1, "farallon", id, -1)==0) + case NS8390_FARALLON: + if(ns8390_probe1(dev, 1, "farallon", id, -1, ndev)==0) return 0; - } - else - { - if(ns8390_probe1(dev, 1, "apple/clone", id, -1)==0) - return 0; + break; + case NS8390_ASANTE: + if(ns8390_probe1(dev, 1, "asante", id, -1, ndev)==0) + return 0; + break; + case NS8390_APPLE: + default: + if(ns8390_probe1(dev, 1, "apple/clone", id, -1, ndev)==0) + return 0; + break; } } /* Interlan */ if(id==NS8390_INTERLAN) { /* As apple and asante */ - dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM); + dev->base_addr=(int)(ndev->board->slot_addr+APPLE_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+APPLE_8390_MEM); dev->mem_end=dev->mem_start+APPLE_MEMSIZE; /* 8K it seems */ - dev->irq=slot; - if(ns8390_probe1(dev, 1, "interlan", id, -1)==0) + dev->irq = SLOT2IRQ(ndev->board->slot); + if(ns8390_probe1(dev, 1, "interlan", id, -1, ndev)==0) return 0; } - /* Kinetics */ + /* Kinetics (Shiva Etherport) */ if(id==NS8390_KINETICS) { - dev->base_addr=(int)(nubus_slot_addr(slot)+KINETICS_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+KINETICS_8390_MEM); + dev->base_addr=(int)(ndev->board->slot_addr+KINETICS_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+KINETICS_8390_MEM); dev->mem_end=dev->mem_start+KINETICS_MEMSIZE; /* 8K it seems */ - dev->irq=slot; - if(ns8390_probe1(dev, 0, "kinetics", id, -1)==0) + dev->irq = SLOT2IRQ(ndev->board->slot); + if(ns8390_probe1(dev, 0, "kinetics", id, -1, ndev)==0) return 0; } - kfree(dev); + + /* We should hopefully not get here */ + printk(KERN_ERR "Probe unsucessful.\n"); return -ENODEV; -membad: - printk("failed.\n"); - kfree(dev); + + membad: + printk(KERN_ERR "failed at %p in %p - %p.\n", i, + (void *)dev->mem_start, (void *)dev->mem_end); return -ENODEV; } -int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, int promoff) +__initfunc(int mac8390_ethernet_addr(struct nubus_dev* ndev, + unsigned char addr[6])) { - static unsigned version_printed = 0; + struct nubus_dir dir; + struct nubus_dirent ent; + + /* Get the functional resource for this device */ + if (nubus_get_func_dir(ndev, &dir) == -1) + return -1; + if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1) + return -1; + + nubus_get_rsrc_mem(addr, &ent, 6); + return 0; +} +__initfunc(int ns8390_probe1(struct device *dev, int word16, char *model_name, + int type, int promoff, struct nubus_dev *ndev)) +{ static u32 fwrd4_offsets[16]={ 0, 4, 8, 12, 16, 20, 24, 28, @@ -352,25 +487,19 @@ int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, in 28, 24, 20, 16, 12, 8, 4, 0 }; + static u32 fwrd2_offsets[16]={ + 0, 2, 4, 6, + 8, 10, 12, 14, + 16, 18, 20, 22, + 24, 26, 28, 30 + }; - unsigned char *prom=((unsigned char *)nubus_slot_addr(dev->irq))+promoff; + unsigned char *prom = (unsigned char*) ndev->board->slot_addr + promoff; - if (ei_debug && version_printed++ == 0) - printk(version); - - /* Snarf the interrupt now. There's no point in waiting since we cannot - share a slot! and the board will usually be enabled. */ - if (nubus_request_irq(dev->irq, dev, ei_interrupt)) - { - printk (" unable to get nubus IRQ %d.\n", dev->irq); - return EAGAIN; - } - /* Allocate dev->priv and fill in 8390 specific dev fields. */ if (ethdev_init(dev)) { - printk (" unable to get memory for dev->priv.\n"); - nubus_free_irq(dev->irq); + printk ("%s: unable to get memory for dev->priv.\n", dev->name); return -ENOMEM; } @@ -378,16 +507,25 @@ int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, in ei_status.name = model_name; ei_status.word16 = word16; - ei_status.tx_start_page = WD_START_PG; - ei_status.rx_start_page = WD_START_PG + TX_PAGES; - dev->rmem_start = dev->mem_start + TX_PAGES*256; - ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; - dev->rmem_end = dev->mem_end; + if (type==NS8390_CABLETRON) { + /* Cabletron card puts the RX buffer before the TX buffer */ + ei_status.tx_start_page = CABLETRON_TX_START_PG; + ei_status.rx_start_page = CABLETRON_RX_START_PG; + ei_status.stop_page = CABLETRON_RX_STOP_PG; + dev->rmem_start = dev->mem_start; + dev->rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256; + } else { + ei_status.tx_start_page = WD_START_PG; + ei_status.rx_start_page = WD_START_PG + TX_PAGES; + ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; + dev->rmem_start = dev->mem_start + TX_PAGES*256; + dev->rmem_end = dev->mem_end; + } if(promoff==-1) /* Use nubus resources ? */ { - if(nubus_ethernet_addr(dev->irq /* slot */, dev->dev_addr)) + if(mac8390_ethernet_addr(ndev, dev->dev_addr)) { printk("mac_ns8390: MAC address not in resources!\n"); return -ENODEV; @@ -400,7 +538,7 @@ int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, in /* These should go in the end I hope */ if(type==NS8390_DAYNA) x=2; - if(type==NS8390_INTERLAN) + if(type==NS8390_INTERLAN || type==NS8390_KINETICS) x=4; while(i<6) { @@ -412,12 +550,24 @@ int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, in } } - printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", - model_name, dev->irq, dev->mem_start, dev->mem_end-1); + printk(KERN_INFO "%s: %s in slot %X (type %s)\n", + dev->name, ndev->board->name, ndev->board->slot, model_name); + printk(KERN_INFO "MAC "); + { + int i; + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } + } + printk(" IRQ %d, shared memory at %#lx-%#lx.\n", + dev->irq, dev->mem_start, dev->mem_end-1); switch(type) { case NS8390_DAYNA: /* Dayna card */ + case NS8390_KINETICS: /* Kinetics -- 8 bit config, but 16 bit mem */ /* 16 bit, 4 word offsets */ ei_status.reset_8390 = &ns8390_no_reset; ei_status.block_input = &dayna_block_input; @@ -425,6 +575,15 @@ int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, in ei_status.get_8390_hdr = &dayna_get_8390_hdr; ei_status.reg_offset = fwrd4_offsets; break; + case NS8390_CABLETRON: /* Cabletron */ + /* 16 bit card, register map is short forward */ + ei_status.reset_8390 = &ns8390_no_reset; + /* Ctron card won't accept 32bit values read or written to it */ + ei_status.block_input = &slow_sane_block_input; + ei_status.block_output = &slow_sane_block_output; + ei_status.get_8390_hdr = &slow_sane_get_8390_hdr; + ei_status.reg_offset = fwrd2_offsets; + break; case NS8390_FARALLON: case NS8390_APPLE: /* Apple/Asante/Farallon */ /* 16 bit card, register map is reversed */ @@ -450,6 +609,8 @@ int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, in ei_status.get_8390_hdr = &sane_get_8390_hdr; ei_status.reg_offset = back4_offsets; break; +#if 0 /* i think this suffered code rot. my kinetics card has much + * different settings. -- CSA [22-May-1999] */ case NS8390_KINETICS: /* Kinetics */ /* 8bit card, map is forward */ ei_status.reset_8390 = &ns8390_no_reset; @@ -458,6 +619,7 @@ int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, in ei_status.get_8390_hdr = &sane_get_8390_hdr; ei_status.reg_offset = back4_offsets; break; +#endif default: panic("Detected a card I can't drive - whoops\n"); } @@ -472,6 +634,19 @@ int ns8390_probe1(struct device *dev, int word16, char *model_name, int type, in static int ns8390_open(struct device *dev) { ei_open(dev); + + /* At least on my card (a Focus Enhancements PDS card) I start */ + /* getting interrupts right away, so the driver needs to be */ + /* completely initialized before enabling the interrupt. */ + /* - funaho@jurai.org (1999-05-17) */ + + /* Non-slow interrupt, works around issues with the SONIC driver */ + if (request_irq(dev->irq, ei_interrupt, 0, "8390 Ethernet", dev)) + { + printk ("%s: unable to get IRQ %d.\n", dev->name, dev->irq); + return EAGAIN; + } + MOD_INC_USE_COUNT; return 0; } @@ -489,24 +664,19 @@ static int ns8390_close_card(struct device *dev) { if (ei_debug > 1) printk("%s: Shutting down ethercard.\n", dev->name); + free_irq(dev->irq, dev); ei_close(dev); MOD_DEC_USE_COUNT; return 0; } -struct nubus_device_specifier nubus_8390={ - ns8390_probe, - NULL -}; - - /* * Interlan Specific Code Starts Here */ static void interlan_reset(struct device *dev) { - unsigned char *target=nubus_slot_addr(dev->irq); + unsigned char *target=nubus_slot_addr(IRQ2SLOT(dev->irq)); if (ei_debug > 1) printk("Need to reset the NS8390 t=%lu...", jiffies); ei_status.txing = 0; @@ -531,16 +701,23 @@ static void interlan_reset(struct device *dev) The only complications are that the ring buffer wraps. */ -static void dayna_cpu_memcpy(struct device *dev, void *to, int from, int count) +static void dayna_memcpy_fromcard(struct device *dev, void *to, int from, int count) { volatile unsigned short *ptr; unsigned short *target=to; from<<=1; /* word, skip overhead */ ptr=(unsigned short *)(dev->mem_start+from); + /* + * Leading byte? + */ + if (from&2) { + *((char *)target)++ = *(((char *)ptr++)-1); + count--; + } while(count>=2) { *target++=*ptr++; /* Copy and */ - ptr++; /* Cruft and */ + ptr++; /* skip cruft */ count-=2; } /* @@ -554,16 +731,24 @@ static void dayna_cpu_memcpy(struct device *dev, void *to, int from, int count) } } -static void cpu_dayna_memcpy(struct device *dev, int to, const void *from, int count) +static void dayna_memcpy_tocard(struct device *dev, int to, const void *from, int count) { volatile unsigned short *ptr; const unsigned short *src=from; to<<=1; /* word, skip overhead */ ptr=(unsigned short *)(dev->mem_start+to); + /* + * Leading byte? + */ + if (to&2) { /* avoid a byte write (stomps on other data) */ + ptr[-1] = (ptr[-1]&0xFF00)|*((unsigned char *)src)++; + ptr++; + count--; + } while(count>=2) { *ptr++=*src++; /* Copy and */ - ptr++; /* Cruft and */ + ptr++; /* skip cruft */ count-=2; } /* @@ -573,14 +758,15 @@ static void cpu_dayna_memcpy(struct device *dev, int to, const void *from, int c { /* Big endian */ unsigned short v=*src; - *((char *)ptr)=v>>8; + /* card doesn't like byte writes */ + *ptr=(*ptr&0x00FF)|(v&0xFF00); } } static void dayna_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) { unsigned long hdr_start = (ring_page - WD_START_PG)<<8; - dayna_cpu_memcpy(dev, (void *)hdr, hdr_start, 4); + dayna_memcpy_fromcard(dev, (void *)hdr, hdr_start, 4); /* Register endianism - fix here rather than 8390.c */ hdr->count=(hdr->count&0xFF)<<8|(hdr->count>>8); } @@ -599,14 +785,14 @@ static void dayna_block_input(struct device *dev, int count, struct sk_buff *skb { /* We must wrap the input move. */ int semi_count = dev->rmem_end - xfer_start; - dayna_cpu_memcpy(dev, skb->data, xfer_base, semi_count); + dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count); count -= semi_count; - dayna_cpu_memcpy(dev, skb->data + semi_count, + dayna_memcpy_fromcard(dev, skb->data + semi_count, dev->rmem_start - dev->mem_start, count); } else { - dayna_cpu_memcpy(dev, skb->data, xfer_base, count); + dayna_memcpy_fromcard(dev, skb->data, xfer_base, count); } } @@ -615,7 +801,7 @@ static void dayna_block_output(struct device *dev, int count, const unsigned cha { long shmem = (start_page - WD_START_PG)<<8; - cpu_dayna_memcpy(dev, shmem, buf, count); + dayna_memcpy_tocard(dev, shmem, buf, count); } /* @@ -739,6 +925,7 @@ static void slow_sane_block_output(struct device *dev, int count, const unsigned * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c daynaport.c" * version-control: t + * c-basic-offset: 4 * tab-width: 4 * kept-new-versions: 5 * End: diff --git a/drivers/net/mac8390.c b/drivers/net/mac8390.c new file mode 100644 index 000000000000..042b34996b88 --- /dev/null +++ b/drivers/net/mac8390.c @@ -0,0 +1,612 @@ +/* mac8390.c: New driver for 8390-based Nubus (or Nubus-alike) + Ethernet cards on Linux */ +/* Based on the former daynaport.c driver, by Alan Cox. Some code + taken from or inspired by skeleton.c by Donald Becker, acenic.c by + Jes Sorensen, and ne2k-pci.c by Donald Becker and Paul Gortmaker. + + This software may be used and distributed according to the terms of + the GNU Public License, incorporated herein by reference. */ + +/* 2000-02-28: support added for Dayna and Kinetics cards by + A.G.deWijn@phys.uu.nl */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "8390.h" + +#if (LINUX_VERSION_CODE < 0x02030e) +#define net_device device +#endif + +#define WD_START_PG 0x00 /* First page of TX buffer */ +#define CABLETRON_RX_START_PG 0x00 /* First page of RX buffer */ +#define CABLETRON_RX_STOP_PG 0x30 /* Last page +1 of RX ring */ +#define CABLETRON_TX_START_PG CABLETRON_RX_STOP_PG /* First page of TX buffer */ + +/* Unfortunately it seems we have to hardcode these for the moment */ +/* Shouldn't the card know about this? Does anyone know where to read it off the card? Do we trust the data provided by the card? */ +#define APPLE_8390_BASE 0xE0000 + +#define DAYNA_8390_BASE 0x80000 +#define DAYNA_8390_MEM 0x00000 + +#define KINETICS_8390_BASE 0x80000 +#define KINETICS_8390_MEM 0x00000 + +#define CABLETRON_8390_BASE 0x90000 + +enum mac8390_type { + MAC8390_NONE = -1, + MAC8390_APPLE, + MAC8390_ASANTE, + MAC8390_FARALLON, /* Apple, Asante, and Farallon are all compatible */ + MAC8390_CABLETRON, + MAC8390_DAYNA, + MAC8390_INTERLAN, + MAC8390_KINETICS, + MAC8390_FOCUS, + MAC8390_SONICSYS +}; + +static const char * cardname[] = { + "apple", + "asante", + "farallon", + "cabletron", + "dayna", + "interlan", + "kinetics", + "focus", + "sonic systems" +}; + +static int word16[] = { + 1, /* apple */ + 1, /* asante */ + 1, /* farallon */ + 1, /* cabletron */ + 0, /* dayna */ + 1, /* interlan */ + 0, /* kinetics */ + 1, /* focus (??) */ + 1 /* sonic systems (??) */ +}; + +/* on which cards do we use NuBus resources? */ +static int useresources[] = { + 1, /* apple */ + 1, /* asante */ + 1, /* farallon */ + 0, /* cabletron */ + 0, /* dayna */ + 0, /* interlan */ + 0, /* kinetics */ + 0, /* focus (??) */ + 0 /* sonic systems (??) */ +}; + +static const char __initdata * version = + "mac8390.c: v0.2 2000-03-25 David Huggins-Daines and others\n"; + +extern int mac8390_probe(struct net_device * dev); +extern enum mac8390_type mac8390_ident(struct nubus_dev * dev); +extern int mac8390_memsize(unsigned long membase); +extern int mac8390_promaddr(struct net_device * dev); +extern int mac8390_memtest(struct net_device * dev); +extern int mac8390_initdev(struct net_device * dev, struct nubus_dev * ndev, + enum mac8390_type type); + +static int mac8390_open(struct net_device * dev); +static int mac8390_close(struct net_device * dev); +static void mac8390_no_reset(struct device *dev); + +/* Sane (32-bit chunk memory read/write) - Apple/Asante/Farallon do this*/ +static void sane_get_8390_hdr(struct device *dev, + struct e8390_pkt_hdr *hdr, int ring_page); +static void sane_block_input(struct net_device * dev, int count, + struct sk_buff * skb, int ring_offset); +static void sane_block_output(struct net_device * dev, int count, + const unsigned char * buf, const int start_page); + +/* dayna_memcpy to and from card */ +static void dayna_memcpy_fromcard(struct device *dev, void *to, + int from, int count); +static void dayna_memcpy_tocard(struct device *dev, int to, + const void *from, int count); + +/* Dayna - Dayna/Kinetics use this */ +static void dayna_get_8390_hdr(struct device *dev, + struct e8390_pkt_hdr *hdr, int ring_page); +static void dayna_block_input(struct device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void dayna_block_output(struct device *dev, int count, + const unsigned char *buf, int start_page); + +enum mac8390_type __init mac8390_ident(struct nubus_dev * dev) +{ + if (dev->dr_sw == NUBUS_DRSW_ASANTE) + return MAC8390_ASANTE; + if (dev->dr_sw == NUBUS_DRSW_FARALLON) + return MAC8390_FARALLON; + if (dev->dr_sw == NUBUS_DRSW_KINETICS) + return MAC8390_KINETICS; + if (dev->dr_sw == NUBUS_DRSW_DAYNA) + return MAC8390_DAYNA; + return MAC8390_NONE; +} + +int __init mac8390_memsize(unsigned long membase) +{ + unsigned long flags; + int i, j; + + save_flags(flags); cli(); + /* Check up to 32K in 4K increments */ + for (i = 0; i < 8; i++) { + volatile unsigned short *m = (unsigned short *) (membase + (i * 0x1000)); + + /* Unwriteable - we have a fully decoded card and the + RAM end located */ + if (hwreg_present(m) == 0) + break; + + /* write a distinctive byte */ + *m = 0xA5A0 | i; + /* check that we read back what we wrote */ + if (*m != (0xA5A0 | i)) + break; + + /* check for partial decode and wrap */ + for (j = 0; j < i; j++) { + volatile unsigned short *p = (unsigned short *) (membase + (j * 0x1000)); + if (*p != (0xA5A0 | j)) + break; + } + } + restore_flags(flags); + /* in any case, we stopped once we tried one block too many, + or once we reached 32K */ + return i * 0x1000; +} + +int __init mac8390_promaddr(struct net_device * dev) +{ + /* XXX: lame, figure out how to do this eventually */ + return -1; +} + +static int probed __initdata = 0; + +int __init mac8390_probe(struct net_device * dev) +{ + int boards_found = 0; + int version_disp = 0; + struct nubus_dev * ndev = NULL; + + struct nubus_dir dir; + struct nubus_dirent ent; + int offset; + + enum mac8390_type cardtype; + + if (probed) + return -ENODEV; + probed++; + + /* probably should check for Nubus instead */ + + if (!MACH_IS_MAC) + return -ENODEV; + + while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, NUBUS_TYPE_ETHERNET, ndev))) { + + dev = NULL; + + if ((cardtype = mac8390_ident(ndev)) == MAC8390_NONE) + continue; + + dev = init_etherdev(dev, 0); + if (dev == NULL) { + printk(KERN_ERR "Unable to allocate etherdev" + "structure!\n"); + return -ENOMEM; + } + + if (version_disp == 0) { + version_disp = 1; + printk(version); + } + + dev->irq = SLOT2IRQ(ndev->board->slot); + /* This is getting to be a habit */ + dev->base_addr = ndev->board->slot_addr | ((ndev->board->slot&0xf) << 20); + + /* Get some Nubus info - we will trust the card's idea + of where its memory and registers are. */ + + if (nubus_get_func_dir(ndev, &dir) == -1) { + printk(KERN_ERR "%s: Unable to get Nubus functional" + " directory for slot %X!\n", + dev->name, ndev->board->slot); + continue; + } + if (useresources[cardtype] == 1) { + if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_BASEOS, &ent) == -1) { + printk(KERN_ERR "%s: Memory offset resource" + " for slot %X not found!\n", + dev->name, ndev->board->slot); + continue; + } + nubus_get_rsrc_mem(&offset, &ent, 4); + dev->mem_start = dev->base_addr + offset; + /* yes, this is how the Apple driver does it */ + dev->base_addr = dev->mem_start + 0x10000; + nubus_rewinddir(&dir); + if (nubus_find_rsrc(&dir, NUBUS_RESID_MINOR_LENGTH, &ent) == -1) { + printk(KERN_INFO "%s: Memory length resource" + " for slot %X not found" + ", probing\n", + dev->name, ndev->board->slot); + offset = mac8390_memsize(dev->mem_start); + } else { + nubus_get_rsrc_mem(&offset, &ent, 4); + } + dev->mem_end = dev->mem_start + offset; + } else { + switch (cardtype) { + case MAC8390_KINETICS: + case MAC8390_DAYNA: /* it's the same */ + dev->base_addr = + (int)(ndev->board->slot_addr + + DAYNA_8390_BASE); + dev->mem_start = + (int)(ndev->board->slot_addr + + DAYNA_8390_MEM); + dev->mem_end = + dev->mem_start + + mac8390_memsize(dev->mem_start); + break; + default: + printk(KERN_ERR "Card type %s is" + " unsupported, sorry\n", + cardname[cardtype]); + return -ENODEV; + } + } + + /* Get the MAC address */ + nubus_rewinddir(&dir); + if ((nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent)) == -1) { + printk(KERN_INFO "%s: Ethernet address not in resources" + ", probing\n", + dev->name); + if ((mac8390_promaddr(dev)) == -1) { + printk(KERN_ERR "%s: Couldn't get MAC" + " address!\n", + dev->name); + continue; + } + } else { + nubus_get_rsrc_mem(dev->dev_addr, &ent, 6); + } + + /* Do the nasty 8390 stuff */ + if (mac8390_initdev(dev, ndev, cardtype)) + continue; + boards_found++; + } + + /* We're outta here */ + if (boards_found > 0) + return 0; + else + return -ENODEV; +} + +#ifdef MODULE +#if LINUX_VERSION_CODE > 0x20118 +MODULE_AUTHOR("David Huggins-Daines and others"); +MODULE_DESCRIPTION("Macintosh NS8390-based Nubus Ethernet driver"); +#endif + +int init_module(void) +{ + if (mac8390_probe(NULL)) { + printk(KERN_NOTICE "mac8390.c: No useable cards found, driver NOT installed.\n"); + return -ENODEV; + } + lock_8390_module(); + return 0; +} + +void cleanup_module(void) +{ + /* FIXME: should probably keep track of net_device structs + somewhere and unregister them here? */ + unlock_8390_module(); +} + +#endif /* MODULE */ + +int __init mac8390_initdev(struct net_device * dev, struct nubus_dev * ndev, + enum mac8390_type type) +{ + static u32 fwrd4_offsets[16]={ + 0, 4, 8, 12, + 16, 20, 24, 28, + 32, 36, 40, 44, + 48, 52, 56, 60 + }; + static u32 back4_offsets[16]={ + 60, 56, 52, 48, + 44, 40, 36, 32, + 28, 24, 20, 16, + 12, 8, 4, 0 + }; +/* Cabletron cards use this, but they aren't supported in mac8390.c */ +#if 0 + static u32 fwrd2_offsets[16]={ + 0, 2, 4, 6, + 8, 10, 12, 14, + 16, 18, 20, 22, + 24, 26, 28, 30 + }; +#endif /* 0 */ + + /* 8390 specific init for dev - allocates dev->priv */ + if (ethdev_init(dev)) { + printk(KERN_ERR "%s: Unable to allocate memory for dev->priv!\n", dev->name); + return -ENOMEM; + } + + /* Now fill in our stuff */ + dev->open = &mac8390_open; + dev->stop = &mac8390_close; + + /* GAR, ei_status is actually a macro even though it looks global */ + ei_status.name = cardname[type]; + ei_status.word16 = word16[type]; + + /* Cabletron's TX/RX buffers are backwards */ + if (type == MAC8390_CABLETRON) { + ei_status.tx_start_page = CABLETRON_TX_START_PG; + ei_status.rx_start_page = CABLETRON_RX_START_PG; + ei_status.stop_page = CABLETRON_RX_STOP_PG; + dev->rmem_start = dev->mem_start; + dev->rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256; + } else { + ei_status.tx_start_page = WD_START_PG; + ei_status.rx_start_page = WD_START_PG + TX_PAGES; + ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; + dev->rmem_start = dev->mem_start + TX_PAGES*256; + dev->rmem_end = dev->mem_end; + } + + /* Fill in model-specific information and functions */ + switch(type) { + case MAC8390_APPLE: + case MAC8390_ASANTE: + case MAC8390_FARALLON: + /* 32 bit card, register map is reversed */ + /* sane */ + ei_status.reset_8390 = &mac8390_no_reset; + ei_status.block_input = &sane_block_input; + ei_status.block_output = &sane_block_output; + ei_status.get_8390_hdr = &sane_get_8390_hdr; + ei_status.reg_offset = back4_offsets; + break; + case MAC8390_DAYNA: + case MAC8390_KINETICS: + /* 16 bit memory */ + /* dayna and similar */ + ei_status.reset_8390 = &mac8390_no_reset; + ei_status.block_input = &dayna_block_input; + ei_status.block_output = &dayna_block_output; + ei_status.get_8390_hdr = &dayna_get_8390_hdr; + ei_status.reg_offset = fwrd4_offsets; + break; + default: + printk(KERN_ERR "Card type %s is unsupported, sorry\n", cardname[type]); + return -ENODEV; + } + + NS8390_init(dev, 0); + + /* Good, done, now spit out some messages */ + printk(KERN_INFO "%s: %s in slot %X (type %s)\n", + dev->name, ndev->board->name, ndev->board->slot, cardname[type]); + printk(KERN_INFO "MAC "); + { + int i; + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } + } + printk(" IRQ %d, shared memory at %#lx-%#lx.\n", + dev->irq, dev->mem_start, dev->mem_end-1); + return 0; +} + +static int mac8390_open(struct device *dev) +{ + ei_open(dev); + if (request_irq(dev->irq, ei_interrupt, 0, "8390 Ethernet", dev)) { + printk ("%s: unable to get IRQ %d.\n", dev->name, dev->irq); + return -EAGAIN; + } + MOD_INC_USE_COUNT; + return 0; +} + +static int mac8390_close(struct device *dev) +{ + free_irq(dev->irq, dev); + ei_close(dev); + MOD_DEC_USE_COUNT; + return 0; +} + +static void mac8390_no_reset(struct device *dev) +{ + ei_status.txing = 0; + if (ei_debug > 1) + printk("reset not supported\n"); + return; +} + +/* dayna_memcpy_fromio/dayna_memcpy_toio */ +/* directly from daynaport.c by Alan Cox */ +static void dayna_memcpy_fromcard(struct device *dev, void *to, int from, int count) +{ + volatile unsigned short *ptr; + unsigned short *target=to; + from<<=1; /* word, skip overhead */ + ptr=(unsigned short *)(dev->mem_start+from); + /* Leading byte? */ + if (from&2) { + *((char *)target)++ = *(((char *)ptr++)-1); + count--; + } + while(count>=2) + { + *target++=*ptr++; /* Copy and */ + ptr++; /* skip cruft */ + count-=2; + } + /* Trailing byte? */ + if(count) + { + /* Big endian */ + unsigned short v=*ptr; + *((char *)target)=v>>8; + } +} + +static void dayna_memcpy_tocard(struct device *dev, int to, const void *from, int count) +{ + volatile unsigned short *ptr; + const unsigned short *src=from; + to<<=1; /* word, skip overhead */ + ptr=(unsigned short *)(dev->mem_start+to); + /* Leading byte? */ + if (to&2) { /* avoid a byte write (stomps on other data) */ + ptr[-1] = (ptr[-1]&0xFF00)|*((unsigned char *)src)++; + ptr++; + count--; + } + while(count>=2) + { + *ptr++=*src++; /* Copy and */ + ptr++; /* skip cruft */ + count-=2; + } + /* Trailing byte? */ + if(count) + { + /* Big endian */ + unsigned short v=*src; + /* card doesn't like byte writes */ + *ptr=(*ptr&0x00FF)|(v&0xFF00); + } +} + +/* sane block input/output */ +static void sane_get_8390_hdr(struct device *dev, + struct e8390_pkt_hdr *hdr, int ring_page) +{ + unsigned long hdr_start = (ring_page - WD_START_PG)<<8; + memcpy_fromio((void *)hdr, (char *)dev->mem_start + hdr_start, 4); + /* Fix endianness */ + hdr->count = swab16(hdr->count); +} + +static void sane_block_input(struct device *dev, int count, + struct sk_buff *skb, int ring_offset) +{ + unsigned long xfer_base = ring_offset - (WD_START_PG<<8); + unsigned long xfer_start = xfer_base + dev->mem_start; + + if (xfer_start + count > dev->rmem_end) { + /* We must wrap the input move. */ + int semi_count = dev->rmem_end - xfer_start; + memcpy_fromio(skb->data, (char *)dev->mem_start + xfer_base, semi_count); + count -= semi_count; + memcpy_toio(skb->data + semi_count, (char *)dev->rmem_start, count); + } else { + memcpy_fromio(skb->data, (char *)dev->mem_start + xfer_base, count); + } +} + +static void sane_block_output(struct device *dev, int count, + const unsigned char *buf, int start_page) +{ + long shmem = (start_page - WD_START_PG)<<8; + + memcpy_toio((char *)dev->mem_start + shmem, buf, count); +} + +/* dayna block input/output */ +static void dayna_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + unsigned long hdr_start = (ring_page - WD_START_PG)<<8; + + dayna_memcpy_fromcard(dev, (void *)hdr, hdr_start, 4); + /* Fix endianness */ + hdr->count=(hdr->count&0xFF)<<8|(hdr->count>>8); +} + +static void dayna_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ + unsigned long xfer_base = ring_offset - (WD_START_PG<<8); + unsigned long xfer_start = xfer_base+dev->mem_start; + + /* Note the offset math is done in card memory space which is word + per long onto our space. */ + + if (xfer_start + count > dev->rmem_end) + { + /* We must wrap the input move. */ + int semi_count = dev->rmem_end - xfer_start; + dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count); + count -= semi_count; + dayna_memcpy_fromcard(dev, skb->data + semi_count, + dev->rmem_start - dev->mem_start, count); + } + else + { + dayna_memcpy_fromcard(dev, skb->data, xfer_base, count); + } +} + +static void dayna_block_output(struct device *dev, int count, const unsigned char *buf, + int start_page) +{ + long shmem = (start_page - WD_START_PG)<<8; + + dayna_memcpy_tocard(dev, shmem, buf, count); +} diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c new file mode 100644 index 000000000000..33cf91ece10d --- /dev/null +++ b/drivers/net/mac89x0.c @@ -0,0 +1,679 @@ +/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */ +/* + Written 1996 by Russell Nelson, with reference to skeleton.c + written 1993-1994 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + The author may be reached at nelson@crynwr.com, Crynwr + Software, 11 Grant St., Potsdam, NY 13676 + + Changelog: + + Mike Cruse : mcruse@cti-ltd.com + : Changes for Linux 2.0 compatibility. + : Added dev_id parameter in net_interrupt(), + : request_irq() and free_irq(). Just NULL for now. + + Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros + : in net_open() and net_close() so kerneld would know + : that the module is in use and wouldn't eject the + : driver prematurely. + + Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c + : as an example. Disabled autoprobing in init_module(), + : not a good thing to do to other devices while Linux + : is running from all accounts. + + Alan Cox : Removed 1.2 support, added 2.1 extra counters. + + David Huggins-Daines + + Split this off into mac89x0.c, and gutted it of all parts which are + not relevant to the existing CS8900 cards on the Macintosh + (i.e. basically the Daynaport CS and LC cards). To be precise: + + * Removed all the media-detection stuff, because these cards are + TP-only. + + * Lobotomized the ISA interrupt bogosity, because these cards use + a hardwired NuBus interrupt and a magic ISAIRQ value in the card. + + * Basically eliminated everything not relevant to getting the + cards minimally functioning on the Macintosh. + + I might add that these cards are badly designed even from the Mac + standpoint, in that Dayna, in their infinite wisdom, used NuBus slot + I/O space and NuBus interrupts for these cards, but neglected to + provide anything even remotely resembling a NuBus ROM. Therefore we + have to probe for them in a brain-damaged ISA-like fashion. +*/ + +static char *version = +"cs89x0.c:v1.02 11/26/96 Russell Nelson \n"; + +/* ======================= configure the driver here ======================= */ + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +/* ======================= end of configuration ======================= */ + + +/* Always include 'config.h' first in case the user wants to turn on + or override something. */ +#ifdef MODULE +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#define PRINTK(x) printk x + +/* + Sources: + + Crynwr packet driver epktisa. + + Crystal Semiconductor data sheets. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "cs89x0.h" + +static unsigned int net_debug = NET_DEBUG; + +/* Information that need to be kept for each board. */ +struct net_local { + struct net_device_stats stats; + int chip_type; /* one of: CS8900, CS8920, CS8920M */ + char chip_revision; /* revision letter of the chip ('A'...) */ + int send_cmd; /* the propercommand used to send a packet. */ + int rx_mode; + int curr_rx_cfg; + int send_underrun; /* keep track of how many underruns in a row we get */ + struct sk_buff *skb; +}; + +/* Index to functions, as function prototypes. */ + +extern int mac89x0_probe(struct device *dev); +extern void reset_chip(struct device *dev); +static int net_open(struct device *dev); +static int net_send_packet(struct sk_buff *skb, struct device *dev); +static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void set_multicast_list(struct device *dev); +static void net_rx(struct device *dev); +static int net_close(struct device *dev); +static struct net_device_stats *net_get_stats(struct device *dev); +static int set_mac_address(struct device *dev, void *addr); + + +/* Example routines you must write ;->. */ +#define tx_done(dev) 1 + +/* For reading/writing registers ISA-style */ +static int inline +readreg_io(struct device *dev, int portno) +{ + writew(swab16(portno), dev->base_addr + ADD_PORT); + return swab16(readw(dev->base_addr + DATA_PORT)); +} + +static void inline +writereg_io(struct device *dev, int portno, int value) +{ + writew(swab16(portno), dev->base_addr + ADD_PORT); + writew(swab16(value), dev->base_addr + DATA_PORT); +} + +/* These are for reading/writing registers in shared memory */ +static int inline +readreg(struct device *dev, int portno) +{ + return swab16(readw(dev->mem_start + portno)); +} + +static void inline +writereg(struct device *dev, int portno, int value) +{ + writew(swab16(value), dev->mem_start + portno); +} + +/* Probe for the CS8900 card in slot E. We won't bother looking + anywhere else until we have a really good reason to do so. */ +__initfunc(int mac89x0_probe(struct device *dev)) +{ + static int once_is_enough = 0; + struct net_local *lp; + static unsigned version_printed = 0; + int i, slot; + unsigned rev_type = 0; + unsigned long ioaddr; + unsigned short sig; + + if (once_is_enough) + return ENODEV; + once_is_enough = 1; + + /* We might have to parameterize this later */ + slot = 0xE; + /* Get out now if there's a real NuBus card in slot E */ + if (nubus_find_slot(slot, NULL) != NULL) + return ENODEV; + + /* The pseudo-ISA bits always live at offset 0x300 (gee, + wonder why...) */ + ioaddr = (unsigned long) + nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE); + { + unsigned long flags; + int card_present; + + save_flags(flags); + cli(); + card_present = hwreg_present((void*) ioaddr+4) + && hwreg_present((void*) ioaddr + DATA_PORT); + restore_flags(flags); + + if (!card_present) + return ENODEV; + } + + writew(0, ioaddr + ADD_PORT); + sig = readw(ioaddr + DATA_PORT); + if (sig != swab16(CHIP_EISA_ID_SIG)) + return ENODEV; + + /* Initialize the device structure. */ + if (dev->priv == NULL) { + dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + memset(dev->priv, 0, sizeof(struct net_local)); + } + lp = (struct net_local *)dev->priv; + + /* Fill in the 'dev' fields. */ + dev->base_addr = ioaddr; + dev->mem_start = (unsigned long) + nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE); + dev->mem_end = dev->mem_start + 0x1000; + + /* Turn on shared memory */ + writereg_io(dev, PP_BusCTL, MEMORY_ON); + + /* get the chip type */ + rev_type = readreg(dev, PRODUCT_ID_ADD); + lp->chip_type = rev_type &~ REVISON_BITS; + lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; + + /* Check the chip type and revision in order to set the correct send command + CS8920 revision C and CS8900 revision F can use the faster send. */ + lp->send_cmd = TX_AFTER_381; + if (lp->chip_type == CS8900 && lp->chip_revision >= 'F') + lp->send_cmd = TX_NOW; + if (lp->chip_type != CS8900 && lp->chip_revision >= 'C') + lp->send_cmd = TX_NOW; + + if (net_debug && version_printed++ == 0) + printk(version); + + printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#8lx", + dev->name, + lp->chip_type==CS8900?'0':'2', + lp->chip_type==CS8920M?"M":"", + lp->chip_revision, + dev->base_addr); + + /* Try to read the MAC address */ + if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) { + printk("\nmac89x0: No EEPROM, giving up now.\n"); + return ENODEV; + } else { + for (i = 0; i < ETH_ALEN; i += 2) { + /* Big-endian (why??!) */ + unsigned short s = readreg(dev, PP_IA + i); + dev->dev_addr[i] = s >> 8; + dev->dev_addr[i+1] = s & 0xff; + } + } + + dev->irq = SLOT2IRQ(slot); + printk(" IRQ %d ADDR ", dev->irq); + + /* print the ethernet address. */ + for (i = 0; i < ETH_ALEN; i++) + printk("%2.2x%s", dev->dev_addr[i], + ((i < ETH_ALEN-1) ? ":" : "")); + + dev->open = net_open; + dev->stop = net_close; + dev->hard_start_xmit = net_send_packet; + dev->get_stats = net_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->set_mac_address = &set_mac_address; + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + + printk("\n"); + return 0; +} + +/* This is useful for something, but I don't know what yet. */ +__initfunc(void +reset_chip(struct device *dev)) +{ + int reset_start_time; + + writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); + + /* wait 30 ms */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(30*HZ/1000); + + /* Wait until the chip is reset */ + reset_start_time = jiffies; + while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2) + ; +} + +/* Open/initialize the board. This is called (in the current kernel) + sometime after booting when the 'ifconfig' program is run. + + This routine should set everything up anew at each open, even + registers that "should" only need to be set once at boot, so that + there is non-reboot way to recover if something goes wrong. + */ +static int +net_open(struct device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + int i; + + /* Disable the interrupt for now */ + writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ); + + /* Grab the interrupt */ + if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) + return -EAGAIN; + + /* Set up the IRQ - Apparently magic */ + if (lp->chip_type == CS8900) + writereg(dev, PP_CS8900_ISAINT, 0); + else + writereg(dev, PP_CS8920_ISAINT, 0); + + /* set the Ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); + + /* Turn on both receive and transmit operations */ + writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON); + + /* Receive only error free packets addressed to this card */ + lp->rx_mode = 0; + writereg(dev, PP_RxCTL, DEF_RX_ACCEPT); + + lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; + + writereg(dev, PP_RxCFG, lp->curr_rx_cfg); + + writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | + TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); + + writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | + TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL); + + /* now that we've got our act together, enable everything */ + writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ); + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + MOD_INC_USE_COUNT; + return 0; +} + +static int +net_send_packet(struct sk_buff *skb, struct device *dev) +{ + if (dev->tbusy) { + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 5) + return 1; + if (net_debug > 0) printk("%s: transmit timed out, %s?\n", dev->name, + tx_done(dev) ? "IRQ conflict" : "network cable problem"); + /* Try to restart the adaptor. */ + dev->tbusy=0; + dev->trans_start = jiffies; + } + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) + printk("%s: Transmitter access conflict.\n", dev->name); + else { + struct net_local *lp = (struct net_local *)dev->priv; + unsigned long flags; + + if (net_debug > 3) + printk("%s: sent %d byte packet of type %x\n", + dev->name, skb->len, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) + | skb->data[ETH_ALEN+ETH_ALEN+1]); + + /* keep the upload from being interrupted, since we + ask the chip to start transmitting before the + whole packet has been completely uploaded. */ + save_flags(flags); + cli(); + + /* initiate a transmit sequence */ + writereg(dev, PP_TxCMD, lp->send_cmd); + writereg(dev, PP_TxLength, skb->len); + + /* Test to see if the chip has allocated memory for the packet */ + if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { + /* Gasp! It hasn't. But that shouldn't happen since + we're waiting for TxOk, so return 1 and requeue this packet. */ + restore_flags(flags); + return 1; + } + + /* Write the contents of the packet */ + memcpy_toio(dev->mem_start + PP_TxFrame, skb->data, skb->len+1); + + restore_flags(flags); + dev->trans_start = jiffies; + } + dev_kfree_skb (skb); + + return 0; +} + +/* The typical workload of the driver: + Handle the network interface interrupts. */ +static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct device *dev = dev_id; + struct net_local *lp; + int ioaddr, status; + + if (dev == NULL) { + printk ("net_interrupt(): irq %d for unknown device.\n", irq); + return; + } + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + dev->interrupt = 1; + + ioaddr = dev->base_addr; + lp = (struct net_local *)dev->priv; + + /* we MUST read all the events out of the ISQ, otherwise we'll never + get interrupted again. As a consequence, we can't have any limit + on the number of times we loop in the interrupt handler. The + hardware guarantees that eventually we'll run out of events. Of + course, if you're on a slow machine, and packets are arriving + faster than you can read them off, you're screwed. Hasta la + vista, baby! */ + while ((status = swab16(readw(dev->base_addr + ISQ_PORT)))) { + if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status); + switch(status & ISQ_EVENT_MASK) { + case ISQ_RECEIVER_EVENT: + /* Got a packet(s). */ + net_rx(dev); + break; + case ISQ_TRANSMITTER_EVENT: + lp->stats.tx_packets++; + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + if ((status & TX_OK) == 0) lp->stats.tx_errors++; + if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++; + if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++; + if (status & TX_LATE_COL) lp->stats.tx_window_errors++; + if (status & TX_16_COL) lp->stats.tx_aborted_errors++; + break; + case ISQ_BUFFER_EVENT: + if (status & READY_FOR_TX) { + /* we tried to transmit a packet earlier, + but inexplicably ran out of buffers. + That shouldn't happen since we only ever + load one packet. Shrug. Do the right + thing anyway. */ + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + } + if (status & TX_UNDERRUN) { + if (net_debug > 0) printk("%s: transmit underrun\n", dev->name); + lp->send_underrun++; + if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; + else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL; + } + break; + case ISQ_RX_MISS_EVENT: + lp->stats.rx_missed_errors += (status >>6); + break; + case ISQ_TX_COL_EVENT: + lp->stats.collisions += (status >>6); + break; + } + } + dev->interrupt = 0; + return; +} + +/* We have a good packet(s), get it/them out of the buffers. */ +static void +net_rx(struct device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + struct sk_buff *skb; + int status, length; + + status = readreg(dev, PP_RxStatus); + if ((status & RX_OK) == 0) { + lp->stats.rx_errors++; + if (status & RX_RUNT) lp->stats.rx_length_errors++; + if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++; + if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT))) + /* per str 172 */ + lp->stats.rx_crc_errors++; + if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++; + return; + } + + length = readreg(dev, PP_RxLength); + /* Malloc up new buffer. */ + skb = alloc_skb(length, GFP_ATOMIC); + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + return; + } + skb->len = length; + skb->dev = dev; + + memcpy_fromio(skb->data, dev->mem_start + PP_RxFrame, length); + + if (net_debug > 3)printk("%s: received %d byte packet of type %x\n", + dev->name, length, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) + | skb->data[ETH_ALEN+ETH_ALEN+1]); + + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + lp->stats.rx_packets++; + lp->stats.rx_bytes+=skb->len; + return; +} + +/* The inverse routine to net_open(). */ +static int +net_close(struct device *dev) +{ + + writereg(dev, PP_RxCFG, 0); + writereg(dev, PP_TxCFG, 0); + writereg(dev, PP_BufCFG, 0); + writereg(dev, PP_BusCTL, 0); + + dev->start = 0; + + free_irq(dev->irq, dev); + + /* Update the statistics here. */ + + MOD_DEC_USE_COUNT; + return 0; + +} + +/* Get the current statistics. This may be called with the card open or + closed. */ +static struct net_device_stats * +net_get_stats(struct device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + cli(); + /* Update the statistics from the device registers. */ + lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6); + lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6); + sti(); + + return &lp->stats; +} + +static void set_multicast_list(struct device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + if(dev->flags&IFF_PROMISC) + { + lp->rx_mode = RX_ALL_ACCEPT; + } + else if((dev->flags&IFF_ALLMULTI)||dev->mc_list) + { + /* The multicast-accept list is initialized to accept-all, and we + rely on higher-level filtering for now. */ + lp->rx_mode = RX_MULTCAST_ACCEPT; + } + else + lp->rx_mode = 0; + + writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode); + + /* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */ + writereg(dev, PP_RxCFG, lp->curr_rx_cfg | + (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0)); +} + + +static int set_mac_address(struct device *dev, void *addr) +{ + int i; + if (dev->start) + return -EBUSY; + printk("%s: Setting MAC address to ", dev->name); + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]); + printk(".\n"); + /* set the Ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); + + return 0; +} + +#ifdef MODULE + +static char namespace[16] = ""; +static struct device dev_cs89x0 = { + NULL, + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL }; + +static int debug=0; + +MODULE_PARM(debug, "i"); + +EXPORT_NO_SYMBOLS; + +int +init_module(void) +{ + struct net_local *lp; + + net_debug = debug; + dev_cs89x0.name = namespace; + dev_cs89x0.init = mac89x0_probe; + dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + memset(dev_cs89x0.priv, 0, sizeof(struct net_local)); + lp = (struct net_local *)dev_cs89x0.priv; + + if (register_netdev(&dev_cs89x0) != 0) { + printk(KERN_WARNING "mac89x0.c: No card found\n"); + return -ENXIO; + } + return 0; +} + +void +cleanup_module(void) +{ + +#endif +#ifdef MODULE + writew(0, dev_cs89x0.base_addr + ADD_PORT); +#endif +#ifdef MODULE + + if (dev_cs89x0.priv != NULL) { + /* Free up the private structure, or leak memory :-) */ + unregister_netdev(&dev_cs89x0); + kfree(dev_cs89x0.priv); + dev_cs89x0.priv = NULL; /* gets re-allocated by cs89x0_probe1 */ + } +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "m68k-linux-gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c -o mac89x0.o mac89x0.c" + * version-control: t + * kept-new-versions: 5 + * c-indent-level: 8 + * tab-width: 8 + * End: + * + */ diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c new file mode 100644 index 000000000000..e7bb472cc67a --- /dev/null +++ b/drivers/net/macmace.c @@ -0,0 +1,793 @@ +/* + * Driver for the Macintosh 68K onboard MACE controller with PSC + * driven DMA. The MACE driver code is derived from mace.c. The + * Mac68k theory of operation is courtesy of the MacBSD wizards. + * + * 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. + * + * Copyright (C) 1996 Paul Mackerras. + * Copyright (C) 1998 Alan Cox + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mace.h" + +#define N_RX_RING 8 +#define N_TX_RING 2 +#define MAX_TX_ACTIVE 1 +#define NCMDS_TX 1 /* dma commands per element in tx ring */ +#define RX_BUFLEN (ETH_FRAME_LEN + 8) +#define TX_TIMEOUT HZ /* 1 second */ + +/* Bits in transmit DMA status */ +#define TX_DMA_ERR 0x80 + +/* The MACE is simply wired down on a Mac68K box */ + +#define MACE_BASE (void *)(0x50F1C000) +#define MACE_PROM (void *)(0x50F08001) + +struct mace68k_data +{ + volatile struct mace *mace; + volatile unsigned char *tx_ring; + volatile unsigned char *rx_ring; + int dma_intr; + unsigned char maccc; + struct net_device_stats stats; + struct timer_list tx_timeout; + int timeout_active; + int rx_slot, rx_done; + int tx_slot, tx_count; +}; + +struct mace_frame +{ + u16 len; + u16 status; + u16 rntpc; + u16 rcvcc; + u32 pad1; + u32 pad2; + u8 data[1]; + /* And frame continues.. */ +}; + +#define PRIV_BYTES sizeof(struct mace68k_data) + +static int mace68k_open(struct device *dev); +static int mace68k_close(struct device *dev); +static int mace68k_xmit_start(struct sk_buff *skb, struct device *dev); +static struct net_device_stats *mace68k_stats(struct device *dev); +static void mace68k_set_multicast(struct device *dev); +static void mace68k_reset(struct device *dev); +static int mace68k_set_address(struct device *dev, void *addr); +static void mace68k_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void mace68k_dma_intr(int irq, void *dev_id, struct pt_regs *regs); +static void mace68k_set_timeout(struct device *dev); +static void mace68k_tx_timeout(unsigned long data); + +/* + * PSC DMA engine control. As you'd expect on a macintosh its + * more like a lawnmower engine supplied without instructions + * + * The basic theory of operation appears to be as follows. + * + * There are two sets of receive DMA registers and two sets + * of transmit DMA registers. Instead of the more traditional + * "ring buffer" approach the Mac68K DMA engine expects you + * to be loading one chain while the other runs, and then + * to flip register set. Each entry in the chain is a fixed + * length. + */ + +/* + * Load a receive DMA channel with a base address and ring length + */ + +static void psc_load_rxdma_base(int set, void *base) +{ + psc_write_word(PSC_ENETRD_CMD + set, 0x0100); + psc_write_long(PSC_ENETRD_ADDR + set, (u32)base); + psc_write_long(PSC_ENETRD_LEN + set, N_RX_RING); + psc_write_word(PSC_ENETRD_CMD + set, 0x9800); +} + +/* + * Reset the receive DMA subsystem + */ + +static void mace68k_rxdma_reset(struct device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mace = mp->mace; + u8 mcc = mace->maccc; + + /* + * Turn off receive + */ + + mcc&=~ENRCV; + mace->maccc=mcc; + + /* + * Program the DMA + */ + + psc_write_word(PSC_ENETRD_CTL, 0x8800); + psc_load_rxdma_base(0x0, (void *)virt_to_bus(mp->rx_ring)); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + psc_write_word(PSC_ENETRD_CTL, 0x8800); + psc_load_rxdma_base(0x10, (void *)virt_to_bus(mp->rx_ring)); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + mace->maccc=mcc|ENRCV; + + psc_write_word(PSC_ENETRD_CTL, 0x9800); + psc_write_word(PSC_ENETRD_CTL+0x10, 0x9800); +} + +/* + * Reset the transmit DMA subsystem + */ + +static void mace68k_txdma_reset(struct device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mace = mp->mace; + u8 mcc = mace->maccc; + + psc_write_word(PSC_ENETWR_CTL,0x8800); + + mace->maccc = mcc&~ENXMT; + psc_write_word(PSC_ENETWR_CTL,0x400); + mace->maccc = mcc; +} + +/* + * Disable DMA + */ + +static void mace68k_dma_off(struct device *dev) +{ + psc_write_word(PSC_ENETRD_CTL, 0x8800); + psc_write_word(PSC_ENETRD_CTL, 0x1000); + psc_write_word(PSC_ENETRD_CMD, 0x1100); + psc_write_word(PSC_ENETRD_CMD+0x10, 0x1100); + + psc_write_word(PSC_ENETWR_CTL, 0x8800); + psc_write_word(PSC_ENETWR_CTL, 0x1000); + psc_write_word(PSC_ENETWR_CMD, 0x1100); + psc_write_word(PSC_ENETWR_CMD+0x10, 0x1100); +} + +/* Bit-reverse one byte of an ethernet hardware address. */ + +static int +bitrev(int b) +{ + int d = 0, i; + + for (i = 0; i < 8; ++i, b >>= 1) + d = (d << 1) | (b & 1); + return d; +} + +/* + * Not really much of a probe. The hardware table tells us if this + * model of Macintrash has a MACE (AV macintoshes) + */ + +int mace68k_probe(struct device *unused) +{ + int j; + static int once=0; + struct mace68k_data *mp; + unsigned char *addr; + struct device *dev; + unsigned char checksum = 0; + + /* + * There can be only one... + */ + + if (once) return -ENODEV; + + once = 1; + + if (macintosh_config->ether_type != MAC_ETHER_MACE) return -ENODEV; + + printk("MACE ethernet should be present "); + + dev = init_etherdev(0, PRIV_BYTES); + if(dev==NULL) + { + printk("no free memory.\n"); + return -ENOMEM; + } + mp = (struct mace68k_data *) dev->priv; + dev->base_addr = (u32)MACE_BASE; + mp->mace = (volatile struct mace *) MACE_BASE; + + printk("at 0x%p", mp->mace); + + /* + * 16K RX ring and 4K TX ring should do nicely + */ + + mp->rx_ring=(void *)__get_free_pages(GFP_KERNEL, 2); + mp->tx_ring=(void *)__get_free_page(GFP_KERNEL); + + printk("."); + + if(mp->tx_ring==NULL || mp->rx_ring==NULL) + { + if(mp->tx_ring) + free_page((u32)mp->tx_ring); +// if(mp->rx_ring) +// __free_pages(mp->rx_ring,2); + printk("\nNo memory for ring buffers.\n"); + return -ENOMEM; + } + + /* We want the receive data to be uncached. We dont care about the + byte reading order */ + + printk("."); + kernel_set_cachemode((void *)mp->rx_ring, 16384, IOMAP_NOCACHE_NONSER); + + printk("."); + /* The transmit buffer needs to be write through */ + kernel_set_cachemode((void *)mp->tx_ring, 4096, IOMAP_WRITETHROUGH); + + printk(" Ok\n"); + dev->irq = IRQ_MAC_MACE; + printk(KERN_INFO "%s: MACE at", dev->name); + + /* + * The PROM contains 8 bytes which total 0xFF when XOR'd + * together. Due to the usual peculiar apple brain damage + * the bytes are spaced out in a strange boundary and the + * bits are reversed. + */ + + addr = (void *)MACE_PROM; + + for (j = 0; j < 6; ++j) + { + u8 v=bitrev(addr[j<<4]); + checksum^=v; + dev->dev_addr[j] = v; + printk("%c%.2x", (j ? ':' : ' '), dev->dev_addr[j]); + } + for (; j < 8; ++j) + { + checksum^=bitrev(addr[j<<4]); + } + + if(checksum!=0xFF) + { + printk(" (invalid checksum)\n"); + return -ENODEV; + } + printk("\n"); + + memset(&mp->stats, 0, sizeof(mp->stats)); + init_timer(&mp->tx_timeout); + mp->timeout_active = 0; + + dev->open = mace68k_open; + dev->stop = mace68k_close; + dev->hard_start_xmit = mace68k_xmit_start; + dev->get_stats = mace68k_stats; + dev->set_multicast_list = mace68k_set_multicast; + dev->set_mac_address = mace68k_set_address; + + ether_setup(dev); + + mp = (struct mace68k_data *) dev->priv; + mp->maccc = ENXMT | ENRCV; + mp->dma_intr = IRQ_MAC_MACE_DMA; + + psc_write_word(PSC_ENETWR_CTL, 0x9000); + psc_write_word(PSC_ENETRD_CTL, 0x9000); + psc_write_word(PSC_ENETWR_CTL, 0x0400); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + mace68k_dma_off(dev); + + return 0; +} + +/* + * Reset a MACE controller + */ + +static void mace68k_reset(struct device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + int i; + + /* soft-reset the chip */ + mb->biucc = SWRST; + udelay(100); + + mb->biucc = XMTSP_64; + mb->imr = 0xff; /* disable all intrs for now */ + i = mb->ir; + mb->maccc = 0; /* turn off tx, rx */ + mb->utr = RTRD; + mb->fifocc = RCVFW_64; + mb->xmtfc = AUTO_PAD_XMIT; /* auto-pad short frames */ + + /* load up the hardware address */ + + mb->iac = ADDRCHG | PHYADDR; + + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 6; ++i) + mb->padr = dev->dev_addr[i]; + + /* clear the multicast filter */ + mb->iac = ADDRCHG | LOGADDR; + + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 8; ++i) + mb->ladrf = 0; + + mb->plscc = PORTSEL_GPSI + ENPLSIO; +} + +/* + * Load the address on a mace controller. + */ + +static int mace68k_set_address(struct device *dev, void *addr) +{ + unsigned char *p = addr; + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + int i; + unsigned long flags; + + save_flags(flags); + cli(); + + /* load up the hardware address */ + mb->iac = ADDRCHG | PHYADDR; + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 6; ++i) + mb->padr = dev->dev_addr[i] = p[i]; + /* note: setting ADDRCHG clears ENRCV */ + mb->maccc = mp->maccc; + restore_flags(flags); + return 0; +} + +/* + * Open the Macintosh MACE. Most of this is playing with the DMA + * engine. The ethernet chip is quite friendly. + */ + +static int mace68k_open(struct device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + + /* reset the chip */ + mace68k_reset(dev); + + mp->rx_done = 0; + mace68k_rxdma_reset(dev); + + /* + * The interrupt is fixed and comes off the PSC. + */ + + if (request_irq(dev->irq, mace68k_interrupt, 0, "68K MACE", dev)) + { + printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq); + return -EAGAIN; + } + + /* + * Ditto the DMA interrupt. + */ + + if (request_irq(IRQ_MAC_MACE_DMA, mace68k_dma_intr, 0, "68K MACE DMA", + dev)) + { + printk(KERN_ERR "MACE: can't get irq %d\n", IRQ_MAC_MACE_DMA); + return -EAGAIN; + } + + /* Activate the Mac DMA engine */ + + mp->tx_slot = 0; /* Using register set 0 */ + mp->tx_count = 1; /* 1 Buffer ready for use */ + mace68k_txdma_reset(dev); + + /* turn it on! */ + mb->maccc = mp->maccc; + /* enable all interrupts except receive interrupts */ + mb->imr = RCVINT; + return 0; +} + +/* + * Shut down the mace and its interrupt channel + */ + +static int mace68k_close(struct device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + + /* disable rx and tx */ + mb->maccc = 0; + mb->imr = 0xff; /* disable all intrs */ + + /* disable rx and tx dma */ + + mace68k_dma_off(dev); + + free_irq(dev->irq, dev); + free_irq(IRQ_MAC_MACE_DMA, dev); + return 0; +} + +static inline void mace68k_set_timeout(struct device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + unsigned long flags; + + save_flags(flags); + cli(); + if (mp->timeout_active) + del_timer(&mp->tx_timeout); + mp->tx_timeout.expires = jiffies + TX_TIMEOUT; + mp->tx_timeout.function = mace68k_tx_timeout; + mp->tx_timeout.data = (unsigned long) dev; + add_timer(&mp->tx_timeout); + mp->timeout_active = 1; + restore_flags(flags); +} + +/* + * Transmit a frame + */ + +static int mace68k_xmit_start(struct sk_buff *skb, struct device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + /* + * This may need atomic types ??? + */ + if(mp->tx_count == 0) + { + dev->tbusy=1; + return 1; + } + mp->tx_count--; + + /* + * FIXME: + * This is hackish. The memcpy probably isnt needed but + * the rules for alignment are not known. Ideally we'd like + * to just blast the skb directly to ethernet. We also don't + * use the ring properly - just a one frame buffer. That + * also requires cache pushes ;). + */ + memcpy((void *)mp->tx_ring, skb, skb->len); + psc_write_long(PSC_ENETWR_ADDR + mp->tx_slot, virt_to_bus(mp->tx_ring)); + psc_write_long(PSC_ENETWR_LEN + mp->tx_slot, skb->len); + psc_write_word(PSC_ENETWR_CMD + mp->tx_slot, 0x9800); + mp->stats.tx_packets++; + mp->stats.tx_bytes+=skb->len; + dev_kfree_skb(skb); + return 0; +} + +static struct net_device_stats *mace68k_stats(struct device *dev) +{ + struct mace68k_data *p = (struct mace68k_data *) dev->priv; + return &p->stats; +} + +/* + * CRC polynomial - used in working out multicast filter bits. + */ +#define CRC_POLY 0xedb88320 + +static void mace68k_set_multicast(struct device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + int i, j, k, b; + unsigned long crc; + + mp->maccc &= ~PROM; + if (dev->flags & IFF_PROMISC) + { + mp->maccc |= PROM; + } else + { + unsigned char multicast_filter[8]; + struct dev_mc_list *dmi = dev->mc_list; + + if (dev->flags & IFF_ALLMULTI) + { + for (i = 0; i < 8; i++) + multicast_filter[i] = 0xff; + } else + { + for (i = 0; i < 8; i++) + multicast_filter[i] = 0; + for (i = 0; i < dev->mc_count; i++) + { + crc = ~0; + for (j = 0; j < 6; ++j) + { + b = dmi->dmi_addr[j]; + for (k = 0; k < 8; ++k) + { + if ((crc ^ b) & 1) + crc = (crc >> 1) ^ CRC_POLY; + else + crc >>= 1; + b >>= 1; + } + } + j = crc >> 26; /* bit number in multicast_filter */ + multicast_filter[j >> 3] |= 1 << (j & 7); + dmi = dmi->next; + } + } +#if 0 + printk("Multicast filter :"); + for (i = 0; i < 8; i++) + printk("%02x ", multicast_filter[i]); + printk("\n"); +#endif + + mb->iac = ADDRCHG | LOGADDR; + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 8; ++i) + mb->ladrf = multicast_filter[i]; + } + /* reset maccc */ + mb->maccc = mp->maccc; +} + +/* + * Miscellaneous interrupts are handled here. We may end up + * having to bash the chip on the head for bad errors + */ + +static void mace68k_handle_misc_intrs(struct mace68k_data *mp, int intr) +{ + volatile struct mace *mb = mp->mace; + static int mace68k_babbles, mace68k_jabbers; + + if (intr & MPCO) + mp->stats.rx_missed_errors += 256; + mp->stats.rx_missed_errors += mb->mpc; /* reading clears it */ + if (intr & RNTPCO) + mp->stats.rx_length_errors += 256; + mp->stats.rx_length_errors += mb->rntpc; /* reading clears it */ + if (intr & CERR) + ++mp->stats.tx_heartbeat_errors; + if (intr & BABBLE) + if (mace68k_babbles++ < 4) + printk(KERN_DEBUG "mace: babbling transmitter\n"); + if (intr & JABBER) + if (mace68k_jabbers++ < 4) + printk(KERN_DEBUG "mace: jabbering transceiver\n"); +} + +/* + * A transmit error has occured. (We kick the transmit side from + * the DMA completion) + */ + +static void mace68k_xmit_error(struct device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + u8 xmtfs, xmtrc; + + xmtfs = mb->xmtfs; + xmtrc = mb->xmtrc; + + if(xmtfs & XMTSV) + { + if(xmtfs & UFLO) + { + printk("%s: DMA underrun.\n", dev->name); + mp->stats.tx_errors++; + mp->stats.tx_fifo_errors++; + mace68k_reset(dev); + } + if(xmtfs & RTRY) + mp->stats.collisions++; + } + mark_bh(NET_BH); +} + +/* + * A receive interrupt occured. + */ + +static void mace68k_recv_interrupt(struct device *dev) +{ +// struct mace68k_data *mp = (struct mace68k_data *) dev->priv; +// volatile struct mace *mb = mp->mace; +} + +/* + * Process the chip interrupt + */ + +static void mace68k_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *) dev_id; + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + u8 ir; + + ir = mb->ir; + mace68k_handle_misc_intrs(mp, ir); + + if(ir&XMTINT) + mace68k_xmit_error(dev); + if(ir&RCVINT) + mace68k_recv_interrupt(dev); +} + +static void mace68k_tx_timeout(unsigned long data) +{ +// struct device *dev = (struct device *) data; +// struct mace68k_data *mp = (struct mace68k_data *) dev->priv; +// volatile struct mace *mb = mp->mace; +} + +/* + * Handle a newly arrived frame + */ + +static void mace_dma_rx_frame(struct device *dev, struct mace_frame *mf) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + struct sk_buff *skb; + + if(mf->status&RS_OFLO) + { + printk("%s: fifo overflow.\n", dev->name); + mp->stats.rx_errors++; + mp->stats.rx_fifo_errors++; + } + if(mf->status&(RS_CLSN|RS_FRAMERR|RS_FCSERR)) + mp->stats.rx_errors++; + + if(mf->status&RS_CLSN) + mp->stats.collisions++; + if(mf->status&RS_FRAMERR) + mp->stats.rx_frame_errors++; + if(mf->status&RS_FCSERR) + mp->stats.rx_crc_errors++; + + skb = dev_alloc_skb(mf->len+2); + if(skb==NULL) + { + mp->stats.rx_dropped++; + return; + } + skb_reserve(skb,2); + memcpy(skb_put(skb, mf->len), mf->data, mf->len); + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + mp->stats.rx_packets++; + mp->stats.rx_bytes+=mf->len; +} + +/* + * The PSC has passed us a DMA interrupt event. + */ + +static void mace68k_dma_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *) dev_id; + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + + u32 psc_status; + + /* It seems this must be allowed to stabilise ?? */ + + while((psc_status=psc_read_long(0x0804))!=psc_read_long(0x0804)); + + /* + * Was this an ethernet event ? + */ + + if(psc_status&0x60000000) + { + /* + * Process the read queue + */ + + u16 psc_status = psc_read_word(PSC_ENETRD_CTL); + + if(psc_status&0x2000) + { + mace68k_rxdma_reset(dev); + mp->rx_done = 0; + } + + else if(psc_status&0x100) + { + int left; + + psc_write_word(PSC_ENETRD_CMD+mp->rx_slot, 0x1100); + left=psc_read_long(PSC_ENETRD_LEN+mp->rx_slot); + /* read packets */ + + while(mp->rx_done < left) + { + struct mace_frame *mf=((struct mace_frame *) + mp->rx_ring)+mp->rx_done++; + mace_dma_rx_frame(dev, mf); + } + + if(left == 0) /* Out of DMA room */ + { + psc_load_rxdma_base(mp->rx_slot, + (void *)virt_to_phys(mp->rx_ring)); + mp->rx_slot^=16; + mp->rx_done = 0; + } + else + { + psc_write_word(PSC_ENETRD_CMD+mp->rx_slot, + 0x9800); + } + + } + + /* + * Process the write queue + */ + + psc_status = psc_read_word(PSC_ENETWR_CTL); + if(psc_status&0x2000) { + mace68k_txdma_reset(dev); + } else if(psc_status&0x0100) { + psc_write_word(PSC_ENETWR_CMD+mp->tx_slot, 0x100); + mp->tx_slot^=16; + mp->tx_count++; + dev->tbusy = 0; + mark_bh(NET_BH); + } + } +} diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c new file mode 100644 index 000000000000..8d1eb7040cba --- /dev/null +++ b/drivers/net/macsonic.c @@ -0,0 +1,624 @@ +/* + * macsonic.c + * + * (C) 1998 Alan Cox + * + * Debugging Andreas Ehliar, Michael Schmitz + * + * Based on code + * (C) 1996 by Thomas Bogendoerfer (tsbogend@bigbug.franken.de) + * + * This driver is based on work from Andreas Busse, but most of + * the code is rewritten. + * + * (C) 1995 by Andreas Busse (andy@waldorf-gmbh.de) + * + * A driver for the Mac onboard Sonic ethernet chip. + * + * 98/12/21 MSch: judged from tests on Q800, it's basically working, + * but eating up both receive and transmit resources + * and duplicating packets. Needs more testing. + * + * 99/01/03 MSch: upgraded to version 0.92 of the core driver, fixed. + */ + +#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 +#include +#include + +#define SREGS_PAD(n) u16 n; + +#include "sonic.h" + +static int sonic_debug = 0; +static int sonic_version_printed = 0; + +extern int macsonic_probe(struct device* dev); +extern int mac_onboard_sonic_probe(struct device* dev); +extern int mac_nubus_sonic_probe(struct device* dev); + +/* For onboard SONIC */ +#define ONBOARD_SONIC_REGISTERS 0x50F0A000 +#define ONBOARD_SONIC_PROM_BASE 0x50f08000 + +enum macsonic_type { + MACSONIC_DUODOCK, + MACSONIC_APPLE, + MACSONIC_APPLE16, + MACSONIC_DAYNA, + MACSONIC_DAYNALINK +}; + +/* For the built-in SONIC in the Duo Dock */ +#define DUODOCK_SONIC_REGISTERS 0xe10000 +#define DUODOCK_SONIC_PROM_BASE 0xe12000 + +/* For Apple-style NuBus SONIC */ +#define APPLE_SONIC_REGISTERS 0 +#define APPLE_SONIC_PROM_BASE 0x40000 + +/* Daynalink LC SONIC */ +#define DAYNALINK_PROM_BASE 0x400000 + +/* For Dayna-style NuBus SONIC (haven't seen one yet) */ +#define DAYNA_SONIC_REGISTERS 0x180000 +/* This is what OpenBSD says. However, this is definitely in NuBus + ROM space so we should be able to get it by walking the NuBus + resource directories */ +#define DAYNA_SONIC_MAC_ADDR 0xffe004 + +#define SONIC_READ_PROM(addr) readb(prom_addr+addr) + +__initfunc(int macsonic_probe(struct device* dev)) +{ + int rv; + + /* This will catch fatal stuff like -ENOMEM as well as success */ + if ((rv = mac_onboard_sonic_probe(dev)) != -ENODEV) + return rv; + return mac_nubus_sonic_probe(dev); +} + +/* + * For reversing the PROM address + */ + +static unsigned char nibbletab[] = {0, 8, 4, 12, 2, 10, 6, 14, + 1, 9, 5, 13, 3, 11, 7, 15}; + +static inline void bit_reverse_addr(unsigned char addr[6]) +{ + int i; + + for(i = 0; i < 6; i++) + addr[i] = ((nibbletab[addr[i] & 0xf] << 4) | + nibbletab[(addr[i] >> 4) &0xf]); +} + +__initfunc(int macsonic_init(struct device* dev)) +{ + struct sonic_local* lp = (struct sonic_local *)dev->priv; + int i; + + /* Allocate the entire chunk of memory for the descriptors. + Note that this cannot cross a 64K boundary. */ + for (i = 0; i < 20; i++) { + unsigned long desc_base, desc_top; + if ((lp->sonic_desc = + kmalloc(SIZEOF_SONIC_DESC + * SONIC_BUS_SCALE(lp->dma_bitmode), GFP_DMA)) == NULL) { + printk(KERN_ERR "%s: couldn't allocate descriptor buffers\n", dev->name); + } + desc_base = (unsigned long) lp->sonic_desc; + desc_top = desc_base + SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode); + if ((desc_top & 0xffff) >= (desc_base & 0xffff)) + break; + /* Hmm. try again (FIXME: does this actually work?) */ + kfree(lp->sonic_desc); + printk(KERN_DEBUG + "%s: didn't get continguous chunk [%08lx - %08lx], trying again\n", + dev->name, desc_base, desc_top); + } + + if (lp->sonic_desc == NULL) { + printk(KERN_ERR "%s: tried 20 times to allocate descriptor buffers, giving up.\n", + dev->name); + return -ENOMEM; + } + + /* Now set up the pointers to point to the appropriate places */ + lp->cda = lp->sonic_desc; + lp->tda = lp->cda + (SIZEOF_SONIC_CDA * SONIC_BUS_SCALE(lp->dma_bitmode)); + lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS + * SONIC_BUS_SCALE(lp->dma_bitmode)); + lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS + * SONIC_BUS_SCALE(lp->dma_bitmode)); + + /* FIXME, maybe we should use skbs */ + if ((lp->rba = (char *) + kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_DMA)) == NULL) { + printk(KERN_ERR "%s: couldn't allocate receive buffers\n", dev->name); + return -ENOMEM; + } + + { + int rs, ds; + + /* almost always 12*4096, but let's not take chances */ + rs = ((SONIC_NUM_RRS * SONIC_RBSIZE + 4095) / 4096) * 4096; + /* almost always under a page, but let's not take chances */ + ds = ((SIZEOF_SONIC_DESC + 4095) / 4096) * 4096; + kernel_set_cachemode(lp->rba, rs, IOMAP_NOCACHE_SER); + kernel_set_cachemode(lp->sonic_desc, ds, IOMAP_NOCACHE_SER); + } + + flush_cache_all(); + + dev->open = sonic_open; + dev->stop = sonic_close; + dev->hard_start_xmit = sonic_send_packet; + dev->get_stats = sonic_get_stats; + dev->set_multicast_list = &sonic_multicast_list; + + /* + * clear tally counter + */ + sonic_write(dev, SONIC_CRCT, 0xffff); + sonic_write(dev, SONIC_FAET, 0xffff); + sonic_write(dev, SONIC_MPT, 0xffff); + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + return 0; +} + +__initfunc(int mac_onboard_sonic_ethernet_addr(struct device* dev)) +{ + const int prom_addr = ONBOARD_SONIC_PROM_BASE; + int i; + + /* On NuBus boards we can sometimes look in the ROM resources. + No such luck for comm-slot/onboard. */ + for(i = 0; i < 6; i++) + dev->dev_addr[i] = SONIC_READ_PROM(i); + + /* Most of the time, the address is bit-reversed. The NetBSD + source has a rather long and detailed historical account of + why this is so. */ + if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && + memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && + memcmp(dev->dev_addr, "\x00\x05\x02", 3)) + bit_reverse_addr(dev->dev_addr); + else + return 0; + + /* If we still have what seems to be a bogus address, we'll + look in the CAM. The top entry should be ours. */ + /* Danger! This only works if MacOS has already initialized + the card... */ + if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && + memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && + memcmp(dev->dev_addr, "\x00\x05\x02", 3)) + { + unsigned short val; + + printk(KERN_INFO "macsonic: PROM seems to be wrong, trying CAM entry 15\n"); + + sonic_write(dev, SONIC_CMD, SONIC_CR_RST); + sonic_write(dev, SONIC_CEP, 15); + + val = sonic_read(dev, SONIC_CAP2); + dev->dev_addr[5] = val >> 8; + dev->dev_addr[4] = val & 0xff; + val = sonic_read(dev, SONIC_CAP1); + dev->dev_addr[3] = val >> 8; + dev->dev_addr[2] = val & 0xff; + val = sonic_read(dev, SONIC_CAP0); + dev->dev_addr[1] = val >> 8; + dev->dev_addr[0] = val & 0xff; + + printk(KERN_INFO "HW Address from CAM 15: "); + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } + printk("\n"); + } else return 0; + + if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && + memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && + memcmp(dev->dev_addr, "\x00\x05\x02", 3)) + { + /* + * Still nonsense ... messed up someplace! + */ + printk(KERN_ERR "macsonic: ERROR (INVALID MAC)\n"); + return -EIO; + } else return 0; +} + +__initfunc(int mac_onboard_sonic_probe(struct device* dev)) +{ + /* Bwahahaha */ + static int once_is_more_than_enough = 0; + struct sonic_local* lp; + int i; + + if (once_is_more_than_enough) + return -ENODEV; + once_is_more_than_enough = 1; + + if (!MACH_IS_MAC) + return -ENODEV; + + printk(KERN_INFO "Checking for internal Macintosh ethernet (SONIC).. "); + + if (macintosh_config->ether_type != MAC_ETHER_SONIC) + { + printk("none.\n"); + return -ENODEV; + } + + /* Bogus probing, on the models which may or may not have + Ethernet (BTW, the Ethernet *is* always at the same + address, and nothing else lives there, at least if Apple's + documentation is to be believed) */ + if (macintosh_config->ident == MAC_MODEL_Q630 || + macintosh_config->ident == MAC_MODEL_P588 || + macintosh_config->ident == MAC_MODEL_C610) { + unsigned long flags; + int card_present; + + save_flags(flags); + cli(); + card_present = hwreg_present((void*)ONBOARD_SONIC_REGISTERS); + restore_flags(flags); + + if (!card_present) { + printk("none.\n"); + return -ENODEV; + } + } + + printk("yes\n"); + + if (dev) { + dev = init_etherdev(dev, sizeof(struct sonic_local)); + /* methinks this will always be true but better safe than sorry */ + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL); + } else { + dev = init_etherdev(NULL, sizeof(struct sonic_local)); + } + + if (dev == NULL) + return -ENOMEM; + + lp = (struct sonic_local*) dev->priv; + memset(lp, 0, sizeof(struct sonic_local)); + /* Danger! My arms are flailing wildly! You *must* set this + before using sonic_read() */ + + dev->base_addr = ONBOARD_SONIC_REGISTERS; + if (via_alt_mapping) + dev->irq = IRQ_AUTO_3; + else + dev->irq = IRQ_NUBUS_9; + + if (!sonic_version_printed) { + printk(KERN_INFO "%s", version); + sonic_version_printed = 1; + } + printk(KERN_INFO "%s: onboard / comm-slot SONIC at 0x%08lx\n", + dev->name, dev->base_addr); + + /* Now do a song and dance routine in an attempt to determine + the bus width */ + + /* The PowerBook's SONIC is 16 bit always. */ + if (macintosh_config->ident == MAC_MODEL_PB520) { + lp->reg_offset = 0; + lp->dma_bitmode = 0; + } else { + /* Some of the comm-slot cards are 16 bit. But some + of them are not. The 32-bit cards use offset 2 and + pad with zeroes or sometimes ones (I think...) + Therefore, if we try offset 0 and get a silicon + revision of 0, we assume 16 bit. */ + int sr; + + /* Technically this is not necessary since we zeroed + it above */ + lp->reg_offset = 0; + lp->dma_bitmode = 0; + sr = sonic_read(dev, SONIC_SR); + if (sr == 0 || sr == 0xffff) { + lp->reg_offset = 2; + /* 83932 is 0x0004, 83934 is 0x0100 or 0x0101 */ + sr = sonic_read(dev, SONIC_SR); + lp->dma_bitmode = 1; + + } + printk(KERN_INFO + "%s: revision 0x%04x, using %d bit DMA and register offset %d\n", + dev->name, sr, lp->dma_bitmode?32:16, lp->reg_offset); + } + + + /* Software reset, then initialize control registers. */ + sonic_write(dev, SONIC_CMD, SONIC_CR_RST); + sonic_write(dev, SONIC_DCR, SONIC_DCR_BMS | + SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_EXBUS | + (lp->dma_bitmode ? SONIC_DCR_DW : 0)); + + /* Clear *and* disable interrupts to be on the safe side */ + sonic_write(dev, SONIC_ISR,0x7fff); + sonic_write(dev, SONIC_IMR,0); + + /* Now look for the MAC address. */ + if (mac_onboard_sonic_ethernet_addr(dev) != 0) + return -ENODEV; + + printk(KERN_INFO "MAC "); + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } + + printk(" IRQ %d\n", dev->irq); + + /* Shared init code */ + return macsonic_init(dev); +} + +__initfunc(int mac_nubus_sonic_ethernet_addr(struct device* dev, + unsigned long prom_addr, + int id)) +{ + int i; + for(i = 0; i < 6; i++) + dev->dev_addr[i] = SONIC_READ_PROM(i); + /* For now we are going to assume that they're all bit-reversed */ + bit_reverse_addr(dev->dev_addr); + + return 0; +} + +__initfunc(int macsonic_ident(struct nubus_dev* ndev)) +{ + if (ndev->dr_hw == NUBUS_DRHW_ASANTE_LC && + ndev->dr_sw == NUBUS_DRSW_SONIC_LC) + return MACSONIC_DAYNALINK; + if (ndev->dr_hw == NUBUS_DRHW_SONIC && + ndev->dr_sw == NUBUS_DRSW_APPLE) { + /* There has to be a better way to do this... */ + if (strstr(ndev->board->name, "DuoDock")) + return MACSONIC_DUODOCK; + else + return MACSONIC_APPLE; + } + if (ndev->dr_hw == NUBUS_DRHW_APPLE_SONIC_LC && + ndev->dr_sw == 0) { /* huh? */ + return MACSONIC_APPLE16; + } + return -1; +} + +__initfunc(int mac_nubus_sonic_probe(struct device* dev)) +{ + static int slots = 0; + struct nubus_dev* ndev = NULL; + struct sonic_local* lp; + unsigned long base_addr, prom_addr; + u16 sonic_dcr; + int id = -1; + int i; + int reg_offset, dma_bitmode; + + /* Find the first SONIC that hasn't been initialized already */ + while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, + NUBUS_TYPE_ETHERNET, ndev)) != NULL) + { + /* Have we seen it already? */ + if (slots & (1<board->slot)) + continue; + slots |= 1<board->slot; + + /* Is it one of ours? */ + if ((id = macsonic_ident(ndev)) != -1) + break; + } + + if (ndev == NULL) + return -ENODEV; + + switch (id) { + case MACSONIC_DUODOCK: + base_addr = ndev->board->slot_addr + DUODOCK_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + DUODOCK_SONIC_PROM_BASE; + sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT0 | SONIC_DCR_RFT1 + | SONIC_DCR_TFT0; + reg_offset = 2; + dma_bitmode = 1; + break; + case MACSONIC_APPLE: + base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE; + sonic_dcr = SONIC_DCR_BMS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0; + reg_offset = 0; + dma_bitmode = 1; + break; + case MACSONIC_APPLE16: + base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE; + sonic_dcr = SONIC_DCR_EXBUS + | SONIC_DCR_RFT1 | SONIC_DCR_TFT0 + | SONIC_DCR_PO1 | SONIC_DCR_BMS; + reg_offset = 0; + dma_bitmode = 0; + break; + case MACSONIC_DAYNALINK: + base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + DAYNALINK_PROM_BASE; + sonic_dcr = SONIC_DCR_RFT1 | SONIC_DCR_TFT0 + | SONIC_DCR_PO1 | SONIC_DCR_BMS; + reg_offset = 0; + dma_bitmode = 0; + break; + case MACSONIC_DAYNA: + base_addr = ndev->board->slot_addr + DAYNA_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + DAYNA_SONIC_MAC_ADDR; + sonic_dcr = SONIC_DCR_BMS + | SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_PO1; + reg_offset = 0; + dma_bitmode = 0; + break; + default: + printk(KERN_ERR "macsonic: WTF, id is %d\n", id); + return -ENODEV; + } + + if (dev) { + dev = init_etherdev(dev, sizeof(struct sonic_local)); + /* methinks this will always be true but better safe than sorry */ + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL); + } else { + dev = init_etherdev(NULL, sizeof(struct sonic_local)); + } + + if (dev == NULL) + return -ENOMEM; + + lp = (struct sonic_local*) dev->priv; + memset(lp, 0, sizeof(struct sonic_local)); + /* Danger! My arms are flailing wildly! You *must* set this + before using sonic_read() */ + lp->reg_offset = reg_offset; + lp->dma_bitmode = dma_bitmode; + dev->base_addr = base_addr; + dev->irq = SLOT2IRQ(ndev->board->slot); + + if (!sonic_version_printed) { + printk(KERN_INFO "%s", version); + sonic_version_printed = 1; + } + printk(KERN_INFO "%s: %s in slot %X\n", + dev->name, ndev->board->name, ndev->board->slot); + printk(KERN_INFO "%s: revision 0x%04x, using %d bit DMA and register offset %d\n", + dev->name, sonic_read(dev, SONIC_SR), dma_bitmode?32:16, reg_offset); + + /* Software reset, then initialize control registers. */ + sonic_write(dev, SONIC_CMD, SONIC_CR_RST); + sonic_write(dev, SONIC_DCR, sonic_dcr + | (dma_bitmode ? SONIC_DCR_DW : 0)); + /* This *must* be written back to in order to restore the + extended programmable output bits */ + sonic_write(dev, SONIC_DCR2, 0); + + /* Clear *and* disable interrupts to be on the safe side */ + sonic_write(dev, SONIC_ISR,0x7fff); + sonic_write(dev, SONIC_IMR,0); + + /* Now look for the MAC address. */ + if (mac_nubus_sonic_ethernet_addr(dev, prom_addr, id) != 0) + return -ENODEV; + + printk(KERN_INFO "MAC "); + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } + printk(" IRQ %d\n", dev->irq); + + /* Shared init code */ + return macsonic_init(dev); +} + +#ifdef MODULE +static char namespace[16] = ""; +static struct device dev_macsonic = { + NULL, + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL }; + +MODULE_PARM(sonic_debug, "i"); + +EXPORT_NO_SYMBOLS; + +int +init_module(void) +{ + dev_macsonic.name = namespace; + dev_macsonic.init = macsonic_probe; + + if (register_netdev(&dev_macsonic) != 0) { + printk(KERN_WARNING "macsonic.c: No card found\n"); + return -ENXIO; + } + return 0; +} + +void +cleanup_module(void) +{ + if (dev_macsonic.priv != NULL) { + unregister_netdev(&dev_macsonic); + kfree(dev_macsonic.priv); + dev_macsonic.priv = NULL; + } +} +#endif /* MODULE */ + + +#define vdma_alloc(foo, bar) ((u32)foo) +#define vdma_free(baz) +#define sonic_chiptomem(bat) (bat) +#define PHYSADDR(quux) (quux) + +#include "sonic.c" + +/* + * Local variables: + * compile-command: "m68k-linux-gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c -o macsonic.o macsonic.c" + * version-control: t + * kept-new-versions: 5 + * c-indent-level: 8 + * tab-width: 8 + * End: + * + */ diff --git a/drivers/net/mvme147.c b/drivers/net/mvme147.c new file mode 100644 index 000000000000..f142cb7b6e4b --- /dev/null +++ b/drivers/net/mvme147.c @@ -0,0 +1,209 @@ +/* mvme147.c : the Linux/mvme147/lance ethernet driver + * + * Copyright (C) 05/1998 Peter Maydell + * Based on the Sun Lance driver and the NetBSD HP Lance driver + * Uses the generic 7990.c LANCE code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Used for the temporal inet entries and routing */ +#include +#include + +#include + +#include +#include +#include + +#include + +/* We have 16834 bytes of RAM for the init block and buffers. This places + * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx + * buffers and 2 Tx buffers. + */ +#define LANCE_LOG_TX_BUFFERS 1 +#define LANCE_LOG_RX_BUFFERS 3 + +#include "7990.h" /* use generic LANCE code */ + +/* Our private data structure */ +struct m147lance_private { + struct lance_private lance; + void *base; + void *ram; +}; + +/* function prototypes... This is easy because all the grot is in the + * generic LANCE support. All we have to support is probing for boards, + * plus board-specific init, open and close actions. + * Oh, and we need to tell the generic code how to read and write LANCE registers... + */ +int mvme147lance_probe(struct device *dev); +static int m147lance_open(struct device *dev); +static int m147lance_close(struct device *dev); +static void m147lance_writerap(struct m147lance_private *lp, unsigned short value); +static void m147lance_writerdp(struct m147lance_private *lp, unsigned short value); +static unsigned short m147lance_readrdp(struct m147lance_private *lp); + +typedef void (*writerap_t)(void *, unsigned short); +typedef void (*writerdp_t)(void *, unsigned short); +typedef void (*readrdp_t)(void *); + +#ifdef MODULE +static struct m147lance_private *root_m147lance_dev = NULL; +#endif + +/* Initialise the one and only on-board 7990 */ +__initfunc(int mvme147lance_probe(struct device *dev)) +{ + static int called = 0; + static const char name[] = "MVME147 LANCE"; + struct m147lance_private *lp; + u_long *addr; + u_long address; + + if (!MACH_IS_MVME147 || called) + return(ENODEV); + called++; + + dev->priv = kmalloc(sizeof(struct m147lance_private), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + memset(dev->priv, 0, sizeof(struct m147lance_private)); + + /* Fill the dev fields */ + dev->base_addr = (unsigned long)MVME147_LANCE_BASE; + dev->open = &m147lance_open; + dev->stop = &m147lance_close; + dev->hard_start_xmit = &lance_start_xmit; + dev->get_stats = &lance_get_stats; + dev->set_multicast_list = &lance_set_multicast; + dev->dma = 0; + + addr=(u_long *)ETHERNET_ADDRESS; + address = *addr; + dev->dev_addr[0]=0x08; + dev->dev_addr[1]=0x00; + dev->dev_addr[2]=0x3e; + address=address>>8; + dev->dev_addr[5]=address&0xff; + address=address>>8; + dev->dev_addr[4]=address&0xff; + address=address>>8; + dev->dev_addr[3]=address&0xff; + + printk("%s: MVME147 at 0x%08lx, irq %d, Hardware Address %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, dev->base_addr, MVME147_LANCE_IRQ, + dev->dev_addr[0], + dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], + dev->dev_addr[5]); + + lp = (struct m147lance_private *)dev->priv; + lp->ram = (void *)__get_dma_pages(GFP_ATOMIC, 3); /* 16K */ + if (!lp->ram) + { + printk("%s: No memory for LANCE buffers\n", dev->name); + return -ENODEV; + } + + lp->lance.name = (char*)name; /* discards const, shut up gcc */ + lp->lance.ll = (struct lance_regs *)(dev->base_addr); + lp->lance.init_block = (struct lance_init_block *)(lp->ram); /* CPU addr */ + lp->lance.lance_init_block = (struct lance_init_block *)(lp->ram); /* LANCE addr of same RAM */ + lp->lance.busmaster_regval = LE_C3_BSWP; /* we're bigendian */ + lp->lance.irq = MVME147_LANCE_IRQ; + lp->lance.writerap = (writerap_t)m147lance_writerap; + lp->lance.writerdp = (writerdp_t)m147lance_writerdp; + lp->lance.readrdp = (readrdp_t)m147lance_readrdp; + lp->lance.lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS; + lp->lance.lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS; + lp->lance.rx_ring_mod_mask = RX_RING_MOD_MASK; + lp->lance.tx_ring_mod_mask = TX_RING_MOD_MASK; + ether_setup(dev); + +#ifdef MODULE + dev->ifindex = dev_new_index(); + lp->next_module = root_m147lance_dev; + root_m147lance_dev = lp; +#endif /* MODULE */ + + return 0; +} + +static void m147lance_writerap(struct m147lance_private *lp, unsigned short value) +{ + lp->lance.ll->rap = value; +} + +static void m147lance_writerdp(struct m147lance_private *lp, unsigned short value) +{ + lp->lance.ll->rdp = value; +} + +static unsigned short m147lance_readrdp(struct m147lance_private *lp) +{ + return lp->lance.ll->rdp; +} + +static int m147lance_open(struct device *dev) +{ + int status; + + status = lance_open(dev); /* call generic lance open code */ + if (status) + return status; + /* enable interrupts at board level. */ + m147_pcc->lan_cntrl=0; /* clear the interrupts (if any) */ + m147_pcc->lan_cntrl=0x08 | 0x04; /* Enable irq 4 */ + + MOD_INC_USE_COUNT; + return 0; +} + +static int m147lance_close(struct device *dev) +{ + /* disable interrupts at boardlevel */ + m147_pcc->lan_cntrl=0x0; /* disable interrupts */ + lance_close(dev); + MOD_DEC_USE_COUNT; + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + root_lance_dev = NULL; + return mvme147lance_probe(NULL); +} + +void cleanup_module(void) +{ + /* Walk the chain of devices, unregistering them */ + struct m147lance_private *lp; + while (root_m147lance_dev) { + lp = root_m147lance_dev->next_module; + unregister_netdev(root_lance_dev->dev); + free_pages(lp->ram, 3); + kfree(root_lance_dev->dev); + root_lance_dev = lp; + } +} + +#endif /* MODULE */ diff --git a/drivers/net/xpds/Makefile b/drivers/net/xpds/Makefile new file mode 100644 index 000000000000..af86cb49cd0f --- /dev/null +++ b/drivers/net/xpds/Makefile @@ -0,0 +1,23 @@ +# File: drivers/xpds/Makefile +# +# Makefile for Xpeed DSL NIC driver +# + +ifeq ($(CONFIG_XPEED),y) + O_TARGET := xpds-fr.o + O_OBJS = xpds.o xpds-fsm.o xpds-sdsl.o xpds-encap-fr.o +else + ifeq ($(CONFIG_XPEED),m) + MOD_LIST_NAME := NET_MODULES + M_OBJS := xpds-fr.o + O_TARGET := xpds-fr.o + O_OBJS = xpds.o xpds-fsm.o xpds-sdsl.o xpds-encap-fr.o + endif +endif + +EXTRA_CFLAGS += -I. -DDEBUG=0 + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s diff --git a/drivers/net/xpds/xpds-encap-fr.c b/drivers/net/xpds/xpds-encap-fr.c new file mode 100644 index 000000000000..3306134f7c62 --- /dev/null +++ b/drivers/net/xpds/xpds-encap-fr.c @@ -0,0 +1,751 @@ +/* + * Copyright 2000 Xpeed, Inc. + * fr.c $Revision: 1.23 $ + * License to copy and distribute is GNU General Public License, version 2. + * Some code adapted from Mike McLagan's dlci.c 0.30 from Linux kernels + * 2.0-2.2. + */ +#define __KERNEL__ 1 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "xpds.h" +#include "xpds-encap-fr.h" +#include "xpds-softnet.h" + +#define XPDS_DLCI_LMI_OFF 0 +#define XPDS_DLCI_LMI_LT 1 +#define XPDS_DLCI_LMI_NT 2 +#define XPDS_DLCI_LMI_NT_BIDIRECTIONAL 3 + +int xpds_dlci_t391 = 10; +int xpds_dlci_n391 = 6; + +#define DEBUG_DLCI 4 +extern int xpds_debug_level; + +#if DEBUG +#define dprintk if (xpds_debug_level & DEBUG_DLCI) printk +#else +#define dprintk if (0) printk +#endif + +#define nrprintk if (net_ratelimit()) printk + +typedef struct { + u8 dlci_header[2] __attribute__ ((packed)); + u8 protocol_discriminator __attribute__ ((packed)); + u8 call_reference __attribute__ ((packed)); + u8 padding __attribute__ ((packed)); +} dlci_status_head_t __attribute__ ((packed)); + +typedef struct { + u8 message_type __attribute__ ((packed)); + u8 locking_shift __attribute__ ((packed)); + u8 report_content_id __attribute__ ((packed)); + u8 report_content_length __attribute__ ((packed)); + u8 report_type __attribute__ ((packed)); + u8 liv_element_id __attribute__ ((packed)); + u8 liv_length __attribute__ ((packed)); + u8 liv_send_sequence __attribute__ ((packed)); + u8 liv_receive_sequence __attribute__ ((packed)); +} dlci_status_tail_t __attribute__ ((packed)); + +#define MESSAGE_STATUS_ENQUIRY 0x75 +#define MESSAGE_STATUS 0x7d + +#define REPORT_FULL_STATUS 0x00 +#define REPORT_LIV 0x01 +#define REPORT_SINGLE_PVC_STATS 0x02 + +typedef struct { + u8 pvc_status_id __attribute__ ((packed)); + u8 length __attribute__ ((packed)); + u8 dlci_info[2] __attribute__ ((packed)); + u8 flags __attribute__ ((packed)); +} dlci_pvc_status_t; + +#define PVC_FLAGS__NEW 0x08 +#define PVC_FLAGS__ACTIVE 0x02 + +typedef struct dlci_timer_data_t { + struct net_device *dev; + int dlci_num; +} dlci_timer_data_t; + +/* + * We generate LMI status enquiries in DLCI_LMI_NT mode. + */ +static void +xpds_dlci_lmi_timer (void *p) +{ + dlci_timer_data_t *data; + struct net_device *dev; + int card_num; + struct frad_local *flp; + dlci_status_head_t *status_head; + dlci_status_tail_t *status_tail; + struct sk_buff *skb; + short dlci, lmi_dlci = 0; + int i; + + data = (dlci_timer_data_t *) p; + + dev = data->dev; + card_num = dev - xpds_devs; + flp = &(xpds_data[card_num].frad_data); + + dprintk (KERN_DEBUG "%s: xpds_dlci_lmi_timer()\n", dev->name); + + if (xpds_data[card_num].dlci_lmi != XPDS_DLCI_LMI_NT && + xpds_data[card_num].dlci_lmi != XPDS_DLCI_LMI_NT_BIDIRECTIONAL){ + return; + } + + if (flp->no_initiate_lmi) return; + + dlci = flp->dlci[data->dlci_num]; + dprintk (KERN_DEBUG "%s: xpds_dlci_lmi_timer(), dev = %p, dlci = %d\n", xpds_devs[card_num].name, dev, dlci); + + if (xpds_if_busy(dev)) { + + skb = alloc_skb (sizeof (dlci_status_head_t) + + sizeof (dlci_status_tail_t), GFP_ATOMIC); + if (skb == NULL) { + printk (KERN_ERR "%s: unable to allocate skb in xlds_dlci_lmi_timer()\n", dev->name); + return; + } + skb->dev = dev; + skb->len = sizeof (dlci_status_head_t) + + sizeof (dlci_status_tail_t); + status_head = (dlci_status_head_t *) skb->data; + status_tail = (dlci_status_tail_t *) (skb->data + + sizeof (dlci_status_head_t)); + status_head->dlci_header[0] = ((lmi_dlci >> 4) << 2) | + xpds_data[card_num].dlci_cr; + status_head->dlci_header[1] = (lmi_dlci << 4) | 1; + status_head->protocol_discriminator = 0x03; + status_head->call_reference = 0x08; + status_head->padding = 0; + + status_tail->message_type = MESSAGE_STATUS_ENQUIRY; + status_tail->locking_shift = 0x95; + status_tail->report_content_id = 0x01; + status_tail->report_content_length = 0x01; + status_tail->liv_element_id = 0x03 /* 0x19 */; + status_tail->liv_length = 0x02; + + status_tail->liv_send_sequence = (flp->liv_send_sequence) ++; + status_tail->liv_receive_sequence = flp->liv_receive_sequence; + if (flp->pvc_active[data->dlci_num]) { + if (flp->remote_liv_receive_sequence >= + flp->new_liv_send_sequence) { + flp->pvc_new[data->dlci_num] = 0; + } else { + flp->pvc_new[data->dlci_num] = 1; + } + } else { + flp->new_liv_send_sequence = + status_tail->liv_send_sequence; + } + dprintk (KERN_DEBUG "%s: dlci = %d ", xpds_devs[card_num].name, dlci); + dprintk (KERN_DEBUG "pvc = %d, new = %d\n", + flp->pvc_active[data->dlci_num], flp->pvc_new[data->dlci_num]); + dprintk (KERN_DEBUG "%s: liv_send_sequence = %d, liv_receive_sequence = %d\n", xpds_devs[card_num].name, status_tail->liv_send_sequence, status_tail->liv_receive_sequence); + flp->message_number ++; + if (flp->message_number >= xpds_dlci_n391) { + flp->message_number = 0; + } + status_tail->report_type = + (flp->message_number == 0) ? + REPORT_FULL_STATUS : REPORT_LIV; + dprintk (KERN_DEBUG "message_number = %d, sending %02x\n", + flp->message_number, status_tail->report_type); + dprintk (KERN_DEBUG "%s: sending LMI packet in xpds_dlci_lmi_timer()\n", xpds_devs[card_num].name); + xpds_tx (skb->data, skb->len, dev); + + dev_kfree_skb (skb); + } + + i = data->dlci_num; + + init_timer(&(xpds_data[card_num].dlci_lmi_timers[i])); + xpds_data[card_num].dlci_lmi_timers[i].function = (void *)&xpds_dlci_lmi_timer; + xpds_data[card_num].dlci_lmi_timers[i].expires = jiffies + xpds_dlci_t391 * HZ; + xpds_data[card_num].dlci_lmi_timers[i].data = (unsigned long)&(xpds_data[card_num].dlci_lmi_timer_data[i]); + add_timer(&(xpds_data[card_num].dlci_lmi_timers[i])); + + dprintk(KERN_DEBUG "%s: xpds_dlci_lmi_timer()\n", dev->name); +} + +void +xpds_dlci_install_lmi_timer (int i, struct net_device *dev) +{ + int card_num; + + card_num = dev - xpds_devs; + + dprintk (KERN_DEBUG "%s: xpds_dlci_install_lmi_timer (%d, %p)\n", xpds_devs[card_num].name, i, dev); + if (i < 0 || i >= CONFIG_DLCI_COUNT) { + printk (KERN_ERR "%s: invalid DLCI device number %d\n", xpds_devs[card_num].name, i); + return; + } + init_timer(&(xpds_data[card_num].dlci_lmi_timers[i])); + xpds_data[card_num].dlci_lmi_timers[i].function = (void *)&xpds_dlci_lmi_timer; + xpds_data[card_num].dlci_lmi_timers[i].expires = jiffies + xpds_dlci_t391 * HZ; + xpds_data[card_num].dlci_lmi_timer_data[i].dev = dev; + xpds_data[card_num].dlci_lmi_timer_data[i].dlci_num = i; + xpds_data[card_num].dlci_lmi_timers[i].data = (unsigned long)&(xpds_data[card_num].dlci_lmi_timer_data[i]); + add_timer(&(xpds_data[card_num].dlci_lmi_timers[i])); + dprintk (KERN_DEBUG "%s: xpds_dlci_install_lmi_timer() done\n", + dev->name); +} + +void +xpds_dlci_remove_lmi_timer (int i, struct net_device *dev) +{ + int card_num; + + card_num = dev - xpds_devs; + + dprintk (KERN_DEBUG "%s: xpds_dlci_remove_lmi_timer (%d, %p)\n", + dev->name, i, dev); + if (i < 0 || i >= CONFIG_DLCI_COUNT) { + printk (KERN_ERR "%s: invalid DLCI device number %d\n", xpds_devs[card_num].name, i); + return; + } + del_timer(&(xpds_data[card_num].dlci_lmi_timers[i])); + dprintk (KERN_DEBUG "%s: xpds_dlci_remove_lmi_timer() done\n", + dev->name); +} + + +static void +xpds_dlci_handle_status (struct sk_buff *skb, struct net_device *dev) +{ + struct frad_local *flp; + dlci_status_head_t *dlci_status_head, *reply_status_head; + dlci_status_tail_t *dlci_status_tail, *reply_status_tail; + dlci_pvc_status_t *dlci_pvc_status, *reply_pvc_status; + struct sk_buff *reply_skb; + short dlci, lmi_dlci = 0; + int i; + int old_pvc_active; + int has_pvc_status; + int has_padding; + int card_num; + + card_num = dev - xpds_devs; + + if (xpds_data[card_num].dlci_lmi == XPDS_DLCI_LMI_OFF) return; + + flp = &(xpds_data[card_num].frad_data); + skb->dev = dev; + + dlci_status_head = (dlci_status_head_t *)skb->data; + if (dlci_status_head->padding != 0) { + /* + * no padding + */ + has_padding = 0; + } else { + /* + * one byte of zero padding between call reference + * and message type + */ + has_padding = 1; + } + dlci_status_tail = (dlci_status_tail_t *) + (skb->data + sizeof (*dlci_status_head) - 1 + has_padding); + if (skb->len < sizeof (*dlci_status_head) + sizeof (*dlci_status_tail) - 1 + has_padding) { + dprintk (KERN_ERR "%s: LMI packet of length %lu is too short\n", xpds_devs[card_num].name, (unsigned long) (skb->len)); + } + dlci_pvc_status = (dlci_pvc_status_t *) + (skb->data + sizeof (*dlci_status_head) + + sizeof (*dlci_status_tail) - 1 + has_padding); + + dprintk (KERN_DEBUG "%s: LMI packet received has protocol discriminator = 0x%02x\n", xpds_devs[card_num].name, dlci_status_head->protocol_discriminator); + dprintk (KERN_DEBUG "%s: LMI packet received has call reference = 0x%02x\n", + dev->name, dlci_status_head->call_reference); + if (dlci_status_head->protocol_discriminator != FRAD_I_UI || + dlci_status_head->call_reference != FRAD_P_Q933) { + printk (KERN_NOTICE "%s: dlci_handle_status called with 0x%02x 0x%02x\n", + dev->name, dlci_status_head->protocol_discriminator, + dlci_status_head->call_reference); + return; + } + + dprintk (KERN_DEBUG "LMI packet received has message type = 0x%02x\n", + dlci_status_tail->message_type); + if (dlci_status_tail->message_type != MESSAGE_STATUS_ENQUIRY && + dlci_status_tail->message_type != MESSAGE_STATUS) { + printk (KERN_NOTICE "unknown message type 0x%02x\n", + dlci_status_tail->message_type); + return; + } + + dprintk (KERN_DEBUG "LMI packet received has report type = 0x%02x\n", + dlci_status_tail->report_type); + if (dlci_status_tail->report_type != REPORT_FULL_STATUS && + dlci_status_tail->report_type != REPORT_LIV) { + dprintk (KERN_DEBUG "report type is not full status or LIV\n"); + return; + } + + dprintk (KERN_DEBUG "LMI packet received has receive sequence = %d\n", + dlci_status_tail->liv_receive_sequence); + dprintk (KERN_DEBUG "LMI packet received has send sequence = %d\n", + dlci_status_tail->liv_send_sequence); + + reply_skb = alloc_skb (sizeof (dlci_status_head_t) + + sizeof (dlci_status_tail_t) + sizeof (dlci_pvc_status_t), + GFP_ATOMIC); + if (reply_skb == NULL) { + printk (KERN_ERR "%s: unable to allocate reply_skb in xpds_dlci_handle_status().\n", dev->name); + return; + } + reply_skb->len = sizeof (dlci_status_head_t) + + sizeof (dlci_status_tail_t) /* + sizeof (dlci_pvc_status_t) */; + reply_status_head = (dlci_status_head_t *) reply_skb->data; + reply_status_tail = (dlci_status_tail_t *) (reply_skb->data + + sizeof (dlci_status_head_t)); + reply_pvc_status = (dlci_pvc_status_t *) (reply_skb->data + + sizeof (dlci_status_head_t) + sizeof (dlci_status_tail_t) ); + + if (dlci_status_tail->liv_receive_sequence > flp->liv_send_sequence){ + dprintk (KERN_DEBUG "received receive sequence number %d > %d\n", + dlci_status_tail->liv_receive_sequence, + flp->liv_send_sequence); + } + flp->remote_liv_receive_sequence = + dlci_status_tail->liv_receive_sequence; + flp->liv_send_sequence = dlci_status_tail->liv_receive_sequence + 1; + + flp->remote_liv_send_sequence = + dlci_status_tail->liv_send_sequence; + flp->liv_receive_sequence = dlci_status_tail->liv_send_sequence; + + if (dlci_status_tail->report_type == REPORT_FULL_STATUS) { + if (skb->len < sizeof (*dlci_status_head) + sizeof (*dlci_status_tail) - 1 + has_padding + sizeof (*dlci_pvc_status)) { + if (dlci_status_tail->message_type == MESSAGE_STATUS) { + printk (KERN_ERR "%s: LMI packet length %lu is too short for report type REPORT_FULL_STATUS\n", xpds_devs[card_num].name, (unsigned long) (skb->len)); + } + dlci = -1; + has_pvc_status = 0; + } else { + dlci = ((dlci_pvc_status->dlci_info[0] & 0x7f) << 4) | + ((dlci_pvc_status->dlci_info[1] & 0x7f) >> 3); + has_pvc_status = 1; + } + } else { + dprintk (KERN_DEBUG "%s: dlci_status_tail->report_type = 0x%02x (not REPORT_FULL_STATUS)\n", xpds_devs[card_num].name, dlci_status_tail->report_type); + dlci = -1; + has_pvc_status = 0; + } + dprintk (KERN_DEBUG "%s: LMI packet received has DLCI = %d, has_pvc_status = %d\n", xpds_devs[card_num].name, dlci, has_pvc_status); + + if (has_pvc_status) { + for (i = 0; i < CONFIG_DLCI_MAX; i ++) { + if (dlci == flp->dlci[i]) break; + } + if (i >= CONFIG_DLCI_MAX) { + int j; + + printk (KERN_ERR "%s: invalid DLCI %d\n", + dev->name, dlci); + + printk (KERN_ERR "%s: flp->dlci[] = {", xpds_devs[card_num].name); + for (j = 0; j < CONFIG_DLCI_MAX; j ++) { + if (j != 0) printk (", "); + printk ("%d", flp->dlci[j]); + } + printk ("}\n"); + dev_kfree_skb (reply_skb); + return; + } + } else { + i = 0; + } + + reply_status_head->dlci_header[0] = ((lmi_dlci >> 4) << 2) | + xpds_data[card_num].dlci_cr; + reply_status_head->dlci_header[1] = (lmi_dlci << 4) | 1; + reply_status_head->protocol_discriminator = 0x03; + reply_status_head->call_reference = 0x08; + reply_status_head->padding = 0; + + reply_status_tail->message_type = MESSAGE_STATUS; + reply_status_tail->locking_shift = 0x95; + reply_status_tail->report_content_id = 0x01; + reply_status_tail->report_content_length = 0x01; + flp->message_number ++; + if (flp->message_number >= xpds_dlci_n391) { + flp->message_number = 0; + } + reply_status_tail->report_type = + (flp->message_number == 0) ? + REPORT_FULL_STATUS : REPORT_LIV; + dprintk (KERN_DEBUG "%s: message_number = %d, sending 0x%02x\n", + dev->name, flp->message_number, reply_status_tail->report_type); + reply_status_tail->liv_element_id = 0x03 /* 0x19 */; + reply_status_tail->liv_length = 0x02; + reply_status_tail->liv_send_sequence = (flp->liv_send_sequence) ++; + reply_status_tail->liv_receive_sequence = flp->liv_receive_sequence; + + if (has_pvc_status) { + reply_pvc_status->pvc_status_id = 0x7; + reply_pvc_status->length = 3; + reply_pvc_status->dlci_info[0] = (dlci >> 6) & 0x3f; + reply_pvc_status->dlci_info[1] = ((dlci << 3) & 0x78) | 0x80; + + dprintk (KERN_DEBUG "dlci_pvc_status->pvc_status_id = 0x%02x\n", + dlci_pvc_status->pvc_status_id); + dprintk (KERN_DEBUG "dlci_pvc_status->length = 0x%02x\n", + dlci_pvc_status->length); + dprintk (KERN_DEBUG "dlci_pvc_status->dlci_info = 0x%02x 0x%02x\n", dlci_pvc_status->dlci_info[0], dlci_pvc_status->dlci_info[1]); + dprintk (KERN_DEBUG "dlci_pvc_status->flags = 0x%02x\n", + dlci_pvc_status->flags); + old_pvc_active = flp->pvc_active[i]; + flp->pvc_active[i] = (dlci_pvc_status->flags & PVC_FLAGS__ACTIVE) != 0; + if (! old_pvc_active && flp->pvc_active[i]) { + printk (KERN_NOTICE "%s: DLCI %d PVC became active\n", xpds_devs[card_num].name, dlci); + } + if (old_pvc_active && ! flp->pvc_active[i]) { + printk (KERN_NOTICE "%s: DLCI %d PVC became inactive\n", xpds_devs[card_num].name, dlci); + } + if (flp->pvc_active[i]) { + if (dlci_status_tail->liv_receive_sequence >= + flp->new_liv_send_sequence) { + flp->pvc_new[i] = 0; + } else { + flp->pvc_new[i] = 1; + } + } else { + flp->new_liv_send_sequence = + reply_status_tail->liv_send_sequence; + } + dprintk (KERN_DEBUG "dlci = %d\n", dlci); + dprintk (KERN_DEBUG "pvc = %d, new = %d\n", + flp->pvc_active[i], flp->pvc_new[i]); + dprintk (KERN_DEBUG "liv_send_sequence = %d, liv_receive_sequence = %d\n", reply_status_tail->liv_send_sequence, reply_status_tail->liv_receive_sequence); + reply_pvc_status->flags = 0x80 | (flp->pvc_new[i] << 3) | + (flp->pvc_active[i] << 1); + } + + /* ... */ + + if (xpds_data[card_num].dlci_lmi == XPDS_DLCI_LMI_LT || + xpds_data[card_num].dlci_lmi == XPDS_DLCI_LMI_NT_BIDIRECTIONAL){ + dprintk (KERN_DEBUG "sending LMI packet in xpds_dlci_handle_status()\n"); + xpds_tx (reply_skb->data, reply_skb->len, dev); + dev_kfree_skb(reply_skb); + } + + /* This is freed by the caller. */ + /* dev_kfree_skb(skb); */ +} + +void +xpds_dlci_receive(struct sk_buff *skb, struct net_device *dev) +{ + int card_num; + struct frhdr *hdr; + struct frad_local *flp; + int process, header, actual_header_size; + + dprintk(KERN_DEBUG "xpds_dlci_receive (%p, %p)\n", skb, dev); + card_num = dev - xpds_devs; + flp = &(xpds_data[card_num].frad_data); + hdr = (struct frhdr *) (skb->data); + process = 0; + header = sizeof(hdr->addr_control); + skb->dev = dev; + + actual_header_size = skb->tail - skb->data; + if (actual_header_size < header + 2) { + nrprintk (KERN_NOTICE "%s: header is too short (%d bytes)\n", + dev->name, skb->tail - skb->data); + dev_kfree_skb(skb); + xpds_data[card_num].stats.rx_errors++; + return; + } + + if (hdr->control != FRAD_I_UI) { + nrprintk(KERN_NOTICE "%s: Invalid header flag 0x%02X.\n", + dev->name, hdr->control); + xpds_data[card_num].stats.rx_errors++; + } else { + switch (hdr->IP_NLPID) { + case FRAD_P_PADDING: + if (hdr->NLPID != FRAD_P_SNAP) { + nrprintk(KERN_NOTICE + "%s: Unsupported NLPID 0x%02X.\n", + dev->name, hdr->NLPID); + xpds_data[card_num].stats.rx_errors++; + break; + } + + if (actual_header_size < sizeof (struct frhdr)) { + nrprintk (KERN_NOTICE "%s: header is too short (%d bytes, IP_NLPID == FRAD_P_PADDING)\n", dev->name, skb->tail - skb->data); + dev_kfree_skb(skb); + return; + } + + if (hdr->OUI[0] == FRAD_OUI_BRIDGED_0 && + hdr->OUI[1] == FRAD_OUI_BRIDGED_1 && + hdr->OUI[2] == FRAD_OUI_BRIDGED_2) { + /* bridged ethernet */ + + struct ethhdr *ehdr; + + header = sizeof(struct frhdr); + ehdr = + (struct ethhdr *) (skb->data + header); + skb->protocol = ntohs(ehdr->h_proto); + /* skb->mac.raw = (char *)&(ehdr->h_dest); */ + dprintk(KERN_DEBUG + "hdr->NLPID = FRAD_P_SNAP (%02X), protocol = 0x%04x\n", + FRAD_P_SNAP, skb->protocol); + process = 1; + break; + } + + if (hdr->OUI[0] + hdr->OUI[1] + hdr->OUI[2] != 0) { + nrprintk(KERN_NOTICE + "%s: Unsupported organizationally unique identifier 0x%02X-%02X-%02X.\n", + dev->name, hdr->OUI[0], hdr->OUI[1], + hdr->OUI[2]); + xpds_data[card_num].stats.rx_errors++; + break; + } + + /* at this point, it's an EtherType frame */ + header = sizeof(struct frhdr); + /* Already in network order ! */ + skb->protocol = hdr->PID; + process = 1; + break; + + case FRAD_P_IP: + dprintk(KERN_DEBUG + "hdr->IP_NLPID = FRAD_P_IP (%02X)\n", + FRAD_P_IP); + header = + sizeof(hdr->addr_control) + + sizeof(hdr->control) + sizeof(hdr->IP_NLPID); + if (actual_header_size < header) { + nrprintk (KERN_NOTICE "%s: header is too short (%d bytes, IP_NLPID == FRAD_P_IP)\n", dev->name, skb->tail - skb->data); + xpds_data[card_num].stats.rx_errors++; + } else { + skb->protocol = htons(ETH_P_IP); + process = 1; + } + break; + + case FRAD_P_Q933: + /* status / status enquiry message */ + dprintk(KERN_DEBUG + "hdr->IP_NLPID = FRAD_P_Q933 (%02X)\n", + FRAD_P_Q933); + xpds_dlci_handle_status(skb, dev); + break; + case FRAD_P_SNAP: + dprintk(KERN_DEBUG + "hdr->IP_NLPID = FRAD_P_SNAP (%02X)\n", + FRAD_P_SNAP); + skb->protocol = htons(ETH_P_ARP); + process = 1; + break; + case FRAD_P_CLNP: + nrprintk(KERN_NOTICE + "%s: Unsupported NLPID 0x%02X.\n", + dev->name, hdr->pad); + xpds_data[card_num].stats.rx_errors++; + break; + + default: + nrprintk(KERN_NOTICE + "%s: Invalid pad byte 0x%02X.\n", dev->name, + hdr->pad); + xpds_data[card_num].stats.rx_errors++; + break; + } + } + + if (process) { + dprintk(KERN_DEBUG "%s: header size = %d\n", dev->name, + header); + if (xpds_data[card_num].bridged_ethernet) { + skb_pull(skb, header); + } else { + struct ethhdr *ehdr; + skb_pull(skb, header); + skb_push(skb, sizeof(struct ethhdr)); + ehdr = (struct ethhdr *) skb->data; + memcpy(ehdr->h_dest, + xpds_data[card_num].serial_data.mac_address, + sizeof(ehdr->h_dest)); + /* +++ */ + memset(ehdr->h_source, 0xff, + sizeof(ehdr->h_source)); + /* +++ */ + ehdr->h_proto = skb->protocol; + } + skb->ip_summed = CHECKSUM_NONE; +#if DEBUG + { + long i, skblen; + + dprintk(KERN_DEBUG + "%s: packet of length %ld to be given to netif_rx():", dev->name, (long) (skb->len)); + skblen = skb->len > 256 ? 256 : skb->len; + for (i = 0; i < skblen; i++) { + dprintk(" %02x", skb->data[i]); + } + if (skb->len > skblen) dprintk (" ..."); + dprintk("\n"); + dprintk(KERN_DEBUG + "%s: skb->head = %p, skb->data = %p, skb->tail = %p, skb->end = %p\n", + dev->name, skb->head, skb->data, skb->tail, + skb->end); + } +#endif + skb->protocol = eth_type_trans(skb, dev); + dprintk(KERN_DEBUG "calling netif_rx (%p)\n", skb); + netif_rx(skb); + } else { + dev_kfree_skb(skb); + } + dprintk(KERN_DEBUG "xpds_dlci_receive done\n"); +} + +static unsigned int +xpds_dlci_add_fr_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, u8 * hdr) +{ + unsigned hlen; + short dlci; + struct frad_local *flp; + int card_num; + struct frhdr *frhdr; + + frhdr = (struct frhdr *) hdr; + card_num = dev - xpds_devs; + dprintk(KERN_DEBUG "%s: adding frame relay header\n", dev->name); + flp = &(xpds_data[card_num].frad_data); + dlci = xpds_data[card_num].dlci; + hdr[0] = ((dlci >> 4) << 2) | xpds_data[card_num].dlci_cr; + hdr[1] = (dlci << 4) | 1; + + frhdr->control = FRAD_I_UI; + dprintk(KERN_DEBUG "%s: type = 0x%04x\n", dev->name, type); + switch (type) { + case ETH_P_IP: + case ETH_P_ARP: + if (xpds_data[card_num].bridged_ethernet) { + frhdr->pad = FRAD_P_PADDING; + frhdr->NLPID = FRAD_P_SNAP; + frhdr->OUI[0] = FRAD_OUI_BRIDGED_0; + frhdr->OUI[1] = FRAD_OUI_BRIDGED_1; + frhdr->OUI[2] = FRAD_OUI_BRIDGED_2; + frhdr->PID = htons(FRAD_PID); + hlen = sizeof(*frhdr); + } else { + frhdr->IP_NLPID = FRAD_P_IP; + hlen = + sizeof(frhdr->addr_control) + + sizeof(frhdr->control) + + sizeof(frhdr->IP_NLPID); + } + break; + + /* feel free to add other types, if necessary */ + + default: + frhdr->pad = FRAD_P_PADDING; + frhdr->NLPID = FRAD_P_SNAP; + memset(frhdr->OUI, 0, sizeof(frhdr->OUI)); + frhdr->PID = htons(type); + hlen = sizeof(*frhdr); + break; + } + + dprintk(KERN_DEBUG "type = 0x%02x, bridged = %d, hlen = %d\n", + type, xpds_data[card_num].bridged_ethernet, hlen); + +#if DEBUG + { + int i; + + dprintk(KERN_DEBUG "dlci_header added:"); + for (i = 0; i < hlen; i++) { + dprintk(" %02x", ((u8 *) hdr)[i]); + } + dprintk("\n"); + } +#endif + return hlen; +} + +int +xpds_dlci_transmit(struct sk_buff *skb, struct net_device *dev) +{ + u8 *hdr, *buffer; + int card_num, rc; + unsigned int ptr, len; + + dprintk(KERN_DEBUG "xpds_dlci_transmit (%p, %p)\n", skb, dev); + card_num = dev - xpds_devs; + + buffer = kmalloc (skb->len + sizeof(struct frhdr), GFP_ATOMIC); + if (buffer == NULL) { + printk (KERN_ERR "%s: failed to allocate buffer in xpds_dlci_transmit()\n", xpds_devs[card_num].name); + return -ENOMEM; + } + hdr = buffer; + ptr = xpds_dlci_add_fr_header(skb, dev, ntohs(skb->protocol), hdr); + + if (xpds_data[card_num].bridged_ethernet) { + memcpy(buffer + ptr, skb->data, + sizeof(struct ethhdr)); + ptr += sizeof(struct ethhdr); + } + memcpy(buffer + ptr, + skb->data + sizeof(struct ethhdr), + skb->len - sizeof(struct ethhdr)); + len = ptr + skb->len - sizeof(struct ethhdr); + + rc = xpds_tx(buffer, len, dev); + kfree (buffer); + + if (rc) { + xpds_data[card_num].stats.tx_errors++; + } else { + xpds_data[card_num].stats.tx_packets++; + dev_kfree_skb(skb); + } + + dprintk(KERN_DEBUG "xpds_dlci_transmit done\n"); + return rc; +} diff --git a/drivers/net/xpds/xpds-encap-fr.h b/drivers/net/xpds/xpds-encap-fr.h new file mode 100644 index 000000000000..cb1a8be8b6fb --- /dev/null +++ b/drivers/net/xpds/xpds-encap-fr.h @@ -0,0 +1,105 @@ +/* + * Copyright 2000, Xpeed, Inc. + * fr.h $Revision: 1.10 $ + * License to copy and distribute is GNU General Public License, version 2. + * Some code adapted from Mike McLagen's /usr/include/linux/if_frad.h in + * Linux kernels 2.0-2.2. + */ + +#ifndef XPDS_ENCAP_FR_H +#define XPDS_ENCAP_FR_H + +#include +#include +#include +#include "xpds-softnet.h" + +#ifndef CONFIG_DLCI_MAX +#define CONFIG_DLCI_MODULE 1 +#define CONFIG_DLCI_MAX 8 +#define CONFIG_DLCI_COUNT 24 +#endif + +#define FRAD_STATION_CPE 0x0000 +#define FRAD_STATION_NODE 0x0001 + +#define FRAD_TX_IGNORE_CIR 0x0001 +#define FRAD_RX_ACCOUNT_CIR 0x0002 +#define FRAD_DROP_ABORTED 0x0004 +#define FRAD_BUFFERIF 0x0008 +#define FRAD_STATS 0x0010 +#define FRAD_MCI 0x0100 +#define FRAD_AUTODLCI 0x8000 +#define FRAD_VALID_FLAGS 0x811F + +#define FRAD_CLOCK_INT 0x0001 +#define FRAD_CLOCK_EXT 0x0000 + +/* these are the fields of an RFC 1490 header */ +struct frhdr { + u8 addr_control[2] __attribute__ ((packed)); + u8 control __attribute__ ((packed)); + + /* for IP packets, this can be the NLPID */ + u8 pad __attribute__ ((packed)); + + u8 NLPID __attribute__ ((packed)); + u8 OUI[3] __attribute__ ((packed)); + u16 PID __attribute__ ((packed)); + +#define IP_NLPID pad +} __attribute__ ((packed)); + +/* see RFC 1490 for the definition of the following */ +#define FRAD_I_UI 0x03 + +#define FRAD_P_PADDING 0x00 +#define FRAD_P_Q933 0x08 +#define FRAD_P_SNAP 0x80 +#define FRAD_P_CLNP 0x81 +#define FRAD_P_IP 0xCC + +#define FRAD_OUI_BRIDGED_0 0x00 +#define FRAD_OUI_BRIDGED_1 0x80 +#define FRAD_OUI_BRIDGED_2 0xc2 + +#define FRAD_PID 0x0007 + +struct frad_local { + short dlci[CONFIG_DLCI_MAX]; + + /* fields for LMI messages */ + int liv_send_sequence; + int liv_receive_sequence; + int remote_liv_send_sequence; + int remote_liv_receive_sequence; + int new_liv_send_sequence; + int pvc_active[CONFIG_DLCI_MAX]; + int pvc_new[CONFIG_DLCI_MAX]; + int no_initiate_lmi; + int message_number; + +}; + +extern void xpds_dlci_receive(struct sk_buff *skb, struct net_device *dev); +extern int xpds_dlci_transmit(struct sk_buff *skb, struct net_device *dev); + +#define XPDS_DEFAULT_DLCI 16 +#define XPDS_DEFAULT_DLCI_CR 0 /* 0 or 2 are the possible values */ + +#define XPDS_DLCI_LMI_LT_OR_NT (-1) +#define XPDS_DLCI_LMI_NONE 0 +#define XPDS_DLCI_LMI_LT 1 +#define XPDS_DLCI_LMI_NT 2 +#define XPDS_DLCI_LMI_NT_BIDIRECTIONAL 3 + +typedef struct dlci_lmi_timer_data_t { + struct net_device *dev; + int dlci_num; +} dlci_lmi_timer_data_t; + +void xpds_dlci_install_lmi_timer (int i, struct net_device *dev); +void xpds_dlci_remove_lmi_timer (int i, struct net_device *dev); + + +#endif diff --git a/drivers/net/xpds/xpds-fsm.c b/drivers/net/xpds/xpds-fsm.c new file mode 100644 index 000000000000..791ad166ab37 --- /dev/null +++ b/drivers/net/xpds/xpds-fsm.c @@ -0,0 +1,790 @@ +/* + * Copyright 1998, 1999, 2000 Xpeed, Inc. + * xpds-fsm.c, $Revision: 1.2 $ + * License to copy and distribute is GNU General Public License, version 2. + */ +#ifdef DEBUG +#define dprintk if (xpds_debug_level & DEBUG_FSM) printk +#else +#define dprintk if (0) printk +#endif + +#ifndef __KERNEL__ +#define __KERNEL__ 1 +#endif +#ifndef MODULE +#define MODULE 1 +#endif + +#define __NO_VERSION__ 1 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#if ! defined (CONFIG_PCI) +#error "CONFIG_PCI is not defined" +#endif + +#include "xpds-reg.h" +#include "xpds-softnet.h" +#include "xpds.h" + +/* + * Time periods in milliseconds. + */ +#define JIFFIES_COUNT(t) ((t)*HZ/1000?(t)*HZ/1000:1) + +#define T1 JIFFIES_COUNT(15000) +#define T2 JIFFIES_COUNT(3) +#define T3 JIFFIES_COUNT(40) +#define T4 JIFFIES_COUNT(6000) +#define T5 JIFFIES_COUNT(1000) +#define T6 JIFFIES_COUNT(6000) +#define T7 JIFFIES_COUNT(40) +#define T8 JIFFIES_COUNT(24) +#define T9 JIFFIES_COUNT(40) +#define T10 JIFFIES_COUNT(40) +#define T11 JIFFIES_COUNT(9) +#define T12 JIFFIES_COUNT(5500) +#define T13 JIFFIES_COUNT(15000) +#define T14 JIFFIES_COUNT(1/2) + +static unsigned long timer_limits[15] = { + 0, T1, T2, T3, T4, T5, T6, T7, + T8, T9, T10, T11, T12, T13, T14 +}; + +static unsigned long timers[15]; + +#define MODE_LT 0 +#define MODE_NT 1 + +/* + * States + */ +#define FSM_LT_RECEIVE_RESET 0 +#define FSM_LT_AWAKE_ERROR 1 +#define FSM_LT_DEACTIVATED 2 +#define FSM_LT_RESET_FOR_LOOP 3 +#define FSM_LT_ALERTING 4 +#define FSM_LT_WAIT_FOR_TN 5 +#define FSM_LT_AWAKE 6 +#define FSM_LT_EC_TRAINING 7 +#define FSM_LT_EC_CONVERGED 8 +#define FSM_LT_EQ_TRAINING 9 +#define FSM_LT_LINE_ACTIVE 10 +#define FSM_LT_PENDING_TRANSPARENT 11 +#define FSM_LT_TRANSPARENT 12 +#define FSM_LT_ST_DEACTIVATED 13 +#define FSM_LT_PENDING_DEACTIVATION 14 +#define FSM_LT_LOSS_OF_SYNC 15 +#define FSM_LT_LOSS_OF_SIGNAL 16 +#define FSM_LT_TEAR_DOWN_ERROR 17 +#define FSM_LT_TEST 18 +#define FSM_LT_TEAR_DOWN 19 + +#define FSM_NT_RECEIVE_RESET 0 +#define FSM_NT_PENDING_TIMING 1 +#define FSM_NT_DEACTIVATED 2 +#define FSM_NT_IOM_AWAKED 3 +#define FSM_NT_ALERTING 4 +#define FSM_NT_EC_TRAINING 5 +#define FSM_NT_EQ_TRAINING 6 +#define FSM_NT_WAIT_FOR_SF 7 +#define FSM_NT_SYNCHRONIZED_1 8 +#define FSM_NT_SYNCHRONIZED_2 9 +#define FSM_NT_WAIT_FOR_ACT 10 +#define FSM_NT_TRANSPARENT 11 +#define FSM_NT_ALERTING_1 12 +#define FSM_NT_EC_TRAINING_1 13 +#define FSM_NT_PEND_DEACT_ST 14 +#define FSM_NT_ERROR_ST 15 +#define FSM_NT_PEND_RECEIVE_RES 16 +#define FSM_NT_PEND_DEACT_U 17 +#define FSM_NT_TEST 18 +#define FSM_NT_EC_TRAINING_AL 19 +#define FSM_NT_WAIT_FOR_SF_AL 20 +#define FSM_NT_ANALOG_LOOP_BACK 21 + +static int lt_state = FSM_LT_TEST; +static int nt_state = FSM_NT_RECEIVE_RESET; + +/* + * Commands (written to TXCI in MCR) + */ +#define CMD_NULL (~0) + +#define CMD_LT_DR 0x00 +#define CMD_LT_RES 0x01 +#define CMD_LT_LTD 0x03 +#define CMD_LT_RES1 0x04 +#define CMD_LT_SSP 0x05 +#define CMD_LT_DT 0x06 +#define CMD_LT_UAR 0x07 +#define CMD_LT_AR 0x08 +#define CMD_LT_ARL 0x0a +#define CMD_LT_AR0 0x0d +#define CMD_LT_DC 0x0f + +#define CMD_NT_TIMING 0x00 +#define CMD_NT_RESET 0x01 +#define CMD_NT_DU 0x03 +#define CMD_NT_EI1 0x04 +#define CMD_NT_SSP 0x05 +#define CMD_NT_DT 0x06 +#define CMD_NT_AR 0x08 +#define CMD_NT_ARL 0x0a +#define CMD_NT_AI 0x0c +#define CMD_NT_DI 0x0f + +#define TXCI_VAL(cmd,channels) (((cmd) & XPDS_MCR_TXCI__MASK) | \ + (channels) | \ + XPDS_MCR_TXCI__PMD_ENABLE | \ + XPDS_MCR_TXCI__PMD_RESQ) +/* + * Indications (read from RXCI in MCR) + */ +#define IND_LT_NULL (~0) +#define IND_LT_DEAC 0x01 +#define IND_LT_FJ 0x02 +#define IND_LT_HI 0x03 +#define IND_LT_RSY 0x04 +#define IND_LT_EI2 0x05 +#define IND_LT_INT 0x06 +#define IND_LT_UAI 0x07 +#define IND_LT_AR 0x08 +#define IND_LT_ARM 0x09 +#define IND_LT_EI3 0x0b +#define IND_LT_AI 0x0c +#define IND_LT_LSL 0x0d +#define IND_LT_DI 0x0f + +#define IND_NT_NULL (~0) +#define IND_NT_DR 0x00 +#define IND_NT_FJ 0x02 +#define IND_NT_EI1 0x04 +#define IND_NT_INT 0x06 +#define IND_NT_PU 0x07 +#define IND_NT_AR 0x08 +#define IND_NT_ARL 0x0a +#define IND_NT_AI 0x0c +#define IND_NT_AIL 0x0e +#define IND_NT_DC 0x0f + +typedef struct { + int state; + u32 rxci; + int timer_start_1; + int timer_start_2; + u32 txci; + int timer_end; + int next_state; +} state_t; + +static state_t lt_states[] = { + { FSM_LT_RECEIVE_RESET, IND_LT_LSL, -1, -1, CMD_NULL, -1, FSM_LT_AWAKE_ERROR }, + { FSM_LT_AWAKE_ERROR, IND_LT_AR, 9, -1, CMD_NULL, 9, FSM_LT_EC_TRAINING }, + { FSM_LT_TEST, IND_LT_DEAC, -1, -1, CMD_LT_DR, -1, FSM_LT_DEACTIVATED }, + { FSM_LT_DEACTIVATED, IND_LT_DI, -1, -1, CMD_NULL, -1, FSM_LT_ALERTING }, + { FSM_LT_RESET_FOR_LOOP, IND_LT_DI, 2, -1, CMD_NULL, 2, FSM_LT_ALERTING }, + { FSM_LT_ALERTING, IND_LT_DI, 1, 2, CMD_LT_AR, 2, FSM_LT_WAIT_FOR_TN }, + { FSM_LT_WAIT_FOR_TN, IND_LT_DI, -1, -1, CMD_NULL, -1, FSM_LT_AWAKE }, + { FSM_LT_AWAKE, IND_LT_AR, 1, -1, CMD_LT_AR, -1, FSM_LT_EC_TRAINING }, + { FSM_LT_EC_TRAINING, IND_LT_AR, 5, -1, CMD_NULL, 5, FSM_LT_EC_CONVERGED }, + { FSM_LT_EC_CONVERGED, IND_LT_ARM, 6, -1, CMD_LT_ARL, 6, FSM_LT_EQ_TRAINING }, + { FSM_LT_EQ_TRAINING, IND_LT_ARM, -1, -1, CMD_NULL, -1, FSM_LT_LINE_ACTIVE }, + { FSM_LT_LINE_ACTIVE, IND_LT_UAI | IND_LT_FJ, -1, -1, CMD_LT_AR0, -1, FSM_LT_PENDING_TRANSPARENT }, + { FSM_LT_PENDING_TRANSPARENT, IND_LT_UAI | IND_LT_FJ, 8, -1, CMD_NULL, 8, FSM_LT_TRANSPARENT }, + { FSM_LT_ST_DEACTIVATED, IND_LT_AR | IND_LT_UAI, -1, -1, CMD_LT_AR0, -1, FSM_LT_LINE_ACTIVE }, + { FSM_LT_LOSS_OF_SIGNAL, IND_LT_LSL, -1, -1, CMD_LT_RES1, -1, FSM_LT_RECEIVE_RESET }, + { FSM_LT_LOSS_OF_SYNC, IND_LT_RSY, -1, -1, CMD_LT_RES1, -1, FSM_LT_TEAR_DOWN_ERROR }, + { FSM_LT_TEAR_DOWN_ERROR, IND_LT_EI3 | IND_LT_RSY, -1, -1, CMD_NULL, -1, FSM_LT_RECEIVE_RESET }, + { FSM_LT_PENDING_DEACTIVATION, IND_LT_DEAC, 10, -1, CMD_NULL, 10, FSM_LT_TEAR_DOWN }, + { FSM_LT_TEAR_DOWN, IND_LT_DEAC, -1, -1, CMD_NULL, -1, FSM_LT_DEACTIVATED }, +}; + +static state_t nt_states[] = { + { FSM_NT_RECEIVE_RESET, IND_NT_DR, 7, -1, CMD_NT_DI, 7, FSM_NT_PENDING_TIMING }, + { FSM_NT_TEST, IND_NT_DR, -1, -1, CMD_NT_DI, -1, FSM_NT_PENDING_TIMING }, + { FSM_NT_PENDING_TIMING, IND_NT_DC, 14, -1, CMD_NULL, 14, FSM_NT_DEACTIVATED }, + { FSM_NT_DEACTIVATED, IND_NT_DC, -1, -1, CMD_NT_TIMING, -1, FSM_NT_IOM_AWAKED }, + { FSM_NT_IOM_AWAKED, IND_NT_PU, -1, -1, CMD_NULL, -1, FSM_NT_ALERTING }, + { FSM_NT_ALERTING, IND_NT_DC, 1, 11, CMD_NT_AR, 11, FSM_NT_EC_TRAINING }, + { FSM_NT_EC_TRAINING, IND_NT_DC, 12, -1, CMD_NULL, 12, FSM_NT_SYNCHRONIZED_1 }, + { FSM_NT_EQ_TRAINING, IND_NT_DC, -1, -1, CMD_NULL, -1, FSM_NT_WAIT_FOR_SF }, + { FSM_NT_EQ_TRAINING, IND_NT_DC, -1, -1, CMD_NULL, 1, FSM_NT_PEND_RECEIVE_RES }, + { FSM_NT_WAIT_FOR_SF, IND_NT_DC, -1, -1, CMD_NULL, -1, FSM_NT_PEND_RECEIVE_RES }, + { FSM_NT_SYNCHRONIZED_1, IND_NT_AR, -1, -1, CMD_NULL, -1, FSM_NT_SYNCHRONIZED_2 }, + { FSM_NT_SYNCHRONIZED_2, IND_NT_AR, -1, -1, CMD_NT_AI, -1, FSM_NT_WAIT_FOR_ACT }, + { FSM_NT_WAIT_FOR_ACT, IND_NT_AR, -1, -1, CMD_NULL, -1, FSM_NT_TRANSPARENT }, + { FSM_NT_TRANSPARENT, IND_NT_AI, -1, -1, CMD_NULL, -1, FSM_NT_ERROR_ST }, + { FSM_NT_ERROR_ST, IND_NT_AR, -1, -1, CMD_NULL, -1, FSM_NT_PEND_RECEIVE_RES }, + { FSM_NT_PEND_RECEIVE_RES, IND_NT_EI1, 13, -1, CMD_NULL, 13, FSM_NT_RECEIVE_RESET }, +}; + +#define LT_NUM_STATES (sizeof (lt_states) / sizeof (*lt_states)) +#define NT_NUM_STATES (sizeof (nt_states) / sizeof (*nt_states)) + +static int +get_state_index (int st, int ltnt) +{ + int i; + + switch (ltnt) { + case MODE_LT: for (i = 0; i < LT_NUM_STATES; i ++) { + if (lt_states[i].state == st) return i; + } + break; + case MODE_NT: for (i = 0; i < NT_NUM_STATES; i ++) { + if (nt_states[i].state == st) return i; + } + break; + } + dprintk (KERN_ERR "Unknown state %d\n", st); + return 0; +} + +static void +set_timers (int state_index, int ltnt) +{ + switch (ltnt) { + case MODE_LT: if (lt_states[state_index].timer_start_1 >= 0) { + timers[lt_states[state_index].timer_start_1] = jiffies; + } + if (lt_states[state_index].timer_start_2 >= 0) { + timers[lt_states[state_index].timer_start_2] = jiffies; + } + break; + case MODE_NT: if (nt_states[state_index].timer_start_1 >= 0) { + timers[nt_states[state_index].timer_start_1] = jiffies; + } + if (nt_states[state_index].timer_start_2 >= 0) { + timers[nt_states[state_index].timer_start_2] = jiffies; + } + break; + } +} + +static void +write_txci (int txci, int channels, int card_num) +{ + u32 val; + + dprintk (KERN_DEBUG "write_txci (0x%x, %d)\n", txci, card_num); + xpds_read_control_register_quiet (card_num, XPDS_MCR_TXCI, + &val, XPDS_MAIN); + + val &= ~0xf; + val |= TXCI_VAL (txci, channels); + + xpds_write_control_register_quiet (card_num, XPDS_MCR_TXCI, + val, XPDS_MAIN); +} + +static void +set_txci (int state_index, int channels, int card_num, int ltnt) +{ + switch (ltnt) { + case MODE_LT: if (lt_states[state_index].txci != CMD_NULL) { + write_txci (lt_states[state_index].txci, channels, card_num); + } + break; + case MODE_NT: if (nt_states[state_index].txci != CMD_NULL) { + write_txci (nt_states[state_index].txci, channels, card_num); + } + break; + } +} + +/* lt */ +static int +check_state_lt (int state_index, int channels, int card_num) +{ + int i; + u32 val; + + if (xpds_data[card_num].rxci_interrupt_received) { + /* lock against interrupts? */ + xpds_data[card_num].rxci_interrupt_received = 0; + xpds_read_control_register_quiet (card_num, XPDS_MCR_RXCI, + &val, XPDS_MAIN); + val &= XPDS_MCR_RXCI__MASK; + /* unlock? */ + } else { + val = IND_LT_NULL; + } + + if (val != lt_states[state_index].rxci && val != IND_LT_NULL) { + dprintk (KERN_DEBUG "%s RXCI = 0x%01x, expected 0x%01x\n", + xpds_devs[card_num].name, val, lt_states[state_index].rxci); + } + + if (lt_state == FSM_LT_AWAKE) { + switch (val) { + case IND_LT_UAI: + dprintk (KERN_DEBUG "AWAKE/UAI -> PENDING_TRANSPARENT\n"); + return FSM_LT_PENDING_TRANSPARENT; + case IND_LT_ARM: + dprintk (KERN_DEBUG "AWAKE/ARM -> EQ_TRAINING\n"); + return FSM_LT_EQ_TRAINING; + case IND_LT_DI: + dprintk (KERN_DEBUG "AWAKE/DI -> TEST/RES\n"); + write_txci (CMD_LT_RES, channels, card_num); + return FSM_LT_TEST; + case IND_LT_EI3: + dprintk (KERN_DEBUG "AWAKE/EI3 -> TEST/RES\n"); + write_txci (CMD_LT_RES, channels, card_num); + return FSM_LT_TEST; + default: + write_txci (CMD_LT_AR, channels, card_num); + return FSM_LT_AWAKE; + } + } else if (lt_state == FSM_LT_DEACTIVATED) { + switch (val) { + case IND_LT_AR: + dprintk (KERN_DEBUG "DEACTIVATED/AR -> AWAKE/AR\n"); + write_txci (CMD_LT_AR, channels, card_num); + return FSM_LT_AWAKE; + case IND_LT_ARM: + dprintk (KERN_DEBUG "DEACTIVATED/ARM -> EQ_TRAINING\n"); + return FSM_LT_EQ_TRAINING; + case IND_LT_EI3: + dprintk (KERN_DEBUG "DEACTIVATED/EI3 -> TEST/RES\n"); + write_txci (CMD_LT_RES, channels, card_num); + return FSM_LT_TEST; + case IND_LT_DI: + dprintk (KERN_DEBUG "DEACTIVATED/DI -> AWAKE/AR\n"); + write_txci (CMD_LT_AR, channels, card_num); + return FSM_LT_AWAKE; + default: + dprintk (KERN_DEBUG "DEACTIVATED/* -> AWAKE/AR\n"); + write_txci (CMD_LT_AR, channels, card_num); + return FSM_LT_AWAKE; + } + } else if (lt_state == FSM_LT_EC_TRAINING) { + switch (val) { + case IND_LT_UAI: + dprintk (KERN_DEBUG "EC_TRAINING/UAI -> PENDING_TRANSPARENT\n"); + return FSM_LT_PENDING_TRANSPARENT; + case IND_LT_AI: + dprintk (KERN_DEBUG "EC_TRAINING/AI -> TRANSPARENT\n"); + return FSM_LT_TRANSPARENT; + default: + break; + } + } else if (lt_state == FSM_LT_EC_CONVERGED) { + switch (val) { + case IND_LT_UAI: + dprintk (KERN_DEBUG "EC_CONVERGED/UAI -> PENDING_TRANSPARENT\n"); + return FSM_LT_PENDING_TRANSPARENT; + case IND_LT_AI: + dprintk (KERN_DEBUG "EC_CONVERGED/AI -> TRANSPARENT\n"); + return FSM_LT_TRANSPARENT; + default: + break; + } + } else if (lt_state == FSM_LT_EQ_TRAINING) { + switch (val) { + case IND_LT_UAI: + dprintk (KERN_DEBUG "EQ_TRAINING/UAI -> PENDING_TRANSPARENT\n"); + return FSM_LT_PENDING_TRANSPARENT; + case IND_LT_AI: + dprintk (KERN_DEBUG "EQ_TRAINING/AI -> TRANSPARENT\n"); + return FSM_LT_TRANSPARENT; + case IND_LT_EI3: + dprintk (KERN_DEBUG "EQ_TRAINING/EI3 -> TEST/RES\n"); + write_txci (CMD_LT_RES, channels, card_num); + return FSM_LT_TEST; + case IND_LT_DI: + dprintk (KERN_DEBUG "EQ_TRAINING/DI -> AWAKE/AR\n"); + write_txci (CMD_LT_AR, channels, card_num); + return FSM_LT_AWAKE; + default: + if (val != IND_LT_NULL) dprintk (KERN_DEBUG "EQ_TRAINING/0x%01x\n", val); + return FSM_LT_EQ_TRAINING; + } + } else if (lt_state == FSM_LT_PENDING_TRANSPARENT) { + switch (val) { + case IND_LT_RSY: + dprintk (KERN_DEBUG "PENDING_TRANSPARENT/RSY -> WAIT_FOR_TN/RES1\n"); + write_txci (CMD_LT_RES1, channels, card_num); + return FSM_LT_WAIT_FOR_TN; + case IND_LT_AI: + dprintk (KERN_DEBUG "PENDING_TRANSPARENT/AI -> TRANSPARENT\n"); + return FSM_LT_TRANSPARENT; + case IND_LT_LSL: + dprintk (KERN_DEBUG "PENDING_TRANSPARENT/LSL -> RECEIVE_RESET/RES1\n"); + write_txci (CMD_LT_RES1, channels, card_num); + return FSM_LT_RECEIVE_RESET; + default: + return FSM_LT_PENDING_TRANSPARENT; + } + } else if (lt_state == FSM_LT_RECEIVE_RESET) { + switch (val) { + case IND_LT_AR: + dprintk (KERN_DEBUG "RECEIVE_RESET/AR -> AWAKE\n"); + return FSM_LT_AWAKE; + case IND_LT_DI: + dprintk (KERN_DEBUG "RECEIVE_RESET/DI -> DEACTIVATED/AR\n"); + write_txci (CMD_LT_AR, channels, card_num); + return FSM_LT_DEACTIVATED; + default: + break; + } + } else if (lt_state == FSM_LT_TEAR_DOWN_ERROR) { + switch (val) { + case IND_LT_LSL: + dprintk (KERN_DEBUG "TEAR_DOWN_ERROR/LSL -> RECEIVE_RESET\n"); + return FSM_LT_RECEIVE_RESET; + default: + break; + } + } else if (lt_state == FSM_LT_TEST) { + switch (val) { + case IND_LT_DEAC: + dprintk (KERN_DEBUG "TEST/DEAC -> DEACTIVATED/DR\n"); + write_txci (CMD_LT_DR, channels, card_num); + return FSM_LT_DEACTIVATED; + case IND_LT_DI: + dprintk (KERN_DEBUG "TEST/DI -> DEACTIVATED/DC\n"); + write_txci (CMD_LT_DC, channels, card_num); + return FSM_LT_DEACTIVATED; + default: + break; + } + } else if (lt_state == FSM_LT_TRANSPARENT) { + switch (val) { + case IND_LT_RSY: + dprintk (KERN_DEBUG "TRANSPARENT/RSY -> TEAR_DOWN_ERROR/RES1\n"); + write_txci (CMD_LT_RES1, channels, card_num); + return FSM_LT_TEAR_DOWN_ERROR; + case IND_LT_LSL: + dprintk (KERN_DEBUG "TRANSPARENT/LSL -> RECEIVE_RESET\n"); + return FSM_LT_RECEIVE_RESET; + default: + break; + } + } else if (lt_state == FSM_LT_WAIT_FOR_TN) { + switch (val) { + case IND_LT_AR: + dprintk (KERN_DEBUG "WAIT_FOR_TN/AR -> AWAKE/AR\n"); + write_txci (CMD_LT_AR, channels, card_num); + return FSM_LT_AWAKE; + case IND_LT_EI3: + dprintk (KERN_DEBUG "WAIT_FOR_TN/EI3 -> TEST/RES\n"); + write_txci (CMD_LT_RES, channels, card_num); + return FSM_LT_TEST; + case IND_LT_DI: + dprintk (KERN_DEBUG "WAIT_FOR_TN/DI -> TEST/RES\n"); + write_txci (CMD_LT_RES, channels, card_num); + return FSM_LT_TEST; + default: + break; + } + } + + for (i = state_index; + lt_states[i].state == lt_states[state_index].state; i ++) { + int timer_done = 0; + + if (lt_states[i].timer_end < 0) { + timer_done = 1; + } else if (jiffies - timers[lt_states[i].timer_end] >= + timer_limits[lt_states[i].timer_end]) { + dprintk (KERN_DEBUG "%s timer %d expired\n", + xpds_devs[card_num].name, lt_states[i].timer_end); + timer_done = 1; + } + + if (timer_done) return lt_states[i].next_state; + } + return lt_states[state_index].state; +} + +/* nt */ +static int +check_state_nt (int state_index, int channels, int card_num) +{ + int i; + u32 val; + + if (xpds_data[card_num].rxci_interrupt_received) { + /* lock ? */ + xpds_data[card_num].rxci_interrupt_received = 0; + xpds_read_control_register_quiet (card_num, XPDS_MCR_RXCI, + &val, XPDS_MAIN); + val &= XPDS_MCR_RXCI__MASK; + /* unlock ? */ + } else { + val = IND_NT_NULL; + } + + if (val != nt_states[state_index].rxci && val != IND_NT_NULL) { + dprintk (KERN_DEBUG "%s RXCI = 0x%01x, expected 0x%01x\n", + xpds_devs[card_num].name, val, nt_states[state_index].rxci); + } + + if (nt_state == FSM_NT_DEACTIVATED) { + switch (val) { + case IND_NT_AR: + dprintk (KERN_DEBUG "DEACTIVATED/AR -> SYNCHRONIZED_2/AI\n"); + write_txci (CMD_NT_AI, channels, card_num); + return FSM_NT_SYNCHRONIZED_2; + case IND_NT_PU: + dprintk (KERN_DEBUG "DEACTIVATED/PU -> IOM_AWAKED/TIMING\n"); + write_txci (CMD_NT_TIMING, channels, card_num); + return FSM_NT_IOM_AWAKED; + default: + return FSM_NT_DEACTIVATED; + } + } else if (nt_state == FSM_NT_IOM_AWAKED) { + switch (val) { + case IND_NT_DC: + dprintk (KERN_DEBUG "IOM_AWAKED/DC -> ALERTING/AR\n"); + write_txci (CMD_NT_AR, channels, card_num); + return FSM_NT_ALERTING; + default: + dprintk (KERN_DEBUG "IOM_AWAKED/* -> ALERTING/AR\n"); + write_txci (CMD_NT_AR, channels, card_num); + return FSM_NT_IOM_AWAKED; + } + } else if (nt_state == FSM_NT_ALERTING) { + switch (val) { + case IND_NT_AR: + dprintk (KERN_DEBUG "ALERTING/AR -> SYNCHRONIZED_2/AI\n"); + write_txci (CMD_NT_AI, channels, card_num); + return FSM_NT_SYNCHRONIZED_2; + default: + return FSM_NT_ALERTING; + } + } else if (nt_state == FSM_NT_EC_TRAINING) { + if (val == IND_NT_AR) { + dprintk (KERN_DEBUG "EC_TRAINING/AR -> WAIT_FOR_ACT/AI\n"); + write_txci (CMD_NT_AI, channels, card_num); + return FSM_NT_WAIT_FOR_ACT; + } + } else if (nt_state == FSM_NT_SYNCHRONIZED_1) { + if (val == IND_NT_AR) { + dprintk (KERN_DEBUG "SYNCHRONIZED_1/AR -> SYNCHRONIZED_2/AI\n"); + write_txci (CMD_NT_AI, channels, card_num); + return FSM_NT_SYNCHRONIZED_2; + } + } else if (nt_state == FSM_NT_SYNCHRONIZED_2) { + switch (val) { + case IND_NT_AR: + dprintk (KERN_DEBUG "SYNCHRONIZED_2/AR -> WAIT_FOR_ACT/AI\n"); + write_txci (CMD_NT_AI, channels, card_num); + return FSM_NT_WAIT_FOR_ACT; + case IND_NT_AI: + dprintk (KERN_DEBUG "SYNCHRONIZED_2/AI -> TRANSPARENT\n"); + return FSM_NT_TRANSPARENT; + case IND_NT_DR: + dprintk (KERN_DEBUG "SYNCHRONIZED_2/DR -> PEND_DEACT_ST/DI\n"); + write_txci (CMD_NT_DI, channels, card_num); + return FSM_NT_PEND_DEACT_ST; + default: + if (val != IND_NT_NULL) dprintk (KERN_DEBUG "SYNCHRONIZED_2/0x%01x\n", val); + return FSM_NT_SYNCHRONIZED_2; + } + } else if (nt_state == FSM_NT_WAIT_FOR_ACT) { + switch (val) { + case IND_NT_AI: + dprintk (KERN_DEBUG "WAIT_FOR_ACT/AI -> TRANSPARENT\n"); + return FSM_NT_TRANSPARENT; + case IND_NT_DR: + write_txci (CMD_NT_DI, channels, card_num); + dprintk (KERN_DEBUG "WAIT_FOR_ACT/DI -> PEND_DEACT_ST\n"); + return FSM_NT_PEND_DEACT_ST; + case IND_NT_AR: + dprintk (KERN_DEBUG "WAIT_FOR_ACT/AR -> WAIT_FOR_ACT/AI\n"); + write_txci (CMD_NT_AI, channels, card_num); + return FSM_NT_WAIT_FOR_ACT; + default: + /* write_txci (CMD_NT_AI, channels, card_num); */ + return FSM_NT_WAIT_FOR_ACT; + } + } else if (nt_state == FSM_NT_TRANSPARENT) { + switch (val) { + case IND_NT_AR: + dprintk (KERN_DEBUG "TRANSPARENT/AR -> ERROR_ST\n"); + return FSM_NT_ERROR_ST; + case IND_NT_DR: + write_txci (CMD_NT_DI, channels, card_num); + dprintk (KERN_DEBUG "TRANSPARENT/DR -> PEND_DEACT_ST\n"); + return FSM_NT_PEND_DEACT_ST; + default: + return FSM_NT_TRANSPARENT; + } + } + + if (val == IND_NT_AR) { + dprintk (KERN_DEBUG "*/AR -> SYNCHRONIZED_2/AI\n"); + write_txci (CMD_NT_AI, channels, card_num); + return FSM_NT_SYNCHRONIZED_2; + } + + for (i = state_index; + nt_states[i].state == nt_states[state_index].state; i ++) { + int timer_done = 0; + + if (nt_states[i].timer_end < 0) { + timer_done = 1; + } else if (jiffies - timers[nt_states[i].timer_end] >= + timer_limits[nt_states[i].timer_end]) { + dprintk (KERN_DEBUG "%s timer %d expired\n", + xpds_devs[card_num].name, nt_states[i].timer_end); + timer_done = 1; + } + + if (timer_done) return nt_states[i].next_state; + } + return nt_states[state_index].state; +} + +/* lt */ +int +xpds_fsm_lt (int card_num, int channels, int guard_time) +{ + u32 guard; + + dprintk (KERN_DEBUG "xpds_fsm_lt (%d) (LT - line termination)\n", card_num); + + guard = jiffies + guard_time * HZ; + + /* + * Reset + */ + xpds_data[card_num].rxci_interrupt_received = 0; + xpds_write_control_register (card_num, XPDS_MCR_TXCI, + TXCI_VAL (CMD_LT_RES, channels), XPDS_MAIN); + /* + * Unreset + */ + xpds_write_control_register (card_num, XPDS_MCR_TXCI, + TXCI_VAL (CMD_LT_DR, channels), XPDS_MAIN); + + lt_state = FSM_LT_TEST; + + dprintk (KERN_DEBUG "waiting for RXCI interrupt\n"); + while (! xpds_data[card_num].rxci_interrupt_received && + jiffies < guard) { + schedule (); + } + + guard = jiffies + guard_time * HZ; + + /* + * For each state set the timers, set the TXCI value, + * then loop, checking for transition. We want to + * get into the transparent state. + */ + while (lt_state != FSM_LT_TRANSPARENT) { + int state_index, new_state; + + dprintk (KERN_DEBUG "%s state %d\n", xpds_devs[card_num].name, lt_state); + state_index = get_state_index (lt_state, MODE_LT); + set_timers (state_index, MODE_LT); + set_txci (state_index, channels, card_num, MODE_LT); + + do { + new_state = check_state_lt (state_index, channels, card_num); + if (guard_time > 0 && jiffies >= guard) { + dprintk (KERN_ERR "%s FSM guard timer (%d seconds) expired in state %d\n", xpds_devs[card_num].name, guard_time, lt_state); + return -ETIME; + } + schedule (); + } while (new_state == lt_state); + + lt_state = new_state; + guard = jiffies + guard_time * HZ; + } + + dprintk (KERN_DEBUG "state %d == TRANSPARENT\n", lt_state); + + return 0; +} + +/* nt */ +int +xpds_fsm_nt (int card_num, int channels, int guard_time) +{ + u32 guard; + + dprintk (KERN_DEBUG "xpds_fsm_nt (%d) (NT - network termination)\n", card_num); + + guard = jiffies + guard_time * HZ; + + /* + * Reset + */ + xpds_data[card_num].rxci_interrupt_received = 0; + xpds_write_control_register (card_num, XPDS_MCR_TXCI, + TXCI_VAL (CMD_NT_RESET, channels), XPDS_MAIN); + /* + * Unreset + */ + xpds_write_control_register (card_num, XPDS_MCR_TXCI, + TXCI_VAL (CMD_NT_DI, channels), XPDS_MAIN); + + nt_state = FSM_NT_TEST; + + dprintk (KERN_DEBUG "waiting for RXCI interrupt\n"); + while ( ! xpds_data[card_num].rxci_interrupt_received && + jiffies < guard) { + schedule (); + } + + guard = jiffies + guard_time * HZ; + + /* + * For each state set the timers, set the TXCI value, + * then loop, checking for transition. We want to + * get into the transparent state. + */ + while (nt_state != FSM_NT_TRANSPARENT) { + int state_index, new_state; + + dprintk (KERN_DEBUG "%s state %d\n", xpds_devs[card_num].name, nt_state); + state_index = get_state_index (nt_state, MODE_NT); + set_timers (state_index, MODE_NT); + set_txci (state_index, channels, card_num, MODE_NT); + + do { + new_state = check_state_nt (state_index, channels, card_num); + if (guard_time > 0 && jiffies >= guard) { + dprintk (KERN_ERR "%s FSM guard timer (%d seconds) expired in state %d\n", xpds_devs[card_num].name, guard_time, nt_state); + return -ETIME; + } + schedule (); + } while (new_state == nt_state); + + nt_state = new_state; + guard = jiffies + guard_time * HZ; + } + + dprintk (KERN_DEBUG "state %d == TRANSPARENT\n", nt_state); + + return 0; +} diff --git a/drivers/net/xpds/xpds-fsm.h b/drivers/net/xpds/xpds-fsm.h new file mode 100644 index 000000000000..f29a7e72a882 --- /dev/null +++ b/drivers/net/xpds/xpds-fsm.h @@ -0,0 +1,12 @@ +/* + * Copyright 1998, 1999, 2000 Xpeed, Inc. + * xpds-fsm.h, $Revision: 1.2 $ + * License to copy and distribute is GNU General Public License, version 2. + */ +#ifndef XPDS_FSM_H +#define XPDS_FSM_H 1 + +int xpds_fsm_nt (int card_num, int channels, int guard_time); +int xpds_fsm_lt (int card_num, int channels, int guard_time); + +#endif diff --git a/drivers/net/xpds/xpds-reg.h b/drivers/net/xpds/xpds-reg.h new file mode 100644 index 000000000000..f80e153b371a --- /dev/null +++ b/drivers/net/xpds/xpds-reg.h @@ -0,0 +1,220 @@ +/* + * Copyright 1998, 1999, 2000 Xpeed, Inc. + * xpds-reg.h, $Revision: 1.2 $ + * License to copy and distribute is GNU General Public License, version 2. + */ +#ifndef XPDS_REG_H +#define XPDS_REG_H 1 + +/* + * Main control registers + */ +#define XPDS_MCR_CONFIG 0x00 +#define XPDS_MCR_TEST 0x04 +#define XPDS_MCR_MASK_SET 0x08 +#define XPDS_MCR_MASK_CLR 0x0c +#define XPDS_MCR_INT_SET 0x10 +#define XPDS_MCR_INT_CLR 0x14 +#define XPDS_MCR_GPIO_SET 0x18 +#define XPDS_MCR_GPIO_CLR 0x1c +#define XPDS_MCR_TXCFG 0x20 +#define XPDS_MCR_RXCFG 0x24 +#define XPDS_MCR_PACKH 0x28 +#define XPDS_MCR_PACKL 0x2c +#define XPDS_MCR_TXCI 0x30 +#define XPDS_MCR_RXCI 0x34 +#define XPDS_MCR_DBG_ADDR 0x38 +#define XPDS_MCR_DBG 0x3c + +#define XPDS_MCR_INT_MASK_SET XPDS_MCR_MASK_SET +#define XPDS_MCR_INT_MASK_CLR XPDS_MCR_MASK_CLR +#define XPDS_MCR_INT_STATUS_SET XPDS_MCR_INT_SET +#define XPDS_MCR_INT_STATUS_CLR XPDS_MCR_INT_CLR +#define XPDS_MCR_TX_CONFIG XPDS_MCR_TXCFG +#define XPDS_MCR_RX_CONFIG XPDS_MCR_RXCFG + +#define XPDS_MCR_CONFIG__REVISION_MASK 0xe0 +#define XPDS_MCR_CONFIG__MODE_NORMAL 0x00 +#define XPDS_MCR_CONFIG__MODE_LOOPBACK 0x18 +#define XPDS_MCR_CONFIG__CONFIG_MASK 0x07 + +#define XPDS_MCR_TEST__BIT_CLK_EN 0x10 +#define XPDS_MCR_TEST__CLK_DIV_4 0x00 +#define XPDS_MCR_TEST__CLK_DIV_8 0x02 +#define XPDS_MCR_TEST__CLK_DIV_16 0x04 +#define XPDS_MCR_TEST__CLK_DIV_32 0x06 +#define XPDS_MCR_TEST__CLK_DIV_64 0x08 +#define XPDS_MCR_TEST__SEL_PMD_CLK 0x01 + +#define XPDS_MCR_MASK__RX_FIFO 0x01 +#define XPDS_MCR_MASK__TX_FIFO 0x02 +#define XPDS_MCR_MASK__RX_DMA 0x04 +#define XPDS_MCR_MASK__TX_DMA 0x08 +#define XPDS_MCR_MASK__PCI_ERROR 0x10 +#define XPDS_MCR_MASK__EXT 0x20 +#define XPDS_MCR_MASK__RXCI 0x40 + +#define XPDS_MCR_INT__RX_FIFO 0x01 +#define XPDS_MCR_INT__TX_FIFO 0x02 +#define XPDS_MCR_INT__RX_DMA 0x04 +#define XPDS_MCR_INT__TX_DMA 0x08 +#define XPDS_MCR_INT__PCI_ERROR 0x10 +#define XPDS_MCR_INT__EXT 0x20 +#define XPDS_MCR_INT__RXCI 0x40 +#define XPDS_MCR_INT__INT 0x80 + +#define XPDS_MCR_GPIO__GP_MASK 0x0f +#define XPDS_MCR_GPIO__GP_SEPROM_CS 0x01 +#define XPDS_MCR_GPIO__GP_SEPROM_SK 0x02 +#define XPDS_MCR_GPIO__GP_SEPROM_DO 0x04 +#define XPDS_MCR_GPIO__GP_SEPROM_DI 0x08 +#define XPDS_MCR_GPIO__GP_FPGA_NT 0x00 +#define XPDS_MCR_GPIO__GP_FPGA_LT 0x01 +#define XPDS_MCR_GPIO__GP_ASIC_ACT_LED 0x02 +#define XPDS_MCR_GPIO__GP_ASIC_LINK_LED 0x08 +#define XPDS_MCR_GPIO__GP_SDSL_ACT_LED 0x08 +#define XPDS_MCR_GPIO__GP_FPGA_LT_NT 0x01 +#define XPDS_MCR_GPIO__GP_FPGA_TX_LED 0x02 +#define XPDS_MCR_GPIO__GP_FPGA_RX_LED 0x04 +#define XPDS_MCR_GPIO__OE_MASK 0xf0 +#define XPDS_MCR_GPIO__OE_SEPROM_CS 0x10 +#define XPDS_MCR_GPIO__OE_SEPROM_SK 0x20 +#define XPDS_MCR_GPIO__OE_SEPROM_DO 0x40 +#define XPDS_MCR_GPIO__OE_SEPROM_DI 0x80 +#define XPDS_MCR_GPIO__OE_ASIC_ACT_LED 0x20 +#define XPDS_MCR_GPIO__OE_ASIC_LINK_LED 0x80 +#define XPDS_MCR_GPIO__OE_SDSL_ACT_LED 0x80 +#define XPDS_MCR_GPIO__OE_FPGA_LT_NT 0x10 +#define XPDS_MCR_GPIO__OE_FPGA_TX_LED 0x20 +#define XPDS_MCR_GPIO__OE_FPGA_RX_LED 0x40 + +#define XPDS_MCR_TXCFG__TRANS 0x01 +#define XPDS_MCR_TXCFG__ENABLE 0x02 +#define XPDS_MCR_TXCFG__FILL 0x04 +#define XPDS_MCR_TXCFG__SKIP_CRC 0x08 +#define XPDS_MCR_TXCFG__CRC 0x10 +#define XPDS_MCR_TXCFG__BIT_REV_CRC 0x20 +#define XPDS_MCR_TXCFG__BIT_REV_DATA 0x40 +#define XPDS_MCR_TXCFG__SWAP_CRC 0x80 + +#define XPDS_MCR_RXCFG__TRANS 0x01 +#define XPDS_MCR_RXCFG__ENABLE 0x02 +#define XPDS_MCR_RXCFG__IGNORE_CRC 0x04 +#define XPDS_MCR_RXCFG__NO_CRC 0x08 +#define XPDS_MCR_RXCFG__CRC 0x10 +#define XPDS_MCR_RXCFG__BIT_REV_CRC 0x20 +#define XPDS_MCR_RXCFG__BIT_REV_DATA 0x40 +#define XPDS_MCR_RXCFG__SWAP_CRC 0x80 + +#define XPDS_MCR_TXCI__PMD_ENABLE 0x80 +#define XPDS_MCR_TXCI__PMD_CONFIG_MASK 0x60 +#define XPDS_MCR_TXCI__PMD_CONFIG_B1 0x00 +#define XPDS_MCR_TXCI__PMD_CONFIG_B2 0x20 +#define XPDS_MCR_TXCI__PMD_CONFIG_B1B2 0x40 +#define XPDS_MCR_TXCI__PMD_CONFIG_B1B2D 0x60 +#define XPDS_MCR_TXCI__PMD_RESQ 0x10 +#define XPDS_MCR_TXCI__MASK 0x0f + +#define XPDS_MCR_RXCI__PMD_ACTIVE 0x40 +#define XPDS_MCR_RXCI__MASK 0x0f + +/* + * FIFO control registers + */ +#define XPDS_FCR_CONFIG 0x00 +#define XPDS_FCR_INC 0x04 +#define XPDS_FCR_STAT 0x08 +#define XPDS_FCR_TAG 0x0c + +#define XPDS_FCR_STATUS XPDS_FCR_STAT + +#define XPDS_FCR_CONFIG__RESET 0x01 +#define XPDS_FCR_CONFIG__PTR_MASK 0x06 + +#define XPDS_FCR_INC__NEXT 0x01 + +#define XPDS_FCR_STAT__LAST 0x80 +#define XPDS_FCR_STAT__SIZE_MASK 0x1f + +#define XPDS_FCR_TAG__MASK 0x0f + +/* + * DMA + */ +#define XPDS_DMA_CONFIG 0x00 +#define XPDS_DMA_GO 0x04 +#define XPDS_DMA_DESC 0x08 +#define XPDS_DMA_SET_MASK 0x0c +#define XPDS_DMA_CLR_MASK 0x10 +#define XPDS_DMA_SET_STAT 0x14 +#define XPDS_DMA_CLR_STAT 0x18 +#define XPDS_DMA_STAT_ADDR 0x1c +#define XPDS_DMA_BUF_ADDR 0x20 +#define XPDS_DMA_DBG_ADDR 0x24 +#define XPDS_DMA_DBG 0x28 + +#define XPDS_DMA_MASK_SET XPDS_DMA_SET_MASK +#define XPDS_DMA_MASK_CLR XPDS_DMA_CLR_MASK +#define XPDS_DMA_STAT_SET XPDS_DMA_SET_STAT +#define XPDS_DMA_STAT_CLR XPDS_DMA_CLR_STAT + +#define XPDS_DMA_CONFIG__TEST_ENABLE 0x08 +#define XPDS_DMA_CONFIG__BURST_ENABLE 0x04 +#define XPDS_DMA_CONFIG__RESET 0x02 +#define XPDS_DMA_CONFIG__ENABLE 0x01 + +#define XPDS_DMA_GO__HUNT 0x01 + +#define XPDS_DMA_DESC__SW_GO 0x80000000 +#define XPDS_DMA_DESC__HW_GO 0x40000000 +#define XPDS_DMA_DESC__NEXT_VALID 0x20000000 +#define XPDS_DMA_DESC__PACKET_TAG_MASK 0x000f0000 +#define XPDS_DMA_DESC__PACKET_TAG_OK 0x00000000 +#define XPDS_DMA_DESC__PACKET_TAG_CRC 0x00010000 +#define XPDS_DMA_DESC__PACKET_TAG_BOUNDARY_VIOLATION 0x00020000 +#define XPDS_DMA_DESC__PACKET_TAG_LONG 0x00040000 +#define XPDS_DMA_DESC__PACKET_TAG_ABORT 0x00050000 +#define XPDS_DMA_DESC__PACKET_TAG_OVERFLOW 0x00080000 +#define XPDS_DMA_DESC__PACKET_LENGTH_MASK 0x0000ffff + +#define XPDS_DMA_MASK__FLOW 0x20 +#define XPDS_DMA_MASK__CRC 0x10 +#define XPDS_DMA_MASK__LONG 0x08 +#define XPDS_DMA_MASK__ABORT 0x04 +#define XPDS_DMA_MASK__DONE 0x01 + +#define XPDS_DMA_STAT__FLOW 0x20 +#define XPDS_DMA_STAT__CRC 0x10 +#define XPDS_DMA_STAT__LONG 0x08 +#define XPDS_DMA_STAT__ABORT 0x04 +#define XPDS_DMA_STAT__DONE 0x01 + +/* + * Auxiliary registers (8032 for SDSL) + */ +#define XPDS_AUX_CONTROL 0x00 +#define XPDS_AUX_WRITE_ADDR 0x04 +#define XPDS_AUX_WRITE_DATA 0x08 +#define XPDS_AUX_READ_DATA 0x04 +#define XPDS_AUX_READ_ADDR 0x08 +#define XPDS_AUX_MAILBOX_DATA 0x0c + +#define XPDS_AUX_MAILBOX__HANDSHAKE 0x80 +#define XPDS_AUX_MAILBOX__WRITE_BOOTDATA 0x00 +#define XPDS_AUX_MAILBOX__WRITE_MAC_ADDR 0x01 +#define XPDS_AUX_MAILBOX__WRITE_HARDWARE_VERSION 0x02 +#define XPDS_AUX_MAILBOX__WRITE_FIRMWARE_VERSION 0x03 +#define XPDS_AUX_MAILBOX__WRITE_PHYSICAL_MODE 0x04 +#define XPDS_AUX_MAILBOX__READ_MAC_ADDR 0x05 +#define XPDS_AUX_MAILBOX__READ_HARDWARE_VERSION 0x06 +#define XPDS_AUX_MAILBOX__READ_FIRMWARE_VERSION 0x07 +#define XPDS_AUX_MAILBOX__READ_PHYSICAL_MODE 0x08 +#define XPDS_AUX_MAILBOX__WRITE_TEST_MODE 0x09 +#define XPDS_AUX_MAILBOX__CLEAR_IRQ 0x0a +#define XPDS_AUX_MAILBOX__READ_PHYSICAL_STATE 0x0b + +#define XPDS_AUX_MAILBOX__NIC_WRITE_DEBUG_PORT 0x0d +#define XPDS_AUX_MAILBOX__NIC_WRITE_IRQ_TYPE 0x0e +#define XPDS_AUX_MAILBOX__NIC_WRITE_EXIT_CODE 0x0f + +#endif diff --git a/drivers/net/xpds/xpds-sdsl.c b/drivers/net/xpds/xpds-sdsl.c new file mode 100644 index 000000000000..b644a7b8854e --- /dev/null +++ b/drivers/net/xpds/xpds-sdsl.c @@ -0,0 +1,965 @@ +/* + * Copyright 1999, 2000 Xpeed, Inc. + * xpds-sdsl.c, $Revision: 1.7 $ + * License to copy and distribute is GNU General Public License, version 2. + */ +#ifdef DEBUG +#define dprintk if (xpds_debug_level & DEBUG_MAIN) printk +#define ddprintk if ((xpds_debug_level & (DEBUG_MAIN | DEBUG_DETAILED)) == (DEBUG_MAIN | DEBUG_DETAILED)) printk +#else +#define dprintk if (0) printk +#define ddprintk if (0) printk +#endif + +#ifndef __KERNEL__ +#define __KERNEL__ 1 +#endif +#ifndef MODULE +#define MODULE 1 +#endif + +#define __NO_VERSION__ 1 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "xpds-encap-fr.h" +#include + +#include + +#include +#include +#include + +#if ! defined (CONFIG_PCI) +#error "CONFIG_PCI is not defined" +#endif + +#include "xpds.h" +#include "xpds-reg.h" + +#define AUX_CONTROL 0x0 +#define AUX_WRITE_ADDR 0x4 +#define AUX_WRITE_DATA 0x8 +#define AUX_READ_DATA 0x4 +#define AUX_READ_ADDR 0x8 +#define AUX_MAILBOX 0xc + +#define AUX_CONTROL__BUSY 0x08 + +#define ASIC_FLASHSIZE 0x8000 +#define FPGA_FLASHSIZE 0x10000 + +/* + * Using __inline__ produces bugs. + */ +#define __inline__ + +#define aux_busy_wait(card_num,n) \ + do { \ + u32 wait_until_abw = jiffies + (n) * HZ; \ + for (;;) { \ + u32 val_d; \ + int rc_abw; \ + rc_abw = xpds_read_control_register_quiet (card_num, AUX_CONTROL, &val_d, AUX); \ + if (rc_abw > 0) return rc_abw; \ + if (! (val_d & AUX_CONTROL__BUSY) ) break; \ + schedule_if_no_interrupt (card_num); \ + if (jiffies > wait_until_abw) return 5; \ + } \ + } while (0) + +#define flash_addr_control(card_num,addr,ctrl) \ + do { \ + int rc_fac; \ + rc_fac = xpds_write_control_register_quiet (card_num, AUX_WRITE_ADDR, (addr), AUX); \ + if (rc_fac > 0) return rc_fac; \ + rc_fac = xpds_write_control_register_quiet (card_num, AUX_CONTROL, (ctrl), AUX); \ + if (rc_fac > 0) return rc_fac; \ + aux_busy_wait (card_num, 1); \ + } while (0) + +#define flash_data_control(card_num,data,ctrl) \ + do { \ + int rc_fdc; \ + rc_fdc = xpds_write_control_register_quiet (card_num, AUX_WRITE_DATA, (data), AUX); \ + if (rc_fdc > 0) return rc_fdc; \ + rc_fdc = xpds_write_control_register_quiet (card_num, AUX_CONTROL, (ctrl), AUX); \ + if (rc_fdc > 0) return rc_fdc; \ + aux_busy_wait (card_num, 1); \ + } while (0) + +__inline__ int +xpds_reset_flash (int card_num) +{ + flash_addr_control (card_num, 0, 0x106); + flash_data_control (card_num, 0xff, 0x105); + return 0; +} + +__inline__ static int +set_flash_read_mode (int card_num) +{ + flash_addr_control (card_num, 0, 0x106); + flash_data_control (card_num, 0, 0x105); + return 0; +} + +__inline__ int +xpds_read_flash_byte (int card_num, u32 address, u8 *valuep) +{ + u32 val; + int rc; + + rc = set_flash_read_mode (card_num); + if (rc > 0) return rc; + + address &= 0xffff; + + flash_addr_control (card_num, address, 0x106); + flash_data_control (card_num, 0xaa, 0x104); + + /* will this work on a big-endian computer? */ + rc = xpds_read_control_register_quiet (card_num, + AUX_READ_DATA, &val, AUX); + if (rc > 0) return rc; + + *valuep = val; + + ddprintk ("flash[%04x]->%02x\n", address, *valuep); + return 0; +} + +__inline__ int +xpds_write_flash_byte (int card_num, u32 address, u8 value) +{ + int rc, done, count; + u8 rval; + + rc = xpds_reset_flash (card_num); + if (rc > 0) return rc; + + address &= 0xffff; + + for (count = 0, done = 0; ! done && count < 100; ) { + + /* program setup command */ + flash_addr_control (card_num, address, 0x106); + flash_data_control (card_num, 0x40, 0x105); + + /* program command */ + flash_addr_control (card_num, address, 0x106); + flash_data_control (card_num, value, 0x105); + + udelay (10); + + /* program verify command */ + flash_addr_control (card_num, address, 0x106); + flash_data_control (card_num, 0xc0, 0x105); + + udelay (6); + + /* read back */ + rc = xpds_read_flash_byte (card_num, address, &rval); + + if (rval == value) { + done = 1; + } else { + count ++; + } + } + if (! done) { + printk (KERN_ERR "write_flash_byte (%d, %04lx, %02x) failed (rval = %02x)\n", card_num, (unsigned long) address, value, rval); + return 6; + } + + ddprintk ("%02x->flash[%04x]\n", value, address); + + return 0; +} + +__inline__ int +xpds_verify_flash (int card_num, u8 *buffer, u8 expected_byte, int length) +{ + int i; + int rc; + + for (i = 0; i < length; i ++) { + u8 byte, ex_byte; + + rc = xpds_read_flash_byte (card_num, i, &byte); + ex_byte = ((buffer != NULL) ? buffer[i] : expected_byte); + if (byte != ex_byte) { + printk (KERN_ERR "xpds_verify_flash() failed at %04x (got %02x, expected %02x)\n", i, byte, ex_byte); + return 6; + } + } + return -1; +} + +__inline__ int +xpds_erase_flash (int card_num) +{ + int i, verified = 0, done = 0, rc; + int flashsize; + + /* write all zeros */ + flashsize = xpds_data[card_num].is_fpga ? + FPGA_FLASHSIZE : ASIC_FLASHSIZE; + for (i = 0; i < flashsize; i ++) { + rc = xpds_write_flash_byte (card_num, i, 0); + if (rc > 0) { + printk (KERN_ERR "%s: writing 0 to flash byte %04x during erase failed\n", xpds_devs[card_num].name, i); + return rc; + } + schedule (); + } + + rc = xpds_verify_flash (card_num, NULL, 0, flashsize); + if (rc > 0) return rc; + + /* bulk erase 1000 times */ + + for (i = 0; i < 1000; ) { + u32 wait_until; + int j; + + /* erase setup */ + flash_addr_control (card_num, 0, 0x106); + flash_data_control (card_num, 0x20, 0x105); + + /* erase */ + flash_addr_control (card_num, 0, 0x106); + flash_data_control (card_num, 0x20, 0x105); + + /* delay 10ms */ + wait_until = jiffies + 10 * HZ / 1000; + while (jiffies < wait_until) { + schedule (); + } + + /* verify that each byte is 0xff */ + for (j = verified; j < flashsize; j ++) { + u32 val; + u8 byte; + + /* erase verify */ + flash_addr_control (card_num, (j & 0xffff), 0x106); + flash_data_control (card_num, 0xa0, 0x105); + udelay (6); + + /* read data */ + flash_addr_control (card_num, (j & 0xffff), 0x106); + flash_data_control (card_num, 0, 0x104); + + rc = xpds_read_control_register (card_num, AUX_READ_DATA, &val, AUX); + byte = val; + if (rc > 0 || byte != 0xff) { + done = 0; + verified = j; + i ++; + break; + } else { + done = 1; + } + } + /* + printk ("i = %d, verified = %04x, done = %d\n", + i, verified, done); + */ + + if (done) break; + } + if (i >= 1000) { + ddprintk ("xpds_erase_flash() failed\n"); + return 6; + } + + xpds_reset_flash (card_num); + ddprintk ("xpds_erase_flash() succeeded\n"); + + rc = xpds_verify_flash (card_num, NULL, 0xff, flashsize); + if (rc > 0) { + printk (KERN_ERR "xpds_verify_flash () during erase failed\n"); + return rc; + } + + return 0; +} + +/* + * IRQ types for value returned by using XPDS_MBX_WRITE_IRQTYPE + * with xpds_mailbox_read(). + */ +typedef enum { /* Codes for physical layer i/o */ + XPDS_SDSL_CMD_FAILED, /* Physical layer command failed */ + XPDS_SDSL_CMD_COMPLETED, /* Physical layer command completed successfully */ + XPDS_SDSL_CMD_TIMEOUT, /* Physical layer command timed out */ + XPDS_SDSL_FLASH_ERASE_ERROR, /* Flash could not be erased */ + XPDS_SDSL_FLASH_ZERO_ERROR, /* Flash could not be zeroed out */ + XPDS_SDSL_FLASH_WRITE_ERROR, /* Flash could not be written */ + XPDS_SDSL_AUX_MODE_PCI, /* PCI is the aux bus master */ + XPDS_SDSL_AUX_MODE_NIC, /* NIC is the aux bus master */ + XPDS_SDSL_AUX_MODE_BOTH, /* Both can be aux master */ + XPDS_SDSL_RAM_ERROR, /* RAM failed a self-test */ + XPDS_SDSL_IRQTYPE_STATE_CHANGE, /* Physical layer has gone into an important state */ + XPDS_SDSL_STATE_BOOTED, /* PL State: Booted */ + XPDS_SDSL_TESTMODE_NORMAL_OPERATION, /* Return to normal operation */ + XPDS_SDSL_TESTMODE_EXTERNAL_ANALOG_LOOPBACK, /* Transmit then receive echo */ + XPDS_SDSL_TESTMODE_FOUR_LEVEL_SCR, /* Transmit a continuous stream of 4 level scrambled ones */ + XPDS_SDSL_TESTMODE_TWO_LEVEL_SCR, /* Transmit a continuous stream of 2 level scrambled ones */ + XPDS_SDSL_TESTMODE_INTERNAL_ANALOG_LOOPBACK, /* Loopback bypassing pins */ + XPDS_SDSL_TESTMODE_FORCE_LINKUP, /* Force link up so driver can start */ + XPDS_SDSL_MAX /* Not a code - Used for bounds checking */ +} xpds_sdsl_codes_t; + +typedef enum { /* Maximum 128 types total */ + /* BEGIN PCI MAILBOX */ + XPDS_MBX_WRITE_BOOTDATA, /* Write bootdata from the mailbox */ + XPDS_MBX_WRITE_MACADDR, /* Write the mac address from the mailbox */ + XPDS_MBX_WRITE_HWVER, /* Write the hardware version from the mailbox */ + XPDS_MBX_WRITE_FWVER, /* Write the firmware version from the mailbox */ + XPDS_MBX_WRITE_PHYSMODE, /* Write the physical layer mode */ + XPDS_MBX_READ_MACADDR, /* Send the mac address to the mailbox */ + XPDS_MBX_READ_HWVER, /* Send the hardware version to the mailbox */ + XPDS_MBX_READ_FWVER, /* Send the firmware version to the mailbox */ + XPDS_MBX_READ_PHYSMODE, /* Write the physical layer mode */ + XPDS_MBX_WRITE_TESTMODE, /* Put the rs8973 into a test mode */ + XPDS_MBX_CLEAR_IRQ, /* Clear the interrupt being generated */ + XPDS_MBX_READ_PHYS_STATE, /* Read the physical layer state */ + XPDS_MBX_WRITE_MFGDATE, /* 4 bytes */ + XPDS_MBX_READ_MFGDATE, + XPDS_MBX_READ_SERIALNUMBER, /* 16 bytes */ + XPDS_MBX_WRITE_SERIALNUMBER, + XPDS_MBX_READ_STAGE, + XPDS_MBX_START_BITPUMP, + XPDS_MBX_START_BITPUMP_FOR_MFG, + XPDS_MBX_READ_PHYSMODE_IN_FLASH, + XPDS_MBX_READ_PHYS_QUALITY, + XPDS_MBX_PCIMASTER_MAXTYPE, /* Types below this value are for the PCI mailbox only */ + /* BEGIN NIC MAILBOX (r/w from NIC perspective) */ + XPDS_MBX_WRITE_DEBUGPORT = 64, /* Get the NIC supplied terminal output from the mailbox */ + XPDS_MBX_WRITE_IRQTYPE, /* Get the type of irq being generated */ + XPDS_MBX_WRITE_EXITCODE, /* Write the completion code */ + XPDS_MBX_NICMASTER_MAXTYPE /* Types below this value are for NIC mailbox only */ +} xpds_mbx_t; + +/* MAILBOX BITMASKS */ +#define XPDS_MBX_PCI_HANDSHAKE_BIT 0x00000080 +#define XPDS_MBX_NIC_HANDSHAKE_BIT 0x00800000 +#define XPDS_MBX_NIC_HANDSHAKE_BIT_SHIFTED 0x00000080 + +typedef union { + u8 bytes[4]; + u32 word; +} mailbox_t; + +#define XPDS_MBX_PCI_FLAGS 0 +#define XPDS_MBX_PCI_DATA 1 +#define XPDS_MBX_NIC_FLAGS 2 +#define XPDS_MBX_NIC_DATA 3 + +#define mailbox_busy_wait(card_num,n,bit) \ + do { \ + u32 wait_until_mbw = jiffies + (n) * HZ / 1000; \ + for (;;) { \ + u32 val_mbw; \ + xpds_read_control_register_quiet (card_num, AUX_MAILBOX, &val_mbw, AUX); \ + if (val_mbw & (bit)) break; \ + schedule_if_no_interrupt (card_num); \ + if (jiffies > wait_until_mbw) { \ + printk (KERN_ERR "(val_mbw=%x,bit=%x)", val_mbw, bit);\ + return 5; \ + } \ + } \ + } while (0) + +#define mailbox_busy_wait_not(card_num,n,bit) \ + do { \ + u32 wait_until_mbw = jiffies + (n) * HZ / 1000; \ + for (;;) { \ + u32 val_mbw; \ + xpds_read_control_register_quiet (card_num, AUX_MAILBOX, &val_mbw, AUX); \ + if (! (val_mbw & (bit))) break; \ + schedule_if_no_interrupt (card_num); \ + if (jiffies > wait_until_mbw) { \ + printk (KERN_ERR "(val_mbw=%x,!bit=%x)", val_mbw, bit);\ + return 5; \ + } \ + } \ + } while (0) + + +static mailbox_t *mailbox = NULL; +/* + * Timeouts in milliseconds. + * Need a longer timeout for use with Copper Mountain when getting the + * speed assigned by the DSLAM. + */ +#define MAILBOX_WRITE_TIMEOUT (xpds_data[card_num].sdsl_speed == 0 ? 1100 :50) +#define MAILBOX_READ_TIMEOUT (xpds_data[card_num].sdsl_speed == 0 ? 1100 :50) + +__inline__ int +xpds_mailbox_write (int card_num, xpds_mbx_t transfer_type, u8 byte) +{ + if (transfer_type < XPDS_MBX_PCIMASTER_MAXTYPE) { + /* PCI mailbox flag */ + + if (mailbox == NULL || card_num >= xpds_max_cards) { + printk (KERN_ERR "mailbox problem: mailbox = %p, xpds_max_cards = %d, card_num = %d\n", mailbox, xpds_max_cards, card_num); + return 2; + } + + /* write data into data mailbox */ + mailbox[card_num].bytes[XPDS_MBX_PCI_DATA] = byte; + xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX); + + /* change transfer type and set handshake bit */ + mailbox[card_num].bytes[XPDS_MBX_PCI_FLAGS] = + transfer_type | XPDS_MBX_PCI_HANDSHAKE_BIT; + xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX); + + /* wait until data has been received */ + mailbox_busy_wait (card_num, MAILBOX_WRITE_TIMEOUT, XPDS_MBX_PCI_HANDSHAKE_BIT); + + /* clear handshake bit */ + mailbox[card_num].bytes[XPDS_MBX_PCI_FLAGS] &= + ~ XPDS_MBX_PCI_HANDSHAKE_BIT; + xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX); + + /* wait until data has been received */ + mailbox_busy_wait_not (card_num, MAILBOX_WRITE_TIMEOUT, XPDS_MBX_PCI_HANDSHAKE_BIT); + + return 0; + } else if (transfer_type < XPDS_MBX_NICMASTER_MAXTYPE) { + /* 8032 mailbox flag */ + return 2; + } else { + return 2; + } +} + +typedef struct { + u8 *bytes; + u32 size; + u32 head; + u32 tail; +} queue_t; + +static queue_t *debug_queue = NULL; +static queue_t *irq_queue = NULL; +static queue_t *exitcode_queue = NULL; + +static int +expand_queue (queue_t *q) +{ + u32 new_size; + u8 *new_bytes; + + if (q->size == 0) { + new_size = 100; + } else { + new_size = q->size * 2; + } + dprintk (KERN_DEBUG "expanding queue %p from %d bytes to %d bytes\n", + q, q->size, new_size); + new_bytes = kmalloc (new_size, GFP_ATOMIC); + if (new_bytes == NULL) { + printk (KERN_ERR "failed to increase size of queue to %d\n", new_size); + return 1; + } + if (q->size > 0) { + memcpy (new_bytes, q->bytes + q->head, q->size - q->head); + memcpy (new_bytes + q->size - q->head, q->bytes, q->head); + } + if (q->bytes != NULL) { + dprintk (KERN_DEBUG "freeing q->bytes (%p)\n", q->bytes); + kfree (q->bytes); + } + q->head = 0; + q->tail = q->size; + q->size = new_size; + q->bytes = new_bytes; + return 0; +} + +__inline__ static int +add_to_queue (queue_t *q, u8 byte) +{ + if (q == NULL) return 1; + if (q->size == 0 || (q->tail + 1) % q->size == q->head) { + int rc; + rc = expand_queue (q); + if (rc) return rc; + } + dprintk (KERN_DEBUG "adding %02x in queue %p position %d\n", + byte, q, q->tail); + q->bytes[q->tail] = byte; + q->tail ++; + q->tail %= q->size; + return 0; +} + +__inline__ static int +get_from_queue (queue_t *q, u8 *bytep) +{ + if (q == NULL) return 1; + if (q->head == q->tail) return 1; + dprintk (KERN_DEBUG "getting %02x from queue %p position %d\n", + q->bytes[q->head], q, q->head); + *bytep = q->bytes[q->head]; + q->head ++; + q->head %= q->size; + return 0; +} + +__inline__ int +xpds_mailbox_read (int card_num, xpds_mbx_t transfer_type, u8 *bytep) +{ + u32 val; + + /* check to see if NIC is the master? */ + + if (transfer_type < XPDS_MBX_PCIMASTER_MAXTYPE) { + /* change transfer type and set handshake bit */ + mailbox[card_num].bytes[XPDS_MBX_PCI_FLAGS] = + transfer_type | XPDS_MBX_PCI_HANDSHAKE_BIT; + xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX); + + /* wait until data has been received */ + mailbox_busy_wait (card_num, MAILBOX_READ_TIMEOUT, XPDS_MBX_PCI_HANDSHAKE_BIT); + + /* get data */ + xpds_read_control_register (card_num, AUX_MAILBOX, &val, AUX); + *bytep = (val & 0xff00) >> 8; + + /* clear handshake bit */ + mailbox[card_num].bytes[XPDS_MBX_PCI_FLAGS] &= + ~ XPDS_MBX_PCI_HANDSHAKE_BIT; + xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX); + + /* wait until handshake clear is received */ + mailbox_busy_wait_not (card_num, MAILBOX_READ_TIMEOUT, XPDS_MBX_PCI_HANDSHAKE_BIT); + + return 0; + } else if (transfer_type < XPDS_MBX_NICMASTER_MAXTYPE) { + queue_t *q; + int rc; + u32 wait_until; + + /* first check the appropriate queue */ + switch (transfer_type) { + case XPDS_MBX_WRITE_DEBUGPORT: + q = &(debug_queue[card_num]); break; + case XPDS_MBX_WRITE_IRQTYPE: + q = &(irq_queue[card_num]); break; + case XPDS_MBX_WRITE_EXITCODE: + q = &(exitcode_queue[card_num]); break; + default: + return 2; + } + rc = get_from_queue (q, bytep); + dprintk (KERN_DEBUG "%s: got %02x from queue (%p/%02x) (rc = %d)\n", xpds_devs[card_num].name, *bytep, q, transfer_type, rc); + if (rc == 0) return 0; + + /* not in queue, get from NIC */ + wait_until = jiffies + (MAILBOX_READ_TIMEOUT * 2) * HZ / 1000; + for (;;) { + u32 val_nic; + queue_t *q_nic; + int done = 0; + + xpds_read_control_register_quiet (card_num, AUX_MAILBOX, &val_nic, AUX); + if (val_nic & XPDS_MBX_NIC_HANDSHAKE_BIT) { + xpds_mbx_t nic_transfer_type; + u8 byte; + + nic_transfer_type = (val_nic & 0x007f0000) >> 16; + + switch (nic_transfer_type) { + case XPDS_MBX_WRITE_DEBUGPORT: + q_nic = &(debug_queue[card_num]); break; + case XPDS_MBX_WRITE_IRQTYPE: + q_nic = &(irq_queue[card_num]); break; + case XPDS_MBX_WRITE_EXITCODE: + q_nic = &(exitcode_queue[card_num]); break; + default: + q_nic = &(debug_queue[card_num]); break; + } + byte = (val_nic & 0xff000000) >> 24; + dprintk (KERN_DEBUG "%s: adding %02x to queue (%p/%02x)\n", xpds_devs[card_num].name, byte, q_nic, nic_transfer_type); + add_to_queue (q_nic, byte); + + mailbox[card_num].bytes[XPDS_MBX_NIC_FLAGS] = + transfer_type | + XPDS_MBX_NIC_HANDSHAKE_BIT_SHIFTED; + xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX); + + mailbox_busy_wait_not (card_num, MAILBOX_READ_TIMEOUT, XPDS_MBX_NIC_HANDSHAKE_BIT); + mailbox[card_num].bytes[XPDS_MBX_NIC_FLAGS] &= + ~ XPDS_MBX_NIC_HANDSHAKE_BIT_SHIFTED; + xpds_write_control_register (card_num, AUX_MAILBOX, mailbox[card_num].word, AUX); + + schedule_if_no_interrupt (card_num); + + if (nic_transfer_type == transfer_type) { + done = 1; + } + } + if (done || jiffies > wait_until) break; + } + rc = get_from_queue (q, bytep); + dprintk (KERN_DEBUG "%s: got %02x from queue (%p/%02x) (rc = %d)\n", xpds_devs[card_num].name, *bytep, q, transfer_type, rc); + if (rc == 0) return 0; + + return 1; + } else { + return 2; + } +} + +int +xpds_get_sdsl_exit_code (int card_num, u8 *bytep) +{ + int rc; + + rc = xpds_mailbox_read (card_num, XPDS_MBX_WRITE_EXITCODE, bytep); + return rc; +} + +/* + * This gets the mode in the flash memory, but the speed section of + * the mode may be a default speed instead of the actual value if the + * speed section is invalid (the SDSL card will operate at the default + * speed in this case). + */ +int +xpds_get_sdsl_mode (int card_num, u32 *val) +{ + int i, rc, rval = 0; + u8 byte = 0; + + *val = 0; + for (i = 0; i < 4; i ++) { + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_PHYSMODE, &byte); + if (rc > 0) rval = 1; + *val |= byte << ((3 - i) * 8); + } + return rval; +} + +/* + * This gets the actual mode in the flash memory. + */ +int +xpds_get_flash_sdsl_mode (int card_num, u32 *val) +{ + int i, rc, rval = 0; + u8 byte = 0; + + *val = 0; + for (i = 0; i < 4; i ++) { + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_PHYSMODE_IN_FLASH, &byte); + if (rc > 0) rval = 1; + *val |= byte << ((3 - i) * 8); + } + return rval; +} + +int +xpds_set_sdsl_mode (int card_num, u32 val) +{ + int i, rc, rval = 0; + u8 byte; + + for (i = 0; i < 4; i ++) { + byte = (val >> ((3 - i) * 8)) & 0xff; + rc = xpds_mailbox_write (card_num, XPDS_MBX_WRITE_PHYSMODE, byte); + if (rc > 0) rval = 1; + } + return rval; +} + +int +xpds_reset_sdsl (int card_num) +{ + u32 wait_until, val; + + /* reset 8032 and Rockwell */ + dprintk (KERN_DEBUG "%s: resetting the 8032\n", xpds_devs[card_num].name); + xpds_read_control_register (card_num, XPDS_MCR_TXCI, &val, MAIN); + xpds_write_control_register (card_num, XPDS_MCR_TXCI, val & 0x6f, MAIN); + xpds_read_control_register (card_num, AUX_CONTROL, &val, AUX); + xpds_write_control_register (card_num, AUX_CONTROL, val & ~0x80, AUX); + xpds_read_control_register (card_num, AUX_CONTROL, &val, AUX); + xpds_write_control_register (card_num, AUX_CONTROL, val | 0x100, AUX); + + wait_until = jiffies + 1 * HZ; + while (jiffies < wait_until) { + schedule (); + } + return 0; +} + +int +xpds_start_sdsl (int card_num) +{ + u32 wait_until, val; + + /* unreset 8032 and Rockwell */ + dprintk (KERN_DEBUG "%s: unresetting the 8032\n", xpds_devs[card_num].name); + + xpds_read_control_register (card_num, AUX_CONTROL, &val, AUX); + xpds_write_control_register (card_num, AUX_CONTROL, val & ~0x100, AUX); + + xpds_read_control_register (card_num, XPDS_MCR_TXCI, &val, MAIN); + xpds_write_control_register (card_num, XPDS_MCR_TXCI, val | 0x90, MAIN); + + xpds_read_control_register (card_num, AUX_CONTROL, &val, AUX); + xpds_write_control_register (card_num, AUX_CONTROL, val | 0x80, AUX); + + wait_until = jiffies + HZ * 1; + while (jiffies < wait_until) { + schedule (); + } + + return 0; +} + +typedef struct { + int size; + u8 *image; +} xpds_flash_image_t; + +int +xpds_install_flash_image (int card_num, xpds_flash_image_t *fd) +{ + int i, rc; + xpds_flash_image_t kfd; + u8 *image; + int flashsize; + + flashsize = xpds_data[card_num].is_fpga ? + FPGA_FLASHSIZE : ASIC_FLASHSIZE; + + printk (KERN_NOTICE "%s: resetting SDSL\n", xpds_devs[card_num].name); + rc = xpds_reset_sdsl (card_num); + if (rc > 0) { + printk (KERN_ERR "%s: SDSL reset failed\n", xpds_devs[card_num].name); + return rc; + } + printk (KERN_NOTICE "%s: resetting flash\n", xpds_devs[card_num].name); + rc = xpds_reset_flash (card_num); + if (rc > 0) { + printk (KERN_ERR "%s: flash reset failed\n", xpds_devs[card_num].name); + return rc; + } + + printk (KERN_NOTICE "%s: erasing flash\n", xpds_devs[card_num].name); + rc = xpds_erase_flash (card_num); + if (rc > 0) { + printk (KERN_ERR "%s: flash erase failed\n", xpds_devs[card_num].name); + return rc; + } + + printk (KERN_NOTICE "%s: writing flash image\n", xpds_devs[card_num].name); + copy_from_user (&kfd, fd, sizeof (kfd)); + if (kfd.size > flashsize) kfd.size = flashsize; + image = kmalloc (kfd.size, GFP_KERNEL); + if (image == NULL) return 1; + copy_from_user (image, kfd.image, kfd.size); + + for (i = 0; i < kfd.size; i ++) { + rc = xpds_write_flash_byte (card_num, i, image[i]); + if (rc > 0) { + printk (KERN_ERR "%s: flash write failed, address %04x, data %02x\n", xpds_devs[card_num].name, i, image[i]); + kfree (image); + return rc; + } + schedule (); + } + printk (KERN_NOTICE "%s: verifying flash image\n", xpds_devs[card_num].name); + rc = xpds_verify_flash (card_num, image, 0, kfd.size); + if (rc > 0) { + printk (KERN_ERR "%s: flash verify failed\n", xpds_devs[card_num].name); + kfree (image); + return rc; + } + + xpds_reset_sdsl (card_num); + xpds_start_sdsl (card_num); + + kfree (image); + return 0; +} + +int +xpds_sdsl_loopback (int card_num) +{ + int rc; + + dprintk (KERN_DEBUG "%s: setting SDSL loopback mode\n", xpds_devs[card_num].name); + rc = xpds_mailbox_write (card_num, XPDS_MBX_WRITE_TESTMODE, 0x10); + return rc; +} + +/* + * Set data like hardware version, serial number, etc. + * Only one exit code is generated for the whole thing, except for + * bytes which were unwritable (which cause additional exit codes). + */ +static int +xpds_set_sdsl_serial_data (int card_num, xpds_mbx_t transfer_type, u8 *data, + int size) +{ + int i, rc, rval = 0; + u8 ec; + + for (i = 0; i < size; i ++) { + rc = xpds_mailbox_write (card_num, transfer_type, data[i]); + if (rc > 0) { + rval = 1; + xpds_get_sdsl_exit_code (card_num, &ec); + } + } + rc = xpds_get_sdsl_exit_code (card_num, &ec); + if (rc > 0 || ec != XPDS_SDSL_CMD_COMPLETED) rval = 1; + return rval; +} + +int +xpds_set_sdsl_info (int card_num) +{ + xpds_serial_data_t *sdata; + int rc, rval = 0; + + sdata = &(xpds_data[card_num].serial_data); + + rc = xpds_set_sdsl_serial_data (card_num, XPDS_MBX_WRITE_HWVER, + sdata->hardware_version, sizeof (sdata->hardware_version)); + if (rc > 0) { + printk (KERN_ERR "%s: failed to write hardware version\n", xpds_devs[card_num].name); + rval = 1; + } + +#if REWRITE_FWVER + rc = xpds_set_sdsl_serial_data (card_num, XPDS_MBX_WRITE_FWVER, + sdata->firmware_version, sizeof (sdata->firmware_version)); + if (rc > 0) { + printk (KERN_ERR "%s: failed to write firmware version\n", xpds_devs[card_num].name); + rval = 1; + } +#else + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_FWVER, + &(sdata->firmware_version[0])); + if (rc) rval = 1; + + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_FWVER, + &(sdata->firmware_version[1])); + if (rc) rval = 1; + + if (rval) { + printk (KERN_ERR "%s: failed to read new firmware version\n", xpds_devs[card_num].name); + sdata->firmware_version[0] = 0; + sdata->firmware_version[1] = 0; + } + + printk (KERN_INFO "%s: new firmware version is %d.%d\n", + xpds_devs[card_num].name, sdata->firmware_version[0], + sdata->firmware_version[1]); +#endif + + rc = xpds_set_sdsl_serial_data (card_num, XPDS_MBX_WRITE_MFGDATE, + sdata->mfg_date, sizeof (sdata->mfg_date)); + if (rc > 0) { + printk (KERN_ERR "%s: failed to write manufacturing date\n", xpds_devs[card_num].name); + rval = 1; + } + + rc = xpds_set_sdsl_serial_data (card_num, XPDS_MBX_WRITE_MACADDR, + sdata->mac_address, sizeof (sdata->mac_address)); + if (rc > 0) { + printk (KERN_ERR "%s: failed to write MAC address\n", xpds_devs[card_num].name); + rval = 1; + } + + rc = xpds_set_sdsl_serial_data (card_num, XPDS_MBX_WRITE_SERIALNUMBER, + sdata->serial_number, sizeof (sdata->serial_number)); + if (rc > 0) { + printk (KERN_ERR "%s: failed to write serial number\n", xpds_devs[card_num].name); + rval = 1; + } + + return rval; +} + +int +xpds_sdsl_allocate (void) +{ + if (debug_queue == NULL) { + debug_queue = kmalloc (xpds_max_cards * sizeof (*debug_queue), GFP_KERNEL); + if (debug_queue == NULL) return -ENOMEM; + memset (debug_queue, 0, xpds_max_cards * sizeof (*debug_queue)); + } + if (irq_queue == NULL) { + irq_queue = kmalloc (xpds_max_cards * sizeof (*irq_queue), GFP_KERNEL); + if (irq_queue == NULL) return -ENOMEM; + memset (irq_queue, 0, xpds_max_cards * sizeof (*irq_queue)); + } + if (exitcode_queue == NULL) { + exitcode_queue = kmalloc (xpds_max_cards * sizeof (*exitcode_queue), GFP_KERNEL); + if (exitcode_queue == NULL) return -ENOMEM; + memset (exitcode_queue, 0, xpds_max_cards * sizeof (*exitcode_queue)); + } + if (mailbox == NULL) { + dprintk (KERN_DEBUG "allocating mailbox (%d * %d)\n", + xpds_max_cards, sizeof (*mailbox)); + mailbox = kmalloc (xpds_max_cards * sizeof (*mailbox), GFP_KERNEL); + if (mailbox == NULL) return -ENOMEM; + memset (mailbox, 0, xpds_max_cards * sizeof (*mailbox)); + } + return 0; +} + +int +xpds_sdsl_get_state (int card_num, u8 *state) +{ + return xpds_mailbox_read (card_num, XPDS_MBX_READ_STAGE, state); +} + +int +xpds_sdsl_cleanup (void) +{ + int i; + + for (i = 0; i < xpds_max_cards; i ++) { + if (debug_queue != NULL && debug_queue[i].bytes != NULL) kfree (debug_queue[i].bytes); + if (irq_queue != NULL && irq_queue[i].bytes != NULL) kfree (irq_queue[i].bytes); + if (exitcode_queue != NULL && exitcode_queue[i].bytes != NULL) kfree (exitcode_queue[i].bytes); + } + if (debug_queue != NULL) kfree (debug_queue); + if (irq_queue != NULL) kfree (irq_queue); + if (exitcode_queue != NULL) kfree (exitcode_queue); + if (mailbox != NULL) kfree (mailbox); + return 0; +} diff --git a/drivers/net/xpds/xpds-sdsl.h b/drivers/net/xpds/xpds-sdsl.h new file mode 100644 index 000000000000..aca46a5fcc65 --- /dev/null +++ b/drivers/net/xpds/xpds-sdsl.h @@ -0,0 +1,122 @@ +/* + * Copyright 1999, 2000 Xpeed, Inc. + * xpds-sdsl.h, $Revision: 1.3 $ + * License to copy and distribute is GNU General Public License, version 2. + */ + +#ifndef XPDS_SDSL_H +#define XPDS_SDSL_H 1 + +int xpds_reset_flash (int card_num); +int xpds_read_flash_byte (int card_num, u32 address, u8 *valuep); +int xpds_write_flash_byte (int card_num, u32 address, u8 value); +int xpds_verify_flash (int card_num, u8 *buffer, u8 expected_byte, int length); +int xpds_erase_flash (int card_num); + +typedef enum { /* Maximum 128 types total */ + /* BEGIN PCI MAILBOX */ +/* Write bootdata from the mailbox */ + XPDS_MBX_WRITE_BOOTDATA, + XPDS_MBX_WRITE_MACADDR, /* Write the mac address from the mailbox */ + XPDS_MBX_WRITE_HWVER, /* Write the hardware version from the mailbox */ + XPDS_MBX_WRITE_FWVER, /* Write the firmware version from the mailbox */ + XPDS_MBX_WRITE_PHYSMODE, /* Write the physical layer mode */ + XPDS_MBX_READ_MACADDR, /* Send the mac address to the mailbox */ + XPDS_MBX_READ_HWVER, /* Send the hardware version to the mailbox */ + XPDS_MBX_READ_FWVER, /* Send the firmware version to the mailbox */ + XPDS_MBX_READ_PHYSMODE, /* Write the physical layer mode */ + XPDS_MBX_WRITE_TESTMODE, /* Put the rs8973 into a test mode */ + XPDS_MBX_CLEAR_IRQ, /* Clear the interrupt being generated */ + XPDS_MBX_READ_PHYS_STATE, /* Read the physical layer state */ + XPDS_MBX_WRITE_MFGDATE, /* 4 bytes */ + XPDS_MBX_READ_MFGDATE, + XPDS_MBX_READ_SERIALNUMBER, /* 16 bytes */ + XPDS_MBX_WRITE_SERIALNUMBER, + XPDS_MBX_READ_STAGE, + XPDS_MBX_START_BITPUMP, + XPDS_MBX_START_BITPUMP_FOR_MFG, + XPDS_MBX_READ_PHYSMODE_IN_FLASH, + XPDS_MBX_READ_PHYS_QUALITY, + XPDS_MBX_PCIMASTER_MAXTYPE, /* Types below this value are for the PCI mailbox only */ + /* BEGIN NIC MAILBOX (r/w from NIC perspective) */ + XPDS_MBX_WRITE_DEBUGPORT = 64, /* Get the NIC supplied terminal output from the mailbox */ + XPDS_MBX_WRITE_IRQTYPE, /* Get the type of irq being generated */ + XPDS_MBX_WRITE_EXITCODE, /* Write the completion code */ + XPDS_MBX_NICMASTER_MAXTYPE /* Types below this value are for NIC mailbox only */ +} xpds_mbx_t; + +/* + * IRQ types for value returned by using XPDS_MBX_WRITE_IRQTYPE + * with xpds_mailbox_read(). + */ +typedef enum { /* Codes for physical layer i/o */ + XPDS_SDSL_CMD_FAILED, /* Physical layer command failed */ + XPDS_SDSL_CMD_COMPLETED, /* Physical layer command completed successfu + lly */ + XPDS_SDSL_CMD_TIMEOUT, /* Physical layer command timed out */ + XPDS_SDSL_FLASH_ERASE_ERROR, /* Flash could not be erased */ + XPDS_SDSL_FLASH_ZERO_ERROR, /* Flash could not be zeroed out */ + XPDS_SDSL_FLASH_WRITE_ERROR, /* Flash could not be written */ + XPDS_SDSL_AUX_MODE_PCI, /* PCI is the aux bus master */ + XPDS_SDSL_AUX_MODE_NIC, /* NIC is the aux bus master */ + XPDS_SDSL_AUX_MODE_BOTH, /* Both can be aux master */ + XPDS_SDSL_RAM_ERROR, /* RAM failed a self-test */ + XPDS_SDSL_IRQTYPE_STATE_CHANGE, /* Physical layer has gone into an important + state */ + XPDS_SDSL_STATE_BOOTED, /* PL State: Booted */ + XPDS_SDSL_TESTMODE_NORMAL_OPERATION, /* Return to normal operation */ + XPDS_SDSL_TESTMODE_EXTERNAL_ANALOG_LOOPBACK, /* Transmit then receive echo */ + XPDS_SDSL_TESTMODE_FOUR_LEVEL_SCR, /* Transmit a continuous stream of 4 level scrambled ones */ + XPDS_SDSL_TESTMODE_TWO_LEVEL_SCR, /* Transmit a continuous stream of 2 level scrambled ones */ + XPDS_SDSL_TESTMODE_INTERNAL_ANALOG_LOOPBACK, /* Loopback bypassing pins */ + XPDS_SDSL_TESTMODE_FORCE_LINKUP, /* Force link up so driver can st + art */ + XPDS_SDSL_MAX /* Not a code - Used for bounds c +hecking */ +} xpds_sdsl_codes_t; + +/* + * Physical layer states for value returned by mailbox read + * with XPDS_RETURN_PHYS_STATE. + */ +#define XPDS_SDSL_STATE_LOS 0x01 +#define XPDS_SDSL_STATE_LOST 0x02 +#define XPDS_SDSL_STATE_TIPRING_REVERSED 0x04 +#define XPDS_SDSL_STATE_2LEVEL_TIMER_EXPIRED 0x08 +#define XPDS_SDSL_STATE_GOOD_NOISE_MARGIN 0x10 +#define XPDS_SDSL_STATE_RESERVED 0x20 +#define XPDS_SDSL_STATE_4LEVEL 0x40 +#define XPDS_SDSL_STATE_LINKUP 0x80 + +/* + * SDSL operating mode definitions + */ +#define XPDS_SDSL_MODE__UNUSED 0xfff80000 +#define XPDS_SDSL_MODE__NT 0x00040000 +#define XPDS_SDSL_MODE__INVERT 0x00020000 +#define XPDS_SDSL_MODE__SWAP 0x00010000 +#define XPDS_SDSL_MODE__SPEED_MASK 0x0000ffff + +typedef struct { + int size; + u8 *image; +} xpds_flash_image_t; + +int xpds_mailbox_write (int card_num, xpds_mbx_t transfer_type, u8 byte); +int xpds_mailbox_read (int card_num, xpds_mbx_t transfer_type, u8 *bytep); +int xpds_get_sdsl_exit_code (int card_num, u8 *bytep); +int xpds_get_sdsl_mode (int card_num, u32 *value); +int xpds_get_flash_sdsl_mode (int card_num, u32 *value); +int xpds_set_sdsl_mode (int card_num, u32 value); +int xpds_reset_sdsl (int card_num); +int xpds_start_sdsl (int card_num); +int xpds_install_flash_image (int card_num, xpds_flash_image_t *fd); +int xpds_sdsl_loopback (int card_num); +int xpds_set_sdsl_info (int card_num); +int xpds_sdsl_allocate (void); +int xpds_sdsl_get_state (int card_num, u8 *state); +int xpds_sdsl_cleanup (void); + +#define XPDS_SDSL_MAILBOX_READ_INCOMPLETE 1000 + +#endif diff --git a/drivers/net/xpds/xpds-softnet.h b/drivers/net/xpds/xpds-softnet.h new file mode 100644 index 000000000000..1ed99a5c8dbb --- /dev/null +++ b/drivers/net/xpds/xpds-softnet.h @@ -0,0 +1,44 @@ +#ifndef XPDS_SOFT_NET_H +#define XPDS_SOFT_NET_H 1 + +/* + * 2.4 softnet api macros taken from rrunner.c + */ +#if (LINUX_VERSION_CODE < 0x02030e) +#define net_device device +#endif + +#if (LINUX_VERSION_CODE >= 0x02031b) +#define NEW_NETINIT 1 +#endif + +#define HAS_SOFT_NET 0x02032b + +#if (LINUX_VERSION_CODE < HAS_SOFT_NET) +/* + * SoftNet changes + */ +#define dev_kfree_skb_irq(a) dev_kfree_skb(a) +#define netif_wake_queue(dev) clear_bit(0, &dev->tbusy) +#define netif_stop_queue(dev) set_bit(0, &dev->tbusy) +#define net_device_stats enet_statistics + +static inline void netif_start_queue(struct net_device *dev) +{ + dev->tbusy = 0; + dev->start = 1; +} + +#define xpds_mark_net_bh(foo) mark_bh(foo) +#define xpds_if_busy(dev) dev->tbusy +#define xpds_if_running(dev) dev->start /* Currently unused. */ +#define xpds_if_down(dev) {do{dev->start = 0;}while (0);} +#else +#define NET_BH 0 +#define xpds_mark_net_bh(foo) {do{} while(0);} +#define xpds_if_busy(dev) netif_queue_stopped(dev) +#define xpds_if_running(dev) netif_running(dev) +#define xpds_if_down(dev) {do{} while(0);} +#endif + +#endif diff --git a/drivers/net/xpds/xpds.c b/drivers/net/xpds/xpds.c new file mode 100644 index 000000000000..460ffda1981c --- /dev/null +++ b/drivers/net/xpds/xpds.c @@ -0,0 +1,4027 @@ +/* + * Copyright 1998, 1999, 2000 Xpeed, Inc. + * xpds.c, $Revision: 1.33 $ + * License to copy and distribute is GNU General Public License, version 2. + */ +#ifndef VERSION_STRING +#define VERSION_STRING "$Revision: 1.33 $" +#endif + +#define LT_IND_AI 0xc +#define NT_IND_AI 0xc + +#ifndef __KERNEL__ +#define __KERNEL__ 1 +#endif +#ifndef MODULE +#define MODULE 1 +#endif + +#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 + +#if ! defined (CONFIG_PCI) +#error "CONFIG_PCI is not defined" +#endif + +#include "xpds-softnet.h" +#include "xpds-reg.h" +#include "xpds-fsm.h" +#include +#include "xpds-sdsl.h" +#include "xpds-encap-fr.h" +#include "xpds.h" + +MODULE_AUTHOR("Timothy J. Lee "); +MODULE_DESCRIPTION("Xpeed 200 IDSL and 300 SDSL NIC driver (frame relay)"); + +/* + * Debugging stuff. + */ +#define DEBUG_MAIN 1 +#define DEBUG_FSM 2 +#define DEBUG_PACKET 64 +#define DEBUG_DETAILED 128 +int xpds_debug_level = 0 /* DEBUG_MAIN | DEBUG_FSM */; +MODULE_PARM(xpds_debug_level, "i"); + +#ifdef DEBUG +#define dprintk if (xpds_debug_level & DEBUG_MAIN) printk +#define dpprintk if ((xpds_debug_level & (DEBUG_MAIN | DEBUG_PACKET)) == (DEBUG_MAIN | DEBUG_PACKET)) printk +#define ddprintk if ((xpds_debug_level & (DEBUG_MAIN | DEBUG_DETAILED)) == (DEBUG_MAIN | DEBUG_DETAILED)) printk +#else +#define dprintk if (0) printk +#define dpprintk if (0) printk +#define ddprintk if (0) printk +#endif + +#define nrprintk if (net_ratelimit()) printk + +/* + * If we are loaded module for flashing, ignore error in detection. + */ +int xpds_load_for_flash = 0; +MODULE_PARM(xpds_load_for_flash, "i"); + +/* + * For loopback testing only. + */ +int xpds_asic_loopback = 0; +MODULE_PARM(xpds_asic_loopback, "i"); +int xpds_external_loopback = 0; +MODULE_PARM(xpds_external_loopback, "i"); + +/* some motherboards have 6 PCI slots... */ +#define XPDS_DEFAULT_MAX 6 +int xpds_max_cards = XPDS_DEFAULT_MAX; +MODULE_PARM(xpds_max_cards, "i"); + +/* time to wait for SDSL physical layer to come up */ +#define XPDS_SDSL_TIMEOUT 300 +int xpds_sdsl_timeout = XPDS_SDSL_TIMEOUT; +MODULE_PARM(xpds_sdsl_timeout, "i"); + +int xpds_default_dlci = XPDS_DEFAULT_DLCI; +MODULE_PARM(xpds_default_dlci, "i"); + +int xpds_default_dlci_cr = XPDS_DEFAULT_DLCI_CR; +MODULE_PARM(xpds_default_dlci_cr, "i"); + +int xpds_default_dlci_lmi = XPDS_DLCI_LMI_LT_OR_NT; +MODULE_PARM(xpds_default_dlci_lmi, "i"); + +int xpds_default_bridged = 0; +MODULE_PARM(xpds_default_bridged, "i"); + +static char *xpds_dev_name = "dsl"; +MODULE_PARM(xpds_dev_name, "s"); + +/* + * At rates below 400 Kbps, the SDSL TX DMA does not work on some + * computers. + */ +#define LOW_BIT_RATE 404 +#define TX_DMA_LOW_RATE_BUG(card_num) \ + (xpds_data[card_num].is_sdsl && \ + xpds_data[card_num].has_tx_dma_low_rate_bug && \ + xpds_data[card_num].sdsl_speed < LOW_BIT_RATE) +#define RX_DMA_LOW_RATE_BUG(card_num) \ + (xpds_data[card_num].is_sdsl && \ + xpds_data[card_num].has_rx_dma_low_rate_bug && \ + xpds_data[card_num].sdsl_speed < LOW_BIT_RATE) + +#define PCI_VENDOR_ID_XPDS 0x14b3 +#define PCI_DEVICE_ID_XPDS_1 0x0000 + +#define ALLOW_OLD_PCI_VENDOR_ID 0 +#define PCI_VENDOR_ID_XPDS_OLD 0xeeee + +/* + * Set xpds_mode by insmod. + */ +#define XPDS_MODE_B1 1 +#define XPDS_MODE_B2 2 +#define XPDS_MODE_D 4 +#define XPDS_MODE_DEFAULT 7 + +int xpds_mode = XPDS_MODE_DEFAULT; +MODULE_PARM(xpds_mode, "i"); + +/* + * Guard time for the state machines. + */ +#define DEFAULT_GUARD_TIME 15 +int xpds_guard_time = DEFAULT_GUARD_TIME; +MODULE_PARM(xpds_guard_time, "i"); + +#define RXTX_BUFFER_SIZE 2048 + +/* + * Maximum packet length that is put into a hardware register. + * Packets received by the hardware are dropped if they are + * equal to or greater in length. + */ +#define DEFAULT_MAX_PACKET_LENGTH 0x600 +u16 xpds_max_packet_length = DEFAULT_MAX_PACKET_LENGTH; +MODULE_PARM(xpds_max_packet_length, "i"); + +#define RXTX_CONTROL__SWGO 0x80000000 +#define RXTX_CONTROL__HWGO 0x40000000 +#define RXTX_CONTROL__NEXT_VALID 0x20000000 +#define RXTX_CONTROL__PACKET_TAG_OFFSET 16 +#define RXTX_CONTROL__PACKET_TAG_MASK 0xf +#define RXTX_CONTROL__PACKET_LENGTH_MASK 0xffff + +#define NUM_MAIN_CONTROL_REGISTERS 16 +#define NUM_FIFO_CONTROL_REGISTERS 16 +#define NUM_DMA_CONTROL_REGISTERS 48 +#define NUM_FIFO_DATA_REGISTERS 32 +#define NUM_AUX_REGISTERS 4 + +#define NUM_FIFO 4 +#define FIFO_SIZE (NUM_FIFO_DATA_REGISTERS / NUM_FIFO) + +xpds_data_t *xpds_data = NULL; +static int num_xpds_found = 0; + +#define XPDS_MAIN 0 +#define XPDS_RX_FIFO 2 +#define XPDS_TX_FIFO 3 +#define XPDS_RX_DMA 4 +#define XPDS_TX_DMA 5 +#define XPDS_RX_FIFO_DATA 6 +#define XPDS_TX_FIFO_DATA 7 +#define XPDS_AUX 8 + +#define MAIN XPDS_MAIN +#define RX_FIFO XPDS_RX_FIFO +#define TX_FIFO XPDS_TX_FIFO +#define RX_DMA XPDS_RX_DMA +#define TX_DMA XPDS_TX_DMA +#define RX_FIFO_DATA XPDS_RX_FIFO_DATA +#define TX_FIFO_DATA XPDS_TX_FIFO_DATA +#define AUX XPDS_AUX + +#define NAME_SIZE 16 + +#define NUM_DESC 14 + +static int xpds_init (struct net_device *dev); + +static char *xpds_names = NULL; +struct net_device *xpds_devs = NULL; + +__inline__ static int +xpds_read_control_register_internal (int xpds_num, int register_number, + u32 *value, int which, int verbose) +{ + register_number >>= 2; + + if (verbose) dprintk (KERN_DEBUG "%s: ", xpds_devs[xpds_num].name); + switch (which) { + case MAIN: + if (register_number > NUM_MAIN_CONTROL_REGISTERS || + xpds_data[xpds_num].main_control_registers == NULL) { + return 4; + } + *value = xpds_data[xpds_num].main_control_registers[register_number]; + if (verbose) { + dprintk ("main_ctl[%02x]->%08x ", + register_number, *value); + } + break; + case RX_FIFO: + if (register_number > NUM_FIFO_CONTROL_REGISTERS || + xpds_data[xpds_num].rx_fifo_control_registers == NULL) { + return 4; + } + *value = xpds_data[xpds_num].rx_fifo_control_registers[register_number]; + if (verbose) dprintk ("rx_fifo_ctl[%02x]->%08x ", + register_number, *value); + break; + case TX_FIFO: + if (register_number > NUM_FIFO_CONTROL_REGISTERS || + xpds_data[xpds_num].tx_fifo_control_registers == NULL) { + return 4; + } + *value = xpds_data[xpds_num].tx_fifo_control_registers[register_number]; + if (verbose) dprintk ("tx_fifo_ctl[%02x]->%08x ", + register_number, *value); + break; + case RX_DMA: + if (register_number > NUM_DMA_CONTROL_REGISTERS || + xpds_data[xpds_num].rx_dma_control_registers == NULL) { + return 4; + } + *value = xpds_data[xpds_num].rx_dma_control_registers[register_number]; + if (verbose) dprintk ("rx_dma_ctl[%02x]->%08x ", + register_number, *value); + break; + case TX_DMA: + if (register_number > NUM_DMA_CONTROL_REGISTERS || + xpds_data[xpds_num].tx_dma_control_registers == NULL) { + return 4; + } + *value = xpds_data[xpds_num].tx_dma_control_registers[register_number]; + if (verbose) dprintk ("tx_dma_ctl[%02x]->%08x ", + register_number, *value); + break; + case RX_FIFO_DATA: + if (register_number > NUM_FIFO_DATA_REGISTERS || + xpds_data[xpds_num].rx_fifo_data_registers == NULL) { + return 4; + } + *value = xpds_data[xpds_num].rx_fifo_data_registers[register_number]; + if (verbose) dprintk ("rx_fifo_data[%02x]->%08x ", + register_number, *value); + break; + case TX_FIFO_DATA: + if (register_number > NUM_FIFO_DATA_REGISTERS || + xpds_data[xpds_num].tx_fifo_data_registers == NULL) { + return 4; + } + *value = xpds_data[xpds_num].tx_fifo_data_registers[register_number]; + if (verbose) dprintk ("tx_fifo_data[%02x]->%08x ", + register_number, *value); + break; + case AUX: + if (register_number > NUM_AUX_REGISTERS || + xpds_data[xpds_num].aux_registers == NULL) { + return 4; + } + *value = xpds_data[xpds_num].aux_registers[register_number]; + if (verbose) dprintk ("aux[%02x]->%08x ", + register_number, *value); + break; + default: + if (verbose) dprintk ("\n"); + return 4; + } + if (verbose) dprintk ("\n"); + return 0; +} + +__inline__ int +xpds_read_control_register (int xpds_num, int register_number, + u32 *value, int which) +{ + return xpds_read_control_register_internal (xpds_num, register_number, + value, which, 0); +} + +__inline__ int +xpds_read_control_register_verbose (int xpds_num, int register_number, + u32 *value, int which) +{ + return xpds_read_control_register_internal (xpds_num, register_number, + value, which, 1); +} + +__inline__ int +xpds_read_control_register_quiet (int xpds_num, int register_number, + u32 *value, int which) +{ + return xpds_read_control_register_internal (xpds_num, register_number, + value, which, 0); +} + +__inline__ static int +xpds_write_control_register_internal (int xpds_num, int register_number, + u32 value, int which, int verbose) +{ + register_number >>= 2; + + if (verbose) dprintk (KERN_DEBUG "%s: ", xpds_devs[xpds_num].name); + + switch (which) { + case MAIN: + if (register_number > NUM_MAIN_CONTROL_REGISTERS) { + return 4; + } + xpds_data[xpds_num].main_control_registers[register_number] = value; + if (verbose) { + dprintk ("%08x->main_ctl[%02x] ", + value, register_number); + } + break; + case RX_FIFO: + if (register_number > NUM_FIFO_CONTROL_REGISTERS) { + return 4; + } + xpds_data[xpds_num].rx_fifo_control_registers[register_number] = value; + if (verbose) dprintk ("%08x->rx_fifo_ctl[%02x] ", + value, register_number); + break; + case TX_FIFO: + if (register_number > NUM_FIFO_CONTROL_REGISTERS) { + return 4; + } + xpds_data[xpds_num].tx_fifo_control_registers[register_number] = value; + if (verbose) dprintk ("%08x->tx_fifo_ctl[%02x] ", + value, register_number); + break; + case RX_DMA: + if (register_number > NUM_DMA_CONTROL_REGISTERS) { + return 4; + } + xpds_data[xpds_num].rx_dma_control_registers[register_number] = value; + if (verbose) dprintk ("%08x->rx_dma_ctl[%02x] ", + value, register_number); + break; + case TX_DMA: + if (register_number > NUM_DMA_CONTROL_REGISTERS) { + return 4; + } + xpds_data[xpds_num].tx_dma_control_registers[register_number] = value; + if (verbose) dprintk ("%08x->tx_dma_ctl[%02x] ", + value, register_number); + break; + case RX_FIFO_DATA: + if (register_number > NUM_FIFO_DATA_REGISTERS) { + return 4; + } + xpds_data[xpds_num].rx_fifo_data_registers[register_number] = value; + if (verbose) dprintk ("%08x->rx_fifo_data[%02x] ", + value, register_number); + break; + case TX_FIFO_DATA: + if (register_number > NUM_FIFO_DATA_REGISTERS) { + if (verbose) dprintk ("\n"); + return 4; + } + xpds_data[xpds_num].tx_fifo_data_registers[register_number] = value; + if (verbose) dprintk ("%08x->tx_fifo_data[%02x] ", + value, register_number); + break; + case AUX: + if (register_number > NUM_AUX_REGISTERS) { + if (verbose) dprintk ("\n"); + return 4; + } + xpds_data[xpds_num].aux_registers[register_number] = value; + if (verbose) dprintk ("%08x->aux[%02x] ", + value, register_number); + break; + default: + if (verbose) dprintk ("\n"); + return 4; + } + if (verbose) dprintk ("\n"); + return 0; +} + +__inline__ int +xpds_write_control_register (int xpds_num, int register_number, + u32 value, int which) +{ + return xpds_write_control_register_internal (xpds_num, register_number, + value, which, 0); +} + +__inline__ int +xpds_write_control_register_verbose (int xpds_num, int register_number, + u32 value, int which) +{ + return xpds_write_control_register_internal (xpds_num, register_number, + value, which, 1); +} + +__inline__ int +xpds_write_control_register_quiet (int xpds_num, int register_number, + u32 value, int which) +{ + return xpds_write_control_register_internal (xpds_num, register_number, + value, which, 0); +} + +/* + * Return 1 if the hardware version is >= major.minor, unless + * the hardware version is the uninitialized 0xff.0xff value, + * which is assumed to be the lowest revision. Return 0 otherwise. + */ +static int +xpds_is_hardware_version (int card_num, int major, int minor) +{ + if (xpds_data[card_num].serial_data.hardware_version[0] == 0xff && + xpds_data[card_num].serial_data.hardware_version[1] == 0xff) { + return 0; + } + if (xpds_data[card_num].serial_data.hardware_version[0] > major) { + return 1; + } + if (xpds_data[card_num].serial_data.hardware_version[0] == major && + xpds_data[card_num].serial_data.hardware_version[1] >= minor) { + return 1; + } + return 0; +} + +static int +xpds_init_descriptors (int card_num) +{ + u32 bus_addr; + + dprintk (KERN_DEBUG "xpds_init_descriptors (%d)\n", card_num); + /* + * lock? + */ + + /* + * Set maximum packet length + */ + dprintk (KERN_DEBUG "setting maximum packet length to %u (0x%x)\n", + xpds_max_packet_length, xpds_max_packet_length); + xpds_write_control_register (card_num, XPDS_MCR_PACKH, + (xpds_max_packet_length >> 8) & 0xff, MAIN); + xpds_write_control_register (card_num, XPDS_MCR_PACKL, + xpds_max_packet_length & 0xff, MAIN); + + /* + * Initialize descriptor + */ + { + volatile xpds_rxtx_list_t *rx_ptr, *tx_ptr; + int i; + + rx_ptr = xpds_data[card_num].rx_dma_list; + tx_ptr = xpds_data[card_num].tx_dma_list; + for (i = 0; i < NUM_DESC; i ++) { + rx_ptr->control = + RXTX_CONTROL__NEXT_VALID | RXTX_CONTROL__HWGO; + rx_ptr = rx_ptr->next; + tx_ptr->control = + RXTX_CONTROL__NEXT_VALID; + tx_ptr = tx_ptr->next; + } + } + + /* + * Initialize pointers to receive and transmit buffers + * that were allocated on module initialization (by + * allocate_rxtx_buffers() ). + */ + bus_addr = virt_to_bus (xpds_data[card_num].rx_dma_list); + dprintk (KERN_DEBUG "%s: writing RX DMA status address %p (bus address %08x)\n", xpds_devs[card_num].name, xpds_data[card_num].rx_dma_list, bus_addr); + xpds_write_control_register (card_num, XPDS_DMA_STAT_ADDR, + bus_addr, RX_DMA); + bus_addr = virt_to_bus (xpds_data[card_num].tx_dma_list); + dprintk (KERN_DEBUG "%s: writing TX DMA status address %p (bus address %08x)\n", xpds_devs[card_num].name, xpds_data[card_num].tx_dma_list, bus_addr); + xpds_write_control_register (card_num, XPDS_DMA_STAT_ADDR, + bus_addr, TX_DMA); + xpds_data[card_num].current_rx_dma = xpds_data[card_num].rx_dma_list; + xpds_data[card_num].current_tx_dma = xpds_data[card_num].tx_dma_list; + xpds_data[card_num].current_hw_tx_dma = xpds_data[card_num].tx_dma_list; + xpds_data[card_num].current_hw_rx_dma = xpds_data[card_num].rx_dma_list; + + /* + * release lock? + */ + + return 0; +} + +static int +xpds_dma_enable (int card_num) +{ + u32 value; + + dprintk (KERN_DEBUG "xpds_dma_enable (%d)\n", card_num); + + /* + * Check SDSL speed. If speed mode had been set to 0 + * (autonegotiation mode), then the speed needs to be + * read before the DMA is enabled, in order to determine + * DMA low rate bug is applicable. + */ + if (xpds_data[card_num].is_sdsl) { + u32 waituntil = jiffies + 90 * HZ; + + do { + u32 sdsl_mode, speed_mode; + int rc; + + rc = xpds_get_sdsl_mode (card_num, &sdsl_mode); + dprintk ("%s: SDSL mode is %08x\n", + xpds_devs[card_num].name, sdsl_mode); + speed_mode = sdsl_mode & XPDS_SDSL_MODE__SPEED_MASK; + dprintk ("%s: SDSL speed is %d\n", + xpds_devs[card_num].name, speed_mode << 3); + xpds_data[card_num].sdsl_speed = speed_mode << 3; + schedule_if_no_interrupt (card_num); + } while (xpds_data[card_num].sdsl_speed == 0 && + jiffies < waituntil); + printk ("%s: SDSL speed is %d kbps.\n", + xpds_devs[card_num].name, + xpds_data[card_num].sdsl_speed); + } + + /* + * Disable all interrupts + */ + xpds_write_control_register (card_num, XPDS_DMA_CLR_MASK, + XPDS_DMA_MASK__DONE | XPDS_DMA_MASK__FLOW | + XPDS_DMA_MASK__LONG | XPDS_DMA_MASK__ABORT | + XPDS_DMA_MASK__CRC, RX_DMA); + xpds_write_control_register (card_num, XPDS_DMA_CLR_MASK, + XPDS_DMA_MASK__DONE | XPDS_DMA_MASK__FLOW | + XPDS_DMA_MASK__LONG | XPDS_DMA_MASK__ABORT | + XPDS_DMA_MASK__CRC, TX_DMA); + xpds_write_control_register (card_num, XPDS_MCR_MASK_CLR, + 0xff /* XPDS_MCR_MASK__RX_FIFO | XPDS_MCR_MASK__TX_FIFO */, MAIN); + + xpds_write_control_register (card_num, XPDS_MCR_INT_CLR, + 0xff, MAIN); + + xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR, + &value, MAIN); + dprintk (KERN_DEBUG "%s: interrupt mask is %02x\n", xpds_devs[card_num].name, value); + + /* + * Enable receive and transmit DMAs. The transmit DMA is only + * enabled if we do not encounter the low bit rate bug. + */ + if (! RX_DMA_LOW_RATE_BUG (card_num) ) { + if (! xpds_data[card_num].has_rx_dma_burst_bug) { + dprintk (KERN_INFO "%s: enabling burst on RX DMA\n", + xpds_devs[card_num].name); + xpds_write_control_register (card_num, XPDS_DMA_CONFIG, + XPDS_DMA_CONFIG__BURST_ENABLE | XPDS_DMA_CONFIG__ENABLE, + RX_DMA); + } else { + xpds_write_control_register (card_num, XPDS_DMA_CONFIG, + XPDS_DMA_CONFIG__ENABLE, RX_DMA); + } + } + if (! TX_DMA_LOW_RATE_BUG (card_num) ) { + if (! xpds_data[card_num].has_tx_dma_burst_bug) { + dprintk (KERN_INFO "%s: enabling burst on TX DMA\n", + xpds_devs[card_num].name); + xpds_write_control_register (card_num, XPDS_DMA_CONFIG, + XPDS_DMA_CONFIG__BURST_ENABLE | XPDS_DMA_CONFIG__ENABLE, + TX_DMA); + } else { + xpds_write_control_register (card_num, XPDS_DMA_CONFIG, + XPDS_DMA_CONFIG__ENABLE, TX_DMA); + } + } + + /* + * Enable DMA interrupts. The transmit DMA interrupt is only + * enabled if the low bit rate bug is not encountered. + */ + if (! RX_DMA_LOW_RATE_BUG (card_num) ) { + xpds_write_control_register (card_num, XPDS_DMA_SET_MASK, + XPDS_DMA_MASK__DONE, RX_DMA); + xpds_read_control_register (card_num, XPDS_DMA_SET_STAT, + &value, RX_DMA); + dprintk (KERN_DEBUG "%s: RX DMA status is %02x\n", + xpds_devs[card_num].name, value); + } + + if (! TX_DMA_LOW_RATE_BUG (card_num) ) { + xpds_write_control_register (card_num, XPDS_DMA_SET_MASK, + XPDS_DMA_MASK__DONE, TX_DMA); + xpds_read_control_register (card_num, XPDS_DMA_SET_STAT, + &value, TX_DMA); + dprintk (KERN_DEBUG "%s: TX DMA status is %02x\n", + xpds_devs[card_num].name, value); + } + + if (xpds_data[card_num].is_sdsl) { + u32 mask; + + mask = XPDS_MCR_MASK__EXT; + if (! TX_DMA_LOW_RATE_BUG (card_num)) { + mask |= XPDS_MCR_MASK__TX_DMA; + } + if (! RX_DMA_LOW_RATE_BUG (card_num)) { + mask |= XPDS_MCR_MASK__RX_DMA; + } else { + mask |= XPDS_MCR_MASK__RX_FIFO; + } + xpds_write_control_register (card_num, + XPDS_MCR_MASK_SET, mask, MAIN); + } else { + xpds_write_control_register (card_num, XPDS_MCR_MASK_SET, + XPDS_MCR_MASK__RX_DMA | XPDS_MCR_MASK__TX_DMA | + XPDS_MCR_MASK__RXCI, MAIN); + } + xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR, + &value, MAIN); +#if DEBUG + dprintk (KERN_DEBUG "%s: interrupt mask is %02x", xpds_devs[card_num].name, value); + if (value & XPDS_MCR_MASK__RX_FIFO) dprintk (" RX_FIFO"); + if (value & XPDS_MCR_MASK__TX_FIFO) dprintk (" TX_FIFO"); + if (value & XPDS_MCR_MASK__RX_DMA) dprintk (" RX_DMA"); + if (value & XPDS_MCR_MASK__TX_DMA) dprintk (" TX_DMA"); + if (value & XPDS_MCR_MASK__PCI_ERROR) dprintk (" PCI_ERROR"); + if (value & XPDS_MCR_MASK__EXT) dprintk (" EXT"); + if (value & XPDS_MCR_MASK__RXCI) dprintk (" RXCI"); + dprintk ("\n"); +#endif + + /* + * Let the RX DMA begin (write to hunt bit). + * If RX DMA bug, reset and unreset the RX FIFO instead. + */ + if (RX_DMA_LOW_RATE_BUG (card_num)) { + xpds_data[card_num].current_rx_fifo = 0; + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, + XPDS_FCR_CONFIG__RESET, RX_FIFO); + udelay (20); + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, + 0, RX_FIFO); + } else { + xpds_write_control_register (card_num, XPDS_DMA_GO, + XPDS_DMA_GO__HUNT, RX_DMA); + } + + /* + * Reset and unreset to enable TX FIFO mode instead of + * TX DMA to work around the TX DMA bug at < 400 Kbps. + */ + if (TX_DMA_LOW_RATE_BUG (card_num)) { + xpds_data[card_num].current_tx_fifo = 0; + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, + XPDS_FCR_CONFIG__RESET, TX_FIFO); + udelay (20); + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, + 0, TX_FIFO); + } + + return 0; +} + +static int +xpds_dma_disable (int card_num) +{ + u32 value; + + dprintk (KERN_DEBUG "xpds_dma_disable (%d)\n", card_num); + + /* + * Reset the DMAs + */ + xpds_write_control_register (card_num, XPDS_DMA_CONFIG, + XPDS_DMA_CONFIG__RESET, RX_DMA); + xpds_write_control_register (card_num, XPDS_DMA_CONFIG, + XPDS_DMA_CONFIG__RESET, TX_DMA); + + /* + * Disable the DMAs + */ + xpds_write_control_register (card_num, XPDS_DMA_CONFIG, 0x0, RX_DMA); + xpds_write_control_register (card_num, XPDS_DMA_CONFIG, 0x0, TX_DMA); + /* + * Disable the DMA interrupts + */ + xpds_write_control_register (card_num, XPDS_DMA_CLR_MASK, + XPDS_DMA_MASK__DONE | XPDS_DMA_MASK__FLOW, RX_DMA); + xpds_write_control_register (card_num, XPDS_DMA_CLR_MASK, + XPDS_DMA_MASK__DONE | XPDS_DMA_MASK__FLOW, TX_DMA); + xpds_write_control_register (card_num, XPDS_MCR_MASK_CLR, + XPDS_MCR_MASK__RX_DMA | XPDS_MCR_MASK__TX_DMA, MAIN); + xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR, + &value, MAIN); + dprintk (KERN_DEBUG "%s: interrupt mask is %02x\n", xpds_devs[card_num].name, value); + + return 0; +} + +#define LED_OFF 0 +#define LED_ON 1 +/* + * Turn on/off the TX or activity LED + */ +__inline__ static void +xpds_tx_led (int card_num, int on) +{ + if (xpds_data[card_num].is_fpga) { + if (on) { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__OE_FPGA_TX_LED, MAIN ); + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_CLR, + XPDS_MCR_GPIO__GP_FPGA_TX_LED, MAIN ); + } else { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__GP_FPGA_TX_LED | + XPDS_MCR_GPIO__OE_FPGA_TX_LED, MAIN ); + } + } else if (xpds_data[card_num].is_sdsl) { + if (on) { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__OE_SDSL_ACT_LED, MAIN); + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_CLR, + XPDS_MCR_GPIO__GP_SDSL_ACT_LED, MAIN); + } else { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__GP_SDSL_ACT_LED | + XPDS_MCR_GPIO__OE_SDSL_ACT_LED, MAIN); + } + } else { + if (on) { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__OE_ASIC_ACT_LED, MAIN); + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_CLR, + XPDS_MCR_GPIO__GP_ASIC_ACT_LED, MAIN); + } else { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__GP_ASIC_ACT_LED | + XPDS_MCR_GPIO__OE_ASIC_ACT_LED, MAIN); + } + } +} + +/* + * Turn on/off the RX or activity LED + */ +__inline__ static void +xpds_rx_led (int card_num, int on) +{ + if (xpds_data[card_num].is_fpga) { + if (on) { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__OE_FPGA_RX_LED, MAIN ); + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_CLR, + XPDS_MCR_GPIO__GP_FPGA_RX_LED, MAIN ); + } else { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__GP_FPGA_RX_LED | + XPDS_MCR_GPIO__OE_FPGA_RX_LED, MAIN ); + } + } else if (xpds_data[card_num].is_sdsl) { + if (on) { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__OE_SDSL_ACT_LED, MAIN); + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_CLR, + XPDS_MCR_GPIO__GP_SDSL_ACT_LED, MAIN); + } else { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__GP_SDSL_ACT_LED | + XPDS_MCR_GPIO__OE_SDSL_ACT_LED, MAIN); + } + } else { + if (on) { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__OE_ASIC_ACT_LED, MAIN); + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_CLR, + XPDS_MCR_GPIO__GP_ASIC_ACT_LED, MAIN); + } else { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__GP_ASIC_ACT_LED | + XPDS_MCR_GPIO__OE_ASIC_ACT_LED, MAIN); + } + } +} + + +/* + * Turn on the link LED + */ +__inline__ static void +xpds_link_led (int card_num, int on) +{ + if (! xpds_data[card_num].is_fpga) { + if (on) { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__OE_ASIC_LINK_LED, MAIN); + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_CLR, + XPDS_MCR_GPIO__GP_ASIC_LINK_LED, MAIN); + } else { + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__GP_ASIC_LINK_LED | + XPDS_MCR_GPIO__OE_ASIC_LINK_LED, MAIN); + } + } +} + +/* + * Resets the XPDS card, including running the PEB 2091 state machine + * into transparent mode by calling xpds_fsm(). + */ +static int +xpds_reset (int card_num) +{ + int rc; + int i; + int speed_mode; + u32 mode; + u32 value; + + dprintk (KERN_DEBUG "%s: xpds_reset\n", xpds_devs[card_num].name); + + rc = xpds_dma_disable (card_num); + if (rc != 0) return rc; + + /* + * Reset the TX FIFO, then unreset it. + */ + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, + XPDS_FCR_CONFIG__RESET, TX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, TX_FIFO); + + /* + * For each FIFO, zero the status. + */ + for (i = 0; i < NUM_FIFO; i ++ ) { + xpds_write_control_register (card_num, XPDS_FCR_STAT, + 0, TX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_INC, + XPDS_FCR_INC__NEXT, TX_FIFO); + } + + /* + * Reset the TX FIFO, then unreset it. + */ + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, + XPDS_FCR_CONFIG__RESET, TX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, TX_FIFO); + + /* + * Reset the RX FIFO, then unreset it. + */ + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, + XPDS_FCR_CONFIG__RESET, RX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, RX_FIFO); + + /* + * For each FIFO, zero the status. + */ + for (i = 0; i < NUM_FIFO; i ++ ) { + xpds_write_control_register (card_num, XPDS_FCR_STAT, + 0, RX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_INC, + XPDS_FCR_INC__NEXT, RX_FIFO); + } + + /* + * Reset the RX FIFO, then unreset it. + */ + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, + XPDS_FCR_CONFIG__RESET, RX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, RX_FIFO); + + /* + * Enable TX and RX HDLC + */ + xpds_write_control_register (card_num, XPDS_MCR_TXCFG, + XPDS_MCR_TXCFG__ENABLE, MAIN); + if (xpds_data[card_num].has_last_byte_bug) { + xpds_write_control_register (card_num, XPDS_MCR_RXCFG, + (xpds_data[card_num].is_fpga ? + XPDS_MCR_RXCFG__ENABLE : + XPDS_MCR_RXCFG__ENABLE | XPDS_MCR_RXCFG__NO_CRC), + MAIN); + } else { + xpds_write_control_register (card_num, XPDS_MCR_RXCFG, + XPDS_MCR_RXCFG__ENABLE, MAIN); + } + + /* + * Set mode register to normal mode. + */ + if (xpds_asic_loopback) { + dprintk (KERN_INFO "%s: setting ASIC loopback\n", xpds_devs[card_num].name); + xpds_write_control_register (card_num, XPDS_MCR_CONFIG, + XPDS_MCR_CONFIG__MODE_LOOPBACK, MAIN); + } else { + xpds_write_control_register (card_num, XPDS_MCR_CONFIG, + XPDS_MCR_CONFIG__MODE_NORMAL, MAIN); + } + + /* + * Turn on activation LED + */ + xpds_write_control_register (card_num, XPDS_MCR_TEST, 1, MAIN); + + /* + * Clear all bits in GPIO + */ + xpds_write_control_register (card_num, XPDS_MCR_GPIO_CLR, 0xff, MAIN); + + /* + * Enable GPIO outputs + */ + xpds_write_control_register (card_num, XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__OE_MASK, MAIN); + + /* + * Enable NT or LT mode + */ + if (xpds_data[card_num].is_fpga) { + xpds_write_control_register (card_num, XPDS_MCR_GPIO_SET, + (xpds_data[card_num].is_lt ? + XPDS_MCR_GPIO__GP_FPGA_LT : + XPDS_MCR_GPIO__GP_FPGA_NT) | + XPDS_MCR_GPIO__OE_MASK, + MAIN); + } + + /* + * Turn off the LEDs + */ + if (xpds_data[card_num].is_fpga) { + xpds_write_control_register (card_num, XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__GP_FPGA_TX_LED | + XPDS_MCR_GPIO__GP_FPGA_RX_LED | + XPDS_MCR_GPIO__OE_FPGA_TX_LED | + XPDS_MCR_GPIO__OE_FPGA_RX_LED, MAIN); + } else { + xpds_write_control_register (card_num, XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__GP_ASIC_ACT_LED | + XPDS_MCR_GPIO__GP_ASIC_LINK_LED | + XPDS_MCR_GPIO__OE_ASIC_ACT_LED | + XPDS_MCR_GPIO__OE_ASIC_LINK_LED, MAIN); + } + + /* + * Enable interrupt which notifies a change in the indication + * code of the PEB 2091 (RXCI) or the interrupt which indicates + * a change in the status of the SDSL (EXT). + */ + if (xpds_data[card_num].is_sdsl) { + xpds_write_control_register (card_num, XPDS_MCR_MASK_SET, + XPDS_MCR_MASK__EXT, MAIN); + } else { + xpds_write_control_register (card_num, XPDS_MCR_MASK_SET, + XPDS_MCR_MASK__RXCI, MAIN); + } + xpds_read_control_register (card_num, XPDS_MCR_MASK_SET, + &value, MAIN); + dprintk (KERN_DEBUG "%s: interrupt mask is %02x\n", + xpds_devs[card_num].name, value); + + rc = xpds_init_descriptors (card_num); + if (rc != 0) return rc; + + mode = 0; + if (xpds_data[card_num].is_sdsl) { + u32 sdsl_mode; + + /* + * Reset and unreset the SDSL, then get the SDSL mode. + */ + rc = xpds_reset_sdsl (card_num); + if (rc != 0) return rc; + rc = xpds_start_sdsl (card_num); + if (rc != 0) return rc; + + rc = xpds_get_sdsl_mode (card_num, &sdsl_mode); + dprintk ("%s: SDSL mode is %08x\n", xpds_devs[card_num].name, sdsl_mode); + speed_mode = sdsl_mode & XPDS_SDSL_MODE__SPEED_MASK; + dprintk ("%s: SDSL speed is %d\n", xpds_devs[card_num].name, speed_mode << 3); + xpds_data[card_num].sdsl_speed = speed_mode << 3; + } else { + /* + * Take the PEB 2091 out of reset (B1, B2, and D channels for + * 144k operation). Only for IDSL card. + */ + speed_mode = xpds_data[card_num].speed_mode; + if (speed_mode & XPDS_MODE_B1) { + if (speed_mode & XPDS_MODE_B2) { + if (speed_mode & XPDS_MODE_D) { + dprintk ("%s: selecting B1B2D (144k)\n", xpds_devs[card_num].name); + mode |= XPDS_MCR_TXCI__PMD_CONFIG_B1B2D; + } else { + dprintk ("%s: selecting B1B2 (128k)\n", xpds_devs[card_num].name); + mode |= XPDS_MCR_TXCI__PMD_CONFIG_B1B2; + } + } else { + dprintk ("%s: selecting B1 (64k)\n", xpds_devs[card_num].name); + mode |= XPDS_MCR_TXCI__PMD_CONFIG_B1; + } + } else if (speed_mode & XPDS_MODE_B2) { + dprintk ("%s: selecting B2 (64k)\n", xpds_devs[card_num].name); + mode |= XPDS_MCR_TXCI__PMD_CONFIG_B2; + } else { + dprintk (KERN_ERR "%s: invalid mode %d\n", xpds_devs[card_num].name, speed_mode); + dprintk ("%s: selecting B1B2D (144k)\n", xpds_devs[card_num].name); + mode |= XPDS_MCR_TXCI__PMD_CONFIG_B1B2D; + } + xpds_write_control_register (card_num, XPDS_MCR_TXCI, mode, MAIN); + } + + /* + * Set the PMD enable bit. + */ + if (! xpds_data[card_num].is_sdsl) { + xpds_read_control_register (card_num, XPDS_MCR_TXCI, &mode, MAIN); + } + mode |= XPDS_MCR_TXCI__PMD_ENABLE | XPDS_MCR_TXCI__PMD_RESQ; + xpds_write_control_register (card_num, XPDS_MCR_TXCI, mode, MAIN); + + if (xpds_data[card_num].is_sdsl) { + rc = xpds_mailbox_write (card_num, XPDS_MBX_START_BITPUMP, 0); + DELAY_HZ (3 * HZ / 2, card_num); + if (xpds_external_loopback) { + xpds_sdsl_loopback (card_num); + DELAY (2, card_num); + } + } else { + /* + * need to control state machine for non-SDSL cards + */ + if (xpds_data[card_num].is_lt) { + rc = xpds_fsm_lt (card_num, mode, xpds_guard_time); + } else { + rc = xpds_fsm_nt (card_num, mode, xpds_guard_time); + } + if (rc != 0) return rc; + } + + /* + * Turn on the link LED + */ + if (! xpds_data[card_num].is_sdsl) xpds_link_led (card_num, 1); + + /* + * Reset the FIFOs. + */ + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, + XPDS_FCR_CONFIG__RESET, TX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, + XPDS_FCR_CONFIG__RESET, RX_FIFO); + + DELAY_HZ (1, card_num); + + /* + * For each FIFO, zero the status. + */ + for (i = 0; i < NUM_FIFO; i ++ ) { + xpds_write_control_register (card_num, XPDS_FCR_STAT, + 0, TX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_INC, + XPDS_FCR_INC__NEXT, TX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_STAT, + 0, RX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_INC, + XPDS_FCR_INC__NEXT, RX_FIFO); + } + + /* + * Unreset the FIFOs. + */ + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, TX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_CONFIG, 0, RX_FIFO); + + xpds_read_control_register (card_num, XPDS_FCR_CONFIG, &value, RX_FIFO); + dprintk (KERN_DEBUG "%s: RX FIFO config register is %08x\n", + xpds_devs[card_num].name, value); + + /* + * For IDSL, physical layer is up. + * For SDSL, wait until interrupt occurs to let us know. + */ + if (! xpds_data[card_num].is_sdsl) { + struct net_device *dev; + + printk ("%s: physical link came up\n", xpds_devs[card_num].name); + xpds_data[card_num].physical_up = 1; + rc = xpds_dma_enable (card_num); + dev = &(xpds_devs[card_num]); + netif_start_queue(dev); + if (rc != 0) return rc; + } + + return 0; +} + +/* + * Called from interrupt handler to receive a packet. + */ +static void +xpds_rx (struct net_device *dev, int len, volatile u8 *buf) +{ + struct sk_buff *skb; + int card_num; + struct frad_local *fp; + short dlci; + xpds_data_t *data_ptr; + int i; + + card_num = dev - xpds_devs; + dprintk (KERN_DEBUG "xpds_rx (%p (%d), %d, %p)\n", + dev, card_num, len, buf); + xpds_rx_led (card_num, LED_ON); + + data_ptr = dev->priv; + fp = &(data_ptr->frad_data); + dlci = data_ptr->dlci; + + ddprintk (KERN_DEBUG "data_ptr = %p, fp = %p\n", data_ptr, fp); + +#if DEBUG + { + int i, debuglen; + + debuglen = len; + if (debuglen > sizeof (struct frhdr)) { + debuglen = sizeof (struct frhdr); + } + dpprintk (KERN_DEBUG "frame header (%p) received:", buf); + for (i = 0; i < debuglen; i ++) { + dpprintk (" %02x", buf[i]); + } + dpprintk ("\n"); + } + { + int i, debuglen; + + debuglen = len; + if (debuglen > 256) debuglen = 256; + dpprintk (KERN_DEBUG "data (%p) received:", + buf + sizeof (struct frhdr)); + for (i = sizeof (struct frhdr); i < debuglen ; i ++) { + dpprintk (" %02x", buf[i]); + } + if (len > debuglen) dpprintk (" ..."); + dpprintk ("\n"); + } +#endif + + if (dlci == 0) { + data_ptr->stats.rx_errors ++; + nrprintk (KERN_ERR "xpds_rx failed -- DLCI 0???\n"); + xpds_rx_led (card_num, LED_OFF); + return; + } + + for (i = 0; i < CONFIG_DLCI_MAX; i ++) { + if (fp->dlci[i] == dlci) break; + } + if (i == CONFIG_DLCI_MAX) { + data_ptr->stats.rx_errors ++; + nrprintk (KERN_ERR "xpds_rx failed -- invalid DLCI %d\n", dlci); + xpds_rx_led (card_num, LED_OFF); + return; + } + ddprintk (KERN_DEBUG "dlci = %d, i = %d\n", dlci, i); + + skb = dev_alloc_skb (len); + if (skb == NULL) { + printk (KERN_ERR "%s: unable to allocate skb for packet reception\n", dev->name); + return; + } + memcpy (skb_put (skb, len), (void *)buf, len); + skb->dev = dev; + /* skb->protocol = eth_type_trans (skb, dev); */ + skb->ip_summed = CHECKSUM_NONE; /* software will do checksum */ + xpds_dlci_receive (skb, dev); + /* netif_rx (skb); */ + + data_ptr->stats.rx_packets ++; + data_ptr->stats.rx_bytes ++; + + dprintk (KERN_DEBUG "xpds_rx done\n"); + + dev->last_rx = jiffies; + xpds_rx_led (card_num, LED_OFF); +} + +static struct tq_struct *xpds_reset_bh_tasks; + +/* + * A bottom half function that is meant to be put on a task queue + * if the link goes down (i.e. PEB 2091 falls out of transparent mode) + * and either a transmit is attempted or an RXCI interrupt (indicating + * that the other side may be back up) is received. + */ +static void +xpds_reset_bh (void *p) +{ + int rc; + int card_num; + struct net_device *dev; + + dev = (struct net_device *)p; + + card_num = dev - xpds_devs; + + dprintk (KERN_DEBUG "%s: retrying physical link / state machine, calling xpds_reset\n", xpds_devs[card_num].name); + + /* + * The guard times should be different on LT vs. NT + * when retrying. Should the guard times change every + * retry? + */ + xpds_guard_time = xpds_data[card_num].is_lt ? 3 : 2; + rc = xpds_reset (card_num); + if (rc == 0) { + printk ("%s: physical link is up\n", xpds_devs[card_num].name); + if (! xpds_data[card_num].is_sdsl) xpds_link_led (card_num, 1); + netif_start_queue(dev); + } + xpds_data[card_num].physical_retrying = 0; +} + +#if DEBUG +static void +xpds_print_interrupt_type (int card_num, u32 interrupt) +{ + u32 value; + + xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR, + &value, MAIN); + dprintk (KERN_DEBUG "%s: interrupt mask is %02x", xpds_devs[card_num].name, value); + if (value & XPDS_MCR_MASK__RX_FIFO) dprintk (" RX_FIFO"); + if (value & XPDS_MCR_MASK__TX_FIFO) dprintk (" TX_FIFO"); + if (value & XPDS_MCR_MASK__RX_DMA) dprintk (" RX_DMA"); + if (value & XPDS_MCR_MASK__TX_DMA) dprintk (" TX_DMA"); + if (value & XPDS_MCR_MASK__PCI_ERROR) dprintk (" PCI_ERROR"); + if (value & XPDS_MCR_MASK__EXT) dprintk (" EXT"); + if (value & XPDS_MCR_MASK__RXCI) dprintk (" RXCI"); + dprintk ("\n"); + dprintk (KERN_DEBUG "%s: received interrupt %02x", + xpds_devs[card_num].name, interrupt); + if (interrupt & XPDS_MCR_MASK__RX_FIFO) dprintk (" RX_FIFO"); + if (interrupt & XPDS_MCR_MASK__TX_FIFO) dprintk (" TX_FIFO"); + if (interrupt & XPDS_MCR_MASK__RX_DMA) dprintk (" RX_DMA"); + if (interrupt & XPDS_MCR_MASK__TX_DMA) dprintk (" TX_DMA"); + if (interrupt & XPDS_MCR_MASK__PCI_ERROR) dprintk (" PCI_ERROR"); + if (interrupt & XPDS_MCR_MASK__EXT) dprintk (" EXT"); + if (interrupt & XPDS_MCR_MASK__RXCI) dprintk (" RXCI"); + dprintk ("\n"); + xpds_read_control_register (card_num, XPDS_DMA_SET_MASK, + &value, RX_DMA); + dprintk (KERN_DEBUG "%s: RX DMA mask is %02x\n", xpds_devs[card_num].name, value); + xpds_read_control_register (card_num, XPDS_DMA_SET_MASK, + &value, TX_DMA); + dprintk (KERN_DEBUG "%s: TX DMA mask is %02x\n", xpds_devs[card_num].name, value); +} + +static void +xpds_print_fifo_data (int card_num, u32 interrupt) +{ + u32 value; + + if ((xpds_debug_level & (DEBUG_MAIN | DEBUG_DETAILED)) != (DEBUG_MAIN | DEBUG_DETAILED)) return; + + if (interrupt & XPDS_MCR_INT__RX_FIFO) { + int i, rc; + u8 *vptr; + const volatile xpds_rxtx_list_t *rxtx; + + ddprintk (KERN_DEBUG "RX FIFO control registers:"); + for (i = 0; i < NUM_FIFO_CONTROL_REGISTERS; i += 4) { + if (i % 16 == 0) { + ddprintk ("\n"); + ddprintk (KERN_DEBUG "%08x:", i); + } + rc = xpds_read_control_register (card_num, i, &value, RX_FIFO); + if (rc > 0) { + ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc); + } else { + vptr = (u8 *) &value; + ddprintk (" %02x %02x %02x %02x", + vptr[0], vptr[1], vptr[2], vptr[3]); + } + } + ddprintk ("\n"); + + ddprintk (KERN_DEBUG "RX FIFO data registers:"); + for (i = 0; i < NUM_FIFO_DATA_REGISTERS; i += 4) { + if (i % 16 == 0) { + ddprintk ("\n"); + ddprintk (KERN_DEBUG "%08x:", i); + } + rc = xpds_read_control_register (card_num, i, &value, RX_FIFO_DATA); + if (rc > 0) { + ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc); + } else { + vptr = (u8 *) &value; + ddprintk (" %02x %02x %02x %02x", + vptr[0], vptr[1], vptr[2], vptr[3]); + } + } + ddprintk ("\n"); + + ddprintk (KERN_DEBUG "TX FIFO control registers:"); + for (i = 0; i < NUM_FIFO_CONTROL_REGISTERS; i += 4) { + if (i % 16 == 0) { + ddprintk ("\n"); + ddprintk (KERN_DEBUG "%08x:", i); + } + rc = xpds_read_control_register (card_num, i, &value, TX_FIFO); + if (rc > 0) { + ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc); + } else { + vptr = (u8 *) &value; + ddprintk (" %02x %02x %02x %02x", + vptr[0], vptr[1], vptr[2], vptr[3]); + } + } + ddprintk ("\n"); + + ddprintk (KERN_DEBUG "TX FIFO data registers:"); + for (i = 0; i < NUM_FIFO_DATA_REGISTERS; i += 4) { + if (i % 16 == 0) { + ddprintk ("\n"); + ddprintk (KERN_DEBUG "%08x:", i); + } + rc = xpds_read_control_register (card_num, i, &value, TX_FIFO_DATA); + if (rc > 0) { + ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc); + } else { + vptr = (u8 *) &value; + ddprintk (" %02x %02x %02x %02x", + vptr[0], vptr[1], vptr[2], vptr[3]); + } + } + ddprintk ("\n"); + + ddprintk (KERN_DEBUG "RX DMA control registers:"); + for (i = 0; i < NUM_DMA_CONTROL_REGISTERS; i += 4) { + if (i % 16 == 0) { + ddprintk ("\n"); + ddprintk (KERN_DEBUG "%08x:", i); + } + rc = xpds_read_control_register (card_num, i, &value, RX_DMA); + if (rc > 0) { + ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc); + } else { + vptr = (u8 *) &value; + ddprintk (" %02x %02x %02x %02x", + vptr[0], vptr[1], vptr[2], vptr[3]); + } + } + ddprintk ("\n"); + + ddprintk (KERN_DEBUG "TX DMA control registers:"); + for (i = 0; i < NUM_DMA_CONTROL_REGISTERS; i += 4) { + if (i % 16 == 0) { + ddprintk ("\n"); + ddprintk (KERN_DEBUG "%08x:", i); + } + rc = xpds_read_control_register (card_num, i, &value, TX_DMA); + if (rc > 0) { + ddprintk (" E%d E%d E%d E%d", rc, rc, rc, rc); + } else { + vptr = (u8 *) &value; + ddprintk (" %02x %02x %02x %02x", + vptr[0], vptr[1], vptr[2], vptr[3]); + } + } + ddprintk ("\n"); + + for (i = 0, rxtx = xpds_data[card_num].current_rx_dma; + i < NUM_DESC; + i ++, rxtx = rxtx->next) { + ddprintk (KERN_DEBUG "rx[%d](%p)->control = %08x", + i, rxtx, rxtx->control); + if (rxtx->control & RXTX_CONTROL__SWGO) { + ddprintk (" SWGO"); + } + if (rxtx->control & RXTX_CONTROL__HWGO) { + ddprintk (" HWGO"); + } + if (rxtx->control & RXTX_CONTROL__NEXT_VALID) { + ddprintk (" NEXT_VALID"); + } + ddprintk ("\n"); + if (! (rxtx->control & RXTX_CONTROL__NEXT_VALID)) { + break; + } + } + for (i = 0, rxtx = xpds_data[card_num].current_tx_dma; + i < NUM_DESC; + i ++, rxtx = rxtx->next) { + ddprintk (KERN_DEBUG "tx[%d](%p)->control = %08x", + i, rxtx, rxtx->control); + if (rxtx->control & RXTX_CONTROL__SWGO) { + ddprintk (" SWGO"); + } + if (rxtx->control & RXTX_CONTROL__HWGO) { + ddprintk (" HWGO"); + } + if (rxtx->control & RXTX_CONTROL__NEXT_VALID) { + ddprintk (" NEXT_VALID"); + } + ddprintk ("\n"); + if (! (rxtx->control & RXTX_CONTROL__NEXT_VALID)) { + break; + } + } + } + xpds_read_control_register (card_num, XPDS_MCR_RXCFG, &value, MAIN); + ddprintk ("%s: RX HDLC config is %08x\n", xpds_devs[card_num].name, value); + xpds_read_control_register (card_num, XPDS_MCR_TXCFG, &value, MAIN); + ddprintk ("%s: TX HDLC config is %08x\n", xpds_devs[card_num].name, value); +} +#endif + +/* + * For SDSL cards with the TX DMA low rate bug, do the transfer from the + * TX DMA descriptor to the TX FIFO. The TX FIFO interrupt indicates that + * the TX FIFO is ready to receive up to 32 bytes of data; this function + * simulates the hardware's method of copying data from the TX DMA to the + * TX FIFO. + * + * Note that this function keep stuffing the TX FIFO as long as it is + * ready (i.e. the TX FIFO interrupt bit shows 1), since if there is + * too long a delay for the next piece of the packet to be put in the + * TX FIFO, the packet will be aborted. + */ +#define TX_FIFO_DO_MORE 1 +#define TX_FIFO_DONE 2 +__inline__ static int +xpds_tx_fifo_interrupt (int card_num) +{ + volatile u32 *tx_fifo_data_pointer; + volatile u8 *data; + int tx_len, copy_len, i; + u32 control, length, offset; + u32 value; + struct timeval tv1, tv2; + int tvdiff; + + /* + * If there is no data in TX DMA (i.e. no more packets to + * send), disallow the TX FIFO interrupt (which will be + * enabled the next time a packet is transmitted by + * xpds_hw_tx() ) and be done. + */ + control = xpds_data[card_num].current_hw_tx_dma->control; + if (! (control & RXTX_CONTROL__HWGO) ) { + xpds_write_control_register (card_num, + XPDS_MCR_MASK_CLR, XPDS_MCR_INT__TX_FIFO, MAIN); + return TX_FIFO_DONE; + } + + /* + * Check TX FIFO interrupt to see if hardware is + * ready for the next TX FIFO to be filled. + * If not, allow TX FIFO interrupts (so that when the + * TX FIFO is ready, we come back here) and be done. + */ + xpds_write_control_register (card_num, XPDS_MCR_INT_CLR, + XPDS_MCR_INT__TX_FIFO, MAIN); + xpds_read_control_register (card_num, XPDS_MCR_INT_CLR, + &value, MAIN); + if (! (value & XPDS_MCR_INT__TX_FIFO)) { + dprintk (KERN_DEBUG "%s: TX FIFO interrupt not asserted\n", xpds_devs[card_num].name); + xpds_write_control_register (card_num, + XPDS_MCR_MASK_SET, XPDS_MCR_INT__TX_FIFO, MAIN); + xpds_write_control_register (card_num, XPDS_MCR_INT_CLR, + XPDS_MCR_INT__TX_FIFO, MAIN); + return TX_FIFO_DONE; + } + do_gettimeofday (&tv1); + + /* + * Get needed information about the length, offset, + * and data pointer in the TX DMA descriptor. Note + * that the length in the control field is one less + * than the actual length; we add 1 here to get the + * true length. + */ + length = (control & RXTX_CONTROL__PACKET_LENGTH_MASK) + 1; + offset = xpds_data[card_num].current_hw_tx_dma->offset; + data = xpds_data[card_num].current_hw_tx_dma->buffer + offset; + +#if DEBUG + xpds_read_control_register (card_num, XPDS_FCR_CONFIG, + &value, TX_FIFO); + dprintk (KERN_DEBUG "%s: TX FIFO config is %02x\n", + xpds_devs[card_num].name, value); +#endif + + /* + * Put data in the appropriate TX FIFO. Data must be + * set / copied into the TX FIFO data registers in 32 + * bit chunks, not 8 bit chunks. + */ + tx_fifo_data_pointer = (volatile u32 *) (xpds_data[card_num].tx_fifo_data_registers + FIFO_SIZE * xpds_data[card_num].current_tx_fifo); + dprintk (KERN_DEBUG "%s: current_tx_fifo=%d, tx_fifo_data_pointer=%p\n", xpds_devs[card_num].name, xpds_data[card_num].current_tx_fifo, tx_fifo_data_pointer); + + if (length - offset > FIFO_SIZE * sizeof (u32)) { + tx_len = FIFO_SIZE * sizeof (u32); + } else { + tx_len = length - offset; + } + + copy_len = tx_len % 4 == 0 ? tx_len : (tx_len + 4) / 4 * 4; + for (i = 0; i < copy_len / 4; i ++) { + tx_fifo_data_pointer[i] = *((u32 *)data + i); + } + + dprintk (KERN_DEBUG "%s: transmitting %d bytes through FIFO (%d left)\n", xpds_devs[card_num].name, tx_len, length - offset); +#if DEBUG + dprintk (KERN_DEBUG "TX FIFO data (%p):", tx_fifo_data_pointer); + for (i = 0; i < tx_len; i ++) { + dprintk (" %02x", ((volatile u8 *)tx_fifo_data_pointer)[i]); + } + dprintk ("\n"); +#endif + + /* + * Increment the current TX FIFO number and data pointer. + * Decrement the length. + */ + xpds_data[card_num].current_tx_fifo ++; + xpds_data[card_num].current_tx_fifo %= NUM_FIFO; + offset += tx_len; + xpds_data[card_num].current_hw_tx_dma->offset = offset; + + /* + * Tell the hardware that the data is ready. Note that + * the length is reduced by 1 for the hardware. + */ + tx_len -= 1; + if (length - offset <= 0) { + tx_len |= XPDS_FCR_STAT__LAST; + xpds_data[card_num].current_hw_tx_dma->control &= + ~ RXTX_CONTROL__HWGO; + xpds_data[card_num].current_hw_tx_dma->control |= + RXTX_CONTROL__SWGO; + xpds_data[card_num].current_hw_tx_dma->offset = 0; + xpds_data[card_num].current_hw_tx_dma = + xpds_data[card_num].current_hw_tx_dma->next; + } + + /* + * Note that due to the bug, we cannot use the FIFO registers + * until at least 10 microseconds after the FIFO ready (interrupt) + * is set. + */ + do_gettimeofday (&tv2); + tvdiff = tv2.tv_usec - tv1.tv_usec + + (tv2.tv_sec - tv1.tv_sec) * 1000000; + if (tvdiff >= 0 && tvdiff < 10) { + udelay (10 - tvdiff); + } + + dprintk (KERN_DEBUG "%s: writing %02x to FIFO stat\n", + xpds_devs[card_num].name, tx_len); + xpds_write_control_register (card_num, XPDS_FCR_STAT, + tx_len, TX_FIFO); + xpds_write_control_register (card_num, XPDS_FCR_INC, + XPDS_FCR_INC__NEXT, TX_FIFO); + +#if DEBUG + xpds_read_control_register (card_num, XPDS_FCR_CONFIG, + &value, TX_FIFO); + dprintk (KERN_DEBUG "%s: TX FIFO config is %02x after FCR_INC\n", xpds_devs[card_num].name, value); + xpds_write_control_register (card_num, XPDS_MCR_INT_CLR, + XPDS_MCR_INT__TX_FIFO, MAIN); + xpds_read_control_register (card_num, XPDS_MCR_INT_CLR, + &value, MAIN); + dprintk (KERN_DEBUG "%s: interrupt value is %02x\n", + xpds_devs[card_num].name, value); +#endif + return TX_FIFO_DO_MORE; +} + +/* + * For SDSL cards with the RX DMA low rate bug, do the transfer from the + * RX FIFO descriptor to the RX DMA. The RX FIFO interrupt indicates that + * there is data in the RX FIFO that should be copied to the RX DMA. + * + * Note that this function keep taking from the RX FIFO as long as it is + * ready (i.e. the RX FIFO interrupt bit shows 1), since if there is + * too long a delay, pieces of the packet may be lost. + */ +#define RX_FIFO_DO_MORE 1 +#define RX_FIFO_DONE 2 +__inline__ static int +xpds_rx_fifo_interrupt (int card_num) +{ + volatile u32 *rx_fifo_data_pointer; + volatile u8 *data; + int rx_len, rx_last, copy_len, i; + u32 control, length, offset; + u32 value; + struct timeval tv1, tv2; + int tvdiff; + + /* + * If the RX DMA buffer is not ready for the hardware, + * stop. The packet is likely to drop in this case... + */ + control = xpds_data[card_num].current_hw_rx_dma->control; + if (! (control & RXTX_CONTROL__HWGO) ) { + xpds_write_control_register (card_num, + XPDS_MCR_MASK_CLR, XPDS_MCR_INT__RX_FIFO, MAIN); + return RX_FIFO_DONE; + } + + /* + * Check RX FIFO interrupt to see if hardware hass + * filled the next RX FIFO. + * If not, allow RX FIFO interrupts (so that when the + * RX FIFO is ready, we come back here) and be done. + */ + xpds_write_control_register (card_num, XPDS_MCR_INT_CLR, + XPDS_MCR_INT__RX_FIFO, MAIN); + xpds_read_control_register (card_num, XPDS_MCR_INT_CLR, + &value, MAIN); + if (! (value & XPDS_MCR_INT__RX_FIFO)) { + dprintk (KERN_DEBUG "%s: RX FIFO interrupt not asserted\n", xpds_devs[card_num].name); + /* + xpds_write_control_register (card_num, + XPDS_MCR_MASK_SET, XPDS_MCR_INT__RX_FIFO, MAIN); + xpds_write_control_register (card_num, XPDS_MCR_INT_CLR, + XPDS_MCR_INT__RX_FIFO, MAIN); + */ + return RX_FIFO_DONE; + } + + do_gettimeofday (&tv1); + + /* + * Get needed information about the length, offset, + * and data pointer in the RX DMA descriptor. Note + * that the length in the control field is one less + * than the actual length; we add 1 here to get the + * true length. + */ + length = (control & RXTX_CONTROL__PACKET_LENGTH_MASK); + offset = xpds_data[card_num].current_hw_rx_dma->offset; + data = xpds_data[card_num].current_hw_rx_dma->buffer + offset; + + dprintk (KERN_DEBUG "%s: current RX DMA buffer = %p\n", + xpds_devs[card_num].name, + xpds_data[card_num].current_hw_rx_dma->buffer); + dprintk (KERN_DEBUG "%s: current RX DMA length = %d\n", + xpds_devs[card_num].name, length); + dprintk (KERN_DEBUG "%s: current RX DMA offset = %d\n", + xpds_devs[card_num].name, offset); + dprintk (KERN_DEBUG "%s: current RX DMA data pointer = %p\n", + xpds_devs[card_num].name, data); + + /* + * Copy data from the RX FIFO into the DMA buffer. Data + * must be set / copied in 32 bit chunks, not 8 bit chunks. + */ + rx_fifo_data_pointer = (volatile u32 *) (xpds_data[card_num].rx_fifo_data_registers + FIFO_SIZE * xpds_data[card_num].current_rx_fifo); + dprintk (KERN_DEBUG "%s: current_rx_fifo=%d, rx_fifo_data_pointer=%p\n", xpds_devs[card_num].name, xpds_data[card_num].current_rx_fifo, rx_fifo_data_pointer); + + /* + * Note that due to the bug, we cannot read the FIFO registers + * until at least 10 microseconds after the FIFO ready (interrupt) + * is set. + */ + do_gettimeofday (&tv2); + tvdiff = tv2.tv_usec - tv1.tv_usec + + (tv2.tv_sec - tv1.tv_sec) * 1000000; + if (tvdiff >= 0 && tvdiff < 10) { + udelay (10 - tvdiff); + } + + xpds_read_control_register (card_num, XPDS_FCR_STAT, + &value, RX_FIFO); + rx_len = value & XPDS_FCR_STAT__SIZE_MASK; + rx_len += 1; + rx_last = value & XPDS_FCR_STAT__LAST; + + if (length + rx_len > RXTX_BUFFER_SIZE) { + dprintk (KERN_ERR "%s: packet too long (length = %d, rx_len = %d)\n", xpds_devs[card_num].name, length, rx_len); + if (rx_last) { + control &= ~ RXTX_CONTROL__PACKET_LENGTH_MASK; + control |= (length - 1); + control &= ~ RXTX_CONTROL__HWGO; + control |= RXTX_CONTROL__SWGO; + control &= ~ XPDS_DMA_DESC__PACKET_TAG_MASK; + control |= XPDS_DMA_DESC__PACKET_TAG_LONG; + xpds_data[card_num].current_hw_rx_dma->control = control; + xpds_data[card_num].current_hw_rx_dma->offset = 0; + } + xpds_data[card_num].current_rx_fifo ++; + xpds_data[card_num].current_rx_fifo %= NUM_FIFO; + xpds_write_control_register (card_num, XPDS_FCR_INC, + XPDS_FCR_INC__NEXT, RX_FIFO); + return RX_FIFO_DO_MORE; + } + dprintk (KERN_DEBUG "%s: receiving %d bytes through FIFO (rx_last=%x)\n", xpds_devs[card_num].name, rx_len, rx_last); +#if DEBUG + dprintk (KERN_DEBUG "RX FIFO data (%p):", rx_fifo_data_pointer); + for (i = 0; i < rx_len; i ++) { + dprintk (" %02x", ((volatile u8 *)rx_fifo_data_pointer)[i]); + } + dprintk ("\n"); +#endif + + copy_len = (rx_len % 4 == 0) ? rx_len : (rx_len + 4) / 4 * 4; + dprintk (KERN_DEBUG "%s: RX DMA offset = %d, data = %p, copy_len = %d\n", xpds_devs[card_num].name, offset, data, copy_len); + for (i = 0; i < copy_len / 4; i ++) { + *((u32 *)data + i) = rx_fifo_data_pointer[i]; + } + + /* + * Increment the RX FIFO number and data pointer. + */ + xpds_data[card_num].current_rx_fifo ++; + xpds_data[card_num].current_rx_fifo %= NUM_FIFO; + xpds_write_control_register (card_num, XPDS_FCR_INC, + XPDS_FCR_INC__NEXT, RX_FIFO); + + /* + * Increment the length and offset in the RX DMA buffer. + * If this is the last piece of the packet, turn on + * SW_GO and turn off HW_GO in the descriptor. + */ + length += rx_len; + if (rx_last) { + u32 tag; + offset = 0; + /* + * Check for tags. + */ + xpds_read_control_register (card_num, XPDS_FCR_TAG, + &value, RX_FIFO); + tag = value & XPDS_FCR_TAG__MASK; + if (tag) { + dprintk (KERN_DEBUG "%s: RX FIFO received tag %x\n", + xpds_devs[card_num].name, tag); + } + + control &= ~ RXTX_CONTROL__PACKET_LENGTH_MASK; + control |= (length - 1); + control &= ~ RXTX_CONTROL__HWGO; + control |= RXTX_CONTROL__SWGO; + control &= ~ XPDS_DMA_DESC__PACKET_TAG_MASK; + control |= ((tag << 16) & XPDS_DMA_DESC__PACKET_TAG_MASK); + dprintk (KERN_DEBUG "%s: packet length is %d\n", + xpds_devs[card_num].name, length); + } else { + control &= ~ RXTX_CONTROL__PACKET_LENGTH_MASK; + control |= length; + offset += rx_len; + } + xpds_data[card_num].current_hw_rx_dma->control = control; + xpds_data[card_num].current_hw_rx_dma->offset = offset; + + if (rx_last) { + xpds_data[card_num].current_hw_rx_dma = xpds_data[card_num].current_hw_rx_dma->next; + if (! (control & RXTX_CONTROL__NEXT_VALID)) { + dprintk (KERN_ERR "%s: RX DMA next valid is not set\n", xpds_devs[card_num].name); + } + } + return RX_FIFO_DO_MORE; +} + +/* + * If the RX or TX low rate bugs exist and the + * RX or TX FIFOs are ready, do the FIFO manipulations. + */ +__inline__ static void +xpds_fifo_interrupts (int card_num, u32 interrupt) +{ + int rx_rc = RX_FIFO_DONE, tx_rc = TX_FIFO_DONE, do_more; + + dprintk (KERN_DEBUG "%s: xpds_fifo_interrupts(%d,%02x) called\n", + xpds_devs[card_num].name, card_num, interrupt); + if ((interrupt & XPDS_MCR_INT__RX_FIFO) && + RX_DMA_LOW_RATE_BUG (card_num)) { + rx_rc = xpds_rx_fifo_interrupt (card_num); + } + if ((interrupt & XPDS_MCR_INT__TX_FIFO) && + TX_DMA_LOW_RATE_BUG (card_num)) { + tx_rc = xpds_tx_fifo_interrupt (card_num); + } + do_more = rx_rc == RX_FIFO_DO_MORE || tx_rc == TX_FIFO_DO_MORE; + while (do_more) { + int i, rc; + + do_more = 0; + for (i = 0; i < xpds_max_cards; i ++) { + if (xpds_data[i].rxtx_mem_allocated == NULL) continue; + if (RX_DMA_LOW_RATE_BUG (i)) { + rc = xpds_rx_fifo_interrupt (i); + if (rc == RX_FIFO_DO_MORE) do_more = 1; + } + if (TX_DMA_LOW_RATE_BUG (i)) { + rc = xpds_tx_fifo_interrupt (i); + if (rc == TX_FIFO_DO_MORE) do_more = 1; + } + } + } +} + +/* + * Handle RXCI interrupt for IDSL, indicating possible physical + * layer state change. + */ +__inline__ static void +xpds_rxci_interrupt (int card_num, struct net_device *dev) +{ + int ind_ai; + u32 value; + + /* + * Get correct IND_AI to look for. + */ + ind_ai = xpds_data[card_num].is_lt ? LT_IND_AI : NT_IND_AI; + + xpds_data[card_num].rxci_interrupt_received = 1; + xpds_read_control_register (card_num, XPDS_MCR_RXCI, + &value, MAIN); + + /* + * If network interface is up, check to see if PEB 2091 + * fell out of transparent mode. + */ + if (xpds_if_busy(dev) && (value & XPDS_MCR_RXCI__MASK) != ind_ai) { + dprintk (KERN_DEBUG "%s: RXCI status change to 0x%02x\n", xpds_devs[card_num].name, value); + printk (KERN_NOTICE "%s: physical link went down\n", xpds_devs[card_num].name); + if (! xpds_data[card_num].is_sdsl) xpds_link_led (card_num, 0); + netif_stop_queue(dev); + xpds_if_down(dev); + xpds_data[card_num].physical_up = 0; + xpds_data[card_num].physical_retrying = 0; + memset (xpds_data[card_num].frad_data.pvc_active, 0, + sizeof (xpds_data[card_num].frad_data.pvc_active)); + memset (xpds_data[card_num].frad_data.pvc_new, 0, + sizeof (xpds_data[card_num].frad_data.pvc_new)); + } else { + dprintk (KERN_DEBUG "%s: RXCI status change to 0x%02x\n", xpds_devs[card_num].name, value); + } +} + +/* + * Handle external interrupt for SDSL, indicating possible physical + * layer state change. + */ +__inline__ static void +xpds_ext_interrupt (int card_num, struct net_device *dev) +{ + u8 irq_type; + int rc; + + /* unmask external interrupt */ + xpds_write_control_register (card_num, XPDS_MCR_MASK_SET, + XPDS_MCR_INT__EXT, MAIN); + + /* get IRQ type */ + rc = xpds_mailbox_read (card_num, XPDS_MBX_WRITE_IRQTYPE, + &irq_type); + if (rc > 0) { + printk (KERN_ERR "%s: mailbox read (WRITE_IRQTYPE) failed (%d)\n", xpds_devs[card_num].name, rc); + } else if (irq_type == XPDS_SDSL_IRQTYPE_STATE_CHANGE) { + u8 state; + + rc = xpds_mailbox_read (card_num, + XPDS_MBX_READ_PHYS_STATE, &state); + if (rc > 0) { + printk (KERN_ERR "%s: mailbox read (READ_PHYS_STATE) failed (%d)\n", xpds_devs[card_num].name, rc); + } else { + if (state & XPDS_SDSL_STATE_LINKUP) { + int rc; + + printk (KERN_NOTICE "%s: physical link came up (state=%02x)\n", xpds_devs[card_num].name, state); + netif_start_queue(dev); + if (! xpds_data[card_num].physical_up) { + xpds_data[card_num].physical_up = 1; + xpds_data[card_num].physical_retrying = 0; + + /* + * Need to re-enable the DMA which was + * disabled when the link went down. + */ + rc = xpds_init_descriptors (card_num); + if (rc) printk (KERN_ERR "%s: xpds_init_descriptors(%d) failed\n", xpds_devs[card_num].name, card_num); + rc = xpds_dma_enable (card_num); + if (rc) printk (KERN_ERR "%s: xpds_dma_enable(%d) failed\n", xpds_devs[card_num].name, card_num); + } + + } else { + printk (KERN_NOTICE "%s: physical link went down (state=%02x)\n", xpds_devs[card_num].name, state); + netif_stop_queue(dev); + xpds_if_down(dev); + if (xpds_data[card_num].physical_up) { + u32 sdsl_mode, speed_mode; + int rc; + + xpds_data[card_num].physical_up = 0; + xpds_data[card_num].physical_retrying = 1; + + /* + * DMA can get jammed up with junk + * packets when link goes down and + * up. Need to disable the DMA here + * to get the ASIC unstuck. + */ + rc = xpds_dma_disable (card_num); + if (rc) printk (KERN_ERR "%s: xpds_dma_disable(%d) failed\n", xpds_devs[card_num].name, card_num); + + /* + * Reset speed mode (in case it is 0, + * need it set that way for mailbox + * timeouts). + */ + rc = xpds_get_sdsl_mode (card_num, + &sdsl_mode); + speed_mode = sdsl_mode & XPDS_SDSL_MODE__SPEED_MASK; + xpds_data[card_num].sdsl_speed = + speed_mode << 3; + + } + } + } + } +} + +#define PACKET_READ 0 +#define RX_DMA_NOT_READY 1 + +/* + * Handle an RX DMA interrupt. Return PACKET_READ if a packet was + * read (erroneous or otherwise), and RX_DMA_NOT_READY if SW_GO is + * not set in the next descriptor. + */ +__inline__ static int +xpds_rx_dma_interrupt (struct net_device *dev, int card_num) +{ + volatile xpds_rxtx_list_t *current_rx; + int len; + int packet_good; +#if DEBUG + u32 value; +#endif + + current_rx = xpds_data[card_num].current_rx_dma; + + dprintk (KERN_DEBUG "%s: xpds_rx_dma_interrupt() called\n", xpds_devs[card_num].name); + dprintk (KERN_DEBUG "%s: current_rx = %p\n", xpds_devs[card_num].name, current_rx); + dprintk (KERN_DEBUG "%s: current_rx->control = %08x\n", xpds_devs[card_num].name, current_rx->control); + dprintk (KERN_DEBUG "%s: current_rx->buffer = %p\n", xpds_devs[card_num].name, current_rx->buffer); + dprintk (KERN_DEBUG "%s: current_rx->next = %p\n", xpds_devs[card_num].name, current_rx->next); + dprintk (KERN_DEBUG "%s: current_rx->prev = %p\n", xpds_devs[card_num].name, current_rx->prev); + { + volatile xpds_rxtx_list_t *rx; + + for (rx = current_rx->next; rx != current_rx; rx = rx->next) { + ddprintk (KERN_DEBUG "rx = %p\n", rx); + ddprintk (KERN_DEBUG "rx->control = %08x\n", rx->control); + ddprintk (KERN_DEBUG "rx->buffer = %p\n", rx->buffer); + ddprintk (KERN_DEBUG "rx->next = %p\n", rx->next); + ddprintk (KERN_DEBUG "rx->prev = %p\n", rx->prev); + } + } + +#if DEBUG + xpds_read_control_register (card_num, XPDS_DMA_CLR_STAT, + &value, RX_DMA); + ddprintk (KERN_DEBUG "%s: RX DMA status is %02x\n", + xpds_devs[card_num].name, value); +#endif + +#if DEBUG + xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR, + &value, MAIN); + ddprintk (KERN_DEBUG "%s: interrupt mask is %02x\n", xpds_devs[card_num].name, value); + xpds_read_control_register (card_num, XPDS_MCR_INT_CLR, + &value, MAIN); + ddprintk (KERN_DEBUG "%s: interrupt is %02x\n", xpds_devs[card_num].name, value); +#endif + + /* + * Hardware should have set the software-go bit. + */ + if (! (current_rx->control & RXTX_CONTROL__SWGO) ) { + dprintk (KERN_DEBUG "%s: xpds_rx_dma_interrupt() done, not ready\n", xpds_devs[card_num].name); + return RX_DMA_NOT_READY; + } + + /* + * Packet is good unless proven otherwise... + */ + packet_good = 1; + + /* + * Check the packet tag. If not OK, mark the packet as not good. + */ + { + u32 packet_tag; + const char *tag_str; + + packet_tag = current_rx->control & XPDS_DMA_DESC__PACKET_TAG_MASK; + if (packet_tag == XPDS_DMA_DESC__PACKET_TAG_CRC) { + tag_str = "CRC error"; + } else if (packet_tag == XPDS_DMA_DESC__PACKET_TAG_BOUNDARY_VIOLATION) { + tag_str = "boundary violation"; + } else if (packet_tag == XPDS_DMA_DESC__PACKET_TAG_LONG) { + tag_str = "long frame"; + } else if (packet_tag == XPDS_DMA_DESC__PACKET_TAG_ABORT) { + tag_str = "abort received"; + } else if (packet_tag == XPDS_DMA_DESC__PACKET_TAG_OK) { + tag_str = "OK"; + } else if (packet_tag & 0x80000) { + tag_str = "overflow/underflow"; + } else { + tag_str = "unlisted error"; + } + if (packet_tag != XPDS_DMA_DESC__PACKET_TAG_OK) { + len = current_rx->control & + RXTX_CONTROL__PACKET_LENGTH_MASK; + len ++; + dprintk (KERN_ERR "%s: received packet of length %d with tag %05x (%s)\n", xpds_devs[card_num].name, len, packet_tag, tag_str); + packet_good = 0; + } + } + + /* + * Get and check length of packet. + */ + len = current_rx->control & RXTX_CONTROL__PACKET_LENGTH_MASK; + len ++; + /* buggy ASIC has packet length 2 too large */ + if (xpds_data[card_num].has_last_byte_bug) { + len -= 2; + if (len < 0) len = 0; + } + if (len > RXTX_BUFFER_SIZE) { + dprintk (KERN_ERR "%s: receiving packet of length %d -- too large\n", xpds_devs[card_num].name, len); + packet_good = 0; + } else if (len < 1) { + dprintk (KERN_ERR "%s: receiving packet of length %d -- too small\n", xpds_devs[card_num].name, len); + packet_good = 0; + } else { + dprintk (KERN_DEBUG "%s: receiving packet of length %d\n", + xpds_devs[card_num].name, len); + } + + /* + * Receive the packet (i.e. copy it to where the kernel + * deals with it) if the packet is ok. + */ + if (packet_good) { + xpds_rx (dev, len, current_rx->buffer); + } + + /* + * Zero the packet length. + */ + ddprintk (KERN_DEBUG "%s: zeroing packet length\n", xpds_devs[card_num].name); + current_rx->control &= ~ (RXTX_CONTROL__PACKET_LENGTH_MASK | (RXTX_CONTROL__PACKET_TAG_MASK << RXTX_CONTROL__PACKET_TAG_OFFSET)); + + /* + * Turn off software-go, then turn on hardware-go to let the + * hardware know that it is ok to reuse the buffer. + */ + current_rx->control &= ~ RXTX_CONTROL__SWGO; + current_rx->control |= RXTX_CONTROL__HWGO; + + /* + * Advance the current to the next descriptor. + */ + xpds_data[card_num].current_rx_dma = current_rx->next; + + /* + * Increment the counters. + */ + if (packet_good) { + xpds_data[card_num].stats.rx_packets ++; + } else { + xpds_data[card_num].stats.rx_errors ++; + } + + dprintk (KERN_DEBUG "%s: xpds_rx_dma_interrupt() done, packet read\n", xpds_devs[card_num].name); + + return PACKET_READ; +} + +typedef struct { + struct net_device *dev; + u32 interrupt; +} interrupt_bh_data_t; + +static struct tq_struct *xpds_interrupt_bh_tasks; + +static void xpds_interrupt_bh (void *p); + +/* + * The interrupt handler for the card. Interrupts may happen due + * to receiving a packet, or due to an RXCI status change (which + * occurs while the state machines on the LT and NT sides are + * negotiating each other into transparent mode). + */ +static void +xpds_interrupt (int irq __attribute__((unused)), void *dev_instance, + struct pt_regs *regs __attribute__((unused))) +{ + struct net_device *dev; + int card_num; + u32 interrupt, bh_interrupts; + + dprintk (KERN_DEBUG "xpds_interrupt (%d, %p, %p)\n", irq, dev_instance, regs); + + dev = (struct net_device *)dev_instance; + + if (dev == NULL) return; + + /* + * Lock. + */ +#ifdef __SMP__ + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + dprintk (KERN_DEBUG "%s: Duplicate entry of the interrupt handler by processor %d.\n", + dev->name, hard_smp_processor_id()); + dev->interrupt = 0; + return; + } +#else +/* if (dev->interrupt) { + dprintk (KERN_DEBUG "%s: Re-entering the interrupt handler.\n", dev->name); + return; + } + dev->interrupt = 1; */ +#endif + + card_num = dev - xpds_devs; + + /* + * Get the interrupt type. + */ + xpds_read_control_register (card_num, XPDS_MCR_INT_SET, + &interrupt, MAIN); +#if DEBUG + xpds_print_interrupt_type (card_num, interrupt); +#endif + + if (RX_DMA_LOW_RATE_BUG (card_num)) { + xpds_write_control_register (card_num, + XPDS_MCR_MASK_CLR, XPDS_MCR_INT__RX_FIFO, MAIN); + } + if (TX_DMA_LOW_RATE_BUG (card_num)) { + xpds_write_control_register (card_num, + XPDS_MCR_MASK_CLR, XPDS_MCR_INT__TX_FIFO, MAIN); + } + + /* + * Tell the hardware to stop generating this interrupt. + * For SDSL, clear the physical layer interrupt first. + * For all cards, clear the interrupt in the MCR. + */ + if ((interrupt & XPDS_MCR_INT__EXT) && xpds_data[card_num].is_sdsl) { + int rc; + + rc = xpds_mailbox_write (card_num, XPDS_MBX_CLEAR_IRQ, 0); + if (rc > 0) printk (KERN_ERR "%s: mailbox write (CLEAR_IRQ, 0) failed\n", xpds_devs[card_num].name); + + /* mask external interrupt */ + xpds_write_control_register (card_num, XPDS_MCR_MASK_CLR, + XPDS_MCR_INT__EXT, MAIN); + } + + if (interrupt & XPDS_MCR_INT__RX_DMA) { + xpds_write_control_register (card_num, XPDS_DMA_CLR_STAT, + 0xff /* XPDS_DMA_STAT__DONE */, RX_DMA); + } + + /* clear the interrupt in the MCR */ + xpds_write_control_register (card_num, XPDS_MCR_INT_CLR, + 0xff, MAIN); + + /* read the interrupt in the MCR */ + if (xpds_debug_level) { + u32 value; + xpds_read_control_register (card_num, XPDS_MCR_INT_SET, + &value, MAIN); + dprintk (KERN_DEBUG "%s: interrupt register is %02x after clearing\n", xpds_devs[card_num].name, value); + } + + /* + * If the RX or TX low rate bugs exist and the + * RX or TX FIFOs are ready, do the FIFO manipulations. + */ + xpds_fifo_interrupts (card_num, interrupt); + +#if DEBUG + xpds_print_fifo_data (card_num, interrupt); +#endif + + /* + * If it is a TX DMA interrupt, clear the HW_GO and SW_GO bits + * in any descriptors where both are set, and clear the interrupt. + */ + if (interrupt & XPDS_MCR_INT__TX_DMA) { + volatile xpds_rxtx_list_t *tx_desc; + int i; + + tx_desc = xpds_data[card_num].current_tx_dma; + for (i = 0; i < NUM_DESC; i ++) { + if ((tx_desc->control & (XPDS_DMA_DESC__HW_GO | XPDS_DMA_DESC__SW_GO)) == (XPDS_DMA_DESC__HW_GO | XPDS_DMA_DESC__SW_GO)) { + tx_desc->control &= ~ (XPDS_DMA_DESC__HW_GO | XPDS_DMA_DESC__SW_GO); + } + tx_desc = tx_desc->next; + } + + ddprintk (KERN_DEBUG "%s: clearing TX DMA interrupt\n", xpds_devs[card_num].name); + xpds_write_control_register (card_num, + XPDS_DMA_CLR_STAT, + 0xff /* XPDS_DMA_STAT__DONE */, TX_DMA); + xpds_write_control_register (card_num, XPDS_MCR_INT_CLR, + XPDS_MCR_INT__TX_DMA, MAIN); + + xpds_tx_led (card_num, LED_OFF); + } + + /* + * Check to see if the hardware has caught up with the + * software. If it has the software-go bit set, then + * reset and start the DMA again. + */ + if (xpds_data[card_num].current_rx_dma->prev->control & RXTX_CONTROL__SWGO) { + if (! RX_DMA_LOW_RATE_BUG (card_num)) { + printk (KERN_INFO "%s: RX DMA overrun -- restarting RX DMA\n", xpds_devs[card_num].name); + xpds_dma_disable (card_num); + xpds_init_descriptors (card_num); + xpds_dma_enable (card_num); + } + } + + mark_bh (NET_BH); + + bh_interrupts = XPDS_MCR_INT__EXT | XPDS_MCR_INT__RXCI | XPDS_MCR_INT__RX_DMA; + if (RX_DMA_LOW_RATE_BUG (card_num) || TX_DMA_LOW_RATE_BUG (card_num)) { + bh_interrupts |= XPDS_MCR_INT__RX_FIFO | XPDS_MCR_INT__TX_FIFO; + } + + if (interrupt & bh_interrupts) { + dprintk (KERN_DEBUG "queuing a bottom half task (dev = %p (%d), interrupt = %02x)\n", dev, card_num, interrupt); + ((interrupt_bh_data_t *)(xpds_interrupt_bh_tasks[card_num].data))->dev = dev; + ((interrupt_bh_data_t *)(xpds_interrupt_bh_tasks[card_num].data))->interrupt = interrupt; + queue_task (&(xpds_interrupt_bh_tasks[card_num]), &tq_immediate); + mark_bh (IMMEDIATE_BH); + } +#if defined(__i386__) +/* clear_bit(0, (void*)&dev->interrupt); */ +#else +/* dev->interrupt = 0; */ +#endif +} + +static void +xpds_interrupt_bh (void *p) +{ + interrupt_bh_data_t *data; + struct net_device *dev; + int card_num; + u32 interrupt; + + dprintk (KERN_DEBUG "xpds_interrupt_bh(%p) started\n", p); + + data = p; + dev = data->dev; + card_num = dev - xpds_devs; + interrupt = data->interrupt; + + dprintk (KERN_DEBUG "card_num = %d, interrupt = %02x\n", + card_num, interrupt); + + /* + * If it is an RX DMA interrupt, need to get the packet + * from the buffer that hardware wrote into. Keep doing + * this until there are no more descriptors to read, + * because there may be multiple descriptors filled + * during one interrupt. + * + * Note that if we have the TX DMA low bit rate bug, we + * need to check see if we can push more data into the TX + * FIFO each time we receive a packet; if this isn't done, + * the TX FIFOs may get starved. + */ + if ((interrupt & XPDS_MCR_INT__RX_DMA) || + ((interrupt & XPDS_MCR_INT__RX_FIFO) && + RX_DMA_LOW_RATE_BUG (card_num)) ) { + int rc; + do { + xpds_fifo_interrupts (card_num, interrupt); + rc = xpds_rx_dma_interrupt (dev, card_num); + } while (rc == PACKET_READ); + } + + /* + * Turn on RX FIFO interrupts if necessary. + */ + if (RX_DMA_LOW_RATE_BUG (card_num)) { + xpds_write_control_register (card_num, + XPDS_MCR_MASK_SET, XPDS_MCR_INT__RX_FIFO, MAIN); + } + + /* + * If it is an RXCI interrupt, clear it, handle it, and go on. + * The RXCI interrupt is only used for IDSL cards. + */ + if ((interrupt & XPDS_MCR_INT__RXCI) && ! xpds_data[card_num].is_sdsl) { + xpds_rxci_interrupt (card_num, dev); + } + + /* + * If it is an external interrupt, clear it, handle it, and go on. + * The external interrupt is only used for SDSL cards. + */ + if ((interrupt & XPDS_MCR_INT__EXT) && xpds_data[card_num].is_sdsl) { + xpds_ext_interrupt (card_num, dev); + } + + dprintk (KERN_DEBUG "xpds_interrupt_bh() finished\n"); +} + + +/* + * Called when "ifconfig xpds0 up" or some such is done. + * Tries to bring up and reset the device. Needs to have + * the other side also trying to get to transparent mode. + */ +static int +xpds_open (struct net_device *dev) +{ + int card_num, rc, flags; + u32 value; + + dprintk (KERN_DEBUG "xpds_open (%p (%d))\n", dev, dev - xpds_devs); + + card_num = dev - xpds_devs; + + /* + * Disable all interrupts. + */ + xpds_write_control_register (card_num, XPDS_MCR_MASK_CLR, + 0xff, MAIN); + xpds_write_control_register (card_num, XPDS_MCR_INT_CLR, + 0xff, MAIN); + xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR, + &value, MAIN); + dprintk (KERN_DEBUG "%s: interrupt mask is %08x\n", xpds_devs[card_num].name, value); + + /* + * Request the IRQ. + */ + dev->irq = xpds_data[card_num].pci_dev->irq; + + flags = SA_SHIRQ; + + dprintk (KERN_DEBUG "%s: request_irq (%d, %p, %08x, %s, %p)\n", + dev->name, dev->irq, xpds_interrupt, flags, dev->name, dev); + /* DELAY (2, card_num); */ + rc = request_irq (dev->irq, &xpds_interrupt, flags, dev->name, dev); + if (rc != 0) { + dprintk (KERN_DEBUG "%s: unable to get IRQ %d (rc=%d)\n", + dev->name, dev->irq, rc); + free_irq (dev->irq, dev); + return -EAGAIN; + } + + dprintk (KERN_DEBUG "%s: %s got interrupt %d\n", dev->name, dev->name, dev->irq); + + /* dev->dev_addr = ??? ; */ + + rc = xpds_reset (card_num); + + xpds_dlci_install_lmi_timer (0, dev); + + MOD_INC_USE_COUNT; + + return 0; +} + +/* + * Called when "ifconfig xpds0 down" or some such is done. + * Brings down the device. + */ +static int +xpds_stop (struct net_device *dev) +{ + int card_num; + int rc; + u32 value; + + card_num = dev - xpds_devs; + + dprintk (KERN_DEBUG "%s: xpds_stop(%p)\n", xpds_devs[card_num].name, dev); + printk (KERN_DEBUG "%s: going down\n", xpds_devs[card_num].name); + + xpds_dlci_remove_lmi_timer (0, dev); + + rc = xpds_dma_disable (card_num); + if (rc != 0) return rc; + + /* + * Turn off link LED, or put the SDSL in reset. + */ + if (! xpds_data[card_num].is_sdsl) { + xpds_link_led (card_num, 0); + } else { + xpds_reset_sdsl (card_num); + } + + /* + * Bring physical layer down. + */ + if (xpds_data[card_num].is_sdsl) { + xpds_reset_sdsl (card_num); + } else { + xpds_read_control_register (card_num, XPDS_MCR_TXCI, &value, + MAIN); + value &= (XPDS_MCR_TXCI__MASK | XPDS_MCR_TXCI__PMD_CONFIG_B1B2D); + xpds_write_control_register (card_num, XPDS_MCR_TXCI, value, + MAIN); + } + xpds_data[card_num].physical_up = 0; + xpds_data[card_num].physical_retrying = 0; + + /* + * Disable interrupts + */ + xpds_write_control_register (card_num, XPDS_MCR_MASK_CLR, 0xff, MAIN); + + /* + * Mark the device as off and busy. + */ + netif_stop_queue(dev); + xpds_if_down(dev); + + MOD_DEC_USE_COUNT; + + free_irq (dev->irq, dev); + + return 0; +} + +/* + * Does the hardware control part of the packet transmission. + */ +static int +xpds_hw_tx (char *data, int len, struct net_device *dev) +{ + int card_num; + volatile xpds_rxtx_list_t *current_tx; + u32 control; + + card_num = dev - xpds_devs; +#if DEBUG + { + u32 value; + + xpds_read_control_register (card_num, XPDS_MCR_MASK_CLR, + &value, MAIN); + dprintk (KERN_DEBUG "%s: interrupt mask is %02x", xpds_devs[card_num].name, value); + if (value & XPDS_MCR_MASK__RX_FIFO) dprintk (" RX_FIFO"); + if (value & XPDS_MCR_MASK__TX_FIFO) dprintk (" TX_FIFO"); + if (value & XPDS_MCR_MASK__RX_DMA) dprintk (" RX_DMA"); + if (value & XPDS_MCR_MASK__TX_DMA) dprintk (" TX_DMA"); + if (value & XPDS_MCR_MASK__PCI_ERROR) dprintk (" PCI_ERROR"); + if (value & XPDS_MCR_MASK__EXT) dprintk (" EXT"); + if (value & XPDS_MCR_MASK__RXCI) dprintk (" RXCI"); + dprintk ("\n"); + } +#endif + + dprintk (KERN_DEBUG "%s: transmitting packet of length %d\n", + xpds_devs[card_num].name, len); + + /* + * Make sure data is not too big. We are assuming that the + * other end has the same maximum packet length limitation. + * Is this necessarily correct? + */ + if (len > xpds_max_packet_length) { + xpds_data[card_num].stats.tx_errors ++; + printk (KERN_ERR "packet size %d is too large (maximum %d)\n", + len, xpds_max_packet_length); + return -E2BIG; + } + + /* + * Get control information for the descriptor. + */ + control = xpds_data[card_num].current_tx_dma->control; + + /* + * Check current descriptor to see if it is available. + * If software-go flag is not set, then the buffer is still + * waiting for the hardware to empty it. + */ + if (! (control & RXTX_CONTROL__SWGO) && (control & RXTX_CONTROL__HWGO) ) { + dprintk (KERN_DEBUG "%s: unable to transmit (SWGO == 0 && HWGO == 1)\n", xpds_devs[card_num].name); + /* xpds_data[card_num].stats.tx_errors ++; */ + return -EBUSY; + } + + /* + * Indicate the time a transmission started. + */ + dev->trans_start = jiffies; + + /* + * Fill in current transmit buffer. + */ + ddprintk (KERN_DEBUG "%s: filling current transmit buffer\n", xpds_devs[card_num].name); + memcpy ((void *)(xpds_data[card_num].current_tx_dma->buffer), data, len); + + /* + * Set up the length in the current descriptor. + */ + ddprintk (KERN_DEBUG "%s: setting length in descriptor\n", xpds_devs[card_num].name); + control &= ~ RXTX_CONTROL__PACKET_LENGTH_MASK; + control |= ((len - 1) & RXTX_CONTROL__PACKET_LENGTH_MASK); + + /* + * Clear the software-go flag and set the hardware-go flag + * in the current descriptor. + */ + ddprintk (KERN_DEBUG "%s: clearing SWGO and setting HWGO\n", xpds_devs[card_num].name); + control &= ~ RXTX_CONTROL__SWGO; + control |= RXTX_CONTROL__HWGO; + + /* + * Put changed control back in the TX descriptor. + */ + xpds_data[card_num].current_tx_dma->control = control; + + /* + * Print out the current TX descriptor. + */ + current_tx = xpds_data[card_num].current_tx_dma; + + /* + * Write a 1 to the hunt bit of the DMA go register for + * the transmit DMA to cause it to fetch the next descriptor. + */ + ddprintk (KERN_DEBUG "%s: setting hunt bit\n", xpds_devs[card_num].name); + xpds_write_control_register (card_num, XPDS_DMA_GO, + XPDS_DMA_GO__HUNT, TX_DMA); + + /* + * Set current transmit buffer to the next one in the + * circular list, after setting the offset to 0 in case + * of the TX DMA bug. + */ + xpds_data[card_num].current_tx_dma->offset = 0; + ddprintk (KERN_DEBUG "%s: setting current transmit buffer to next\n", xpds_devs[card_num].name); + xpds_data[card_num].current_tx_dma = + xpds_data[card_num].current_tx_dma->next; + + /* + * Increment the counters. + */ + xpds_data[card_num].stats.tx_packets ++; + xpds_data[card_num].stats.tx_bytes += len; + + dprintk (KERN_DEBUG "%s: transmitted packet %ld\n", + xpds_devs[card_num].name, (long) (xpds_data[card_num].stats.tx_packets)); + + /* + * If we have the TX DMA bug we need to enable the TX FIFO + * interrupt so that when the TX FIFO is ready, we can simulate + * the hardware DMA->FIFO transfer in the driver. + */ + if (TX_DMA_LOW_RATE_BUG (card_num)) { + xpds_write_control_register (card_num, + XPDS_MCR_MASK_SET, XPDS_MCR_INT__TX_FIFO, MAIN); + } + + dprintk (KERN_DEBUG "%s: xpds_hw_tx () done\n", xpds_devs[card_num].name); + + return 0; +} + +/* + * Called whenever a packet needs to be transmitted. + * Note that if the physical link is down (not in transparent + * mode), a bottom half to reset the device is placed on the + * scheduler queue. + */ +int +xpds_tx (u8 *data, unsigned int len, struct net_device *dev) +{ + int card_num; + int rc; + u32 control; + + card_num = dev - xpds_devs; + + xpds_tx_led (card_num, LED_ON); + + dprintk (KERN_DEBUG "%s: xpds_tx (%p, %u, %p)\n", xpds_devs[card_num].name, + data, len, dev); + +#if DEBUG + { + int i, debuglen; + + debuglen = len; + if (debuglen > sizeof (struct frhdr)) { + debuglen = sizeof (struct frhdr); + } + dpprintk (KERN_DEBUG "frame header sent:"); + for (i = 0; i < sizeof (struct frhdr); i ++) { + dpprintk (" %02x", data[i]); + } + dpprintk ("\n"); + } + { + int i, debuglen; + + /* debuglen = ETH_ZLEN < len ? len : ETH_ZLEN; */ + debuglen = len; + if (debuglen > 256) debuglen = 256; + dpprintk (KERN_DEBUG "data sent:"); + for (i = sizeof (struct frhdr); i < debuglen ; i ++) { + dpprintk (" %02x", data[i]); + } + if (len > debuglen) dpprintk (" ..."); + dpprintk ("\n"); + } +#endif + if (! xpds_data[card_num].physical_up) { + dprintk (KERN_DEBUG "%s: physical link is down\n", xpds_devs[card_num].name); + if (xpds_data[card_num].is_sdsl) { + return -EBUSY; + } + if (! xpds_data[card_num].physical_retrying) { + xpds_data[card_num].physical_retrying = 1; + + xpds_reset_bh_tasks[card_num].next = NULL; + xpds_reset_bh_tasks[card_num].sync = 0; + xpds_reset_bh_tasks[card_num].routine = xpds_reset_bh; + xpds_reset_bh_tasks[card_num].data = dev; + + queue_task (&(xpds_reset_bh_tasks[card_num]), + &tq_scheduler); + } + xpds_tx_led (card_num, LED_OFF); + return -EBUSY; + } + + /* + * Atomically test and set (bit 0 of) the busy flag. + * If busy, return BUSY. + */ + if (xpds_if_busy(dev)) { + dprintk (KERN_DEBUG "%s: busy\n", xpds_devs[card_num].name); + xpds_tx_led (card_num, LED_OFF); + /* xpds_data[card_num].stats.tx_errors ++; */ + + return -EBUSY; + } + netif_stop_queue(dev); + + /* len = ETH_ZLEN < len ? len : ETH_ZLEN; */ + dev->trans_start = jiffies; + + rc = xpds_hw_tx (data, len, dev); + + if (rc == 0) { + /* dev_kfree_skb (skb, FREE_WRITE); */ + } else { + xpds_tx_led (card_num, LED_OFF); + } + + /* + * Get control information for the descriptor (after + * the one that we just wrote into). + */ + control = xpds_data[card_num].current_tx_dma->control; + + /* + * Atomically clear the busy bit. + */ +/* if (test_and_clear_bit (0, (void *)&(dev->tbusy)) == 0) { + printk (KERN_ERR "%s: test_and_clear_bit(0, &(dev->tbusy)) in xpds_tx() found bit already clear\n", xpds_devs[card_num].name); + } */ + netif_wake_queue(dev); + + /* + * Return OK if xpds_tx returned 0 (OK), BUSY if -EBUSY, + * ERR otherwise. + */ + return rc; +} + +struct net_device_stats * +xpds_get_stats (struct net_device *dev) +{ + xpds_data_t *data_ptr; + + data_ptr = dev->priv; + return &(data_ptr->stats); +} + +static int +xpds_set_config (struct net_device *dev, + struct ifmap *map __attribute__ ((unused)) ) +{ + dprintk (KERN_DEBUG "%s: xpds_set_config() called\n", + dev->name); + + if (dev->flags & IFF_UP) return -EBUSY; + + /* nothing */ + + return 0; +} + +static void +xpds_reboot_sdsl_physical_layer (int card_num) +{ + if (xpds_data[card_num].physical_up || + xpds_data[card_num].physical_retrying) { + xpds_reset_sdsl (card_num); + xpds_start_sdsl (card_num); + } +} + +static int xpds_init_frad_data (struct frad_local *fp, short dlci); + +static int +xpds_ioctl (struct net_device *dev, struct ifreq *rq, + int cmd) +{ + xpds_data_t *data_ptr; + int val, card_num, rc = 0; + + if (! suser ()) return -EACCES; + + data_ptr = dev->priv; + card_num = data_ptr - xpds_data; + val = (int)(rq->ifr_data); + if (cmd == XPDS_IOCTL_SET_DEBUG) { +#if DEBUG + int i; + + printk ("XPDS driver debug level set to %d\n", val); + xpds_debug_level = val; + for (i = 0; i < xpds_max_cards; i ++) { + xpds_devs[i].flags &= ~ IFF_DEBUG; + xpds_devs[i].flags |= val ? IFF_DEBUG : 0; + } +#else + printk (KERN_ERR "This version of the XPDS driver does not have debug capability.\n"); +#endif + } else if (cmd == XPDS_IOCTL_SET_LT) { + printk ("%s: set to %s\n", + xpds_devs[card_num].name, + val ? "LT (line termination)" : + "NT (network termination)"); + val = (val != 0); + data_ptr->is_lt = val; + data_ptr->frad_data.no_initiate_lmi = val; + if (data_ptr->is_sdsl) { + u32 sdsl_mode; + u8 byte; + + xpds_get_flash_sdsl_mode (card_num, &sdsl_mode); + printk (KERN_DEBUG "%s: sdsl_mode = %08x\n", + xpds_devs[card_num].name, sdsl_mode); + sdsl_mode &= ~ XPDS_SDSL_MODE__UNUSED; + sdsl_mode &= ~ XPDS_SDSL_MODE__NT; + sdsl_mode |= data_ptr->is_lt ? 0 : XPDS_SDSL_MODE__NT; + printk (KERN_DEBUG "%s: changed sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode); + rc = xpds_set_sdsl_mode (card_num, sdsl_mode); + if (rc > 0) printk (KERN_ERR "%s: xpds_set_sdsl_mode() failed\n", xpds_devs[card_num].name); + rc = xpds_get_sdsl_exit_code (card_num, &byte); + if (rc > 0 || byte != XPDS_SDSL_CMD_COMPLETED) { + printk (KERN_ERR "%s: SDSL exit code indicated failure (rc=%d, ec=%d)\n", xpds_devs[card_num].name, rc, byte); + rc = -EBUSY; + } + } + } else if (cmd == XPDS_IOCTL_SET_IDSL_MODE) { + if (! data_ptr->is_sdsl) { + printk ("%s: set to mode %d\n", xpds_devs[card_num].name, val); + data_ptr->speed_mode = val; + } else { + printk (KERN_ERR "Setting the IDSL mode is only applicable to IDSL.\n"); + rc = -EINVAL; + } + } else if (cmd == XPDS_IOCTL_SET_SDSL_SPEED) { + if (data_ptr->is_sdsl) { + u32 sdsl_mode; + u8 byte; + + printk ("%s: SDSL speed set to %d\n", + xpds_devs[card_num].name, + ((val >> 3) & XPDS_SDSL_MODE__SPEED_MASK) << 3); + rc = xpds_get_flash_sdsl_mode (card_num, &sdsl_mode); + if (rc > 0) printk (KERN_ERR "%s: xpds_get_flash_sdsl_mode() failed\n", xpds_devs[card_num].name); + printk (KERN_DEBUG "%s: sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode); + sdsl_mode &= ~ XPDS_SDSL_MODE__UNUSED; + sdsl_mode &= ~ XPDS_SDSL_MODE__SPEED_MASK; + sdsl_mode |= (val >> 3) & XPDS_SDSL_MODE__SPEED_MASK; + printk (KERN_DEBUG "%s: changed sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode); + rc = xpds_set_sdsl_mode (card_num, sdsl_mode); + if (rc > 0) printk (KERN_ERR "%s: xpds_set_sdsl_mode() failed\n", xpds_devs[card_num].name); + rc = xpds_get_sdsl_exit_code (card_num, &byte); + if (rc > 0 || byte != XPDS_SDSL_CMD_COMPLETED) { + printk (KERN_ERR "%s: SDSL exit code indicated failure (rc=%d, ec=%d)\n", xpds_devs[card_num].name, rc, byte); + rc = -EBUSY; + } + + xpds_reboot_sdsl_physical_layer (card_num); + + if ((xpds_data[card_num].has_tx_dma_low_rate_bug || + xpds_data[card_num].has_rx_dma_low_rate_bug) && + xpds_data[card_num].physical_up) { + if ((xpds_data[card_num].sdsl_speed < LOW_BIT_RATE && val >= LOW_BIT_RATE) || (xpds_data[card_num].sdsl_speed >= LOW_BIT_RATE && val < LOW_BIT_RATE)) { + /* + * If card has the low bit rate bug, + * and the speed change moved across + * the value that triggers the bug, + * need to reset the DMA / FIFO. + */ + xpds_dma_disable (card_num); + xpds_dma_enable (card_num); + } + } + xpds_data[card_num].sdsl_speed = val; + } else { + printk (KERN_ERR "Setting the SDSL speed is only applicable to SDSL.\n"); + rc = -EINVAL; + } + } else if (cmd == XPDS_IOCTL_SET_SDSL_INVERT) { + if (data_ptr->is_sdsl) { + u32 sdsl_mode; + u8 byte; + + printk ("%s: SDSL invert set to %d\n", + xpds_devs[card_num].name, val ? 1 : 0); + rc = xpds_get_flash_sdsl_mode (card_num, &sdsl_mode); + if (rc > 0) printk (KERN_ERR "%s: xpds_get_flash_sdsl_mode() failed\n", xpds_devs[card_num].name); + printk (KERN_DEBUG "%s: sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode); + sdsl_mode &= ~ XPDS_SDSL_MODE__UNUSED; + sdsl_mode &= ~ XPDS_SDSL_MODE__INVERT; + sdsl_mode |= (val ? XPDS_SDSL_MODE__INVERT : 0); + printk (KERN_DEBUG "%s: changed sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode); + xpds_set_sdsl_mode (card_num, sdsl_mode); + if (rc > 0) printk (KERN_ERR "%s: xpds_set_sdsl_mode() failed\n", xpds_devs[card_num].name); + rc = xpds_get_sdsl_exit_code (card_num, &byte); + if (rc > 0 || byte != XPDS_SDSL_CMD_COMPLETED) { + printk (KERN_ERR "%s: SDSL exit code indicated failure (rc=%d, ec=%d)\n", xpds_devs[card_num].name, rc, byte); + rc = -EBUSY; + } + xpds_reboot_sdsl_physical_layer (card_num); + } else { + printk (KERN_ERR "Setting the invert is only applicable to SDSL.\n"); + rc = -EINVAL; + } + } else if (cmd == XPDS_IOCTL_SET_SDSL_SWAP) { + if (data_ptr->is_sdsl) { + u32 sdsl_mode; + u8 byte; + + printk ("%s: SDSL swap set to %d\n", + xpds_devs[card_num].name, val ? 1 : 0); + rc = xpds_get_flash_sdsl_mode (card_num, &sdsl_mode); + if (rc > 0) printk (KERN_ERR "%s: xpds_get_flash_sdsl_mode() failed\n", xpds_devs[card_num].name); + printk (KERN_DEBUG "%s: sdsl_mode = %08x\n", + xpds_devs[card_num].name, sdsl_mode); + sdsl_mode &= ~ XPDS_SDSL_MODE__UNUSED; + sdsl_mode &= ~ XPDS_SDSL_MODE__SWAP; + sdsl_mode |= (val ? XPDS_SDSL_MODE__SWAP : 0); + printk (KERN_DEBUG "%s: changed sdsl_mode = %08x\n", xpds_devs[card_num].name, sdsl_mode); + rc = xpds_set_sdsl_mode (card_num, sdsl_mode); + if (rc > 0) printk (KERN_ERR "%s: xpds_set_sdsl_mode() failed\n", xpds_devs[card_num].name); + rc = xpds_get_sdsl_exit_code (card_num, &byte); + if (rc > 0 || byte != XPDS_SDSL_CMD_COMPLETED) { + printk (KERN_ERR "%s: SDSL exit code indicated failure (rc=%d, ec=%d)\n", xpds_devs[card_num].name, rc, byte); + rc = -EBUSY; + } + xpds_reboot_sdsl_physical_layer (card_num); + } else { + printk (KERN_ERR "Setting the swap is only applicable to SDSL.\n"); + rc = -EINVAL; + } + } else if (cmd == XPDS_IOCTL_INSTALL_FLASH) { + rc = xpds_install_flash_image (card_num, + (xpds_flash_image_t *)(rq->ifr_data)); + if (rc > 0) rc = -EBUSY; + } else if (cmd == XPDS_IOCTL_GET_SDSL_INFO) { + if (data_ptr->is_sdsl) { + printk ("%s: getting SDSL serial info for user process\n", xpds_devs[card_num].name); + copy_to_user ((void *)(rq->ifr_data), + &(data_ptr->serial_data), + sizeof (data_ptr->serial_data)); + } else { + printk (KERN_ERR "%s: cannot get SDSL info on IDSL card\n", xpds_devs[card_num].name); + rc = -EINVAL; + } + } else if (cmd == XPDS_IOCTL_SET_SDSL_INFO) { + if (data_ptr->is_sdsl) { + printk ("%s: setting SDSL serial info from user process\n", xpds_devs[card_num].name); + copy_from_user (&(data_ptr->serial_data), + (void *)(rq->ifr_data), + sizeof (data_ptr->serial_data)); + rc = xpds_set_sdsl_info (card_num); + if (rc > 0) { + printk (KERN_ERR "%s: unable to set SDSL info\n", xpds_devs[card_num].name); + rc = -EBUSY; + } + } else { + printk (KERN_ERR "%s: cannot set SDSL info on IDSL card\n", xpds_devs[card_num].name); + rc = -EINVAL; + } + } else if (cmd == XPDS_IOCTL_GET_SDSL_STATE) { + if (data_ptr->is_sdsl) { + u8 state; + rc = xpds_sdsl_get_state (card_num, &state); + if (rc != 0) { + printk (KERN_ERR "%s: get SDSL state failed, rc = %d\n", xpds_devs[card_num].name, rc); + state = -1; + rc = -EBUSY; + } else { + printk (KERN_NOTICE "%s: SDSL state is 0x%02x\n", xpds_devs[card_num].name, state); + } + copy_to_user (rq->ifr_data, &state, 1); + } else { + printk (KERN_ERR "%s: cannot set SDSL state on IDSL card\n", xpds_devs[card_num].name); + rc = -EINVAL; + } + } else if (cmd == XPDS_IOCTL_SET_LOOPBACK) { + xpds_loopback_parameters_t loopback_params; + copy_from_user (&loopback_params, + (void *)(rq->ifr_data), + sizeof (loopback_params)); + if (loopback_params.loopback_type == XPDS_ASIC_LOOPBACK) { + if (loopback_params.on) { + printk (KERN_NOTICE "%s: setting ASIC loopback\n", xpds_devs[card_num].name); + xpds_write_control_register (card_num, XPDS_MCR_CONFIG, + XPDS_MCR_CONFIG__MODE_LOOPBACK, MAIN); + netif_start_queue(dev); + xpds_data[card_num].physical_up = 1; + } else { + printk (KERN_NOTICE "%s: unsetting ASIC loopback\n", xpds_devs[card_num].name); + xpds_write_control_register (card_num, XPDS_MCR_CONFIG, + XPDS_MCR_CONFIG__MODE_NORMAL, MAIN); + netif_stop_queue(dev); + xpds_if_down(dev); + xpds_data[card_num].physical_up = 0; + } + } else if (loopback_params.loopback_type == XPDS_SDSL_LOOPBACK) { + if (data_ptr->is_sdsl) { + printk (KERN_NOTICE "%s: setting SDSL external loopback\n", xpds_devs[card_num].name); + rc = xpds_sdsl_loopback (card_num); + if (rc != 0) { + printk (KERN_ERR "%s: setting SDSL external loopback failed, rc = %d\n", xpds_devs[card_num].name, rc); + rc = -EBUSY; + } + } else { + printk (KERN_ERR "%s: cannot set SDSL external loopback on IDSL card\n", xpds_devs[card_num].name); + rc = -EINVAL; + } + } else { + printk (KERN_ERR "%s: invalid loopback type %d\n", + xpds_devs[card_num].name, loopback_params.loopback_type); + } + } else if (cmd == XPDS_IOCTL_SET_BRIDGED_ETHERNET) { + /* + * 1 for bridged ethernet mode, 0 otherwise. + */ + printk ("%s: bridged ethernet set to %d (%s)\n", xpds_devs[card_num].name, + val, val ? "on" : "off"); + xpds_data[card_num].bridged_ethernet = val; + } else if (cmd == XPDS_IOCTL_SET_DLCI_CR) { + /* + * Should be 2, except for buggy Ascends which need 0. + */ + printk ("%s: DLCI CR set to %d\n", xpds_devs[card_num].name, val); + xpds_data[card_num].dlci_cr = val; + } else if (cmd == XPDS_IOCTL_SET_DLCI_LMI) { + /* + * 0 LMI off + * 1 LMI in LT mode (responds to LMI messages) + * 2 LMI in NT mode (generates LMI messages) + * 3 LMI in NT bidirectional mode (generates and + * responds to LMI messages) + */ + printk ("%s: DLCI LMI set to %d\n", xpds_devs[card_num].name, val); + xpds_data[card_num].dlci_lmi = val; + } else if (cmd == XPDS_IOCTL_SET_DLCI) { + printk ("%s: DLCI set to %d\n", xpds_devs[card_num].name, val); + xpds_data[card_num].dlci = val; + xpds_init_frad_data (&(xpds_data[card_num].frad_data), val); + } else { + printk (KERN_ERR "%s: received cmd = %d, data = %d\n", + xpds_devs[card_num].name, cmd, val); + rc = -EINVAL; + } + return rc; +} + +static int +xpds_init_frad_data (struct frad_local *fp, short dlci) +{ + memset (&(fp->dlci), 0, sizeof (fp->dlci)); + fp->dlci[0] = dlci; + + return 0; +} + +static int +xpds_init (struct net_device *dev) +{ + xpds_data_t *data_ptr; + int card_num; + + card_num = dev - xpds_devs; + + dprintk (KERN_DEBUG "xpds_init (%p (%d))\n", dev, card_num); + + netif_stop_queue(dev); + xpds_if_down(dev); + + ether_setup (dev); + dev->open = xpds_open; + dev->stop = xpds_stop; + dev->hard_start_xmit = /* xpds_tx */ xpds_dlci_transmit; + dev->get_stats = xpds_get_stats; + dev->set_config = xpds_set_config; + /* dev->set_mac_address = xpds_set_mac_addr; */ + /* dev->rebuild_header = xpds_rebuild_header; */ + dev->do_ioctl = xpds_ioctl; + /* dev->change_mtu = xpds_change_mtu; */ + dev->priv = &(xpds_data[card_num]); +#if DEBUG + if (xpds_debug_level) dev->flags |= IFF_DEBUG; +#endif + dev->flags &= ~ (IFF_BROADCAST | IFF_MULTICAST); + + data_ptr = dev->priv; + memset (&(data_ptr->stats), 0, sizeof (data_ptr->stats)); + + /* memset(dev->dev_addr, 0, sizeof(dev->dev_addr)); */ + memcpy(dev->dev_addr, + xpds_data[dev - xpds_devs].serial_data.mac_address, + sizeof(dev->dev_addr)); + /* dev->hard_header_len = sizeof (struct frhdr); */ + + /* + * Make sure that the payload cannot be equal to or + * greater than the maximum packet length minus headers. + * For some reason, it must be 2 less, not 1 less??? + * If dev->mtu from ether_setup() is already smaller, + * leave it alone. + */ + if (dev->mtu > xpds_max_packet_length - sizeof (struct frhdr) - 2) { + dev->mtu = xpds_max_packet_length - sizeof (struct frhdr) - 2; + dprintk (KERN_DEBUG "dev->mtu set to %d\n", dev->mtu); + } + + xpds_init_frad_data (&(xpds_data[card_num].frad_data), + xpds_default_dlci); + + /* + * Should be set by ioctls... + */ + xpds_data[card_num].bridged_ethernet = xpds_default_bridged; + xpds_data[card_num].dlci = xpds_default_dlci; + /* dlci_cr needs to be 0 for buggy Ascends */ + xpds_data[card_num].dlci_cr = xpds_default_dlci_cr; + if (xpds_default_dlci_lmi == XPDS_DLCI_LMI_LT_OR_NT) { + if (xpds_data[card_num].is_lt) { + xpds_data[card_num].dlci_lmi = XPDS_DLCI_LMI_LT; + } else { + xpds_data[card_num].dlci_lmi = XPDS_DLCI_LMI_NT; + } + } else { + xpds_data[card_num].dlci_lmi = xpds_default_dlci_lmi; + } + + dprintk (KERN_INFO "%s: bridged ethernet mode is %d (%s)\n", + xpds_devs[card_num].name, xpds_data[card_num].bridged_ethernet, + xpds_data[card_num].bridged_ethernet ? "on" : "off"); + dprintk (KERN_INFO "%s: DLCI = %d\n", + xpds_devs[card_num].name, xpds_data[card_num].dlci); + dprintk (KERN_INFO "%s: DLCI CR = %d\n", + xpds_devs[card_num].name, xpds_data[card_num].dlci_cr); + dprintk (KERN_INFO "%s: DLCI LMI = %d\n", + xpds_devs[card_num].name, xpds_data[card_num].dlci_lmi); + + dprintk (KERN_DEBUG "xpds_init done\n"); + + return 0; +} + +#define SEPROM_SIZE 17 + +#define SEPROM_WRITE_OPCODE 0x5 +#define SEPROM_READ_OPCODE 0x6 +#define SEPROM_ERASE_OPCODE 0x7 + +#define SEPROM_READ_OPERAND_LEN 9 +#define SEPROM_READ_DATA_LEN 17 + +/* + * Read the given address out of the serial EPROM from an IDSL card. + */ +static u16 +read_seprom_data (int card_num, int address) +{ + int i; + u16 value = 0; + int opcode; + + if (address < 0 || address >= SEPROM_SIZE) { + printk (KERN_ERR "%s: read_seprom_data (%d (invalid))\n", + xpds_devs[card_num].name, address); + return 0; + } + + opcode = (SEPROM_READ_OPCODE << 6) | address; + + /* + * Clear the GPIO. + */ + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_CLR, 0xff, MAIN ); + + /* + * Enable the SK, DI, and CS bits in the GPIO, + * and set the CS. + */ + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, + XPDS_MCR_GPIO__OE_SEPROM_SK | + XPDS_MCR_GPIO__OE_SEPROM_DI | + XPDS_MCR_GPIO__GP_SEPROM_CS | + XPDS_MCR_GPIO__OE_SEPROM_CS, + MAIN ); + /* + * Give the address to the serial SEPROM. + */ + for (i = 0; i < SEPROM_READ_OPERAND_LEN; i ++) { + int bit; + + bit = opcode >> ((SEPROM_READ_OPERAND_LEN - 1) - i); + bit &= 0x1; + bit = bit ? XPDS_MCR_GPIO__GP_SEPROM_DI : 0; + /* 0 -> SK, DI */ + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_CLR, + XPDS_MCR_GPIO__GP_SEPROM_SK | + XPDS_MCR_GPIO__GP_SEPROM_DI, + MAIN); + udelay (3); + /* bit -> DI */ + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, bit, MAIN); + /* setup time */ + udelay (3); + /* 1 -> SK */ + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, XPDS_MCR_GPIO__GP_SEPROM_SK, MAIN); + /* hold time */ + udelay (3); + } + /* + * Get the data from the serial SEPROM. + */ + for (i = 0; i < SEPROM_READ_DATA_LEN; i ++) { + u32 bit; + + /* 0 -> SK */ + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_CLR, XPDS_MCR_GPIO__GP_SEPROM_SK, MAIN); + /* setup time */ + udelay (3); + /* 1 -> SK */ + xpds_write_control_register (card_num, + XPDS_MCR_GPIO_SET, XPDS_MCR_GPIO__GP_SEPROM_SK, MAIN); + /* hold time */ + udelay (3); + + /* read bit */ + xpds_read_control_register (card_num, XPDS_MCR_GPIO_SET, + &bit, MAIN); + bit &= XPDS_MCR_GPIO__GP_SEPROM_DO; + bit = bit ? 1 : 0; + value <<= 1; + value |= bit; + } + value >>= 1; + + /* + dprintk (KERN_DEBUG "%s: read_seprom (%d) -> %x\n", + xpds_devs[card_num].name, address, value); + */ + + return value; +} + +#define SEPROM_REVISION 0 +#define SEPROM_HARDWARE_VERSION 1 +#define SEPROM_SOFTWARE_VERSION 2 +#define SEPROM_FIRMWARE_VERSION 2 +#define SEPROM_MFG_DATE_HI 4 +#define SEPROM_MFG_DATE_LO 5 +#define SEPROM_MAC_ADDR_0_1 6 +#define SEPROM_MAC_ADDR_2_3 7 +#define SEPROM_MAC_ADDR_4_5 8 +#define SEPROM_SERIAL_0 9 +#define SEPROM_SERIAL_1 10 +#define SEPROM_SERIAL_2 11 +#define SEPROM_SERIAL_3 12 +#define SEPROM_SERIAL_4 13 +#define SEPROM_SERIAL_5 14 +#define SEPROM_SERIAL_6 15 +#define SEPROM_SERIAL_7 16 + +/* + * Determine if the card is an IDSL or SDSL card. Only the IDSL + * card has the serial EPROM, so garbage indicates an SDSL card. + * Note: an FPGA card will have is_sdsl set to 1; read_sdsl_info() + * will set is_sdsl back to 0 if it appears to be an IDSL FPGA card. + * If IDSL, determine if it has the last byte bug. + */ +static void +read_seprom_info (int card_num) +{ + u16 value1, value2, value3, value4; + xpds_serial_data_t *sdata; + + if (xpds_data[card_num].is_fpga) { + /* + * FPGA card may be SDSL, so set is_sdsl so that + * read_sdsl_info() is called. read_sdsl_info() + * will unset if unable to get SDSL information. + */ + xpds_data[card_num].is_sdsl = 1; + dprintk (KERN_INFO "No serial EPROM for FPGA device.\n"); + return; + } + + sdata = &(xpds_data[card_num].serial_data); + + value1 = read_seprom_data (card_num, SEPROM_SERIAL_0); + value2 = read_seprom_data (card_num, SEPROM_SERIAL_1); + value3 = read_seprom_data (card_num, SEPROM_SERIAL_2); + value4 = read_seprom_data (card_num, SEPROM_SERIAL_3); + if (value1 != 0x5854 /* 'XT' */ || value2 != 0x414e /* 'AN' */ || + value3 != 0x3230 /* '20' */ || (value4 >> 8) != '0') { + dprintk (KERN_DEBUG "Serial EPROM serial number does not begin with XTAN200; not an IDSL card.\n"); + xpds_data[card_num].is_sdsl = 1; + return; + } + + value1 = read_seprom_data (card_num, SEPROM_REVISION); + dprintk (KERN_DEBUG "Serial EPROM revision %d\n", value1); + sdata->seprom_revision = value1; + + value1 = read_seprom_data (card_num, SEPROM_HARDWARE_VERSION); + dprintk (KERN_DEBUG "Hardware version %d.%d\n", + value1 >> 8, value1 & 0xff); + sdata->hardware_version[0] = value1 >> 8; + sdata->hardware_version[1] = value1 & 0xff; + + value1 = read_seprom_data (card_num, SEPROM_SOFTWARE_VERSION); + dprintk (KERN_DEBUG "Software version %d.%d\n", + value1 >> 8, value1 & 0xff); + sdata->software_version[0] = value1 >> 8; + sdata->software_version[1] = value1 & 0xff; + + value1 = read_seprom_data (card_num, SEPROM_FIRMWARE_VERSION); + dprintk (KERN_DEBUG "Firmware version %d.%d\n", + value1 >> 8, value1 & 0xff); + sdata->firmware_version[0] = value1 >> 8; + sdata->firmware_version[1] = value1 & 0xff; + + value1 = read_seprom_data (card_num, SEPROM_MFG_DATE_HI); + value2 = read_seprom_data (card_num, SEPROM_MFG_DATE_LO); + dprintk (KERN_DEBUG "Manufacturing date %d.%d.%d\n", + value1, value2 >> 8, value2 & 0xff); + sdata->mfg_date[0] = value1 >> 8; + sdata->mfg_date[1] = value1 & 0xff; + sdata->mfg_date[2] = value2 >> 8; + sdata->mfg_date[3] = value2 & 0xff; + + value1 = read_seprom_data (card_num, SEPROM_MAC_ADDR_0_1); + value2 = read_seprom_data (card_num, SEPROM_MAC_ADDR_2_3); + value3 = read_seprom_data (card_num, SEPROM_MAC_ADDR_4_5); + dprintk (KERN_DEBUG "MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", + value1 >> 8, value1 & 0xff, value2 >> 8, value2 & 0xff, + value3 >> 8, value3 & 0xff); + sdata->mac_address[0] = value1 >> 8; + sdata->mac_address[1] = value1 & 0xff; + sdata->mac_address[2] = value2 >> 8; + sdata->mac_address[3] = value2 & 0xff; + sdata->mac_address[4] = value3 >> 8; + sdata->mac_address[5] = value3 & 0xff; + + dprintk (KERN_DEBUG "Serial number "); + value1 = read_seprom_data (card_num, SEPROM_SERIAL_0); + dprintk ("%c%c", value1 >> 8, value1 & 0xff); + sdata->serial_number[0] = value1 >> 8; + sdata->serial_number[1] = value1 & 0xff; + value1 = read_seprom_data (card_num, SEPROM_SERIAL_1); + dprintk ("%c%c", value1 >> 8, value1 & 0xff); + sdata->serial_number[2] = value1 >> 8; + sdata->serial_number[3] = value1 & 0xff; + value1 = read_seprom_data (card_num, SEPROM_SERIAL_2); + dprintk ("%c%c", value1 >> 8, value1 & 0xff); + sdata->serial_number[4] = value1 >> 8; + sdata->serial_number[5] = value1 & 0xff; + value1 = read_seprom_data (card_num, SEPROM_SERIAL_3); + dprintk ("%c%c", value1 >> 8, value1 & 0xff); + sdata->serial_number[6] = value1 >> 8; + sdata->serial_number[7] = value1 & 0xff; + value1 = read_seprom_data (card_num, SEPROM_SERIAL_4); + dprintk ("%c%c", value1 >> 8, value1 & 0xff); + sdata->serial_number[8] = value1 >> 8; + sdata->serial_number[9] = value1 & 0xff; + value1 = read_seprom_data (card_num, SEPROM_SERIAL_5); + dprintk ("%c%c", value1 >> 8, value1 & 0xff); + sdata->serial_number[10] = value1 >> 8; + sdata->serial_number[11] = value1 & 0xff; + value1 = read_seprom_data (card_num, SEPROM_SERIAL_6); + dprintk ("%c%c", value1 >> 8, value1 & 0xff); + sdata->serial_number[12] = value1 >> 8; + sdata->serial_number[13] = value1 & 0xff; + value1 = read_seprom_data (card_num, SEPROM_SERIAL_7); + dprintk ("%c%c", value1 >> 8, value1 & 0xff); + sdata->serial_number[14] = value1 >> 8; + sdata->serial_number[15] = value1 & 0xff; + dprintk ("\n"); +} + +/* + * Read information from the SDSL card (previously identified + * because the serial EPROM did not exist). + * Note that if the mailbox reads fail, it is probably an IDSL + * FPGA card. + */ +static int +read_sdsl_info (int card_num) +{ + u8 byte, byte1, byte2; + int i, rc, rval = 0; + xpds_serial_data_t *sdata; + + if (! xpds_data[card_num].is_sdsl) return 1; + + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_HWVER, &byte1); + if (rc) rval = 1; + + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_HWVER, &byte2); + if (rc) rval = 1; + + if (rval) { + /* IDSL FPGA card */ + xpds_data[card_num].is_sdsl = 0; + return rval; + } + + sdata = &(xpds_data[card_num].serial_data); + + dprintk (KERN_INFO "Hardware version "); + dprintk ("%d.", byte1); + dprintk ("%d\n", byte2); + + sdata->hardware_version[0] = byte1; + sdata->hardware_version[1] = byte2; + + dprintk (KERN_INFO "Firmware version "); + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_FWVER, &byte); + dprintk ("%d.", byte); + if (rc) rval = 1; + sdata->firmware_version[0] = byte; + + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_FWVER, &byte); + dprintk ("%d\n", byte); + if (rc) rval = 1; + sdata->firmware_version[1] = byte; + + if (rval) return rval; + + dprintk (KERN_INFO "Manufacturing date "); + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_MFGDATE, &byte1); + if (rc) rval = 1; + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_MFGDATE, &byte2); + if (rc) rval = 1; + dprintk ("%d.", (byte1 << 8) + byte2); + sdata->mfg_date[0] = byte1; + sdata->mfg_date[1] = byte2; + + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_MFGDATE, &byte); + if (rc) rval = 1; + dprintk ("%d.", byte); + sdata->mfg_date[2] = byte; + + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_MFGDATE, &byte); + if (rc) rval = 1; + dprintk ("%d\n", byte); + sdata->mfg_date[3] = byte; + + if (rval) return rval; + + dprintk (KERN_INFO "MAC address "); + for (i = 0; i < 6; i ++) { + if (i != 0) dprintk (":"); + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_MACADDR, &byte); + dprintk ("%02x", byte); + if (rc) rval = 1; + sdata->mac_address[i] = byte; + } + dprintk ("\n"); + + if (rval) return rval; + + dprintk (KERN_INFO "Serial number "); + for (i = 0; i < 16; i ++) { + rc = xpds_mailbox_read (card_num, XPDS_MBX_READ_SERIALNUMBER, &byte); + dprintk ("%c", byte); + if (rc) rval = 1; + sdata->serial_number[i] = byte; + } + dprintk ("\n"); + + return rval; +} + +static int +get_pci_config (struct pci_dev *dev, volatile void **config_mem) +{ + u32 value; + int rc; + + rc = pci_read_config_dword (dev, PCI_BASE_ADDRESS_0, &value); + + if (rc != PCIBIOS_SUCCESSFUL) { + printk (KERN_ERR "unable to read PCI configuration register 4, error = %d\n", rc); + return -EIO; + } + + dprintk (KERN_DEBUG "PCI config register 4 value=%08x\n", value); + + value &= PCI_BASE_ADDRESS_IO_MASK; + + *config_mem = ioremap (value, 32768); + + dprintk (KERN_DEBUG "remapped hw_config_mem=%08x\n", (u32) *config_mem); +#if (LINUX_VERSION_CODE < 0x02030d) + dprintk (KERN_DEBUG "dev->base_address[0]=%08x\n", (u32) dev->base_address[0]); + dprintk (KERN_DEBUG "dev->base_address[1]=%08x\n", (u32) dev->base_address[1]); + dprintk (KERN_DEBUG "dev->base_address[2]=%08x\n", (u32) dev->base_address[2]); + dprintk (KERN_DEBUG "dev->base_address[3]=%08x\n", (u32) dev->base_address[3]); + dprintk (KERN_DEBUG "dev->base_address[4]=%08x\n", (u32) dev->base_address[4]); + dprintk (KERN_DEBUG "dev->base_address[5]=%08x\n", (u32) dev->base_address[5]); +#else + dprintk (KERN_DEBUG "dev->base_address[0]=%08x\n", (u32) dev->resource[0].start); + dprintk (KERN_DEBUG "dev->base_address[1]=%08x\n", (u32) dev->resource[1].start); + dprintk (KERN_DEBUG "dev->base_address[2]=%08x\n", (u32) dev->resource[2].start); + dprintk (KERN_DEBUG "dev->base_address[3]=%08x\n", (u32) dev->resource[3].start); + dprintk (KERN_DEBUG "dev->base_address[4]=%08x\n", (u32) dev->resource[4].start); + dprintk (KERN_DEBUG "dev->base_address[5]=%08x\n", (u32) dev->resource[5].start); +#endif + + return config_mem == NULL ? -ENOMEM : 0; +} + +#define TRIES 8 + +static int +allocate_rxtx_buffers (int xpds_num, int num_rxtx, volatile void **rxtx_mem, + volatile xpds_rxtx_list_t **rx_list, + volatile xpds_rxtx_list_t **tx_list) +{ + int size, i, j; + volatile u8 *rx_buffer, *tx_buffer; + volatile xpds_rxtx_list_t *rx_ptr, *tx_ptr; + volatile void *old_mem[TRIES]; + + size = (num_rxtx + num_rxtx) * sizeof (xpds_rxtx_list_t) + + (num_rxtx + num_rxtx) * RXTX_BUFFER_SIZE; + + /* + * Prevent RX/TX memory from crossing a 16 bit page (64k + * byte) boundary. This is due to a hardware limitation. + */ + memset (old_mem, 0, sizeof (old_mem)); + for (i = 0; i < TRIES; i ++) { + u32 start, end; + + *rxtx_mem = kmalloc (size + 16, GFP_KERNEL /* | GFP_DMA */); + if (*rxtx_mem == NULL) break; + + start = virt_to_bus (*rxtx_mem); + end = start + size + 16; + + start &= 0xffff; + end &= 0xffff; + + if (start < end) break; + + printk (KERN_DEBUG "%s: RX/TX buffer allocation crossed 16 bit boundary, reallocating.\n", xpds_devs[xpds_num].name); + old_mem[i] = *rxtx_mem; + } + for (j = 0; j < i; j ++) { + if (old_mem[j] != NULL) kfree ((void *)(old_mem[j])); + } + if (i == TRIES || *rxtx_mem == NULL) return -ENOMEM; + + xpds_data[xpds_num].rxtx_mem_allocated = *rxtx_mem; + + if ((u32)*rxtx_mem & 0xf) { + *rxtx_mem = (volatile void *) (((u32) *rxtx_mem + 16) & 0xfffffff0); + } + + *rx_list = (volatile xpds_rxtx_list_t *) *rxtx_mem; + *tx_list = (volatile xpds_rxtx_list_t *) ((u32) *rxtx_mem + + sizeof (xpds_rxtx_list_t) * num_rxtx); + + rx_buffer = (volatile u8 *) ((u32) *tx_list + + sizeof (xpds_rxtx_list_t) * num_rxtx); + tx_buffer = (volatile u8 *) ((u32) rx_buffer + + RXTX_BUFFER_SIZE * num_rxtx); + + rx_ptr = *rx_list; + tx_ptr = *tx_list; + + for (i = 0; i < num_rxtx; i ++) { + rx_ptr->control = RXTX_CONTROL__NEXT_VALID | RXTX_CONTROL__HWGO; + rx_ptr->buffer = rx_buffer + i * RXTX_BUFFER_SIZE; + rx_ptr->buffer_bus_addr = virt_to_bus (rx_ptr->buffer); + rx_ptr->next = (i == num_rxtx - 1) ? + *rx_list : + (volatile xpds_rxtx_list_t *) ((u32) rx_ptr + + sizeof (xpds_rxtx_list_t)); + rx_ptr->next_bus_addr = virt_to_bus (rx_ptr->next); + rx_ptr->unused1 = 0; + rx_ptr->prev = (i == 0) ? + (volatile xpds_rxtx_list_t *) ((u32) *rx_list + + sizeof (xpds_rxtx_list_t) * (num_rxtx - 1)) : + (volatile xpds_rxtx_list_t *) ((u32) rx_ptr - + sizeof (xpds_rxtx_list_t)); + rx_ptr->offset = 0; + rx_ptr = rx_ptr->next; + + tx_ptr->control = RXTX_CONTROL__NEXT_VALID; + tx_ptr->buffer = tx_buffer + i * RXTX_BUFFER_SIZE; + tx_ptr->buffer_bus_addr = virt_to_bus (tx_ptr->buffer); + tx_ptr->next = (i == num_rxtx - 1) ? + *tx_list : + (volatile xpds_rxtx_list_t *) ((u32) tx_ptr + + sizeof (xpds_rxtx_list_t)); + tx_ptr->next_bus_addr = virt_to_bus (tx_ptr->next); + tx_ptr->unused1 = 0; + tx_ptr->prev = (i == 0) ? + (volatile xpds_rxtx_list_t *) ((u32) *tx_list + + sizeof (xpds_rxtx_list_t) * (num_rxtx - 1)) : + (volatile xpds_rxtx_list_t *) ((u32) tx_ptr - + sizeof (xpds_rxtx_list_t)); + tx_ptr->offset = 0; + tx_ptr = tx_ptr->next; + } + + dprintk (KERN_DEBUG "rxtx_mem = %08x, rx_list = %08x, tx_list = %08x,\n", + (u32) *rxtx_mem, (u32) *rx_list, (u32) *tx_list); + dprintk (KERN_DEBUG "rx_buffer = %08x, tx_buffer = %08x\n", + (u32) rx_buffer, (u32) tx_buffer); + return 0; +} + +/* + * Given a pointer to memory, set the control register pointers. + */ +static int +set_control_register_pointers (int xpds_num, volatile void *mem, + volatile xpds_rxtx_list_t *rx_list, volatile xpds_rxtx_list_t *tx_list) +{ + int rc; + + dprintk (KERN_DEBUG "setting %s: control register pointers\n", xpds_devs[xpds_num].name); + xpds_data[xpds_num].main_control_registers = + (volatile u32 *) ( (volatile u8 *) mem + 0x0000 ); + xpds_data[xpds_num].rx_fifo_control_registers = + (volatile u32 *) ( (volatile u8 *) mem + 0x0050 ); + xpds_data[xpds_num].tx_fifo_control_registers = + (volatile u32 *) ( (volatile u8 *) mem + 0x0040 ); + xpds_data[xpds_num].rx_dma_control_registers = + (volatile u32 *) ( (volatile u8 *) mem + 0x00c0 ); + xpds_data[xpds_num].tx_dma_control_registers = + (volatile u32 *) ( (volatile u8 *) mem + 0x0080 ); + xpds_data[xpds_num].rx_fifo_data_registers = + (volatile u32 *) ( (volatile u8 *) mem + + (xpds_data[xpds_num].is_fpga ? 0x0600 : 0x0500) ); + xpds_data[xpds_num].tx_fifo_data_registers = + (volatile u32 *) ( (volatile u8 *) mem + 0x0400 ); + xpds_data[xpds_num].aux_registers = + (volatile u32 *) ( (volatile u8 *) mem + 0x0060 ); + dprintk (KERN_DEBUG "memory=%08x\n", (u32)mem); + dprintk (KERN_DEBUG "main_control_registers=%08x\n", + (u32)(xpds_data[xpds_num].main_control_registers)); + dprintk (KERN_DEBUG "rx_fifo_control_registers=%08x\n", + (u32)(xpds_data[xpds_num].rx_fifo_control_registers)); + dprintk (KERN_DEBUG "tx_fifo_control_registers=%08x\n", + (u32)(xpds_data[xpds_num].tx_fifo_control_registers)); + dprintk (KERN_DEBUG "rx_dma_control_registers=%08x\n", + (u32)(xpds_data[xpds_num].rx_dma_control_registers)); + dprintk (KERN_DEBUG "tx_dma_control_registers=%08x\n", + (u32)(xpds_data[xpds_num].tx_dma_control_registers)); + dprintk (KERN_DEBUG "rx_fifo_data_registers=%08x\n", + (u32)(xpds_data[xpds_num].rx_fifo_data_registers)); + dprintk (KERN_DEBUG "tx_fifo_data_registers=%08x\n", + (u32)(xpds_data[xpds_num].tx_fifo_data_registers)); + dprintk (KERN_DEBUG "aux_registers=%08x\n", + (u32)(xpds_data[xpds_num].aux_registers)); + + /* + * Install the buffer lists for RX and TX DMA. + */ + xpds_data[xpds_num].rx_dma_list = rx_list; + dprintk (KERN_DEBUG "initializing %s: RX DMA Status Address:\n", xpds_devs[xpds_num].name); + rc = xpds_write_control_register (xpds_num, XPDS_DMA_STAT_ADDR, + (u32) rx_list, RX_DMA); + if (rc != 0) return rc; + dprintk ("\n"); + xpds_data[xpds_num].tx_dma_list = tx_list; + dprintk (KERN_DEBUG "initializing %s: TX DMA Status Address:\n", xpds_devs[xpds_num].name); + rc = xpds_write_control_register (xpds_num, XPDS_DMA_STAT_ADDR, + (u32) tx_list, TX_DMA); + dprintk ("\n"); + if (rc != 0) return rc; + + return 0; +} + +static int +xpds_alloc_data (void) +{ + int rc; + + if (xpds_data == NULL) { + xpds_data = kmalloc (sizeof (*xpds_data) * xpds_max_cards, GFP_KERNEL); + if (xpds_data == NULL) return -ENOMEM; + memset (xpds_data, 0, sizeof (*xpds_data) * xpds_max_cards); + } + if (xpds_names == NULL) { + xpds_names = kmalloc (NAME_SIZE * xpds_max_cards, GFP_KERNEL); + if (xpds_names == NULL) return -ENOMEM; + memset (xpds_names, 0, NAME_SIZE * xpds_max_cards); + } + if (xpds_reset_bh_tasks == NULL) { + xpds_reset_bh_tasks = kmalloc (sizeof (*xpds_reset_bh_tasks) * xpds_max_cards, GFP_KERNEL); + if (xpds_reset_bh_tasks == NULL) return -ENOMEM; + memset (xpds_reset_bh_tasks, 0, sizeof (*xpds_reset_bh_tasks) * xpds_max_cards); + } + if (xpds_interrupt_bh_tasks == NULL) { + int i; + xpds_interrupt_bh_tasks = kmalloc (sizeof (*xpds_interrupt_bh_tasks) * xpds_max_cards, GFP_KERNEL); + if (xpds_interrupt_bh_tasks == NULL) return -ENOMEM; + memset (xpds_interrupt_bh_tasks, 0, sizeof (*xpds_interrupt_bh_tasks) * xpds_max_cards); + for (i = 0; i < xpds_max_cards; i ++) { + xpds_interrupt_bh_tasks[i].data = kmalloc (sizeof (interrupt_bh_data_t), GFP_KERNEL); + xpds_interrupt_bh_tasks[i].next = NULL; + xpds_interrupt_bh_tasks[i].sync = 0; + xpds_interrupt_bh_tasks[i].routine = xpds_interrupt_bh; + } + } + if (xpds_devs == NULL) { + int i; + xpds_devs = kmalloc (sizeof (*xpds_devs) * xpds_max_cards, GFP_KERNEL); + if (xpds_devs == NULL) return -ENOMEM; + memset (xpds_devs, 0, sizeof (*xpds_devs) * xpds_max_cards); + + for (i = 0; i < xpds_max_cards; i ++) { +#if (LINUX_VERSION_CODE < HAS_SOFT_NET) + xpds_devs[i].name = xpds_names + NAME_SIZE * i; +#endif + xpds_devs[i].init = xpds_init; + } + } + rc = xpds_sdsl_allocate (); + if (rc) return rc; + return 0; +} + +static void +xpds_free_data (void) +{ + if (xpds_data != NULL) kfree (xpds_data); + if (xpds_names != NULL) kfree (xpds_names); + if (xpds_reset_bh_tasks != NULL) kfree (xpds_reset_bh_tasks); + if (xpds_devs != NULL) kfree (xpds_devs); + xpds_sdsl_cleanup (); +} + +#ifdef MODULE +int init_module (void) +#else +int xpds_init(void) +#endif +{ + int i; + int rc; + volatile xpds_rxtx_list_t *rx_list, *tx_list; + volatile void *rxtx_mem; + int found_sdsl = 0; + struct pci_dev *dev = NULL; +#if ALLOW_OLD_PCI_VENDOR_ID + int old_offset = -1; +#endif + + printk (KERN_NOTICE "xpds.c: Xpeed XPDS frame relay driver %s, linux@xpeed.com, Copyright 1999, 2000 Xpeed, Inc.\n", VERSION_STRING); + rc = xpds_alloc_data (); + if (rc != 0) { + printk (KERN_ERR "Unable to allocate memory for XPDS data.\n"); + xpds_free_data (); + return rc; + } + + dprintk (KERN_DEBUG "xpds_data = %p, &(xpds_data[0].rxci_interrupt_received) = %p\n", xpds_data, &(xpds_data[0].rxci_interrupt_received)); + + /* + * Make sure that there is a PCI present, and the + * desired device is there. + */ + if ( ! pci_present ()) { + printk (KERN_ERR "No PCI present.\n"); + xpds_free_data (); + return -ENODEV; + } + + for (i = 0; i < xpds_max_cards; i ++) { + char devname[NAME_SIZE], name[NAME_SIZE]; + int n; + + sprintf (devname, "%.*s%%d", NAME_SIZE - 4, xpds_dev_name); + n = dev_alloc_name (&(xpds_devs[i]), devname); + if (n < 0) { + printk(KERN_ERR "%s: dev_alloc_name failed (%d)\n", xpds_dev_name, n); + xpds_free_data (); + return -ENODEV; + } + sprintf (name, "%.*s%d", NAME_SIZE - 4, xpds_dev_name, n); + strcpy (xpds_devs[i].name, name); + } + + for (i = 0; i < xpds_max_cards; i ++) { + memset (&(xpds_data[i]), 0, sizeof (xpds_data[i])); + xpds_data[i].speed_mode = xpds_mode; + +#if ALLOW_OLD_PCI_VENDOR_ID + if (old_offset < 0) { + dev = pci_find_device (PCI_VENDOR_ID_XPDS, + PCI_DEVICE_ID_XPDS_1, dev); + if (dev == NULL) { + old_offset = i; + dev = pci_find_device (PCI_VENDOR_ID_XPDS_OLD, + PCI_DEVICE_ID_XPDS_1, dev); + } + } else { + dev = pci_find_device (PCI_VENDOR_ID_XPDS_OLD, + PCI_DEVICE_ID_XPDS_1, dev); + } +#else + dev = pci_find_device (PCI_VENDOR_ID_XPDS, + PCI_DEVICE_ID_XPDS_1, dev); +#endif + if (dev == NULL) break; + dprintk (KERN_DEBUG "dev = %p\n", dev); + + xpds_data[i].pci_dev = dev; + + pci_set_master (dev); + +#if ALLOW_OLD_PCI_VENDOR_ID + if (old_offset >= 0) { + dprintk (KERN_DEBUG "XPDS FPGA device %d at pci_bus=0x%02x, pci_dev_fn=0x%02x, pci_irq_line=0x%02x.\n", + i, + xpds_data[i].pci_dev->bus->number, + xpds_data[i].pci_dev->devfn, + xpds_data[i].pci_dev->irq); + xpds_data[i].is_fpga = 1; + } else { + dprintk (KERN_DEBUG "XPDS device %d found at pci_bus=0x%02x, pci_dev_fn=0x%02x, pci_irq_line=0x%02x.\n", + i, + xpds_data[i].pci_dev->bus->number, + xpds_data[i].pci_dev->devfn, + xpds_data[i].pci_dev->irq); + xpds_data[i].is_fpga = 0; + } +#else + dprintk (KERN_DEBUG "XPDS device %d found at pci_bus=0x%02x, pci_dev_fn=0x%02x, pci_irq_line=0x%02x.\n", + i, + xpds_data[i].pci_dev->bus->number, + xpds_data[i].pci_dev->devfn, + xpds_data[i].pci_dev->irq); + xpds_data[i].is_fpga = 0; +#endif + + /* + * Get the 32k of memory from hardware. + */ + dprintk (KERN_DEBUG "%s: setting up PCI configuration\n", xpds_devs[i].name); + rc = get_pci_config (xpds_data[i].pci_dev, + &(xpds_data[i].config_mem_remapped)); + if (rc != 0) { + xpds_free_data (); + return rc; + } + + /* + * Allocate the RX and TX lists and buffers. + */ + dprintk (KERN_DEBUG "%s: allocating RX/TX buffers\n", xpds_devs[i].name); + rc = allocate_rxtx_buffers (i, NUM_DESC, &rxtx_mem, + &rx_list, &tx_list); + if (rc != 0) { + xpds_free_data (); + return rc; + } + + /* + * Set up the control register pointers to the RX/TX buffers. + */ + rc = set_control_register_pointers (i, + xpds_data[i].config_mem_remapped, rx_list, tx_list); + if (rc != 0) { + xpds_free_data (); + return rc; + } + + /* + * Read the serial EPROM for some more information + * about the device. May set xpds_data[i].is_sdsl or + * xpds_data[i].has_last_byte_bug . + */ + read_seprom_info (i); + + /* + * If the serial EPROM read failed, then it should be + * an SDSL device. Read the information from the SDSL + * device. If an error, then something is wrong... + */ + if (xpds_data[i].is_sdsl) { + xpds_reset_sdsl (i); + xpds_start_sdsl (i); + DELAY_HZ (3 * HZ / 2, i); + rc = read_sdsl_info (i); + if (rc != 0) { + if (xpds_data[i].is_sdsl) { + printk (KERN_ERR "%s: Unable to determine if %s is an IDSL or SDSL card.\n", xpds_devs[i].name, xpds_devs[i].name); + printk (KERN_ERR "%s: %s may be defective\n", xpds_devs[i].name, xpds_devs[i].name); + if (! xpds_load_for_flash) { + xpds_free_data (); + return -ENODEV; + } + } else { + /* FPGA IDSL board */ + printk (KERN_DEBUG "%s: %s has no SDSL infomation.\n", xpds_devs[i].name, xpds_devs[i].name); + printk (KERN_DEBUG "%s: is assumed to be an FPGA IDSL card.\n", xpds_devs[i].name); + } + } + } + + /* + * Hardware bugs: + * ASIC < 1.1: last byte corruption bug + * hardware < 1.1: RX DMA burst bug + * hardware < 1.2: TX DMA burst bug + */ + if (! xpds_data[i].is_fpga && ! xpds_is_hardware_version (i, 1, 1)) { + dprintk (KERN_DEBUG "Has last byte bug, using workaround.\n"); + xpds_data[i].has_last_byte_bug = 1; + } + + if (! xpds_is_hardware_version (i, 1, 1)) { + dprintk (KERN_DEBUG "Has RX DMA burst bug, not using RX DMA burst mode.\n"); + xpds_data[i].has_rx_dma_burst_bug = 1; + } + + if (! xpds_is_hardware_version (i, 1, 2)) { + dprintk (KERN_DEBUG "Has TX DMA burst bug, not using TX DMA burst mode.\n"); + xpds_data[i].has_tx_dma_burst_bug = 1; + dprintk (KERN_DEBUG "Has TX DMA low rate (<%d Kbps) bug.\n", LOW_BIT_RATE); + xpds_data[i].has_tx_dma_low_rate_bug = 1; + } + + printk (KERN_DEBUG "%s: Xpeed %c00 %cDSL NIC, %02X %02X %02X %02X %02X %02X, IRQ %d.\n", + xpds_devs[i].name, + xpds_data[i].is_sdsl ? '3' : '2', + xpds_data[i].is_sdsl ? 'S' : 'I', + xpds_data[i].serial_data.mac_address[0], + xpds_data[i].serial_data.mac_address[1], + xpds_data[i].serial_data.mac_address[2], + xpds_data[i].serial_data.mac_address[3], + xpds_data[i].serial_data.mac_address[4], + xpds_data[i].serial_data.mac_address[5], + xpds_data[i].pci_dev->irq); + + if (xpds_data[i].is_sdsl) { + int rc; + u32 mode, speed, swap, invert, nt; + + rc = xpds_get_flash_sdsl_mode (i, &mode); + + if (rc) { + printk (KERN_ERR "%s: unable to get speed, swap, and invert\n", xpds_devs[i].name); + } else { + speed = (mode & XPDS_SDSL_MODE__SPEED_MASK) << 3; + nt = (mode & XPDS_SDSL_MODE__NT); + swap = (mode & XPDS_SDSL_MODE__SWAP); + invert = (mode & XPDS_SDSL_MODE__INVERT); + printk (KERN_DEBUG "%s: speed = %d%s, swap %s, invert %s, %s mode\n", xpds_devs[i].name, speed, (speed == 0) ? " (CM auto)" : "", swap ? "on" : "off", invert ? "on" : "off", nt ? "NT" : "LT"); + } + } + + if (xpds_data[i].is_sdsl) found_sdsl = 1; + + /* + * Register network device. + */ + rc = register_netdev (&(xpds_devs[i])); + if (rc != 0) { + xpds_free_data (); + return rc; + } + } + + num_xpds_found = i; + + if (num_xpds_found < 1) { + printk (KERN_ERR "PCI error: XPDS device not found.\n"); + xpds_free_data (); + return -ENODEV; + } + + return 0; +} + +void cleanup_module (void) +{ + int i; + + for (i = 0; i < num_xpds_found; i ++) { + if (xpds_data[i].rxtx_mem_allocated != NULL) { + kfree ((void *)(xpds_data[i].rxtx_mem_allocated)); + } + if (xpds_data[i].config_mem_remapped != NULL) { + iounmap ((void *)(xpds_data[i].config_mem_remapped)); + } + dprintk (KERN_DEBUG "unregistering XPDS network device %d\n", i); + unregister_netdev (&(xpds_devs[i])); + } + xpds_free_data (); + dprintk (KERN_DEBUG "removing Xpeed XPDS frame relay driver\n"); +} diff --git a/drivers/net/xpds/xpds.h b/drivers/net/xpds/xpds.h new file mode 100644 index 000000000000..30d10f0c7c8e --- /dev/null +++ b/drivers/net/xpds/xpds.h @@ -0,0 +1,121 @@ +/* + * Copyright 1998, 1999, 2000 Xpeed, Inc. + * xpds-fr.h, $Revision: 1.10 $ + */ +#ifndef XPDS_H +#define XPDS_H 1 + +#include +#include "xpds-encap-fr.h" +#include + +extern int xpds_max_cards; + +#define DEBUG_MAIN 1 +#define DEBUG_FSM 2 +#define DEBUG_DETAILED 128 + +extern int xpds_debug_level; + +typedef struct xpds_rxtx_list_t { + volatile u32 control; + u32 buffer_bus_addr; + u32 next_bus_addr; + u32 unused1; + volatile u8 *buffer; + volatile struct xpds_rxtx_list_t *next; + volatile struct xpds_rxtx_list_t *prev; + u32 offset; +} xpds_rxtx_list_t; + +typedef struct { + struct pci_dev *pci_dev; + volatile u32 *main_control_registers; + volatile u32 *rx_fifo_control_registers; + volatile u32 *tx_fifo_control_registers; + volatile u32 *rx_dma_control_registers; + volatile u32 *tx_dma_control_registers; + volatile u32 *rx_fifo_data_registers; + volatile u32 *tx_fifo_data_registers; + volatile u32 *aux_registers; + volatile void *rxtx_mem_allocated; + volatile xpds_rxtx_list_t *rx_dma_list; + volatile xpds_rxtx_list_t *tx_dma_list; + volatile xpds_rxtx_list_t *current_rx_dma; + volatile xpds_rxtx_list_t *current_tx_dma; + struct net_device_stats stats; + volatile int rxci_interrupt_received; + int physical_up; + int physical_retrying; + int is_fpga; + int has_last_byte_bug; + int is_sdsl; + int is_lt; + int speed_mode; + short dlci; + xpds_serial_data_t serial_data; + int has_rx_dma_burst_bug; + int has_tx_dma_burst_bug; + volatile void *config_mem_remapped; + int has_tx_dma_low_rate_bug; + int sdsl_speed; + int current_tx_fifo; + volatile xpds_rxtx_list_t *current_hw_tx_dma; + int current_rx_fifo; + volatile xpds_rxtx_list_t *current_hw_rx_dma; + int has_rx_dma_low_rate_bug; + struct frad_local frad_data; + int bridged_ethernet; + u8 dlci_cr; + u8 dlci_lmi; + dlci_lmi_timer_data_t dlci_lmi_timer_data[CONFIG_DLCI_COUNT]; + struct timer_list dlci_lmi_timers[CONFIG_DLCI_COUNT]; +} xpds_data_t; + +extern struct net_device *xpds_devs; +extern xpds_data_t *xpds_data; + +extern int xpds_read_control_register_quiet (int xpds_num, int register_number, + u32 *value, int which); +extern int xpds_write_control_register_quiet (int xpds_num, int register_number, + u32 value, int which); +extern int xpds_read_control_register (int xpds_num, int register_number, + u32 *value, int which); +extern int xpds_write_control_register (int xpds_num, int register_number, + u32 value, int which); + +#define XPDS_MAIN 0 +#define XPDS_RX_FIFO 2 +#define XPDS_TX_FIFO 3 +#define XPDS_RX_DMA 4 +#define XPDS_TX_DMA 5 +#define XPDS_RX_FIFO_DATA 6 +#define XPDS_TX_FIFO_DATA 7 +#define XPDS_AUX 8 + +#define MAIN XPDS_MAIN +#define RX_FIFO XPDS_RX_FIFO +#define TX_FIFO XPDS_TX_FIFO +#define RX_DMA XPDS_RX_DMA +#define TX_DMA XPDS_TX_DMA +#define RX_FIFO_DATA XPDS_RX_FIFO_DATA +#define TX_FIFO_DATA XPDS_TX_FIFO_DATA +#define AUX XPDS_AUX + +extern int xpds_tx (u8 *buffer, unsigned int len, struct net_device *dev); + +#ifdef __SMP__ +#define schedule_if_no_interrupt(card_num) \ + do { \ + } while (0) +#else +#define schedule_if_no_interrupt(card_num) \ + do { \ + if (!in_interrupt()) schedule (); \ + } while (0) +#endif + +#define DELAY(n,card_num) schedule_timeout(HZ*(n)) +#define DELAY_HZ(n,card_num) schedule_timeout(n) + +#endif diff --git a/drivers/nubus/Makefile b/drivers/nubus/Makefile index 781edd0521a9..4d42035c3ec5 100644 --- a/drivers/nubus/Makefile +++ b/drivers/nubus/Makefile @@ -9,7 +9,19 @@ # parent makefile. # -L_OBJS := nubus.o L_TARGET := nubus.a +ifeq ($(CONFIG_MODULES),y) +O_TARGET := nubus_n_syms.o +OX_OBJS := nubus_syms.o +O_OBJS := nubus.o +L_OBJS := nubus_n_syms.o +else +L_OBJS := nubus.o +endif + +ifdef CONFIG_PROC_FS +L_OBJS += proc.o +endif + include $(TOPDIR)/Rules.make diff --git a/drivers/nubus/nubus.c b/drivers/nubus/nubus.c index 483d2f8968b6..ae7451a26260 100644 --- a/drivers/nubus/nubus.c +++ b/drivers/nubus/nubus.c @@ -1,78 +1,112 @@ /* * Macintosh Nubus Interface Code + * + * Originally by Alan Cox + * + * Mostly rewritten by David Huggins-Daines, C. Scott Ananian, + * and others. */ #include #include #include #include -#include -#include #include #include #include +#include +#include #include #include #include #include -/* for LCIII stuff; better find a general way like MACH_HAS_NUBUS */ -#include #include +#include +#include +extern void via_nubus_init(void); +extern void oss_nubus_init(void); -#undef LCIII_WEIRDNESS - -static struct nubus_slot nubus_slots[16]; - -/* - * Please skip to the bottom of this file if you ate lunch recently - * -- Alan - */ - +/* Constants */ -/* - * Yes this sucks. The ROM can appear on arbitary bytes of the long - * word. We are not amused. - */ +/* This is, of course, the size in bytelanes, rather than the size in + actual bytes */ +#define FORMAT_BLOCK_SIZE 20 +#define ROM_DIR_OFFSET 0x24 + +#define NUBUS_TEST_PATTERN 0x5A932BC7 + +/* Globals */ + +struct nubus_dev* nubus_devices; +struct nubus_board* nubus_boards; + +extern int console_loglevel; + +/* Meaning of "bytelanes": + + The card ROM may appear on any or all bytes of each long word in + NuBus memory. The low 4 bits of the "map" value found in the + format block (at the top of the slot address space, as well as at + the top of the MacOS ROM) tells us which bytelanes, i.e. which byte + offsets within each longword, are valid. Thus: + + A map of 0x0f, as found in the MacOS ROM, means that all bytelanes + are valid. + + A map of 0xf0 means that no bytelanes are valid (We pray that we + will never encounter this, but stranger things have happened) + + A map of 0xe1 means that only the MSB of each long word is actually + part of the card ROM. (We hope to never encounter NuBus on a + little-endian machine. Again, stranger things have happened) + + A map of 0x78 means that only the LSB of each long word is valid. + + Etcetera, etcetera. Hopefully this clears up some confusion over + what the following code actually does. */ -extern __inline__ int not_useful(void *p, int map) +extern inline int not_useful(void *p, int map) { unsigned long pv=(unsigned long)p; - pv&=3; - if(map&(1<65536) - printk("rewind of %d!\n", len); + + /* Sanity check */ + if(len > 65536) + printk(KERN_ERR "rewind of 0x%08x!\n", len); while(len) { do { p--; } - while(not_useful(p,map)); + while(not_useful(p, map)); len--; } *ptr=p; @@ -80,9 +114,9 @@ static void nubus_rewind(unsigned char **ptr, int len, int map) static void nubus_advance(unsigned char **ptr, int len, int map) { - unsigned char *p=*ptr; + unsigned char *p = *ptr; if(len>65536) - printk("advance of %d!\n", len); + printk(KERN_ERR "advance of 0x%08x!\n", len); while(len) { while(not_useful(p,map)) @@ -90,29 +124,32 @@ static void nubus_advance(unsigned char **ptr, int len, int map) p++; len--; } - *ptr=p; + *ptr = p; } -/* - * 24bit signed offset to 32bit - */ - -static unsigned long nubus_expand32(unsigned long foo) +static void nubus_move(unsigned char **ptr, int len, int map) { - if(foo&0x00800000) /* 24bit negative */ - foo|=0xFF000000; - return foo; + if(len > 0) + nubus_advance(ptr, len, map); + else if(len < 0) + nubus_rewind(ptr, -len, map); } -static void nubus_move(unsigned char **ptr, int len, int map) +/* Now, functions to read the sResource tree */ + +/* Each sResource entry consists of a 1-byte ID and a 3-byte data + field. If that data field contains an offset, then obviously we + have to expand it from a 24-bit signed number to a 32-bit signed + number. */ + +extern inline long nubus_expand32(long foo) { - if(len>0) - nubus_advance(ptr,len,map); - else if(len<0) - nubus_rewind(ptr,-len,map); + if(foo & 0x00800000) /* 24bit negative */ + foo |= 0xFF000000; + return foo; } -static void *nubus_rom_addr(int slot) +extern inline void *nubus_rom_addr(int slot) { /* * Returns the first byte after the card. We then walk @@ -121,472 +158,734 @@ static void *nubus_rom_addr(int slot) return (void *)(0xF1000000+(slot<<24)); } -void nubus_memcpy(int slot, void *to, unsigned char *p, int len) +static unsigned char *nubus_dirptr(const struct nubus_dirent *nd) { - unsigned char *t=(unsigned char *)to; + unsigned char *p = nd->base; + /* Essentially, just step over the bytelanes using whatever + offset we might have found */ + nubus_move(&p, nubus_expand32(nd->data), nd->mask); + /* And return the value */ + return p; +} + +/* These two are for pulling resource data blocks (i.e. stuff that's + pointed to with offsets) out of the card ROM. */ + +void nubus_get_rsrc_mem(void *dest, const struct nubus_dirent* dirent, + int len) +{ + unsigned char *t = (unsigned char *)dest; + unsigned char *p = nubus_dirptr(dirent); while(len) { - *t++=nubus_get_rom(&p,1, nubus_slots[slot].slot_lanes); + *t++ = nubus_get_rom(&p, 1, dirent->mask); len--; } } -void nubus_strncpy(int slot, void *to, unsigned char *p, int len) +void nubus_get_rsrc_str(void *dest, const struct nubus_dirent* dirent, + int len) { - unsigned char *t=(unsigned char *)to; + unsigned char *t=(unsigned char *)dest; + unsigned char *p = nubus_dirptr(dirent); while(len) { - *t=nubus_get_rom(&p,1, nubus_slots[slot].slot_lanes); + *t = nubus_get_rom(&p, 1, dirent->mask); if(!*t++) break; len--; } } - - - -unsigned char *nubus_dirptr(struct nubus_dirent *nd) +int nubus_get_root_dir(const struct nubus_board* board, + struct nubus_dir* dir) { - unsigned char *p=(unsigned char *)(nd->base); - - nubus_move(&p, nubus_expand32(nd->value), nd->mask); - return p; + dir->ptr = dir->base = board->directory; + dir->done = 0; + dir->mask = board->lanes; + return 0; } - -struct nubus_dir *nubus_openrootdir(int slot) +/* This is a slyly renamed version of the above */ +int nubus_get_func_dir(const struct nubus_dev* dev, + struct nubus_dir* dir) { - static struct nubus_dir nbdir; - unsigned char *rp=nubus_rom_addr(slot); - - nubus_rewind(&rp,20, nubus_slots[slot].slot_lanes); - - nubus_move(&rp, nubus_expand32(nubus_slots[slot].slot_directory), - nubus_slots[slot].slot_lanes); - - nbdir.base=rp; - nbdir.length=nubus_slots[slot].slot_dlength; - nbdir.count=0; - nbdir.mask=nubus_slots[slot].slot_lanes; - return &nbdir; + dir->ptr = dir->base = dev->directory; + dir->done = 0; + dir->mask = dev->board->lanes; + return 0; } -struct nubus_dir *nubus_opensubdir(struct nubus_dirent *d) +int nubus_get_board_dir(const struct nubus_board* board, + struct nubus_dir* dir) { - static struct nubus_dir nbdir; - unsigned char *rp=nubus_dirptr(d); - nbdir.base=rp; - nbdir.length=99999;/*slots[i].slot_dlength;*/ - nbdir.count=0; - nbdir.mask=d->mask; - return &nbdir; + struct nubus_dirent ent; + + dir->ptr = dir->base = board->directory; + dir->done = 0; + dir->mask = board->lanes; + + /* Now dereference it (the first directory is always the board + directory) */ + if (nubus_readdir(dir, &ent) == -1) + return -1; + if (nubus_get_subdir(&ent, dir) == -1) + return -1; + return 0; } -void nubus_closedir(struct nubus_dir *nd) +int nubus_get_subdir(const struct nubus_dirent *ent, + struct nubus_dir *dir) { - ; + dir->ptr = dir->base = nubus_dirptr(ent); + dir->done = 0; + dir->mask = ent->mask; + return 0; } -struct nubus_dirent *nubus_readdir(struct nubus_dir *nd) +int nubus_readdir(struct nubus_dir *nd, struct nubus_dirent *ent) { u32 resid; - u8 rescode; - static struct nubus_dirent d; - - if(nd->count==nd->length) - return NULL; + if (nd->done) + return -1; - d.base=(unsigned long)nd->base; - - resid=nubus_get_rom(&nd->base, 4, nd->mask); - nd->count++; - rescode=resid>>24; - if(rescode==0xFF) + /* Do this first, otherwise nubus_rewind & co are off by 4 */ + ent->base = nd->ptr; + + /* This moves nd->ptr forward */ + resid = nubus_get_rom(&nd->ptr, 4, nd->mask); + + /* EOL marker, as per the Apple docs */ + if((resid&0xff000000) == 0xff000000) { - nd->count=nd->length; - return NULL; + /* Mark it as done */ + nd->done = 1; + return -1; } - d.type=rescode; - d.value=resid&0xFFFFFF; - d.mask=nd->mask; - return &d; + + /* First byte is the resource ID */ + ent->type = resid >> 24; + /* Low 3 bytes might contain data (or might not) */ + ent->data = resid & 0xffffff; + ent->mask = nd->mask; + return 0; } -/* - * MAC video handling irritations - */ +int nubus_rewinddir(struct nubus_dir* dir) +{ + dir->ptr = dir->base; + dir->done = 0; + return 0; +} -static unsigned char nubus_vid_byte[16]; -static unsigned long nubus_vid_offset[16]; +/* Driver interface functions, more or less like in pci.c */ -static void nubus_irqsplat(int slot, void *dev_id, struct pt_regs *regs) +struct nubus_dev* +nubus_find_device(unsigned short category, + unsigned short type, + unsigned short dr_hw, + unsigned short dr_sw, + const struct nubus_dev* from) { - unsigned char *p=((unsigned char *)nubus_slot_addr(slot))+ - nubus_vid_offset[slot]; - *p=nubus_vid_byte[slot]; + struct nubus_dev* itor = + from ? from->next : nubus_devices; + + while (itor) { + if (itor->category == category + && itor->type == type + && itor->dr_hw == dr_hw + && itor->dr_sw == dr_sw) + return itor; + itor = itor->next; + } + return NULL; } -static int nubus_add_irqsplatter(int slot, unsigned long ptr, unsigned char v) +struct nubus_dev* +nubus_find_type(unsigned short category, + unsigned short type, + const struct nubus_dev* from) { - nubus_vid_byte[slot]=v; - nubus_vid_offset[slot]=ptr; - nubus_request_irq(slot, NULL, nubus_irqsplat); - return 0; + struct nubus_dev* itor = + from ? from->next : nubus_devices; + + while (itor) { + if (itor->category == category + && itor->type == type) + return itor; + itor = itor->next; + } + return NULL; } - -void nubus_video_shutup(int slot, struct nubus_type *nt) + +struct nubus_dev* +nubus_find_slot(unsigned int slot, + const struct nubus_dev* from) { - if(nt->category!=3 /* Display */ || nt->type!=1 /* Video */ - || nt->DrSW!=1 /* Quickdraw device */) - return; - switch(nt->DrHW) - { - /* - * Toby and MacII Hires cards. These behave in a MacII - * anyway but not on an RBV box - */ - case 0x0001: - case 0x0013: - nubus_add_irqsplatter(slot, 0xA0000, 0); - break; - /* - * Apple workstation video card. - */ - case 0x0006: - nubus_add_irqsplatter(slot, 0xA00000, 0); - break; - /* - * Futura cards - */ - case 0x0417: - case 0x042F: - nubus_add_irqsplatter(slot, 0xF05000, 0x80); - break; - - /* - * Fingers crossed 8) - * - * If you have another card and an RBV based mac you'll - * almost certainly have to add it here to make it work. - */ - - default: - break; + struct nubus_dev* itor = + from ? from->next : nubus_devices; + + while (itor) { + if (itor->board->slot == slot) + return itor; + itor = itor->next; } + return NULL; } -/* - * Device list - */ - -static struct nubus_device_specifier *nubus_device_list=NULL; - -void register_nubus_device(struct nubus_device_specifier *d) +int +nubus_find_rsrc(struct nubus_dir* dir, unsigned char rsrc_type, + struct nubus_dirent* ent) { - d->next=nubus_device_list; - nubus_device_list=d; + while (nubus_readdir(dir, ent) != -1) { + if (ent->type == rsrc_type) + return 0; + } + return -1; } -void unregister_nubus_device(struct nubus_device_specifier *nb) +/* Initialization functions - decide which slots contain stuff worth + looking at, and print out lots and lots of information from the + resource blocks. */ + +/* FIXME: A lot of this stuff will eventually be useful after + initializaton, for intelligently probing Ethernet and video chips, + among other things. The rest of it should go in the /proc code. + For now, we just use it to give verbose boot logs. */ + +static int __init nubus_show_display_resource(struct nubus_dev* dev, + const struct nubus_dirent* ent) { - struct nubus_device_specifier **t=&nubus_device_list; - while(*t!=nb && *t) - t=&((*t)->next); - *t=nb->next; + switch (ent->type) { + case NUBUS_RESID_GAMMADIR: + printk(KERN_INFO " gamma directory offset: 0x%06x\n", ent->data); + break; + case 0x0080 ... 0x0085: + printk(KERN_INFO " mode %02X info offset: 0x%06x\n", + ent->type, ent->data); + break; + default: + printk(KERN_INFO " unknown resource %02X, data 0x%06x\n", + ent->type, ent->data); + } + return 0; } -static struct nubus_device_specifier *find_nubus_device(int slot, struct nubus_type *nt) +static int __init nubus_show_network_resource(struct nubus_dev* dev, + const struct nubus_dirent* ent) { - struct nubus_device_specifier *t=nubus_device_list; - while(t!=NULL) + switch (ent->type) { + case NUBUS_RESID_MAC_ADDRESS: { - if(t->setup(t,slot, nt)==0) - return t; - t=t->next; + char addr[6]; + int i; + + nubus_get_rsrc_mem(addr, ent, 6); + printk(KERN_INFO " MAC address: "); + for (i = 0; i < 6; i++) + printk("%02x%s", addr[i] & 0xff, + i == 5 ? "" : ":"); + printk("\n"); + break; } - printk("No driver for device [%d %d %d %d]\n", - nt->category, nt->type, nt->DrHW, nt->DrSW); - return NULL; + default: + printk(KERN_INFO " unknown resource %02X, data 0x%06x\n", + ent->type, ent->data); + } + return 0; } -/* - * Probe a nubus slot - */ +static int __init nubus_show_cpu_resource(struct nubus_dev* dev, + const struct nubus_dirent* ent) +{ + switch (ent->type) { + case NUBUS_RESID_MEMINFO: + { + unsigned long meminfo[2]; + nubus_get_rsrc_mem(&meminfo, ent, 8); + printk(KERN_INFO " memory: [ 0x%08lx 0x%08lx ]\n", + meminfo[0], meminfo[1]); + break; + } + case NUBUS_RESID_ROMINFO: + { + unsigned long rominfo[2]; + nubus_get_rsrc_mem(&rominfo, ent, 8); + printk(KERN_INFO " ROM: [ 0x%08lx 0x%08lx ]\n", + rominfo[0], rominfo[1]); + break; + } + default: + printk(KERN_INFO " unknown resource %02X, data 0x%06x\n", + ent->type, ent->data); + } + return 0; +} -void nubus_probe_slot(int slot, int mode) +static int __init nubus_show_private_resource(struct nubus_dev* dev, + const struct nubus_dirent* ent) { - unsigned char *rp; - unsigned char dp; - int lanes; - int i; - unsigned long dpat; - struct nubus_dir *dir; - struct nubus_dirent *nd; - struct nubus_type type_info; + switch (dev->category) { + case NUBUS_CAT_DISPLAY: + nubus_show_display_resource(dev, ent); + break; + case NUBUS_CAT_NETWORK: + nubus_show_network_resource(dev, ent); + break; + case NUBUS_CAT_CPU: + nubus_show_cpu_resource(dev, ent); + break; + default: + printk(KERN_INFO " unknown resource %02X, data 0x%06x\n", + ent->type, ent->data); + } + return 0; +} - /* - * Ok see whats cooking in the bytelanes - */ +static struct nubus_dev* __init +nubus_get_functional_resource(struct nubus_board* board, + int slot, + const struct nubus_dirent* parent) +{ + struct nubus_dir dir; + struct nubus_dirent ent; + struct nubus_dev* dev; - rp=nubus_rom_addr(slot); + printk(KERN_INFO " Function 0x%02x:\n", parent->type); + nubus_get_subdir(parent, &dir); + + printk(KERN_DEBUG "nubus_get_functional_resource: parent is 0x%p, dir is 0x%p\n", + parent->base, dir.base); + + /* Actually we should probably panic if this fails */ + if ((dev = kmalloc(sizeof(*dev), GFP_ATOMIC)) == NULL) + return NULL; + memset(dev, 0, sizeof(*dev)); + dev->resid = parent->type; + dev->directory = dir.base; + dev->board = board; - for(i=4;i;i--) + while (nubus_readdir(&dir, &ent) != -1) { - rp--; - - if(!hwreg_present(rp)) - continue; - - dp=*rp; - - if(dp==0) - continue; - - /* - * Valid ? - */ - - if((((dp>>4)^dp)&0x0F)!=0x0F) - continue; - - if((dp&0x0F) >= 1<type==0x80/*RES_ID_BOARD_DIR*/) - break; + unsigned short nbtdata[4]; + nubus_get_rsrc_mem(nbtdata, &ent, 8); + dev->category = nbtdata[0]; + dev->type = nbtdata[1]; + dev->dr_sw = nbtdata[2]; + dev->dr_hw = nbtdata[3]; + printk(KERN_INFO " type: [cat 0x%x type 0x%x sw 0x%x hw 0x%x]\n", + nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]); + break; } - - nubus_closedir(dir); - - if(nd==NULL) + case NUBUS_RESID_NAME: { - printk("board resource not found!\n"); - return; + nubus_get_rsrc_str(dev->name, &ent, 64); + printk(KERN_INFO " name: %s\n", dev->name); + break; } - - dir=nubus_opensubdir(nd); - - /* - * Walk the board resource - */ - - while((nd=nubus_readdir(dir))!=NULL) + case NUBUS_RESID_DRVRDIR: { - switch(nd->type) - { - case RES_ID_TYPE: - { - unsigned short nbtdata[4]; - nubus_memcpy(slot, nbtdata, nubus_dirptr(nd), 8); - type_info.category=nbtdata[0]; - type_info.type=nbtdata[1]; - type_info.DrHW=nbtdata[2]; - type_info.DrSW=nbtdata[3]; - break; - } - case RES_ID_NAME: - nubus_strncpy(slot, nubus_slots[slot].slot_cardname,nubus_dirptr(nd),64); - break; - default: - ; - } + /* MacOS driver. If we were NetBSD we might + use this :-) */ + struct nubus_dir drvr_dir; + struct nubus_dirent drvr_ent; + nubus_get_subdir(&ent, &drvr_dir); + nubus_readdir(&drvr_dir, &drvr_ent); + dev->driver = nubus_dirptr(&drvr_ent); + printk(KERN_INFO " driver at: 0x%p\n", + dev->driver); + break; } - - nubus_closedir(dir); - - /* - * Attempt to bind a driver to this slot - */ - - if (mode==0) { - printk("%s\n", - nubus_slots[slot].slot_cardname); - find_nubus_device(slot,&type_info); + case NUBUS_RESID_MINOR_BASEOS: + /* We will need this in order to support + multiple framebuffers. It might be handy + for Ethernet as well */ + nubus_get_rsrc_mem(&dev->iobase, &ent, 4); + printk(KERN_INFO " memory offset: 0x%08lx\n", + dev->iobase); + break; + case NUBUS_RESID_MINOR_LENGTH: + /* Ditto */ + nubus_get_rsrc_mem(&dev->iosize, &ent, 4); + printk(KERN_INFO " memory length: 0x%08lx\n", + dev->iosize); + break; + case NUBUS_RESID_FLAGS: + dev->flags = ent.data; + printk(KERN_INFO " flags: 0x%06x\n", dev->flags); + break; + case NUBUS_RESID_HWDEVID: + dev->hwdevid = ent.data; + printk(KERN_INFO " hwdevid: 0x%06x\n", dev->hwdevid); + break; + default: + /* Local/Private resources have their own + function */ + nubus_show_private_resource(dev, &ent); } - if (mode==1) - nubus_video_shutup(slot, &type_info); - - return; } + + return dev; } - -void nubus_probe_bus(void) +/* This is cool. */ +static int __init nubus_get_vidnames(struct nubus_board* board, + const struct nubus_dirent* parent) { - int i; - for(i=9;i<15;i++) + struct nubus_dir dir; + struct nubus_dirent ent; + /* FIXME: obviously we want to put this in a header file soon */ + struct vidmode { + u32 size; + /* Don't know what this is yet */ + u16 id; + /* Longest one I've seen so far is 26 characters */ + char name[32]; + }; + + printk(KERN_INFO " video modes supported:\n"); + nubus_get_subdir(parent, &dir); + printk(KERN_DEBUG "nubus_get_vidnames: parent is 0x%p, dir is 0x%p\n", + parent->base, dir.base); + + while(nubus_readdir(&dir, &ent) != -1) { - /* printk("nubus: probing slot %d !\n", i); */ - nubus_probe_slot(i, 0); + struct vidmode mode; + u32 size; + + /* First get the length */ + nubus_get_rsrc_mem(&size, &ent, 4); + + /* Now clobber the whole thing */ + if (size > sizeof(mode) - 1) + size = sizeof(mode) - 1; + memset(&mode, sizeof(mode), 0); + nubus_get_rsrc_mem(&mode, &ent, size); + printk (KERN_INFO " %02X: (%02X) %s\n", ent.type, + mode.id, mode.name); } + return 0; } -/* - * RBV machines have level triggered video interrupts, and a VIA - * emulation that doesn't always seem to include being able to disable - * an interrupt. Totally lusing hardware. Before we can init irq's we - * have to install a handler to shut the bloody things up. - */ +/* This is *really* cool. */ +static int __init nubus_get_icon(struct nubus_board* board, + const struct nubus_dirent* ent) +{ + /* Should be 32x32 if my memory serves me correctly */ + unsigned char icon[128]; + int x, y; + + nubus_get_rsrc_mem(&icon, ent, 128); + printk(KERN_INFO " icon:\n"); + + /* We should actually plot these somewhere in the framebuffer + init. This is just to demonstrate that they do, in fact, + exist */ + for (y = 0; y < 32; y++) { + printk(KERN_INFO " "); + for (x = 0; x < 32; x++) { + if (icon[y*4 + x/8] + & (0x80 >> (x%8))) + printk("*"); + else + printk(" "); + } + printk("\n"); + } + return 0; +} -void nubus_sweep_video(void) +static int __init nubus_get_vendorinfo(struct nubus_board* board, + const struct nubus_dirent* parent) { - int i; - return; /* XXX why ?? */ - for(i=9;i<15;i++) + struct nubus_dir dir; + struct nubus_dirent ent; + static char* vendor_fields[6] = {"ID", "serial", "revision", + "part", "date", "unknown field"}; + + printk(KERN_INFO " vendor info:\n"); + nubus_get_subdir(parent, &dir); + printk(KERN_DEBUG "nubus_get_vendorinfo: parent is 0x%p, dir is 0x%p\n", + parent->base, dir.base); + + while(nubus_readdir(&dir, &ent) != -1) { - nubus_probe_slot(i,1); + char name[64]; + + /* These are all strings, we think */ + nubus_get_rsrc_str(name, &ent, 64); + if (ent.type > 5) + ent.type = 5; + printk(KERN_INFO " %s: %s\n", + vendor_fields[ent.type-1], name); } + return 0; } -/* - * Support functions - */ - -int nubus_ethernet_addr(int slot, unsigned char *addr) +static int __init nubus_get_board_resource(struct nubus_board* board, int slot, + const struct nubus_dirent* parent) { - struct nubus_dir *nb; - struct nubus_dirent *d; - int ng=-ENOENT; - - nb=nubus_openrootdir(slot); + struct nubus_dir dir; + struct nubus_dirent ent; - if(nb==NULL) - return -ENOENT; - - while((d=nubus_readdir(nb))!=NULL) + nubus_get_subdir(parent, &dir); + printk(KERN_DEBUG "nubus_get_board_resource: parent is 0x%p, dir is 0x%p\n", + parent->base, dir.base); + + while(nubus_readdir(&dir, &ent) != -1) { - if(d->type==0x80) /* First private resource */ + switch (ent.type) { + case NUBUS_RESID_TYPE: + { + unsigned short nbtdata[4]; + /* This type is always the same, and is not + useful except insofar as it tells us that + we really are looking at a board resource. */ + nubus_get_rsrc_mem(nbtdata, &ent, 8); + printk(KERN_INFO " type: [cat 0x%x type 0x%x hw 0x%x sw 0x%x]\n", + nbtdata[0], nbtdata[1], nbtdata[2], + nbtdata[3]); + if (nbtdata[0] != 1 || nbtdata[1] != 0 || + nbtdata[2] != 0 || nbtdata[3] != 0) + printk(KERN_ERR "this sResource is not a board resource!\n"); + break; + } + case NUBUS_RESID_NAME: + nubus_get_rsrc_str(board->name, &ent, 64); + printk(KERN_INFO " name: %s\n", board->name); + break; + case NUBUS_RESID_ICON: + nubus_get_icon(board, &ent); + break; + case NUBUS_RESID_BOARDID: + printk(KERN_INFO " board id: 0x%x\n", ent.data); + break; + case NUBUS_RESID_PRIMARYINIT: + printk(KERN_INFO " primary init offset: 0x%06x\n", ent.data); + break; + case NUBUS_RESID_VENDORINFO: + nubus_get_vendorinfo(board, &ent); break; + case NUBUS_RESID_FLAGS: + printk(KERN_INFO " flags: 0x%06x\n", ent.data); + break; + case NUBUS_RESID_HWDEVID: + printk(KERN_INFO " hwdevid: 0x%06x\n", ent.data); + break; + case NUBUS_RESID_SECONDINIT: + printk(KERN_INFO " secondary init offset: 0x%06x\n", ent.data); + break; + /* WTF isn't this in the functional resources? */ + case NUBUS_RESID_VIDNAMES: + nubus_get_vidnames(board, &ent); + break; + /* Same goes for this */ + case NUBUS_RESID_VIDMODES: + printk(KERN_INFO " video mode parameter directory offset: 0x%06x\n", + ent.data); + break; + default: + printk(KERN_INFO " unknown resource %02X, data 0x%06x\n", + ent.type, ent.data); + } + } + return 0; +} + +/* Add a board (might be many devices) to the list */ +static struct nubus_board* __init nubus_add_board(int slot, int bytelanes) +{ + struct nubus_board* board; + struct nubus_board** boardp; + + unsigned char *rp; + unsigned long dpat; + struct nubus_dir dir; + struct nubus_dirent ent; + + /* Move to the start of the format block */ + rp = nubus_rom_addr(slot); + nubus_rewind(&rp, FORMAT_BLOCK_SIZE, bytelanes); + + /* Actually we should probably panic if this fails */ + if ((board = kmalloc(sizeof(*board), GFP_ATOMIC)) == NULL) + return NULL; + memset(board, 0, sizeof(*board)); + board->fblock = rp; + + /* Dump the format block for debugging purposes */ + if (console_loglevel >= 10) { + int i; + printk(KERN_DEBUG "Slot %X, format block at 0x%p\n", + slot, rp); + printk(KERN_DEBUG "Format block: "); + for (i = 0; i < FORMAT_BLOCK_SIZE; i += 4) { + unsigned short foo, bar; + foo = nubus_get_rom(&rp, 2, bytelanes); + bar = nubus_get_rom(&rp, 2, bytelanes); + printk("%04x %04x ", foo, bar); + } + printk("\n"); + rp = board->fblock; } - if(d==NULL) - return -ENOENT; - nb=nubus_opensubdir(d); + board->slot = slot; + board->slot_addr = (unsigned long) nubus_slot_addr(slot); + board->doffset = nubus_get_rom(&rp, 4, bytelanes); + /* rom_length is *supposed* to be the total length of the + * ROM. In practice it is the "amount of ROM used to compute + * the CRC." So some jokers decide to set it to zero and + * set the crc to zero so they don't have to do any math. + * See the Performa 460 ROM, for example. Those Apple "engineers". + */ + board->rom_length = nubus_get_rom(&rp, 4, bytelanes); + board->crc = nubus_get_rom(&rp, 4, bytelanes); + board->rev = nubus_get_rom(&rp, 1, bytelanes); + board->format = nubus_get_rom(&rp,1, bytelanes); + board->lanes = bytelanes; + + /* Directory offset should be small and negative... */ + if(!(board->doffset & 0x00FF0000)) + printk(KERN_WARNING "Dodgy doffset!\n"); + dpat = nubus_get_rom(&rp, 4, bytelanes); + if(dpat != NUBUS_TEST_PATTERN) + printk(KERN_WARNING "Wrong test pattern %08lx!\n", dpat); + + /* + * I wonder how the CRC is meant to work - + * any takers ? + * CSA: According to MAC docs, not all cards pass the CRC anyway, + * since the initial Macintosh ROM releases skipped the check. + */ + + /* Set up the directory pointer */ + board->directory = board->fblock; + nubus_move(&board->directory, nubus_expand32(board->doffset), board->lanes); + + nubus_get_root_dir(board, &dir); + + /* We're ready to rock */ + printk(KERN_INFO "Slot %X:\n", slot); + + /* Each slot should have one board resource and any number of + functional resources. So we'll fill in some fields in the + struct nubus_board from the board resource, then walk down + the list of functional resources, spinning out a nubus_dev + for each of them. */ + if (nubus_readdir(&dir, &ent) == -1) { + /* We can't have this! */ + printk(KERN_ERR "Board resource not found!\n"); + return NULL; + } else { + printk(KERN_INFO " Board resource:\n"); + nubus_get_board_resource(board, slot, &ent); + } + + while (nubus_readdir(&dir, &ent) != -1) { + struct nubus_dev* dev; + struct nubus_dev** devp; + dev = nubus_get_functional_resource(board, slot, &ent); + if (dev == NULL) + continue; + + /* We zeroed this out above */ + if (board->first_dev == NULL) + board->first_dev = dev; + + /* Put it on the global NuBus device chain. Keep entries in order. */ + for (devp=&nubus_devices; *devp!=NULL; devp=&((*devp)->next)) + /* spin */; + *devp = dev; + dev->next = NULL; + } + + /* Put it on the global NuBus board chain. Keep entries in order. */ + for (boardp=&nubus_boards; *boardp!=NULL; boardp=&((*boardp)->next)) + /* spin */; + *boardp = board; + board->next = NULL; - while((d=nubus_readdir(nb))!=NULL) + return board; +} + +void __init nubus_probe_slot(int slot) +{ + unsigned char dp; + unsigned char* rp; + int i; + + rp = nubus_rom_addr(slot); + for(i = 4; i; i--) { - if(d->type==0x80) /* First private field is the mac */ - { - int i; - nubus_memcpy(slot, addr, nubus_dirptr(d), 6); -/* printk("d.base=%lX, d.value=%lX\n", - d->base,d->value); - memcpy(addr,"\xC0\xC1\xC2\xC3\xC4\xC5",6);*/ - printk("MAC address: "); - for(i=0;i<6;i++) - { - printk("%s%02X", i?":":"", addr[i]); - } - ng=0; - break; - } - else - printk("ID=%d val=%x\n", - d->type, d->value); + unsigned long flags; + int card_present; + + rp--; + save_flags(flags); + cli(); + card_present = hwreg_present(rp); + restore_flags(flags); + + if (!card_present) + continue; + + dp = *rp; + if(dp == 0) + continue; + + /* The last byte of the format block consists of two + nybbles which are "mirror images" of each other. + These show us the valid bytelanes */ + if ((((dp>>4) ^ dp) & 0x0F) != 0x0F) + continue; + /* Check that this value is actually *on* one of the + bytelanes it claims are valid! */ + if ((dp & 0x0F) >= (1<slot, board->name); + return strlen(ptr); } +/* We're going to have to be a bit more sophisticated about this, I + think, because it doesn't really seem to work right when you do a + full listing of boards and devices */ int get_nubus_list(char *buf) { int nprinted, len, size; - int slot; + struct nubus_board* board; #define MSG "\nwarning: page-size limit reached!\n" /* reserve same for truncation warning message: */ size = PAGE_SIZE - (strlen(MSG) + 1); - len = sprintf(buf, "Nubus devices found:\n"); + len = sprintf(buf, "Nubus boards found:\n"); - for (slot=0; slot< 16; slot++) + /* Walk the list of NuBus boards */ + for (board = nubus_boards; board != NULL; board = board->next) { - if(!(nubus_slots[slot].slot_flags&NUBUS_DEVICE_PRESENT)) - continue; - nprinted = sprint_nubus_config(slot, buf + len, size - len); + nprinted = sprint_nubus_board(board, buf + len, size - len); if (nprinted < 0) { return len + sprintf(buf + len, MSG); } @@ -595,46 +894,42 @@ int get_nubus_list(char *buf) return len; } -static struct proc_dir_entry proc_nubus = { +static struct proc_dir_entry proc_old_nubus = { PROC_NUBUS, 5, "nubus", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_array_inode_operations }; -#endif +#endif /* CONFIG_PROC_FS */ -void nubus_init(void) +void __init nubus_scan_bus(void) { - /* - * Register cards - */ -#ifdef CONFIG_DAYNAPORT - extern struct nubus_device_specifier nubus_8390; -#endif + int slot; + for(slot = 9; slot < 15; slot++) + { + nubus_probe_slot(slot); + } +} +void __init nubus_init(void) +{ if (!MACH_IS_MAC) return; -#ifdef LCIII_WEIRDNESS - if (macintosh_config->ident == MAC_MODEL_LCIII) { - printk("nubus init: LCIII has no nubus!\n"); - return; + /* Initialize the NuBus interrupts */ + if (oss_present) { + oss_nubus_init(); + } else { + via_nubus_init(); } -#endif -#ifdef CONFIG_DAYNAPORT - register_nubus_device(&nubus_8390); -#endif + /* And probe */ + printk("NuBus: Scanning NuBus slots.\n"); + nubus_devices = NULL; + nubus_boards = NULL; + nubus_scan_bus(); - /* - * And probe - */ - - nubus_init_via(); - printk("Scanning nubus slots.\n"); - nubus_probe_bus(); #ifdef CONFIG_PROC_FS - proc_register(&proc_root, &proc_nubus); + proc_register(&proc_root, &proc_old_nubus); + nubus_proc_init(); #endif } - - \ No newline at end of file diff --git a/drivers/nubus/nubus_syms.c b/drivers/nubus/nubus_syms.c new file mode 100644 index 000000000000..cd9de65f0395 --- /dev/null +++ b/drivers/nubus/nubus_syms.c @@ -0,0 +1,27 @@ +/* Exported symbols for NuBus services + + (c) 1999 David Huggins-Daines */ + +#include +#include +#include +#include + +#ifdef CONFIG_PROC_FS +EXPORT_SYMBOL(nubus_proc_attach_device); +EXPORT_SYMBOL(nubus_proc_detach_device); +#endif + +EXPORT_SYMBOL(nubus_find_device); +EXPORT_SYMBOL(nubus_find_type); +EXPORT_SYMBOL(nubus_find_slot); +EXPORT_SYMBOL(nubus_get_root_dir); +EXPORT_SYMBOL(nubus_get_board_dir); +EXPORT_SYMBOL(nubus_get_func_dir); +EXPORT_SYMBOL(nubus_readdir); +EXPORT_SYMBOL(nubus_find_rsrc); +EXPORT_SYMBOL(nubus_rewinddir); +EXPORT_SYMBOL(nubus_get_subdir); +EXPORT_SYMBOL(nubus_get_rsrc_mem); +EXPORT_SYMBOL(nubus_get_rsrc_str); + diff --git a/drivers/nubus/proc.c b/drivers/nubus/proc.c new file mode 100644 index 000000000000..f2f9ede59071 --- /dev/null +++ b/drivers/nubus/proc.c @@ -0,0 +1,183 @@ +/* drivers/nubus/proc.c: Proc FS interface for NuBus. + + By David Huggins-Daines + + Much code and many ideas from drivers/pci/proc.c: + Copyright (c) 1997, 1998 Martin Mares + + This is initially based on the Zorro and PCI interfaces. However, + it works somewhat differently. The intent is to provide a + structure in /proc analogous to the structure of the NuBus ROM + resources. + + Therefore each NuBus device is in fact a directory, which may in + turn contain subdirectories. The "files" correspond to NuBus + resource records. For those types of records which we know how to + convert to formats that are meaningful to userspace (mostly just + icons) these files will provide "cooked" data. Otherwise they will + simply provide raw access (read-only of course) to the ROM. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int +get_nubus_dev_info(char *buf, char **start, off_t pos, int count, int wr) +{ + struct nubus_dev *dev = nubus_devices; + off_t at = 0; + int len, cnt; + + cnt = 0; + while (dev && count > cnt) { + len = sprintf(buf, "%x\t%04x %04x %04x %04x", + dev->board->slot, + dev->category, + dev->type, + dev->dr_sw, + dev->dr_hw); + len += sprintf(buf+len, + "\t%08lx", + dev->board->slot_addr); + buf[len++] = '\n'; + at += len; + if (at >= pos) { + if (!*start) { + *start = buf + (pos - (at - len)); + cnt = at - pos; + } else + cnt += len; + buf += len; + } + dev = dev->next; + } + return (count > cnt) ? cnt : count; +} + +static struct proc_dir_entry proc_nubus_devices = { + PROC_BUS_NUBUS_DEVICES, 7, "devices", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_array_inode_operations, + get_nubus_dev_info +}; + +static struct proc_dir_entry *proc_bus_nubus_dir; + +static void nubus_proc_subdir(struct nubus_dev* dev, + struct proc_dir_entry* parent, + struct nubus_dir* dir) +{ + struct nubus_dirent ent; + + /* Some of these are directories, others aren't */ + while (nubus_readdir(dir, &ent) != -1) { + char name[8]; + struct proc_dir_entry* e; + + sprintf(name, "%x", ent.type); + e = create_proc_entry(name, S_IFREG | S_IRUGO | + S_IWUSR, parent); + if (!e) return; + } +} + +/* Can't do this recursively since the root directory is structured + somewhat differently from the subdirectories */ +static void nubus_proc_populate(struct nubus_dev* dev, + struct proc_dir_entry* parent, + struct nubus_dir* root) +{ + struct nubus_dirent ent; + + /* We know these are all directories (board resource + one or + more functional resources) */ + while (nubus_readdir(root, &ent) != -1) { + char name[8]; + struct proc_dir_entry* e; + struct nubus_dir dir; + + sprintf(name, "%x", ent.type); + e = create_proc_entry(name, S_IFDIR, parent); + if (!e) return; + + /* And descend */ + if (nubus_get_subdir(&ent, &dir) == -1) { + /* This shouldn't happen */ + printk(KERN_ERR "NuBus root directory node %x:%x has no subdir!\n", + dev->board->slot, ent.type); + continue; + } else { + nubus_proc_subdir(dev, e, &dir); + } + } +} + +int nubus_proc_attach_device(struct nubus_dev *dev) +{ + struct proc_dir_entry *e; + struct nubus_dir root; + char name[8]; + + if (dev == NULL) { + printk(KERN_ERR + "NULL pointer in nubus_proc_attach_device, shoot the programmer!\n"); + return -1; + } + + if (dev->board == NULL) { + printk(KERN_ERR + "NULL pointer in nubus_proc_attach_device, shoot the programmer!\n"); + printk("dev = %p, dev->board = %p\n", dev, dev->board); + return -1; + } + + /* Create a directory */ + sprintf(name, "%x", dev->board->slot); + e = dev->procdir = create_proc_entry(name, S_IFDIR, + proc_bus_nubus_dir); + if (!e) + return -ENOMEM; + + /* Now recursively populate it with files */ + nubus_get_root_dir(dev->board, &root); + nubus_proc_populate(dev, e, &root); + + return 0; +} + +/* FIXME: this is certainly broken! */ +int nubus_proc_detach_device(struct nubus_dev *dev) +{ + struct proc_dir_entry *e; + + if ((e = dev->procdir)) { + if (e->count) + return -EBUSY; + remove_proc_entry(e->name, proc_bus_nubus_dir); + dev->procdir = NULL; + } + return 0; +} + +__initfunc(void proc_bus_nubus_add_devices(void)) +{ + struct nubus_dev *dev; + + for(dev = nubus_devices; dev; dev = dev->next) + nubus_proc_attach_device(dev); +} + +__initfunc(void nubus_proc_init(void)) +{ + if (!MACH_IS_MAC) + return; + proc_bus_nubus_dir = create_proc_entry("nubus", S_IFDIR, proc_bus); + proc_register(proc_bus_nubus_dir, &proc_nubus_devices); + proc_bus_nubus_add_devices(); +} diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c index eb440ca6d476..93907455f6bd 100644 --- a/drivers/scsi/a2091.c +++ b/drivers/scsi/a2091.c @@ -44,30 +44,16 @@ static void a2091_intr (int irq, void *dummy, struct pt_regs *fp) continue; if (status & ISTR_INTS) - { - /* disable PORTS interrupt */ - custom.intena = IF_PORTS; wd33c93_intr (instance); - /* enable PORTS interrupt */ - custom.intena = IF_SETCLR | IF_PORTS; - } } } -static void do_a2091_intr (int irq, void *dummy, struct pt_regs *fp) -{ - unsigned long flags; - - spin_lock_irqsave(&io_request_lock, flags); - a2091_intr(irq, dummy, fp); - spin_unlock_irqrestore(&io_request_lock, flags); -} - static int dma_setup (Scsi_Cmnd *cmd, int dir_in) { unsigned short cntr = CNTR_PDMD | CNTR_INTEN; - unsigned long addr = VTOP(cmd->SCp.ptr); + unsigned long addr = virt_to_bus(cmd->SCp.ptr); struct Scsi_Host *instance = cmd->host; + static int scsi_alloc_out_of_range = 0; /* don't allow DMA if the physical address is bad */ if (addr & A2091_XFER_MASK || @@ -75,41 +61,55 @@ static int dma_setup (Scsi_Cmnd *cmd, int dir_in) { HDATA(instance)->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff; - HDATA(instance)->dma_bounce_buffer = - scsi_malloc (HDATA(instance)->dma_bounce_len); - - /* can't allocate memory; use PIO */ - if (!HDATA(instance)->dma_bounce_buffer) { - HDATA(instance)->dma_bounce_len = 0; - return 1; + if( !scsi_alloc_out_of_range ) { + HDATA(cmd->host)->dma_bounce_buffer = + scsi_malloc (HDATA(cmd->host)->dma_bounce_len); + HDATA(cmd->host)->dma_buffer_pool = BUF_SCSI_ALLOCED; } - /* get the physical address of the bounce buffer */ - addr = VTOP(HDATA(instance)->dma_bounce_buffer); + if ( scsi_alloc_out_of_range || !HDATA(cmd->host)->dma_bounce_buffer) { + HDATA(cmd->host)->dma_bounce_buffer = + amiga_chip_alloc(HDATA(cmd->host)->dma_bounce_len); - /* the bounce buffer may not be in the first 16M of physmem */ - if (addr & A2091_XFER_MASK) { - /* we could use chipmem... maybe later */ - scsi_free (HDATA(instance)->dma_bounce_buffer, - HDATA(instance)->dma_bounce_len); - HDATA(instance)->dma_bounce_buffer = NULL; - HDATA(instance)->dma_bounce_len = 0; - return 1; + if(!HDATA(cmd->host)->dma_bounce_buffer) + { + HDATA(cmd->host)->dma_bounce_len = 0; + return 1; + } + + HDATA(cmd->host)->dma_buffer_pool = BUF_CHIP_ALLOCED; } + /* check if the address of the bounce buffer is OK */ + addr = virt_to_bus(HDATA(cmd->host)->dma_bounce_buffer); + + if (addr & A2091_XFER_MASK) { + /* fall back to Chip RAM if address out of range */ + if( HDATA(cmd->host)->dma_buffer_pool == BUF_SCSI_ALLOCED) { + scsi_free (HDATA(cmd->host)->dma_bounce_buffer, + HDATA(cmd->host)->dma_bounce_len); + scsi_alloc_out_of_range = 1; + } else { + amiga_chip_free (HDATA(cmd->host)->dma_bounce_buffer); + } + + HDATA(cmd->host)->dma_bounce_buffer = + amiga_chip_alloc(HDATA(cmd->host)->dma_bounce_len); + + if(!HDATA(cmd->host)->dma_bounce_buffer) + { + HDATA(cmd->host)->dma_bounce_len = 0; + return 1; + } + + addr = virt_to_bus(HDATA(cmd->host)->dma_bounce_buffer); + HDATA(cmd->host)->dma_buffer_pool = BUF_CHIP_ALLOCED; + } + if (!dir_in) { /* copy to bounce buffer for a write */ - if (cmd->use_sg) -#if 0 - panic ("scsi%ddma: incomplete s/g support", - instance->host_no); -#else - memcpy (HDATA(instance)->dma_bounce_buffer, - cmd->SCp.ptr, cmd->SCp.this_residual); -#endif - else - memcpy (HDATA(instance)->dma_bounce_buffer, - cmd->request_buffer, cmd->request_bufflen); + memcpy (HDATA(cmd->host)->dma_bounce_buffer, + cmd->SCp.ptr, cmd->SCp.this_residual); } } @@ -169,32 +169,19 @@ static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt, /* copy from a bounce buffer, if necessary */ if (status && HDATA(instance)->dma_bounce_buffer) { - if (SCpnt && SCpnt->use_sg) { -#if 0 - panic ("scsi%d: incomplete s/g support", - instance->host_no); -#else - if( HDATA(instance)->dma_dir ) - memcpy (SCpnt->SCp.ptr, - HDATA(instance)->dma_bounce_buffer, - SCpnt->SCp.this_residual); - scsi_free (HDATA(instance)->dma_bounce_buffer, - HDATA(instance)->dma_bounce_len); - HDATA(instance)->dma_bounce_buffer = NULL; - HDATA(instance)->dma_bounce_len = 0; - -#endif - } else { - if (HDATA(instance)->dma_dir && SCpnt) - memcpy (SCpnt->request_buffer, - HDATA(instance)->dma_bounce_buffer, - SCpnt->request_bufflen); - + if (HDATA(instance)->dma_dir && SCpnt) + memcpy (SCpnt->SCp.ptr, + HDATA(instance)->dma_bounce_buffer, + SCpnt->SCp.this_residual); + + if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED) scsi_free (HDATA(instance)->dma_bounce_buffer, HDATA(instance)->dma_bounce_len); - HDATA(instance)->dma_bounce_buffer = NULL; - HDATA(instance)->dma_bounce_len = 0; - } + else + amiga_chip_free(HDATA(instance)->dma_bounce_buffer); + + HDATA(instance)->dma_bounce_buffer = NULL; + HDATA(instance)->dma_bounce_len = 0; } } @@ -229,7 +216,7 @@ __initfunc(int a2091_detect(Scsi_Host_Template *tpnt)) if (num_a2091++ == 0) { first_instance = instance; a2091_template = instance->hostt; - request_irq(IRQ_AMIGA_PORTS, do_a2091_intr, 0, "A2091 SCSI", a2091_intr); + request_irq(IRQ_AMIGA_PORTS, a2091_intr, 0, "A2091 SCSI", a2091_intr); } DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN; zorro_config_board(key, 0); diff --git a/drivers/scsi/a3000.c b/drivers/scsi/a3000.c index 099d33704aa2..c715acc9ccba 100644 --- a/drivers/scsi/a3000.c +++ b/drivers/scsi/a3000.c @@ -38,28 +38,16 @@ static void a3000_intr (int irq, void *dummy, struct pt_regs *fp) return; if (status & ISTR_INTS) { - /* disable PORTS interrupt */ - custom.intena = IF_PORTS; wd33c93_intr (a3000_host); - /* enable PORTS interrupt */ - custom.intena = IF_SETCLR | IF_PORTS; } else { - printk("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status); + printk("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status); } } -static void do_a3000_intr (int irq, void *dummy, struct pt_regs *fp) -{ - unsigned long flags; - - spin_lock_irqsave(&io_request_lock, flags); - a3000_intr(irq, dummy, fp); - spin_unlock_irqrestore(&io_request_lock, flags); -} static int dma_setup (Scsi_Cmnd *cmd, int dir_in) { unsigned short cntr = CNTR_PDMD | CNTR_INTEN; - unsigned long addr = VTOP(cmd->SCp.ptr); + unsigned long addr = virt_to_bus(cmd->SCp.ptr); /* * if the physical address has the wrong alignment, or if @@ -91,7 +79,7 @@ static int dma_setup (Scsi_Cmnd *cmd, int dir_in) cmd->request_buffer, cmd->request_bufflen); } - addr = VTOP(HDATA(a3000_host)->dma_bounce_buffer); + addr = virt_to_bus(HDATA(a3000_host)->dma_bounce_buffer); } /* setup dma direction */ @@ -194,7 +182,7 @@ __initfunc(int a3000_detect(Scsi_Host_Template *tpnt)) DMA(a3000_host)->DAWR = DAWR_A3000; wd33c93_init(a3000_host, (wd33c93_regs *)&(DMA(a3000_host)->SASR), dma_setup, dma_stop, WD33C93_FS_12_15); - request_irq(IRQ_AMIGA_PORTS, do_a3000_intr, 0, "A3000 SCSI", a3000_intr); + request_irq(IRQ_AMIGA_PORTS, a3000_intr, 0, "A3000 SCSI", a3000_intr); DMA(a3000_host)->CNTR = CNTR_PDMD | CNTR_INTEN; called = 1; diff --git a/drivers/scsi/amiga7xx.c b/drivers/scsi/amiga7xx.c index 25d3b193a07e..1b804aa19926 100644 --- a/drivers/scsi/amiga7xx.c +++ b/drivers/scsi/amiga7xx.c @@ -15,11 +15,14 @@ #include #include +#include #include #include #include #include +#include #include +#include #include "scsi.h" #include "hosts.h" @@ -33,11 +36,11 @@ struct proc_dir_entry proc_scsi_amiga7xx = { S_IFDIR | S_IRUGO | S_IXUGO, 2 }; -extern ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, - u32 base, int io_port, int irq, int dma, - long long options, int clock); +extern int ncr53c7xx_init (Scsi_Host_Template *tpnt, int board, int chip, + u32 base, int io_port, int irq, int dma, + long long options, int clock); -int amiga7xx_detect(Scsi_Host_Template *tpnt) +__initfunc(int amiga7xx_detect(Scsi_Host_Template *tpnt)) { static unsigned char called = 0; unsigned int key; @@ -54,18 +57,13 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) if ((key = zorro_find(ZORRO_PROD_PHASE5_BLIZZARD_603E_PLUS, 0, 0))) { cd = zorro_get_board(key); -/* - unsigned long address; - address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, - cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL); -*/ + options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT; clock = 50000000; /* 50MHz SCSI Clock */ - ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(0x80f40000), - 0, IRQ_AMIGA_PORTS, DMA_NONE, - options, clock); + ncr53c7xx_init(tpnt, 0, 710, (u32)ZTWO_VADDR(0xf40000), 0, + IRQ_AMIGA_PORTS, DMA_NONE, options, clock); zorro_config_board(key, 0); num++; @@ -77,16 +75,15 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) { unsigned long address; cd = zorro_get_board(key); - address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, - cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL); + address = (unsigned long)ioremap((unsigned long)cd->cd_BoardAddr, + cd->cd_BoardSize); options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT; clock = 50000000; /* 50MHz SCSI Clock */ ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address + 0x40000), - 0, IRQ_AMIGA_PORTS, DMA_NONE, - options, clock); + 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); zorro_config_board(key, 0); num++; @@ -100,9 +97,8 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) clock = 50000000; /* 50MHz SCSI Clock */ - ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)ZTWO_VADDR(0xDD0040), - 0, IRQ_AMIGA_PORTS, DMA_NONE, - options, clock); + ncr53c7xx_init(tpnt, 0, 710, (u32)ZTWO_VADDR(0xDD0040), 0, + IRQ_AMIGA_PORTS, DMA_NONE, options, clock); num++; } #endif @@ -113,8 +109,8 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) { unsigned long address; cd = zorro_get_board(key); - address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, - cd->cd_BoardSize, KERNELMAP_NOCACHE_SER, NULL); + address = (unsigned long)ioremap((unsigned long)cd->cd_BoardAddr, + cd->cd_BoardSize); options = OPTION_MEMORY_MAPPED|OPTION_DEBUG_TEST1|OPTION_INTFLY|OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS|OPTION_DISCONNECT; diff --git a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c index c77e5cd71f9e..c3cef553dec6 100644 --- a/drivers/scsi/atari_NCR5380.c +++ b/drivers/scsi/atari_NCR5380.c @@ -472,9 +472,9 @@ static void merge_contiguous_buffers( Scsi_Cmnd *cmd ) int cnt = 1; #endif - for (endaddr = VTOP(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1; + for (endaddr = virt_to_phys(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1; cmd->SCp.buffers_residual && - VTOP(cmd->SCp.buffer[1].address) == endaddr; ) { + virt_to_phys(cmd->SCp.buffer[1].address) == endaddr; ) { MER_PRINTK("VTOP(%p) == %08lx -> merging\n", cmd->SCp.buffer[1].address, endaddr); @@ -1459,9 +1459,9 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) unsigned long timeout = jiffies + 2*NCR_TIMEOUT; while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS) - && jiffies < timeout && !hostdata->connected) + && time_before(jiffies, timeout) && !hostdata->connected) ; - if (jiffies >= timeout) + if (time_after_eq(jiffies, timeout)) { printk("scsi : arbitration timeout at %d\n", __LINE__); NCR5380_write(MODE_REG, MR_BASE); @@ -1616,7 +1616,7 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) * only wait for BSY... (Famous german words: Der Klügere gibt nach :-) */ - while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) & + while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & (SR_BSY | SR_IO))); if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == @@ -1629,7 +1629,7 @@ static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) return -1; } #else - while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY)); + while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY)); #endif /* diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c index 4e98df49cac6..0da9a5fd0a31 100644 --- a/drivers/scsi/atari_scsi.c +++ b/drivers/scsi/atari_scsi.c @@ -132,51 +132,51 @@ struct proc_dir_entry proc_scsi_atari = { } while(0) #define SCSI_DMA_READ_P(elt) \ - (((unsigned long)tt_scsi_dma.elt##_hi << 24) | \ - ((unsigned long)tt_scsi_dma.elt##_hmd << 16) | \ - ((unsigned long)tt_scsi_dma.elt##_lmd << 8) | \ - (unsigned long)tt_scsi_dma.elt##_lo) - - -#define SCSI_DMA_SETADR(adr) \ - do { \ - unsigned long __adr = (adr); \ - st_dma.dma_lo = (unsigned char)__adr; \ - MFPDELAY(); \ - __adr >>= 8; \ - st_dma.dma_md = (unsigned char)__adr; \ - MFPDELAY(); \ - __adr >>= 8; \ - st_dma.dma_hi = (unsigned char)__adr; \ - MFPDELAY(); \ - } while(0) - -#define SCSI_DMA_GETADR() ({ \ - unsigned long __adr; \ - __adr = st_dma.dma_lo; \ - MFPDELAY(); \ - __adr |= (st_dma.dma_md & 0xff) << 8; \ - MFPDELAY(); \ - __adr |= (st_dma.dma_hi & 0xff) << 16; \ - MFPDELAY(); \ - __adr; \ -}) - -#define ENABLE_IRQ() \ - do { \ - if (IS_A_TT()) \ - atari_enable_irq( IRQ_TT_MFP_SCSI ); \ - else \ - atari_enable_irq( IRQ_MFP_FSCSI ); \ - } while(0) + (((((((unsigned long)tt_scsi_dma.elt##_hi << 8) | \ + (unsigned long)tt_scsi_dma.elt##_hmd) << 8) | \ + (unsigned long)tt_scsi_dma.elt##_lmd) << 8) | \ + (unsigned long)tt_scsi_dma.elt##_lo) -#define DISABLE_IRQ() \ - do { \ - if (IS_A_TT()) \ - atari_disable_irq( IRQ_TT_MFP_SCSI ); \ - else \ - atari_disable_irq( IRQ_MFP_FSCSI ); \ - } while(0) + +static inline void SCSI_DMA_SETADR(unsigned long adr) +{ + st_dma.dma_lo = (unsigned char)adr; + MFPDELAY(); + adr >>= 8; + st_dma.dma_md = (unsigned char)adr; + MFPDELAY(); + adr >>= 8; + st_dma.dma_hi = (unsigned char)adr; + MFPDELAY(); +} + +static inline unsigned long SCSI_DMA_GETADR(void) +{ + unsigned long adr; + adr = st_dma.dma_lo; + MFPDELAY(); + adr |= (st_dma.dma_md & 0xff) << 8; + MFPDELAY(); + adr |= (st_dma.dma_hi & 0xff) << 16; + MFPDELAY(); + return adr; +} + +static inline void ENABLE_IRQ(void) +{ + if (IS_A_TT()) + atari_enable_irq(IRQ_TT_MFP_SCSI); + else + atari_enable_irq(IRQ_MFP_FSCSI); +} + +static inline void DISABLE_IRQ(void) +{ + if (IS_A_TT()) + atari_disable_irq(IRQ_TT_MFP_SCSI); + else + atari_disable_irq(IRQ_MFP_FSCSI); +} #define HOSTDATA_DMALEN (((struct NCR5380_hostdata *) \ @@ -461,8 +461,8 @@ static void scsi_falcon_intr (int irq, void *dummy, struct pt_regs *fp) /* If the dribble buffer was used on a read operation, copy the DMA-ed * data to the original destination address. */ - memcpy( atari_dma_orig_addr, (void *)PTOV(atari_dma_startaddr), - HOSTDATA_DMALEN - atari_dma_residual ); + memcpy(atari_dma_orig_addr, phys_to_virt(atari_dma_startaddr), + HOSTDATA_DMALEN - atari_dma_residual); atari_dma_orig_addr = NULL; } @@ -476,20 +476,22 @@ static void scsi_falcon_intr (int irq, void *dummy, struct pt_regs *fp) static void atari_scsi_fetch_restbytes( void ) { int nr; - char *src, *dst; + char *src, *dst; + unsigned long phys_dst; /* fetch rest bytes in the DMA register */ - dst = (char *)SCSI_DMA_READ_P( dma_addr ); - if ((nr = ((long)dst & 3))) { - /* there are 'nr' bytes left for the last long address before the - DMA pointer */ - dst = (char *)( (unsigned long)dst & ~3 ); + phys_dst = SCSI_DMA_READ_P(dma_addr); + nr = phys_dst & 3; + if (nr) { + /* there are 'nr' bytes left for the last long address + before the DMA pointer */ + phys_dst ^= nr; DMA_PRINTK("SCSI DMA: there are %d rest bytes for phys addr 0x%08lx", - nr, (long)dst); - dst = (char *)PTOV(dst); /* The content of the DMA pointer - * is a physical address! */ - DMA_PRINTK(" = virt addr 0x%08lx\n", (long)dst); - for( src = (char *)&tt_scsi_dma.dma_restdata; nr > 0; --nr ) + nr, phys_dst); + /* The content of the DMA pointer is a physical address! */ + dst = phys_to_virt(phys_dst); + DMA_PRINTK(" = virt addr %p\n", dst); + for (src = (char *)&tt_scsi_dma.dma_restdata; nr != 0; --nr) *dst++ = *src++; } } @@ -665,7 +667,7 @@ int atari_scsi_detect (Scsi_Host_Template *host) "double buffer\n" ); return( 0 ); } - atari_dma_phys_buffer = VTOP( atari_dma_buffer ); + atari_dma_phys_buffer = virt_to_phys( atari_dma_buffer ); atari_dma_orig_addr = 0; } #endif @@ -764,7 +766,7 @@ int atari_scsi_release (struct Scsi_Host *sh) } #endif -__initfunc(void atari_scsi_setup( char *str, int *ints )) +void __init atari_scsi_setup(char *str, int *ints) { /* Format of atascsi parameter is: * atascsi=,,,, @@ -772,23 +774,6 @@ __initfunc(void atari_scsi_setup( char *str, int *ints )) * Negative values mean don't change. */ - /* Grmbl... the standard parameter parsing can't handle negative numbers - * :-( So let's do it ourselves! - */ - - int i = ints[0]+1, fact; - - while( str && (isdigit(*str) || *str == '-') && i <= 10) { - if (*str == '-') - fact = -1, ++str; - else - fact = 1; - ints[i++] = simple_strtoul( str, NULL, 0 ) * fact; - if ((str = strchr( str, ',' )) != NULL) - ++str; - } - ints[0] = i-1; - if (ints[0] < 1) { printk( "atari_scsi_setup: no arguments!\n" ); return; @@ -869,7 +854,7 @@ int atari_scsi_reset( Scsi_Cmnd *cmd, unsigned int reset_flags) #ifdef CONFIG_ATARI_SCSI_RESET_BOOT -__initfunc(static void atari_scsi_reset_boot( void )) +static void __init atari_scsi_reset_boot(void) { unsigned long end; @@ -892,7 +877,8 @@ __initfunc(static void atari_scsi_reset_boot( void )) NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE ); NCR5380_read( RESET_PARITY_INTERRUPT_REG ); - for( end = jiffies + AFTER_RESET_DELAY; jiffies < end; ) + end = jiffies + AFTER_RESET_DELAY; + while (time_before(jiffies, end)) barrier(); printk( " done\n" ); @@ -913,7 +899,7 @@ const char * atari_scsi_info (struct Scsi_Host *host) unsigned long atari_scsi_dma_setup( struct Scsi_Host *instance, void *data, unsigned long count, int dir ) { - unsigned long addr = VTOP( data ); + unsigned long addr = virt_to_phys( data ); DMA_PRINTK("scsi%d: setting up dma, data = %p, phys = %lx, count = %ld, " "dir = %d\n", instance->host_no, data, addr, count, dir); @@ -1097,7 +1083,7 @@ static unsigned long atari_dma_xfer_len( unsigned long wanted_len, } /* Last step: apply the hard limit on DMA transfers */ - limit = (atari_dma_buffer && !STRAM_ADDR( VTOP(cmd->SCp.ptr) )) ? + limit = (atari_dma_buffer && !STRAM_ADDR( virt_to_phys(cmd->SCp.ptr) )) ? STRAM_BUFFER_SIZE : 255*512; if (possible_len > limit) possible_len = limit; diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c index 0d8e89c74790..72fe3fcc56ce 100644 --- a/drivers/scsi/gvp11.c +++ b/drivers/scsi/gvp11.c @@ -44,23 +44,10 @@ static void gvp11_intr (int irq, void *dummy, struct pt_regs *fp) if (!(status & GVP11_DMAC_INT_PENDING)) continue; - /* disable PORTS interrupt */ - custom.intena = IF_PORTS; wd33c93_intr (instance); - /* enable PORTS interrupt */ - custom.intena = IF_SETCLR | IF_PORTS; } } -static void do_gvp11_intr (int irq, void *dummy, struct pt_regs *fp) -{ - unsigned long flags; - - spin_lock_irqsave(&io_request_lock, flags); - gvp11_intr(irq, dummy, fp); - spin_unlock_irqrestore(&io_request_lock, flags); -} - static int gvp11_xfer_mask = 0; void gvp11_setup (char *str, int *ints) @@ -71,20 +58,24 @@ void gvp11_setup (char *str, int *ints) static int dma_setup (Scsi_Cmnd *cmd, int dir_in) { unsigned short cntr = GVP11_DMAC_INT_ENABLE; - unsigned long addr = VTOP(cmd->SCp.ptr); + unsigned long addr = virt_to_bus(cmd->SCp.ptr); int bank_mask; + static int scsi_alloc_out_of_range = 0; - /* don't allow DMA if the physical address is bad */ + /* use bounce buffer if the physical address is bad */ if (addr & HDATA(cmd->host)->dma_xfer_mask || (!dir_in && mm_end_of_chunk (addr, cmd->SCp.this_residual))) { HDATA(cmd->host)->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff; - HDATA(cmd->host)->dma_bounce_buffer = - scsi_malloc (HDATA(cmd->host)->dma_bounce_len); - HDATA(cmd->host)->dma_buffer_pool = BUF_SCSI_ALLOCED; - - if (!HDATA(cmd->host)->dma_bounce_buffer) { + + if( !scsi_alloc_out_of_range ) { + HDATA(cmd->host)->dma_bounce_buffer = + scsi_malloc (HDATA(cmd->host)->dma_bounce_len); + HDATA(cmd->host)->dma_buffer_pool = BUF_SCSI_ALLOCED; + } + + if ( scsi_alloc_out_of_range || !HDATA(cmd->host)->dma_bounce_buffer) { HDATA(cmd->host)->dma_bounce_buffer = amiga_chip_alloc(HDATA(cmd->host)->dma_bounce_len); @@ -98,15 +89,17 @@ static int dma_setup (Scsi_Cmnd *cmd, int dir_in) } /* check if the address of the bounce buffer is OK */ - addr = VTOP(HDATA(cmd->host)->dma_bounce_buffer); + addr = virt_to_bus(HDATA(cmd->host)->dma_bounce_buffer); if (addr & HDATA(cmd->host)->dma_xfer_mask) { /* fall back to Chip RAM if address out of range */ - if( HDATA(cmd->host)->dma_buffer_pool == BUF_SCSI_ALLOCED) + if( HDATA(cmd->host)->dma_buffer_pool == BUF_SCSI_ALLOCED) { scsi_free (HDATA(cmd->host)->dma_bounce_buffer, HDATA(cmd->host)->dma_bounce_len); - else + scsi_alloc_out_of_range = 1; + } else { amiga_chip_free (HDATA(cmd->host)->dma_bounce_buffer); + } HDATA(cmd->host)->dma_bounce_buffer = amiga_chip_alloc(HDATA(cmd->host)->dma_bounce_len); @@ -117,18 +110,14 @@ static int dma_setup (Scsi_Cmnd *cmd, int dir_in) return 1; } - addr = VTOP(HDATA(cmd->host)->dma_bounce_buffer); + addr = virt_to_bus(HDATA(cmd->host)->dma_bounce_buffer); HDATA(cmd->host)->dma_buffer_pool = BUF_CHIP_ALLOCED; } if (!dir_in) { /* copy to bounce buffer for a write */ - if (cmd->use_sg) - memcpy (HDATA(cmd->host)->dma_bounce_buffer, - cmd->SCp.ptr, cmd->SCp.this_residual); - else - memcpy (HDATA(cmd->host)->dma_bounce_buffer, - cmd->request_buffer, cmd->request_bufflen); + memcpy (HDATA(cmd->host)->dma_bounce_buffer, + cmd->SCp.ptr, cmd->SCp.this_residual); } } @@ -169,41 +158,19 @@ static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt, /* copy from a bounce buffer, if necessary */ if (status && HDATA(instance)->dma_bounce_buffer) { - if (SCpnt && SCpnt->use_sg) { -#if 0 - panic ("scsi%d: incomplete s/g support", - instance->host_no); -#else - if( HDATA(instance)->dma_dir ) - memcpy (SCpnt->SCp.ptr, - HDATA(instance)->dma_bounce_buffer, - SCpnt->SCp.this_residual); - - if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED) - scsi_free (HDATA(instance)->dma_bounce_buffer, - HDATA(instance)->dma_bounce_len); - else - amiga_chip_free(HDATA(instance)->dma_bounce_buffer); - - HDATA(instance)->dma_bounce_buffer = NULL; - HDATA(instance)->dma_bounce_len = 0; - -#endif - } else { - if (HDATA(instance)->dma_dir && SCpnt) - memcpy (SCpnt->request_buffer, - HDATA(instance)->dma_bounce_buffer, - SCpnt->request_bufflen); - - if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED) - scsi_free (HDATA(instance)->dma_bounce_buffer, - HDATA(instance)->dma_bounce_len); - else - amiga_chip_free(HDATA(instance)->dma_bounce_buffer); - - HDATA(instance)->dma_bounce_buffer = NULL; - HDATA(instance)->dma_bounce_len = 0; - } + if (HDATA(instance)->dma_dir && SCpnt) + memcpy (SCpnt->SCp.ptr, + HDATA(instance)->dma_bounce_buffer, + SCpnt->SCp.this_residual); + + if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED) + scsi_free (HDATA(instance)->dma_bounce_buffer, + HDATA(instance)->dma_bounce_len); + else + amiga_chip_free(HDATA(instance)->dma_bounce_buffer); + + HDATA(instance)->dma_bounce_buffer = NULL; + HDATA(instance)->dma_bounce_len = 0; } } @@ -371,7 +338,7 @@ __initfunc(int gvp11_detect(Scsi_Host_Template *tpnt)) if (num_gvp11++ == 0) { first_instance = instance; gvp11_template = instance->hostt; - request_irq(IRQ_AMIGA_PORTS, do_gvp11_intr, 0, + request_irq(IRQ_AMIGA_PORTS, gvp11_intr, 0, "GVP11 SCSI", gvp11_intr); } DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index efd0789f9920..cde2be7c4450 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -96,6 +96,7 @@ typedef struct { unsigned long flags; /* Status/Action flags */ unsigned long transform; /* SCSI cmd translation layer */ unsigned long log; /* log flags */ + int last_lun; /* last LUN */ } idescsi_scsi_t; /* @@ -508,8 +509,11 @@ static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi, int id) drive->ready_stat = 0; memset (scsi, 0, sizeof (idescsi_scsi_t)); scsi->drive = drive; - if (drive->id && (drive->id->config & 0x0060) == 0x20) - set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags); + if (drive->id) { + if ((drive->id->config & 0x0060) == 0x20) + set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags); + scsi->last_lun = drive->id->last_lun; + } set_bit(IDESCSI_TRANSFORM, &scsi->transform); clear_bit(IDESCSI_SG_TRANSFORM, &scsi->transform); #if IDESCSI_DEBUG_LOG @@ -603,11 +607,15 @@ int idescsi_init (void) int idescsi_detect (Scsi_Host_Template *host_template) { struct Scsi_Host *host; + int last_lun = 0; int id; host_template->proc_dir = &idescsi_proc_dir; host = scsi_register(host_template, 0); - for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++); + for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++) + last_lun = IDE_MAX(last_lun, idescsi_drives[id]->id->last_lun); + + host->max_lun = last_lun + 1; host->max_id = id; host->can_queue = host->cmd_per_lun * id; return 1; @@ -736,20 +744,24 @@ int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) if (!drive) { printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->target); - goto abort; + goto error; } scsi = drive->driver_data; pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC); rq = kmalloc (sizeof (struct request), GFP_ATOMIC); if (rq == NULL || pc == NULL) { printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name); - goto abort; + goto error; } memset (pc->c, 0, 12); pc->flags = 0; pc->rq = rq; memcpy (pc->c, cmd->cmnd, cmd->cmd_len); + if ((pc->c[1] >> 5) > scsi->last_lun) { + cmd->result = DID_ABORT << 16; + goto abort; + } if (cmd->use_sg) { pc->buffer = NULL; pc->sg = cmd->request_buffer; @@ -784,10 +796,12 @@ int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) (void) ide_do_drive_cmd (drive, rq, ide_end); spin_lock_irq(&io_request_lock); return 0; + +error: + cmd->result = DID_ERROR << 16; abort: if (pc) kfree (pc); if (rq) kfree (rq); - cmd->result = DID_ERROR << 16; done(cmd); return 0; } diff --git a/drivers/scsi/mac_NCR5380.c b/drivers/scsi/mac_NCR5380.c deleted file mode 100644 index a562e7f6d94b..000000000000 --- a/drivers/scsi/mac_NCR5380.c +++ /dev/null @@ -1,3139 +0,0 @@ -/* - * NCR 5380 generic driver routines. These should make it *trivial* - * to implement 5380 SCSI drivers under Linux with a non-trantor - * architecture. - * - * Note that these routines also work with NR53c400 family chips. - * - * Copyright 1993, Drew Eckhardt - * Visionary Computing - * (Unix and Linux consulting and custom programming) - * drew@colorado.edu - * +1 (303) 666-5836 - * - * DISTRIBUTION RELEASE 6. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 - */ - -/* - * ++roman: To port the 5380 driver to the Atari, I had to do some changes in - * this file, too: - * - * - Some of the debug statements were incorrect (undefined variables and the - * like). I fixed that. - * - * - In information_transfer(), I think a #ifdef was wrong. Looking at the - * possible DMA transfer size should also happen for REAL_DMA. I added this - * in the #if statement. - * - * - When using real DMA, information_transfer() should return in a DATAOUT - * phase after starting the DMA. It has nothing more to do. - * - * - The interrupt service routine should run main after end of DMA, too (not - * only after RESELECTION interrupts). Additionally, it should _not_ test - * for more interrupts after running main, since a DMA process may have - * been started and interrupts are turned on now. The new int could happen - * inside the execution of NCR5380_intr(), leading to recursive - * calls. - * - * - I've added a function merge_contiguous_buffers() that tries to - * merge scatter-gather buffers that are located at contiguous - * physical addresses and can be processed with the same DMA setup. - * Since most scatter-gather operations work on a page (4K) of - * 4 buffers (1K), in more than 90% of all cases three interrupts and - * DMA setup actions are saved. - * - * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA - * and USLEEP, because these were messing up readability and will never be - * needed for Atari SCSI. - * - * - I've revised the NCR5380_main() calling scheme (relax the 'main_running' - * stuff), and 'main' is executed in a bottom half if awoken by an - * interrupt. - * - * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..." - * constructs. In my eyes, this made the source rather unreadable, so I - * finally replaced that by the *_PRINTK() macros. - * - */ - -/* - * Further development / testing that should be done : - * 1. Test linked command handling code after Eric is ready with - * the high level code. - */ - -/* - * Michael: To port Romans driver to the Macintosh, I've left most of the code - * unchanged, in order to make later implemantation of REAL_DMA easier. - * - * Alan: In order to make it easier to read and as the 5380 based Mac's never - * have DMA I took the real DMA out of mac_scsi.c but not this file. - * - * With luck we can merge this back with the ST folks in time. - * - * Changes: - * - * - all Falcon-specific stuff (ST-DMA locking) was removed - * - * - */ - -#if (NDEBUG & NDEBUG_LISTS) -#define LIST(x,y) \ - { printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); \ - if ((x)==(y)) udelay(5); } -#define REMOVE(w,x,y,z) \ - { printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, \ - (void*)(w), (void*)(x), (void*)(y), (void*)(z)); \ - if ((x)==(y)) udelay(5); } -#else -#define LIST(x,y) -#define REMOVE(w,x,y,z) -#endif - -#ifndef notyet -#undef LINKED -#endif - -/* - * Design - * Issues : - * - * The other Linux SCSI drivers were written when Linux was Intel PC-only, - * and specifically for each board rather than each chip. This makes their - * adaptation to platforms like the Mac (Some of which use NCR5380's) - * more difficult than it has to be. - * - * Also, many of the SCSI drivers were written before the command queuing - * routines were implemented, meaning their implementations of queued - * commands were hacked on rather than designed in from the start. - * - * When I designed the Linux SCSI drivers I figured that - * while having two different SCSI boards in a system might be useful - * for debugging things, two of the same type wouldn't be used. - * Well, I was wrong and a number of users have mailed me about running - * multiple high-performance SCSI boards in a server. - * - * Finally, when I get questions from users, I have no idea what - * revision of my driver they are running. - * - * This driver attempts to address these problems : - * This is a generic 5380 driver. To use it on a different platform, - * one simply writes appropriate system specific macros (ie, data - * transfer - some PC's will use the I/O bus, 68K's must use - * memory mapped) and drops this file in their 'C' wrapper. - * - * As far as command queueing, two queues are maintained for - * each 5380 in the system - commands that haven't been issued yet, - * and commands that are currently executing. This means that an - * unlimited number of commands may be queued, letting - * more commands propagate from the higher driver levels giving higher - * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported, - * allowing multiple commands to propagate all the way to a SCSI-II device - * while a command is already executing. - * - * To solve the multiple-boards-in-the-same-system problem, - * there is a separate instance structure for each instance - * of a 5380 in the system. So, multiple NCR5380 drivers will - * be able to coexist with appropriate changes to the high level - * SCSI code. - * - * A NCR5380_PUBLIC_REVISION macro is provided, with the release - * number (updated for each public release) printed by the - * NCR5380_print_options command, which should be called from the - * wrapper detect function, so that I know what release of the driver - * users are using. - * - * Issues specific to the NCR5380 : - * - * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead - * piece of hardware that requires you to sit in a loop polling for - * the REQ signal as long as you are connected. Some devices are - * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect - * while doing long seek operations. - * - * The workaround for this is to keep track of devices that have - * disconnected. If the device hasn't disconnected, for commands that - * should disconnect, we do something like - * - * while (!REQ is asserted) { sleep for N usecs; poll for M usecs } - * - * Some tweaking of N and M needs to be done. An algorithm based - * on "time to data" would give the best results as long as short time - * to datas (ie, on the same track) were considered, however these - * broken devices are the exception rather than the rule and I'd rather - * spend my time optimizing for the normal case. - * - * Architecture : - * - * At the heart of the design is a coroutine, NCR5380_main, - * which is started when not running by the interrupt handler, - * timer, and queue command function. It attempts to establish - * I_T_L or I_T_L_Q nexuses by removing the commands from the - * issue queue and calling NCR5380_select() if a nexus - * is not established. - * - * Once a nexus is established, the NCR5380_information_transfer() - * phase goes through the various phases as instructed by the target. - * if the target goes into MSG IN and sends a DISCONNECT message, - * the command structure is placed into the per instance disconnected - * queue, and NCR5380_main tries to find more work. If USLEEP - * was defined, and the target is idle for too long, the system - * will try to sleep. - * - * If a command has disconnected, eventually an interrupt will trigger, - * calling NCR5380_intr() which will in turn call NCR5380_reselect - * to reestablish a nexus. This will run main if necessary. - * - * On command termination, the done function will be called as - * appropriate. - * - * SCSI pointers are maintained in the SCp field of SCSI command - * structures, being initialized after the command is connected - * in NCR5380_select, and set as appropriate in NCR5380_information_transfer. - * Note that in violation of the standard, an implicit SAVE POINTERS operation - * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS. - */ - -/* - * Using this file : - * This file a skeleton Linux SCSI driver for the NCR 5380 series - * of chips. To use it, you write a architecture specific functions - * and macros and include this file in your driver. - * - * These macros control options : - * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically - * for commands that return with a CHECK CONDITION status. - * - * LINKED - if defined, linked commands are supported. - * - * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases. - * - * REAL_DMA - if defined, REAL DMA is used during the data transfer phases. - * - * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible - * - * These macros MUST be defined : - * - * NCR5380_read(register) - read from the specified register - * - * NCR5380_write(register, value) - write to the specific register - * - * Either real DMA *or* pseudo DMA may be implemented - * REAL functions : - * NCR5380_REAL_DMA should be defined if real DMA is to be used. - * Note that the DMA setup functions should return the number of bytes - * that they were able to program the controller for. - * - * Also note that generic i386/PC versions of these macros are - * available as NCR5380_i386_dma_write_setup, - * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual. - * - * NCR5380_dma_write_setup(instance, src, count) - initialize - * NCR5380_dma_read_setup(instance, dst, count) - initialize - * NCR5380_dma_residual(instance); - residual count - * - * PSEUDO functions : - * NCR5380_pwrite(instance, src, count) - * NCR5380_pread(instance, dst, count); - * - * If nothing specific to this implementation needs doing (ie, with external - * hardware), you must also define - * - * NCR5380_queue_command - * NCR5380_reset - * NCR5380_abort - * NCR5380_proc_info - * - * to be the global entry points into the specific driver, ie - * #define NCR5380_queue_command t128_queue_command. - * - * If this is not done, the routines will be defined as static functions - * with the NCR5380* names and the user must provide a globally - * accessible wrapper function. - * - * The generic driver is initialized by calling NCR5380_init(instance), - * after setting the appropriate host specific fields and ID. If the - * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance, - * possible) function may be used. Before the specific driver initialization - * code finishes, NCR5380_print_options should be called. - */ - -static struct Scsi_Host *first_instance = NULL; -static Scsi_Host_Template *the_template = NULL; - -/* Macros ease life... :-) */ -#define SETUP_HOSTDATA(in) \ - struct NCR5380_hostdata *hostdata = \ - (struct NCR5380_hostdata *)(in)->hostdata -#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata) - -#define NEXT(cmd) ((Scsi_Cmnd *)((cmd)->host_scribble)) -#define NEXTADDR(cmd) ((Scsi_Cmnd **)&((cmd)->host_scribble)) - -#define HOSTNO instance->host_no -#define H_NO(cmd) (cmd)->host->host_no - -#ifdef SUPPORT_TAGS - -/* - * Functions for handling tagged queuing - * ===================================== - * - * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes: - * - * Using consecutive numbers for the tags is no good idea in my eyes. There - * could be wrong re-usings if the counter (8 bit!) wraps and some early - * command has been preempted for a long time. My solution: a bitfield for - * remembering used tags. - * - * There's also the problem that each target has a certain queue size, but we - * cannot know it in advance :-( We just see a QUEUE_FULL status being - * returned. So, in this case, the driver internal queue size assumption is - * reduced to the number of active tags if QUEUE_FULL is returned by the - * target. The command is returned to the mid-level, but with status changed - * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL - * correctly. - * - * We're also not allowed running tagged commands as long as an untagged - * command is active. And REQUEST SENSE commands after a contingent allegiance - * condition _must_ be untagged. To keep track whether an untagged command has - * been issued, the host->busy array is still employed, as it is without - * support for tagged queuing. - * - * One could suspect that there are possible race conditions between - * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the - * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(), - * which already guaranteed to be running at most once. It is also the only - * place where tags/LUNs are allocated. So no other allocation can slip - * between that pair, there could only happen a reselection, which can free a - * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes - * important: the tag bit must be cleared before 'nr_allocated' is decreased. - */ - -/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */ -#undef TAG_NONE -#define TAG_NONE 0xff - -/* For the m68k, the number of bits in 'allocated' must be a multiple of 32! */ -#if (MAX_TAGS % 32) != 0 -#error "MAX_TAGS must be a multiple of 32!" -#endif - -typedef struct { - char allocated[MAX_TAGS/8]; - int nr_allocated; - int queue_size; -} TAG_ALLOC; - -static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */ - - -static void init_tags( void ) -{ - int target, lun; - TAG_ALLOC *ta; - - if (!setup_use_tagged_queuing) - return; - - for( target = 0; target < 8; ++target ) { - for( lun = 0; lun < 8; ++lun ) { - ta = &TagAlloc[target][lun]; - memset( &ta->allocated, 0, MAX_TAGS/8 ); - ta->nr_allocated = 0; - /* At the beginning, assume the maximum queue size we could - * support (MAX_TAGS). This value will be decreased if the target - * returns QUEUE_FULL status. - */ - ta->queue_size = MAX_TAGS; - } - } -} - - -/* Check if we can issue a command to this LUN: First see if the LUN is marked - * busy by an untagged command. If the command should use tagged queuing, also - * check that there is a free tag and the target's queue won't overflow. This - * function should be called with interrupts disabled to avoid race - * conditions. - */ - -static int is_lun_busy( Scsi_Cmnd *cmd, int should_be_tagged ) -{ - SETUP_HOSTDATA(cmd->host); - - if (hostdata->busy[cmd->target] & (1 << cmd->lun)) - return( 1 ); - if (!should_be_tagged || - !setup_use_tagged_queuing || !cmd->device->tagged_supported) - return( 0 ); - if (TagAlloc[cmd->target][cmd->lun].nr_allocated >= - TagAlloc[cmd->target][cmd->lun].queue_size ) { - TAG_PRINTK( "scsi%d: target %d lun %d: no free tags\n", - H_NO(cmd), cmd->target, cmd->lun ); - return( 1 ); - } - return( 0 ); -} - - -/* Allocate a tag for a command (there are no checks anymore, check_lun_busy() - * must be called before!), or reserve the LUN in 'busy' if the command is - * untagged. - */ - -static void cmd_get_tag( Scsi_Cmnd *cmd, int should_be_tagged ) -{ - SETUP_HOSTDATA(cmd->host); - - /* If we or the target don't support tagged queuing, allocate the LUN for - * an untagged command. - */ - if (!should_be_tagged || - !setup_use_tagged_queuing || !cmd->device->tagged_supported) { - cmd->tag = TAG_NONE; - hostdata->busy[cmd->target] |= (1 << cmd->lun); - TAG_PRINTK( "scsi%d: target %d lun %d now allocated by untagged " - "command\n", H_NO(cmd), cmd->target, cmd->lun ); - } - else { - TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun]; - - cmd->tag = find_first_zero_bit( &ta->allocated, MAX_TAGS ); - set_bit( cmd->tag, &ta->allocated ); - ta->nr_allocated++; - TAG_PRINTK( "scsi%d: using tag %d for target %d lun %d " - "(now %d tags in use)\n", - H_NO(cmd), cmd->tag, cmd->target, cmd->lun, - ta->nr_allocated ); - } -} - - -/* Mark the tag of command 'cmd' as free, or in case of an untagged command, - * unlock the LUN. - */ - -static void cmd_free_tag( Scsi_Cmnd *cmd ) -{ - SETUP_HOSTDATA(cmd->host); - - if (cmd->tag == TAG_NONE) { - hostdata->busy[cmd->target] &= ~(1 << cmd->lun); - TAG_PRINTK( "scsi%d: target %d lun %d untagged cmd finished\n", - H_NO(cmd), cmd->target, cmd->lun ); - } - else if (cmd->tag >= MAX_TAGS) { - printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n", - H_NO(cmd), cmd->tag ); - } - else { - TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun]; - clear_bit( cmd->tag, &ta->allocated ); - ta->nr_allocated--; - TAG_PRINTK( "scsi%d: freed tag %d for target %d lun %d\n", - H_NO(cmd), cmd->tag, cmd->target, cmd->lun ); - } -} - - -static void free_all_tags( void ) -{ - int target, lun; - TAG_ALLOC *ta; - - if (!setup_use_tagged_queuing) - return; - - for( target = 0; target < 8; ++target ) { - for( lun = 0; lun < 8; ++lun ) { - ta = &TagAlloc[target][lun]; - memset( &ta->allocated, 0, MAX_TAGS/8 ); - ta->nr_allocated = 0; - } - } -} - -#endif /* SUPPORT_TAGS */ - - -/* - * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd ) - * - * Purpose: Try to merge several scatter-gather requests into one DMA - * transfer. This is possible if the scatter buffers lie on - * physical contiguous addresses. - * - * Parameters: Scsi_Cmnd *cmd - * The command to work on. The first scatter buffer's data are - * assumed to be already transfered into ptr/this_residual. - */ - -static void merge_contiguous_buffers( Scsi_Cmnd *cmd ) -{ - unsigned long endaddr; -#if (NDEBUG & NDEBUG_MERGING) - unsigned long oldlen = cmd->SCp.this_residual; - int cnt = 1; -#endif - - for (endaddr = VTOP(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1; - cmd->SCp.buffers_residual && - VTOP(cmd->SCp.buffer[1].address) == endaddr; ) { - - MER_PRINTK("VTOP(%p) == %08lx -> merging\n", - cmd->SCp.buffer[1].address, endaddr); -#if (NDEBUG & NDEBUG_MERGING) - ++cnt; -#endif - ++cmd->SCp.buffer; - --cmd->SCp.buffers_residual; - cmd->SCp.this_residual += cmd->SCp.buffer->length; - endaddr += cmd->SCp.buffer->length; - } -#if (NDEBUG & NDEBUG_MERGING) - if (oldlen != cmd->SCp.this_residual) - MER_PRINTK("merged %d buffers from %p, new length %08x\n", - cnt, cmd->SCp.ptr, cmd->SCp.this_residual); -#endif -} - -/* - * Function : void initialize_SCp(Scsi_Cmnd *cmd) - * - * Purpose : initialize the saved data pointers for cmd to point to the - * start of the buffer. - * - * Inputs : cmd - Scsi_Cmnd structure to have pointers reset. - */ - -static __inline__ void initialize_SCp(Scsi_Cmnd *cmd) -{ - /* - * Initialize the Scsi Pointer field so that all of the commands in the - * various queues are valid. - */ - - if (cmd->use_sg) { - cmd->SCp.buffer = (struct scatterlist *) cmd->buffer; - cmd->SCp.buffers_residual = cmd->use_sg - 1; - cmd->SCp.ptr = (char *) cmd->SCp.buffer->address; - cmd->SCp.this_residual = cmd->SCp.buffer->length; - /* ++roman: Try to merge some scatter-buffers if they are at - * contiguous physical addresses. - */ - merge_contiguous_buffers( cmd ); - } else { - cmd->SCp.buffer = NULL; - cmd->SCp.buffers_residual = 0; - cmd->SCp.ptr = (char *) cmd->request_buffer; - cmd->SCp.this_residual = cmd->request_bufflen; - } -} - -#include -#include - -#if 1 -static struct { - unsigned char mask; - const char * name;} -signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" }, - { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" }, - { SR_SEL, "SEL" }, {0, NULL}}, -basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}}, -icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"}, - {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"}, - {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"}, - {0, NULL}}, -mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"}, - {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR, - "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"}, - {MR_MONITOR_BSY, "MODE MONITOR BSY"}, - {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"}, - {0, NULL}}; - -/* - * Function : void NCR5380_print(struct Scsi_Host *instance) - * - * Purpose : print the SCSI bus signals for debugging purposes - * - * Input : instance - which NCR5380 - */ - -static void NCR5380_print(struct Scsi_Host *instance) { - unsigned char status, data, basr, mr, icr, i; - unsigned long flags; - - save_flags(flags); - cli(); - data = NCR5380_read(CURRENT_SCSI_DATA_REG); - status = NCR5380_read(STATUS_REG); - mr = NCR5380_read(MODE_REG); - icr = NCR5380_read(INITIATOR_COMMAND_REG); - basr = NCR5380_read(BUS_AND_STATUS_REG); - restore_flags(flags); - printk("STATUS_REG: %02x ", status); - for (i = 0; signals[i].mask ; ++i) - if (status & signals[i].mask) - printk(",%s", signals[i].name); - printk("\nBASR: %02x ", basr); - for (i = 0; basrs[i].mask ; ++i) - if (basr & basrs[i].mask) - printk(",%s", basrs[i].name); - printk("\nICR: %02x ", icr); - for (i = 0; icrs[i].mask; ++i) - if (icr & icrs[i].mask) - printk(",%s", icrs[i].name); - printk("\nMODE: %02x ", mr); - for (i = 0; mrs[i].mask; ++i) - if (mr & mrs[i].mask) - printk(",%s", mrs[i].name); - printk("\n"); -} - -static struct { - unsigned char value; - const char *name; -} phases[] = { - {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"}, - {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"}, - {PHASE_UNKNOWN, "UNKNOWN"}}; - -/* - * Function : void NCR5380_print_phase(struct Scsi_Host *instance) - * - * Purpose : print the current SCSI phase for debugging purposes - * - * Input : instance - which NCR5380 - */ - -static void NCR5380_print_phase(struct Scsi_Host *instance) -{ - unsigned char status; - int i; - - status = NCR5380_read(STATUS_REG); - if (!(status & SR_REQ)) - printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO); - else { - for (i = 0; (phases[i].value != PHASE_UNKNOWN) && - (phases[i].value != (status & PHASE_MASK)); ++i); - printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name); - } -} - -#else /* !NDEBUG */ - -/* dummies... */ -__inline__ void NCR5380_print(struct Scsi_Host *instance) { }; -__inline__ void NCR5380_print_phase(struct Scsi_Host *instance) { }; - -#endif - -/* - * ++roman: New scheme of calling NCR5380_main() - * - * If we're not in an interrupt, we can call our main directly, it cannot be - * already running. Else, we queue it on a task queue, if not 'main_running' - * tells us that a lower level is already executing it. This way, - * 'main_running' needs not be protected in a special way. - * - * queue_main() is a utility function for putting our main onto the task - * queue, if main_running is false. It should be called only from a - * interrupt or bottom half. - */ - -#include -#include - -static volatile int main_running = 0; -static struct tq_struct NCR5380_tqueue = { - NULL, /* next */ - 0, /* sync */ - (void (*)(void*))NCR5380_main, /* routine, must have (void *) arg... */ - NULL /* data */ -}; - -static __inline__ void queue_main(void) -{ - if (!main_running) { - /* If in interrupt and NCR5380_main() not already running, - queue it on the 'immediate' task queue, to be processed - immediately after the current interrupt processing has - finished. */ - queue_task(&NCR5380_tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); - } - /* else: nothing to do: the running NCR5380_main() will pick up - any newly queued command. */ -} - - -static void NCR5380_all_init (void) -{ - static int done = 0; - if (!done) { - INI_PRINTK("scsi : NCR5380_all_init()\n"); - done = 1; - } -} - - -/* - * Function : void NCR58380_print_options (struct Scsi_Host *instance) - * - * Purpose : called by probe code indicating the NCR5380 driver - * options that were selected. - * - * Inputs : instance, pointer to this instance. Unused. - */ - -static void NCR5380_print_options (struct Scsi_Host *instance) -{ - printk(" generic options" -#ifdef AUTOSENSE - " AUTOSENSE" -#endif -#ifdef REAL_DMA - " REAL DMA" -#endif -#ifdef PSEUDO_DMA - " PSEUDO DMA" -#endif -#ifdef PARITY - " PARITY" -#endif -#ifdef SUPPORT_TAGS - " SCSI-2 TAGGED QUEUING" -#endif - ); - printk(" generic release=%d", NCR5380_PUBLIC_RELEASE); -} - -/* - * Function : void NCR5380_print_status (struct Scsi_Host *instance) - * - * Purpose : print commands in the various queues, called from - * NCR5380_abort and NCR5380_debug to aid debugging. - * - * Inputs : instance, pointer to this instance. - */ - -static void NCR5380_print_status (struct Scsi_Host *instance) -{ - char *pr_bfr; - char *start; - int len; - - NCR_PRINT(NDEBUG_ANY); - NCR_PRINT_PHASE(NDEBUG_ANY); - - pr_bfr = (char *) __get_free_page(GFP_ATOMIC); - if (!pr_bfr) { - printk("NCR5380_print_status: no memory for print buffer\n"); - return; - } - len = NCR5380_proc_info(pr_bfr, &start, 0, PAGE_SIZE, HOSTNO, 0); - pr_bfr[len] = 0; - printk("\n%s\n", pr_bfr); - free_page((unsigned long) pr_bfr); -} - - -/******************************************/ -/* - * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED] - * - * *buffer: I/O buffer - * **start: if inout == FALSE pointer into buffer where user read should start - * offset: current offset - * length: length of buffer - * hostno: Scsi_Host host_no - * inout: TRUE - user is writing; FALSE - user is reading - * - * Return the number of bytes read from or written -*/ - -#undef SPRINTF -#define SPRINTF(fmt,args...) \ - do { if (pos + strlen(fmt) + 20 /* slop */ < buffer + length) \ - pos += sprintf(pos, fmt , ## args); } while(0) -static -char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length); - -#ifndef NCR5380_proc_info -static -#endif -int NCR5380_proc_info (char *buffer, char **start, off_t offset, - int length, int hostno, int inout) -{ - char *pos = buffer; - struct Scsi_Host *instance; - struct NCR5380_hostdata *hostdata; - Scsi_Cmnd *ptr; - unsigned long flags; - off_t begin = 0; -#define check_offset() \ - do { \ - if (pos - buffer < offset - begin) { \ - begin += pos - buffer; \ - pos = buffer; \ - } \ - } while (0) - - for (instance = first_instance; instance && HOSTNO != hostno; - instance = instance->next) - ; - if (!instance) - return(-ESRCH); - hostdata = (struct NCR5380_hostdata *)instance->hostdata; - - if (inout) { /* Has data been written to the file ? */ - return(-ENOSYS); /* Currently this is a no-op */ - } - SPRINTF("NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE); - check_offset(); - save_flags(flags); - cli(); - SPRINTF("NCR5380: coroutine is%s running.\n", main_running ? "" : "n't"); - check_offset(); - if (!hostdata->connected) - SPRINTF("scsi%d: no currently connected command\n", HOSTNO); - else - pos = lprint_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected, - pos, buffer, length); - SPRINTF("scsi%d: issue_queue\n", HOSTNO); - check_offset(); - for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = NEXT(ptr)) { - pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length); - check_offset(); - } - - SPRINTF("scsi%d: disconnected_queue\n", HOSTNO); - check_offset(); - for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; - ptr = NEXT(ptr)) { - pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length); - check_offset(); - } - - restore_flags(flags); - *start = buffer + (offset - begin); - if (pos - buffer < offset - begin) - return 0; - else if (pos - buffer - (offset - begin) < length) - return pos - buffer - (offset - begin); - return length; -} - -static char * -lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length) -{ - int i, s; - unsigned char *command; - SPRINTF("scsi%d: destination target %d, lun %d\n", - H_NO(cmd), cmd->target, cmd->lun); - SPRINTF(" command = "); - command = cmd->cmnd; - SPRINTF("%2d (0x%02x)", command[0], command[0]); - for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i) - SPRINTF(" %02x", command[i]); - SPRINTF("\n"); - return pos; -} - - -/* - * Function : void NCR5380_init (struct Scsi_Host *instance) - * - * Purpose : initializes *instance and corresponding 5380 chip. - * - * Inputs : instance - instantiation of the 5380 driver. - * - * Notes : I assume that the host, hostno, and id bits have been - * set correctly. I don't care about the irq and other fields. - * - */ - -static void NCR5380_init (struct Scsi_Host *instance, int flags) -{ - int i; - SETUP_HOSTDATA(instance); - - NCR5380_all_init(); - - hostdata->aborted = 0; - hostdata->id_mask = 1 << instance->this_id; - hostdata->id_higher_mask = 0; - for (i = hostdata->id_mask; i <= 0x80; i <<= 1) - if (i > hostdata->id_mask) - hostdata->id_higher_mask |= i; - for (i = 0; i < 8; ++i) - hostdata->busy[i] = 0; -#ifdef SUPPORT_TAGS - init_tags(); -#endif -#if defined (REAL_DMA) - hostdata->dma_len = 0; -#endif - hostdata->targets_present = 0; - hostdata->connected = NULL; - hostdata->issue_queue = NULL; - hostdata->disconnected_queue = NULL; - hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT; - - if (!the_template) { - the_template = instance->hostt; - first_instance = instance; - } - - -#ifndef AUTOSENSE - if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1)) - printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n" - " without AUTOSENSE option, contingent allegiance conditions may\n" - " be incorrectly cleared.\n", HOSTNO); -#endif /* def AUTOSENSE */ - - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(TARGET_COMMAND_REG, 0); - NCR5380_write(SELECT_ENABLE_REG, 0); -} - -/* - * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd, - * void (*done)(Scsi_Cmnd *)) - * - * Purpose : enqueues a SCSI command - * - * Inputs : cmd - SCSI command, done - function called on completion, with - * a pointer to the command descriptor. - * - * Returns : 0 - * - * Side effects : - * cmd is added to the per instance issue_queue, with minor - * twiddling done to the host specific fields of cmd. If the - * main coroutine is not running, it is restarted. - * - */ - -/* Only make static if a wrapper function is used */ -#ifndef NCR5380_queue_command -static -#endif -int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) -{ - SETUP_HOSTDATA(cmd->host); - Scsi_Cmnd *tmp; - int oldto; - unsigned long flags; - extern int update_timeout(Scsi_Cmnd * SCset, int timeout); - -#if (NDEBUG & NDEBUG_NO_WRITE) - switch (cmd->cmnd[0]) { - case WRITE_6: - case WRITE_10: - printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n", - H_NO(cmd)); - cmd->result = (DID_ERROR << 16); - done(cmd); - return 0; - } -#endif /* (NDEBUG & NDEBUG_NO_WRITE) */ - - -#ifdef NCR5380_STATS -# if 0 - if (!hostdata->connected && !hostdata->issue_queue && - !hostdata->disconnected_queue) { - hostdata->timebase = jiffies; - } -# endif -# ifdef NCR5380_STAT_LIMIT - if (cmd->request_bufflen > NCR5380_STAT_LIMIT) -# endif - switch (cmd->cmnd[0]) - { - case WRITE: - case WRITE_6: - case WRITE_10: - hostdata->time_write[cmd->target] -= (jiffies - hostdata->timebase); - hostdata->bytes_write[cmd->target] += cmd->request_bufflen; - hostdata->pendingw++; - break; - case READ: - case READ_6: - case READ_10: - hostdata->time_read[cmd->target] -= (jiffies - hostdata->timebase); - hostdata->bytes_read[cmd->target] += cmd->request_bufflen; - hostdata->pendingr++; - break; - } -#endif - - /* - * We use the host_scribble field as a pointer to the next command - * in a queue - */ - - NEXT(cmd) = NULL; - cmd->scsi_done = done; - - cmd->result = 0; - - - /* - * Insert the cmd into the issue queue. Note that REQUEST SENSE - * commands are added to the head of the queue since any command will - * clear the contingent allegiance condition that exists and the - * sense data is only guaranteed to be valid while the condition exists. - */ - - save_flags(flags); - cli(); - - if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { - LIST(cmd, hostdata->issue_queue); - NEXT(cmd) = hostdata->issue_queue; - hostdata->issue_queue = cmd; - } else { - for (tmp = (Scsi_Cmnd *)hostdata->issue_queue; - NEXT(tmp); tmp = NEXT(tmp)) - ; - LIST(cmd, tmp); - NEXT(tmp) = cmd; - } - restore_flags(flags); - - QU_PRINTK("scsi%d: command added to %s of queue\n", H_NO(cmd), - (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"); - - /* If queue_command() is called from an interrupt (real one or bottom - * half), we let queue_main() do the job of taking care about main. If it - * is already running, this is a no-op, else main will be queued. - * - * If we're not in an interrupt, we can call NCR5380_main() - * unconditionally, because it cannot be already running. - */ - if (in_interrupt() > 0 || ((flags >> 8) & 7) >= 6) - queue_main(); - else - NCR5380_main(); - return 0; -} - -/* - * Function : NCR5380_main (void) - * - * Purpose : NCR5380_main is a coroutine that runs as long as more work can - * be done on the NCR5380 host adapters in a system. Both - * NCR5380_queue_command() and NCR5380_intr() will try to start it - * in case it is not running. - * - * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should - * reenable them. This prevents reentrancy and kernel stack overflow. - */ - -static void NCR5380_main (void) -{ - Scsi_Cmnd *tmp, *prev; - struct Scsi_Host *instance = first_instance; - struct NCR5380_hostdata *hostdata = HOSTDATA(instance); - int done; - unsigned long flags; - - /* - * We run (with interrupts disabled) until we're sure that none of - * the host adapters have anything that can be done, at which point - * we set main_running to 0 and exit. - * - * Interrupts are enabled before doing various other internal - * instructions, after we've decided that we need to run through - * the loop again. - * - * this should prevent any race conditions. - * - * ++roman: Just disabling the NCR interrupt isn't sufficient here, - * because also a timer int can trigger an abort or reset, which can - * alter queues and touch the Falcon lock. - */ - - /* Tell int handlers main() is now already executing. Note that - no races are possible here. If an int comes in before - 'main_running' is set here, and queues/executes main via the - task queue, it doesn't do any harm, just this instance of main - won't find any work left to do. */ - if (main_running) - return; - main_running = 1; - - save_flags(flags); - do { - cli(); /* Freeze request queues */ - done = 1; - - if (!hostdata->connected) { - MAIN_PRINTK( "scsi%d: not connected\n", HOSTNO ); - /* - * Search through the issue_queue for a command destined - * for a target that's not busy. - */ -#if (NDEBUG & NDEBUG_LISTS) - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; - tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp)) - ; - /*printk("%p ", tmp);*/ - if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/ -#endif - for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, - prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) { - -#if (NDEBUG & NDEBUG_LISTS) - if (prev != tmp) - printk("MAIN tmp=%p target=%d busy=%d lun=%d\n", - tmp, tmp->target, hostdata->busy[tmp->target], - tmp->lun); -#endif - /* When we find one, remove it from the issue queue. */ - if ( -#ifdef SUPPORT_TAGS - !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE) -#else - !(hostdata->busy[tmp->target] & (1 << tmp->lun)) -#endif - ) { - cli(); /* ++guenther: just to be sure, this must be atomic */ - if (prev) { - REMOVE(prev, NEXT(prev), tmp, NEXT(tmp)); - NEXT(prev) = NEXT(tmp); - } else { - REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp)); - hostdata->issue_queue = NEXT(tmp); - } - NEXT(tmp) = NULL; - - /* reenable interrupts after finding one */ - restore_flags(flags); - - /* - * Attempt to establish an I_T_L nexus here. - * On success, instance->hostdata->connected is set. - * On failure, we must add the command back to the - * issue queue so we can keep trying. - */ - MAIN_PRINTK("scsi%d: main(): command for target %d " - "lun %d removed from issue_queue\n", - HOSTNO, tmp->target, tmp->lun); - /* - * REQUEST SENSE commands are issued without tagged - * queueing, even on SCSI-II devices because the - * contingent allegiance condition exists for the - * entire unit. - */ - /* ++roman: ...and the standard also requires that - * REQUEST SENSE command are untagged. - */ - -#ifdef SUPPORT_TAGS - cmd_get_tag( tmp, tmp->cmnd[0] != REQUEST_SENSE ); -#endif - if (!NCR5380_select(instance, tmp, - (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : - TAG_NEXT)) { - break; - } else { - cli(); - LIST(tmp, hostdata->issue_queue); - NEXT(tmp) = hostdata->issue_queue; - hostdata->issue_queue = tmp; -#ifdef SUPPORT_TAGS - cmd_free_tag( tmp ); -#endif - restore_flags(flags); - MAIN_PRINTK("scsi%d: main(): select() failed, " - "returned to issue_queue\n", HOSTNO); - if (hostdata->connected) - break; - } - } /* if target/lun/target queue is not busy */ - } /* for issue_queue */ - } /* if (!hostdata->connected) */ - - if (hostdata->connected -#ifdef REAL_DMA - && !hostdata->dma_len -#endif - ) { - restore_flags(flags); - MAIN_PRINTK("scsi%d: main: performing information transfer\n", - HOSTNO); - NCR5380_information_transfer(instance); - MAIN_PRINTK("scsi%d: main: done set false\n", HOSTNO); - done = 0; - } - } while (!done); - - /* Better allow ints _after_ 'main_running' has been cleared, else - an interrupt could believe we'll pick up the work it left for - us, but we won't see it anymore here... */ - main_running = 0; - restore_flags(flags); -} - - -#ifdef REAL_DMA -/* - * Function : void NCR5380_dma_complete (struct Scsi_Host *instance) - * - * Purpose : Called by interrupt handler when DMA finishes or a phase - * mismatch occurs (which would finish the DMA transfer). - * - * Inputs : instance - this instance of the NCR5380. - * - */ - -static void NCR5380_dma_complete( struct Scsi_Host *instance ) -{ - SETUP_HOSTDATA(instance); - int transfered, saved_data = 0, overrun = 0, cnt, toPIO; - unsigned char **data, p; - volatile int *count; - - if (!hostdata->connected) { - printk(KERN_WARNING "scsi%d: received end of DMA interrupt with " - "no connected cmd\n", HOSTNO); - return; - } - - if (mac_read_overruns) { - p = hostdata->connected->SCp.phase; - if (p & SR_IO) { - udelay(10); - if ((((NCR5380_read(BUS_AND_STATUS_REG)) & - (BASR_PHASE_MATCH|BASR_ACK)) == - (BASR_PHASE_MATCH|BASR_ACK))) { - saved_data = NCR5380_read(INPUT_DATA_REG); - overrun = 1; - DMA_PRINTK("scsi%d: read overrun handled\n", HOSTNO); - } - } - } - - DMA_PRINTK("scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n", - HOSTNO, NCR5380_read(BUS_AND_STATUS_REG), - NCR5380_read(STATUS_REG)); - - (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - transfered = hostdata->dma_len - NCR5380_dma_residual(instance); - hostdata->dma_len = 0; - - data = (unsigned char **) &(hostdata->connected->SCp.ptr); - count = &(hostdata->connected->SCp.this_residual); - *data += transfered; - *count -= transfered; - - if (mac_read_overruns) { - if ((NCR5380_read(STATUS_REG) & PHASE_MASK) == p && (p & SR_IO)) { - cnt = toPIO = mac_read_overruns; - if (overrun) { - DMA_PRINTK("Got an input overrun, using saved byte\n"); - *(*data)++ = saved_data; - (*count)--; - cnt--; - toPIO--; - } - DMA_PRINTK("Doing %d-byte PIO to 0x%08lx\n", cnt, (long)*data); - NCR5380_transfer_pio(instance, &p, &cnt, data); - *count -= toPIO - cnt; - } - } -} -#endif /* REAL_DMA */ - - -/* - * Function : void NCR5380_intr (int irq) - * - * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses - * from the disconnected queue, and restarting NCR5380_main() - * as required. - * - * Inputs : int irq, irq that caused this interrupt. - * - */ - -static void NCR5380_intr (int irq, void *dev_id, struct pt_regs *regs) -{ - struct Scsi_Host *instance = first_instance; - int done = 1; - unsigned char basr; - - INT_PRINTK("scsi%d: NCR5380 irq triggered\n", HOSTNO); - - /* Look for pending interrupts */ - basr = NCR5380_read(BUS_AND_STATUS_REG); - INT_PRINTK("scsi%d: BASR=%02x\n", HOSTNO, basr); - /* dispatch to appropriate routine if found and done=0 */ - if (basr & BASR_IRQ) { - NCR_PRINT(NDEBUG_INTR); - if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) { - done = 0; - ENABLE_IRQ(); - INT_PRINTK("scsi%d: SEL interrupt\n", HOSTNO); - NCR5380_reselect(instance); - (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); - } - else if (basr & BASR_PARITY_ERROR) { - INT_PRINTK("scsi%d: PARITY interrupt\n", HOSTNO); - (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); - } - else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) { - INT_PRINTK("scsi%d: RESET interrupt\n", HOSTNO); - (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG); - } - else { - /* - * The rest of the interrupt conditions can occur only during a - * DMA transfer - */ - -#if defined(REAL_DMA) - /* - * We should only get PHASE MISMATCH and EOP interrupts if we have - * DMA enabled, so do a sanity check based on the current setting - * of the MODE register. - */ - - if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) && - ((basr & BASR_END_DMA_TRANSFER) || - !(basr & BASR_PHASE_MATCH))) { - - INT_PRINTK("scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO); - NCR5380_dma_complete( instance ); - done = 0; - ENABLE_IRQ(); - } else -#endif /* REAL_DMA */ - { -/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */ - if (basr & BASR_PHASE_MATCH) - printk(KERN_NOTICE "scsi%d: unknown interrupt, " - "BASR 0x%x, MR 0x%x, SR 0x%x\n", - HOSTNO, basr, NCR5380_read(MODE_REG), - NCR5380_read(STATUS_REG)); - (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); - } - } /* if !(SELECTION || PARITY) */ - } /* BASR & IRQ */ - else { - printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, " - "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr, - NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG)); - (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG); - } - - if (!done) { - INT_PRINTK("scsi%d: in int routine, calling main\n", HOSTNO); - /* Put a call to NCR5380_main() on the queue... */ - queue_main(); - } -} - -#ifdef NCR5380_STATS -static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd* cmd) -{ -# ifdef NCR5380_STAT_LIMIT - if (cmd->request_bufflen > NCR5380_STAT_LIMIT) -# endif - switch (cmd->cmnd[0]) - { - case WRITE: - case WRITE_6: - case WRITE_10: - hostdata->time_write[cmd->target] += (jiffies - hostdata->timebase); - /*hostdata->bytes_write[cmd->target] += cmd->request_bufflen;*/ - hostdata->pendingw--; - break; - case READ: - case READ_6: - case READ_10: - hostdata->time_read[cmd->target] += (jiffies - hostdata->timebase); - /*hostdata->bytes_read[cmd->target] += cmd->request_bufflen;*/ - hostdata->pendingr--; - break; - } -} -#endif - -/* - * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, - * int tag); - * - * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command, - * including ARBITRATION, SELECTION, and initial message out for - * IDENTIFY and queue messages. - * - * Inputs : instance - instantiation of the 5380 driver on which this - * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for - * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for - * the command that is presently connected. - * - * Returns : -1 if selection could not execute for some reason, - * 0 if selection succeeded or failed because the target - * did not respond. - * - * Side effects : - * If bus busy, arbitration failed, etc, NCR5380_select() will exit - * with registers as they should have been on entry - ie - * SELECT_ENABLE will be set appropriately, the NCR5380 - * will cease to drive any SCSI bus signals. - * - * If successful : I_T_L or I_T_L_Q nexus will be established, - * instance->connected will be set to cmd. - * SELECT interrupt will be disabled. - * - * If failed (no target) : cmd->scsi_done() will be called, and the - * cmd->result host byte set to DID_BAD_TARGET. - */ - -static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag) -{ - SETUP_HOSTDATA(instance); - unsigned char tmp[3], phase; - unsigned char *data; - int len; - unsigned long timeout; - unsigned long flags; - - hostdata->restart_select = 0; - NCR_PRINT(NDEBUG_ARBITRATION); - ARB_PRINTK("scsi%d: starting arbitration, id = %d\n", HOSTNO, - instance->this_id); - - /* - * Set the phase bits to 0, otherwise the NCR5380 won't drive the - * data bus during SELECTION. - */ - - save_flags(flags); - cli(); - if (hostdata->connected) { - restore_flags(flags); - return -1; - } - NCR5380_write(TARGET_COMMAND_REG, 0); - - - /* - * Start arbitration. - */ - - NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask); - NCR5380_write(MODE_REG, MR_ARBITRATE); - - restore_flags(flags); - - /* Wait for arbitration logic to complete */ -#if NCR_TIMEOUT - { - unsigned long timeout = jiffies + 2*NCR_TIMEOUT; - - while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS) - && time_before(jiffies, timeout) && !hostdata->connected) - ; - if (time_after_eq(jiffies, timeout)) - { - printk("scsi : arbitration timeout at %d\n", __LINE__); - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - return -1; - } - } -#else /* NCR_TIMEOUT */ - while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS) - && !hostdata->connected); -#endif - - ARB_PRINTK("scsi%d: arbitration complete\n", HOSTNO); - - if (hostdata->connected) { - NCR5380_write(MODE_REG, MR_BASE); - return -1; - } - /* - * The arbitration delay is 2.2us, but this is a minimum and there is - * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate - * the integral nature of udelay(). - * - */ - - udelay(3); - - /* Check for lost arbitration */ - if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || - (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) || - (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || - hostdata->connected) { - NCR5380_write(MODE_REG, MR_BASE); - ARB_PRINTK("scsi%d: lost arbitration, deasserting MR_ARBITRATE\n", - HOSTNO); - return -1; - } - - /* after/during arbitration, BSY should be asserted. - IBM DPES-31080 Version S31Q works now */ - /* Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman) */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL | - ICR_ASSERT_BSY ) ; - - if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) || - hostdata->connected) { - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - ARB_PRINTK("scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n", - HOSTNO); - return -1; - } - - /* - * Again, bus clear + bus settle time is 1.2us, however, this is - * a minimum so we'll udelay ceil(1.2) - */ - -#ifdef CONFIG_ATARI_SCSI_TOSHIBA_DELAY - /* ++roman: But some targets (see above :-) seem to need a bit more... */ - udelay(15); -#else - udelay(2); -#endif - - if (hostdata->connected) { - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - return -1; - } - - ARB_PRINTK("scsi%d: won arbitration\n", HOSTNO); - - /* - * Now that we have won arbitration, start Selection process, asserting - * the host and target ID's on the SCSI bus. - */ - - NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->target))); - - /* - * Raise ATN while SEL is true before BSY goes false from arbitration, - * since this is the only way to guarantee that we'll get a MESSAGE OUT - * phase immediately after selection. - */ - - NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY | - ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL )); - NCR5380_write(MODE_REG, MR_BASE); - - /* - * Reselect interrupts must be turned off prior to the dropping of BSY, - * otherwise we will trigger an interrupt. - */ - - if (hostdata->connected) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - return -1; - } - - NCR5380_write(SELECT_ENABLE_REG, 0); - - /* - * The initiator shall then wait at least two deskew delays and release - * the BSY signal. - */ - udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */ - - /* Reset BSY */ - NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA | - ICR_ASSERT_ATN | ICR_ASSERT_SEL)); - - /* - * Something weird happens when we cease to drive BSY - looks - * like the board/chip is letting us do another read before the - * appropriate propagation delay has expired, and we're confusing - * a BSY signal from ourselves as the target's response to SELECTION. - * - * A small delay (the 'C++' frontend breaks the pipeline with an - * unnecessary jump, making it work on my 386-33/Trantor T128, the - * tighter 'C' code breaks and requires this) solves the problem - - * the 1 us delay is arbitrary, and only used because this delay will - * be the same on other platforms and since it works here, it should - * work there. - * - * wingel suggests that this could be due to failing to wait - * one deskew delay. - */ - - udelay(1); - - SEL_PRINTK("scsi%d: selecting target %d\n", HOSTNO, cmd->target); - - /* - * The SCSI specification calls for a 250 ms timeout for the actual - * selection. - */ - - timeout = jiffies + 25; - - /* - * XXX very interesting - we're seeing a bounce where the BSY we - * asserted is being reflected / still asserted (propagation delay?) - * and it's detecting as true. Sigh. - */ - -#if 0 - /* ++roman: If a target conformed to the SCSI standard, it wouldn't assert - * IO while SEL is true. But again, there are some disks out the in the - * world that do that nevertheless. (Somebody claimed that this announces - * reselection capability of the target.) So we better skip that test and - * only wait for BSY... (Famous german words: Der Klügere gibt nach :-) - */ - - while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & - (SR_BSY | SR_IO))); - - if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == - (SR_SEL | SR_IO)) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - NCR5380_reselect(instance); - printk (KERN_ERR "scsi%d: reselection after won arbitration?\n", - HOSTNO); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - return -1; - } -#else - while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY)); -#endif - - /* - * No less than two deskew delays after the initiator detects the - * BSY signal is true, it shall release the SEL signal and may - * change the DATA BUS. -wingel - */ - - udelay(1); - - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); - - if (!(NCR5380_read(STATUS_REG) & SR_BSY)) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - if (hostdata->targets_present & (1 << cmd->target)) { - printk(KERN_ERR "scsi%d: weirdness\n", HOSTNO); - if (hostdata->restart_select) - printk(KERN_NOTICE "\trestart select\n"); - NCR_PRINT(NDEBUG_ANY); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - return -1; - } - cmd->result = DID_BAD_TARGET << 16; -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif -#ifdef SUPPORT_TAGS - cmd_free_tag( cmd ); -#endif - cmd->scsi_done(cmd); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - SEL_PRINTK("scsi%d: target did not respond within 250ms\n", HOSTNO); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - return 0; - } - - hostdata->targets_present |= (1 << cmd->target); - - /* - * Since we followed the SCSI spec, and raised ATN while SEL - * was true but before BSY was false during selection, the information - * transfer phase should be a MESSAGE OUT phase so that we can send the - * IDENTIFY message. - * - * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG - * message (2 bytes) with a tag ID that we increment with every command - * until it wraps back to 0. - * - * XXX - it turns out that there are some broken SCSI-II devices, - * which claim to support tagged queuing but fail when more than - * some number of commands are issued at once. - */ - - /* Wait for start of REQ/ACK handshake */ - while (!(NCR5380_read(STATUS_REG) & SR_REQ)); - - SEL_PRINTK("scsi%d: target %d selected, going into MESSAGE OUT phase.\n", - HOSTNO, cmd->target); - tmp[0] = IDENTIFY(1, cmd->lun); - -#ifdef SUPPORT_TAGS - if (cmd->tag != TAG_NONE) { - tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG; - tmp[2] = cmd->tag; - len = 3; - } else - len = 1; -#else - len = 1; - cmd->tag=0; -#endif /* SUPPORT_TAGS */ - - /* Send message(s) */ - data = tmp; - phase = PHASE_MSGOUT; - NCR5380_transfer_pio(instance, &phase, &len, &data); - SEL_PRINTK("scsi%d: nexus established.\n", HOSTNO); - /* XXX need to handle errors here */ - hostdata->connected = cmd; -#ifndef SUPPORT_TAGS - hostdata->busy[cmd->target] |= (1 << cmd->lun); -#endif - - initialize_SCp(cmd); - - - return 0; -} - -/* - * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance, - * unsigned char *phase, int *count, unsigned char **data) - * - * Purpose : transfers data in given phase using polled I/O - * - * Inputs : instance - instance of driver, *phase - pointer to - * what phase is expected, *count - pointer to number of - * bytes to transfer, **data - pointer to data pointer. - * - * Returns : -1 when different phase is entered without transferring - * maximum number of bytes, 0 if all bytes are transfered or exit - * is in same phase. - * - * Also, *phase, *count, *data are modified in place. - * - * XXX Note : handling for bus free may be useful. - */ - -/* - * Note : this code is not as quick as it could be, however it - * IS 100% reliable, and for the actual data transfer where speed - * counts, we will always do a pseudo DMA or DMA transfer. - */ - -static int NCR5380_transfer_pio( struct Scsi_Host *instance, - unsigned char *phase, int *count, - unsigned char **data) -{ - register unsigned char p = *phase, tmp; - register int c = *count; - register unsigned char *d = *data; - - /* - * The NCR5380 chip will only drive the SCSI bus when the - * phase specified in the appropriate bits of the TARGET COMMAND - * REGISTER match the STATUS REGISTER - */ - - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p)); - - do { - /* - * Wait for assertion of REQ, after which the phase bits will be - * valid - */ - while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ)); - - HSH_PRINTK("scsi%d: REQ detected\n", HOSTNO); - - /* Check for phase mismatch */ - if ((tmp & PHASE_MASK) != p) { - PIO_PRINTK("scsi%d: phase mismatch\n", HOSTNO); - NCR_PRINT_PHASE(NDEBUG_PIO); - break; - } - - /* Do actual transfer from SCSI bus to / from memory */ - if (!(p & SR_IO)) - NCR5380_write(OUTPUT_DATA_REG, *d); - else - *d = NCR5380_read(CURRENT_SCSI_DATA_REG); - - ++d; - - /* - * The SCSI standard suggests that in MSGOUT phase, the initiator - * should drop ATN on the last byte of the message phase - * after REQ has been asserted for the handshake but before - * the initiator raises ACK. - */ - - if (!(p & SR_IO)) { - if (!((p & SR_MSG) && c > 1)) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA); - NCR_PRINT(NDEBUG_PIO); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA | ICR_ASSERT_ACK); - } else { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA | ICR_ASSERT_ATN); - NCR_PRINT(NDEBUG_PIO); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK); - } - } else { - NCR_PRINT(NDEBUG_PIO); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK); - } - - while (NCR5380_read(STATUS_REG) & SR_REQ); - - HSH_PRINTK("scsi%d: req false, handshake complete\n", HOSTNO); - -/* - * We have several special cases to consider during REQ/ACK handshaking : - * 1. We were in MSGOUT phase, and we are on the last byte of the - * message. ATN must be dropped as ACK is dropped. - * - * 2. We are in a MSGIN phase, and we are on the last byte of the - * message. We must exit with ACK asserted, so that the calling - * code may raise ATN before dropping ACK to reject the message. - * - * 3. ACK and ATN are clear and the target may proceed as normal. - */ - if (!(p == PHASE_MSGIN && c == 1)) { - if (p == PHASE_MSGOUT && c > 1) - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); - else - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - } - } while (--c); - - PIO_PRINTK("scsi%d: residual %d\n", HOSTNO, c); - - *count = c; - *data = d; - tmp = NCR5380_read(STATUS_REG); - /* The phase read from the bus is valid if either REQ is (already) - * asserted or if ACK hasn't been released yet. The latter is the case if - * we're in MSGIN and all wanted bytes have been received. */ - if ((tmp & SR_REQ) || (p == PHASE_MSGIN && c == 0)) - *phase = tmp & PHASE_MASK; - else - *phase = PHASE_UNKNOWN; - - if (!c || (*phase == p)) - return 0; - else - return -1; -} - -/* - * Function : do_abort (Scsi_Host *host) - * - * Purpose : abort the currently established nexus. Should only be - * called from a routine which can drop into a - * - * Returns : 0 on success, -1 on failure. - */ - -static int do_abort (struct Scsi_Host *host) -{ - unsigned char tmp, *msgptr, phase; - int len; - - /* Request message out phase */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); - - /* - * Wait for the target to indicate a valid phase by asserting - * REQ. Once this happens, we'll have either a MSGOUT phase - * and can immediately send the ABORT message, or we'll have some - * other phase and will have to source/sink data. - * - * We really don't care what value was on the bus or what value - * the target sees, so we just handshake. - */ - - while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ); - - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp)); - - if ((tmp & PHASE_MASK) != PHASE_MSGOUT) { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | - ICR_ASSERT_ACK); - while (NCR5380_read(STATUS_REG) & SR_REQ); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); - } - - tmp = ABORT; - msgptr = &tmp; - len = 1; - phase = PHASE_MSGOUT; - NCR5380_transfer_pio (host, &phase, &len, &msgptr); - - /* - * If we got here, and the command completed successfully, - * we're about to go into bus free state. - */ - - return len ? -1 : 0; -} - -#if defined(REAL_DMA) || defined(PSEUDO_DMA) -/* - * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance, - * unsigned char *phase, int *count, unsigned char **data) - * - * Purpose : transfers data in given phase using either real - * or pseudo DMA. - * - * Inputs : instance - instance of driver, *phase - pointer to - * what phase is expected, *count - pointer to number of - * bytes to transfer, **data - pointer to data pointer. - * - * Returns : -1 when different phase is entered without transferring - * maximum number of bytes, 0 if all bytes or transfered or exit - * is in same phase. - * - * Also, *phase, *count, *data are modified in place. - * - */ - - -static int NCR5380_transfer_dma( struct Scsi_Host *instance, - unsigned char *phase, int *count, - unsigned char **data) -{ - SETUP_HOSTDATA(instance); - register int c = *count; - register unsigned char p = *phase; - register unsigned char *d = *data; - register int foo; - unsigned char tmp; - unsigned long flags; - - - if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) { - *phase = tmp; - return -1; - } - - if (mac_read_overruns && (p & SR_IO)) { - c -= mac_read_overruns; - } - - DMA_PRINTK("scsi%d: initializing DMA for %s, %d bytes %s %p\n", - HOSTNO, (p & SR_IO) ? "reading" : "writing", - c, (p & SR_IO) ? "to" : "from", d); - - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p)); - -#ifdef REAL_DMA - NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY); -#else /* PSEUDO_DMA! */ -#if defined(PSEUDO_DMA) && !defined(UNSAFE) - save_flags(flags); - cli(); -#endif - /* KLL May need eop and parity in 53c400 */ - if (hostdata->flags & FLAG_NCR53C400) - NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK - | MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE - | MR_MONITOR_BSY); - else -#ifndef EMULATE_PSEUDO_DMA - NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE); -#else - NCR5380_write(MODE_REG, MR_BASE); -#endif -#endif /* def REAL_DMA */ - -#ifdef REAL_DMA - /* On the Medusa, it is a must to initialize the DMA before - * starting the NCR. This is also the cleaner way for the TT. - */ - save_flags(flags); - cli(); - hostdata->dma_len = (p & SR_IO) ? - NCR5380_dma_read_setup(instance, d, c) : - NCR5380_dma_write_setup(instance, d, c); - restore_flags(flags); -#endif /* def REAL_DMA */ - -#ifndef EMULATE_PSEUDO_DMA - if (p & SR_IO) - NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0); - else { - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA); - NCR5380_write(START_DMA_SEND_REG, 0); - } -#else - hostdata->dma_len = c; -#endif - -#if defined(REAL_DMA) - return 0; -#else /* defined(PSEUDO_DMA) */ - if (p & SR_IO) { -#ifdef DMA_WORKS_RIGHT - foo = NCR5380_pread(instance, d, c); -#else - int diff = 1; - if (hostdata->flags & FLAG_NCR53C400) { - diff=0; - } - - if (!(foo = NCR5380_pread(instance, d, c - diff))) { - /* - * We can't disable DMA mode after successfully transferring - * what we plan to be the last byte, since that would open up - * a race condition where if the target asserted REQ before - * we got the DMA mode reset, the NCR5380 would have latched - * an additional byte into the INPUT DATA register and we'd - * have dropped it. - * - * The workaround was to transfer one fewer bytes than we - * intended to with the pseudo-DMA read function, wait for - * the chip to latch the last byte, read it, and then disable - * pseudo-DMA mode. - * - * After REQ is asserted, the NCR5380 asserts DRQ and ACK. - * REQ is deasserted when ACK is asserted, and not reasserted - * until ACK goes false. Since the NCR5380 won't lower ACK - * until DACK is asserted, which won't happen unless we twiddle - * the DMA port or we take the NCR5380 out of DMA mode, we - * can guarantee that we won't handshake another extra - * byte. - */ - - if (!(hostdata->flags & FLAG_NCR53C400)) { - while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)); - /* Wait for clean handshake */ - while (NCR5380_read(STATUS_REG) & SR_REQ); - d[c - 1] = NCR5380_read(INPUT_DATA_REG); - } - } -#endif - } else { -#ifdef DMA_WORKS_RIGHT - foo = NCR5380_pwrite(instance, d, c); -#else - int timeout; -#if (NDEBUG & NDEBUG_C400_PWRITE) - printk("About to pwrite %d bytes\n", c); -#endif - if (!(foo = NCR5380_pwrite(instance, d, c))) { - /* - * Wait for the last byte to be sent. If REQ is being asserted for - * the byte we're interested, we'll ACK it and it will go false. - */ - if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) { - timeout = 20000; -#if 1 -#if 1 - while (!(NCR5380_read(BUS_AND_STATUS_REG) & - BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) & - BASR_PHASE_MATCH)); -#else - if (NCR5380_read(STATUS_REG) & SR_REQ) { - for (; timeout && - !(NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK); - --timeout); - for (; timeout && (NCR5380_read(STATUS_REG) & SR_REQ); - --timeout); - } -#endif - - -#if (NDEBUG & NDEBUG_LAST_BYTE_SENT) - if (!timeout) - printk("scsi%d : timed out on last byte\n", - instance->host_no); -#endif - - - if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) { - hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT; - if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) { - hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT; -#if (NDEBUG & NDEBUG_LAST_BYTE_SENT) - printk("scsi%d : last bit sent works\n", - instance->host_no); -#endif - } - } - } else { -#if (NDEBUG & NDEBUG_C400_PWRITE) - printk("Waiting for LASTBYTE\n"); -#endif - while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT)); -#if (NDEBUG & NDEBUG_C400_PWRITE) - printk("Got LASTBYTE\n"); -#endif - } -#else - udelay (5); -#endif - } -#endif - } - - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - if ((!(p & SR_IO)) && (hostdata->flags & FLAG_NCR53C400)) { -#if (NDEBUG & NDEBUG_C400_PWRITE) - printk("53C400w: Checking for IRQ\n"); -#endif - if (NCR5380_read(BUS_AND_STATUS_REG) & BASR_IRQ) { -#if (NDEBUG & NDEBUG_C400_PWRITE) - printk("53C400w: got it, reading reset interrupt reg\n"); -#endif - NCR5380_read(RESET_PARITY_INTERRUPT_REG); - } else { - printk("53C400w: IRQ NOT THERE!\n"); - } - } - - *data = d + c; - *count = 0; - *phase = NCR5380_read(STATUS_REG) & PHASE_MASK; -#if 0 - NCR5380_print_phase(instance); -#endif -#if defined(PSEUDO_DMA) && !defined(UNSAFE) - restore_flags(flags); -#endif /* defined(REAL_DMA_POLL) */ - return foo; -#endif /* def REAL_DMA */ -} -#endif /* defined(REAL_DMA) || defined(PSEUDO_DMA) */ - - -/* - * Function : NCR5380_information_transfer (struct Scsi_Host *instance) - * - * Purpose : run through the various SCSI phases and do as the target - * directs us to. Operates on the currently connected command, - * instance->connected. - * - * Inputs : instance, instance for which we are doing commands - * - * Side effects : SCSI things happen, the disconnected queue will be - * modified if a command disconnects, *instance->connected will - * change. - * - * XXX Note : we need to watch for bus free or a reset condition here - * to recover from an unexpected bus free condition. - */ - -static void NCR5380_information_transfer (struct Scsi_Host *instance) -{ - SETUP_HOSTDATA(instance); - unsigned long flags; - unsigned char msgout = NOP; - int sink = 0; - int len; -#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) - int transfersize; -#endif - unsigned char *data; - unsigned char phase, tmp, extended_msg[10], old_phase=0xff; - Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected; - - while (1) { - tmp = NCR5380_read(STATUS_REG); - /* We only have a valid SCSI phase when REQ is asserted */ - if (tmp & SR_REQ) { - phase = (tmp & PHASE_MASK); - if (phase != old_phase) { - old_phase = phase; - NCR_PRINT_PHASE(NDEBUG_INFORMATION); - } - - if (sink && (phase != PHASE_MSGOUT)) { - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp)); - - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN | - ICR_ASSERT_ACK); - while (NCR5380_read(STATUS_REG) & SR_REQ); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_ATN); - sink = 0; - continue; - } - - switch (phase) { - case PHASE_DATAOUT: -#if (NDEBUG & NDEBUG_NO_DATAOUT) - printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT " - "aborted\n", HOSTNO); - sink = 1; - do_abort(instance); - cmd->result = DID_ERROR << 16; - cmd->done(cmd); - return; -#endif - case PHASE_DATAIN: - /* - * If there is no room left in the current buffer in the - * scatter-gather list, move onto the next one. - */ - - if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) { - ++cmd->SCp.buffer; - --cmd->SCp.buffers_residual; - cmd->SCp.this_residual = cmd->SCp.buffer->length; - cmd->SCp.ptr = cmd->SCp.buffer->address; - /* ++roman: Try to merge some scatter-buffers if - * they are at contiguous physical addresses. - */ - merge_contiguous_buffers( cmd ); - INF_PRINTK("scsi%d: %d bytes and %d buffers left\n", - HOSTNO, cmd->SCp.this_residual, - cmd->SCp.buffers_residual); - } - - /* - * The preferred transfer method is going to be - * PSEUDO-DMA for systems that are strictly PIO, - * since we can let the hardware do the handshaking. - * - * For this to work, we need to know the transfersize - * ahead of time, since the pseudo-DMA code will sit - * in an unconditional loop. - */ - -/* ++roman: I suggest, this should be - * #if def(REAL_DMA) - * instead of leaving REAL_DMA out. - */ - -#if defined(REAL_DMA) || defined(PSEUDO_DMA) - if (!cmd->device->borken && - !(hostdata->flags & FLAG_NO_PSEUDO_DMA) && - (transfersize = NCR5380_dma_xfer_len(instance,cmd,phase)) > 31) { - - len = transfersize; - cmd->SCp.phase = phase; - if (NCR5380_transfer_dma(instance, &phase, - &len, (unsigned char **) &cmd->SCp.ptr)) { - /* - * If the watchdog timer fires, all future - * accesses to this device will use the - * polled-IO. */ - printk(KERN_NOTICE "scsi%d: switching target %d " - "lun %d to slow handshake\n", HOSTNO, - cmd->target, cmd->lun); - cmd->device->borken = 1; - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_ATN); - sink = 1; - do_abort(instance); - cmd->result = DID_ERROR << 16; - cmd->done(cmd); - /* XXX - need to source or sink data here, as appropriate */ - } else { -#ifdef REAL_DMA - /* ++roman: When using real DMA, - * information_transfer() should return after - * starting DMA since it has nothing more to - * do. - */ - return; -#else - /* Michael: When using pseudo-DMA emulation, we must - * take care to take into account the residual from - * the current transfer as determined by either the - * interrupt routine ot the pseudo-transfer functions - * (whichever notices it first). - */ - if (mac_pdma_residual) - len -= mac_pdma_residual; - cmd->SCp.this_residual -= transfersize - len; -#endif - } - } else -#endif /* defined(REAL_DMA) || defined(PSEUDO_DMA) */ - NCR5380_transfer_pio(instance, &phase, - (int *) &cmd->SCp.this_residual, (unsigned char **) - &cmd->SCp.ptr); - break; - case PHASE_MSGIN: - len = 1; - data = &tmp; - NCR5380_write(SELECT_ENABLE_REG, 0); /* disable reselects */ - NCR5380_transfer_pio(instance, &phase, &len, &data); - cmd->SCp.Message = tmp; - - switch (tmp) { - /* - * Linking lets us reduce the time required to get the - * next command out to the device, hopefully this will - * mean we don't waste another revolution due to the delays - * required by ARBITRATION and another SELECTION. - * - * In the current implementation proposal, low level drivers - * merely have to start the next command, pointed to by - * next_link, done() is called as with unlinked commands. - */ -#ifdef LINKED - case LINKED_CMD_COMPLETE: - case LINKED_FLG_CMD_COMPLETE: - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - LNK_PRINTK("scsi%d: target %d lun %d linked command " - "complete.\n", HOSTNO, cmd->target, cmd->lun); - - /* Enable reselect interrupts */ - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - /* - * Sanity check : A linked command should only terminate - * with one of these messages if there are more linked - * commands available. - */ - - if (!cmd->next_link) { - printk(KERN_NOTICE "scsi%d: target %d lun %d " - "linked command complete, no next_link\n", - HOSTNO, cmd->target, cmd->lun); - sink = 1; - do_abort (instance); - return; - } - - initialize_SCp(cmd->next_link); - /* The next command is still part of this process; copy it - * and don't free it! */ - cmd->next_link->tag = cmd->tag; - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - LNK_PRINTK("scsi%d: target %d lun %d linked request " - "done, calling scsi_done().\n", - HOSTNO, cmd->target, cmd->lun); -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif - cmd->scsi_done(cmd); - cmd = hostdata->connected; - break; -#endif /* def LINKED */ - case ABORT: - case COMMAND_COMPLETE: - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - hostdata->connected = NULL; - QU_PRINTK("scsi%d: command for target %d, lun %d " - "completed\n", HOSTNO, cmd->target, cmd->lun); -#ifdef SUPPORT_TAGS - cmd_free_tag( cmd ); - if (status_byte(cmd->SCp.Status) == QUEUE_FULL) { - /* Turn a QUEUE FULL status into BUSY, I think the - * mid level cannot handle QUEUE FULL :-( (The - * command is retried after BUSY). Also update our - * queue size to the number of currently issued - * commands now. - */ - /* ++Andreas: the mid level code knows about - QUEUE_FULL now. */ - TAG_ALLOC *ta = &TagAlloc[cmd->target][cmd->lun]; - TAG_PRINTK("scsi%d: target %d lun %d returned " - "QUEUE_FULL after %d commands\n", - HOSTNO, cmd->target, cmd->lun, - ta->nr_allocated); - if (ta->queue_size > ta->nr_allocated) - ta->nr_allocated = ta->queue_size; - } -#else - hostdata->busy[cmd->target] &= ~(1 << cmd->lun); -#endif - /* Enable reselect interrupts */ - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - - /* - * I'm not sure what the correct thing to do here is : - * - * If the command that just executed is NOT a request - * sense, the obvious thing to do is to set the result - * code to the values of the stored parameters. - * - * If it was a REQUEST SENSE command, we need some way to - * differentiate between the failure code of the original - * and the failure code of the REQUEST sense - the obvious - * case is success, where we fall through and leave the - * result code unchanged. - * - * The non-obvious place is where the REQUEST SENSE failed - */ - - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - else if (status_byte(cmd->SCp.Status) != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); - -#ifdef AUTOSENSE - if ((cmd->cmnd[0] != REQUEST_SENSE) && - (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) { - ASEN_PRINTK("scsi%d: performing request sense\n", - HOSTNO); - cmd->cmnd[0] = REQUEST_SENSE; - cmd->cmnd[1] &= 0xe0; - cmd->cmnd[2] = 0; - cmd->cmnd[3] = 0; - cmd->cmnd[4] = sizeof(cmd->sense_buffer); - cmd->cmnd[5] = 0; - cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); - - cmd->use_sg = 0; - /* this is initialized from initialize_SCp - cmd->SCp.buffer = NULL; - cmd->SCp.buffers_residual = 0; - */ - cmd->request_buffer = (char *) cmd->sense_buffer; - cmd->request_bufflen = sizeof(cmd->sense_buffer); - - save_flags(flags); - cli(); - LIST(cmd,hostdata->issue_queue); - NEXT(cmd) = hostdata->issue_queue; - hostdata->issue_queue = (Scsi_Cmnd *) cmd; - restore_flags(flags); - QU_PRINTK("scsi%d: REQUEST SENSE added to head of " - "issue queue\n", H_NO(cmd)); - } else -#endif /* def AUTOSENSE */ - { -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif - cmd->scsi_done(cmd); - } - - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - /* - * Restore phase bits to 0 so an interrupted selection, - * arbitration can resume. - */ - NCR5380_write(TARGET_COMMAND_REG, 0); - - while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) - barrier(); - - return; - case MESSAGE_REJECT: - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - /* Enable reselect interrupts */ - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - switch (hostdata->last_message) { - case HEAD_OF_QUEUE_TAG: - case ORDERED_QUEUE_TAG: - case SIMPLE_QUEUE_TAG: - /* The target obviously doesn't support tagged - * queuing, even though it announced this ability in - * its INQUIRY data ?!? (maybe only this LUN?) Ok, - * clear 'tagged_supported' and lock the LUN, since - * the command is treated as untagged further on. - */ - cmd->device->tagged_supported = 0; - hostdata->busy[cmd->target] |= (1 << cmd->lun); - cmd->tag = TAG_NONE; - TAG_PRINTK("scsi%d: target %d lun %d rejected " - "QUEUE_TAG message; tagged queuing " - "disabled\n", - HOSTNO, cmd->target, cmd->lun); - break; - } - break; - case DISCONNECT: - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - save_flags(flags); - cli(); - cmd->device->disconnect = 1; - LIST(cmd,hostdata->disconnected_queue); - NEXT(cmd) = hostdata->disconnected_queue; - hostdata->connected = NULL; - hostdata->disconnected_queue = cmd; - restore_flags(flags); - QU_PRINTK("scsi%d: command for target %d lun %d was " - "moved from connected to the " - "disconnected_queue\n", HOSTNO, - cmd->target, cmd->lun); - /* - * Restore phase bits to 0 so an interrupted selection, - * arbitration can resume. - */ - NCR5380_write(TARGET_COMMAND_REG, 0); - - /* Enable reselect interrupts */ - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - /* Wait for bus free to avoid nasty timeouts */ - while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected) - barrier(); - return; - /* - * The SCSI data pointer is *IMPLICITLY* saved on a disconnect - * operation, in violation of the SCSI spec so we can safely - * ignore SAVE/RESTORE pointers calls. - * - * Unfortunately, some disks violate the SCSI spec and - * don't issue the required SAVE_POINTERS message before - * disconnecting, and we have to break spec to remain - * compatible. - */ - case SAVE_POINTERS: - case RESTORE_POINTERS: - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - /* Enable reselect interrupts */ - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - break; - case EXTENDED_MESSAGE: -/* - * Extended messages are sent in the following format : - * Byte - * 0 EXTENDED_MESSAGE == 1 - * 1 length (includes one byte for code, doesn't - * include first two bytes) - * 2 code - * 3..length+1 arguments - * - * Start the extended message buffer with the EXTENDED_MESSAGE - * byte, since print_msg() wants the whole thing. - */ - extended_msg[0] = EXTENDED_MESSAGE; - /* Accept first byte by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - EXT_PRINTK("scsi%d: receiving extended message\n", HOSTNO); - - len = 2; - data = extended_msg + 1; - phase = PHASE_MSGIN; - NCR5380_transfer_pio(instance, &phase, &len, &data); - EXT_PRINTK("scsi%d: length=%d, code=0x%02x\n", HOSTNO, - (int)extended_msg[1], (int)extended_msg[2]); - - if (!len && extended_msg[1] <= - (sizeof (extended_msg) - 1)) { - /* Accept third byte by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - len = extended_msg[1] - 1; - data = extended_msg + 3; - phase = PHASE_MSGIN; - - NCR5380_transfer_pio(instance, &phase, &len, &data); - EXT_PRINTK("scsi%d: message received, residual %d\n", - HOSTNO, len); - - switch (extended_msg[2]) { - case EXTENDED_SDTR: - case EXTENDED_WDTR: - case EXTENDED_MODIFY_DATA_POINTER: - case EXTENDED_EXTENDED_IDENTIFY: - tmp = 0; - } - } else if (len) { - printk(KERN_NOTICE "scsi%d: error receiving " - "extended message\n", HOSTNO); - tmp = 0; - } else { - printk(KERN_NOTICE "scsi%d: extended message " - "code %02x length %d is too long\n", - HOSTNO, extended_msg[2], extended_msg[1]); - tmp = 0; - } - /* Fall through to reject message */ - - /* - * If we get something weird that we aren't expecting, - * reject it. - */ - default: - if (!tmp) { - printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO); - print_msg (extended_msg); - printk("\n"); - } else if (tmp != EXTENDED_MESSAGE) - printk(KERN_DEBUG "scsi%d: rejecting unknown " - "message %02x from target %d, lun %d\n", - HOSTNO, tmp, cmd->target, cmd->lun); - else - printk(KERN_DEBUG "scsi%d: rejecting unknown " - "extended message " - "code %02x, length %d from target %d, lun %d\n", - HOSTNO, extended_msg[1], extended_msg[0], - cmd->target, cmd->lun); - - - msgout = MESSAGE_REJECT; - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_ATN); - break; - } /* switch (tmp) */ - break; - case PHASE_MSGOUT: - len = 1; - data = &msgout; - hostdata->last_message = msgout; - NCR5380_transfer_pio(instance, &phase, &len, &data); - if (msgout == ABORT) { -#ifdef SUPPORT_TAGS - cmd_free_tag( cmd ); -#else - hostdata->busy[cmd->target] &= ~(1 << cmd->lun); -#endif - hostdata->connected = NULL; - cmd->result = DID_ERROR << 16; -#ifdef NCR5380_STATS - collect_stats(hostdata, cmd); -#endif - cmd->scsi_done(cmd); - NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); - return; - } - msgout = NOP; - break; - case PHASE_CMDOUT: - len = cmd->cmd_len; - data = cmd->cmnd; - /* - * XXX for performance reasons, on machines with a - * PSEUDO-DMA architecture we should probably - * use the dma transfer function. - */ - NCR5380_transfer_pio(instance, &phase, &len, - &data); - break; - case PHASE_STATIN: - len = 1; - data = &tmp; - NCR5380_transfer_pio(instance, &phase, &len, &data); - cmd->SCp.Status = tmp; - break; - default: - printk("scsi%d: unknown phase\n", HOSTNO); - NCR_PRINT(NDEBUG_ANY); - } /* switch(phase) */ - } /* if (tmp * SR_REQ) */ - } /* while (1) */ -} - -/* - * Function : void NCR5380_reselect (struct Scsi_Host *instance) - * - * Purpose : does reselection, initializing the instance->connected - * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q - * nexus has been reestablished, - * - * Inputs : instance - this instance of the NCR5380. - * - */ - - -static void NCR5380_reselect (struct Scsi_Host *instance) -{ - SETUP_HOSTDATA(instance); - unsigned char target_mask; - unsigned char lun, phase; - int len; -#ifdef SUPPORT_TAGS - unsigned char tag; -#endif - unsigned char msg[3]; - unsigned char *data; - Scsi_Cmnd *tmp = NULL, *prev; -/* unsigned long flags; */ - - /* - * Disable arbitration, etc. since the host adapter obviously - * lost, and tell an interrupted NCR5380_select() to restart. - */ - - NCR5380_write(MODE_REG, MR_BASE); - hostdata->restart_select = 1; - - target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask); - - RSL_PRINTK("scsi%d: reselect\n", HOSTNO); - - /* - * At this point, we have detected that our SCSI ID is on the bus, - * SEL is true and BSY was false for at least one bus settle delay - * (400 ns). - * - * We must assert BSY ourselves, until the target drops the SEL - * signal. - */ - - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY); - - while (NCR5380_read(STATUS_REG) & SR_SEL); - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - /* - * Wait for target to go into MSGIN. - */ - - while (!(NCR5380_read(STATUS_REG) & SR_REQ)); - - len = 1; - data = msg; - phase = PHASE_MSGIN; - NCR5380_transfer_pio(instance, &phase, &len, &data); - - if (!msg[0] & 0x80) { - printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO); - print_msg(msg); - do_abort(instance); - return; - } - lun = (msg[0] & 0x07); - -#ifdef SUPPORT_TAGS - /* If the phase is still MSGIN, the target wants to send some more - * messages. In case it supports tagged queuing, this is probably a - * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus. - */ - tag = TAG_NONE; - if (phase == PHASE_MSGIN && setup_use_tagged_queuing) { - /* Accept previous IDENTIFY message by clearing ACK */ - NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE ); - len = 2; - data = msg+1; - if (!NCR5380_transfer_pio(instance, &phase, &len, &data) && - msg[1] == SIMPLE_QUEUE_TAG) - tag = msg[2]; - TAG_PRINTK("scsi%d: target mask %02x, lun %d sent tag %d at " - "reselection\n", HOSTNO, target_mask, lun, tag); - } -#endif - - /* - * Find the command corresponding to the I_T_L or I_T_L_Q nexus we - * just reestablished, and remove it from the disconnected queue. - */ - - for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; - tmp; prev = tmp, tmp = NEXT(tmp) ) { - if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun) -#ifdef SUPPORT_TAGS - && (tag == tmp->tag) -#endif - ) { - if (prev) { - REMOVE(prev, NEXT(prev), tmp, NEXT(tmp)); - NEXT(prev) = NEXT(tmp); - } else { - REMOVE(-1, hostdata->disconnected_queue, tmp, NEXT(tmp)); - hostdata->disconnected_queue = NEXT(tmp); - } - NEXT(tmp) = NULL; - break; - } - } - - if (!tmp) { - printk(KERN_WARNING "scsi%d: warning: target bitmask %02x lun %d " -#ifdef SUPPORT_TAGS - "tag %d " -#endif - "not in disconnect_queue.\n", - HOSTNO, target_mask, lun -#ifdef SUPPORT_TAGS - , tag -#endif - ); - /* - * Since we have an established nexus that we can't do anything - * with, we must abort it. - */ - do_abort(instance); - return; - } - - /* Accept message by clearing ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - hostdata->connected = tmp; - RSL_PRINTK("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n", - HOSTNO, tmp->target, tmp->lun, tmp->tag); -} - - -/* - * Function : int NCR5380_abort (Scsi_Cmnd *cmd) - * - * Purpose : abort a command - * - * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the - * host byte of the result field to, if zero DID_ABORTED is - * used. - * - * Returns : 0 - success, -1 on failure. - * - * XXX - there is no way to abort the command that is currently - * connected, you have to wait for it to complete. If this is - * a problem, we could implement longjmp() / setjmp(), setjmp() - * called where the loop started in NCR5380_main(). - */ - -#ifndef NCR5380_abort -static -#endif -int NCR5380_abort (Scsi_Cmnd *cmd) -{ - struct Scsi_Host *instance = cmd->host; - SETUP_HOSTDATA(instance); - Scsi_Cmnd *tmp, **prev; - unsigned long flags; - - printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO); - print_Scsi_Cmnd (cmd); - - NCR5380_print_status (instance); - - save_flags(flags); - cli(); - - ABRT_PRINTK("scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO, - NCR5380_read(BUS_AND_STATUS_REG), - NCR5380_read(STATUS_REG)); - -#if 1 -/* - * Case 1 : If the command is the currently executing command, - * we'll set the aborted flag and return control so that - * information transfer routine can exit cleanly. - */ - - if (hostdata->connected == cmd) { - - ABRT_PRINTK("scsi%d: aborting connected command\n", HOSTNO); -/* - * We should perform BSY checking, and make sure we haven't slipped - * into BUS FREE. - */ - -/* NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */ -/* - * Since we can't change phases until we've completed the current - * handshake, we have to source or sink a byte of data if the current - * phase is not MSGOUT. - */ - -/* - * Return control to the executing NCR drive so we can clear the - * aborted flag and get back into our main loop. - */ - - if (do_abort(instance) == 0) { - hostdata->aborted = 1; - hostdata->connected = NULL; - cmd->result = DID_ABORT << 16; -#ifdef SUPPORT_TAGS - cmd_free_tag( cmd ); -#else - hostdata->busy[cmd->target] &= ~(1 << cmd->lun); -#endif - restore_flags(flags); - cmd->scsi_done(cmd); - return SCSI_ABORT_SUCCESS; - } else { -/* restore_flags(flags); */ - printk("scsi%d: abort of connected command failed!\n", HOSTNO); - return SCSI_ABORT_ERROR; - } - } -#endif - -/* - * Case 2 : If the command hasn't been issued yet, we simply remove it - * from the issue queue. - */ - for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue), - tmp = (Scsi_Cmnd *) hostdata->issue_queue; - tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) ) - if (cmd == tmp) { - REMOVE(5, *prev, tmp, NEXT(tmp)); - (*prev) = NEXT(tmp); - NEXT(tmp) = NULL; - tmp->result = DID_ABORT << 16; - restore_flags(flags); - ABRT_PRINTK("scsi%d: abort removed command from issue queue.\n", - HOSTNO); - /* Tagged queuing note: no tag to free here, hasn't been assigned - * yet... */ - tmp->scsi_done(tmp); - return SCSI_ABORT_SUCCESS; - } - -/* - * Case 3 : If any commands are connected, we're going to fail the abort - * and let the high level SCSI driver retry at a later time or - * issue a reset. - * - * Timeouts, and therefore aborted commands, will be highly unlikely - * and handling them cleanly in this situation would make the common - * case of noresets less efficient, and would pollute our code. So, - * we fail. - */ - - if (hostdata->connected) { - restore_flags(flags); - ABRT_PRINTK("scsi%d: abort failed, command connected.\n", HOSTNO); - return SCSI_ABORT_SNOOZE; - } - -/* - * Case 4: If the command is currently disconnected from the bus, and - * there are no connected commands, we reconnect the I_T_L or - * I_T_L_Q nexus associated with it, go into message out, and send - * an abort message. - * - * This case is especially ugly. In order to reestablish the nexus, we - * need to call NCR5380_select(). The easiest way to implement this - * function was to abort if the bus was busy, and let the interrupt - * handler triggered on the SEL for reselect take care of lost arbitrations - * where necessary, meaning interrupts need to be enabled. - * - * When interrupts are enabled, the queues may change - so we - * can't remove it from the disconnected queue before selecting it - * because that could cause a failure in hashing the nexus if that - * device reselected. - * - * Since the queues may change, we can't use the pointers from when we - * first locate it. - * - * So, we must first locate the command, and if NCR5380_select() - * succeeds, then issue the abort, relocate the command and remove - * it from the disconnected queue. - */ - - for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; - tmp = NEXT(tmp)) - if (cmd == tmp) { - restore_flags(flags); - ABRT_PRINTK("scsi%d: aborting disconnected command.\n", HOSTNO); - - if (NCR5380_select (instance, cmd, (int) cmd->tag)) - return SCSI_ABORT_BUSY; - - ABRT_PRINTK("scsi%d: nexus reestablished.\n", HOSTNO); - - do_abort (instance); - - save_flags(flags); - cli(); - for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue), - tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; - tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) ) - if (cmd == tmp) { - REMOVE(5, *prev, tmp, NEXT(tmp)); - *prev = NEXT(tmp); - NEXT(tmp) = NULL; - tmp->result = DID_ABORT << 16; - /* We must unlock the tag/LUN immediately here, since the - * target goes to BUS FREE and doesn't send us another - * message (COMMAND_COMPLETE or the like) - */ -#ifdef SUPPORT_TAGS - cmd_free_tag( tmp ); -#else - hostdata->busy[cmd->target] &= ~(1 << cmd->lun); -#endif - restore_flags(flags); - tmp->scsi_done(tmp); - return SCSI_ABORT_SUCCESS; - } - } - -/* - * Case 5 : If we reached this point, the command was not found in any of - * the queues. - * - * We probably reached this point because of an unlikely race condition - * between the command completing successfully and the abortion code, - * so we won't panic, but we will notify the user in case something really - * broke. - */ - - restore_flags(flags); - printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully\n" - KERN_INFO " before abortion\n", HOSTNO); - -/* Maybe it is sufficient just to release the ST-DMA lock... (if - * possible at all) At least, we should check if the lock could be - * released after the abort, in case it is kept due to some bug. - */ - - return SCSI_ABORT_NOT_RUNNING; -} - - -/* - * Function : int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags) - * - * Purpose : reset the SCSI bus. - * - * Returns : SCSI_RESET_WAKEUP - * - */ - -static int NCR5380_reset( Scsi_Cmnd *cmd, unsigned int reset_flags) -{ - SETUP_HOSTDATA(cmd->host); - int i; - unsigned long flags; -#if 1 - Scsi_Cmnd *connected, *disconnected_queue; -#endif - - NCR5380_print_status (cmd->host); - - /* get in phase */ - NCR5380_write( TARGET_COMMAND_REG, - PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) )); - /* assert RST */ - NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST ); - udelay (40); - /* reset NCR registers */ - NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE ); - NCR5380_write( MODE_REG, MR_BASE ); - NCR5380_write( TARGET_COMMAND_REG, 0 ); - NCR5380_write( SELECT_ENABLE_REG, 0 ); - /* ++roman: reset interrupt condition! otherwise no interrupts don't get - * through anymore ... */ - (void)NCR5380_read( RESET_PARITY_INTERRUPT_REG ); - -#if 1 /* XXX Should now be done by midlevel code, but it's broken XXX */ - /* XXX see below XXX */ - - /* MSch: old-style reset: actually abort all command processing here */ - - /* After the reset, there are no more connected or disconnected commands - * and no busy units; to avoid problems with re-inserting the commands - * into the issue_queue (via scsi_done()), the aborted commands are - * remembered in local variables first. - */ - save_flags(flags); - cli(); - connected = (Scsi_Cmnd *)hostdata->connected; - hostdata->connected = NULL; - disconnected_queue = (Scsi_Cmnd *)hostdata->disconnected_queue; - hostdata->disconnected_queue = NULL; -#ifdef SUPPORT_TAGS - free_all_tags(); -#endif - for( i = 0; i < 8; ++i ) - hostdata->busy[i] = 0; -#ifdef REAL_DMA - hostdata->dma_len = 0; -#endif - restore_flags(flags); - - /* In order to tell the mid-level code which commands were aborted, - * set the command status to DID_RESET and call scsi_done() !!! - * This ultimately aborts processing of these commands in the mid-level. - */ - - if ((cmd = connected)) { - ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd)); - cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16); - cmd->scsi_done( cmd ); - } - - for (i = 0; (cmd = disconnected_queue); ++i) { - disconnected_queue = NEXT(cmd); - NEXT(cmd) = NULL; - cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16); - cmd->scsi_done( cmd ); - } - if (i > 0) - ABRT_PRINTK("scsi: reset aborted %d disconnected command(s)\n", i); - - /* since all commands have been explicitly terminated, we need to tell - * the midlevel code that the reset was SUCCESSFUL, and there is no - * need to 'wake up' the commands by a request_sense - */ - return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET; -#else /* 1 */ - - /* MSch: new-style reset handling: let the mid-level do what it can */ - - /* ++guenther: MID-LEVEL IS STILL BROKEN. - * Mid-level is supposed to requeue all commands that were active on the - * various low-level queues. In fact it does this, but that's not enough - * because all these commands are subject to timeout. And if a timeout - * happens for any removed command, *_abort() is called but all queues - * are now empty. Abort then gives up the falcon lock, which is fatal, - * since the mid-level will queue more commands and must have the lock - * (it's all happening inside timer interrupt handler!!). - * Even worse, abort will return NOT_RUNNING for all those commands not - * on any queue, so they won't be retried ... - * - * Conclusion: either scsi.c disables timeout for all resetted commands - * immediately, or we loose! As of linux-2.0.20 it doesn't. - */ - - /* After the reset, there are no more connected or disconnected commands - * and no busy units; so clear the low-level status here to avoid - * conflicts when the mid-level code tries to wake up the affected - * commands! - */ - - if (hostdata->issue_queue) - ABRT_PRINTK("scsi%d: reset aborted issued command(s)\n", H_NO(cmd)); - if (hostdata->connected) - ABRT_PRINTK("scsi%d: reset aborted a connected command\n", H_NO(cmd)); - if (hostdata->disconnected_queue) - ABRT_PRINTK("scsi%d: reset aborted disconnected command(s)\n", H_NO(cmd)); - - save_flags(flags); - cli(); - hostdata->issue_queue = NULL; - hostdata->connected = NULL; - hostdata->disconnected_queue = NULL; -#ifdef SUPPORT_TAGS - free_all_tags(); -#endif - for( i = 0; i < 8; ++i ) - hostdata->busy[i] = 0; -#ifdef REAL_DMA - hostdata->dma_len = 0; -#endif - restore_flags(flags); - - /* we did no complete reset of all commands, so a wakeup is required */ - return SCSI_RESET_WAKEUP | SCSI_RESET_BUS_RESET; -#endif /* 1 */ -} - -/* Local Variables: */ -/* tab-width: 8 */ -/* End: */ diff --git a/drivers/scsi/mac_esp.c b/drivers/scsi/mac_esp.c index 9e6fa65f4826..c18678aa251d 100644 --- a/drivers/scsi/mac_esp.c +++ b/drivers/scsi/mac_esp.c @@ -28,14 +28,13 @@ #include "NCR53C9x.h" #include "mac_esp.h" -#include "../../arch/m68k/mac/via6522.h" /* huh? */ - #include #include #include #include #include +#include #include @@ -59,10 +58,15 @@ static int dma_ports_p(struct NCR_ESP *esp); static void dma_setup(struct NCR_ESP * esp, __u32 addr, int count, int write); static void dma_setup_quick(struct NCR_ESP * esp, __u32 addr, int count, int write); - static int esp_dafb_dma_irq_p(struct NCR_ESP * espdev); static int esp_iosb_dma_irq_p(struct NCR_ESP * espdev); +volatile unsigned char cmd_buffer[16]; + /* This is where all commands are put + * before they are transfered to the ESP chip + * via PIO. + */ + static int esp_initialized = 0; static int setup_num_esps = -1; @@ -287,6 +291,9 @@ int mac_esp_detect(Scsi_Host_Template * tpnt) unsigned long timeout; #endif + if (esp_initialized > 0) + return -ENODEV; + /* what do we have in this machine... */ if (MACHW_PRESENT(MAC_SCSI_96)) { chipspresent ++; @@ -358,10 +365,13 @@ int mac_esp_detect(Scsi_Host_Template * tpnt) } /* chipnum == 0 */ - /* use pio for command bytes; pio for message/data: TBI */ esp->do_pio_cmds = 1; + /* Set the command buffer */ + esp->esp_command = (volatile unsigned char*) cmd_buffer; + esp->esp_command_dvma = (volatile unsigned char*) cmd_buffer; + /* various functions */ esp->dma_bytes_sent = &dma_bytes_sent; esp->dma_can_transfer = &dma_can_transfer; @@ -400,8 +410,7 @@ int mac_esp_detect(Scsi_Host_Template * tpnt) esp->irq = IRQ_MAC_SCSI; - request_irq(IRQ_MAC_SCSI, esp_intr, 0, "Mac ESP SCSI", esp); - request_irq(IRQ_MAC_SCSIDRQ, fake_drq, 0, "Mac ESP DRQ", esp); + request_irq(esp->irq, esp_intr, 0, "Mac ESP SCSI", esp); if (macintosh_config->scsi_type == MAC_SCSI_QUADRA) { esp->cfreq = 16500000; @@ -411,10 +420,8 @@ int mac_esp_detect(Scsi_Host_Template * tpnt) } else { /* chipnum == 1 */ - - esp->irq = IRQ_MAC_SCSIDRQ; - - request_irq(IRQ_MAC_SCSIDRQ, esp_intr, 0, "Mac ESP SCSI 2", esp); + esp->irq = IRQ_MAC_SCSI; + request_irq(esp->irq, esp_intr, 0, "Mac ESP SCSI 2", esp); esp->cfreq = 25000000; @@ -469,7 +476,7 @@ int mac_esp_detect(Scsi_Host_Template * tpnt) static int esp_dafb_dma_irq_p(struct NCR_ESP * esp) { unsigned int ret; - int sreg = esp->eregs->esp_status; + int sreg = esp_read(esp->eregs->esp_status); #ifdef DEBUG_MAC_ESP printk("mac_esp: esp_dafb_dma_irq_p dafb %d irq %d\n", @@ -510,7 +517,7 @@ static int esp_dafb_dma_irq_p(struct NCR_ESP * esp) static int esp_iosb_dma_irq_p(struct NCR_ESP * esp) { int ret = mac_irq_pending(IRQ_MAC_SCSI) || mac_irq_pending(IRQ_MAC_SCSIDRQ); - int sreg = esp->eregs->esp_status; + int sreg = esp_read(esp->eregs->esp_status); #ifdef DEBUG_MAC_ESP printk("mac_esp: dma_irq_p drq %d irq %d sreg %x curr %p disc %p\n", @@ -599,13 +606,13 @@ static void dma_init_write(struct NCR_ESP * esp, char * vaddress, int length) static void dma_ints_off(struct NCR_ESP * esp) { - mac_turnoff_irq(esp->irq); + mac_disable_irq(esp->irq); } static void dma_ints_on(struct NCR_ESP * esp) { - mac_turnon_irq(esp->irq); + mac_enable_irq(esp->irq); } /* @@ -614,7 +621,7 @@ static void dma_ints_on(struct NCR_ESP * esp) static int dma_irq_p(struct NCR_ESP * esp) { - int i = esp->eregs->esp_status; + int i = esp_read(esp->eregs->esp_status); #ifdef DEBUG_MAC_ESP printk("mac_esp: dma_irq_p status %d\n", i); @@ -629,7 +636,7 @@ static int dma_irq_p_quick(struct NCR_ESP * esp) * Copied from iosb_dma_irq_p() */ int ret = mac_irq_pending(IRQ_MAC_SCSI) || mac_irq_pending(IRQ_MAC_SCSIDRQ); - int sreg = esp->eregs->esp_status; + int sreg = esp_read(esp->eregs->esp_status); #ifdef DEBUG_MAC_ESP printk("mac_esp: dma_irq_p drq %d irq %d sreg %x curr %p disc %p\n", diff --git a/drivers/scsi/mac_esp.h b/drivers/scsi/mac_esp.h index 9de3186d0f4c..5ab45c081571 100644 --- a/drivers/scsi/mac_esp.h +++ b/drivers/scsi/mac_esp.h @@ -19,9 +19,11 @@ extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); extern int esp_command(Scsi_Cmnd *); extern int esp_abort(Scsi_Cmnd *); extern int esp_reset(Scsi_Cmnd *, unsigned int); - +extern int esp_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout); #define SCSI_MAC_ESP { proc_dir: &proc_scsi_esp, \ + proc_info: esp_proc_info, \ name: "Mac 53C9x SCSI", \ detect: mac_esp_detect, \ release: NULL, \ diff --git a/drivers/scsi/mac_scsi.c b/drivers/scsi/mac_scsi.c index 466fcb91d020..c0e805fcac13 100644 --- a/drivers/scsi/mac_scsi.c +++ b/drivers/scsi/mac_scsi.c @@ -1,553 +1,115 @@ /* - * mac_scsi.c -- Device dependent functions for the Macintosh NCR5380 SCSI - * port (MacII style machines, no DMA). + * Generic Macintosh NCR5380 driver * - * based on: + * Copyright 1998, Michael Schmitz + * + * Pseudo-DMA, Ove Edlund , June 2000 + * + * derived in part from: */ - /* - * atari_scsi.c -- Device dependent functions for the Atari generic SCSI port + * Generic Generic NCR5380 driver * - * Copyright 1994 Roman Hodek + * Copyright 1995, Russell King * - * Loosely based on the work of Robert De Vries' team and added: - * - working real DMA - * - Falcon support (untested yet!) ++bjoern fixed and now it works - * - lots of extensions and bug fixes. + * ALPHA RELEASE 1. * - * 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. + * For more information, please consult * + * NCR 5380 Family + * SCSI Protocol Controller + * Databook + * + * NCR Microelectronics + * 1635 Aeroplaza Drive + * Colorado Springs, CO 80916 + * 1+ (719) 578-3400 + * 1+ (800) 334-5454 */ - -#include -#include - -#define NDEBUG_ABORT 0x800000 -#define NDEBUG_TAGS 0x1000000 -#define NDEBUG_MERGING 0x2000000 - -#define NDEBUG (0) - -#define AUTOSENSE -/* MSch: Tested the pseudo-DMA code on Atari for the Mac68k port ... */ -/* Defining neither PSEUDO_DMA nor REAL_DMA -> PIO transfer, sloooow ! */ -/*#define REAL_DMA*/ /* never supported on NCR5380 Macs */ /* - * Usage: define PSEUDO_DMA to use hardware-handshaked PIO mode (TBI) - * undef PSEUDO_DMA to use pure PIO mode + * $Log: mac_NCR5380.c,v $ */ -/*#define PSEUDO_DMA*/ /* currently gives trouble on some Macs */ - -#ifdef PSEUDO_DMA -#define EMULATE_PSEUDO_DMA -#define DMA_WORKS_RIGHT -#define UNSAFE -#endif - -/* Support tagged queuing? (on devices that are able to... :-) */ -#define SUPPORT_TAGS -#define MAX_TAGS 32 - #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 "scsi.h" #include "hosts.h" #include "mac_scsi.h" #include "NCR5380.h" #include "constants.h" -#include -#include - -struct proc_dir_entry proc_scsi_mac = { - PROC_SCSI_MAC, 8, "mac_5380", - S_IFDIR | S_IRUGO | S_IXUGO, 2 -}; - -/* - * Define RBV_HACK to run the SCSI driver on RBV Macs; undefine to - * try getting better interrupt latency - */ -#define RBV_HACK - -#ifdef RBV_HACK -#define ENABLE_IRQ() mac_turnon_irq( IRQ_MAC_SCSI ); -#define DISABLE_IRQ() mac_turnoff_irq( IRQ_MAC_SCSI ); -#else -#define ENABLE_IRQ() mac_enable_irq( IRQ_MAC_SCSI ); -#define DISABLE_IRQ() mac_disable_irq( IRQ_MAC_SCSI ); -#endif - -#define HOSTDATA_DMALEN (((struct NCR5380_hostdata *) \ - (mac_scsi_host->hostdata))->dma_len) - -/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms, - * we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more - * need ten times the standard value... */ -#ifndef CONFIG_MAC_SCSI_TOSHIBA_DELAY -#define AFTER_RESET_DELAY (HZ/2) +#if 0 +#define NDEBUG (NDEBUG_INTR | NDEBUG_PSEUDO_DMA | NDEBUG_ARBITRATION | NDEBUG_SELECTION | NDEBUG_RESELECTION) #else -#define AFTER_RESET_DELAY (5*HZ/2) +#define NDEBUG (NDEBUG_ABORT) #endif -/***************************** Prototypes *****************************/ - -#ifdef REAL_DMA -static int scsi_dma_is_ignored_buserr( unsigned char dma_stat ); -static void mac_scsi_fetch_restbytes( void ); -static long mac_scsi_dma_residual( struct Scsi_Host *instance ); -static int mac_classify_cmd( Scsi_Cmnd *cmd ); -static unsigned long mac_dma_xfer_len( unsigned long wanted_len, - Scsi_Cmnd *cmd, int write_flag ); -#endif -#ifdef PSEUDO_DMA -static int mac_pdma_read(struct Scsi_Host *instance, unsigned char *dst, - int len); -static int mac_pdma_write(struct Scsi_Host *instance, unsigned char *src, - int len); -static unsigned long mac_dma_xfer_len( unsigned long wanted_len, - Scsi_Cmnd *cmd, int write_flag ); -#endif -static void scsi_mac_intr( int irq, void *dummy, struct pt_regs *fp); -static void mac_scsi_reset_boot( void ); -static unsigned char mac_scsi_reg_read( unsigned char reg ); -static void mac_scsi_reg_write( unsigned char reg, unsigned char value); - -/************************* End of Prototypes **************************/ +#define ENABLE_IRQ() mac_enable_irq( IRQ_MAC_SCSI ); +#define DISABLE_IRQ() mac_disable_irq( IRQ_MAC_SCSI ); +extern void via_scsi_clear(void); -static struct Scsi_Host *mac_scsi_host = NULL; -#if 0 -static unsigned char (*mac_scsi_reg_read)( unsigned char reg ); -static void (*mac_scsi_reg_write)( unsigned char reg, unsigned char value ); -#endif +static void mac_scsi_reset_boot(struct Scsi_Host *instance); +static char macscsi_read(struct Scsi_Host *instance, int reg); +static void macscsi_write(struct Scsi_Host *instance, int reg, int value); -#ifdef REAL_DMA -static unsigned long mac_dma_residual, mac_dma_startaddr; -static short mac_dma_active; -/* pointer to the dribble buffer */ -static char *mac_dma_buffer = NULL; -/* precalculated physical address of the dribble buffer */ -static unsigned long mac_dma_phys_buffer; -/* != 0 tells the int handler to copy data from the dribble buffer */ -static char *mac_dma_orig_addr; -/* size of the dribble buffer; 4k seems enough, since the Falcon cannot use - * scatter-gather anyway, so most transfers are 1024 byte only. In the rare - * cases where requests to physical contiguous buffers have been merged, this - * request is <= 4k (one page). So I don't think we have to split transfers - * just due to this buffer size... - */ -#define MAC_BUFFER_SIZE (4096) -#if 1 /* FIXME: is that an issue for Macs?? */ -/* mask for address bits that can't be used with the ST-DMA */ -static unsigned long mac_dma_stram_mask; -#define STRAM_ADDR(a) (((a) & mac_dma_stram_mask) == 0) -#endif -/* number of bytes to cut from a transfer to handle NCR overruns */ -static int mac_read_overruns = 0; -#endif -#ifdef PSEUDO_DMA -static unsigned long mac_pdma_residual, mac_pdma_startaddr, mac_pdma_current; -static short mac_pdma_active; -/* FIXME: is that an issue for Macs?? */ -/* mask for address bits that can't be used with the ST-DMA */ -static unsigned long mac_dma_stram_mask; -#define STRAM_ADDR(a) (((a) & mac_dma_stram_mask) == 0) -static int mac_read_overruns = 0; -#endif static int setup_can_queue = -1; static int setup_cmd_per_lun = -1; static int setup_sg_tablesize = -1; -#ifdef SUPPORT_TAGS static int setup_use_tagged_queuing = -1; -#endif static int setup_hostid = -1; +/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms, + * we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more + * need ten times the standard value... */ +#define TOSHIBA_DELAY -#if defined(REAL_DMA) - -#if 0 /* FIXME */ -static int scsi_dma_is_ignored_buserr( unsigned char dma_stat ) -{ - int i; - unsigned long addr = SCSI_DMA_READ_P( dma_addr ), end_addr; - - if (dma_stat & 0x01) { - - /* A bus error happens when DMA-ing from the last page of a - * physical memory chunk (DMA prefetch!), but that doesn't hurt. - * Check for this case: - */ - - for( i = 0; i < boot_info.num_memory; ++i ) { - end_addr = boot_info.memory[i].addr + - boot_info.memory[i].size; - if (end_addr <= addr && addr <= end_addr + 4) - return( 1 ); - } - } - return( 0 ); -} - - -/* Dead code... wasn't called anyway :-) and causes some trouble, because at - * end-of-DMA, both SCSI ints are triggered simultaneously, so the NCR int has - * to clear the DMA int pending bit before it allows other level 6 interrupts. - */ -static void scsi_dma_buserr (int irq, void *dummy, struct pt_regs *fp) -{ - unsigned char dma_stat = tt_scsi_dma.dma_ctrl; - - /* Don't do anything if a NCR interrupt is pending. Probably it's just - * masked... */ - if (mac_irq_pending( IRQ_MAC_SCSI )) - return; - - printk("Bad SCSI DMA interrupt! dma_addr=0x%08lx dma_stat=%02x dma_cnt=%08lx\n", - SCSI_DMA_READ_P(dma_addr), dma_stat, SCSI_DMA_READ_P(dma_cnt)); - if (dma_stat & 0x80) { - if (!scsi_dma_is_ignored_buserr( dma_stat )) - printk( "SCSI DMA bus error -- bad DMA programming!\n" ); - } - else { - /* Under normal circumstances we never should get to this point, - * since both interrupts are triggered simultaneously and the 5380 - * int has higher priority. When this irq is handled, that DMA - * interrupt is cleared. So a warning message is printed here. - */ - printk( "SCSI DMA intr ?? -- this shouldn't happen!\n" ); - } -} -#endif - -#endif - -void restore_irq(struct pt_regs *regs) -{ - unsigned long flags; - - save_flags(flags); - flags = (flags & ~0x0700) | (regs->sr & 0x0700); - restore_flags(flags); -} - -static int polled_scsi_on = 0; -static unsigned char *mac_scsi_regp = NULL; - -void scsi_mac_polled (void) -{ - if (polled_scsi_on) - { - if(NCR5380_read(BUS_AND_STATUS_REG)&BASR_IRQ) - { - printk("SCSI poll\n"); - scsi_mac_intr(IRQ_MAC_SCSI, NULL, NULL); - } - } -} - -static void scsi_mac_intr (int irq, void *dummy, struct pt_regs *fp) -{ - unsigned long flags; -#ifdef REAL_DMA - int dma_stat; - - dma_stat = mac_scsi_dma.dma_ctrl; - - INT_PRINTK("scsi%d: NCR5380 interrupt, DMA status = %02x\n", - mac_scsi_host->host_no, dma_stat & 0xff); - - /* Look if it was the DMA that has interrupted: First possibility - * is that a bus error occurred... - */ - if (dma_stat & 0x80) { - if (!scsi_dma_is_ignored_buserr( dma_stat )) { - printk(KERN_ERR "SCSI DMA caused bus error near 0x%08lx\n", - SCSI_DMA_READ_P(dma_addr)); - printk(KERN_CRIT "SCSI DMA bus error -- bad DMA programming!"); - } - } - - /* If the DMA is active but not finished, we have the the case - * that some other 5380 interrupt occurred within the DMA transfer. - * This means we have residual bytes, if the desired end address - * is not yet reached. Maybe we have to fetch some bytes from the - * rest data register, too. The residual must be calculated from - * the address pointer, not the counter register, because only the - * addr reg counts bytes not yet written and pending in the rest - * data reg! - */ - if ((dma_stat & 0x02) && !(dma_stat & 0x40)) { - mac_dma_residual = HOSTDATA_DMALEN - (SCSI_DMA_READ_P( dma_addr ) - - mac_dma_startaddr); - - DMA_PRINTK("SCSI DMA: There are %ld residual bytes.\n", - mac_dma_residual); - - if ((signed int)mac_dma_residual < 0) - mac_dma_residual = 0; - if ((dma_stat & 1) == 0) { - /* After read operations, we maybe have to - transport some rest bytes */ - mac_scsi_fetch_restbytes(); - } - else { - /* There seems to be a nasty bug in some SCSI-DMA/NCR - combinations: If a target disconnects while a write - operation is going on, the address register of the - DMA may be a few bytes farer than it actually read. - This is probably due to DMA prefetching and a delay - between DMA and NCR. Experiments showed that the - dma_addr is 9 bytes to high, but this could vary. - The problem is, that the residual is thus calculated - wrong and the next transfer will start behind where - it should. So we round up the residual to the next - multiple of a sector size, if it isn't already a - multiple and the originally expected transfer size - was. The latter condition is there to ensure that - the correction is taken only for "real" data - transfers and not for, e.g., the parameters of some - other command. These shouldn't disconnect anyway. - */ - if (mac_dma_residual & 0x1ff) { - DMA_PRINTK("SCSI DMA: DMA bug corrected, " - "difference %ld bytes\n", - 512 - (mac_dma_residual & 0x1ff)); - mac_dma_residual = (mac_dma_residual + 511) & ~0x1ff; - } - } - mac_scsi_dma.dma_ctrl = 0; - } - - /* If the DMA is finished, fetch the rest bytes and turn it off */ - if (dma_stat & 0x40) { - atari_dma_residual = 0; - if ((dma_stat & 1) == 0) - atari_scsi_fetch_restbytes(); - tt_scsi_dma.dma_ctrl = 0; - } - -#endif /* REAL_DMA */ - -#ifdef PSEUDO_DMA - /* determine if there is any residual for the current transfer */ - if (mac_pdma_active) { - /* probably EOP interrupt, signaling i.e. target disconnect. - * We must figure out the residual from the source/destination - * pointers here ... */ - /* Should check bus status here to make sure it wasn't reselect or reset */ - mac_pdma_residual = HOSTDATA_DMALEN - (mac_pdma_current - mac_pdma_startaddr); - mac_pdma_active = 0; - } -#endif - -#ifdef RBV_HACK - mac_turnoff_irq( IRQ_MAC_SCSI ); -#else - mac_disable_irq( IRQ_MAC_SCSI ); -#endif - - save_flags(flags); -#ifndef RBV_HACK /* interferes with level triggered RBV IRQs ?? */ - restore_irq(fp); -#endif - - if ( irq == IRQ_IDX(IRQ_MAC_SCSI) ) - NCR5380_intr (irq, dummy, fp); - - restore_flags(flags); - - /* To be sure the int is not masked */ -#ifdef RBV_HACK - mac_turnon_irq( IRQ_MAC_SCSI ); -#else - mac_enable_irq( IRQ_MAC_SCSI ); -#endif - - /* Clear the IRQ */ - via_scsi_clear(); -} - - -#ifdef REAL_DMA -static void mac_scsi_fetch_restbytes( void ) -{ - int nr; - char *src, *dst; - - /* fetch rest bytes in the DMA register */ - dst = (char *)SCSI_DMA_READ_P( dma_addr ); - if ((nr = ((long)dst & 3))) { - /* there are 'nr' bytes left for the last long address before the - DMA pointer */ - dst = (char *)( (unsigned long)dst & ~3 ); - DMA_PRINTK("SCSI DMA: there are %d rest bytes for phys addr 0x%08lx", - nr, (long)dst); - dst = (char *)PTOV(dst); /* The content of the DMA pointer - * is a physical address! */ - DMA_PRINTK(" = virt addr 0x%08lx\n", (long)dst); - for( src = (char *)&mac_scsi_dma.dma_restdata; nr > 0; --nr ) - *dst++ = *src++; - } -} -#endif /* REAL_DMA */ - -#if 0 /* FIXME : how is the host ID determined on a Mac? */ -#define RTC_READ(reg) \ - ({ unsigned char __val; \ - outb(reg,&tt_rtc.regsel); \ - __val = tt_rtc.data; \ - __val; \ - }) - -#define RTC_WRITE(reg,val) \ - do { \ - outb(reg,&tt_rtc.regsel); \ - tt_rtc.data = (val); \ - } while(0) -#endif - -int mac_scsi_detect (Scsi_Host_Template *host) -{ - static int called = 0; - struct Scsi_Host *instance; - - if (!MACH_IS_MAC || called) - return( 0 ); - - if (macintosh_config->scsi_type != MAC_SCSI_OLD) - return( 0 ); - - host->proc_dir = &proc_scsi_mac; - - /* testing: IIfx SCSI without IOP ?? */ - if (macintosh_config->ident == MAC_MODEL_IIFX) - mac_scsi_regp = via1_regp+0x8000; - else - mac_scsi_regp = via1_regp+0x10000; - -#if 0 /* maybe if different SCSI versions show up ? */ - mac_scsi_reg_read = IS_A_TT() ? atari_scsi_tt_reg_read : - atari_scsi_falcon_reg_read; - mac_scsi_reg_write = IS_A_TT() ? atari_scsi_tt_reg_write : -#endif atari_scsi_falcon_reg_write; - - /* setup variables */ - host->can_queue = - (setup_can_queue > 0) ? setup_can_queue : - MAC_SCSI_CAN_QUEUE; - host->cmd_per_lun = - (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : - MAC_SCSI_CMD_PER_LUN; - host->sg_tablesize = - (setup_sg_tablesize >= 0) ? setup_sg_tablesize : MAC_SCSI_SG_TABLESIZE; - - if (setup_hostid >= 0) - host->this_id = setup_hostid; - else { - /* use 7 as default */ - host->this_id = 7; - } - -#ifdef SUPPORT_TAGS - if (setup_use_tagged_queuing < 0) - setup_use_tagged_queuing = DEFAULT_USE_TAGGED_QUEUING; -#endif - - instance = scsi_register (host, sizeof (struct NCR5380_hostdata)); - mac_scsi_host = instance; - - /* truncation of machspec bit not critical as instance->irq never used */ -#if 0 /* Might work; problem was only with Falcon lock */ - instance->irq = IRQ_MAC_SCSI; +#ifdef TOSHIBA_DELAY +#define AFTER_RESET_DELAY (5*HZ/2) #else - instance->irq = 0; -#endif - mac_scsi_reset_boot(); - NCR5380_init (instance, 0); - - /* This int is actually "pseudo-slow", i.e. it acts like a slow - * interrupt after having cleared the pending flag for the DMA - * interrupt. */ - request_irq(IRQ_MAC_SCSI, scsi_mac_intr, IRQ_TYPE_SLOW, - "SCSI NCR5380", scsi_mac_intr); -#ifdef REAL_DMA - tt_scsi_dma.dma_ctrl = 0; - atari_dma_residual = 0; - - if (is_brokenscsi) { - /* While the read overruns (described by Drew Eckhardt in - * NCR5380.c) never happened on TTs, they do in fact on the Medusa - * (This was the cause why SCSI didn't work right for so long - * there.) Since handling the overruns slows down a bit, I turned - * the #ifdef's into a runtime condition. - * - * In principle it should be sufficient to do max. 1 byte with - * PIO, but there is another problem on the Medusa with the DMA - * rest data register. So 'atari_read_overruns' is currently set - * to 4 to avoid having transfers that aren't a multiple of 4. If - * the rest data bug is fixed, this can be lowered to 1. - */ - mac_read_overruns = 4; - } -#endif /* REAL_DMA */ - -#ifdef PSEUDO_DMA - mac_pdma_residual = 0; - mac_pdma_active = 0; +#define AFTER_RESET_DELAY (HZ/2) #endif - printk(KERN_INFO "scsi%d: options CAN_QUEUE=%d CMD_PER_LUN=%d SCAT-GAT=%d " -#ifdef SUPPORT_TAGS - "TAGGED-QUEUING=%s " -#endif - "HOSTID=%d", - instance->host_no, instance->hostt->can_queue, - instance->hostt->cmd_per_lun, - instance->hostt->sg_tablesize, -#ifdef SUPPORT_TAGS - setup_use_tagged_queuing ? "yes" : "no", -#endif - instance->hostt->this_id ); - NCR5380_print_options (instance); - printk ("\n"); +static struct proc_dir_entry proc_scsi_mac5380 = { + PROC_SCSI_MAC, 13, "Mac 5380 SCSI", S_IFDIR | S_IRUGO, S_IXUGO, 2 +}; - called = 1; - return( 1 ); -} +/* Must move these into a per-host thingy once the DuoDock is supported */ +static volatile unsigned char *mac_scsi_regp = NULL; +static volatile unsigned char *mac_scsi_drq = NULL; +static volatile unsigned char *mac_scsi_nodrq = NULL; -#ifdef MODULE -int mac_scsi_release (struct Scsi_Host *sh) -{ - free_irq(IRQ_MAC_SCSI, scsi_mac_intr); - if (mac_dma_buffer) - scsi_init_free (mac_dma_buffer, MAC_BUFFER_SIZE); - return 1; -} -#endif +/* + * Function : mac_scsi_setup(char *str, int *ints) + * + * Purpose : booter command line initialization of the overrides array, + * + * Inputs : str - unused, ints - array of integer parameters with ints[0] + * equal to the number of ints. + * + */ -void mac_scsi_setup( char *str, int *ints ) +void mac_scsi_setup(char *str, int *ints) { /* Format of mac5380 parameter is: * mac5380=,,,, @@ -600,45 +162,138 @@ void mac_scsi_setup( char *str, int *ints ) else if (ints[4] > 7) printk( "mac_scsi_setup: invalid host ID %d !\n", ints[4] ); } -#ifdef SUPPORT_TAGS if (ints[0] >= 5) { if (ints[5] >= 0) setup_use_tagged_queuing = !!ints[5]; } -#endif } -int mac_scsi_reset( Scsi_Cmnd *cmd, unsigned int reset_flags) +/* + * XXX: status debug + */ +static struct Scsi_Host *default_instance; + +/* + * Function : int macscsi_detect(Scsi_Host_Template * tpnt) + * + * Purpose : initializes mac NCR5380 driver based on the + * command line / compile time port and irq definitions. + * + * Inputs : tpnt - template for this SCSI adapter. + * + * Returns : 1 if a host adapter was found, 0 if not. + * + */ + +int macscsi_detect(Scsi_Host_Template * tpnt) { - int rv; - struct NCR5380_hostdata *hostdata = - (struct NCR5380_hostdata *)cmd->host->hostdata; + static int called = 0; + int flags = 0; + struct Scsi_Host *instance; - /* For doing the reset, SCSI interrupts must be disabled first, - * since the 5380 raises its IRQ line while _RST is active and we - * can't disable interrupts completely, since we need the timer. - */ - /* And abort a maybe active DMA transfer */ - mac_turnoff_irq( IRQ_MAC_SCSI ); -#ifdef REAL_DMA - mac_scsi_dma.dma_ctrl = 0; -#endif /* REAL_DMA */ -#ifdef PSEUDO_DMA - mac_pdma_active = 0; -#endif + if (!MACH_IS_MAC || called) + return( 0 ); + + if (macintosh_config->scsi_type != MAC_SCSI_OLD) + return( 0 ); + + tpnt->proc_dir = &proc_scsi_mac5380; + + /* setup variables */ + tpnt->can_queue = + (setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE; + tpnt->cmd_per_lun = + (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN; + tpnt->sg_tablesize = + (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE; + + if (setup_hostid >= 0) + tpnt->this_id = setup_hostid; + else { + /* use 7 as default */ + tpnt->this_id = 7; + } + + if (setup_use_tagged_queuing < 0) + setup_use_tagged_queuing = USE_TAGGED_QUEUING; + + /* Once we support multiple 5380s (e.g. DuoDock) we'll do + something different here */ + instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); + default_instance = instance; + + if (macintosh_config->ident == MAC_MODEL_IIFX) { + mac_scsi_regp = via1+0x8000; + mac_scsi_drq = via1+0xE000; + mac_scsi_nodrq = via1+0xC000; + flags = FLAG_NO_PSEUDO_DMA; + } else { + mac_scsi_regp = via1+0x10000; + mac_scsi_drq = via1+0x6000; + mac_scsi_nodrq = via1+0x12000; + } + + instance->io_port = (unsigned long) mac_scsi_regp; + instance->irq = IRQ_MAC_SCSI; + + /* Turn off pseudo DMA and IRQs for II, IIx, IIcx, and SE/30. + * XXX: This is a kludge until we can figure out why interrupts + * won't work. + */ + + if (macintosh_config->via_type == MAC_VIA_II) { + flags = FLAG_NO_PSEUDO_DMA; + instance->irq = IRQ_NONE; + } + + mac_scsi_reset_boot(instance); + + NCR5380_init(instance, flags); + + instance->n_io_port = 255; + + ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0; - rv = NCR5380_reset(cmd, reset_flags); + if (instance->irq != IRQ_NONE) + if (request_irq(instance->irq, macscsi_intr, 0, "MacSCSI-5380", + (void *) mac_scsi_regp)) { + printk("scsi%d: IRQ%d not free, interrupts disabled\n", + instance->host_no, instance->irq); + instance->irq = IRQ_NONE; + } - /* Re-enable ints */ - mac_turnon_irq( IRQ_MAC_SCSI ); + printk("scsi%d: generic 5380 at port %lX irq", instance->host_no, instance->io_port); + if (instance->irq == IRQ_NONE) + printk ("s disabled"); + else + printk (" %d", instance->irq); + printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", + instance->can_queue, instance->cmd_per_lun, MACSCSI_PUBLIC_RELEASE); + printk("\nscsi%d:", instance->host_no); + NCR5380_print_options(instance); + printk("\n"); + called = 1; + return 1; +} - return( rv ); +int macscsi_release (struct Scsi_Host *shpnt) +{ + if (shpnt->irq != IRQ_NONE) + free_irq (shpnt->irq, NULL); + + return 0; } - -static void mac_scsi_reset_boot( void ) +/* + * Our 'bus reset on boot' function + */ + +static void mac_scsi_reset_boot(struct Scsi_Host *instance) { unsigned long end; + + NCR5380_local_declare(); + NCR5380_setup(instance); /* * Do a SCSI reset to clean up the bus during initialization. No messing @@ -648,7 +303,7 @@ static void mac_scsi_reset_boot( void ) printk( "Macintosh SCSI: resetting the SCSI bus..." ); /* switch off SCSI IRQ - catch an interrupt without IRQ bit set else */ - mac_turnoff_irq( IRQ_MAC_SCSI ); + DISABLE_IRQ() /* get in phase */ NCR5380_write( TARGET_COMMAND_REG, @@ -662,446 +317,186 @@ static void mac_scsi_reset_boot( void ) NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE ); NCR5380_read( RESET_PARITY_INTERRUPT_REG ); - for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); ) + for( end = jiffies + AFTER_RESET_DELAY; jiffies < end; ) barrier(); /* switch on SCSI IRQ again */ - mac_turnon_irq( IRQ_MAC_SCSI ); + ENABLE_IRQ() printk( " done\n" ); } +/* + * NCR 5380 register access functions + */ -const char * mac_scsi_info (struct Scsi_Host *host) -{ - /* mac_scsi_detect() is verbose enough... */ - static const char string[] = "Macintosh NCR5380 SCSI"; - return string; -} - - -#if defined(REAL_DMA) +#define CTRL(p,v) (*ctrl = (v)) -unsigned long mac_scsi_dma_setup( struct Scsi_Host *instance, void *data, - unsigned long count, int dir ) +static char macscsi_read(struct Scsi_Host *instance, int reg) { - unsigned long addr = VTOP( data ); - - DMA_PRINTK("scsi%d: setting up dma, data = %p, phys = %lx, count = %ld, " - "dir = %d\n", instance->host_no, data, addr, count, dir); - - if (!STRAM_ADDR(addr)) { - /* If we have a non-DMAable address on a Falcon, use the dribble - * buffer; 'orig_addr' != 0 in the read case tells the interrupt - * handler to copy data from the dribble buffer to the originally - * wanted address. - */ - if (dir) - memcpy( mac_dma_buffer, data, count ); - else - mac_dma_orig_addr = data; - addr = mac_dma_phys_buffer; - } - - mac_dma_startaddr = addr; /* Needed for calculating residual later. */ - - /* Cache cleanup stuff: On writes, push any dirty cache out before sending - * it to the peripheral. (Must be done before DMA setup, since at least - * the ST-DMA begins to fill internal buffers right after setup. For - * reads, invalidate any cache, may be altered after DMA without CPU - * knowledge. - * - * ++roman: For the Medusa, there's no need at all for that cache stuff, - * because the hardware does bus snooping (fine!). - */ - dma_cache_maintenance( addr, count, dir ); - - if (count == 0) - printk(KERN_NOTICE "SCSI warning: DMA programmed for 0 bytes !\n"); + int iobase = instance->io_port; + int i; + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - mac_scsi_dma.dma_ctrl = dir; - SCSI_DMA_WRITE_P( dma_addr, addr ); - SCSI_DMA_WRITE_P( dma_cnt, count ); - mac_scsi_dma.dma_ctrl = dir | 2; + CTRL(iobase, 0); + i = inb(iobase + (reg<<4)); + CTRL(iobase, 0x40); - return( count ); + return i; } - -static long mac_scsi_dma_residual( struct Scsi_Host *instance ) +static void macscsi_write(struct Scsi_Host *instance, int reg, int value) { - return( mac_dma_residual ); -} + int iobase = instance->io_port; + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + CTRL(iobase, 0); + outb(value, iobase + (reg<<4)); + CTRL(iobase, 0x40); +} -#define CMD_SURELY_BLOCK_MODE 0 -#define CMD_SURELY_BYTE_MODE 1 -#define CMD_MODE_UNKNOWN 2 +/* Pseudo-DMA stuff */ -static int mac_classify_cmd( Scsi_Cmnd *cmd ) +static inline void cp_io_to_mem(volatile unsigned char *s, volatile unsigned char *d, int len) { - unsigned char opcode = cmd->cmnd[0]; - - if (opcode == READ_DEFECT_DATA || opcode == READ_LONG || - opcode == READ_BUFFER) - return( CMD_SURELY_BYTE_MODE ); - else if (opcode == READ_6 || opcode == READ_10 || - opcode == 0xa8 /* READ_12 */ || opcode == READ_REVERSE || - opcode == RECOVER_BUFFERED_DATA) { - /* In case of a sequential-access target (tape), special care is - * needed here: The transfer is block-mode only if the 'fixed' bit is - * set! */ - if (cmd->device->type == TYPE_TAPE && !(cmd->cmnd[1] & 1)) - return( CMD_SURELY_BYTE_MODE ); - else - return( CMD_SURELY_BLOCK_MODE ); - } - else - return( CMD_MODE_UNKNOWN ); + asm volatile(" cmp.w #4,%2; \ + ble 8f; \ + move.w %1,%%d0; \ + neg.b %%d0; \ + and.w #3,%%d0; \ + sub.w %%d0,%2; \ + bra 2f; \ + 1: move.b (%0),(%1)+; \ + 2: dbf %%d0,1b; \ + move.w %2,%%d0; \ + lsr.w #5,%%d0; \ + bra 4f; \ + 3: move.l (%0),(%1)+; \ + move.l (%0),(%1)+; \ + move.l (%0),(%1)+; \ + move.l (%0),(%1)+; \ + move.l (%0),(%1)+; \ + move.l (%0),(%1)+; \ + move.l (%0),(%1)+; \ + move.l (%0),(%1)+; \ + 4: dbf %%d0,3b; \ + move.w %2,%%d0; \ + lsr.w #2,%%d0; \ + and.w #7,%%d0; \ + bra 6f; \ + 5: move.l (%0),(%1)+; \ + 6: dbf %%d0,5b; \ + and.w #3,%2 + bra 8f; \ + 7: move.b (%0),(%1)+; \ + 8: dbf %2,7b" + :: "a" (s), "a" (d), "d" (len) + : "%%d0"); } -#endif /* REAL_DMA */ - -#if defined(REAL_DMA) || defined(PSEUDO_DMA) -/* This function calculates the number of bytes that can be transferred via - * DMA. On the TT, this is arbitrary, but on the Falcon we have to use the - * ST-DMA chip. There are only multiples of 512 bytes possible and max. - * 255*512 bytes :-( This means also, that defining READ_OVERRUNS is not - * possible on the Falcon, since that would require to program the DMA for - * n*512 - atari_read_overrun bytes. But it seems that the Falcon doesn't have - * the overrun problem, so this question is academic :-) - */ - -static unsigned long mac_dma_xfer_len( unsigned long wanted_len, - Scsi_Cmnd *cmd, - int write_flag ) +static inline int macscsi_pread (struct Scsi_Host *instance, + unsigned char *dst, int len) { - unsigned long possible_len, limit; - -#if defined(REAL_DMA) - if (IS_A_TT()) - /* TT SCSI DMA can transfer arbitrary #bytes */ - return( wanted_len ); - - /* ST DMA chip is stupid -- only multiples of 512 bytes! (and max. - * 255*512 bytes, but this should be enough) - * - * ++roman: Aaargl! Another Falcon-SCSI problem... There are some commands - * that return a number of bytes which cannot be known beforehand. In this - * case, the given transfer length is an "allocation length". Now it - * can happen that this allocation length is a multiple of 512 bytes and - * the DMA is used. But if not n*512 bytes really arrive, some input data - * will be lost in the ST-DMA's FIFO :-( Thus, we have to distinguish - * between commands that do block transfers and those that do byte - * transfers. But this isn't easy... there are lots of vendor specific - * commands, and the user can issue any command via the - * SCSI_IOCTL_SEND_COMMAND. - * - * The solution: We classify SCSI commands in 1) surely block-mode cmd.s, - * 2) surely byte-mode cmd.s and 3) cmd.s with unknown mode. In case 1) - * and 3), the thing to do is obvious: allow any number of blocks via DMA - * or none. In case 2), we apply some heuristic: Byte mode is assumed if - * the transfer (allocation) length is < 1024, hoping that no cmd. not - * explicitly known as byte mode have such big allocation lengths... - * BTW, all the discussion above applies only to reads. DMA writes are - * unproblematic anyways, since the targets aborts the transfer after - * receiving a sufficient number of bytes. - * - * Another point: If the transfer is from/to an non-ST-RAM address, we - * use the dribble buffer and thus can do only STRAM_BUFFER_SIZE bytes. - */ - - if (write_flag) { - /* Write operation can always use the DMA, but the transfer size must - * be rounded up to the next multiple of 512 (atari_dma_setup() does - * this). - */ - possible_len = wanted_len; - } - else { - /* Read operations: if the wanted transfer length is not a multiple of - * 512, we cannot use DMA, since the ST-DMA cannot split transfers - * (no interrupt on DMA finished!) - */ - if (wanted_len & 0x1ff) - possible_len = 0; - else { - /* Now classify the command (see above) and decide whether it is - * allowed to do DMA at all */ - switch( falcon_classify_cmd( cmd )) { - case CMD_SURELY_BLOCK_MODE: - possible_len = wanted_len; - break; - case CMD_SURELY_BYTE_MODE: - possible_len = 0; /* DMA prohibited */ - break; - case CMD_MODE_UNKNOWN: - default: - /* For unknown commands assume block transfers if the transfer - * size/allocation length is >= 1024 */ - possible_len = (wanted_len < 1024) ? 0 : wanted_len; - break; - } - } - } + unsigned char *d; + volatile unsigned char *s; - /* Last step: apply the hard limit on DMA transfers */ - limit = (atari_dma_buffer && !STRAM_ADDR( VTOP(cmd->SCp.ptr) )) ? - STRAM_BUFFER_SIZE : 255*512; - if (possible_len > limit) - possible_len = limit; + NCR5380_local_declare(); + NCR5380_setup(instance); - if (possible_len != wanted_len) - DMA_PRINTK("Sorry, must cut DMA transfer size to %ld bytes " - "instead of %ld\n", possible_len, wanted_len); + s = mac_scsi_drq+0x60; + d = dst; -#else /* REAL_DMA */ - possible_len = wanted_len; -#endif - - return( possible_len ); -} -#endif /* REAL_DMA || PSEUDO_DMA */ +/* These conditions are derived from MacOS. (How to read CONTROL_STATUS_REG?) */ + while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) + && !(NCR5380_read(STATUS_REG) & SR_REQ)) + ; + if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) + && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) { + printk("Error in macscsi_pread\n"); + return 5; + } -/* - * FIXME !!! - */ + cp_io_to_mem(s, d, len); + + return 0; +} -/* NCR5380 register access functions - */ -static unsigned char mac_scsi_reg_read( unsigned char reg ) -{ - return( mac_scsi_regp[reg << 4] ); -} -static void mac_scsi_reg_write( unsigned char reg, unsigned char value ) +static inline void cp_mem_to_io(volatile unsigned char *s, volatile unsigned char *d, int len) { - mac_scsi_regp[reg << 4] = value; + asm volatile(" cmp.w #4,%2; \ + ble 8f; \ + move.w %0,%%d0; \ + neg.b %%d0; \ + and.w #3,%%d0; \ + sub.w %%d0,%2; \ + bra 2f; \ + 1: move.b (%0)+,(%1); \ + 2: dbf %%d0,1b; \ + move.w %2,%%d0; \ + lsr.w #5,%%d0; \ + bra 4f; \ + 3: move.l (%0)+,(%1); \ + move.l (%0)+,(%1); \ + move.l (%0)+,(%1); \ + move.l (%0)+,(%1); \ + move.l (%0)+,(%1); \ + move.l (%0)+,(%1); \ + move.l (%0)+,(%1); \ + move.l (%0)+,(%1); \ + 4: dbf %%d0,3b; \ + move.w %2,%%d0; \ + lsr.w #2,%%d0; \ + and.w #7,%%d0; \ + bra 6f; \ + 5: move.l (%0)+,(%1); \ + 6: dbf %%d0,5b; \ + and.w #3,%2 + bra 8f; \ + 7: move.b (%0)+,(%1); \ + 8: dbf %2,7b" + :: "a" (s), "a" (d), "d" (len) + : "%%d0"); } -#include "mac_NCR5380.c" - -#ifdef PSEUDO_DMA - -/* - * slightly optimized PIO transfer routines, experimental! - * command may time out if interrupts are left enabled - */ - -static inline int mac_pdma_read (struct Scsi_Host *instance, unsigned char *dst, int len) +static inline int macscsi_pwrite (struct Scsi_Host *instance, + unsigned char *src, int len) { - register unsigned char *d = dst; - register i = len; - register unsigned char p, tmp; - -#if (NDEBUG & NDEBUG_PSEUDO_DMA) - printk("pdma_read: reading %d bytes to %p\n", len, dst); -#endif - - mac_pdma_residual = len; - if (mac_pdma_active) { - printk("pseudo-DMA already active in pread!\n"); - return -1; - } - mac_pdma_active = 1; - mac_pdma_startaddr = (unsigned long) dst; - mac_pdma_current = (unsigned long) dst; - - /* - * Get the phase from the bus (sanity check) - * Hopefully, the phase bits are valid here ... - */ - p = NCR5380_read(STATUS_REG) & PHASE_MASK; - if (!(p & SR_IO)) { - PDMA_PRINTK("NCR5380_pread: initial phase mismatch!\n"); - NCR_PRINT_PHASE(NDEBUG_ANY); - return -1; - } - - /* - * The NCR5380 chip will only drive the SCSI bus when the - * phase specified in the appropriate bits of the TARGET COMMAND - * REGISTER match the STATUS REGISTER - */ - -#if 0 /* done in transfer_dma */ - p = PHASE_DATAIN; - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p)); -#endif - - for (; i; --i) { - HSH_PRINTK(" read %d ..", i); - /* check if we were interrupted ... */ - if (!mac_pdma_active) { - printk("pwrite: interrupt detected!\n"); - break; - } - - /* - * Wait for assertion of REQ, after which the phase bits will be - * valid - */ - while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ)) - barrier(); - - HSH_PRINTK(" REQ .."); - - /* Check for phase mismatch */ - if ((tmp & PHASE_MASK) != p) { - if (!mac_pdma_active) - printk("scsi%d : phase mismatch after interrupt\n", instance->host_no); - else - printk("scsi%d : phase mismatch w/o interrupt\n", instance->host_no); - NCR_PRINT_PHASE(NDEBUG_ANY); - break; - } - - /* Do actual transfer from SCSI bus to memory */ - *d = NCR5380_read(CURRENT_SCSI_DATA_REG); - - d++; - - /* Handshake ... */ - - /* Assert ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK); - HSH_PRINTK(" ACK .."); - - /* Wait for REQ to be dropped */ - while (NCR5380_read(STATUS_REG) & SR_REQ) - barrier(); - HSH_PRINTK(" /REQ .."); + unsigned char *d; - /* Drop ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - HSH_PRINTK(" /ACK !\n"); + NCR5380_local_declare(); + NCR5380_setup(instance); - mac_pdma_current = (unsigned long) d; - mac_pdma_residual--; - } + d = mac_scsi_drq; + +/* These conditions are derived from MacOS. (How to read CONTROL_STATUS_REG?) */ -#if (NDEBUG & NDEBUG_PSEUDO_DMA) - printk("pdma_read: read at %d bytes to %p\n", i, dst); -#endif + while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) + && !(NCR5380_read(STATUS_REG) & SR_REQ) + && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) + ; + if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)) { + printk("Error in macscsi_pwrite\n"); + return 5; + } - if (mac_pdma_residual) - printk("pread: leaving with residual %ld of %ld\n", - mac_pdma_residual, len); - mac_pdma_active = 0; + cp_mem_to_io(src, d, len); - /* ?? */ -#if 0 - NCR5380_write(MODE_REG, MR_BASE); - NCR5380_read(RESET_PARITY_INTERRUPT_REG); -#endif - return 0; + return 0; } - -static inline int mac_pdma_write (struct Scsi_Host *instance, unsigned char *src, int len) -{ - register unsigned char *s = src; - register i = len; - register unsigned char p, tmp; - -#if (NDEBUG & NDEBUG_PSEUDO_DMA) - printk("pdma_write: writing %d bytes from %p\n", len, src); -#endif - - mac_pdma_residual = len; - if (mac_pdma_active) { - printk("pseudo-DMA already active in pwrite!\n"); - return -1; - } - mac_pdma_active = 1; - mac_pdma_startaddr = (unsigned long) src; - mac_pdma_current = (unsigned long) src; - /* - * Get the phase from the bus (sanity check) - */ - p = NCR5380_read(STATUS_REG) & PHASE_MASK; - if (p & SR_IO) { - printk("NCR5380_pwrite: initial phase mismatch!\n"); - NCR_PRINT_PHASE(NDEBUG_ANY); - return -1; - } - - /* - * The NCR5380 chip will only drive the SCSI bus when the - * phase specified in the appropriate bits of the TARGET COMMAND - * REGISTER match the STATUS REGISTER - */ - -#if 0 /* already done in transfer_dma */ - p = PHASE_DATAOUT; - NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p)); -#endif - - for (; i; --i) { - /* check if we were interrupted ... */ - if (!mac_pdma_active) { - printk("pwrite: interrupt detected!\n"); - break; - } - - /* - * Wait for assertion of REQ, after which the phase bits will be - * valid - */ - while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ)); - - /* Check for phase mismatch */ - if ((tmp & PHASE_MASK) != p) { - if (!mac_pdma_active) - printk("scsi%d : phase mismatch after interrupt\n", instance->host_no); - else - printk("scsi%d : phase mismatch w/o interrupt\n", instance->host_no); - NCR_PRINT_PHASE(NDEBUG_ANY); - /* should we signal an error here?? */ - break; - } - - /* Do actual transfer to SCSI bus from memory */ - - NCR5380_write(OUTPUT_DATA_REG, *s); - - s++; - - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA); - - /* Handshake ... assert ACK */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | - ICR_ASSERT_DATA | ICR_ASSERT_ACK); - - /* ... wait for REQ to be dropped */ - while (NCR5380_read(STATUS_REG) & SR_REQ); - - /* and drop ACK (and DATA) ! */ - NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - - mac_pdma_current = (unsigned long) s; - mac_pdma_residual--; - } - -#if (NDEBUG & NDEBUG_PSEUDO_DMA) - printk("pdma_write: write at %d bytes from %p\n", i, src); -#endif - - if (mac_pdma_residual) - printk("pwrite: leaving with residual %ld of len %ld \n", - mac_pdma_residual, len); - mac_pdma_active = 0; - - return 0; +/* These control the behaviour of the generic 5380 core */ +#define AUTOSENSE +#undef DMA_WORKS_RIGHT +#define PSEUDO_DMA -} -#endif /* PSEUDO_DMA */ +#include "NCR5380.c" #ifdef MODULE -Scsi_Host_Template driver_template = MAC_SCSI; + +Scsi_Host_Template driver_template = MAC_NCR5380; #include "scsi_module.c" #endif diff --git a/drivers/scsi/mac_scsi.h b/drivers/scsi/mac_scsi.h index da186f9d406b..d0975cb8f439 100644 --- a/drivers/scsi/mac_scsi.h +++ b/drivers/scsi/mac_scsi.h @@ -1,286 +1,111 @@ /* - * mac_scsi.h -- Header file for the Macintosh native SCSI driver + * Cumana Generic NCR5380 driver defines * - * based on Roman Hodeks atari_scsi.h - */ - -/* - * atari_scsi.h -- Header file for the Atari native SCSI driver + * Copyright 1993, Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * drew@colorado.edu + * +1 (303) 440-4894 * - * Copyright 1994 Roman Hodek + * ALPHA RELEASE 1. * - * (Loosely based on the work of Robert De Vries' team) + * For more information, please consult * - * 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. + * NCR 5380 Family + * SCSI Protocol Controller + * Databook * + * NCR Microelectronics + * 1635 Aeroplaza Drive + * Colorado Springs, CO 80916 + * 1+ (719) 578-3400 + * 1+ (800) 334-5454 */ +/* + * $Log: cumana_NCR5380.h,v $ + */ -#ifndef MAC_SCSI_H -#define MAC_SCSI_H - -/* (I_HAVE_OVERRUNS stuff removed) */ +#ifndef MAC_NCR5380_H +#define MAC_NCR5380_H -#ifndef ASM -int mac_scsi_abort (Scsi_Cmnd *); -int mac_scsi_detect (Scsi_Host_Template *); -const char * mac_scsi_info (struct Scsi_Host *host); -int mac_scsi_queue_command (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); -int mac_scsi_reset (Scsi_Cmnd *, unsigned int); -int mac_scsi_proc_info (char *, char **, off_t, int, int, int); -#ifdef MODULE -int mac_scsi_release (struct Scsi_Host *); -#else -#define mac_scsi_release NULL -#endif +#define MACSCSI_PUBLIC_RELEASE 1 -/* The values for CMD_PER_LUN and CAN_QUEUE are somehow arbitrary. Higher - * values should work, too; try it! (but cmd_per_lun costs memory!) */ -/* But there seems to be a bug somewhere that requires CAN_QUEUE to be - * 2*CMD_PER_LUN. At least on a TT, no spurious timeouts seen since - * changed CMD_PER_LUN... */ +#ifndef ASM +int macscsi_abort (Scsi_Cmnd *); +int macscsi_detect (Scsi_Host_Template *); +int macscsi_release (struct Scsi_Host *); +int macscsi_reset(Scsi_Cmnd *, unsigned int); +int macscsi_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +int macscsi_proc_info (char *buffer, char **start, off_t offset, + int length, int hostno, int inout); -/* Note: The Falcon currently uses 8/1 setting due to unsolved problems with - * cmd_per_lun != 1 */ +#ifndef NULL +#define NULL 0 +#endif -#define MAC_SCSI_CAN_QUEUE 16 -#define MAC_SCSI_CMD_PER_LUN 8 -#define MAC_SCSI_SG_TABLESIZE SG_ALL +#ifndef CMD_PER_LUN +#define CMD_PER_LUN 2 +#endif -#define DEFAULT_USE_TAGGED_QUEUING 0 +#ifndef CAN_QUEUE +#define CAN_QUEUE 16 +#endif +#ifndef SG_TABLESIZE +#define SG_TABLESIZE SG_NONE +#endif -#if defined (HOSTS_C) || defined (MODULE) +#ifndef USE_TAGGED_QUEUING +#define USE_TAGGED_QUEUING 0 +#endif -#define MAC_SCSI { NULL, NULL, NULL, \ - mac_scsi_proc_info, \ - "Macintosh NCR5380 SCSI", \ - mac_scsi_detect, \ - mac_scsi_release, \ - mac_scsi_info, \ - /* command */ NULL, \ - mac_scsi_queue_command, \ - mac_scsi_abort, \ - mac_scsi_reset, \ - /* slave_attach */ NULL, \ - /* bios_param */ NULL, \ - /* can queue */ 0, /* initialized at run-time */ \ - /* host_id */ 0, /* initialized at run-time */ \ - /* scatter gather */ 0, /* initialized at run-time */ \ - /* cmd per lun */ 0, /* initialized at run-time */ \ - /* present */ 0, \ - /* unchecked ISA DMA */ 0, \ - /* use_clustering */ DISABLE_CLUSTERING } +#include -#endif +#define MAC_NCR5380 { \ +name: "Macintosh NCR5380 SCSI", \ +detect: macscsi_detect, \ +release: macscsi_release, /* Release */ \ +queuecommand: macscsi_queue_command, \ +abort: macscsi_abort, \ +reset: macscsi_reset, \ +bios_param: scsicam_bios_param, /* biosparam */ \ +can_queue: CAN_QUEUE, /* can queue */ \ +this_id: 7, /* id */ \ +sg_tablesize: SG_ALL, /* sg_tablesize */ \ +cmd_per_lun: CMD_PER_LUN, /* cmd per lun */ \ +unchecked_isa_dma: 0, /* unchecked_isa_dma */ \ +use_clustering: DISABLE_CLUSTERING \ + } #ifndef HOSTS_C -#define NCR5380_implementation_fields /* none */ +#define NCR5380_implementation_fields \ + int port, ctrl -#define NCR5380_read(reg) mac_scsi_reg_read( reg ) -#define NCR5380_write(reg, value) mac_scsi_reg_write( reg, value ) +#define NCR5380_local_declare() \ + struct Scsi_Host *_instance -#define NCR5380_intr mac_scsi_intr -#define NCR5380_queue_command mac_scsi_queue_command -#define NCR5380_abort mac_scsi_abort -#define NCR5380_proc_info mac_scsi_proc_info -#define NCR5380_dma_read_setup(inst,d,c) mac_scsi_dma_setup (inst, d, c, 0) -#define NCR5380_dma_write_setup(inst,d,c) mac_scsi_dma_setup (inst, d, c, 1) -#define NCR5380_dma_residual(inst) mac_scsi_dma_residual( inst ) -#define NCR5380_dma_xfer_len(i,cmd,phase) \ - mac_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1) -#ifdef PSEUDO_DMA -#define NCR5380_pread(inst,d,l) mac_pdma_read (inst, d, l) -#define NCR5380_pwrite(inst,d,l) mac_pdma_write (inst, d, l) -#endif +#define NCR5380_setup(instance) \ + _instance = instance -/* Debugging printk definitions: - * - * ARB -> arbitration - * ASEN -> auto-sense - * DMA -> DMA - * HSH -> PIO handshake - * INF -> information transfer - * INI -> initialization - * INT -> interrupt - * LNK -> linked commands - * MAIN -> NCR5380_main() control flow - * NDAT -> no data-out phase - * NWR -> no write commands - * PIO -> PIO transfers - * PDMA -> pseudo DMA (unused on MAC) - * QU -> queues - * RSL -> reselections - * SEL -> selections - * USL -> usleep cpde (unused on MAC) - * LBS -> last byte sent (unused on MAC) - * RSS -> restarting of selections - * EXT -> extended messages - * ABRT -> aborting and resetting - * TAG -> queue tag handling - * MER -> merging of consec. buffers - * - */ +#define NCR5380_read(reg) macscsi_read(_instance, reg) +#define NCR5380_write(reg, value) macscsi_write(_instance, reg, value) -#if NDEBUG & NDEBUG_ARBITRATION -#define ARB_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define ARB_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_AUTOSENSE -#define ASEN_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define ASEN_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_DMA -#define DMA_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define DMA_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_HANDSHAKE -#define HSH_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define HSH_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_INFORMATION -#define INF_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define INF_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_INIT -#define INI_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define INI_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_INTR -#define INT_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define INT_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_LINKED -#define LNK_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define LNK_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_MAIN -#define MAIN_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define MAIN_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_NO_DATAOUT -#define NDAT_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define NDAT_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_NO_WRITE -#define NWR_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define NWR_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_PIO -#define PIO_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define PIO_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_PSEUDO_DMA -#define PDMA_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define PDMA_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_QUEUES -#define QU_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define QU_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_RESELECTION -#define RSL_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define RSL_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_SELECTION -#define SEL_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define SEL_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_USLEEP -#define USL_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define USL_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_LAST_BYTE_SENT -#define LBS_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define LBS_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_RESTART_SELECT -#define RSS_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define RSS_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_EXTENDED -#define EXT_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define EXT_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_ABORT -#define ABRT_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define ABRT_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_TAGS -#define TAG_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define TAG_PRINTK(format, args...) -#endif -#if NDEBUG & NDEBUG_MERGING -#define MER_PRINTK(format, args...) \ - printk(KERN_DEBUG format , ## args) -#else -#define MER_PRINTK(format, args...) -#endif +#define NCR5380_pread macscsi_pread +#define NCR5380_pwrite macscsi_pwrite -/* conditional macros for NCR5380_print_{,phase,status} */ +#define NCR5380_intr macscsi_intr +#define NCR5380_queue_command macscsi_queue_command +#define NCR5380_abort macscsi_abort +#define NCR5380_reset macscsi_reset +#define NCR5380_proc_info macscsi_proc_info -#define NCR_PRINT(mask) \ - ((NDEBUG & (mask)) ? NCR5380_print(instance) : (void)0) +#define BOARD_NORMAL 0 +#define BOARD_NCR53C400 1 -#define NCR_PRINT_PHASE(mask) \ - ((NDEBUG & (mask)) ? NCR5380_print_phase(instance) : (void)0) - -#define NCR_PRINT_STATUS(mask) \ - ((NDEBUG & (mask)) ? NCR5380_print_status(instance) : (void)0) - -#define NDEBUG_ANY 0xffffffff - - -#endif /* else def HOSTS_C */ +#endif /* ndef HOSTS_C */ #endif /* ndef ASM */ -#endif /* MAC_SCSI_H */ - +#endif /* MAC_NCR5380_H */ diff --git a/drivers/scsi/oktagon_esp.c b/drivers/scsi/oktagon_esp.c new file mode 100644 index 000000000000..f36235e6854b --- /dev/null +++ b/drivers/scsi/oktagon_esp.c @@ -0,0 +1,598 @@ +/* + * Oktagon_esp.c -- Driver for bsc Oktagon + * + * Written by Carsten Pluntke 1998 + * + * Based on cyber_esp.c + */ + +#include + +#if defined(CONFIG_AMIGA) || defined(CONFIG_APUS) +#define USE_BOTTOM_HALF +#endif + +#define __KERNEL_SYSCALLS__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "NCR53C9x.h" +#include "oktagon_esp.h" + +#include +#include +#include +#include + +#include + +#ifdef USE_BOTTOM_HALF +#include +#include +#endif + +#include + +static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count); +static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_dump_state(struct NCR_ESP *esp); +static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length); +static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length); +static void dma_ints_off(struct NCR_ESP *esp); +static void dma_ints_on(struct NCR_ESP *esp); +static int dma_irq_p(struct NCR_ESP *esp); +static void dma_led_off(struct NCR_ESP *esp); +static void dma_led_on(struct NCR_ESP *esp); +static int dma_ports_p(struct NCR_ESP *esp); +static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write); + +static void dma_irq_exit(struct NCR_ESP *esp); +static void dma_invalidate(struct NCR_ESP *esp); + +static void dma_mmu_get_scsi_one(struct NCR_ESP *,Scsi_Cmnd *); +static void dma_mmu_get_scsi_sgl(struct NCR_ESP *,Scsi_Cmnd *); +static void dma_mmu_release_scsi_one(struct NCR_ESP *,Scsi_Cmnd *); +static void dma_mmu_release_scsi_sgl(struct NCR_ESP *,Scsi_Cmnd *); +static void dma_advance_sg(Scsi_Cmnd *); +static int oktagon_notify_reboot(struct notifier_block *this, unsigned long code, void *x); + +void esp_bootup_reset(struct NCR_ESP *esp,struct ESP_regs *eregs); + +#ifdef USE_BOTTOM_HALF +static void dma_commit(void *opaque); + +long oktag_to_io(long *paddr, long *addr, long len); +long oktag_from_io(long *addr, long *paddr, long len); + +static struct tq_struct tq_fake_dma = { NULL, 0, dma_commit, NULL }; + +#define DMA_MAXTRANSFER 0x8000 + +#else + +/* + * No bottom half. Use transfer directly from IRQ. Find a narrow path + * between too much IRQ overhead and clogging the IRQ for too long. + */ + +#define DMA_MAXTRANSFER 0x1000 + +#endif + +static struct notifier_block oktagon_notifier = { + oktagon_notify_reboot, + NULL, + 0 +}; + +static long *paddress; +static long *address; +static long len; +static long dma_on; +static int direction; +static struct NCR_ESP *current_esp; + + +volatile unsigned char cmd_buffer[16]; + /* This is where all commands are put + * before they are trasfered to the ESP chip + * via PIO. + */ + +/***************************************************************** Detection */ +int oktagon_esp_detect(Scsi_Host_Template *tpnt) +{ + struct NCR_ESP *esp; + const struct ConfigDev *esp_dev; + int key; + unsigned long address; + struct ESP_regs *eregs; + + if((key = zorro_find(ZORRO_PROD_BSC_OKTAGON_2008, 0, 0))){ + esp_dev = zorro_get_board(key); + + /* + * It is a SCSI controller. + * Hardwire Host adapter to SCSI ID 7 + */ + + address = (unsigned long)ZTWO_VADDR(esp_dev->cd_BoardAddr); + eregs = (struct ESP_regs *)(address + OKTAGON_ESP_ADDR); + + /* This line was 5 lines lower */ + esp = esp_allocate(tpnt, (void *) esp_dev); + + /* we have to shift the registers only one bit for oktagon */ + esp->shift = 1; + + esp_write(eregs->esp_cfg1, (ESP_CONFIG1_PENABLE | 7)); + udelay(5); + if (esp_read(eregs->esp_cfg1) != (ESP_CONFIG1_PENABLE | 7)) + return 0; /* Bail out if address did not hold data */ + + /* Do command transfer with programmed I/O */ + esp->do_pio_cmds = 1; + + /* Required functions */ + esp->dma_bytes_sent = &dma_bytes_sent; + esp->dma_can_transfer = &dma_can_transfer; + esp->dma_dump_state = &dma_dump_state; + esp->dma_init_read = &dma_init_read; + esp->dma_init_write = &dma_init_write; + esp->dma_ints_off = &dma_ints_off; + esp->dma_ints_on = &dma_ints_on; + esp->dma_irq_p = &dma_irq_p; + esp->dma_ports_p = &dma_ports_p; + esp->dma_setup = &dma_setup; + + /* Optional functions */ + esp->dma_barrier = 0; + esp->dma_drain = 0; + esp->dma_invalidate = &dma_invalidate; + esp->dma_irq_entry = 0; + esp->dma_irq_exit = &dma_irq_exit; + esp->dma_led_on = &dma_led_on; + esp->dma_led_off = &dma_led_off; + esp->dma_poll = 0; + esp->dma_reset = 0; + + esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one; + esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl; + esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one; + esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl; + esp->dma_advance_sg = &dma_advance_sg; + + /* SCSI chip speed */ + /* Looking at the quartz of the SCSI board... */ + esp->cfreq = 25000000; + + /* The DMA registers on the CyberStorm are mapped + * relative to the device (i.e. in the same Zorro + * I/O block). + */ + esp->dregs = (void *)(address + OKTAGON_DMA_ADDR); + + paddress = (long *) esp->dregs; + + /* ESP register base */ + esp->eregs = eregs; + + /* Set the command buffer */ + esp->esp_command = (volatile unsigned char*) cmd_buffer; + + /* Yes, the virtual address. See below. */ + esp->esp_command_dvma = (__u32) cmd_buffer; + + esp->irq = IRQ_AMIGA_PORTS; + esp->slot = key; + request_irq(IRQ_AMIGA_PORTS, esp_intr, 0, "BSC Oktagon SCSI", esp_intr); + + /* Figure out our scsi ID on the bus */ + esp->scsi_id = 7; + + /* We don't have a differential SCSI-bus. */ + esp->diff = 0; + + esp_initialize(esp); + + zorro_config_board(key, 0); + printk("ESP_Oktagon Driver 1.1" +#ifdef USE_BOTTOM_HALF + " [BOTTOM_HALF]" +#else + " [IRQ]" +#endif + " registered.\n"); + printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps,esps_in_use); + esps_running = esps_in_use; + current_esp = esp; + register_reboot_notifier(&oktagon_notifier); + return esps_in_use; + } + return 0; +} + + +/* + * On certain configurations the SCSI equipment gets confused on reboot, + * so we have to reset it then. + */ + +static int +oktagon_notify_reboot(struct notifier_block *this, unsigned long code, void *x) +{ + struct NCR_ESP *esp; + + if((code == SYS_DOWN || code == SYS_HALT) && (esp = current_esp)) + { + esp_bootup_reset(esp,esp->eregs); + udelay(500); /* Settle time. Maybe unneccessary. */ + } + return NOTIFY_DONE; +} + + + +#ifdef USE_BOTTOM_HALF + + +/* + * The bsc Oktagon controller has no real DMA, so we have to do the 'DMA + * transfer' in the interrupt (Yikes!) or use a bottom half to not to clutter + * IRQ's for longer-than-good. + * + * FIXME + * BIG PROBLEM: 'len' is usually the buffer length, not the expected length + * of the data. So DMA may finish prematurely, further reads lead to + * 'machine check' on APUS systems (don't know about m68k systems, AmigaOS + * deliberately ignores the bus faults) and a normal copy-loop can't + * be exited prematurely just at the right moment by the dma_invalidate IRQ. + * So do it the hard way, write an own copier in assembler and + * catch the exception. + * -- Carsten + */ + + +static void dma_commit(void *opaque) +{ + long wait,len2,pos; + struct NCR_ESP *esp; + + ESPDATA(("Transfer: %ld bytes, Address 0x%08lX, Direction: %d\n", + len,(long) address,direction)); + dma_ints_off(current_esp); + + pos = 0; + wait = 1; + if(direction) /* write? (memory to device) */ + { + while(len > 0) + { + len2 = oktag_to_io(paddress, address+pos, len); + if(!len2) + { + if(wait > 1000) + { + printk("Expedited DMA exit (writing) %ld\n",len); + break; + } + mdelay(wait); + wait *= 2; + } + pos += len2; + len -= len2*sizeof(long); + } + } else { + while(len > 0) + { + len2 = oktag_from_io(address+pos, paddress, len); + if(!len2) + { + if(wait > 1000) + { + printk("Expedited DMA exit (reading) %ld\n",len); + break; + } + mdelay(wait); + wait *= 2; + } + pos += len2; + len -= len2*sizeof(long); + } + } + + /* to make esp->shift work */ + esp=current_esp; + +#if 0 + len2 = (esp_read(current_esp->eregs->esp_tclow) & 0xff) | + ((esp_read(current_esp->eregs->esp_tcmed) & 0xff) << 8); + + /* + * Uh uh. If you see this, len and transfer count registers were out of + * sync. That means really serious trouble. + */ + + if(len2) + printk("Eeeek!! Transfer count still %ld!\n",len2); +#endif + + /* + * Normally we just need to exit and wait for the interrupt to come. + * But at least one device (my Microtek ScanMaker 630) regularly mis- + * calculates the bytes it should send which is really ugly because + * it locks up the SCSI bus if not accounted for. + */ + + if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR)) + { + long len = 100; + long trash[10]; + + /* + * Interrupt bit was not set. Either the device is just plain lazy + * so we give it a 10 ms chance or... + */ + while(len-- && (!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR))) + udelay(100); + + + if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR)) + { + /* + * So we think that the transfer count is out of sync. Since we + * have all we want we are happy and can ditch the trash. + */ + + len = DMA_MAXTRANSFER; + + while(len-- && (!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR))) + oktag_from_io(trash,paddress,2); + + if(!(esp_read(current_esp->eregs->esp_status) & ESP_STAT_INTR)) + { + /* + * Things really have gone wrong. If we leave the system in that + * state, the SCSI bus is locked forever. I hope that this will + * turn the system in a more or less running state. + */ + printk("Device is bolixed, trying bus reset...\n"); + esp_bootup_reset(current_esp,current_esp->eregs); + } + } + } + + ESPDATA(("Transfer_finale: do_data_finale should come\n")); + + len = 0; + dma_on = 0; + dma_ints_on(current_esp); +} + +#endif + +/************************************************************* DMA Functions */ +static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count) +{ + /* Since the CyberStorm DMA is fully dedicated to the ESP chip, + * the number of bytes sent (to the ESP chip) equals the number + * of bytes in the FIFO - there is no buffering in the DMA controller. + * XXXX Do I read this right? It is from host to ESP, right? + */ + return fifo_count; +} + +static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + unsigned long sz = sp->SCp.this_residual; + if(sz > DMA_MAXTRANSFER) + sz = DMA_MAXTRANSFER; + return sz; +} + +static void dma_dump_state(struct NCR_ESP *esp) +{ +} + +/* + * What the f$@& is this? + * + * Some SCSI devices (like my Microtek ScanMaker 630 scanner) want to transfer + * more data than requested. How much? Dunno. So ditch the bogus data into + * the sink, hoping the device will advance to the next phase sooner or later. + * + * -- Carsten + */ + +static long oktag_eva_buffer[16]; /* The data sink */ + +static void oktag_check_dma(void) +{ + struct NCR_ESP *esp; + + esp=current_esp; + if(!len) + { + address = oktag_eva_buffer; + len = 2; + /* esp_do_data sets them to zero like len */ + esp_write(current_esp->eregs->esp_tclow,2); + esp_write(current_esp->eregs->esp_tcmed,0); + } +} + +static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length) +{ + /* Zorro is noncached, everything else done using processor. */ + /* cache_clear(addr, length); */ + + if(dma_on) + panic("dma_init_read while dma process is initialized/running!\n"); + direction = 0; + address = (long *) vaddress; + current_esp = esp; + len = length; + oktag_check_dma(); + dma_on = 1; +} + +static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length) +{ + /* cache_push(addr, length); */ + + if(dma_on) + panic("dma_init_write while dma process is initialized/running!\n"); + direction = 1; + address = (long *) vaddress; + current_esp = esp; + len = length; + oktag_check_dma(); + dma_on = 1; +} + +static void dma_ints_off(struct NCR_ESP *esp) +{ + disable_irq(esp->irq); +} + +static void dma_ints_on(struct NCR_ESP *esp) +{ + enable_irq(esp->irq); +} + +static int dma_irq_p(struct NCR_ESP *esp) +{ + /* It's important to check the DMA IRQ bit in the correct way! */ + return (esp_read(esp->eregs->esp_status) & ESP_STAT_INTR); +} + +static void dma_led_off(struct NCR_ESP *esp) +{ +} + +static void dma_led_on(struct NCR_ESP *esp) +{ +} + +static int dma_ports_p(struct NCR_ESP *esp) +{ + return ((custom.intenar) & IF_PORTS); +} + +static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write) +{ + /* On the Sparc, DMA_ST_WRITE means "move data from device to memory" + * so when (write) is true, it actually means READ! + */ + if(write){ + dma_init_read(esp, addr, count); + } else { + dma_init_write(esp, addr, count); + } +} + +/* + * IRQ entry when DMA transfer is ready to be started + */ + +static void dma_irq_exit(struct NCR_ESP *esp) +{ +#ifdef USE_BOTTOM_HALF + if(dma_on) + { + tq_fake_dma.sync = 0; + queue_task(&tq_fake_dma,&tq_immediate); + mark_bh(IMMEDIATE_BH); + } +#else + while(len && !dma_irq_p(esp)) + { + if(direction) + *paddress = *address++; + else + *address++ = *paddress; + len -= (sizeof(long)); + } + len = 0; + dma_on = 0; +#endif +} + +/* + * IRQ entry when DMA has just finished + */ + +static void dma_invalidate(struct NCR_ESP *esp) +{ +} + +/* + * Since the processor does the data transfer we have to use the custom + * mmu interface to pass the virtual address, not the physical. + */ + +void dma_mmu_get_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + sp->SCp.have_data_in = (int) sp->SCp.ptr = + sp->request_buffer; +} + +void dma_mmu_get_scsi_sgl(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + sp->SCp.ptr = + sp->SCp.buffer->address; +} + +void dma_mmu_release_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ +} + +void dma_mmu_release_scsi_sgl(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ +} + +void dma_advance_sg(Scsi_Cmnd *sp) +{ + sp->SCp.ptr = sp->SCp.buffer->address; +} + +#ifdef MODULE + +#define HOSTS_C + +#include "oktagon_esp.h" + +Scsi_Host_Template driver_template = SCSI_OKTAGON_ESP; + +#include "scsi_module.c" + +#endif + +int oktagon_esp_release(struct Scsi_Host *instance) +{ +#ifdef MODULE + unsigned int key; + + key = ((struct NCR_ESP *)instance->hostdata)->slot; + esp_release(); + zorro_unconfig_board(key, 0); + free_irq(IRQ_AMIGA_PORTS, esp_intr); + unregister_reboot_notifier(&oktagon_notifier); +#endif + return 1; +} diff --git a/drivers/scsi/oktagon_esp.h b/drivers/scsi/oktagon_esp.h new file mode 100644 index 000000000000..91a3a7cfedec --- /dev/null +++ b/drivers/scsi/oktagon_esp.h @@ -0,0 +1,57 @@ +/* oktagon_esp.h: Defines and structures for the CyberStorm SCSI Mk II driver. + * + * Copyright (C) 1996 Jesper Skov (jskov@cs.auc.dk) + */ + +#include "NCR53C9x.h" + +#ifndef OKTAGON_ESP_H +#define OKTAGON_ESP_H + +/* The controller registers can be found in the Z2 config area at these + * offsets: + */ +#define OKTAGON_ESP_ADDR 0x03000 +#define OKTAGON_DMA_ADDR 0x01000 + + +/* The CyberStorm II DMA interface */ +struct oktagon_dma_registers { + volatile unsigned char cond_reg; /* DMA cond (ro) [0x000] */ +#define ctrl_reg cond_reg /* DMA control (wo) [0x000] */ + unsigned char dmapad4[0x3f]; + volatile unsigned char dma_addr0; /* DMA address (MSB) [0x040] */ + unsigned char dmapad1[3]; + volatile unsigned char dma_addr1; /* DMA address [0x044] */ + unsigned char dmapad2[3]; + volatile unsigned char dma_addr2; /* DMA address [0x048] */ + unsigned char dmapad3[3]; + volatile unsigned char dma_addr3; /* DMA address (LSB) [0x04c] */ +}; + +extern int oktagon_esp_detect(struct SHT *); +extern int oktagon_esp_release(struct Scsi_Host *); +extern const char *esp_info(struct Scsi_Host *); +extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int esp_command(Scsi_Cmnd *); +extern int esp_abort(Scsi_Cmnd *); +extern int esp_reset(Scsi_Cmnd *, unsigned int); +extern int esp_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout); + +#define SCSI_OKTAGON_ESP { \ + proc_dir: &proc_scsi_esp, \ + proc_info: &esp_proc_info, \ + name: "BSC Oktagon SCSI", \ + detect: oktagon_esp_detect, \ + release: oktagon_esp_release, \ + queuecommand: esp_queue, \ + abort: esp_abort, \ + reset: esp_reset, \ + can_queue: 7, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: ENABLE_CLUSTERING } + +#endif /* OKTAGON_ESP_H */ diff --git a/drivers/scsi/oktagon_io.S b/drivers/scsi/oktagon_io.S new file mode 100644 index 000000000000..08ce8d80d8f5 --- /dev/null +++ b/drivers/scsi/oktagon_io.S @@ -0,0 +1,195 @@ +/* -*- mode: asm -*- + * Due to problems while transferring data I've put these routines as assembly + * code. + * Since I'm no PPC assembler guru, the code is just the assembler version of + +int oktag_to_io(long *paddr,long *addr,long len) +{ + long *addr2 = addr; + for(len=(len+sizeof(long)-1)/sizeof(long);len--;) + *paddr = *addr2++; + return addr2 - addr; +} + +int oktag_from_io(long *addr,long *paddr,long len) +{ + long *addr2 = addr; + for(len=(len+sizeof(long)-1)/sizeof(long);len--;) + *addr2++ = *paddr; + return addr2 - addr; +} + + * assembled using gcc -O2 -S, with two exception catch points where data + * is moved to/from the IO register. + */ + +#include + +#ifdef CONFIG_APUS + + .file "oktagon_io.c" + +gcc2_compiled.: +/* + .section ".text" +*/ + .align 2 + .globl oktag_to_io + .type oktag_to_io,@function +oktag_to_io: + addi 5,5,3 + srwi 5,5,2 + cmpwi 1,5,0 + mr 9,3 + mr 3,4 + addi 5,5,-1 + bc 12,6,.L3 +.L5: + cmpwi 1,5,0 + lwz 0,0(3) + addi 3,3,4 + addi 5,5,-1 +exp1: stw 0,0(9) + bc 4,6,.L5 +.L3: +ret1: subf 3,4,3 + srawi 3,3,2 + blr +.Lfe1: + .size oktag_to_io,.Lfe1-oktag_to_io + .align 2 + .globl oktag_from_io + .type oktag_from_io,@function +oktag_from_io: + addi 5,5,3 + srwi 5,5,2 + cmpwi 1,5,0 + mr 9,3 + addi 5,5,-1 + bc 12,6,.L9 +.L11: + cmpwi 1,5,0 +exp2: lwz 0,0(4) + addi 5,5,-1 + stw 0,0(3) + addi 3,3,4 + bc 4,6,.L11 +.L9: +ret2: subf 3,9,3 + srawi 3,3,2 + blr +.Lfe2: + .size oktag_from_io,.Lfe2-oktag_from_io + .ident "GCC: (GNU) egcs-2.90.29 980515 (egcs-1.0.3 release)" + +/* + * Exception table. + * Second longword shows where to jump when an exception at the addr the first + * longword is pointing to is caught. + */ + +.section __ex_table,"a" + .align 2 +oktagon_except: + .long exp1,ret1 + .long exp2,ret2 + +#else + +/* +The code which follows is for 680x0 based assembler and is meant for +Linux/m68k. It was created by cross compiling the code using the +instructions given above. I then added the four labels used in the +exception handler table at the bottom of this file. +- Kevin +*/ + +#ifdef CONFIG_AMIGA + + .file "oktagon_io.c" + .version "01.01" +gcc2_compiled.: +.text + .align 2 +.globl oktag_to_io + .type oktag_to_io,@function +oktag_to_io: + link.w %a6,#0 + move.l %d2,-(%sp) + move.l 8(%a6),%a1 + move.l 12(%a6),%d1 + move.l %d1,%a0 + move.l 16(%a6),%d0 + addq.l #3,%d0 + lsr.l #2,%d0 + subq.l #1,%d0 + moveq.l #-1,%d2 + cmp.l %d0,%d2 + jbeq .L3 +.L5: +exp1: + move.l (%a0)+,(%a1) + dbra %d0,.L5 + clr.w %d0 + subq.l #1,%d0 + jbcc .L5 +.L3: +ret1: + move.l %a0,%d0 + sub.l %d1,%d0 + asr.l #2,%d0 + move.l -4(%a6),%d2 + unlk %a6 + rts + +.Lfe1: + .size oktag_to_io,.Lfe1-oktag_to_io + .align 2 +.globl oktag_from_io + .type oktag_from_io,@function +oktag_from_io: + link.w %a6,#0 + move.l %d2,-(%sp) + move.l 8(%a6),%d1 + move.l 12(%a6),%a1 + move.l %d1,%a0 + move.l 16(%a6),%d0 + addq.l #3,%d0 + lsr.l #2,%d0 + subq.l #1,%d0 + moveq.l #-1,%d2 + cmp.l %d0,%d2 + jbeq .L9 +.L11: +exp2: + move.l (%a1),(%a0)+ + dbra %d0,.L11 + clr.w %d0 + subq.l #1,%d0 + jbcc .L11 +.L9: +ret2: + move.l %a0,%d0 + sub.l %d1,%d0 + asr.l #2,%d0 + move.l -4(%a6),%d2 + unlk %a6 + rts +.Lfe2: + .size oktag_from_io,.Lfe2-oktag_from_io + .ident "GCC: (GNU) 2.7.2.1" + +/* + * Exception table. + * Second longword shows where to jump when an exception at the addr the first + * longword is pointing to is caught. + */ + +.section __ex_table,"a" + .align 2 +oktagon_except: + .long exp1,ret1 + .long exp2,ret2 + +#endif +#endif diff --git a/drivers/scsi/sun3x_esp.c b/drivers/scsi/sun3x_esp.c new file mode 100644 index 000000000000..84ad404f0347 --- /dev/null +++ b/drivers/scsi/sun3x_esp.c @@ -0,0 +1,290 @@ +/* sun3x_esp.c: EnhancedScsiProcessor Sun3x SCSI driver code. + * + * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + * + * Based on David S. Miller's esp driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "NCR53C9x.h" + +#include "sun3x_esp.h" +#include +#include + +extern struct NCR_ESP *espchain; + +static void dma_barrier(struct NCR_ESP *esp); +static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count); +static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_drain(struct NCR_ESP *esp); +static void dma_dump_state(struct NCR_ESP *esp); +static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length); +static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length); +static void dma_ints_off(struct NCR_ESP *esp); +static void dma_ints_on(struct NCR_ESP *esp); +static int dma_irq_p(struct NCR_ESP *esp); +static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr); +static int dma_ports_p(struct NCR_ESP *esp); +static void dma_reset(struct NCR_ESP *esp); +static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write); +static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); +static void dma_advance_sg (Scsi_Cmnd *sp); + +volatile unsigned char cmd_buffer[16]; + /* This is where all commands are put + * before they are trasfered to the ESP chip + * via PIO. + */ + +/* Detecting ESP chips on the machine. This is the simple and easy + * version. + */ +int sun3x_esp_detect(Scsi_Host_Template *tpnt) +{ + struct NCR_ESP *esp; + struct ConfigDev *esp_dev; + + esp_dev = 0; + esp = esp_allocate(tpnt, (void *) esp_dev); + + /* Do command transfer with DMA */ + esp->do_pio_cmds = 0; + + /* Required functions */ + esp->dma_bytes_sent = &dma_bytes_sent; + esp->dma_can_transfer = &dma_can_transfer; + esp->dma_dump_state = &dma_dump_state; + esp->dma_init_read = &dma_init_read; + esp->dma_init_write = &dma_init_write; + esp->dma_ints_off = &dma_ints_off; + esp->dma_ints_on = &dma_ints_on; + esp->dma_irq_p = &dma_irq_p; + esp->dma_ports_p = &dma_ports_p; + esp->dma_setup = &dma_setup; + + /* Optional functions */ + esp->dma_barrier = &dma_barrier; + esp->dma_drain = &dma_drain; + esp->dma_irq_entry = &dma_ints_off; + esp->dma_irq_exit = &dma_ints_on; + esp->dma_led_on = 0; + esp->dma_led_off = 0; + esp->dma_poll = &dma_poll; + esp->dma_reset = &dma_reset; + + /* virtual DMA functions */ + esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one; + esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl; + esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one; + esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl; + esp->dma_advance_sg = &dma_advance_sg; + + /* SCSI chip speed */ + esp->cfreq = 20000000; + esp->eregs = (struct ESP_regs *)(SUN3X_ESP_BASE); + esp->dregs = (void *)SUN3X_ESP_DMA; + + esp->esp_command = (volatile unsigned char *)cmd_buffer; + esp->esp_command_dvma = dvma_alloc(virt_to_phys(cmd_buffer), + sizeof (cmd_buffer)); + + esp->irq = 2; + request_irq(esp->irq, esp_intr, SA_INTERRUPT, "SUN3X SCSI", NULL); + + esp->scsi_id = 7; + esp->diff = 0; + + esp_initialize(esp); + + printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, + esps_in_use); + esps_running = esps_in_use; + return esps_in_use; +} + +static void dma_barrier(struct NCR_ESP *esp) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + while(dregs->cond_reg & DMA_PEND_READ) + udelay(1); + dregs->cond_reg &= ~(DMA_ENABLE); +} + +/* This uses various DMA csr fields and the fifo flags count value to + * determine how many bytes were successfully sent/received by the ESP. + */ +static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + int rval = dregs->st_addr - esp->esp_command_dvma; + + return rval - fifo_count; +} + +static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + __u32 base, end, sz; + + base = ((__u32)sp->SCp.ptr); + base &= (0x1000000 - 1); + end = (base + sp->SCp.this_residual); + if(end > 0x1000000) + end = 0x1000000; + sz = (end - base); + return sz; +} + +static void dma_drain(struct NCR_ESP *esp) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + if(dregs->cond_reg & DMA_FIFO_ISDRAIN) { + dregs->cond_reg |= DMA_FIFO_STDRAIN; + while(dregs->cond_reg & DMA_FIFO_ISDRAIN) + udelay(1); + } +} + +static void dma_dump_state(struct NCR_ESP *esp) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + ESPLOG(("esp%d: dma -- cond_reg<%08lx> addr<%p>\n", + esp->esp_id, dregs->cond_reg, dregs->st_addr)); +} + +static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE); + dregs->st_addr = vaddress; +} + +static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + + /* Set up the DMA counters */ + dregs->cond_reg = ((dregs->cond_reg & ~(DMA_ST_WRITE)) | DMA_ENABLE); + dregs->st_addr = vaddress; +} + +static void dma_ints_off(struct NCR_ESP *esp) +{ + DMA_INTSOFF((struct sparc_dma_registers *) esp->dregs); +} + +static void dma_ints_on(struct NCR_ESP *esp) +{ + DMA_INTSON((struct sparc_dma_registers *) esp->dregs); +} + +static int dma_irq_p(struct NCR_ESP *esp) +{ + return DMA_IRQ_P((struct sparc_dma_registers *) esp->dregs); +} + +static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr) +{ + dma_drain(esp); + + /* Wait till the first bits settle. */ + while(vaddr[0] == 0xff) + udelay(1); +} + +static int dma_ports_p(struct NCR_ESP *esp) +{ + return (((struct sparc_dma_registers *) esp->dregs)->cond_reg + & DMA_INT_ENAB); +} + +/* Resetting various pieces of the ESP scsi driver chipset/buses. */ +static void dma_reset(struct NCR_ESP *esp) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *)esp->dregs; + + /* Punt the DVMA into a known state. */ + dregs->cond_reg |= DMA_RST_SCSI; + dregs->cond_reg &= ~(DMA_RST_SCSI); + DMA_INTSON(dregs); +} + +static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write) +{ + struct sparc_dma_registers *dregs = + (struct sparc_dma_registers *) esp->dregs; + unsigned long nreg = dregs->cond_reg; + + if(write) + nreg |= DMA_ST_WRITE; + else + nreg &= ~(DMA_ST_WRITE); + nreg |= DMA_ENABLE; + dregs->cond_reg = nreg; + dregs->st_addr = addr; +} + +static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + sp->SCp.have_data_in = dvma_alloc(virt_to_phys(sp->SCp.buffer), + sp->SCp.this_residual); + sp->SCp.ptr = (char *)((unsigned long)sp->SCp.have_data_in); +} + +static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + int sz = sp->SCp.buffers_residual; + struct mmu_sglist *sg = (struct mmu_sglist *) sp->SCp.buffer; + + while (sz >= 0) { + sg[sz].dvma_addr = dvma_alloc(virt_to_phys(sg[sz].addr), sg[sz].len); + sz--; + } + sp->SCp.ptr=(char *)((unsigned long)sp->SCp.buffer->dvma_address); +} + +static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + dvma_free(sp->SCp.have_data_in, sp->request_bufflen); +} + +static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) +{ + int sz = sp->use_sg - 1; + struct mmu_sglist *sg = (struct mmu_sglist *)sp->buffer; + + while(sz >= 0) { + dvma_free(sg[sz].dvma_addr,sg[sz].len); + sz--; + } +} + +static void dma_advance_sg (Scsi_Cmnd *sp) +{ + sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dvma_address); +} diff --git a/drivers/scsi/sun3x_esp.h b/drivers/scsi/sun3x_esp.h new file mode 100644 index 000000000000..e00cd850d1de --- /dev/null +++ b/drivers/scsi/sun3x_esp.h @@ -0,0 +1,41 @@ +/* sun3x_esp.h: Defines and structures for the Sun3x ESP + * + * (C) 1995 Thomas Bogendoerfer (tsbogend@alpha.franken.de) + */ + +#ifndef _SUN3X_ESP_H +#define _SUN3X_ESP_H + +/* For dvma controller register definitions. */ +#include + +extern int sun3x_esp_detect(struct SHT *); +extern const char *esp_info(struct Scsi_Host *); +extern int esp_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int esp_command(Scsi_Cmnd *); +extern int esp_abort(Scsi_Cmnd *); +extern int esp_reset(Scsi_Cmnd *, unsigned int); +extern int esp_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout); + +extern struct proc_dir_entry proc_scsi_esp; + +#define DMA_PORTS_P (dregs->cond_reg & DMA_INT_ENAB) + +#define SCSI_SUN3X_ESP { \ + proc_dir: &proc_scsi_esp, \ + proc_info: &esp_proc_info, \ + name: "Sun ESP 100/100a/200", \ + detect: sun3x_esp_detect, \ + info: esp_info, \ + command: esp_command, \ + queuecommand: esp_queue, \ + abort: esp_abort, \ + reset: esp_reset, \ + can_queue: 7, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING, } + +#endif /* !(_SUN3X_ESP_H) */ diff --git a/drivers/scsi/sym53c8xx_defs.h b/drivers/scsi/sym53c8xx_defs.h index 444d56890e1f..d5b6f0e1b7fd 100644 --- a/drivers/scsi/sym53c8xx_defs.h +++ b/drivers/scsi/sym53c8xx_defs.h @@ -822,11 +822,11 @@ typedef struct { FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ FE_RAM|FE_IO256} \ , \ - {PCI_DEVICE_ID_LSI_53C1010, 0xff, "1010", 6, 62, 7, \ + {PCI_DEVICE_ID_LSI_53C1010, 0xff, "1010", 6, 31, 7, \ FE_WIDE|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ FE_RAM|FE_RAM8K|FE_64BIT|FE_IO256|FE_NOPM|FE_LEDC|FE_ULTRA3} \ , \ - {PCI_DEVICE_ID_LSI_53C1010_66, 0xff, "1010_66", 6, 62, 7, \ + {PCI_DEVICE_ID_LSI_53C1010_66, 0xff, "1010_66", 6, 31, 7, \ FE_WIDE|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN| \ FE_RAM|FE_RAM8K|FE_64BIT|FE_IO256|FE_NOPM|FE_LEDC|FE_ULTRA3|FE_66MHZ} \ } diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 12b105d4f9a4..575593c38517 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -176,6 +176,7 @@ MODULE_PARM(setup_strings, "s"); static inline uchar read_wd33c93(wd33c93_regs *regp,uchar reg_num) { regp->SASR = reg_num; + mb(); return(regp->SCMD); } @@ -186,14 +187,18 @@ static inline uchar read_wd33c93(wd33c93_regs *regp,uchar reg_num) static inline void write_wd33c93(wd33c93_regs *regp,uchar reg_num, uchar value) { regp->SASR = reg_num; + mb(); regp->SCMD = value; + mb(); } static inline void write_wd33c93_cmd(wd33c93_regs *regp, uchar cmd) { regp->SASR = WD_COMMAND; + mb(); regp->SCMD = cmd; + mb(); } @@ -216,9 +221,11 @@ uchar x = 0; static void write_wd33c93_count(wd33c93_regs *regp,unsigned long value) { regp->SASR = WD_TRANSFER_COUNT_MSB; + mb(); regp->SCMD = value >> 16; regp->SCMD = value >> 8; regp->SCMD = value; + mb(); } @@ -227,9 +234,11 @@ static unsigned long read_wd33c93_count(wd33c93_regs *regp) unsigned long value; regp->SASR = WD_TRANSFER_COUNT_MSB; + mb(); value = regp->SCMD << 16; value |= regp->SCMD << 8; value |= regp->SCMD; + mb(); return value; } diff --git a/drivers/scsi/wd33c93.h b/drivers/scsi/wd33c93.h index d559f05f3078..23029ea130e5 100644 --- a/drivers/scsi/wd33c93.h +++ b/drivers/scsi/wd33c93.h @@ -227,13 +227,13 @@ struct WD33C93_hostdata { uchar clock_freq; uchar chip; /* what kind of wd33c93? */ uchar microcode; /* microcode rev */ + uchar dma_buffer_pool; /* FEF: buffer from chip_ram? */ int dma_dir; /* data transfer dir. */ dma_setup_t dma_setup; dma_stop_t dma_stop; unsigned int dma_xfer_mask; uchar *dma_bounce_buffer; unsigned int dma_bounce_len; - uchar dma_buffer_pool; /* FEF: buffer from chip_ram? */ volatile uchar busy[8]; /* index = target, bit = lun */ volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */ volatile Scsi_Cmnd *selecting; /* trying to select this command */ diff --git a/drivers/sound/cs46xx.c b/drivers/sound/cs46xx.c index 1d3a060f30d7..78dba73bcd9e 100644 --- a/drivers/sound/cs46xx.c +++ b/drivers/sound/cs46xx.c @@ -22,6 +22,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * Changes: + * 20000909 Changed cs_read, cs_write and drain_dac + * Nils Faerber */ #include @@ -777,7 +780,7 @@ static int drain_dac(struct cs_state *state, int nonblock) tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; tmo >>= sample_shift[dmabuf->fmt]; - tmo += (4096*HZ)/dmabuf->rate; + tmo += (2048*HZ)/dmabuf->rate; if (!schedule_timeout(tmo ? tmo : 1) && tmo){ printk(KERN_ERR "cs461x: drain_dac, dma timeout? %d\n", count); @@ -921,6 +924,7 @@ static ssize_t cs_read(struct file *file, char *buffer, size_t count, loff_t *pp { struct cs_state *state = (struct cs_state *)file->private_data; struct dmabuf *dmabuf = &state->dmabuf; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned swptr; @@ -940,6 +944,7 @@ static ssize_t cs_read(struct file *file, char *buffer, size_t count, loff_t *pp return -EFAULT; ret = 0; + add_wait_queue(&state->dmabuf.wait, &wait); while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); if (dmabuf->count > (signed) dmabuf->dmasize) { @@ -952,6 +957,8 @@ static ssize_t cs_read(struct file *file, char *buffer, size_t count, loff_t *pp cnt = dmabuf->dmasize - swptr; if (dmabuf->count < cnt) cnt = dmabuf->count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&state->card->lock, flags); if (cnt > count) @@ -963,38 +970,20 @@ static ssize_t cs_read(struct file *file, char *buffer, size_t count, loff_t *pp start_adc(state); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; - return ret; - } - /* This isnt strictly right for the 810 but it'll do */ - tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); - tmo >>= sample_shift[dmabuf->fmt]; - /* There are two situations when sleep_on_timeout returns, one is when - the interrupt is serviced correctly and the process is waked up by - ISR ON TIME. Another is when timeout is expired, which means that - either interrupt is NOT serviced correctly (pending interrupt) or it - is TOO LATE for the process to be scheduled to run (scheduler latency) - which results in a (potential) buffer overrun. And worse, there is - NOTHING we can do to prevent it. */ - if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { -#ifdef DEBUG - printk(KERN_ERR "cs461x: recording schedule timeout, " - "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, - dmabuf->hwptr, dmabuf->swptr); -#endif - /* a buffer overrun, we delay the recovery untill next time the - while loop begin and we REALLY have space to record */ + remove_wait_queue(&state->dmabuf.wait, &wait); + break; } + schedule(); if (signal_pending(current)) { ret = ret ? ret : -ERESTARTSYS; - return ret; + break; } continue; } if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { if (!ret) ret = -EFAULT; - return ret; + break; } swptr = (swptr + cnt) % dmabuf->dmasize; @@ -1009,6 +998,8 @@ static ssize_t cs_read(struct file *file, char *buffer, size_t count, loff_t *pp ret += cnt; start_adc(state); } + remove_wait_queue(&state->dmabuf.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } @@ -1017,8 +1008,9 @@ static ssize_t cs_read(struct file *file, char *buffer, size_t count, loff_t *pp static ssize_t cs_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct cs_state *state = (struct cs_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); struct dmabuf *dmabuf = &state->dmabuf; - ssize_t ret; + ssize_t ret = 0; unsigned long flags; unsigned swptr; int cnt; @@ -1035,8 +1027,7 @@ static ssize_t cs_write(struct file *file, const char *buffer, size_t count, lof return ret; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; - ret = 0; - + add_wait_queue(&state->dmabuf.wait, &wait); while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); if (dmabuf->count < 0) { @@ -1049,6 +1040,8 @@ static ssize_t cs_write(struct file *file, const char *buffer, size_t count, lof cnt = dmabuf->dmasize - swptr; if (dmabuf->count + cnt > dmabuf->dmasize) cnt = dmabuf->dmasize - dmabuf->count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&state->card->lock, flags); if (cnt > count) @@ -1060,37 +1053,18 @@ static ssize_t cs_write(struct file *file, const char *buffer, size_t count, lof start_dac(state); if (file->f_flags & O_NONBLOCK) { if (!ret) ret = -EAGAIN; - return ret; - } - /* Not strictly correct but works */ - tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); - tmo >>= sample_shift[dmabuf->fmt]; - /* There are two situations when sleep_on_timeout returns, one is when - the interrupt is serviced correctly and the process is waked up by - ISR ON TIME. Another is when timeout is expired, which means that - either interrupt is NOT serviced correctly (pending interrupt) or it - is TOO LATE for the process to be scheduled to run (scheduler latency) - which results in a (potential) buffer underrun. And worse, there is - NOTHING we can do to prevent it. */ - if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { -#ifdef DEBUG - printk(KERN_ERR "cs461x: playback schedule timeout, " - "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, - dmabuf->hwptr, dmabuf->swptr); -#endif - /* a buffer underrun, we delay the recovery untill next time the - while loop begin and we REALLY have data to play */ + break; } + schedule(); if (signal_pending(current)) { if (!ret) ret = -ERESTARTSYS; - return ret; + break; } continue; } if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { if (!ret) ret = -EFAULT; - return ret; + break; } swptr = (swptr + cnt) % dmabuf->dmasize; @@ -1106,6 +1080,8 @@ static ssize_t cs_write(struct file *file, const char *buffer, size_t count, lof ret += cnt; start_dac(state); } + remove_wait_queue(&state->dmabuf.wait, &wait); + set_current_state(TASK_RUNNING); return ret; } @@ -2524,7 +2500,7 @@ static struct cs_card_type __initdata cards[]={ {PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, clkrun_hack}, {PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL}, {0, 0, "Card without SSID set", NULL, NULL }, - {0, 0, NULL, NULL} + {0, 0, NULL, NULL, NULL} }; static int __init cs_install(struct pci_dev *pci_dev) diff --git a/drivers/sound/pas2_mixer.c b/drivers/sound/pas2_mixer.c index 04a98feadb7d..e87a36fc603f 100644 --- a/drivers/sound/pas2_mixer.c +++ b/drivers/sound/pas2_mixer.c @@ -71,7 +71,7 @@ mix_write(unsigned char data, int ioaddr) if (pas_model == 4) { - outw(data | (data << 8), (ioaddr ^ translate_code) - 1); + outw(data | (data << 8), (ioaddr + translate_code) - 1); outb((0x80), 0); } else pas_write(data, ioaddr); diff --git a/drivers/sound/trident.c b/drivers/sound/trident.c index 2f2993fd8d3f..4c8a5edbb040 100644 --- a/drivers/sound/trident.c +++ b/drivers/sound/trident.c @@ -115,6 +115,10 @@ #include #include +#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC +#include +#endif + #include "dm.h" #include "trident.h" @@ -2899,13 +2903,23 @@ static int __init trident_install(struct pci_dev *pci_dev, struct pci_audio_info outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { - /* edited by HMSEO for GT sound */ -#ifdef CONFIG_ALPHA_NAUTILUS - u16 ac97_data = trident_ac97_get (card->ac97_codec[0], AC97_POWER_CONTROL); - trident_ac97_set (card->ac97_codec[0], AC97_POWER_CONTROL, - ac97_data | ALI_EAPD_POWER_DOWN); -#endif - /* edited by HMSEO for GT sound*/ + /* edited by HMSEO for GT sound + 09-20-00 changed by Rich Payne (rdp@alphalinux.org) to take into account + generic kernels running on Nautilus */ + +#if defined CONFIG_ALPHA_NAUTILUS || CONFIG_ALPHA_GENERIC + u16 ac97_data; + extern struct hwrpb_struct *hwrpb; + + if ((hwrpb->sys_type) == 201) { + printk(KERN_INFO "trident: Running on Alpha system type Nautilus\n"); + ac97_data = trident_ac97_get (card->ac97_codec[0], AC97_POWER_CONTROL); + trident_ac97_set (card->ac97_codec[0], AC97_POWER_CONTROL, + ac97_data | ALI_EAPD_POWER_DOWN); + } +#endif + /* edited by HMSEO for GT sound*/ + } /* Enable Address Engine Interrupts */ diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index ea00f8db663e..13bf863a041e 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -10,6 +10,7 @@ if [ ! "$CONFIG_USB" = "n" ]; then comment 'Miscellaneous USB options' bool ' Preliminary USB device filesystem' CONFIG_USB_DEVICEFS + bool ' Support for hot-pluggable USB devices' CONFIG_HOTPLUG if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH else diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 51d8562f4314..39711c3eeef0 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -1005,8 +1005,10 @@ static int usbin_start(struct usb_audiodev *as) } spin_lock_irqsave(&as->lock, flags); } - if (u->dma.count >= u->dma.dmasize && !u->dma.mapped) + if (u->dma.count >= u->dma.dmasize && !u->dma.mapped) { + spin_unlock_irqrestore(&as->lock, flags); return 0; + } u->flags |= FLG_RUNNING; if (!(u->flags & FLG_URB0RUNNING)) { urb = &u->durb[0].urb; @@ -1366,8 +1368,10 @@ static int usbout_start(struct usb_audiodev *as) } spin_lock_irqsave(&as->lock, flags); } - if (u->dma.count <= 0 && !u->dma.mapped) + if (u->dma.count <= 0 && !u->dma.mapped) { + spin_unlock_irqrestore(&as->lock, flags); return 0; + } u->flags |= FLG_RUNNING; if (!(u->flags & FLG_URB0RUNNING)) { urb = &u->durb[0].urb; diff --git a/drivers/usb/bluetooth.c b/drivers/usb/bluetooth.c index 1b02b021dd24..e841d4915f47 100644 --- a/drivers/usb/bluetooth.c +++ b/drivers/usb/bluetooth.c @@ -1,11 +1,16 @@ /* - * bluetooth.c Version 0.5 + * bluetooth.c Version 0.6 * * Copyright (c) 2000 Greg Kroah-Hartman * Copyright (c) 2000 Mark Douglas Corner * * USB Bluetooth driver, based on the Bluetooth Spec version 1.0B * + * (10/05/2000) Version 0.6 gkh + * Fixed bug with urb->dev not being set properly, now that the usb + * core needs it. + * Got a real major id number and name. + * * (08/06/2000) Version 0.5 gkh * Fixed problem of not resubmitting the bulk read urb if there is * an error in the callback. Ericsson devices seem to need this. @@ -90,8 +95,8 @@ MODULE_DESCRIPTION("USB Bluetooth driver"); #define BLUETOOTH_PROGRAMMING_PROTOCOL_CODE 0x01 -#define BLUETOOTH_TTY_MAJOR 240 /* Prototype number for now */ -#define BLUETOOTH_TTY_MINORS 8 +#define BLUETOOTH_TTY_MAJOR 216 /* real device node major id */ +#define BLUETOOTH_TTY_MINORS 256 /* whole lotta bluetooth devices */ #define USB_BLUETOOTH_MAGIC 0x6d02 /* magic number for bluetooth struct */ @@ -139,11 +144,16 @@ struct usb_bluetooth { unsigned char * interrupt_in_buffer; struct urb * interrupt_in_urb; + __u8 interrupt_in_endpointAddress; + __u8 interrupt_in_interval; + int interrupt_in_buffer_size; unsigned char * bulk_in_buffer; struct urb * read_urb; + __u8 bulk_in_endpointAddress; + int bulk_in_buffer_size; - int bulk_out_size; + int bulk_out_buffer_size; struct urb * write_urb_pool[NUM_BULK_URBS]; __u8 bulk_out_endpointAddress; @@ -313,10 +323,18 @@ static int bluetooth_open (struct tty_struct *tty, struct file * filp) #ifndef BTBUGGYHARDWARE /* Start reading from the device */ + FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, + usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress), + bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, + bluetooth_read_bulk_callback, bluetooth); result = usb_submit_urb(bluetooth->read_urb); if (result) dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed with status %d", result); #endif + FILL_INT_URB(bluetooth->interrupt_in_urb, bluetooth->dev, + usb_rcvintpipe(bluetooth->dev, bluetooth->interrupt_in_endpointAddress), + bluetooth->interrupt_in_buffer, bluetooth->interrupt_in_buffer_size, + bluetooth_int_callback, bluetooth, bluetooth->interrupt_in_interval); result = usb_submit_urb(bluetooth->interrupt_in_urb); if (result) dbg(__FUNCTION__ " - usb_submit_urb(interrupt in) failed with status %d", result); @@ -446,7 +464,7 @@ static int bluetooth_write (struct tty_struct * tty, int from_user, const unsign urb->transfer_buffer = NULL; } - buffer_size = MIN (count, bluetooth->bulk_out_size); + buffer_size = MIN (count, bluetooth->bulk_out_buffer_size); new_buffer = kmalloc (buffer_size, GFP_KERNEL); if (new_buffer == NULL) { @@ -509,7 +527,7 @@ static int bluetooth_write_room (struct tty_struct *tty) for (i = 0; i < NUM_BULK_URBS; ++i) { if (bluetooth->write_urb_pool[i]->status != -EINPROGRESS) { - room += bluetooth->bulk_out_size; + room += bluetooth->bulk_out_buffer_size; } } @@ -628,6 +646,7 @@ static void bluetooth_set_termios (struct tty_struct *tty, struct termios * old) #ifdef BTBUGGYHARDWARE void btusb_enable_bulk_read(struct tty_struct *tty){ struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__); + int result; if (!bluetooth) { return; @@ -640,9 +659,15 @@ void btusb_enable_bulk_read(struct tty_struct *tty){ return; } - if (bluetooth->read_urb) - if (usb_submit_urb(bluetooth->read_urb)) - dbg (__FUNCTION__ " - usb_submit_urb(read bulk) failed"); + if (bluetooth->read_urb) { + FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, + usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress), + bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, + bluetooth_read_bulk_callback, bluetooth); + result = usb_submit_urb(bluetooth->read_urb); + if (result) + err (__FUNCTION__ " - failed submitting read urb, error %d", result); + } } void btusb_disable_bulk_read(struct tty_struct *tty){ @@ -783,14 +808,20 @@ static void bluetooth_read_bulk_callback (struct urb *urb) unsigned char *data = urb->transfer_buffer; unsigned int count = urb->actual_length; unsigned int i; - uint packet_size; + unsigned int packet_size; + int result; #ifdef BTBUGGYHARDWARE if ((count == 4) && (data[0] == 0x00) && (data[1] == 0x00) && (data[2] == 0x00) && (data[3] == 0x00)) { urb->actual_length = 0; - if (usb_submit_urb(urb)) - dbg(__FUNCTION__ " - failed resubmitting read urb"); + FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, + usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress), + bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, + bluetooth_read_bulk_callback, bluetooth); + result = usb_submit_urb(bluetooth->read_urb); + if (result) + err (__FUNCTION__ " - failed resubmitting read urb, error %d", result); return; } @@ -864,8 +895,13 @@ static void bluetooth_read_bulk_callback (struct urb *urb) } exit: - if (usb_submit_urb(urb)) - dbg(__FUNCTION__ " - failed resubmitting read urb"); + FILL_BULK_URB(bluetooth->read_urb, bluetooth->dev, + usb_rcvbulkpipe(bluetooth->dev, bluetooth->bulk_in_endpointAddress), + bluetooth->bulk_in_buffer, bluetooth->bulk_in_buffer_size, + bluetooth_read_bulk_callback, bluetooth); + result = usb_submit_urb(bluetooth->read_urb); + if (result) + err (__FUNCTION__ " - failed resubmitting read urb, error %d", result); return; } @@ -1028,7 +1064,8 @@ static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum) err("No free urbs available"); goto probe_error; } - buffer_size = endpoint->wMaxPacketSize; + bluetooth->bulk_in_buffer_size = buffer_size = endpoint->wMaxPacketSize; + bluetooth->bulk_in_endpointAddress = endpoint->bEndpointAddress; bluetooth->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); if (!bluetooth->bulk_in_buffer) { err("Couldn't allocate bulk_in_buffer"); @@ -1051,7 +1088,7 @@ static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum) bluetooth->write_urb_pool[i] = urb; } - bluetooth->bulk_out_size = endpoint->wMaxPacketSize * 2; + bluetooth->bulk_out_buffer_size = endpoint->wMaxPacketSize * 2; endpoint = interrupt_in_endpoint[0]; bluetooth->interrupt_in_urb = usb_alloc_urb(0); @@ -1059,7 +1096,9 @@ static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum) err("No free urbs available"); goto probe_error; } - buffer_size = endpoint->wMaxPacketSize; + bluetooth->interrupt_in_buffer_size = buffer_size = endpoint->wMaxPacketSize; + bluetooth->interrupt_in_endpointAddress = endpoint->bEndpointAddress; + bluetooth->interrupt_in_interval = endpoint->bInterval; bluetooth->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL); if (!bluetooth->interrupt_in_buffer) { err("Couldn't allocate interrupt_in_buffer"); @@ -1071,7 +1110,7 @@ static void * usb_bluetooth_probe(struct usb_device *dev, unsigned int ifnum) /* initialize the devfs nodes for this device and let the user know what bluetooths we are bound to */ tty_register_devfs (&bluetooth_tty_driver, 0, minor); - info("Bluetooth converter now attached to ttyBLUE%d (or usb/ttblue/%d for devfs)", minor, minor); + info("Bluetooth converter now attached to ttyUB%d (or usb/ttub/%d for devfs)", minor, minor); bluetooth_table[minor] = bluetooth; @@ -1146,7 +1185,7 @@ static void usb_bluetooth_disconnect(struct usb_device *dev, void *ptr) } } - info("Bluetooth converter now disconnected from ttyBLUE%d", bluetooth->minor); + info("Bluetooth converter now disconnected from ttyUB%d", bluetooth->minor); bluetooth_table[bluetooth->minor] = NULL; @@ -1164,7 +1203,7 @@ static void usb_bluetooth_disconnect(struct usb_device *dev, void *ptr) static struct tty_driver bluetooth_tty_driver = { magic: TTY_DRIVER_MAGIC, driver_name: "usb-bluetooth", - name: "usb/ttblue/%d", + name: "usb/ttub/%d", major: BLUETOOTH_TTY_MAJOR, minor_start: 0, num: BLUETOOTH_TTY_MINORS, diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index 6cd04557cb02..c8b82c8ff9c8 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -1,11 +1,43 @@ /* ** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller ** -** Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net) +** Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@dce.bg) +** ** -** Distribute under GPL version 2 or later. +** ChangeLog: +** .... Most of the time spend reading sources & docs. +** v0.2.x First official release for the Linux kernel. +** v0.3.0 Beutified and structured, some bugs fixed. +** v0.3.x URBifying bulk requests and bugfixing. First relatively +** stable release. Still can touch device's registers only +** from top-halves. +** v0.4.0 Control messages remained unurbified are now URBs. +** Now we can touch the HW at any time. +** v0.4.9 Control urbs again use process context to wait. Argh... +** Some long standing bugs (enable_net_traffic) fixed. +** Also nasty trick about resubmiting control urb from +** interrupt context used. Please let me know how it +** behaves. Pegasus II support added since this version. +** TODO: suppressing HCD warnings spewage on disconnect. */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include #include #include @@ -16,240 +48,573 @@ #include -static const char *version = __FILE__ ": v0.3.14 2000/06/09 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; +static const char *version = __FILE__ ": v0.4.10 2000/09/25 (C) 1999-2000 Petko Manolov (petkan@dce.bg)"; + + +#define PEGASUS_USE_INTR + +#define PEGASUS_II 0x80000000 +#define HAS_HOME_PNA 0x40000000 #define PEGASUS_MTU 1500 #define PEGASUS_MAX_MTU 1536 -#define SROM_WRITE 0x01 -#define SROM_READ 0x02 -#define PEGASUS_TX_TIMEOUT (HZ*5) -#define PEGASUS_RESET 1 + +#define EPROM_WRITE 0x01 +#define EPROM_READ 0x02 +#define EPROM_DONE 0x04 +#define EPROM_WR_ENABLE 0x10 +#define EPROM_LOAD 0x20 + +#define MII_BMCR 0x00 +#define MII_BMSR 0x01 +#define BMSR_MEDIA 0x7808 +#define MII_ANLPA 0x05 +#define ANLPA_100TX_FD 0x0100 +#define ANLPA_100TX_HD 0x0080 +#define ANLPA_10T_FD 0x0040 +#define ANLPA_10T_HD 0x0020 +#define PHY_DONE 0x80 +#define PHY_READ 0x40 +#define PHY_WRITE 0x20 +#define DEFAULT_GPIO_RESET 0x24 +#define LINKSYS_GPIO_RESET 0x24 +#define DEFAULT_GPIO_SET 0x26 + +#define PEGASUS_PRESENT 0x00000001 +#define PEGASUS_RUNNING 0x00000002 +#define PEGASUS_TX_BUSY 0x00000004 +#define PEGASUS_RX_BUSY 0x00000008 +#define CTRL_URB_RUNNING 0x00000010 +#define CTRL_URB_SLEEP 0x00000020 +#define ETH_REGS_CHANGE 0x40000000 +#define ETH_REGS_CHANGED 0x80000000 + +#define RX_MULTICAST 2 +#define RX_PROMISCUOUS 4 + +#define REG_TIMEOUT (HZ) +#define PEGASUS_TX_TIMEOUT (HZ*10) + +#ifdef PEGASUS_USE_INTR +# define INTR_IVAL 0x80 +#else +# define INTR_IVAL 0 +#endif + +#define TX_UNDERRUN 0x80 +#define EXCESSIVE_COL 0x40 +#define LATE_COL 0x20 +#define NO_CARRIER 0x10 +#define LOSS_CARRIER 0x08 +#define JABBER_TIMEOUT 0x04 + +#define PEGASUS_REQT_READ 0xc0 +#define PEGASUS_REQT_WRITE 0x40 +#define PEGASUS_REQ_GET_REGS 0xf0 +#define PEGASUS_REQ_SET_REGS 0xf1 +#define PEGASUS_REQ_SET_REG PEGASUS_REQ_SET_REGS +#define NUM_CTRL_URBS 0x10 #define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) +enum pegasus_registers { + EthCtrl0 = 0, + EthCtrl1 = 1, + EthCtrl2 = 2, + EthID = 0x10, + Reg1d = 0x1d, + EpromOffset = 0x20, + EpromData = 0x21, /* 0x21 low, 0x22 high byte */ + EpromCtrl = 0x23, + PhyAddr = 0x25, + PhyData = 0x26, /* 0x26 low, 0x27 high byte */ + PhyCtrl = 0x28, + UsbStst = 0x2a, + EthTxStat0 = 0x2b, + EthTxStat1 = 0x2c, + EthRxStat = 0x2d, + Reg7b = 0x7b, + Gpio0 = 0x7e, + Gpio1 = 0x7f, + Reg81 = 0x81, +}; + -struct pegasus { +typedef struct pegasus { struct usb_device *usb; struct net_device *net; struct net_device_stats stats; - int flags; - spinlock_t pegasus_lock; - struct urb rx_urb, tx_urb, intr_urb; - unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); - unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); + unsigned flags; + unsigned features; + struct urb ctrl_urb, rx_urb, tx_urb, intr_urb; + devrequest dr; + wait_queue_head_t ctrl_wait; + struct semaphore ctrl_sem; + unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); + unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); unsigned char ALIGN(intr_buff[8]); -}; + __u8 eth_regs[4]; + __u8 phy; + __u8 gpio_res; +} pegasus_t; struct usb_eth_dev { char *name; __u16 vendor; __u16 device; - void *private; + __u32 private; /* LSB is gpio reset value */ }; static int loopback = 0; +static int mii_mode = 0; static int multicast_filter_limit = 32; -MODULE_AUTHOR("Petko Manolov "); +MODULE_AUTHOR("Petko Manolov "); MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver"); MODULE_PARM(loopback, "i"); +MODULE_PARM(mode, "i"); +MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)"); +MODULE_PARM_DESC(mode, "Enable HomePNA mode (bit 0) - default = MII mode = 0"); static struct usb_eth_dev usb_dev_id[] = { - {"Billionton USB-100", 0x08dd, 0x0986, NULL}, - {"Corega FEter USB-TX", 0x7aa, 0x0004, NULL}, - {"MELCO/BUFFALO LUA-TX", 0x0411, 0x0001, NULL}, - {"D-Link DSB-650TX", 0x2001, 0x4001, NULL}, - {"D-Link DSB-650TX", 0x2001, 0x4002, NULL}, - {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL}, - {"D-Link DU-10", 0x07b8, 0xabc1, NULL}, - {"D-Link DU-E100", 0x07b8, 0x4002, NULL}, - {"Linksys USB100TX", 0x066b, 0x2203, NULL}, - {"Linksys USB100TX", 0x066b, 0x2204, NULL}, - {"Linksys USB Ethernet Adapter", 0x066b, 0x2206, NULL}, - {"SMC 202 USB Ethernet", 0x0707, 0x0200, NULL}, - {"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, NULL}, - {"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, NULL}, - {"IO DATA USB ET/TX", 0x04bb, 0x0904, NULL}, - {"LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002, NULL}, - {NULL, 0, 0, NULL} + {"Billionton USB-100", 0x08dd, 0x0986, DEFAULT_GPIO_RESET}, + {"Corega FEter USB-TX", 0x7aa, 0x0004, DEFAULT_GPIO_RESET}, + {"MELCO/BUFFALO LUA-TX", 0x0411, 0x0001, DEFAULT_GPIO_RESET}, + {"D-Link DSB-650TX", 0x2001, 0x4001, LINKSYS_GPIO_RESET}, + {"D-Link DSB-650TX", 0x2001, 0x4002, LINKSYS_GPIO_RESET}, + {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, DEFAULT_GPIO_RESET}, + {"D-Link DSB-650", 0x2001, 0xabc1, DEFAULT_GPIO_RESET}, + {"D-Link DU-E10", 0x07b8, 0xabc1, DEFAULT_GPIO_RESET}, + {"D-Link DU-E100", 0x07b8, 0x4002, DEFAULT_GPIO_RESET}, + {"Linksys USB10TX", 0x066b, 0x2202, LINKSYS_GPIO_RESET}, + {"Linksys USB100TX", 0x066b, 0x2203, LINKSYS_GPIO_RESET}, + {"Linksys USB100TX", 0x066b, 0x2204, LINKSYS_GPIO_RESET}, + {"Linksys USB Ethernet Adapter", 0x066b, 0x2206, LINKSYS_GPIO_RESET}, + {"SMC 202 USB Ethernet", 0x0707, 0x0200, DEFAULT_GPIO_RESET}, + {"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, + HAS_HOME_PNA | DEFAULT_GPIO_RESET}, + {"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, + DEFAULT_GPIO_RESET}, + {"IO DATA USB ET/TX", 0x04bb, 0x0904, DEFAULT_GPIO_RESET}, + {"LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002, DEFAULT_GPIO_RESET}, + {"SOHOware NUB100 Ethernet", 0x15e8, 0x9100, DEFAULT_GPIO_RESET}, + {"ADMtek ADM8511 \"Pegasus II\" USB Ethernet", 0x07a6, 0x8511, + PEGASUS_II | DEFAULT_GPIO_RESET}, + {NULL, 0, 0, 0} }; -#define pegasus_get_registers(dev, indx, size, data)\ - usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ); -#define pegasus_set_registers(dev, indx, size, data)\ - usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ); -#define pegasus_set_register(dev, indx, value) \ - { __u8 __data = value; \ - usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, __data, indx, &__data, 1, HZ);} +static int update_eth_regs_async( pegasus_t * ); +/* Aargh!!! I _really_ hate such tweaks */ +static void ctrl_callback( urb_t *urb ) +{ + pegasus_t *pegasus = urb->context; + if ( !pegasus ) + return; -static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata) + switch ( urb->status ) { + case USB_ST_NOERROR: + if ( pegasus->flags & ETH_REGS_CHANGE ) { + pegasus->flags &= ~ETH_REGS_CHANGE; + pegasus->flags |= ETH_REGS_CHANGED; + update_eth_regs_async( pegasus ); + return; + } + break; + case USB_ST_URB_PENDING: + return; + case USB_ST_URB_KILLED: + break; + default: + warn( __FUNCTION__ " status %d", urb->status); + } + pegasus->flags &= ~ETH_REGS_CHANGED; + if ( pegasus->flags & CTRL_URB_SLEEP ) { + pegasus->flags &= ~CTRL_URB_SLEEP; + wake_up_interruptible( &pegasus->ctrl_wait ); + } +} + + +static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) { - int i; - __u8 data[4] = { 1, 0, 0, 0x40 + index }; + int ret; - pegasus_set_registers(dev, 0x25, 4, data); - for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 0x26, 3, data); - if (data[2] & 0x80) { - *regdata = *(__u16 *)(data); - return 0; - } - udelay(100); + if ( pegasus->flags & ETH_REGS_CHANGED ) { + pegasus->flags |= CTRL_URB_SLEEP; + interruptible_sleep_on( &pegasus->ctrl_wait ); } + pegasus->dr.requesttype = PEGASUS_REQT_READ; + pegasus->dr.request = PEGASUS_REQ_GET_REGS; + pegasus->dr.value = 0; + pegasus->dr.index = cpu_to_le16p(&indx); + pegasus->dr.length = + pegasus->ctrl_urb.transfer_buffer_length = cpu_to_le16p(&size); + + FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + usb_rcvctrlpipe(pegasus->usb,0), + (char *)&pegasus->dr, + data, size, ctrl_callback, pegasus ); + + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { + err( __FUNCTION__ " BAD CTRLs %d", ret); + return ret; + } + pegasus->flags |= CTRL_URB_SLEEP; + interruptible_sleep_on( &pegasus->ctrl_wait ); - warn("read_phy_word() failed"); - return 1; + return ret; } -static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata) +static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) { - int i; - __u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index }; + int ret; - pegasus_set_registers(dev, 0x25, 4, data); - for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 0x28, 1, data); - if (data[0] & 0x80) - return 0; - udelay(100); + if ( pegasus->flags & ETH_REGS_CHANGED ) { + pegasus->flags |= CTRL_URB_SLEEP ; + interruptible_sleep_on( &pegasus->ctrl_wait ); + } + pegasus->dr.requesttype = PEGASUS_REQT_WRITE; + pegasus->dr.request = PEGASUS_REQ_SET_REGS; + pegasus->dr.value = 0; + pegasus->dr.index = cpu_to_le16p( &indx ); + pegasus->dr.length = + pegasus->ctrl_urb.transfer_buffer_length = cpu_to_le16p( &size ); + + FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb,0), + (char *)&pegasus->dr, + data, size, ctrl_callback, pegasus ); + + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { + err( __FUNCTION__ " BAD CTRL %d", ret); + return ret; + } + pegasus->flags |= CTRL_URB_SLEEP; + interruptible_sleep_on( &pegasus->ctrl_wait ); + + return ret; +} + + +static int set_register( pegasus_t *pegasus, __u16 indx, __u8 data ) +{ + int ret; + + if ( pegasus->flags & ETH_REGS_CHANGED ) { + pegasus->flags |= CTRL_URB_SLEEP; + interruptible_sleep_on( &pegasus->ctrl_wait ); + } + pegasus->dr.requesttype = PEGASUS_REQT_WRITE; + pegasus->dr.request = PEGASUS_REQ_SET_REG; + pegasus->dr.value = data; + pegasus->dr.index = cpu_to_le16p( &indx ); + pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = 1; + + FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb,0), + (char *)&pegasus->dr, + &data, 1, ctrl_callback, pegasus ); + + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) { + err( __FUNCTION__ " BAD CTRL %d", ret); + return ret; } + pegasus->flags |= CTRL_URB_SLEEP; + interruptible_sleep_on( &pegasus->ctrl_wait ); + + return ret; +} + + +static int update_eth_regs_async( pegasus_t *pegasus ) +{ + int ret; + + pegasus->dr.requesttype = PEGASUS_REQT_WRITE; + pegasus->dr.request = PEGASUS_REQ_SET_REGS; + pegasus->dr.value = 0; + pegasus->dr.index = EthCtrl0; + pegasus->dr.length = + pegasus->ctrl_urb.transfer_buffer_length = 3; + + FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb,0), + (char *)&pegasus->dr, + pegasus->eth_regs, 3, ctrl_callback, pegasus ); + + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) + err( __FUNCTION__ " BAD CTRL %d, flags %x",ret,pegasus->flags ); + + return ret; +} + - warn("write_phy_word() failed"); +static int read_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd ) +{ + int i; + __u8 data[4] = { phy, 0, 0, indx }; + + set_register( pegasus, PhyCtrl, 0 ); + set_registers( pegasus, PhyAddr, sizeof(data), data ); + set_register( pegasus, PhyCtrl, (indx | PHY_READ) ); + for (i = 0; i < REG_TIMEOUT; i++) { + get_registers(pegasus, PhyCtrl, 1, data); + if ( data[0] & PHY_DONE ) + break; + } + if ( i < REG_TIMEOUT ) { + get_registers( pegasus, PhyData, 2, regd ); + return 0; + } + warn( __FUNCTION__ " failed" ); + return 1; } -static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata, __u8 direction) +static int write_phy_word( pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 regd ) { - int i; - __u8 data[4] = { index, 0, 0, direction }; - - pegasus_set_registers(dev, 0x20, 4, data); - for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 0x23, 1, data); - if (data[0] & 4) { - pegasus_get_registers(dev, 0x21, 2, data); - *retdata = *(__u16 *)data; - return 0; - } + int i; + __u8 data[4] = { phy, 0, 0, indx }; + + *(data + 1) = cpu_to_le16p( ®d ); + set_register( pegasus, PhyCtrl, 0 ); + set_registers( pegasus, PhyAddr, 4, data ); + set_register( pegasus, PhyCtrl, (indx | PHY_WRITE) ); + for (i = 0; i < REG_TIMEOUT; i++) { + get_registers(pegasus, PhyCtrl, 1, data); + if ( data[0] & PHY_DONE ) + break; } + if ( i < REG_TIMEOUT ) + return 0; + warn( __FUNCTION__ " failed" ); - warn("pegasus_rw_srom_word() failed"); return 1; } -static int pegasus_get_node_id(struct usb_device *dev, __u8 *id) +static int read_eprom_word( pegasus_t *pegasus, __u8 index, __u16 *retdata ) +{ + int i, tmp; + + set_register( pegasus, EpromCtrl, 0 ); + set_register( pegasus, EpromOffset, index ); + set_register( pegasus, EpromCtrl, EPROM_READ); + for ( i=0; i < REG_TIMEOUT; i++ ) { + get_registers( pegasus, EpromCtrl, 1, &tmp ); + if ( tmp & EPROM_DONE ) + break; + } + if ( i < REG_TIMEOUT ) { + get_registers( pegasus, EpromData, 2, retdata ); + return 0; + } + warn( __FUNCTION__ " failed" ); + + return -1; +} + + +static inline void enable_eprom_write( pegasus_t *pegasus ) +{ + __u8 tmp; + + get_registers( pegasus, EthCtrl2, 1, &tmp ); + set_register( pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE ); +} + + +static inline void disable_eprom_write( pegasus_t *pegasus ) +{ + __u8 tmp; + + get_registers( pegasus, EthCtrl2, 1, &tmp ); + set_register( pegasus, EpromCtrl, 0 ); + set_register( pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE ); +} + + +static int write_eprom_word( pegasus_t *pegasus, __u8 index, __u16 data ) +{ + int i, tmp; + __u8 d[4] = {0x3f, 0, 0, EPROM_WRITE}; + + set_registers( pegasus, EpromOffset, 4, d ); + enable_eprom_write( pegasus ); + set_register( pegasus, EpromOffset, index ); + set_registers( pegasus, EpromData, 2, &data ); + set_register( pegasus, EpromCtrl, EPROM_WRITE ); + + for ( i=0; i < REG_TIMEOUT; i++ ) { + get_registers( pegasus, EpromCtrl, 1, &tmp ); + if ( tmp & EPROM_DONE ) + break; + } + disable_eprom_write( pegasus ); + if ( i < REG_TIMEOUT ) + return 0; + warn( __FUNCTION__ " failed" ); + return -1; +} + + +static void set_intr_interval( pegasus_t *pegasus ) +{ + __u16 d; + __u8 tmp; + + read_eprom_word( pegasus, 4, &d ); + ((__u8 *)&d)[1] = INTR_IVAL; + write_eprom_word( pegasus, 4, d ); + get_registers( pegasus, EthCtrl2, 1, &tmp ); + set_register( pegasus, EthCtrl2, tmp | EPROM_LOAD ); + udelay( 10000 ); + set_register( pegasus, EthCtrl2, tmp ); +#ifdef PEGASUS_DUMP_EEPROM + { int i; + for ( i=0; i < 0x40; i++ ) { + read_eprom_word( pegasus, i, &d ); + printk( "eepromword %02x-%04x, ", i, d ); + } + printk( "\n" ); + } +#endif +} + + +static inline int get_node_id( pegasus_t *pegasus, __u8 *id ) { int i; + for (i = 0; i < 3; i++) - if (pegasus_rw_srom_word(dev,i,(__u16 *)&id[i * 2],SROM_READ)) + if ( read_eprom_word( pegasus, i, (__u16 *)&id[i*2]) ) return 1; return 0; } -static int pegasus_reset_mac(struct usb_device *dev) +static inline int reset_mac( pegasus_t *pegasus ) { - __u8 data = 0x8; - int i; + __u8 data = 0x8; + int i; - pegasus_set_register(dev, 1, data); - for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 1, 1, &data); + set_register(pegasus, EthCtrl1, data); + for (i = 0; i < REG_TIMEOUT; i++) { + get_registers(pegasus, EthCtrl1, 1, &data); if (~data & 0x08) { if (loopback & 1) - return 0; - if (loopback & 2) - pegasus_write_phy_word(dev, 0, 0x4000); - pegasus_set_register(dev, 0x7e, 0x24); - pegasus_set_register(dev, 0x7e, 0x27); - return 0; + break; + if ( mii_mode && (pegasus->features & HAS_HOME_PNA) ) + set_register( pegasus, Gpio1, 0x34 ); + else + set_register( pegasus, Gpio1, 0x26 ); + set_register( pegasus, Gpio0, pegasus->features ); + set_register( pegasus, Gpio0, DEFAULT_GPIO_SET ); + break; } } - - return 1; + if ( i == REG_TIMEOUT ) + return 1; + return 0; } -static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) +static int enable_net_traffic( struct net_device *dev, struct usb_device *usb ) { - __u16 partmedia, temp; - __u8 node_id[6]; - __u8 data[4]; + __u16 linkpart, bmsr; + __u8 node_id[6]; + __u8 data[4]; + pegasus_t *pegasus = dev->priv; - if (pegasus_get_node_id(usb, node_id)) + if ( get_node_id(pegasus, node_id) ) return 1; - pegasus_set_registers(usb, 0x10, 6, node_id); - memcpy(dev->dev_addr, node_id, 6); - if (pegasus_read_phy_word(usb, 1, &temp)) + set_registers( pegasus, EthID, sizeof(node_id), node_id ); + memcpy( dev->dev_addr, node_id, sizeof(node_id) ); + if ( read_phy_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) ) return 2; - - if ((~temp & 4) && !loopback) { - warn("%s: link NOT established (0x%x), check the cable.", - dev->name, temp); - /* return 3; FIXME */ - } - - if (pegasus_read_phy_word(usb, 5, &partmedia)) + if ( !(bmsr & 0x20) && !loopback ) + warn( "%s: link NOT established (0x%x) - check the cable.", + dev->name, bmsr ); + if ( read_phy_word(pegasus, pegasus->phy, MII_ANLPA, &linkpart) ) return 4; - - if ((partmedia & 0x1f) != 1) { - warn("party FAIL %x", partmedia); - /* return 5; FIXME */ - } + if ( !(linkpart & 1) ) + warn( "link partner stat %x", linkpart ); data[0] = 0xc9; - data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0); + data[1] = 0; + if ( linkpart & (ANLPA_100TX_FD | ANLPA_10T_FD) ) + data[1] |= 0x20; /* set full duplex */ + if ( linkpart & (ANLPA_100TX_FD | ANLPA_100TX_HD) ) + data[1] |= 0x10; /* set 100 Mbps */ + if ( mii_mode ) + data[1] = 0; data[2] = (loopback & 1) ? 0x09 : 0x01; - pegasus_set_registers(usb, 0, 3, data); + *(unsigned *)pegasus->eth_regs = *(unsigned *)data; + + set_registers( pegasus, EthCtrl0, 3, data ); return 0; } -static void pegasus_read_bulk(struct urb *urb) +static void read_bulk_callback( struct urb *urb ) { - struct pegasus *pegasus = urb->context; - struct net_device *net = pegasus->net; + pegasus_t *pegasus = urb->context; + struct net_device *net; int count = urb->actual_length, res; - int rx_status = *(int *)(pegasus->rx_buff + count - 4); + int rx_status; struct sk_buff *skb; __u16 pkt_len; + if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) ) + return; + + net = pegasus->net; + if ( !netif_device_present(net) ) + return; + + if ( pegasus->flags & PEGASUS_RX_BUSY ) { + pegasus->stats.rx_errors++; + return; + } + pegasus->flags |= PEGASUS_RX_BUSY; + + rx_status = *(int *)(pegasus->rx_buff + count - 4); + if (urb->status) { dbg("%s: RX status %d", net->name, urb->status); goto goon; } - if (!count) + if ( !count ) goto goon; -#if 0 - if (rx_status & 0x00010000) - goto goon; -#endif - if (rx_status & 0x000e0000) { + + if ( rx_status & 0x000e0000 ) { dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000); pegasus->stats.rx_errors++; - if(rx_status & 0x060000) pegasus->stats.rx_length_errors++; - if(rx_status & 0x080000) pegasus->stats.rx_crc_errors++; - if(rx_status & 0x100000) pegasus->stats.rx_frame_errors++; + if ( rx_status & 0x060000 ) + pegasus->stats.rx_length_errors++; + if ( rx_status & 0x080000 ) + pegasus->stats.rx_crc_errors++; + if ( rx_status & 0x100000 ) + pegasus->stats.rx_frame_errors++; goto goon; } pkt_len = (rx_status & 0xfff) - 8; - if(!(skb = dev_alloc_skb(pkt_len+2))) + if ( !(skb = dev_alloc_skb(pkt_len+2)) ) goto goon; skb->dev = net; @@ -263,79 +628,103 @@ static void pegasus_read_bulk(struct urb *urb) pegasus->stats.rx_bytes += pkt_len; goon: - if ((res = usb_submit_urb(&pegasus->rx_urb))) - warn("(prb)failed rx_urb %d", res); + FILL_BULK_URB( &pegasus->rx_urb, pegasus->usb, + usb_rcvbulkpipe(pegasus->usb, 1), + pegasus->rx_buff, PEGASUS_MAX_MTU, + read_bulk_callback, pegasus ); + if ( (res = usb_submit_urb(&pegasus->rx_urb)) ) + warn( __FUNCTION__ " failed submint rx_urb %d", res); + pegasus->flags &= ~PEGASUS_RX_BUSY; } -static void pegasus_irq(urb_t *urb) +static void write_bulk_callback( struct urb *urb ) { - __u8 *d = urb->transfer_buffer; - - if ( d[0] ) - dbg("txst0=0x%2x", d[0]); -} + pegasus_t *pegasus = urb->context; + if ( !pegasus || !(pegasus->flags & PEGASUS_RUNNING) ) + return; -static void pegasus_write_bulk(struct urb *urb) -{ - struct pegasus *pegasus = urb->context; + if ( !netif_device_present(pegasus->net) ) + return; + + if ( urb->status ) + info("%s: TX status %d", pegasus->net->name, urb->status); - spin_lock(&pegasus->pegasus_lock); + netif_wake_queue( pegasus->net ); +} - if (urb->status) - info("%s: TX status %d", pegasus->net->name, urb->status); - netif_wake_queue(pegasus->net); - spin_unlock(&pegasus->pegasus_lock); +static void intr_callback( struct urb *urb ) +{ + pegasus_t *pegasus = urb->context; + struct net_device *net; + __u8 *d; + + if ( !pegasus ) + return; + d = urb->transfer_buffer; + net = pegasus->net; + if ( d[0] & 0xfc ) { + pegasus->stats.tx_errors++; + if ( d[0] & TX_UNDERRUN ) + pegasus->stats.tx_fifo_errors++; + if ( d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT) ) + pegasus->stats.tx_aborted_errors++; + if ( d[0] & LATE_COL ) + pegasus->stats.tx_window_errors++; + if ( d[0] & (NO_CARRIER | LOSS_CARRIER) ) + pegasus->stats.tx_carrier_errors++; + } + switch ( urb->status ) { + case USB_ST_NOERROR: + break; + case USB_ST_URB_KILLED: + break; + default: + info("intr status %d", urb->status); + } } -#if 0 -static void pegasus_tx_timeout(struct net_device *net) + +static void pegasus_tx_timeout( struct net_device *net ) { - struct pegasus *pegasus = net->priv; + pegasus_t *pegasus = net->priv; - warn("%s: Tx timed out. Reseting...", net->name); - usb_unlink_urb(&pegasus->tx_urb); + if ( !pegasus ) + return; + + usb_unlink_urb( &pegasus->tx_urb ); + warn("%s: Tx timed out.", net->name); pegasus->stats.tx_errors++; net->trans_start = jiffies; - pegasus->flags |= PEGASUS_RESET; - netif_wake_queue(net); + netif_wake_queue( net ); } -#endif -static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) +static int pegasus_start_xmit( struct sk_buff *skb, struct net_device *net ) { - struct pegasus *pegasus = net->priv; - int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3; - int res; - - if (net->tbusy) { - int tickssofar = jiffies - net->trans_start; - if (tickssofar < PEGASUS_TX_TIMEOUT) - return 1; - warn("%s: Tx timed out. Reseting...", net->name); - pegasus->stats.tx_errors++; - net->trans_start = jiffies; - net->tbusy = 0; - netif_wake_queue(net); - dev_kfree_skb(skb); - return 0; - } - spin_lock(&pegasus->pegasus_lock); - - netif_stop_queue(net); + pegasus_t *pegasus = net->priv; + int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3; + int res; + netif_stop_queue( net ); + if ( !(pegasus->flags & PEGASUS_RUNNING) ) + return 0; + ((__u16 *)pegasus->tx_buff)[0] = skb->len; memcpy(pegasus->tx_buff+2, skb->data, skb->len); - (&pegasus->tx_urb)->transfer_buffer_length = count; - + FILL_BULK_URB( &pegasus->tx_urb, pegasus->usb, + usb_sndbulkpipe(pegasus->usb, 2), + pegasus->tx_buff, PEGASUS_MAX_MTU, + write_bulk_callback, pegasus ); + pegasus->tx_urb.transfer_buffer_length = count; + pegasus->tx_urb.transfer_flags |= USB_ASYNC_UNLINK; if ((res = usb_submit_urb(&pegasus->tx_urb))) { warn("failed tx_urb %d", res); pegasus->stats.tx_errors++; - netif_start_queue(net); + netif_start_queue( net ); } else { pegasus->stats.tx_packets++; pegasus->stats.tx_bytes += skb->len; @@ -344,35 +733,41 @@ static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) dev_kfree_skb(skb); - spin_unlock(&pegasus->pegasus_lock); - return 0; } -static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) +static struct net_device_stats *pegasus_netdev_stats( struct net_device *dev ) { - return &((struct pegasus *)dev->priv)->stats; + return &((pegasus_t *)dev->priv)->stats; +} + + +static inline void disable_net_traffic( pegasus_t *pegasus ) +{ + int tmp=0; + + set_registers( pegasus, EthCtrl0, 2, &tmp ); } static int pegasus_open(struct net_device *net) { - struct pegasus *pegasus = (struct pegasus *)net->priv; - int res; + pegasus_t *pegasus = (pegasus_t *)net->priv; + int res; - if ((res = pegasus_start_net(net, pegasus->usb))) { - err("can't start_net() - %d", res); + if ( (res = enable_net_traffic(net, pegasus->usb)) ) { + err("can't enable_net_traffic() - %d", res); return -EIO; } - - if ((res = usb_submit_urb(&pegasus->rx_urb))) - warn("(open)failed rx_urb %d", res); - - if ((res = usb_submit_urb(&pegasus->intr_urb))) - warn("(open)failed intr_urb %d", res); - - netif_start_queue(net); + if ( (res = usb_submit_urb(&pegasus->rx_urb)) ) + warn( __FUNCTION__ " failed rx_urb %d", res ); +#ifdef PEGASUS_USE_INTR + if ( (res = usb_submit_urb(&pegasus->intr_urb)) ) + warn( __FUNCTION__ " failed intr_urb %d", res); +#endif + netif_start_queue( net ); + pegasus->flags |= PEGASUS_RUNNING; MOD_INC_USE_COUNT; @@ -380,18 +775,18 @@ static int pegasus_open(struct net_device *net) } -static int pegasus_close(struct net_device *net) +static int pegasus_close( struct net_device *net ) { - struct pegasus *pegasus = net->priv; + pegasus_t *pegasus = net->priv; - netif_stop_queue(net); + netif_stop_queue( net ); + pegasus->flags &= ~PEGASUS_RUNNING; + disable_net_traffic( pegasus ); - if ( pegasus->rx_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->rx_urb); - if ( pegasus->tx_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->tx_urb); - if ( pegasus->intr_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->intr_urb); + usb_unlink_urb( &pegasus->rx_urb ); + usb_unlink_urb( &pegasus->tx_urb ); + usb_unlink_urb( &pegasus->ctrl_urb ); + usb_unlink_urb( &pegasus->intr_urb ); MOD_DEC_USE_COUNT; @@ -399,21 +794,21 @@ static int pegasus_close(struct net_device *net) } -static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) +static int pegasus_ioctl( struct net_device *net, struct ifreq *rq, int cmd ) { __u16 *data = (__u16 *)&rq->ifr_data; - struct pegasus *pegasus = net->priv; + pegasus_t *pegasus = net->priv; switch(cmd) { case SIOCDEVPRIVATE: - data[0] = 1; + data[0] = pegasus->phy; case SIOCDEVPRIVATE+1: - pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]); + read_phy_word(pegasus, data[0], data[1]&0x1f, &data[3]); return 0; case SIOCDEVPRIVATE+2: - if (!capable(CAP_NET_ADMIN)) + if ( !capable(CAP_NET_ADMIN) ) return -EPERM; - pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]); + write_phy_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); return 0; default: return -EOPNOTSUPP; @@ -421,24 +816,29 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) } -static void pegasus_set_rx_mode(struct net_device *net) +static void pegasus_set_multicast( struct net_device *net ) { - struct pegasus *pegasus = net->priv; + pegasus_t *pegasus = net->priv; netif_stop_queue(net); if (net->flags & IFF_PROMISC) { + pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS; info("%s: Promiscuous mode enabled", net->name); -/* pegasus_set_register(pegasus->usb, 2, 0x04); FIXME */ } else if ((net->mc_count > multicast_filter_limit) || (net->flags & IFF_ALLMULTI)) { - pegasus_set_register(pegasus->usb, 0, 0xfa); - pegasus_set_register(pegasus->usb, 2, 0); + pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST; + pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; info("%s set allmulti", net->name); } else { + pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST; + pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; info("%s: set Rx mode", net->name); } + pegasus->flags |= ETH_REGS_CHANGE; + ctrl_callback( &pegasus->ctrl_urb ); + netif_wake_queue(net); } @@ -457,10 +857,38 @@ static int check_device_ids( __u16 vendor, __u16 product ) } -static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) +static __u8 mii_phy_probe( pegasus_t *pegasus ) +{ + int i; + __u16 tmp; + + for ( i=0; i < 32; i++ ) { + read_phy_word( pegasus, i, MII_BMSR, &tmp ); + if ( tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0 ) + continue; + else + return i; + } + + return 0; +} + + +static inline void setup_pegasus_II( pegasus_t *pegasus ) +{ + set_register( pegasus, Reg1d, 0 ); + set_register( pegasus, Reg7b, 2 ); + if ( pegasus->features & HAS_HOME_PNA && mii_mode ) + set_register( pegasus, Reg81, 6 ); + else + set_register( pegasus, Reg81, 2 ); +} + + +static void * pegasus_probe( struct usb_device *dev, unsigned int ifnum ) { struct net_device *net; - struct pegasus *pegasus; + pegasus_t *pegasus; int dev_indx; if ( (dev_indx = check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct)) == -1 ) { @@ -478,12 +906,6 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) } memset(pegasus, 0, sizeof(struct pegasus)); - if (pegasus_reset_mac(dev)) { - err("can't reset MAC"); - kfree(pegasus); - return NULL; - } - net = init_etherdev(0, 0); net->priv = pegasus; net->open = pegasus_open; @@ -494,52 +916,74 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) #endif net->do_ioctl = pegasus_ioctl; net->hard_start_xmit = pegasus_start_xmit; - net->set_multicast_list = pegasus_set_rx_mode; + net->set_multicast_list = pegasus_set_multicast; net->get_stats = pegasus_netdev_stats; net->mtu = PEGASUS_MTU; + init_MUTEX( &pegasus-> ctrl_sem ); + init_waitqueue_head( &pegasus->ctrl_wait ); + pegasus->usb = dev; pegasus->net = net; - pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED; - FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1), - pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk, - pegasus); - FILL_BULK_URB(&pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2), - pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk, - pegasus); - FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3), - pegasus->intr_buff, 8, pegasus_irq, pegasus, 500); + FILL_BULK_URB( &pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1), + pegasus->rx_buff, PEGASUS_MAX_MTU, + read_bulk_callback, pegasus ); + FILL_BULK_URB( &pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2), + pegasus->tx_buff, PEGASUS_MAX_MTU, + write_bulk_callback, pegasus ); + FILL_INT_URB( &pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3), + pegasus->intr_buff, 8, intr_callback, + pegasus, INTR_IVAL ); + + pegasus->features = usb_dev_id[dev_indx].private; + if ( reset_mac(pegasus) ) { + err("can't reset MAC"); + unregister_netdev( pegasus->net ); + kfree(pegasus); + pegasus = NULL; + return NULL; + } + + if ( pegasus->features & PEGASUS_II ) { + info( "setup Pegasus II specific registers" ); + setup_pegasus_II( pegasus ); + } + pegasus->phy = mii_phy_probe( pegasus ); + if ( !pegasus->phy ) { + warn( "can't locate MII phy, using default" ); + pegasus->phy = 1; + } - printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name); + set_intr_interval( pegasus ); + + info( "%s: %s", net->name, usb_dev_id[dev_indx].name ); return pegasus; } -static void pegasus_disconnect(struct usb_device *dev, void *ptr) +static void pegasus_disconnect( struct usb_device *dev, void *ptr ) { struct pegasus *pegasus = ptr; - if (!pegasus) { + if ( !pegasus ) { warn("unregistering non-existant device"); return; } - if (pegasus->net->flags & IFF_UP) - dev_close(pegasus->net); - - unregister_netdev(pegasus->net); - - if ( pegasus->rx_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->rx_urb); - if ( pegasus->tx_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->tx_urb); - if ( pegasus->intr_urb.status == -EINPROGRESS ) - usb_unlink_urb(&pegasus->intr_urb); - - kfree(pegasus); + netif_stop_queue( pegasus->net ); + if ( pegasus->flags & PEGASUS_RUNNING ) { + pegasus->flags &= ~PEGASUS_RUNNING; + usb_unlink_urb( &pegasus->rx_urb ); + usb_unlink_urb( &pegasus->tx_urb ); + usb_unlink_urb( &pegasus->ctrl_urb ); + usb_unlink_urb( &pegasus->intr_urb ); + } + unregister_netdev( pegasus->net ); + kfree( pegasus ); + pegasus = NULL; } @@ -551,14 +995,14 @@ static struct usb_driver pegasus_driver = { int __init pegasus_init(void) { - printk( version ); - return usb_register(&pegasus_driver); + info( "%s", version ); + return usb_register( &pegasus_driver ); } void __exit pegasus_exit(void) { - usb_deregister(&pegasus_driver); + usb_deregister( &pegasus_driver ); } -module_init(pegasus_init); -module_exit(pegasus_exit); +module_init( pegasus_init ); +module_exit( pegasus_exit ); diff --git a/drivers/usb/plusb.c b/drivers/usb/plusb.c index a8ffa3aba690..b3f4f2f5a91d 100644 --- a/drivers/usb/plusb.c +++ b/drivers/usb/plusb.c @@ -40,7 +40,7 @@ #include #include #include -//#define DEBUG +#define DEBUG #include #include "plusb.h" @@ -457,7 +457,10 @@ static void plusb_disconnect (struct usb_device *usbdev, void *ptr) { plusb_t *s = ptr; + printk ("plusb_net_disconnect: Starting\n"); + dbg("plusb_disconnect"); + s->connected = 0; plusb_free_all(s); @@ -466,9 +469,16 @@ static void plusb_disconnect (struct usb_device *usbdev, void *ptr) dbg("unregistering netdev: %s",s->net_dev.name); unregister_netdev(&s->net_dev); s->net_dev.name[0] = '\0'; +#if (LINUX_VERSION_CODE < 0x020300) + kfree (s->net_dev.name); + s->net_dev.name = NULL; +#endif } dbg("plusb_disconnect: finished"); + + printk ("plusb_net_disconnect: Finished\n"); + MOD_DEC_USE_COUNT; } @@ -497,8 +507,14 @@ static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum) { plusb_t *s; - dbg("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); + printk ("plusb_probe: Starting\n"); + + if (usbdev) { + printk("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); + } else { + printk ("plusb: usbdev is NULL!\n"); + } if (usbdev->descriptor.idVendor != 0x067b || usbdev->descriptor.idProduct > 0x1) return NULL; @@ -507,22 +523,61 @@ static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum) if (usbdev->descriptor.bNumConfigurations != 1) return NULL; + printk ("plusb_probe: Looking for Struct\n"); s = plusb_find_struct (); if (!s) return NULL; s->usbdev = usbdev; + printk ("plusb_probe: Setting Configuration\n"); if (usb_set_configuration (s->usbdev, usbdev->config[0].bConfigurationValue) < 0) { err("set_configuration failed"); return NULL; } + printk ("plusb_probe: Setting Interface\n"); if (usb_set_interface (s->usbdev, 0, 0) < 0) { err("set_interface failed"); return NULL; } + printk ("plusb_probe: Checking device name\n"); + +#if (LINUX_VERSION_CODE < 0x020300) + { + int i; + + /* EZA: find the device number... we seem to have lost it...*/ + for (i=0; inet_dev.name) { + s->net_dev.name = kmalloc(strlen("plusbXXXX"), GFP_KERNEL); + sprintf (s->net_dev.name, "plusb%d", i); + s->net_dev.init=plusb_net_init; + s->net_dev.priv=s; + + printk ("plusb_probe: Registering Device\n"); + if(!register_netdev(&s->net_dev)) + info("registered: %s", s->net_dev.name); + else { + err("register_netdev failed"); + s->net_dev.name[0] = '\0'; + } + printk ("plusb_probe: Connected!\n"); + } + } +#else + /* Kernel version 2.3+ works a little bit differently */ if(!s->net_dev.name[0]) { strcpy(s->net_dev.name, "plusb%d"); s->net_dev.init=plusb_net_init; @@ -534,8 +589,10 @@ static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum) s->net_dev.name[0] = '\0'; } } - + +#endif s->connected = 1; + printk ("plusb_probe: Set Connected\n"); if(s->opened) { dbg("net device already allocated, restarting USB transfers"); @@ -560,19 +617,27 @@ static struct usb_driver plusb_driver = static int __init plusb_init (void) { unsigned u; + dbg("plusb_init"); /* initialize struct */ for (u = 0; u < NRPLUSB; u++) { - plusb_t *s = &plusb[u]; + plusb_t *s; + dbg("plusb_init: u=%ud about to assign s\n", u); + s= &plusb[u]; + dbg("plusb_init: u=%ud about to memset\n", u); memset (s, 0, sizeof (plusb_t)); s->bh.routine = (void (*)(void *))plusb_bh; s->bh.data = s; + dbg("plusb_init: u=%ud about to init list head\n", u); INIT_LIST_HEAD (&s->tx_skb_list); INIT_LIST_HEAD (&s->free_skb_list); + dbg("plusb_init: u=%ud about to init spin lock\n", u); spin_lock_init (&s->lock); } - + + dbg ("plusb_init: Initialized structure\n"); + /* register misc device */ usb_register (&plusb_driver); diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c index 6b57728c65ab..753038f1b896 100644 --- a/drivers/usb/usb-core.c +++ b/drivers/usb/usb-core.c @@ -19,27 +19,16 @@ /* * USB core */ - -int usb_hub_init(void); -void usb_hub_cleanup(void); -int usb_major_init(void); -void usb_major_cleanup(void); +extern int usb_hub_init(void); +extern void usb_hub_cleanup(void); +extern int usb_major_init(void); +extern void usb_major_cleanup(void); -/* - * HCI drivers - */ - -int uhci_init(void); -int ohci_hcd_init(void); - -#ifdef MODULE - /* * Cleanup */ - -void cleanup_module(void) +static void __exit usb_exit(void) { usb_major_cleanup(); usbdevfs_cleanup(); @@ -49,20 +38,14 @@ void cleanup_module(void) /* * Init */ - -int init_module(void) -#else int usb_init(void) -#endif { usb_major_init(); usbdevfs_init(); usb_hub_init(); -#ifndef CONFIG_USB_MODULE -#ifdef CONFIG_USB_OHCI - ohci_hcd_init(); -#endif -#endif return 0; } + +module_init (usb_init); +module_exit (usb_exit); diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c index 61011d31aef9..2a340c4bd31f 100644 --- a/drivers/usb/usb-ohci.c +++ b/drivers/usb/usb-ohci.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include /* for in_interrupt() */ @@ -2091,7 +2092,7 @@ static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) #define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310 -int ohci_hcd_init (void) +static int __init ohci_hcd_init (void) { int ret = -ENODEV; struct pci_dev * dev = NULL; @@ -2108,15 +2109,7 @@ int ohci_hcd_init (void) /*-------------------------------------------------------------------------*/ -#ifdef MODULE -int init_module (void) -{ - return ohci_hcd_init (); -} - -/*-------------------------------------------------------------------------*/ - -void cleanup_module (void) +static void __exit ohci_hcd_cleanup (void) { ohci_t * ohci; @@ -2133,5 +2126,9 @@ void cleanup_module (void) hc_release_ohci (ohci); } } -#endif //MODULE +module_init (ohci_hcd_init); +module_exit (ohci_hcd_cleanup); + +MODULE_AUTHOR ("Roman Weissgaerber , David Brownell"); +MODULE_DESCRIPTION ("USB OHCI Host Controller Driver"); diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 98e4c28f5f33..3d2ae33bbe37 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -25,6 +25,21 @@ #include #include #include /* for in_interrupt() */ + + +#if defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG) +#include +#include +#include + +#define __KERNEL_SYSCALLS__ +#include + +/* waitpid() call glue uses this */ +static int errno; +#endif + + #ifdef CONFIG_USB_DEBUG #define DEBUG #else @@ -428,7 +443,7 @@ int usb_interface_claimed(struct usb_interface *iface) void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) { /* this should never happen, don't release something that's not ours */ - if (iface->driver != driver || !iface) + if (!iface || iface->driver != driver) return; iface->driver = NULL; @@ -481,6 +496,222 @@ static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) return -1; } + +#if defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG) + +/* + * USB hotplugging invokes what /proc/sys/kernel/hotplug says + * (normally /sbin/hotplug) when USB devices get added or removed. + */ + +static int exec_helper (void *arg) +{ + void **params = (void **) arg; + char *path = (char *) params [0]; + char **argv = (char **) params [1]; + char **envp = (char **) params [2]; + return exec_usermodehelper (path, argv, envp); +} + +int call_usermodehelper (char *path, char **argv, char **envp) +{ + void *params [3] = { path, argv, envp }; + int pid, pid2, retval; + mm_segment_t fs; + + if ((pid = kernel_thread (exec_helper, (void *) params, 0)) < 0) { + err ("failed fork of %s, errno = %d", argv [0], -pid); + return -1; + } + + /* set signal mask? */ + fs = get_fs (); + set_fs (KERNEL_DS); /* retval is in kernel space. */ + pid2 = waitpid (pid, &retval, __WCLONE); /* "errno" gets assigned */ + set_fs (fs); + /* restore signal mask? */ + + if (pid2 != pid) { + err ("waitpid(%d) failed, returned %d\n", pid, pid2); + return -1; + } + return retval; +} + +static int to_bcd (char *buf, __u16 *bcdValue) +{ + int retval = 0; + char *value = (char *) bcdValue; + int temp; + + /* digits are 0-9 then ":;<=>?" for devices using + * non-bcd (non-standard!) values here ... */ + + /* No leading (or later, trailing) zeroes since scripts do + * literal matches, and that's how they're doing them. */ + if ((temp = value [1] & 0xf0) != 0) { + temp >>= 4; + temp += '0'; + *buf++ = (char) temp; + retval++; + } + + temp = value [1] & 0x0f; + temp += '0'; + *buf++ = (char) temp; + retval++; + + *buf++ = '.'; + retval++; + + temp = value [0] & 0xf0; + temp >>= 4; + temp += '0'; + *buf++ = (char) temp; + retval++; + + if ((temp = value [0] & 0x0f) != 0) { + temp += '0'; + *buf++ = (char) temp; + retval++; + } + *buf++ = 0; + + return retval; +} + +/* + * This invokes a user mode policy agent, typically helping to load driver + * or other modules, configure the device, or both. + * + * Some synchronization is important: removes can't start processing + * before the add-device processing completes, and vice versa. That keeps + * a stack of USB-related identifiers stable while they're in use. If we + * know that agents won't complete after they return (such as by forking + * a process that completes later), it's enough to just waitpid() for the + * agent -- as is currently done. + * + * The reason: we know we're called either from khubd (the typical case) + * or from root hub initialization (init, kapmd, modprobe, etc). In both + * cases, we know no other thread can recycle our address, since we must + * already have been serialized enough to prevent that. + */ +static void call_policy (char *verb, struct usb_device *dev) +{ + char *argv [3], **envp, *buf, *scratch; + int i = 0, value; + + if (!hotplug_path [0]) + return; + if (in_interrupt ()) { + dbg ("In_interrupt"); + return; + } + if (!current->fs->root) { + /* statically linked USB is initted rather early */ + dbg ("call_policy %s, num %d -- no FS yet", verb, dev->devnum); + return; + } + if (dev->devnum < 0) { + dbg ("device already deleted ??"); + return; + } + if (!(envp = (char **) kmalloc (20 * sizeof (char *), GFP_KERNEL))) { + dbg ("enomem"); + return; + } + if (!(buf = kmalloc (256, GFP_KERNEL))) { + kfree (envp); + dbg ("enomem2"); + return; + } + + /* only one standardized param to hotplug command: type */ + argv [0] = hotplug_path; + argv [1] = "usb"; + argv [2] = 0; + + /* minimal command environment */ + envp [i++] = "HOME=/"; + envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + +#ifdef DEBUG + /* hint that policy agent should enter no-stdout debug mode */ + envp [i++] = "DEBUG=kernel"; +#endif + /* extensible set of named bus-specific parameters, + * supporting multiple driver selection algorithms. + */ + scratch = buf; + + /* action: add, remove */ + envp [i++] = scratch; + scratch += sprintf (scratch, "ACTION=%s", verb) + 1; + +#ifdef CONFIG_USB_DEVICEFS + /* If this is available, userspace programs can directly read + * all the device descriptors we don't tell them about. Or + * even act as usermode drivers. + * + * XXX how little intelligence can we hardwire? + * (a) mount point: /devfs, /dev, /proc/bus/usb etc. + * (b) naming convention: bus1/device3, 001/003 etc. + */ + envp [i++] = "DEVFS=/proc/bus/usb"; + envp [i++] = scratch; + scratch += sprintf (scratch, "DEVICE=/proc/bus/usb/%03d/%03d", + dev->bus->busnum, dev->devnum) + 1; +#endif + + /* per-device configuration hacks are often necessary */ + envp [i++] = scratch; + scratch += sprintf (scratch, "PRODUCT=%x/%x/", + dev->descriptor.idVendor, + dev->descriptor.idProduct); + scratch += to_bcd (scratch, &dev->descriptor.bcdDevice) + 1; + + /* otherwise, use a simple (so far) generic driver binding model */ + envp [i++] = scratch; + if (dev->descriptor.bDeviceClass == 0) { + int alt = dev->actconfig->interface [0].act_altsetting; + + /* simple/common case: one config, one interface, one driver + * unsimple cases: everything else + */ + scratch += sprintf (scratch, "INTERFACE=%d/%d/%d", + dev->actconfig->interface [0].altsetting [alt].bInterfaceClass, + dev->actconfig->interface [0].altsetting [alt].bInterfaceSubClass, + dev->actconfig->interface [0].altsetting [alt].bInterfaceProtocol) + + 1; + /* INTERFACE-0, INTERFACE-1, ... ? */ + } else { + /* simple/common case: generic device, handled generically */ + scratch += sprintf (scratch, "TYPE=%d/%d/%d", + dev->descriptor.bDeviceClass, + dev->descriptor.bDeviceSubClass, + dev->descriptor.bDeviceProtocol) + 1; + } + envp [i++] = 0; + /* assert: (scratch - buf) < sizeof buf */ + + /* NOTE: user mode daemons can call the agents too */ + + dbg ("kusbd: %s %s %d", argv [0], verb, dev->devnum); + value = call_usermodehelper (argv [0], argv, envp); + kfree (buf); + kfree (envp); + dbg ("kusbd policy returned 0x%x", value); +} + +#else + +static inline void +call_policy (char *verb, struct usb_device *dev) +{ } + +#endif /* KMOD && HOTPLUG */ + + /* * This entrypoint gets called for each new device. * @@ -506,7 +737,10 @@ static void usb_find_drivers(struct usb_device *dev) dbg("unhandled interfaces on device"); if (!claimed) { - warn("This device is not recognized by any installed USB driver."); + warn("USB device %d (prod/vend 0x%x/0x%x) is not claimed by any active driver.", + dev->devnum, + dev->descriptor.idVendor, + dev->descriptor.idProduct); #ifdef DEBUG usb_show_device(dev); #endif @@ -541,8 +775,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) void usb_free_dev(struct usb_device *dev) { if (atomic_dec_and_test(&dev->refcnt)) { - usb_destroy_configuration(dev); dev->bus->op->deallocate(dev); + usb_destroy_configuration(dev); kfree(dev); } } @@ -585,7 +819,7 @@ int usb_submit_urb(urb_t *urb) if (urb && urb->dev) return urb->dev->bus->op->submit_urb(urb); else - return -1; + return -ENODEV; } /*-------------------------------------------------------------------*/ @@ -594,7 +828,7 @@ int usb_unlink_urb(urb_t *urb) if (urb && urb->dev) return urb->dev->bus->op->unlink_urb(urb); else - return -1; + return -ENODEV; } /*-------------------------------------------------------------------* * COMPLETION HANDLERS * @@ -1018,19 +1252,54 @@ int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor size -= config->bLength; for (i = 0; i < config->bNumInterfaces; i++) { - header = (struct usb_descriptor_header *)buffer; - if ((header->bLength > size) || (header->bLength <= 2)) { - err("ran out of descriptors parsing"); - return -1; - } - - if (header->bDescriptorType != USB_DT_INTERFACE) { - warn("unexpected descriptor 0x%X", - header->bDescriptorType); + int numskipped, len; + char *begin; + + /* Skip over the rest of the Class Specific or Vendor */ + /* Specific descriptors */ + begin = buffer; + numskipped = 0; + while (size >= sizeof(struct usb_descriptor_header)) { + header = (struct usb_descriptor_header *)buffer; + + if ((header->bLength > size) || (header->bLength < 2)) { + err("invalid descriptor length of %d", header->bLength); + return -1; + } + + /* If we find another descriptor which is at or below */ + /* us in the descriptor heirarchy then we're done */ + if ((header->bDescriptorType == USB_DT_ENDPOINT) || + (header->bDescriptorType == USB_DT_INTERFACE) || + (header->bDescriptorType == USB_DT_CONFIG) || + (header->bDescriptorType == USB_DT_DEVICE)) + break; + + dbg("skipping descriptor 0x%X", header->bDescriptorType); + numskipped++; buffer += header->bLength; size -= header->bLength; - continue; + } + if (numskipped) + dbg("skipped %d class/vendor specific endpoint descriptors", numskipped); + + /* Copy any unknown descriptors into a storage area for */ + /* drivers to later parse */ + len = (int)(buffer - begin); + if (!len) { + config->extra = NULL; + config->extralen = 0; + } else { + config->extra = kmalloc(len, GFP_KERNEL); + if (!config->extra) { + err("couldn't allocate memory for config extra descriptors"); + config->extralen = 0; + return -1; + } + + memcpy(config->extra, begin, len); + config->extralen = len; } retval = usb_parse_interface(dev, config->interface + i, buffer, size); @@ -1096,13 +1365,6 @@ void usb_destroy_configuration(struct usb_device *dev) } kfree(dev->config); } - -void usb_init_root_hub(struct usb_device *dev) -{ - dev->devnum = -1; - dev->slow = 0; - dev->actconfig = NULL; -} /* for returning string descriptors in UTF-16LE */ static int ascii2utf (char *ascii, __u8 *utf, int utfmax) @@ -1195,6 +1457,8 @@ void usb_disconnect(struct usb_device **pdev) info("USB disconnect on device %d", dev->devnum); + call_policy ("remove", dev); + if (dev->actconfig) { for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { struct usb_interface *interface = &dev->actconfig->interface[i]; @@ -1215,13 +1479,13 @@ void usb_disconnect(struct usb_device **pdev) usb_disconnect(child); } - /* remove /proc/bus/usb entry */ - usbdevfs_remove_device(dev); - - /* Free up the device itself, including its device number */ - if (dev->devnum > 0) + /* Free the device number and remove the /proc/bus/usb entry */ + if (dev->devnum > 0) { clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + usbdevfs_remove_device(dev); + } + /* Free up the device itself */ usb_free_dev(dev); } @@ -1395,7 +1659,7 @@ int usb_clear_halt(struct usb_device *dev, int pipe) if (result < 0) return result; - if (status & 1) + if (le16_to_cpu(status) & 1) return -EPIPE; /* still halted */ usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); @@ -1443,7 +1707,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) } if (!cp) { warn("selecting invalid configuration %d", configuration); - return -1; + return -EINVAL; } if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -1483,12 +1747,12 @@ int usb_get_configuration(struct usb_device *dev) if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) { warn("too many configurations"); - return -1; + return -EINVAL; } if (dev->descriptor.bNumConfigurations < 1) { warn("not enough configurations"); - return -1; + return -EINVAL; } dev->config = (struct usb_config_descriptor *) @@ -1496,7 +1760,7 @@ int usb_get_configuration(struct usb_device *dev) sizeof(struct usb_config_descriptor), GFP_KERNEL); if (!dev->config) { err("out of memory"); - return -1; + return -ENOMEM; } memset(dev->config, 0, dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor)); @@ -1505,7 +1769,7 @@ int usb_get_configuration(struct usb_device *dev) dev->descriptor.bNumConfigurations, GFP_KERNEL); if (!dev->rawdescriptors) { err("out of memory"); - return -1; + return -ENOMEM; } for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) { @@ -1515,8 +1779,10 @@ int usb_get_configuration(struct usb_device *dev) if (result < 8) { if (result < 0) err("unable to get descriptor"); - else + else { err("config descriptor too short (expected %i, got %i)", 8, result); + result = -EINVAL; + } goto err; } @@ -1540,6 +1806,7 @@ int usb_get_configuration(struct usb_device *dev) if (result < length) { err("config descriptor too short (expected %i, got %i)", length, result); + result = -EINVAL; kfree(bigbuffer); goto err; } @@ -1550,7 +1817,7 @@ int usb_get_configuration(struct usb_device *dev) if (result > 0) dbg("descriptor data left"); else if (result < 0) { - result = -1; + result = -EINVAL; goto err; } } @@ -1680,18 +1947,19 @@ int usb_new_device(struct usb_device *dev) err = usb_get_configuration(dev); if (err < 0) { err("unable to get configuration (error=%d)", err); + usb_destroy_configuration(dev); clear_bit(dev->devnum, &dev->bus->devmap.devicemap); dev->devnum = -1; return 1; } - dev->actconfig = dev->config; - usb_set_maxpacket(dev); - /* we set the default configuration here */ - if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { - err("failed to set default configuration"); - return -1; + err = usb_set_configuration(dev, dev->config[0].bConfigurationValue); + if (err) { + err("failed to set default configuration (error=%d)", err); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return 1; } dbg("new device strings: Mfr=%d, Product=%d, SerialNumber=%d", @@ -1711,6 +1979,9 @@ int usb_new_device(struct usb_device *dev) /* find drivers willing to handle this device */ usb_find_drivers(dev); + /* userspace may load modules and/or configure further */ + call_policy ("add", dev); + return 0; } @@ -1782,7 +2053,6 @@ EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_interface_claimed); EXPORT_SYMBOL(usb_driver_release_interface); -EXPORT_SYMBOL(usb_init_root_hub); EXPORT_SYMBOL(usb_root_hub_string); EXPORT_SYMBOL(usb_new_device); EXPORT_SYMBOL(usb_reset_device); diff --git a/drivers/video/Config.in b/drivers/video/Config.in index bd07dfa548ce..8587f310cfea 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -66,6 +66,8 @@ if [ "$CONFIG_FB" = "y" ]; then fi fi if [ "$CONFIG_MAC" = "y" ]; then + bool 'Apple "valkyrie" display support' CONFIG_FB_VALKYRIE +# bool 'Apple DAFB display support' CONFIG_FB_DAFB define_bool CONFIG_FB_MAC y fi if [ "$CONFIG_HP300" = "y" ]; then diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 5be2bf8eab68..db49f97ebc2b 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -153,7 +153,7 @@ else endif ifeq ($(CONFIG_FB_MAC),y) -L_OBJS += macfb.o +L_OBJS += macfb.o macmodes.o endif ifeq ($(CONFIG_FB_HP300),y) diff --git a/drivers/video/cyberfb.c b/drivers/video/cyberfb.c index 6882ebfb0c82..6183722fef2c 100644 --- a/drivers/video/cyberfb.c +++ b/drivers/video/cyberfb.c @@ -109,7 +109,10 @@ static void cv64_dump(void); #define arraysize(x) (sizeof(x)/sizeof(*(x))) -#define wb_64(reg,dat) (*((unsigned char volatile *)CyberRegs + reg) = dat) +#define wb_64(regs,reg,dat) (*(((volatile unsigned char *)regs) + reg) = dat) +#define rb_64(regs, reg) (*(((volatile unsigned char *)regs) + reg)) + +#define ww_64(regs,reg,dat) (*((volatile unsigned short *)(regs + reg) = dat) struct cyberfb_par { struct fb_var_screeninfo var; @@ -129,58 +132,27 @@ static struct fb_info fb_info; /* -* Switch for Chipset Independency -*/ - -static struct fb_hwswitch { - -/* Initialisation */ - -int (*init)(void); - -/* Display Control */ - -int (*encode_fix)(struct fb_fix_screeninfo *fix, struct cyberfb_par *par); -int (*decode_var)(struct fb_var_screeninfo *var, struct cyberfb_par *par); -int (*encode_var)(struct fb_var_screeninfo *var, struct cyberfb_par *par); -int (*getcolreg)(u_int regno, u_int *red, u_int *green, u_int *blue, - u_int *transp, struct fb_info *info); -int (*setcolreg)(u_int regno, u_int red, u_int green, u_int blue, - u_int transp, struct fb_info *info); -void (*blank)(int blank); -} *fbhw; - - -/* -* Frame Buffer Name -*/ + * Frame Buffer Name + */ static char cyberfb_name[16] = "Cybervision"; /* -* Cybervision Graphics Board -*/ + * CyberVision Graphics Board + */ static unsigned char Cyber_colour_table [256][3]; -static unsigned long CyberMem; static unsigned long CyberSize; -static volatile char *CyberRegs; +static volatile unsigned char *CyberBase; +static volatile unsigned char *CyberMem; +static volatile unsigned char *CyberRegs; static unsigned long CyberMem_phys; static unsigned long CyberRegs_phys; -/* From cvision.c for cvision_core.c */ -static unsigned long cv64_mem; -static unsigned long cv64_fbmem; -static volatile char *cv64_regs; -static unsigned long cv64_size; -#if 0 -static int cvision_custom_mode = 0; -static int hbs, hbe, hss, hse, ht, vbs, vbe, vss, vse, vt; -#endif /* -* Predefined Video Modes -*/ + * Predefined Video Modes + */ static struct fb_videomode cyberfb_predefined[] __initdata = { { "640x480-8", { /* Default 8 BPP mode (cyber8) */ @@ -251,17 +223,18 @@ static struct fb_videomode cyberfb_predefined[] __initdata = { static int Cyberfb_inverse = 0; /* -* Some default modes -*/ + * Some default modes + */ #define CYBER8_DEFMODE (0) #define CYBER16_DEFMODE (1) static struct fb_var_screeninfo cyberfb_default; +static int cyberfb_usermode __initdata = 0; /* -* Interface used by the world -*/ + * Interface used by the world + */ void cyberfb_setup(char *options, int *ints); @@ -283,8 +256,8 @@ static int cyberfb_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg, int con, struct fb_info *info); /* -* Interface to the low level console driver -*/ + * Interface to the low level console driver + */ void cyberfb_init(void); static int Cyberfb_switch(int con, struct fb_info *info); @@ -292,16 +265,16 @@ static int Cyberfb_updatevar(int con, struct fb_info *info); static void Cyberfb_blank(int blank, struct fb_info *info); /* -* Text console acceleration -*/ + * Text console acceleration + */ #ifdef FBCON_HAS_CFB8 static struct display_switch fbcon_cyber8; #endif /* -* Accelerated Functions used by the low level console driver -*/ + * Accelerated Functions used by the low level console driver + */ static void Cyber_WaitQueue(u_short fifo); static void Cyber_WaitBlit(void); @@ -310,11 +283,13 @@ static void Cyber_BitBLT(u_short curx, u_short cury, u_short destx, u_short mode); static void Cyber_RectFill(u_short x, u_short y, u_short width, u_short height, u_short mode, u_short color); +#if 0 static void Cyber_MoveCursor(u_short x, u_short y); +#endif /* -* Hardware Specific Routines -*/ + * Hardware Specific Routines + */ static int Cyber_init(void); static int Cyber_encode_fix(struct fb_fix_screeninfo *fix, @@ -327,11 +302,10 @@ static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *transp, struct fb_info *info); static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info); -static void Cyber_blank(int blank); /* -* Internal routines -*/ + * Internal routines + */ static void cyberfb_get_par(struct cyberfb_par *par); static void cyberfb_set_par(struct cyberfb_par *par); @@ -342,7 +316,7 @@ static int get_video_mode(const char *name); /* For cvision_core.c */ static unsigned short cv64_compute_clock(unsigned long); -static int cv_has_4mb (volatile caddr_t); +static int cv_has_4mb (volatile unsigned char *); static void cv64_board_init (void); static void cv64_load_video_mode (struct fb_var_screeninfo *); @@ -351,16 +325,17 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *); /* -* Initialization -* -* Set the default video mode for this chipset. If a video mode was -* specified on the command line, it will override the default mode. -*/ + * Initialization + * + * Set the default video mode for this chipset. If a video mode was + * specified on the command line, it will override the default mode. + */ static int Cyber_init(void) { + volatile unsigned char *regs = CyberRegs; + volatile unsigned long *CursorBase; int i; - volatile u_long *CursorBase; DPRINTK("ENTER\n"); /* Init local cmap as greyscale levels */ @@ -371,27 +346,25 @@ static int Cyber_init(void) } /* Initialize the board and determine fbmem size */ - cv64_board_init (); + cv64_board_init(); #ifdef CYBERFBDEBUG DPRINTK("Register state after initing board\n"); cv64_dump(); #endif /* Clear framebuffer memory */ DPRINTK("Clear framebuffer memory\n"); - memset ((char *) cv64_fbmem, 0, cv64_size); + memset ((char *)CyberMem, 0, CyberSize); /* Disable hardware cursor */ DPRINTK("Disable HW cursor\n"); - wb_64(S3_CRTC_ADR, S3_REG_LOCK2); - wb_64(S3_CRTC_DATA, 0xa0); - wb_64(S3_CRTC_ADR, S3_HGC_MODE); - wb_64(S3_CRTC_DATA, 0x00); - wb_64(S3_CRTC_ADR, S3_HWGC_DX); - wb_64(S3_CRTC_DATA, 0x00); - wb_64(S3_CRTC_ADR, S3_HWGC_DY); - wb_64(S3_CRTC_DATA, 0x00); - - CyberSize = cv64_size; + wb_64(regs, S3_CRTC_ADR, S3_REG_LOCK2); + wb_64(regs, S3_CRTC_DATA, 0xa0); + wb_64(regs, S3_CRTC_ADR, S3_HGC_MODE); + wb_64(regs, S3_CRTC_DATA, 0x00); + wb_64(regs, S3_CRTC_ADR, S3_HWGC_DX); + wb_64(regs, S3_CRTC_DATA, 0x00); + wb_64(regs, S3_CRTC_ADR, S3_HWGC_DY); + wb_64(regs, S3_CRTC_DATA, 0x00); /* Initialize hardware cursor */ DPRINTK("Init HW cursor\n"); @@ -420,9 +393,9 @@ static int Cyber_init(void) /* -* This function should fill in the `fix' structure based on the -* values in the `par' structure. -*/ + * This function should fill in the `fix' structure based on the + * values in the `par' structure. + */ static int Cyber_encode_fix(struct fb_fix_screeninfo *fix, struct cyberfb_par *par) @@ -546,19 +519,21 @@ static int Cyber_encode_var(struct fb_var_screeninfo *var, /* -* Set a single color register. Return != 0 for invalid regno. -*/ + * Set a single color register. Return != 0 for invalid regno. + */ static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info) { + volatile unsigned char *regs = CyberRegs; + /*DPRINTK("ENTER\n");*/ if (regno > 255) { DPRINTK("EXIT - Register # > 255\n"); return (1); } - wb_64(0x3c8, (unsigned char) regno); + wb_64(regs, 0x3c8, (unsigned char) regno); red >>= 10; green >>= 10; @@ -568,9 +543,9 @@ static int Cyber_setcolreg(u_int regno, u_int red, u_int green, u_int blue, Cyber_colour_table [regno][1] = green; Cyber_colour_table [regno][2] = blue; - wb_64(0x3c9, red); - wb_64(0x3c9, green); - wb_64(0x3c9, blue); + wb_64(regs, 0x3c9, red); + wb_64(regs, 0x3c9, green); + wb_64(regs, 0x3c9, blue); /*DPRINTK("EXIT\n");*/ return (0); @@ -611,29 +586,30 @@ static int Cyber_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, * 0 = restore fb cmap from local cmap */ -void Cyber_blank(int blank) +void Cyberfb_blank(int blank, struct fb_info *info) { + volatile unsigned char *regs = CyberRegs; int i; DPRINTK("ENTER\n"); #if 0 /* Blank by turning gfx off */ - gfx_on_off (1, cv64_regs); + gfx_on_off (1, regs); #else if (blank) { for (i = 0; i < 256; i++) { - wb_64(0x3c8, (unsigned char) i); + wb_64(regs, 0x3c8, (unsigned char) i); /* ARB Pale red to detect this blanking method */ - wb_64(0x3c9, 48); - wb_64(0x3c9, 0); - wb_64(0x3c9, 0); + wb_64(regs, 0x3c9, 48); + wb_64(regs, 0x3c9, 0); + wb_64(regs, 0x3c9, 0); } } else { for (i = 0; i < 256; i++) { - wb_64(0x3c8, (unsigned char) i); - wb_64(0x3c9, Cyber_colour_table[i][0]); - wb_64(0x3c9, Cyber_colour_table[i][1]); - wb_64(0x3c9, Cyber_colour_table[i][2]); + wb_64(regs, 0x3c8, (unsigned char) i); + wb_64(regs, 0x3c9, Cyber_colour_table[i][0]); + wb_64(regs, 0x3c9, Cyber_colour_table[i][1]); + wb_64(regs, 0x3c9, Cyber_colour_table[i][2]); } } #endif @@ -646,13 +622,12 @@ void Cyber_blank(int blank) */ static void Cyber_WaitQueue (u_short fifo) { - u_short status; + unsigned short status; DPRINTK("ENTER\n"); do { status = *((u_short volatile *)(CyberRegs + S3_GP_STAT)); - } - while (status & fifo); + } while (status & fifo); DPRINTK("EXIT\n"); } @@ -661,13 +636,12 @@ static void Cyber_WaitQueue (u_short fifo) */ static void Cyber_WaitBlit (void) { - u_short status; + unsigned short status; DPRINTK("ENTER\n"); do { status = *((u_short volatile *)(CyberRegs + S3_GP_STAT)); - } - while (status & S3_HDW_BUSY); + } while (status & S3_HDW_BUSY); DPRINTK("EXIT\n"); } @@ -678,6 +652,7 @@ static void Cyber_BitBLT (u_short curx, u_short cury, u_short destx, u_short desty, u_short width, u_short height, u_short mode) { + volatile unsigned char *regs = CyberRegs; u_short blitcmd = S3_BITBLT; DPRINTK("ENTER\n"); @@ -699,19 +674,19 @@ static void Cyber_BitBLT (u_short curx, u_short cury, u_short destx, Cyber_WaitQueue (0x8000); - *((u_short volatile *)(CyberRegs + S3_PIXEL_CNTL)) = 0xa000; - *((u_short volatile *)(CyberRegs + S3_FRGD_MIX)) = (0x0060 | mode); + *((u_short volatile *)(regs + S3_PIXEL_CNTL)) = 0xa000; + *((u_short volatile *)(regs + S3_FRGD_MIX)) = (0x0060 | mode); - *((u_short volatile *)(CyberRegs + S3_CUR_X)) = curx; - *((u_short volatile *)(CyberRegs + S3_CUR_Y)) = cury; + *((u_short volatile *)(regs + S3_CUR_X)) = curx; + *((u_short volatile *)(regs + S3_CUR_Y)) = cury; - *((u_short volatile *)(CyberRegs + S3_DESTX_DIASTP)) = destx; - *((u_short volatile *)(CyberRegs + S3_DESTY_AXSTP)) = desty; + *((u_short volatile *)(regs + S3_DESTX_DIASTP)) = destx; + *((u_short volatile *)(regs + S3_DESTY_AXSTP)) = desty; - *((u_short volatile *)(CyberRegs + S3_MIN_AXIS_PCNT)) = height - 1; - *((u_short volatile *)(CyberRegs + S3_MAJ_AXIS_PCNT)) = width - 1; + *((u_short volatile *)(regs + S3_MIN_AXIS_PCNT)) = height - 1; + *((u_short volatile *)(regs + S3_MAJ_AXIS_PCNT)) = width - 1; - *((u_short volatile *)(CyberRegs + S3_CMD)) = blitcmd; + *((u_short volatile *)(regs + S3_CMD)) = blitcmd; DPRINTK("EXIT\n"); } @@ -721,56 +696,52 @@ static void Cyber_BitBLT (u_short curx, u_short cury, u_short destx, static void Cyber_RectFill (u_short x, u_short y, u_short width, u_short height, u_short mode, u_short color) { + volatile unsigned char *regs = CyberRegs; u_short blitcmd = S3_FILLEDRECT; DPRINTK("ENTER\n"); Cyber_WaitQueue (0x8000); - *((u_short volatile *)(CyberRegs + S3_PIXEL_CNTL)) = 0xa000; - *((u_short volatile *)(CyberRegs + S3_FRGD_MIX)) = (0x0020 | mode); + *((u_short volatile *)(regs + S3_PIXEL_CNTL)) = 0xa000; + *((u_short volatile *)(regs + S3_FRGD_MIX)) = (0x0020 | mode); - *((u_short volatile *)(CyberRegs + S3_MULT_MISC)) = 0xe000; - *((u_short volatile *)(CyberRegs + S3_FRGD_COLOR)) = color; + *((u_short volatile *)(regs + S3_MULT_MISC)) = 0xe000; + *((u_short volatile *)(regs + S3_FRGD_COLOR)) = color; - *((u_short volatile *)(CyberRegs + S3_CUR_X)) = x; - *((u_short volatile *)(CyberRegs + S3_CUR_Y)) = y; + *((u_short volatile *)(regs + S3_CUR_X)) = x; + *((u_short volatile *)(regs + S3_CUR_Y)) = y; - *((u_short volatile *)(CyberRegs + S3_MIN_AXIS_PCNT)) = height - 1; - *((u_short volatile *)(CyberRegs + S3_MAJ_AXIS_PCNT)) = width - 1; + *((u_short volatile *)(regs + S3_MIN_AXIS_PCNT)) = height - 1; + *((u_short volatile *)(regs + S3_MAJ_AXIS_PCNT)) = width - 1; - *((u_short volatile *)(CyberRegs + S3_CMD)) = blitcmd; + *((u_short volatile *)(regs + S3_CMD)) = blitcmd; DPRINTK("EXIT\n"); } + +#if 0 /************************************************************** * Move cursor to x, y */ static void Cyber_MoveCursor (u_short x, u_short y) { + volatile unsigned char *regs = CyberRegs; DPRINTK("ENTER\n"); - *(CyberRegs + S3_CRTC_ADR) = 0x39; - *(CyberRegs + S3_CRTC_DATA) = 0xa0; - - *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGX_H; - *(CyberRegs + S3_CRTC_DATA) = (char)((x & 0x0700) >> 8); - *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGX_L; - *(CyberRegs + S3_CRTC_DATA) = (char)(x & 0x00ff); - - *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGY_H; - *(CyberRegs + S3_CRTC_DATA) = (char)((y & 0x0700) >> 8); - *(CyberRegs + S3_CRTC_ADR) = S3_HWGC_ORGY_L; - *(CyberRegs + S3_CRTC_DATA) = (char)(y & 0x00ff); + *(regs + S3_CRTC_ADR) = 0x39; + *(regs + S3_CRTC_DATA) = 0xa0; + + *(regs + S3_CRTC_ADR) = S3_HWGC_ORGX_H; + *(regs + S3_CRTC_DATA) = (char)((x & 0x0700) >> 8); + *(regs + S3_CRTC_ADR) = S3_HWGC_ORGX_L; + *(regs + S3_CRTC_DATA) = (char)(x & 0x00ff); + + *(regs + S3_CRTC_ADR) = S3_HWGC_ORGY_H; + *(regs + S3_CRTC_DATA) = (char)((y & 0x0700) >> 8); + *(regs + S3_CRTC_ADR) = S3_HWGC_ORGY_L; + *(regs + S3_CRTC_DATA) = (char)(y & 0x00ff); DPRINTK("EXIT\n"); } - - -/* -------------------- Interfaces to hardware functions -------------------- */ - - -static struct fb_hwswitch Cyber_switch = { - Cyber_init, Cyber_encode_fix, Cyber_decode_var, Cyber_encode_var, - Cyber_getcolreg, Cyber_setcolreg, Cyber_blank -}; +#endif /* -------------------- Generic routines ---------------------------------- */ @@ -786,7 +757,7 @@ static void cyberfb_get_par(struct cyberfb_par *par) if (current_par_valid) { *par = current_par; } else { - fbhw->decode_var(&cyberfb_default, par); + Cyber_decode_var(&cyberfb_default, par); } DPRINTK("EXIT\n"); } @@ -819,14 +790,14 @@ static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive) struct cyberfb_par par; DPRINTK("ENTER\n"); - if ((err = fbhw->decode_var(var, &par))) { + if ((err = Cyber_decode_var(var, &par))) { DPRINTK("EXIT - decode_var failed\n"); return(err); } activate = var->activate; if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW && isactive) cyberfb_set_par(&par); - fbhw->encode_var(var, &par); + Cyber_encode_var(var, &par); var->activate = activate; cyber_set_video(var); @@ -844,11 +815,11 @@ static void do_install_cmap(int con, struct fb_info *info) } if (fb_display[con].cmap.len) { DPRINTK("Use console cmap\n"); - fb_set_cmap(&fb_display[con].cmap, 1, fbhw->setcolreg, info); + fb_set_cmap(&fb_display[con].cmap, 1, Cyber_setcolreg, info); } else { DPRINTK("Use default cmap\n"); fb_set_cmap(fb_default_cmap(1<setcolreg, info); + 1, Cyber_setcolreg, info); } DPRINTK("EXIT\n"); } @@ -889,10 +860,10 @@ static int cyberfb_get_fix(struct fb_fix_screeninfo *fix, int con, if (con == -1) { cyberfb_get_par(&par); } else { - error = fbhw->decode_var(&fb_display[con].var, &par); + error = Cyber_decode_var(&fb_display[con].var, &par); } DPRINTK("EXIT\n"); - return(error ? error : fbhw->encode_fix(fix, &par)); + return(error ? error : Cyber_encode_fix(fix, &par)); } @@ -909,7 +880,7 @@ static int cyberfb_get_var(struct fb_var_screeninfo *var, int con, DPRINTK("ENTER\n"); if (con == -1) { cyberfb_get_par(&par); - error = fbhw->encode_var(var, &par); + error = Cyber_encode_var(var, &par); disp.var = *var; /* ++Andre: don't know if this is the right place */ } else { *var = fb_display[con].var; @@ -934,7 +905,7 @@ static void cyberfb_set_disp(int con, struct fb_info *info) cyberfb_get_fix(&fix, con, info); if (con == -1) con = 0; - display->screen_base = phys_to_virt ((unsigned long) fix.smem_start); + display->screen_base = (unsigned char *)CyberMem; display->visual = fix.visual; display->type = fix.type; display->type_aux = fix.type_aux; @@ -1014,7 +985,7 @@ static int cyberfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, DPRINTK("ENTER\n"); if (con == currcon) { /* current console? */ DPRINTK("EXIT - console is current console\n"); - return(fb_get_cmap(cmap, kspc, fbhw->getcolreg, info)); + return(fb_get_cmap(cmap, kspc, Cyber_getcolreg, info)); } else if (fb_display[con].cmap.len) { /* non default colormap? */ DPRINTK("Use console cmap\n"); fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); @@ -1048,7 +1019,7 @@ static int cyberfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, } if (con == currcon) { /* current console? */ DPRINTK("EXIT - Current console\n"); - return(fb_set_cmap(cmap, kspc, fbhw->setcolreg, info)); + return(fb_set_cmap(cmap, kspc, Cyber_setcolreg, info)); } else { fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); } @@ -1066,7 +1037,7 @@ static int cyberfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, static int cyberfb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info) { - return(-EINVAL); + return -EINVAL; } @@ -1077,7 +1048,7 @@ static int cyberfb_pan_display(struct fb_var_screeninfo *var, int con, static int cyberfb_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg, int con, struct fb_info *info) { - return(-EINVAL); + return -EINVAL; } @@ -1109,8 +1080,10 @@ __initfunc(void cyberfb_setup(char *options, int *ints)) strcpy(fb_info.fontname, this_opt+5); } else if (!strcmp (this_opt, "cyber8")) { cyberfb_default = cyberfb_predefined[CYBER8_DEFMODE].var; + cyberfb_usermode = 1; } else if (!strcmp (this_opt, "cyber16")) { cyberfb_default = cyberfb_predefined[CYBER16_DEFMODE].var; + cyberfb_usermode = 1; } else get_video_mode(this_opt); } @@ -1146,14 +1119,12 @@ __initfunc(void cyberfb_init(void)) DPRINTK("board_addr=%08lx\n", board_addr); DPRINTK("board_size=%08lx\n", board_size); - cv64_mem = ioremap(board_addr, board_size); - cv64_regs = (volatile char *)(cv64_mem + 0x02000000); - cv64_fbmem = cv64_mem + 0x01400000; - DPRINTK("cv64_mem=%08lx cv64_regs=%08lx cv64_fbmem=%08lx\n", - cv64_mem, (long unsigned int)cv64_regs, cv64_fbmem); + CyberBase = ioremap(board_addr, board_size); + CyberRegs = CyberBase + 0x02000000; + CyberMem = CyberBase + 0x01400000; + DPRINTK("CyberBase=%08lx CyberRegs=%08lx CyberMem=%08lx\n", + CyberBase, (long unsigned int)CyberRegs, CyberMem); - CyberMem = cv64_fbmem; - CyberRegs = cv64_regs; CyberMem_phys = board_addr + 0x01400000; CyberRegs_phys = CyberMem_phys + 0x00c00000; DPRINTK("CyberMem=%08lx CyberRegs=%08lx\n", CyberMem, @@ -1164,8 +1135,6 @@ __initfunc(void cyberfb_init(void)) cv64_dump(); #endif - fbhw = &Cyber_switch; - strcpy(fb_info.modename, cyberfb_name); fb_info.changevar = NULL; fb_info.node = -1; @@ -1175,9 +1144,14 @@ __initfunc(void cyberfb_init(void)) fb_info.updatevar = &Cyberfb_updatevar; fb_info.blank = &Cyberfb_blank; - fbhw->init(); - fbhw->decode_var(&cyberfb_default, &par); - fbhw->encode_var(&cyberfb_default, &par); + Cyber_init(); + /* ++Andre: set cyberfb default mode */ + if (!cyberfb_usermode) { + cyberfb_default = cyberfb_predefined[CYBER8_DEFMODE].var; + DPRINTK("Use default cyber8 mode\n"); + } + Cyber_decode_var(&cyberfb_default, &par); + Cyber_encode_var(&cyberfb_default, &par); do_fb_set_var(&cyberfb_default, 1); cyberfb_get_var(&fb_display[0].var, -1, &fb_info); @@ -1203,7 +1177,7 @@ static int Cyberfb_switch(int con, struct fb_info *info) DPRINTK("ENTER\n"); /* Do we have to save the colormap? */ if (fb_display[currcon].cmap.len) { - fb_get_cmap(&fb_display[currcon].cmap, 1, fbhw->getcolreg, + fb_get_cmap(&fb_display[currcon].cmap, 1, Cyber_getcolreg, info); } @@ -1230,18 +1204,6 @@ static int Cyberfb_updatevar(int con, struct fb_info *info) } -/* - * Blank the display. - */ - -static void Cyberfb_blank(int blank, struct fb_info *info) -{ - DPRINTK("Enter\n"); - fbhw->blank(blank); - DPRINTK("Exit\n"); -} - - /* * Get a Video Mode */ @@ -1254,13 +1216,11 @@ __initfunc(static int get_video_mode(const char *name)) for (i = 0; i < NUM_TOTAL_MODES; i++) { if (!strcmp(name, cyberfb_predefined[i].name)) { cyberfb_default = cyberfb_predefined[i].var; + cyberfb_usermode = 1; DPRINTK("EXIT - Matched predefined mode\n"); return(i); } } - /* ++Andre: set cyberfb default mode */ - cyberfb_default = cyberfb_predefined[CYBER8_DEFMODE].var; - DPRINTK("EXIT - Use default cyber8 mode\n"); return(0); } @@ -1454,71 +1414,38 @@ unsigned char cvconscolors[16][3] = { /* background, foreground, hilite */ }; /* -------------------- Hardware specific routines ------------------------- */ -#if 0 -/* ARB Generates 100 usec delay */ -inline void __cv_delay (unsigned long usecs) -{ - int k; - - for (k = 0; k < 1000; k++) { - asm volatile ("nop"); - } -} -#endif - -/* Wait while Graphics Engine is busy */ -inline void GfxBusyWait (volatile caddr_t board) -{ - int test; - DPRINTK("ENTER\n"); - - do { - test = vgar16 (board, ECR_GP_STAT); - asm volatile ("nop"); - } while (test & (1 << 9)); - DPRINTK("EXIT\n"); -} - -/* Wait for any of the 8 Trio32 FIFOs to be free */ -inline void GfxFifoWait (volatile caddr_t board) -{ - int test; - DPRINTK("ENTER\n"); - - do { - test = vgar16 (board, ECR_GP_STAT); - } while (test & 0x0f); - DPRINTK("EXIT\n"); -} /* Read Attribute Controller Register=idx */ -inline unsigned char RAttr (volatile caddr_t board, short idx) +inline unsigned char RAttr (volatile unsigned char *regs, short idx) { - vgaw (board, ACT_ADDRESS_W, idx); + wb_64 (regs, ACT_ADDRESS_W, idx); + mb(); udelay(100); - /* __cv_delay (0); */ - return (vgar (board, ACT_ADDRESS_R)); + return (rb_64(regs, ACT_ADDRESS_R)); } /* Read Sequencer Register=idx */ -inline unsigned char RSeq (volatile caddr_t board, short idx) +inline unsigned char RSeq (volatile unsigned char *regs, short idx) { - vgaw (board, SEQ_ADDRESS, idx); - return (vgar (board, SEQ_ADDRESS_R)); + wb_64 (regs, SEQ_ADDRESS, idx); + mb(); + return (rb_64(regs, SEQ_ADDRESS_R)); } /* Read CRT Controller Register=idx */ -inline unsigned char RCrt (volatile caddr_t board, short idx) +inline unsigned char RCrt (volatile unsigned char *regs, short idx) { - vgaw (board, CRT_ADDRESS, idx); - return (vgar (board, CRT_ADDRESS_R)); + wb_64 (regs, CRT_ADDRESS, idx); + mb(); + return (rb_64(regs, CRT_ADDRESS_R)); } /* Read Graphics Controller Register=idx */ -inline unsigned char RGfx (volatile caddr_t board, short idx) +inline unsigned char RGfx (volatile unsigned char *regs, short idx) { - vgaw (board, GCT_ADDRESS, idx); - return (vgar (board, GCT_ADDRESS_R)); + wb_64 (regs, GCT_ADDRESS, idx); + mb(); + return (rb_64(regs, GCT_ADDRESS_R)); } /* @@ -1526,13 +1453,13 @@ inline unsigned char RGfx (volatile caddr_t board, short idx) */ inline void cv64_write_port (unsigned short bits, - volatile unsigned char *board) + volatile unsigned char *base) { volatile unsigned char *addr; static unsigned char cvportbits = 0; /* Mirror port bits here */ DPRINTK("ENTER\n"); - addr = board + 0x40001; + addr = base + 0x40001; if (bits & 0x8000) { cvportbits |= bits & 0xff; /* Set bits */ DPRINTK("Set bits: %04x\n", bits); @@ -1542,7 +1469,7 @@ inline void cv64_write_port (unsigned short bits, cvportbits &= bits; /* Clear bits */ DPRINTK("Clear bits: %04x\n", bits); } - + *addr = cvportbits; DPRINTK("EXIT\n"); } @@ -1572,7 +1499,7 @@ inline void cvscreen (int toggle, volatile unsigned char *board) /* Control screen display */ /* toggle: 0 = on, 1 = off */ /* board = registerbase */ -inline void gfx_on_off (int toggle, volatile unsigned char *board) +inline void gfx_on_off(int toggle, volatile unsigned char *regs) { int r; DPRINTK("ENTER\n"); @@ -1581,10 +1508,10 @@ inline void gfx_on_off (int toggle, volatile unsigned char *board) toggle = toggle << 5; DPRINTK("Turn display %s\n", (toggle ? "off" : "on")); - r = (int) RSeq ((volatile caddr_t) board, SEQ_ID_CLOCKING_MODE); + r = (int) RSeq(regs, SEQ_ID_CLOCKING_MODE); r &= 0xdf; /* Set bit 5 to 0 */ - WSeq (board, SEQ_ID_CLOCKING_MODE, r | toggle); + WSeq (regs, SEQ_ID_CLOCKING_MODE, r | toggle); DPRINTK("EXIT\n"); } @@ -1640,11 +1567,11 @@ static unsigned short cv64_compute_clock(unsigned long freq) return (erg); } -static int cv_has_4mb (volatile caddr_t fb) +static int cv_has_4mb (volatile unsigned char *fb) { volatile unsigned long *tr, *tw; DPRINTK("ENTER\n"); - + /* write patterns in memory and test if they can be read */ tw = (volatile unsigned long *) fb; tr = (volatile unsigned long *) (fb + 0x02000000); @@ -1655,41 +1582,42 @@ static int cv_has_4mb (volatile caddr_t fb) DPRINTK("EXIT - <4MB\n"); return (0); } - + /* upper memory region */ tw = (volatile unsigned long *) (fb + 0x00200000); tr = (volatile unsigned long *) (fb + 0x02200000); - + *tw = 0x87654321; - + if (*tr != 0x87654321) { DPRINTK("EXIT - <4MB\n"); return (0); } - + *tw = 0xAAAAAAAA; - + if (*tr != 0xAAAAAAAA) { DPRINTK("EXIT - <4MB\n"); return (0); } - + *tw = 0x55555555; - + if (*tr != 0x55555555) { DPRINTK("EXIT - <4MB\n"); return (0); } - + DPRINTK("EXIT\n"); return (1); } static void cv64_board_init (void) { + volatile unsigned char *regs = CyberRegs; int i; - unsigned char test; unsigned int clockpar; + unsigned char test; DPRINTK("ENTER\n"); @@ -1698,259 +1626,252 @@ static void cv64_board_init (void) */ /* Reset board */ for (i = 0; i < 6; i++) { - cv64_write_port (0xff, (volatile unsigned char *) cv64_mem); + cv64_write_port (0xff, CyberBase); } /* Return to operational mode */ - cv64_write_port (0x8004, (volatile unsigned char *) cv64_mem); + cv64_write_port (0x8004, CyberBase); - /* - * Generic (?) S3 chip wakeup - */ - /* Disable I/O & memory decoders, video in setup mode */ - vgaw (cv64_regs, SREG_VIDEO_SUBS_ENABLE, 0x10); - /* Video responds to cmds, addrs & data */ - vgaw (cv64_regs, SREG_OPTION_SELECT, 0x1); - /* Enable I/O & memory decoders, video in operational mode */ - vgaw (cv64_regs, SREG_VIDEO_SUBS_ENABLE, 0x8); - /* VGA color emulation, enable cpu access to display mem */ - vgaw (cv64_regs, GREG_MISC_OUTPUT_W, 0x03); - /* Unlock S3 VGA regs */ - WCrt (cv64_regs, CRT_ID_REGISTER_LOCK_1, 0x48); - /* Unlock system control & extension registers */ - WCrt (cv64_regs, CRT_ID_REGISTER_LOCK_2, 0xA5); + /* + * Generic (?) S3 chip wakeup + */ + /* Disable I/O & memory decoders, video in setup mode */ + wb_64 (regs, SREG_VIDEO_SUBS_ENABLE, 0x10); + /* Video responds to cmds, addrs & data */ + wb_64 (regs, SREG_OPTION_SELECT, 0x1); + /* Enable I/O & memory decoders, video in operational mode */ + wb_64 (regs, SREG_VIDEO_SUBS_ENABLE, 0x8); + /* VGA color emulation, enable cpu access to display mem */ + wb_64 (regs, GREG_MISC_OUTPUT_W, 0x03); + /* Unlock S3 VGA regs */ + WCrt (regs, CRT_ID_REGISTER_LOCK_1, 0x48); + /* Unlock system control & extension registers */ + WCrt (regs, CRT_ID_REGISTER_LOCK_2, 0xA5); /* GRF - Enable interrupts */ - /* Enable enhanced regs access, Ready cntl 0 wait states */ - test = RCrt (cv64_regs, CRT_ID_SYSTEM_CONFIG); - test = test | 0x01; /* enable enhaced register access */ - test = test & 0xEF; /* clear bit 4, 0 wait state */ - WCrt (cv64_regs, CRT_ID_SYSTEM_CONFIG, test); - /* - * bit 0=1: Enable enhaced mode functions - * bit 2=0: Enhanced mode 8+ bits/pixel - * bit 4=1: Enable linear addressing - * bit 5=1: Enable MMIO - */ - vgaw (cv64_regs, ECR_ADV_FUNC_CNTL, 0x31); - /* - * bit 0=1: Color emulation - * bit 1=1: Enable CPU access to display memory - * bit 5=1: Select high 64K memory page - */ + /* Enable enhanced regs access, Ready cntl 0 wait states */ + test = RCrt (regs, CRT_ID_SYSTEM_CONFIG); + test = test | 0x01; /* enable enhanced register access */ + test = test & 0xEF; /* clear bit 4, 0 wait state */ + WCrt (regs, CRT_ID_SYSTEM_CONFIG, test); + /* + * bit 0=1: Enable enhaced mode functions + * bit 2=0: Enhanced mode 8+ bits/pixel + * bit 4=1: Enable linear addressing + * bit 5=1: Enable MMIO + */ + wb_64 (regs, ECR_ADV_FUNC_CNTL, 0x31); + /* + * bit 0=1: Color emulation + * bit 1=1: Enable CPU access to display memory + * bit 5=1: Select high 64K memory page + */ /* GRF - 0xE3 */ - vgaw (cv64_regs, GREG_MISC_OUTPUT_W, 0x23); - - /* Cpu base addr */ - WCrt (cv64_regs, CRT_ID_EXT_SYS_CNTL_4, 0x0); - - /* Reset. This does nothing on Trio, but standard VGA practice */ - /* WSeq (cv64_regs, SEQ_ID_RESET, 0x03); */ - /* Character clocks 8 dots wide */ - WSeq (cv64_regs, SEQ_ID_CLOCKING_MODE, 0x01); - /* Enable cpu write to all color planes */ - WSeq (cv64_regs, SEQ_ID_MAP_MASK, 0x0F); - /* Font table in 1st 8k of plane 2, font A=B disables swtich */ - WSeq (cv64_regs, SEQ_ID_CHAR_MAP_SELECT, 0x0); - /* Allow mem access to 256kb */ - WSeq (cv64_regs, SEQ_ID_MEMORY_MODE, 0x2); - /* Unlock S3 extensions to VGA Sequencer regs */ - WSeq (cv64_regs, SEQ_ID_UNLOCK_EXT, 0x6); - - /* Enable 4MB fast page mode */ - test = RSeq (cv64_regs, SEQ_ID_BUS_REQ_CNTL); - test = test | 1 << 6; - WSeq (cv64_regs, SEQ_ID_BUS_REQ_CNTL, test); - - /* Faster LUT write: 1 DCLK LUT write cycle, RAMDAC clk doubled */ - WSeq (cv64_regs, SEQ_ID_RAMDAC_CNTL, 0xC0); - - /* Clear immediate clock load bit */ - test = RSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2); - test = test & 0xDF; - /* If > 55MHz, enable 2 cycle memory write */ - if (cv64_memclk >= 55000000) { - test |= 0x80; - } - WSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2, test); - - /* Set MCLK value */ - clockpar = cv64_compute_clock (cv64_memclk); - test = (clockpar & 0xFF00) >> 8; - WSeq (cv64_regs, SEQ_ID_MCLK_HI, test); - test = clockpar & 0xFF; - WSeq (cv64_regs, SEQ_ID_MCLK_LO, test); - - /* Chip rev specific: Not in my Trio manual!!! */ - if (RCrt (cv64_regs, CRT_ID_REVISION) == 0x10) - WSeq (cv64_regs, SEQ_ID_MORE_MAGIC, test); - - /* We now load an 25 MHz, 31kHz, 640x480 standard VGA Mode. */ - - /* Set DCLK value */ - WSeq (cv64_regs, SEQ_ID_DCLK_HI, 0x13); - WSeq (cv64_regs, SEQ_ID_DCLK_LO, 0x41); - - /* Load DCLK (and MCLK?) immediately */ - test = RSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2); - test = test | 0x22; - WSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2, test); - - /* Enable loading of DCLK */ - test = vgar (cv64_regs, GREG_MISC_OUTPUT_R); - test = test | 0x0C; - vgaw (cv64_regs, GREG_MISC_OUTPUT_W, test); - - /* Turn off immediate xCLK load */ - WSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2, 0x2); - - /* Horizontal character clock counts */ - /* 8 LSB of 9 bits = total line - 5 */ - WCrt (cv64_regs, CRT_ID_HOR_TOTAL, 0x5F); - /* Active display line */ - WCrt (cv64_regs, CRT_ID_HOR_DISP_ENA_END, 0x4F); - /* Blank assertion start */ - WCrt (cv64_regs, CRT_ID_START_HOR_BLANK, 0x50); - /* Blank assertion end */ - WCrt (cv64_regs, CRT_ID_END_HOR_BLANK, 0x82); - /* HSYNC assertion start */ - WCrt (cv64_regs, CRT_ID_START_HOR_RETR, 0x54); - /* HSYNC assertion end */ - WCrt (cv64_regs, CRT_ID_END_HOR_RETR, 0x80); - WCrt (cv64_regs, CRT_ID_VER_TOTAL, 0xBF); - WCrt (cv64_regs, CRT_ID_OVERFLOW, 0x1F); - WCrt (cv64_regs, CRT_ID_PRESET_ROW_SCAN, 0x0); - WCrt (cv64_regs, CRT_ID_MAX_SCAN_LINE, 0x40); - WCrt (cv64_regs, CRT_ID_CURSOR_START, 0x00); - WCrt (cv64_regs, CRT_ID_CURSOR_END, 0x00); - WCrt (cv64_regs, CRT_ID_START_ADDR_HIGH, 0x00); - WCrt (cv64_regs, CRT_ID_START_ADDR_LOW, 0x00); - WCrt (cv64_regs, CRT_ID_CURSOR_LOC_HIGH, 0x00); - WCrt (cv64_regs, CRT_ID_CURSOR_LOC_LOW, 0x00); - WCrt (cv64_regs, CRT_ID_START_VER_RETR, 0x9C); - WCrt (cv64_regs, CRT_ID_END_VER_RETR, 0x0E); - WCrt (cv64_regs, CRT_ID_VER_DISP_ENA_END, 0x8F); - WCrt (cv64_regs, CRT_ID_SCREEN_OFFSET, 0x50); - WCrt (cv64_regs, CRT_ID_UNDERLINE_LOC, 0x00); - WCrt (cv64_regs, CRT_ID_START_VER_BLANK, 0x96); - WCrt (cv64_regs, CRT_ID_END_VER_BLANK, 0xB9); - WCrt (cv64_regs, CRT_ID_MODE_CONTROL, 0xE3); - WCrt (cv64_regs, CRT_ID_LINE_COMPARE, 0xFF); - WCrt (cv64_regs, CRT_ID_BACKWAD_COMP_3, 0x10); /* FIFO enabled */ - WCrt (cv64_regs, CRT_ID_MISC_1, 0x35); - WCrt (cv64_regs, CRT_ID_DISPLAY_FIFO, 0x5A); - WCrt (cv64_regs, CRT_ID_EXT_MEM_CNTL_2, 0x70); - WCrt (cv64_regs, CRT_ID_LAW_POS_LO, 0x40); - WCrt (cv64_regs, CRT_ID_EXT_MEM_CNTL_3, 0xFF); - - WGfx (cv64_regs, GCT_ID_SET_RESET, 0x0); - WGfx (cv64_regs, GCT_ID_ENABLE_SET_RESET, 0x0); - WGfx (cv64_regs, GCT_ID_COLOR_COMPARE, 0x0); - WGfx (cv64_regs, GCT_ID_DATA_ROTATE, 0x0); - WGfx (cv64_regs, GCT_ID_READ_MAP_SELECT, 0x0); - WGfx (cv64_regs, GCT_ID_GRAPHICS_MODE, 0x40); - WGfx (cv64_regs, GCT_ID_MISC, 0x01); - WGfx (cv64_regs, GCT_ID_COLOR_XCARE, 0x0F); - WGfx (cv64_regs, GCT_ID_BITMASK, 0xFF); - - /* Colors for text mode */ - for (i = 0; i < 0xf; i++) - WAttr (cv64_regs, i, i); - - WAttr (cv64_regs, ACT_ID_ATTR_MODE_CNTL, 0x41); - WAttr (cv64_regs, ACT_ID_OVERSCAN_COLOR, 0x01); - WAttr (cv64_regs, ACT_ID_COLOR_PLANE_ENA, 0x0F); - WAttr (cv64_regs, ACT_ID_HOR_PEL_PANNING, 0x0); - WAttr (cv64_regs, ACT_ID_COLOR_SELECT, 0x0); - - vgaw (cv64_regs, VDAC_MASK, 0xFF); - - *((unsigned long *) (cv64_regs + ECR_FRGD_COLOR)) = 0xFF; - *((unsigned long *) (cv64_regs + ECR_BKGD_COLOR)) = 0; - - /* Colors initially set to grayscale */ - - vgaw (cv64_regs, VDAC_ADDRESS_W, 0); - for (i = 255; i >= 0; i--) { - vgaw (cv64_regs, VDAC_DATA, i); - vgaw (cv64_regs, VDAC_DATA, i); - vgaw (cv64_regs, VDAC_DATA, i); - } + wb_64 (regs, GREG_MISC_OUTPUT_W, 0x23); + + /* Cpu base addr */ + WCrt (regs, CRT_ID_EXT_SYS_CNTL_4, 0x0); + + /* Reset. This does nothing on Trio, but standard VGA practice */ + /* WSeq (CyberRegs, SEQ_ID_RESET, 0x03); */ + /* Character clocks 8 dots wide */ + WSeq (regs, SEQ_ID_CLOCKING_MODE, 0x01); + /* Enable cpu write to all color planes */ + WSeq (regs, SEQ_ID_MAP_MASK, 0x0F); + /* Font table in 1st 8k of plane 2, font A=B disables swtich */ + WSeq (regs, SEQ_ID_CHAR_MAP_SELECT, 0x0); + /* Allow mem access to 256kb */ + WSeq (regs, SEQ_ID_MEMORY_MODE, 0x2); + /* Unlock S3 extensions to VGA Sequencer regs */ + WSeq (regs, SEQ_ID_UNLOCK_EXT, 0x6); + + /* Enable 4MB fast page mode */ + test = RSeq (regs, SEQ_ID_BUS_REQ_CNTL); + test = test | 1 << 6; + WSeq (regs, SEQ_ID_BUS_REQ_CNTL, test); + + /* Faster LUT write: 1 DCLK LUT write cycle, RAMDAC clk doubled */ + WSeq (regs, SEQ_ID_RAMDAC_CNTL, 0xC0); + + /* Clear immediate clock load bit */ + test = RSeq (regs, SEQ_ID_CLKSYN_CNTL_2); + test = test & 0xDF; + /* If > 55MHz, enable 2 cycle memory write */ + if (cv64_memclk >= 55000000) { + test |= 0x80; + } + WSeq (regs, SEQ_ID_CLKSYN_CNTL_2, test); + + /* Set MCLK value */ + clockpar = cv64_compute_clock (cv64_memclk); + test = (clockpar & 0xFF00) >> 8; + WSeq (regs, SEQ_ID_MCLK_HI, test); + test = clockpar & 0xFF; + WSeq (regs, SEQ_ID_MCLK_LO, test); + + /* Chip rev specific: Not in my Trio manual!!! */ + if (RCrt (regs, CRT_ID_REVISION) == 0x10) + WSeq (regs, SEQ_ID_MORE_MAGIC, test); + + /* We now load an 25 MHz, 31kHz, 640x480 standard VGA Mode. */ + + /* Set DCLK value */ + WSeq (regs, SEQ_ID_DCLK_HI, 0x13); + WSeq (regs, SEQ_ID_DCLK_LO, 0x41); + + /* Load DCLK (and MCLK?) immediately */ + test = RSeq (regs, SEQ_ID_CLKSYN_CNTL_2); + test = test | 0x22; + WSeq (regs, SEQ_ID_CLKSYN_CNTL_2, test); + + /* Enable loading of DCLK */ + test = rb_64(regs, GREG_MISC_OUTPUT_R); + test = test | 0x0C; + wb_64 (regs, GREG_MISC_OUTPUT_W, test); + + /* Turn off immediate xCLK load */ + WSeq (regs, SEQ_ID_CLKSYN_CNTL_2, 0x2); + + /* Horizontal character clock counts */ + /* 8 LSB of 9 bits = total line - 5 */ + WCrt (regs, CRT_ID_HOR_TOTAL, 0x5F); + /* Active display line */ + WCrt (regs, CRT_ID_HOR_DISP_ENA_END, 0x4F); + /* Blank assertion start */ + WCrt (regs, CRT_ID_START_HOR_BLANK, 0x50); + /* Blank assertion end */ + WCrt (regs, CRT_ID_END_HOR_BLANK, 0x82); + /* HSYNC assertion start */ + WCrt (regs, CRT_ID_START_HOR_RETR, 0x54); + /* HSYNC assertion end */ + WCrt (regs, CRT_ID_END_HOR_RETR, 0x80); + WCrt (regs, CRT_ID_VER_TOTAL, 0xBF); + WCrt (regs, CRT_ID_OVERFLOW, 0x1F); + WCrt (regs, CRT_ID_PRESET_ROW_SCAN, 0x0); + WCrt (regs, CRT_ID_MAX_SCAN_LINE, 0x40); + WCrt (regs, CRT_ID_CURSOR_START, 0x00); + WCrt (regs, CRT_ID_CURSOR_END, 0x00); + WCrt (regs, CRT_ID_START_ADDR_HIGH, 0x00); + WCrt (regs, CRT_ID_START_ADDR_LOW, 0x00); + WCrt (regs, CRT_ID_CURSOR_LOC_HIGH, 0x00); + WCrt (regs, CRT_ID_CURSOR_LOC_LOW, 0x00); + WCrt (regs, CRT_ID_START_VER_RETR, 0x9C); + WCrt (regs, CRT_ID_END_VER_RETR, 0x0E); + WCrt (regs, CRT_ID_VER_DISP_ENA_END, 0x8F); + WCrt (regs, CRT_ID_SCREEN_OFFSET, 0x50); + WCrt (regs, CRT_ID_UNDERLINE_LOC, 0x00); + WCrt (regs, CRT_ID_START_VER_BLANK, 0x96); + WCrt (regs, CRT_ID_END_VER_BLANK, 0xB9); + WCrt (regs, CRT_ID_MODE_CONTROL, 0xE3); + WCrt (regs, CRT_ID_LINE_COMPARE, 0xFF); + WCrt (regs, CRT_ID_BACKWAD_COMP_3, 0x10); /* FIFO enabled */ + WCrt (regs, CRT_ID_MISC_1, 0x35); + WCrt (regs, CRT_ID_DISPLAY_FIFO, 0x5A); + WCrt (regs, CRT_ID_EXT_MEM_CNTL_2, 0x70); + WCrt (regs, CRT_ID_LAW_POS_LO, 0x40); + WCrt (regs, CRT_ID_EXT_MEM_CNTL_3, 0xFF); + + WGfx (regs, GCT_ID_SET_RESET, 0x0); + WGfx (regs, GCT_ID_ENABLE_SET_RESET, 0x0); + WGfx (regs, GCT_ID_COLOR_COMPARE, 0x0); + WGfx (regs, GCT_ID_DATA_ROTATE, 0x0); + WGfx (regs, GCT_ID_READ_MAP_SELECT, 0x0); + WGfx (regs, GCT_ID_GRAPHICS_MODE, 0x40); + WGfx (regs, GCT_ID_MISC, 0x01); + WGfx (regs, GCT_ID_COLOR_XCARE, 0x0F); + WGfx (regs, GCT_ID_BITMASK, 0xFF); + + /* Colors for text mode */ + for (i = 0; i < 0xf; i++) + WAttr (regs, i, i); + + WAttr (regs, ACT_ID_ATTR_MODE_CNTL, 0x41); + WAttr (regs, ACT_ID_OVERSCAN_COLOR, 0x01); + WAttr (regs, ACT_ID_COLOR_PLANE_ENA, 0x0F); + WAttr (regs, ACT_ID_HOR_PEL_PANNING, 0x0); + WAttr (regs, ACT_ID_COLOR_SELECT, 0x0); + + wb_64 (regs, VDAC_MASK, 0xFF); + + *((unsigned long *) (regs + ECR_FRGD_COLOR)) = 0xFF; + *((unsigned long *) (regs + ECR_BKGD_COLOR)) = 0; + + /* Colors initially set to grayscale */ + + wb_64 (regs, VDAC_ADDRESS_W, 0); + for (i = 255; i >= 0; i--) { + wb_64(regs, VDAC_DATA, i); + wb_64(regs, VDAC_DATA, i); + wb_64(regs, VDAC_DATA, i); + } - /* GFx hardware cursor off */ - WCrt (cv64_regs, CRT_ID_HWGC_MODE, 0x00); + /* GFx hardware cursor off */ + WCrt (regs, CRT_ID_HWGC_MODE, 0x00); - /* Set first to 4MB, so test will work */ - WCrt (cv64_regs, CRT_ID_LAW_CNTL, 0x13); - /* Find "correct" size of fbmem of Z3 board */ - if (cv_has_4mb ((volatile caddr_t) cv64_fbmem)) { - cv64_size = 1024 * 1024 * 4; - WCrt (cv64_regs, CRT_ID_LAW_CNTL, 0x13); - DPRINTK("4MB board\n"); - } else { - cv64_size = 1024 * 1024 * 2; - WCrt (cv64_regs, CRT_ID_LAW_CNTL, 0x12); - DPRINTK("2MB board\n"); - } - - /* Initialize graphics engine */ - Cyber_WaitBlit(); - /* GfxBusyWait (cv64_regs); */ - vgaw16 (cv64_regs, ECR_FRGD_MIX, 0x27); - vgaw16 (cv64_regs, ECR_BKGD_MIX, 0x07); - vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0x1000); - udelay(200); - /* __cv_delay (200000); */ - vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0x2000); - Cyber_WaitBlit(); - /* GfxBusyWait (cv64_regs); */ - vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0x3FFF); - Cyber_WaitBlit(); - /* GfxBusyWait (cv64_regs); */ - udelay(200); - /* __cv_delay (200000); */ - vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0x4FFF); - Cyber_WaitBlit(); - /* GfxBusyWait (cv64_regs); */ - vgaw16 (cv64_regs, ECR_BITPLANE_WRITE_MASK, ~0); - Cyber_WaitBlit(); - /* GfxBusyWait (cv64_regs); */ - vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0xE000); - vgaw16 (cv64_regs, ECR_CURRENT_Y_POS2, 0x00); - vgaw16 (cv64_regs, ECR_CURRENT_X_POS2, 0x00); - vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0xA000); - vgaw16 (cv64_regs, ECR_DEST_Y__AX_STEP, 0x00); - vgaw16 (cv64_regs, ECR_DEST_Y2__AX_STEP2, 0x00); - vgaw16 (cv64_regs, ECR_DEST_X__DIA_STEP, 0x00); - vgaw16 (cv64_regs, ECR_DEST_X2__DIA_STEP2, 0x00); - vgaw16 (cv64_regs, ECR_SHORT_STROKE, 0x00); - vgaw16 (cv64_regs, ECR_DRAW_CMD, 0x01); - - Cyber_WaitBlit(); - /* GfxBusyWait (cv64_regs); */ - - vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0x4FFF); - vgaw16 (cv64_regs, ECR_BKGD_COLOR, 0x01); - vgaw16 (cv64_regs, ECR_FRGD_COLOR, 0x00); - - - /* Enable video display (set bit 5) */ + /* Set first to 4MB, so test will work */ + WCrt (regs, CRT_ID_LAW_CNTL, 0x13); + /* Find "correct" size of fbmem of Z3 board */ + if (cv_has_4mb (CyberMem)) { + CyberSize = 1024 * 1024 * 4; + WCrt (regs, CRT_ID_LAW_CNTL, 0x13); + DPRINTK("4MB board\n"); + } else { + CyberSize = 1024 * 1024 * 2; + WCrt (regs, CRT_ID_LAW_CNTL, 0x12); + DPRINTK("2MB board\n"); + } + + /* Initialize graphics engine */ + Cyber_WaitBlit(); + vgaw16 (regs, ECR_FRGD_MIX, 0x27); + vgaw16 (regs, ECR_BKGD_MIX, 0x07); + vgaw16 (regs, ECR_READ_REG_DATA, 0x1000); + udelay(200); + vgaw16 (regs, ECR_READ_REG_DATA, 0x2000); + Cyber_WaitBlit(); + vgaw16 (regs, ECR_READ_REG_DATA, 0x3FFF); + Cyber_WaitBlit(); + udelay(200); + vgaw16 (regs, ECR_READ_REG_DATA, 0x4FFF); + Cyber_WaitBlit(); + vgaw16 (regs, ECR_BITPLANE_WRITE_MASK, ~0); + Cyber_WaitBlit(); + vgaw16 (regs, ECR_READ_REG_DATA, 0xE000); + vgaw16 (regs, ECR_CURRENT_Y_POS2, 0x00); + vgaw16 (regs, ECR_CURRENT_X_POS2, 0x00); + vgaw16 (regs, ECR_READ_REG_DATA, 0xA000); + vgaw16 (regs, ECR_DEST_Y__AX_STEP, 0x00); + vgaw16 (regs, ECR_DEST_Y2__AX_STEP2, 0x00); + vgaw16 (regs, ECR_DEST_X__DIA_STEP, 0x00); + vgaw16 (regs, ECR_DEST_X2__DIA_STEP2, 0x00); + vgaw16 (regs, ECR_SHORT_STROKE, 0x00); + vgaw16 (regs, ECR_DRAW_CMD, 0x01); + + Cyber_WaitBlit(); + + vgaw16 (regs, ECR_READ_REG_DATA, 0x4FFF); + vgaw16 (regs, ECR_BKGD_COLOR, 0x01); + vgaw16 (regs, ECR_FRGD_COLOR, 0x00); + + + /* Enable video display (set bit 5) */ /* ARB - Would also seem to write to AR13. * May want to use parts of WAttr to set JUST bit 5 */ - WAttr (cv64_regs, 0x33, 0); + WAttr (regs, 0x33, 0); /* GRF - function code ended here */ - /* Turn gfx on again */ - gfx_on_off (0, cv64_regs); - - /* Pass-through */ - cvscreen (0, (volatile unsigned char *) cv64_mem); + /* Turn gfx on again */ + gfx_on_off (0, regs); - DPRINTK("EXIT\n"); + /* Pass-through */ + cvscreen (0, CyberBase); + + DPRINTK("EXIT\n"); } static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) { + volatile unsigned char *regs = CyberRegs; int fx, fy; unsigned short mnr; unsigned short HT, HDE, HBS, HBE, HSS, HSE, VDE, VBS, VBE, VSS, VSE, VT; @@ -1977,7 +1898,7 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) /* GRF - Disable interrupts */ - gfx_on_off (1, cv64_regs); + gfx_on_off (1, regs); switch (video_mode->bits_per_pixel) { case 15: @@ -2049,7 +1970,7 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) VT = yres + vfront + vsync + vback - 2; } - vgaw (cv64_regs, ECR_ADV_FUNC_CNTL, (TEXT ? 0x00 : 0x31)); + wb_64 (regs, ECR_ADV_FUNC_CNTL, (TEXT ? 0x00 : 0x31)); if (TEXT) HDE = ((video_mode->xres + fx - 1) / fx) - 1; @@ -2058,15 +1979,15 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) VDE = video_mode->yres - 1; - WCrt (cv64_regs, CRT_ID_HWGC_MODE, 0x00); - WCrt (cv64_regs, CRT_ID_EXT_DAC_CNTL, 0x00); + WCrt (regs, CRT_ID_HWGC_MODE, 0x00); + WCrt (regs, CRT_ID_EXT_DAC_CNTL, 0x00); - WSeq (cv64_regs, SEQ_ID_MEMORY_MODE, + WSeq (regs, SEQ_ID_MEMORY_MODE, (TEXT || (video_mode->bits_per_pixel == 1)) ? 0x06 : 0x0e); - WGfx (cv64_regs, GCT_ID_READ_MAP_SELECT, 0x00); - WSeq (cv64_regs, SEQ_ID_MAP_MASK, + WGfx (regs, GCT_ID_READ_MAP_SELECT, 0x00); + WSeq (regs, SEQ_ID_MAP_MASK, (video_mode->bits_per_pixel == 1) ? 0x01 : 0xFF); - WSeq (cv64_regs, SEQ_ID_CHAR_MAP_SELECT, 0x00); + WSeq (regs, SEQ_ID_CHAR_MAP_SELECT, 0x00); /* cv64_compute_clock accepts arguments in Hz */ /* pixclock is in ps ... convert to Hz */ @@ -2081,11 +2002,11 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) #endif mnr = cv64_compute_clock (freq); - WSeq (cv64_regs, SEQ_ID_DCLK_HI, ((mnr & 0xFF00) >> 8)); - WSeq (cv64_regs, SEQ_ID_DCLK_LO, (mnr & 0xFF)); + WSeq (regs, SEQ_ID_DCLK_HI, ((mnr & 0xFF00) >> 8)); + WSeq (regs, SEQ_ID_DCLK_LO, (mnr & 0xFF)); /* Load display parameters into board */ - WCrt (cv64_regs, CRT_ID_EXT_HOR_OVF, + WCrt (regs, CRT_ID_EXT_HOR_OVF, ((HT & 0x100) ? 0x01 : 0x00) | ((HDE & 0x100) ? 0x02 : 0x00) | ((HBS & 0x100) ? 0x04 : 0x00) | @@ -2095,7 +2016,7 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) (((HT-5) & 0x100) ? 0x40 : 0x00) ); - WCrt (cv64_regs, CRT_ID_EXT_VER_OVF, + WCrt (regs, CRT_ID_EXT_VER_OVF, 0x40 | ((VT & 0x400) ? 0x01 : 0x00) | ((VDE & 0x400) ? 0x02 : 0x00) | @@ -2103,18 +2024,18 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) ((VSS & 0x400) ? 0x10 : 0x00) ); - WCrt (cv64_regs, CRT_ID_HOR_TOTAL, HT); - WCrt (cv64_regs, CRT_ID_DISPLAY_FIFO, HT - 5); - WCrt (cv64_regs, CRT_ID_HOR_DISP_ENA_END, ((HDE >= HBS) ? (HBS - 1) : HDE)); - WCrt (cv64_regs, CRT_ID_START_HOR_BLANK, HBS); - WCrt (cv64_regs, CRT_ID_END_HOR_BLANK, ((HBE & 0x1F) | 0x80)); - WCrt (cv64_regs, CRT_ID_START_HOR_RETR, HSS); - WCrt (cv64_regs, CRT_ID_END_HOR_RETR, + WCrt (regs, CRT_ID_HOR_TOTAL, HT); + WCrt (regs, CRT_ID_DISPLAY_FIFO, HT - 5); + WCrt (regs, CRT_ID_HOR_DISP_ENA_END, ((HDE >= HBS) ? (HBS - 1) : HDE)); + WCrt (regs, CRT_ID_START_HOR_BLANK, HBS); + WCrt (regs, CRT_ID_END_HOR_BLANK, ((HBE & 0x1F) | 0x80)); + WCrt (regs, CRT_ID_START_HOR_RETR, HSS); + WCrt (regs, CRT_ID_END_HOR_RETR, (HSE & 0x1F) | ((HBE & 0x20) ? 0x80 : 0x00) ); - WCrt (cv64_regs, CRT_ID_VER_TOTAL, VT); - WCrt (cv64_regs, CRT_ID_OVERFLOW, + WCrt (regs, CRT_ID_VER_TOTAL, VT); + WCrt (regs, CRT_ID_OVERFLOW, 0x10 | ((VT & 0x100) ? 0x01 : 0x00) | ((VDE & 0x100) ? 0x02 : 0x00) | @@ -2124,65 +2045,65 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) ((VDE & 0x200) ? 0x40 : 0x00) | ((VSS & 0x200) ? 0x80 : 0x00) ); - WCrt (cv64_regs, CRT_ID_MAX_SCAN_LINE, + WCrt (regs, CRT_ID_MAX_SCAN_LINE, 0x40 | (DBLSCAN ? 0x80 : 0x00) | ((VBS & 0x200) ? 0x20 : 0x00) | (TEXT ? ((fy - 1) & 0x1F) : 0x00) ); - WCrt (cv64_regs, CRT_ID_MODE_CONTROL, 0xE3); + WCrt (regs, CRT_ID_MODE_CONTROL, 0xE3); /* Text cursor */ if (TEXT) { #if 1 - WCrt (cv64_regs, CRT_ID_CURSOR_START, (fy & 0x1f) - 2); - WCrt (cv64_regs, CRT_ID_CURSOR_END, (fy & 0x1F) - 1); + WCrt (regs, CRT_ID_CURSOR_START, (fy & 0x1f) - 2); + WCrt (regs, CRT_ID_CURSOR_END, (fy & 0x1F) - 1); #else - WCrt (cv64_regs, CRT_ID_CURSOR_START, 0x00); - WCrt (cv64_regs, CRT_ID_CURSOR_END, fy & 0x1F); + WCrt (regs, CRT_ID_CURSOR_START, 0x00); + WCrt (regs, CRT_ID_CURSOR_END, fy & 0x1F); #endif - WCrt (cv64_regs, CRT_ID_UNDERLINE_LOC, (fy - 1) & 0x1F); - WCrt (cv64_regs, CRT_ID_CURSOR_LOC_HIGH, 0x00); - WCrt (cv64_regs, CRT_ID_CURSOR_LOC_LOW, 0x00); + WCrt (regs, CRT_ID_UNDERLINE_LOC, (fy - 1) & 0x1F); + WCrt (regs, CRT_ID_CURSOR_LOC_HIGH, 0x00); + WCrt (regs, CRT_ID_CURSOR_LOC_LOW, 0x00); } - WCrt (cv64_regs, CRT_ID_START_ADDR_HIGH, 0x00); - WCrt (cv64_regs, CRT_ID_START_ADDR_LOW, 0x00); - WCrt (cv64_regs, CRT_ID_START_VER_RETR, VSS); - WCrt (cv64_regs, CRT_ID_END_VER_RETR, (VSE & 0x0F)); - WCrt (cv64_regs, CRT_ID_VER_DISP_ENA_END, VDE); - WCrt (cv64_regs, CRT_ID_START_VER_BLANK, VBS); - WCrt (cv64_regs, CRT_ID_END_VER_BLANK, VBE); - WCrt (cv64_regs, CRT_ID_LINE_COMPARE, 0xFF); - WCrt (cv64_regs, CRT_ID_LACE_RETR_START, HT / 2); - WCrt (cv64_regs, CRT_ID_LACE_CONTROL, (LACE ? 0x20 : 0x00)); - WGfx (cv64_regs, GCT_ID_GRAPHICS_MODE, + WCrt (regs, CRT_ID_START_ADDR_HIGH, 0x00); + WCrt (regs, CRT_ID_START_ADDR_LOW, 0x00); + WCrt (regs, CRT_ID_START_VER_RETR, VSS); + WCrt (regs, CRT_ID_END_VER_RETR, (VSE & 0x0F)); + WCrt (regs, CRT_ID_VER_DISP_ENA_END, VDE); + WCrt (regs, CRT_ID_START_VER_BLANK, VBS); + WCrt (regs, CRT_ID_END_VER_BLANK, VBE); + WCrt (regs, CRT_ID_LINE_COMPARE, 0xFF); + WCrt (regs, CRT_ID_LACE_RETR_START, HT / 2); + WCrt (regs, CRT_ID_LACE_CONTROL, (LACE ? 0x20 : 0x00)); + WGfx (regs, GCT_ID_GRAPHICS_MODE, ((TEXT || (video_mode->bits_per_pixel == 1)) ? 0x00 : 0x40)); - WGfx (cv64_regs, GCT_ID_MISC, (TEXT ? 0x04 : 0x01)); - WSeq (cv64_regs, SEQ_ID_MEMORY_MODE, + WGfx (regs, GCT_ID_MISC, (TEXT ? 0x04 : 0x01)); + WSeq (regs, SEQ_ID_MEMORY_MODE, ((TEXT || (video_mode->bits_per_pixel == 1)) ? 0x06 : 0x02)); - vgaw (cv64_regs, VDAC_MASK, 0xFF); + wb_64 (regs, VDAC_MASK, 0xFF); /* Blank border */ - test = RCrt (cv64_regs, CRT_ID_BACKWAD_COMP_2); - WCrt (cv64_regs, CRT_ID_BACKWAD_COMP_2, (test | 0x20)); + test = RCrt (regs, CRT_ID_BACKWAD_COMP_2); + WCrt (regs, CRT_ID_BACKWAD_COMP_2, (test | 0x20)); - sr15 = RSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2); + sr15 = RSeq (regs, SEQ_ID_CLKSYN_CNTL_2); sr15 &= 0xEF; - sr18 = RSeq (cv64_regs, SEQ_ID_RAMDAC_CNTL); + sr18 = RSeq (regs, SEQ_ID_RAMDAC_CNTL); sr18 &= 0x7F; clock_mode = 0x00; cr50 = 0x00; - test = RCrt (cv64_regs, CRT_ID_EXT_MISC_CNTL_2); + test = RCrt (regs, CRT_ID_EXT_MISC_CNTL_2); test &= 0xD; /* Clear roxxler byte-swapping... */ - cv64_write_port (0x0040, (volatile unsigned char *) cv64_mem); - cv64_write_port (0x0020, (volatile unsigned char *) cv64_mem); + cv64_write_port (0x0040, CyberBase); + cv64_write_port (0x0020, CyberBase); switch (video_mode->bits_per_pixel) { case 1: @@ -2201,14 +2122,14 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) break; case 15: - cv64_write_port (0x8020, (volatile unsigned char *) cv64_mem); + cv64_write_port (0x8020, CyberBase); clock_mode = 0x30; HDE = video_mode->xres / 4; cr50 |= 0x10; break; case 16: - cv64_write_port (0x8020, (volatile unsigned char *) cv64_mem); + cv64_write_port (0x8020, CyberBase); clock_mode = 0x50; HDE = video_mode->xres / 4; cr50 |= 0x10; @@ -2216,24 +2137,24 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) case 24: case 32: - cv64_write_port (0x8040, (volatile unsigned char *) cv64_mem); + cv64_write_port (0x8040, CyberBase); clock_mode = 0xD0; HDE = video_mode->xres / 2; cr50 |= 0x30; break; } - - WCrt (cv64_regs, CRT_ID_EXT_MISC_CNTL_2, clock_mode | test); - WSeq (cv64_regs, SEQ_ID_CLKSYN_CNTL_2, sr15); - WSeq (cv64_regs, SEQ_ID_RAMDAC_CNTL, sr18); - WCrt (cv64_regs, CRT_ID_SCREEN_OFFSET, HDE); - WCrt (cv64_regs, CRT_ID_MISC_1, (TEXT ? 0x05 : 0x35)); + WCrt (regs, CRT_ID_EXT_MISC_CNTL_2, clock_mode | test); + WSeq (regs, SEQ_ID_CLKSYN_CNTL_2, sr15); + WSeq (regs, SEQ_ID_RAMDAC_CNTL, sr18); + WCrt (regs, CRT_ID_SCREEN_OFFSET, HDE); + + WCrt (regs, CRT_ID_MISC_1, (TEXT ? 0x05 : 0x35)); - test = RCrt (cv64_regs, CRT_ID_EXT_SYS_CNTL_2); + test = RCrt (regs, CRT_ID_EXT_SYS_CNTL_2); test &= ~0x30; test |= (HDE >> 4) & 0x30; - WCrt (cv64_regs, CRT_ID_EXT_SYS_CNTL_2, test); + WCrt (regs, CRT_ID_EXT_SYS_CNTL_2, test); /* Set up graphics engine */ switch (video_mode->xres) { @@ -2265,17 +2186,14 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) break; } - WCrt (cv64_regs, CRT_ID_EXT_SYS_CNTL_1, cr50); + WCrt (regs, CRT_ID_EXT_SYS_CNTL_1, cr50); udelay(100); - /* __cv_delay (100000); */ - WAttr (cv64_regs, ACT_ID_ATTR_MODE_CNTL, (TEXT ? 0x08 : 0x41)); + WAttr (regs, ACT_ID_ATTR_MODE_CNTL, (TEXT ? 0x08 : 0x41)); udelay(100); - /* __cv_delay (100000); */ - WAttr (cv64_regs, ACT_ID_COLOR_PLANE_ENA, + WAttr (regs, ACT_ID_COLOR_PLANE_ENA, (video_mode->bits_per_pixel == 1) ? 0x01 : 0x0F); udelay(100); - /* __cv_delay (100000); */ tfillm = (96 * (cv64_memclk / 1000)) / 240000; @@ -2304,10 +2222,9 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) m = 0x18; n = 0xFF; - WCrt (cv64_regs, CRT_ID_EXT_MEM_CNTL_2, m); - WCrt (cv64_regs, CRT_ID_EXT_MEM_CNTL_3, n); + WCrt (regs, CRT_ID_EXT_MEM_CNTL_2, m); + WCrt (regs, CRT_ID_EXT_MEM_CNTL_3, n); udelay(10); - /* __cv_delay (10000); */ /* Text initialization */ @@ -2317,21 +2234,21 @@ static void cv64_load_video_mode (struct fb_var_screeninfo *video_mode) if (CONSOLE) { int i; - vgaw (cv64_regs, VDAC_ADDRESS_W, 0); + wb_64 (regs, VDAC_ADDRESS_W, 0); for (i = 0; i < 4; i++) { - vgaw (cv64_regs, VDAC_DATA, cvconscolors [i][0]); - vgaw (cv64_regs, VDAC_DATA, cvconscolors [i][1]); - vgaw (cv64_regs, VDAC_DATA, cvconscolors [i][2]); + wb_64 (regs, VDAC_DATA, cvconscolors [i][0]); + wb_64 (regs, VDAC_DATA, cvconscolors [i][1]); + wb_64 (regs, VDAC_DATA, cvconscolors [i][2]); } } - WAttr (cv64_regs, 0x33, 0); + WAttr (regs, 0x33, 0); /* Turn gfx on again */ - gfx_on_off (0, (volatile unsigned char *) cv64_regs); + gfx_on_off (0, (volatile unsigned char *) regs); /* Pass-through */ - cvscreen (0, (volatile unsigned char *) cv64_mem); + cvscreen (0, CyberBase); DPRINTK("EXIT\n"); } @@ -2339,6 +2256,7 @@ DPRINTK("EXIT\n"); void cvision_bitblt (u_short sx, u_short sy, u_short dx, u_short dy, u_short w, u_short h) { + volatile unsigned char *regs = CyberRegs; unsigned short drawdir = 0; DPRINTK("ENTER\n"); @@ -2357,37 +2275,36 @@ void cvision_bitblt (u_short sx, u_short sy, u_short dx, u_short dy, } Cyber_WaitBlit(); - /* GfxBusyWait (cv64_regs); */ - vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0xA000); - vgaw16 (cv64_regs, ECR_BKGD_MIX, 0x7); - vgaw16 (cv64_regs, ECR_FRGD_MIX, 0x67); - vgaw16 (cv64_regs, ECR_BKGD_COLOR, 0x0); - vgaw16 (cv64_regs, ECR_FRGD_COLOR, 0x1); - vgaw16 (cv64_regs, ECR_BITPLANE_READ_MASK, 0x1); - vgaw16 (cv64_regs, ECR_BITPLANE_WRITE_MASK, 0xFFF); - vgaw16 (cv64_regs, ECR_CURRENT_Y_POS, sy); - vgaw16 (cv64_regs, ECR_CURRENT_X_POS, sx); - vgaw16 (cv64_regs, ECR_DEST_Y__AX_STEP, dy); - vgaw16 (cv64_regs, ECR_DEST_X__DIA_STEP, dx); - vgaw16 (cv64_regs, ECR_READ_REG_DATA, h - 1); - vgaw16 (cv64_regs, ECR_MAJ_AXIS_PIX_CNT, w - 1); - vgaw16 (cv64_regs, ECR_DRAW_CMD, 0xC051 | drawdir); + vgaw16 (regs, ECR_READ_REG_DATA, 0xA000); + vgaw16 (regs, ECR_BKGD_MIX, 0x7); + vgaw16 (regs, ECR_FRGD_MIX, 0x67); + vgaw16 (regs, ECR_BKGD_COLOR, 0x0); + vgaw16 (regs, ECR_FRGD_COLOR, 0x1); + vgaw16 (regs, ECR_BITPLANE_READ_MASK, 0x1); + vgaw16 (regs, ECR_BITPLANE_WRITE_MASK, 0xFFF); + vgaw16 (regs, ECR_CURRENT_Y_POS, sy); + vgaw16 (regs, ECR_CURRENT_X_POS, sx); + vgaw16 (regs, ECR_DEST_Y__AX_STEP, dy); + vgaw16 (regs, ECR_DEST_X__DIA_STEP, dx); + vgaw16 (regs, ECR_READ_REG_DATA, h - 1); + vgaw16 (regs, ECR_MAJ_AXIS_PIX_CNT, w - 1); + vgaw16 (regs, ECR_DRAW_CMD, 0xC051 | drawdir); DPRINTK("EXIT\n"); } void cvision_clear (u_short dx, u_short dy, u_short w, u_short h, u_short bg) { + volatile unsigned char *regs = CyberRegs; DPRINTK("ENTER\n"); Cyber_WaitBlit(); - /* GfxBusyWait (cv64_regs); */ - vgaw16 (cv64_regs, ECR_FRGD_MIX, 0x0027); - vgaw16 (cv64_regs, ECR_FRGD_COLOR, bg); - vgaw16 (cv64_regs, ECR_READ_REG_DATA, 0xA000); - vgaw16 (cv64_regs, ECR_CURRENT_Y_POS, dy); - vgaw16 (cv64_regs, ECR_CURRENT_X_POS, dx); - vgaw16 (cv64_regs, ECR_READ_REG_DATA, h - 1); - vgaw16 (cv64_regs, ECR_MAJ_AXIS_PIX_CNT, w - 1); - vgaw16 (cv64_regs, ECR_DRAW_CMD, 0x40B1); + vgaw16 (regs, ECR_FRGD_MIX, 0x0027); + vgaw16 (regs, ECR_FRGD_COLOR, bg); + vgaw16 (regs, ECR_READ_REG_DATA, 0xA000); + vgaw16 (regs, ECR_CURRENT_Y_POS, dy); + vgaw16 (regs, ECR_CURRENT_X_POS, dx); + vgaw16 (regs, ECR_READ_REG_DATA, h - 1); + vgaw16 (regs, ECR_MAJ_AXIS_PIX_CNT, w - 1); + vgaw16 (regs, ECR_DRAW_CMD, 0x40B1); DPRINTK("EXIT\n"); } @@ -2397,83 +2314,84 @@ void cvision_clear (u_short dx, u_short dy, u_short w, u_short h, u_short bg) */ static void cv64_dump (void) { + volatile unsigned char *regs = CyberRegs; DPRINTK("ENTER\n"); /* Dump the VGA setup values */ - *(CyberRegs + S3_CRTC_ADR) = 0x00; - DPRINTK("CR00 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x01; - DPRINTK("CR01 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x02; - DPRINTK("CR02 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x03; - DPRINTK("CR03 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x04; - DPRINTK("CR04 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x05; - DPRINTK("CR05 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x06; - DPRINTK("CR06 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x07; - DPRINTK("CR07 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x08; - DPRINTK("CR08 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x09; - DPRINTK("CR09 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x10; - DPRINTK("CR10 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x11; - DPRINTK("CR11 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x12; - DPRINTK("CR12 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x13; - DPRINTK("CR13 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x15; - DPRINTK("CR15 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x16; - DPRINTK("CR16 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x36; - DPRINTK("CR36 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x37; - DPRINTK("CR37 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x42; - DPRINTK("CR42 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x43; - DPRINTK("CR43 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x50; - DPRINTK("CR50 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x51; - DPRINTK("CR51 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x53; - DPRINTK("CR53 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x58; - DPRINTK("CR58 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x59; - DPRINTK("CR59 = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x5A; - DPRINTK("CR5A = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x5D; - DPRINTK("CR5D = %x\n", *(CyberRegs + S3_CRTC_DATA)); - *(CyberRegs + S3_CRTC_ADR) = 0x5E; - DPRINTK("CR5E = %x\n", *(CyberRegs + S3_CRTC_DATA)); - DPRINTK("MISC = %x\n", *(CyberRegs + GREG_MISC_OUTPUT_R)); - *(CyberRegs + SEQ_ADDRESS) = 0x01; - DPRINTK("SR01 = %x\n", *(CyberRegs + SEQ_ADDRESS_R)); - *(CyberRegs + SEQ_ADDRESS) = 0x02; - DPRINTK("SR02 = %x\n", *(CyberRegs + SEQ_ADDRESS_R)); - *(CyberRegs + SEQ_ADDRESS) = 0x03; - DPRINTK("SR03 = %x\n", *(CyberRegs + SEQ_ADDRESS_R)); - *(CyberRegs + SEQ_ADDRESS) = 0x09; - DPRINTK("SR09 = %x\n", *(CyberRegs + SEQ_ADDRESS_R)); - *(CyberRegs + SEQ_ADDRESS) = 0x10; - DPRINTK("SR10 = %x\n", *(CyberRegs + SEQ_ADDRESS_R)); - *(CyberRegs + SEQ_ADDRESS) = 0x11; - DPRINTK("SR11 = %x\n", *(CyberRegs + SEQ_ADDRESS_R)); - *(CyberRegs + SEQ_ADDRESS) = 0x12; - DPRINTK("SR12 = %x\n", *(CyberRegs + SEQ_ADDRESS_R)); - *(CyberRegs + SEQ_ADDRESS) = 0x13; - DPRINTK("SR13 = %x\n", *(CyberRegs + SEQ_ADDRESS_R)); - *(CyberRegs + SEQ_ADDRESS) = 0x15; - DPRINTK("SR15 = %x\n", *(CyberRegs + SEQ_ADDRESS_R)); + *(regs + S3_CRTC_ADR) = 0x00; + DPRINTK("CR00 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x01; + DPRINTK("CR01 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x02; + DPRINTK("CR02 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x03; + DPRINTK("CR03 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x04; + DPRINTK("CR04 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x05; + DPRINTK("CR05 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x06; + DPRINTK("CR06 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x07; + DPRINTK("CR07 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x08; + DPRINTK("CR08 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x09; + DPRINTK("CR09 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x10; + DPRINTK("CR10 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x11; + DPRINTK("CR11 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x12; + DPRINTK("CR12 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x13; + DPRINTK("CR13 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x15; + DPRINTK("CR15 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x16; + DPRINTK("CR16 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x36; + DPRINTK("CR36 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x37; + DPRINTK("CR37 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x42; + DPRINTK("CR42 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x43; + DPRINTK("CR43 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x50; + DPRINTK("CR50 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x51; + DPRINTK("CR51 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x53; + DPRINTK("CR53 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x58; + DPRINTK("CR58 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x59; + DPRINTK("CR59 = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x5A; + DPRINTK("CR5A = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x5D; + DPRINTK("CR5D = %x\n", *(regs + S3_CRTC_DATA)); + *(regs + S3_CRTC_ADR) = 0x5E; + DPRINTK("CR5E = %x\n", *(regs + S3_CRTC_DATA)); + DPRINTK("MISC = %x\n", *(regs + GREG_MISC_OUTPUT_R)); + *(regs + SEQ_ADDRESS) = 0x01; + DPRINTK("SR01 = %x\n", *(regs + SEQ_ADDRESS_R)); + *(regs + SEQ_ADDRESS) = 0x02; + DPRINTK("SR02 = %x\n", *(regs + SEQ_ADDRESS_R)); + *(regs + SEQ_ADDRESS) = 0x03; + DPRINTK("SR03 = %x\n", *(regs + SEQ_ADDRESS_R)); + *(regs + SEQ_ADDRESS) = 0x09; + DPRINTK("SR09 = %x\n", *(regs + SEQ_ADDRESS_R)); + *(regs + SEQ_ADDRESS) = 0x10; + DPRINTK("SR10 = %x\n", *(regs + SEQ_ADDRESS_R)); + *(regs + SEQ_ADDRESS) = 0x11; + DPRINTK("SR11 = %x\n", *(regs + SEQ_ADDRESS_R)); + *(regs + SEQ_ADDRESS) = 0x12; + DPRINTK("SR12 = %x\n", *(regs + SEQ_ADDRESS_R)); + *(regs + SEQ_ADDRESS) = 0x13; + DPRINTK("SR13 = %x\n", *(regs + SEQ_ADDRESS_R)); + *(regs + SEQ_ADDRESS) = 0x15; + DPRINTK("SR15 = %x\n", *(regs + SEQ_ADDRESS_R)); return; } diff --git a/drivers/video/cyberfb.h b/drivers/video/cyberfb.h index bce02a462d05..2acff941a099 100644 --- a/drivers/video/cyberfb.h +++ b/drivers/video/cyberfb.h @@ -128,26 +128,9 @@ #define GRFBBOPset 0xf /* 1 */ -/* Read VGA register */ -#define vgar(ba, reg) (*(((volatile caddr_t)ba)+reg)) - -/* Write VGA register */ -#define vgaw(ba, reg, val) \ -*(((volatile caddr_t)ba)+reg) = ((val) & 0xff) - -/* Read 16 Bit VGA register */ -#define vgar16(ba, reg) ( *((unsigned short *) (((volatile caddr_t)ba)+reg)) ) - /* Write 16 Bit VGA register */ #define vgaw16(ba, reg, val) \ -*((unsigned short *) (((volatile caddr_t)ba)+reg)) = val - -/* Read 32 Bit VGA register */ -#define vgar32(ba, reg) ( *((unsigned long *) (((volatile caddr_t)ba)+reg)) ) - -/* Write 32 Bit VGA register */ -#define vgaw32(ba, reg, val) \ - *((unsigned long *) (((volatile caddr_t)ba)+reg)) = val +*((unsigned short *) (((volatile unsigned char *)ba)+reg)) = val /* * Defines for the used register addresses (mw) @@ -394,20 +377,20 @@ #define WGfx(ba, idx, val) \ -do { vgaw(ba, GCT_ADDRESS, idx); vgaw(ba, GCT_ADDRESS_W , val); } while (0) +do { wb_64(ba, GCT_ADDRESS, idx); wb_64(ba, GCT_ADDRESS_W , val); } while (0) #define WSeq(ba, idx, val) \ -do { vgaw(ba, SEQ_ADDRESS, idx); vgaw(ba, SEQ_ADDRESS_W , val); } while (0) +do { wb_64(ba, SEQ_ADDRESS, idx); wb_64(ba, SEQ_ADDRESS_W , val); } while (0) #define WCrt(ba, idx, val) \ -do { vgaw(ba, CRT_ADDRESS, idx); vgaw(ba, CRT_ADDRESS_W , val); } while (0) +do { wb_64(ba, CRT_ADDRESS, idx); wb_64(ba, CRT_ADDRESS_W , val); } while (0) #define WAttr(ba, idx, val) \ do { \ unsigned char tmp;\ - tmp = vgar(ba, ACT_ADDRESS_RESET);\ - vgaw(ba, ACT_ADDRESS_W, idx);\ - vgaw(ba, ACT_ADDRESS_W, val);\ + tmp = rb_64(ba, ACT_ADDRESS_RESET);\ + wb_64(ba, ACT_ADDRESS_W, idx);\ + wb_64(ba, ACT_ADDRESS_W, val);\ } while (0) #define SetTextPlane(ba, m) \ @@ -420,21 +403,17 @@ do { \ /* prototypes */ /* --------------------------------- */ -/* in cvision_core.c */ -inline void __cv_delay(unsigned long usecs); -inline void GfxBusyWait(volatile caddr_t board); -inline void GfxFifoWait(volatile caddr_t board); -inline unsigned char RAttr(volatile caddr_t board, short idx); -inline unsigned char RSeq(volatile caddr_t board, short idx); -inline unsigned char RCrt(volatile caddr_t board, short idx); -inline unsigned char RGfx(volatile caddr_t board, short idx); +inline unsigned char RAttr(volatile unsigned char * board, short idx); +inline unsigned char RSeq(volatile unsigned char * board, short idx); +inline unsigned char RCrt(volatile unsigned char * board, short idx); +inline unsigned char RGfx(volatile unsigned char * board, short idx); inline void cv64_write_port(unsigned short bits, volatile unsigned char *board); inline void cvscreen(int toggle, volatile unsigned char *board); inline void gfx_on_off(int toggle, volatile unsigned char *board); #if 0 unsigned short cv64_compute_clock(unsigned long freq); -int cv_has_4mb(volatile caddr_t fb); +int cv_has_4mb(volatile unsigned char * fb); void cv64_board_init(void); void cv64_load_video_mode(struct fb_var_screeninfo *video_mode); #endif diff --git a/drivers/video/fbcon-mac.c b/drivers/video/fbcon-mac.c index 752a85a7282a..7171ab39e655 100644 --- a/drivers/video/fbcon-mac.c +++ b/drivers/video/fbcon-mac.c @@ -331,7 +331,8 @@ static void plot_pixel_mac(struct display *p, int bw, int pixel_x, int pixel_y) u16 *dest16, pix16; u32 *dest32, pix32; - if (pixel_x < 0 || pixel_y < 0 || pixel_x >= 832 || pixel_y >= 624) { + /* There *are* 68k Macs that support more than 832x624, you know :-) */ + if (pixel_x < 0 || pixel_y < 0 || pixel_x >= p->var.xres || pixel_y >= p->var.yres) { int cnt; printk ("ERROR: pixel_x == %d, pixel_y == %d", pixel_x, pixel_y); for(cnt = 0; cnt < 100000; cnt++) diff --git a/drivers/video/macfb.c b/drivers/video/macfb.c index 0f964f60a4d0..31460e43c640 100644 --- a/drivers/video/macfb.c +++ b/drivers/video/macfb.c @@ -1,6 +1,22 @@ -/* - * We've been given MAC frame buffer info by the booter. Now go set it up - */ +/* macfb.c: Generic framebuffer for Macs whose colourmaps/modes we + don't know how to set */ + +/* (c) 1999 David Huggins-Daines + + Primarily based on vesafb.c, by Gerd Knorr + (c) 1998 Gerd Knorr + + Also uses information and code from: + + The original macfb.c from Linux/mac68k 2.0, by Alan Cox, Juergen + Mellinger, Mikael Forselius, Michael Schmitz, and others. + + valkyriefb.c, by Martin Costabel, Kevin Schoedel, Barry Nathan, Dan + Jacobowitz, Paul Mackerras, Fabio Riccardi, and Geert Uytterhoeven. + + This code is free software. You may copy, modify, and distribute + it subject to the terms and conditions of the GNU General Public + License, version 2, or any later version, at your convenience. */ #include #include @@ -13,6 +29,7 @@ #include #include #include +#include #include #include @@ -20,16 +37,120 @@ #include #include #include -#include +#include +#include #include